From 3fe2e6d13d118b5872c9fce8acdece1a90800bad Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Sat, 6 Feb 2021 09:26:44 +0100 Subject: [PATCH 01/22] WIP: Adding cv32e40p core --- Makefile | 15 --------- configs/common.sh | 7 ++++- configs/pulpissimo-cv32e40p.sh | 31 +++++++++++++++++++ .../chips/pulpissimo/pulpissimo_cv32e40p.json | 18 +++++++++++ .../configs/config/pulpissimo_cv32e40p.json | 18 +++++++++++ .../configs/ips/riscv/cv32e40p.json | 26 ++++++++++++++++ .../pulp/models/pulp/udma/udma_v3_impl.cpp | 4 ++- .../pulp/models/pulp/udma/udma_v3_impl.hpp | 4 +-- 8 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 configs/pulpissimo-cv32e40p.sh create mode 100644 tools/gap-configs/configs/chips/pulpissimo/pulpissimo_cv32e40p.json create mode 100644 tools/gap-configs/configs/config/pulpissimo_cv32e40p.json create mode 100644 tools/gap-configs/configs/ips/riscv/cv32e40p.json diff --git a/Makefile b/Makefile index b14c1a7e..a9f114d8 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,8 @@ ifndef PULP_SDK_HOME endif BUILD_DIR ?= $(CURDIR)/build -ARTIFACT_PATH ?= $(CURDIR)/artifact export BUILD_DIR -export ARTIFACT_PATH include rules/json-tools.mk include rules/gap-configs.mk @@ -17,19 +15,6 @@ include rules/dpi-models.mk include rules/gvsoc.mk include rules/pulpos.mk -checkout: gvsoc.checkout.all - git submodule update --init rtos/pulpos/pulp_hal rtos/pulpos/pulp_archi tests/pmsis_tests rtos/pulpos/pulp rtos/pmsis/pmsis_api tools/gapy - build: gvsoc.build.all clean: gvsoc.clean - -all: checkout build - -artifact: gvsoc.artifact pulpos.artifact - rsync -avR --exclude=".git*" Makefile rules configs tools/rules README.md LICENSE $(ARTIFACT_PATH) - - mkdir -p $(ARTIFACT_PATH)/tools/jenkins - git rev-parse HEAD &> $(ARTIFACT_PATH)/tools/jenkins/sdk_version.txt - -.PHONY: artifact diff --git a/configs/common.sh b/configs/common.sh index 32cea6fa..b8ce649b 100644 --- a/configs/common.sh +++ b/configs/common.sh @@ -4,7 +4,12 @@ if [ -n "$PULP_RISCV_GCC_TOOLCHAIN_BASE" ]; then export PULP_RISCV_GCC_TOOLCHAIN=$PULP_RISCV_GCC_TOOLCHAIN_BASE/1.3 fi -export PATH="$PULP_RISCV_GCC_TOOLCHAIN/bin":"$PULP_SDK_HOME/tools/bin":$PATH +if [[ $CORE_NAME = "cv32e40p" ]] +then + export PATH="$COREV_RISCV_GCC_TOOLCHAIN/bin":"$PULP_SDK_HOME/tools/bin":$PATH +else + export PATH="$PULP_RISCV_GCC_TOOLCHAIN/bin":"$PULP_SDK_HOME/tools/bin":$PATH +fi # keep compatibility with gap_sdk export GAP_SDK_HOME=$PULP_SDK_HOME diff --git a/configs/pulpissimo-cv32e40p.sh b/configs/pulpissimo-cv32e40p.sh new file mode 100644 index 00000000..2754c45f --- /dev/null +++ b/configs/pulpissimo-cv32e40p.sh @@ -0,0 +1,31 @@ +#! /bin/bash + + +if [ -n "${ZSH_VERSION:-}" ]; then + DIR="$(readlink -f -- "${(%):-%x}")" + DIRNAME="$(dirname $DIR)" + PULP_SDK_HOME=$(dirname $DIRNAME) + export PULP_SDK_HOME + #echo $(dirname "$(readlink -f ${(%):-%N})") +else + export PULP_SDK_HOME="$(dirname $(dirname "$(readlink -f "${BASH_SOURCE[0]}")"))" +fi + +export TARGET_CHIP_FAMILY="PULP" +export TARGET_CHIP="PULPISSIMO" +export TARGET_NAME="pulpissimo" +export BOARD_NAME=pulpissimo +export PULP_CURRENT_CONFIG=$BOARD_NAME@config_file=config/${BOARD_NAME}_cv32e40p.json + +export PULPOS_BOARD=pulp +export PULPOS_BOARD_VERSION=pulp +export PULPOS_BOARD_PROFILE=pulp +export PULPOS_TARGET=pulp +export PULPOS_SYSTEM=pulp +export GAPY_TARGET=pulp + +export PULPOS_MODULES="$PULP_SDK_HOME/rtos/pulpos/pulp $PULP_SDK_HOME/rtos/pmsis/pmsis_bsp" + +export GVSOC_MODULES="$PULP_SDK_HOME/tools/gvsoc/common $PULP_SDK_HOME/tools/gvsoc/pulp/models" + +source $PULP_SDK_HOME/configs/common.sh diff --git a/tools/gap-configs/configs/chips/pulpissimo/pulpissimo_cv32e40p.json b/tools/gap-configs/configs/chips/pulpissimo/pulpissimo_cv32e40p.json new file mode 100644 index 00000000..5950813f --- /dev/null +++ b/tools/gap-configs/configs/chips/pulpissimo/pulpissimo_cv32e40p.json @@ -0,0 +1,18 @@ +{ + "@includes@": [ "chips/pulpissimo/pulpissimo.json" ], + + "soc": { + "fc": { + "core" : "cv32e40p" + } + }, + + "config": { + + "install_name": "pulpissimo_cv32e40p", + + "vsim": { + "tcl_args": ["-gCORE_TYPE=1"] + } + } +} \ No newline at end of file diff --git a/tools/gap-configs/configs/config/pulpissimo_cv32e40p.json b/tools/gap-configs/configs/config/pulpissimo_cv32e40p.json new file mode 100644 index 00000000..5950813f --- /dev/null +++ b/tools/gap-configs/configs/config/pulpissimo_cv32e40p.json @@ -0,0 +1,18 @@ +{ + "@includes@": [ "chips/pulpissimo/pulpissimo.json" ], + + "soc": { + "fc": { + "core" : "cv32e40p" + } + }, + + "config": { + + "install_name": "pulpissimo_cv32e40p", + + "vsim": { + "tcl_args": ["-gCORE_TYPE=1"] + } + } +} \ No newline at end of file diff --git a/tools/gap-configs/configs/ips/riscv/cv32e40p.json b/tools/gap-configs/configs/ips/riscv/cv32e40p.json new file mode 100644 index 00000000..a57c9161 --- /dev/null +++ b/tools/gap-configs/configs/ips/riscv/cv32e40p.json @@ -0,0 +1,26 @@ +{ + "version" : "cv32e40p", + "archi" : "riscv", + "implementation": "cv32e40p", + "gv_isa" : ["--pulpv2", "--pulp", "--rv32m", "--itc-external-req"], + "march" : "imcXcorev", + "isa" : "rv32imcXcorev", + "priv_version" : 1.9, + "perf_counters" : true, + "features" : [ "misaligned", "perf" ], + "hal_files" : [ "hal/riscv/riscv_v4.h", "hal/riscv/types.h" ], + "archi_files" : [ "archi/riscv/priv_1_9.h", "archi/riscv/builtins_v2.h", "archi/riscv/builtins_v2_emu.h", "archi/riscv/pcer_v1.h" ], + "defines" : [ "ARCHI_CORE_HAS_PULPV2", "CORE_PULP_BUILTINS" ], + "vp_class": "cpu/iss/iss", + "vp_component": "cpu/iss/iss_riscy", + "bootaddr_offset": "0x80", + "fetch_enable" : false, + "boot_addr" : "0x00000000", + "debug_binaries": [], + "power_models": { + "@includes@": [ + "power_models/core/riscy.json" + ] + }, + "iss_class": "iss_riscy" +} diff --git a/tools/gvsoc/pulp/models/pulp/udma/udma_v3_impl.cpp b/tools/gvsoc/pulp/models/pulp/udma/udma_v3_impl.cpp index 5f61d6d0..2fe5457c 100644 --- a/tools/gvsoc/pulp/models/pulp/udma/udma_v3_impl.cpp +++ b/tools/gvsoc/pulp/models/pulp/udma/udma_v3_impl.cpp @@ -592,7 +592,7 @@ vp::io_req_status_e udma::periph_req(vp::io_req *req, uint64_t offset) int periph_id = UDMA_PERIPH_GET(offset); -#if HAS_HYPER +#if HAS_HYPER && (HYPER_VERSION == 3) if (periph_id >= nb_periphs + HYPER_NB_CHANNELS || periphs[periph_id] == NULL) #else if (periph_id >= nb_periphs || periphs[periph_id] == NULL) @@ -765,10 +765,12 @@ int udma::build() { Hyper_periph_v3 *periph = new Hyper_periph_v3(this, id, j); periphs[id] = periph; +#if HYPER_VERSION == 3 for(int channel_count=1; channel_count<=HYPER_NB_CHANNELS; channel_count++) { periphs[id+channel_count] = periph; } +#endif } else { diff --git a/tools/gvsoc/pulp/models/pulp/udma/udma_v3_impl.hpp b/tools/gvsoc/pulp/models/pulp/udma/udma_v3_impl.hpp index 068c7a44..22d27b3d 100644 --- a/tools/gvsoc/pulp/models/pulp/udma/udma_v3_impl.hpp +++ b/tools/gvsoc/pulp/models/pulp/udma/udma_v3_impl.hpp @@ -743,7 +743,7 @@ class Cpi_periph_v1 : public Udma_periph // }; // //#endif - +#if HYPER_VERSION == 3 class Hyper_periph_v3; class Hyper_transfer : public Udma_transfer @@ -902,7 +902,7 @@ class Hyper_periph_v3 : public Udma_periph int mem_sel; }; - +#endif /* * UDMA From a918fb4fa820e487fe22ba89a9fa3d212ce56386 Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Wed, 10 Feb 2021 17:07:02 +0100 Subject: [PATCH 02/22] Add target for cv32e40p core --- configs/common.sh | 7 +-- ...ulpissimo-cv32e40p.sh => pulp-cv32e40p.sh} | 10 ++-- .../rules/pulpos/targets/pulp_cv32e40p.mk | 48 +++++++++++++++++++ .../configs/chips/pulp/pulp_cv32e40p.json | 13 +++++ .../chips/pulpissimo/pulpissimo_cv32e40p.json | 18 ------- .../configs/config/pulp_cv32e40p.json | 13 +++++ .../configs/config/pulpissimo_cv32e40p.json | 18 ------- .../configs/ips/riscv/cv32e40p.json | 31 ++++++------ tools/gapy/targets/pulp_cv32e40p.json | 25 ++++++++++ 9 files changed, 122 insertions(+), 61 deletions(-) rename configs/{pulpissimo-cv32e40p.sh => pulp-cv32e40p.sh} (84%) create mode 100644 rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk create mode 100644 tools/gap-configs/configs/chips/pulp/pulp_cv32e40p.json delete mode 100644 tools/gap-configs/configs/chips/pulpissimo/pulpissimo_cv32e40p.json create mode 100644 tools/gap-configs/configs/config/pulp_cv32e40p.json delete mode 100644 tools/gap-configs/configs/config/pulpissimo_cv32e40p.json create mode 100644 tools/gapy/targets/pulp_cv32e40p.json diff --git a/configs/common.sh b/configs/common.sh index b8ce649b..32cea6fa 100644 --- a/configs/common.sh +++ b/configs/common.sh @@ -4,12 +4,7 @@ if [ -n "$PULP_RISCV_GCC_TOOLCHAIN_BASE" ]; then export PULP_RISCV_GCC_TOOLCHAIN=$PULP_RISCV_GCC_TOOLCHAIN_BASE/1.3 fi -if [[ $CORE_NAME = "cv32e40p" ]] -then - export PATH="$COREV_RISCV_GCC_TOOLCHAIN/bin":"$PULP_SDK_HOME/tools/bin":$PATH -else - export PATH="$PULP_RISCV_GCC_TOOLCHAIN/bin":"$PULP_SDK_HOME/tools/bin":$PATH -fi +export PATH="$PULP_RISCV_GCC_TOOLCHAIN/bin":"$PULP_SDK_HOME/tools/bin":$PATH # keep compatibility with gap_sdk export GAP_SDK_HOME=$PULP_SDK_HOME diff --git a/configs/pulpissimo-cv32e40p.sh b/configs/pulp-cv32e40p.sh similarity index 84% rename from configs/pulpissimo-cv32e40p.sh rename to configs/pulp-cv32e40p.sh index 2754c45f..21adcaa6 100644 --- a/configs/pulpissimo-cv32e40p.sh +++ b/configs/pulp-cv32e40p.sh @@ -12,17 +12,17 @@ else fi export TARGET_CHIP_FAMILY="PULP" -export TARGET_CHIP="PULPISSIMO" -export TARGET_NAME="pulpissimo" -export BOARD_NAME=pulpissimo +export TARGET_CHIP="PULP" +export TARGET_NAME="pulp" +export BOARD_NAME=pulp export PULP_CURRENT_CONFIG=$BOARD_NAME@config_file=config/${BOARD_NAME}_cv32e40p.json export PULPOS_BOARD=pulp export PULPOS_BOARD_VERSION=pulp export PULPOS_BOARD_PROFILE=pulp -export PULPOS_TARGET=pulp +export PULPOS_TARGET=pulp_cv32e40p export PULPOS_SYSTEM=pulp -export GAPY_TARGET=pulp +export GAPY_TARGET=pulp_cv32e40p export PULPOS_MODULES="$PULP_SDK_HOME/rtos/pulpos/pulp $PULP_SDK_HOME/rtos/pmsis/pmsis_bsp" diff --git a/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk b/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk new file mode 100644 index 00000000..58472079 --- /dev/null +++ b/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk @@ -0,0 +1,48 @@ +CONFIG_NB_CLUSTER_PE ?= 8 + +PULP_LDFLAGS += +PULP_CFLAGS += -D__riscv__ +PULP_ARCH_CFLAGS ?= -march=rv32imcxcorev -mPE=$(CONFIG_NB_CLUSTER_PE) -mFC=1 +PULP_ARCH_LDFLAGS ?= -march=rv32imcxcorev -mPE=$(CONFIG_NB_CLUSTER_PE) -mFC=1 +PULP_ARCH_OBJDFLAGS ?= -Mmarch=rv32imcxcorev +PULP_CFLAGS += -fdata-sections -ffunction-sections -include pos/chips/pulp/config.h -I$(PULPOS_PULP_HOME)/include/pos/chips/pulp +PULP_OMP_CFLAGS += -fopenmp -mnativeomp +PULP_LDFLAGS += -nostartfiles -nostdlib -Wl,--gc-sections -L$(PULPOS_PULP_HOME)/kernel -Tchips/pulp/link.ld -lgcc + +PULP_CC = riscv32-unknown-elf-gcc +PULP_AR ?= riscv32-unknown-elf-ar +PULP_LD ?= riscv32-unknown-elf-gcc +PULP_OBJDUMP ?= riscv32-unknown-elf-objdump + +fc/archi=riscv +pe/archi=riscv +pulp_chip=pulp +pulp_chip_family=pulp +cluster/version=5 +fc_itc/version=1 +udma/cpi/version=1 +udma/i2c/version=2 +soc/fll/version=1 +udma/i2s/version=2 +udma/uart/version=1 +event_unit/version=3 +perf_counters=True +fll/version=1 +#padframe/version=1 +udma/spim/version=3 +#gpio/version=2 +udma/archi=3 +udma/version=3 +soc_eu/version=2 + +udma/hyper/version=3 + + +# FLL +PULP_SRCS += kernel/fll-v$(fll/version).c +PULP_SRCS += kernel/freq-domains.c +PULP_SRCS += kernel/chips/pulp/soc.c + + +include $(PULPOS_HOME)/rules/pulpos/configs/default.mk +include $(PULPOS_HOME)/rules/pulpos/default_rules.mk \ No newline at end of file diff --git a/tools/gap-configs/configs/chips/pulp/pulp_cv32e40p.json b/tools/gap-configs/configs/chips/pulp/pulp_cv32e40p.json new file mode 100644 index 00000000..48b7b534 --- /dev/null +++ b/tools/gap-configs/configs/chips/pulp/pulp_cv32e40p.json @@ -0,0 +1,13 @@ +{ + "@includes@": [ "chips/pulp/pulp.json" ], + + "soc": { + "fc": { + "core" : "cv32e40p" + } + }, + + "cluster": { + "core": "cv32e40p" + } +} \ No newline at end of file diff --git a/tools/gap-configs/configs/chips/pulpissimo/pulpissimo_cv32e40p.json b/tools/gap-configs/configs/chips/pulpissimo/pulpissimo_cv32e40p.json deleted file mode 100644 index 5950813f..00000000 --- a/tools/gap-configs/configs/chips/pulpissimo/pulpissimo_cv32e40p.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "@includes@": [ "chips/pulpissimo/pulpissimo.json" ], - - "soc": { - "fc": { - "core" : "cv32e40p" - } - }, - - "config": { - - "install_name": "pulpissimo_cv32e40p", - - "vsim": { - "tcl_args": ["-gCORE_TYPE=1"] - } - } -} \ No newline at end of file diff --git a/tools/gap-configs/configs/config/pulp_cv32e40p.json b/tools/gap-configs/configs/config/pulp_cv32e40p.json new file mode 100644 index 00000000..48b7b534 --- /dev/null +++ b/tools/gap-configs/configs/config/pulp_cv32e40p.json @@ -0,0 +1,13 @@ +{ + "@includes@": [ "chips/pulp/pulp.json" ], + + "soc": { + "fc": { + "core" : "cv32e40p" + } + }, + + "cluster": { + "core": "cv32e40p" + } +} \ No newline at end of file diff --git a/tools/gap-configs/configs/config/pulpissimo_cv32e40p.json b/tools/gap-configs/configs/config/pulpissimo_cv32e40p.json deleted file mode 100644 index 5950813f..00000000 --- a/tools/gap-configs/configs/config/pulpissimo_cv32e40p.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "@includes@": [ "chips/pulpissimo/pulpissimo.json" ], - - "soc": { - "fc": { - "core" : "cv32e40p" - } - }, - - "config": { - - "install_name": "pulpissimo_cv32e40p", - - "vsim": { - "tcl_args": ["-gCORE_TYPE=1"] - } - } -} \ No newline at end of file diff --git a/tools/gap-configs/configs/ips/riscv/cv32e40p.json b/tools/gap-configs/configs/ips/riscv/cv32e40p.json index a57c9161..7d262bed 100644 --- a/tools/gap-configs/configs/ips/riscv/cv32e40p.json +++ b/tools/gap-configs/configs/ips/riscv/cv32e40p.json @@ -1,26 +1,29 @@ { - "version" : "cv32e40p", + "version" : "ri5cyv2", + "bootaddr_offset": "0x00", "archi" : "riscv", - "implementation": "cv32e40p", - "gv_isa" : ["--pulpv2", "--pulp", "--rv32m", "--itc-external-req"], - "march" : "imcXcorev", - "isa" : "rv32imcXcorev", + "implementation": "ri5cy", + "gv_isa" : ["--pulp", "--rv32m", "--pulpv2", "--pulp-perf-counters", "--pulp-hw-loop", "--itc-external-req", "--fpu", "--fpud", "--shared-fpu"], + "isa" : "rv32imfcXpulpv2Xf8Xf16XfvecXfauxXf16altXgap9", + "march" : "imfcXpulpv2Xf8Xf16XfvecXfauxXf16alt", "priv_version" : 1.9, "perf_counters" : true, - "features" : [ "misaligned", "perf" ], - "hal_files" : [ "hal/riscv/riscv_v4.h", "hal/riscv/types.h" ], - "archi_files" : [ "archi/riscv/priv_1_9.h", "archi/riscv/builtins_v2.h", "archi/riscv/builtins_v2_emu.h", "archi/riscv/pcer_v1.h" ], - "defines" : [ "ARCHI_CORE_HAS_PULPV2", "CORE_PULP_BUILTINS" ], + "first_ext_counter": 12, + "features" : [ "misaligned", "perf"], + "hal_files" : [ "hal/riscv/riscv_v5.h", "hal/riscv/types.h" ], + "archi_files" : [ "archi/riscv/priv_1_10.h", "archi/riscv/builtins_v2.h", "archi/riscv/builtins_v2_emu.h", "archi/riscv/pcer_v2.h" ], + "defines" : [ "ARCHI_CORE_HAS_PULPV2", "CORE_PULP_BUILTINS", "ARCHI_CORE_HAS_1_10" ], "vp_class": "cpu/iss/iss", - "vp_component": "cpu/iss/iss_riscy", - "bootaddr_offset": "0x80", - "fetch_enable" : false, - "boot_addr" : "0x00000000", + "vp_component": "cpu/iss/iss_riscy_v2_5_single_regfile", + "first_external_pcer": 12, + "riscv_dbg_unit": true, "debug_binaries": [], + "debug_handler" : "0x1a190800", "power_models": { "@includes@": [ "power_models/core/riscy.json" ] }, - "iss_class": "iss_riscy" + "iss_class": "iss_riscy_v2_5_single_regfile" } + diff --git a/tools/gapy/targets/pulp_cv32e40p.json b/tools/gapy/targets/pulp_cv32e40p.json new file mode 100644 index 00000000..346389f6 --- /dev/null +++ b/tools/gapy/targets/pulp_cv32e40p.json @@ -0,0 +1,25 @@ +{ + "@include@": "common.json", + + "gvsoc": { + "runner_module": "gv.chips.pulp" + }, + + "rtl": { + "runner_module": "runner.rtl.chips.pulp" + }, + + "target": { + "@includes@": [ "chips/pulp/pulp_cv32e40p.json" ] + }, + + "runner": { + "flash_devices": [ + "target/board/devices/flash" + ], + "boot": { + "mode": "flash", + "device": "target/board/devices/flash" + } + } +} \ No newline at end of file From 9fd205eea78b2783ab86008b3a1600225ae94507 Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Fri, 12 Feb 2021 16:30:07 +0100 Subject: [PATCH 03/22] Add corev ISA --- ...pulp-cv32e40p.sh => pulp-open-cv32e40p.sh} | 0 .../models/cpu/iss/isa_gen/isa_riscv_gen.py | 375 +++++++++++++++++- 2 files changed, 373 insertions(+), 2 deletions(-) rename configs/{pulp-cv32e40p.sh => pulp-open-cv32e40p.sh} (100%) diff --git a/configs/pulp-cv32e40p.sh b/configs/pulp-open-cv32e40p.sh similarity index 100% rename from configs/pulp-cv32e40p.sh rename to configs/pulp-open-cv32e40p.sh diff --git a/tools/gvsoc/common/models/cpu/iss/isa_gen/isa_riscv_gen.py b/tools/gvsoc/common/models/cpu/iss/isa_gen/isa_riscv_gen.py index a4ec6db1..3d8eac3d 100755 --- a/tools/gvsoc/common/models/cpu/iss/isa_gen/isa_riscv_gen.py +++ b/tools/gvsoc/common/models/cpu/iss/isa_gen/isa_riscv_gen.py @@ -1715,6 +1715,376 @@ def __init__(self, label, format, encoding, decode=None, N=None, L=None, mapTo=N ] +corev_insns = [ + + + R5('LB_POSTINC', 'LPOST', '------- ----- ----- 000 ----- 0001011', L='cv.lb' , fast_handler=True, tags=["load"]), + R5('LBU_POSTINC', 'LPOST', '------- ----- ----- 100 ----- 0001011', L='cv.lbu', fast_handler=True, tags=["load"]), + R5('LH_POSTINC', 'LPOST', '------- ----- ----- 001 ----- 0001011', L='cv.lh' , fast_handler=True, tags=["load"]), + R5('LHU_POSTINC', 'LPOST', '------- ----- ----- 101 ----- 0001011', L='cv.lhu', fast_handler=True, tags=["load"]), + R5('LW_POSTINC', 'LPOST', '------- ----- ----- 010 ----- 0001011', L='cv.lw' , fast_handler=True, tags=["load"]), + + R5('LB_RR_POSTINC', 'LRPOST', '0000000 ----- ----- 111 ----- 0001011', L='cv.lb' , fast_handler=True, tags=["load"]), + R5('LBU_RR_POSTINC', 'LRPOST', '0100000 ----- ----- 111 ----- 0001011', L='cv.lbu', fast_handler=True, tags=["load"]), + R5('LH_RR_POSTINC', 'LRPOST', '0001000 ----- ----- 111 ----- 0001011', L='cv.lh' , fast_handler=True, tags=["load"]), + R5('LHU_RR_POSTINC', 'LRPOST', '0101000 ----- ----- 111 ----- 0001011', L='cv.lhu', fast_handler=True, tags=["load"]), + R5('LW_RR_POSTINC', 'LRPOST', '0010000 ----- ----- 111 ----- 0001011', L='cv.lw' , fast_handler=True, tags=["load"]), + + R5('LB_RR', 'LR', '0000000 ----- ----- 111 ----- 0000011', L='cv.lb' , fast_handler=True, tags=["load"]), + R5('LBU_RR', 'LR', '0100000 ----- ----- 111 ----- 0000011', L='cv.lbu', fast_handler=True, tags=["load"]), + R5('LH_RR', 'LR', '0001000 ----- ----- 111 ----- 0000011', L='cv.lh' , fast_handler=True, tags=["load"]), + R5('LHU_RR', 'LR', '0101000 ----- ----- 111 ----- 0000011', L='cv.lhu', fast_handler=True, tags=["load"]), + R5('LW_RR', 'LR', '0010000 ----- ----- 111 ----- 0000011', L='cv.lw' , fast_handler=True, tags=["load"]), + + R5('SB_POSTINC', 'SPOST', '------- ----- ----- 000 ----- 0101011', L='cv.sb' , fast_handler=True), + R5('SH_POSTINC', 'SPOST', '------- ----- ----- 001 ----- 0101011', L='cv.sh' , fast_handler=True), + R5('SW_POSTINC', 'SPOST', '------- ----- ----- 010 ----- 0101011', L='cv.sw' , fast_handler=True), + + R5('SB_RR_POSTINC', 'SRPOST', '0000000 ----- ----- 100 ----- 0101011', L='cv.sb' , fast_handler=True), + R5('SH_RR_POSTINC', 'SRPOST', '0000000 ----- ----- 101 ----- 0101011', L='cv.sh' , fast_handler=True), + R5('SW_RR_POSTINC', 'SRPOST', '0000000 ----- ----- 110 ----- 0101011', L='cv.sw' , fast_handler=True), + + R5('SB_RR', 'SR', '0000000 ----- ----- 100 ----- 0100011', L='cv.sb', fast_handler=True), + R5('SH_RR', 'SR', '0000000 ----- ----- 101 ----- 0100011', L='cv.sh', fast_handler=True), + R5('SW_RR', 'SR', '0000000 ----- ----- 110 ----- 0100011', L='cv.sw', fast_handler=True), + + R5('cv.elw', 'L', '------- ----- ----- 110 ----- 0000011', tags=["load"]), + + R5('cv.starti','HL0','------- ----- 00000 000 0000- 1111011'), + R5('cv.endi', 'HL0','------- ----- 00000 001 0000- 1111011'), + R5('cv.count', 'HL0','0000000 00000 ----- 010 0000- 1111011'), + R5('cv.counti','HL0','------- ----- 00000 011 0000- 1111011'), + R5('cv.setup', 'HL0','------- ----- ----- 100 0000- 1111011'), + R5('cv.setupi','HL1','------- ----- ----- 101 0000- 1111011'), + + R5('cv.extract', 'I4U', '11----- ----- ----- 000 ----- 0110011'), + R5('cv.extractu', 'I4U', '11----- ----- ----- 001 ----- 0110011'), + R5('cv.insert', 'I5U', '11----- ----- ----- 010 ----- 0110011'), + R5('cv.bclr', 'I4U', '11----- ----- ----- 011 ----- 0110011'), + R5('cv.bset', 'I4U', '11----- ----- ----- 100 ----- 0110011'), + R5('cv.extractr', 'R', '1000000 ----- ----- 000 ----- 0110011'), + R5('cv.extractur', 'R', '1000000 ----- ----- 001 ----- 0110011'), + R5('cv.insertr', 'I5U2', '1000000 ----- ----- 010 ----- 0110011'), + R5('cv.bclrr', 'R', '1000000 ----- ----- 011 ----- 0110011'), + R5('cv.bsetr', 'R', '1000000 ----- ----- 100 ----- 0110011'), + R5('cv.bitrev', 'BITREV', '11000-- ----- ----- 101 ----- 0110011', mapTo="gap9_BITREV"),#preso da gap9 + + R5('cv.ror', 'R', '0000100 ----- ----- 101 ----- 0110011'), + R5('cv.ff1', 'R1', '0001000 00000 ----- 000 ----- 0110011'), + R5('cv.fl1','R1', '0001000 00000 ----- 001 ----- 0110011'), + R5('cv.clb', 'R1', '0001000 00000 ----- 010 ----- 0110011'), + R5('cv.cnt', 'R1', '0001000 00000 ----- 011 ----- 0110011'), + + R5('cv.abs', 'R1', '0000010 00000 ----- 000 ----- 0110011'), + R5('cv.slet','R', '0000010 ----- ----- 010 ----- 0110011'), + R5('cv.sletu','R', '0000010 ----- ----- 011 ----- 0110011'), + R5('cv.min', 'R', '0000010 ----- ----- 100 ----- 0110011'), + R5('cv.minu','R', '0000010 ----- ----- 101 ----- 0110011'), + R5('cv.max', 'R', '0000010 ----- ----- 110 ----- 0110011'), + R5('cv.maxu','R', '0000010 ----- ----- 111 ----- 0110011'), + R5('cv.exths','R1', '0001000 00000 ----- 100 ----- 0110011'), + R5('cv.exthz','R1', '0001000 00000 ----- 101 ----- 0110011'), + R5('cv.extbs','R1', '0001000 00000 ----- 110 ----- 0110011'), + R5('cv.extbz','R1', '0001000 00000 ----- 111 ----- 0110011'), + + R5('cv.clip', 'I1U', '0001010 ----- ----- 001 ----- 0110011'), + R5('cv.clipu', 'I1U', '0001010 ----- ----- 010 ----- 0110011'), + R5('cv.clipr', 'R', '0001010 ----- ----- 101 ----- 0110011'), + R5('cv.clipur', 'R', '0001010 ----- ----- 110 ----- 0110011'), + + R5('cv.addN', 'RRRU2','00----- ----- ----- 010 ----- 1011011'), + R5('cv.adduN', 'RRRU2','10----- ----- ----- 010 ----- 1011011'), + R5('cv.addRN', 'RRRU2','00----- ----- ----- 110 ----- 1011011'), + R5('cv.adduRN', 'RRRU2','10----- ----- ----- 110 ----- 1011011'), + + R5('cv.subN', 'RRRU2','00----- ----- ----- 011 ----- 1011011'), + R5('cv.subuN', 'RRRU2','10----- ----- ----- 011 ----- 1011011'), + R5('cv.subRN', 'RRRU2','00----- ----- ----- 111 ----- 1011011'), + R5('cv.subuRN', 'RRRU2','10----- ----- ----- 111 ----- 1011011'), + + R5('cv.addNr', 'RRRR2', '0100000 ----- ----- 010 ----- 1011011'), + R5('cv.adduNr', 'RRRR2', '1100000 ----- ----- 010 ----- 1011011'), + R5('cv.addRNr', 'RRRR2', '0100000 ----- ----- 110 ----- 1011011'), + R5('cv.adduRNr', 'RRRR2', '1100000 ----- ----- 110 ----- 1011011'), + + R5('cv.subNr', 'RRRR2', '0100000 ----- ----- 011 ----- 1011011'), + R5('cv.subuNr', 'RRRR2', '1100000 ----- ----- 011 ----- 1011011'), + R5('cv.subRNr', 'RRRR2', '0100000 ----- ----- 111 ----- 1011011'), + R5('cv.subuRNr', 'RRRR2', '1100000 ----- ----- 111 ----- 1011011'), + + R5('cv.beqimm', 'SB2', '------- ----- ----- 010 ----- 1100011', fast_handler=True, decode='bxx_decode'), + R5('cv.bneimm', 'SB2', '------- ----- ----- 011 ----- 1100011', fast_handler=True, decode='bxx_decode'), + + R5('cv.mac', 'RRRR', '0100001 ----- ----- 000 ----- 0110011'), + R5('cv.msu', 'RRRR', '0100001 ----- ----- 001 ----- 0110011'), + + R5('cv.muls', 'R', '1000000 ----- ----- 000 ----- 1011011'), + R5('cv.mulhhs', 'R', '1100000 ----- ----- 000 ----- 1011011'), + R5('cv.mulsN', 'RRRU2','10----- ----- ----- 000 ----- 1011011'), + R5('cv.mulhhsN', 'RRRU2','11----- ----- ----- 000 ----- 1011011'), + R5('cv.mulsRN', 'RRRU2','10----- ----- ----- 100 ----- 1011011'), + R5('cv.mulhhsRN', 'RRRU2','11----- ----- ----- 100 ----- 1011011'), + R5('cv.mulu', 'R', '0000000 ----- ----- 000 ----- 1011011'), + R5('cv.mulhhu', 'R', '0100000 ----- ----- 000 ----- 1011011'), + R5('cv.muluN', 'RRRU2','00----- ----- ----- 000 ----- 1011011'), + R5('cv.mulhhuN', 'RRRU2','01----- ----- ----- 000 ----- 1011011'), + R5('cv.muluRN', 'RRRU2','00----- ----- ----- 100 ----- 1011011'), + R5('cv.mulhhuRN', 'RRRU2','01----- ----- ----- 100 ----- 1011011'), + R5('cv.macsN', 'RRRRU','10----- ----- ----- 001 ----- 1011011'), + R5('cv.machhsN', 'RRRRU','11----- ----- ----- 001 ----- 1011011'), + R5('cv.macsRN', 'RRRRU','10----- ----- ----- 101 ----- 1011011'), + R5('cv.machhsRN', 'RRRRU','11----- ----- ----- 101 ----- 1011011'), + R5('cv.macuN', 'RRRRU','00----- ----- ----- 001 ----- 1011011'), + R5('cv.machhuN', 'RRRRU','01----- ----- ----- 001 ----- 1011011'), + R5('cv.macuRN', 'RRRRU','00----- ----- ----- 101 ----- 1011011'), + R5('cv.machhuRN', 'RRRRU','01----- ----- ----- 101 ----- 1011011'), + + # R5('pv.add.h', 'R', '000000- ----- ----- 000 ----- 1010111'), + # R5('pv.add.sc.h', 'R', '000000- ----- ----- 100 ----- 1010111'), + # R5('pv.add.sci.h', 'RRS', '000000- ----- ----- 110 ----- 1010111'), + # R5('pv.add.b', 'R', '000000- ----- ----- 001 ----- 1010111'), + # R5('pv.add.sc.b', 'R', '000000- ----- ----- 101 ----- 1010111'), + # R5('pv.add.sci.b', 'RRS', '000000- ----- ----- 111 ----- 1010111'), + + # R5('pv.sub.h', 'R', '000010- ----- ----- 000 ----- 1010111'), + # R5('pv.sub.sc.h', 'R', '000010- ----- ----- 100 ----- 1010111'), + # R5('pv.sub.sci.h', 'RRS', '000010- ----- ----- 110 ----- 1010111'), + # R5('pv.sub.b', 'R', '000010- ----- ----- 001 ----- 1010111'), + # R5('pv.sub.sc.b', 'R', '000010- ----- ----- 101 ----- 1010111'), + # R5('pv.sub.sci.b', 'RRS', '000010- ----- ----- 111 ----- 1010111'), + + # R5('pv.avg.h', 'R', '000100- ----- ----- 000 ----- 1010111'), + # R5('pv.avg.sc.h', 'R', '000100- ----- ----- 100 ----- 1010111'), + # R5('pv.avg.sci.h', 'RRS', '000100- ----- ----- 110 ----- 1010111'), + # R5('pv.avg.b', 'R', '000100- ----- ----- 001 ----- 1010111'), + # R5('pv.avg.sc.b', 'R', '000100- ----- ----- 101 ----- 1010111'), + # R5('pv.avg.sci.b', 'RRS', '000100- ----- ----- 111 ----- 1010111'), + + # R5('pv.avgu.h', 'R', '000110- ----- ----- 000 ----- 1010111'), + # R5('pv.avgu.sc.h', 'R', '000110- ----- ----- 100 ----- 1010111'), + # R5('pv.avgu.sci.h', 'RRU', '000110- ----- ----- 110 ----- 1010111'), + # R5('pv.avgu.b', 'R', '000110- ----- ----- 001 ----- 1010111'), + # R5('pv.avgu.sc.b', 'R', '000110- ----- ----- 101 ----- 1010111'), + # R5('pv.avgu.sci.b', 'RRU', '000110- ----- ----- 111 ----- 1010111'), + + # R5('pv.min.h', 'R', '001000- ----- ----- 000 ----- 1010111'), + # R5('pv.min.sc.h', 'R', '001000- ----- ----- 100 ----- 1010111'), + # R5('pv.min.sci.h', 'RRS', '001000- ----- ----- 110 ----- 1010111'), + # R5('pv.min.b', 'R', '001000- ----- ----- 001 ----- 1010111'), + # R5('pv.min.sc.b', 'R', '001000- ----- ----- 101 ----- 1010111'), + # R5('pv.min.sci.b', 'RRS', '001000- ----- ----- 111 ----- 1010111'), + + # R5('pv.minu.h', 'R', '001010- ----- ----- 000 ----- 1010111'), + # R5('pv.minu.sc.h', 'R', '001010- ----- ----- 100 ----- 1010111'), + # R5('pv.minu.sci.h', 'RRU', '001010- ----- ----- 110 ----- 1010111'), + # R5('pv.minu.b', 'R', '001010- ----- ----- 001 ----- 1010111'), + # R5('pv.minu.sc.b', 'R', '001010- ----- ----- 101 ----- 1010111'), + # R5('pv.minu.sci.b', 'RRU', '001010- ----- ----- 111 ----- 1010111'), + + # R5('pv.max.h', 'R', '001100- ----- ----- 000 ----- 1010111'), + # R5('pv.max.sc.h', 'R', '001100- ----- ----- 100 ----- 1010111'), + # R5('pv.max.sci.h', 'RRS', '001100- ----- ----- 110 ----- 1010111'), + # R5('pv.max.b', 'R', '001100- ----- ----- 001 ----- 1010111'), + # R5('pv.max.sc.b', 'R', '001100- ----- ----- 101 ----- 1010111'), + # R5('pv.max.sci.b', 'RRS', '001100- ----- ----- 111 ----- 1010111'), + + # R5('pv.maxu.h', 'R', '001110- ----- ----- 000 ----- 1010111'), + # R5('pv.maxu.sc.h', 'R', '001110- ----- ----- 100 ----- 1010111'), + # R5('pv.maxu.sci.h', 'RRU', '001110- ----- ----- 110 ----- 1010111'), + # R5('pv.maxu.b', 'R', '001110- ----- ----- 001 ----- 1010111'), + # R5('pv.maxu.sc.b', 'R', '001110- ----- ----- 101 ----- 1010111'), + # R5('pv.maxu.sci.b', 'RRU', '001110- ----- ----- 111 ----- 1010111'), + + # R5('pv.srl.h', 'R', '010000- ----- ----- 000 ----- 1010111'), + # R5('pv.srl.sc.h', 'R', '010000- ----- ----- 100 ----- 1010111'), + # R5('pv.srl.sci.h', 'RRU', '010000- ----- ----- 110 ----- 1010111'), + # R5('pv.srl.b', 'R', '010000- ----- ----- 001 ----- 1010111'), + # R5('pv.srl.sc.b', 'R', '010000- ----- ----- 101 ----- 1010111'), + # R5('pv.srl.sci.b', 'RRU', '010000- ----- ----- 111 ----- 1010111'), + + # R5('pv.sra.h', 'R', '010010- ----- ----- 000 ----- 1010111'), + # R5('pv.sra.sc.h', 'R', '010010- ----- ----- 100 ----- 1010111'), + # R5('pv.sra.sci.h', 'RRS', '010010- ----- ----- 110 ----- 1010111'), + # R5('pv.sra.b', 'R', '010010- ----- ----- 001 ----- 1010111'), + # R5('pv.sra.sc.b', 'R', '010010- ----- ----- 101 ----- 1010111'), + # R5('pv.sra.sci.b', 'RRS', '010010- ----- ----- 111 ----- 1010111'), + + # R5('pv.sll.h', 'R', '010100- ----- ----- 000 ----- 1010111'), + # R5('pv.sll.sc.h', 'R', '010100- ----- ----- 100 ----- 1010111'), + # R5('pv.sll.sci.h', 'RRU', '010100- ----- ----- 110 ----- 1010111'), + # R5('pv.sll.b', 'R', '010100- ----- ----- 001 ----- 1010111'), + # R5('pv.sll.sc.b', 'R', '010100- ----- ----- 101 ----- 1010111'), + # R5('pv.sll.sci.b', 'RRU', '010100- ----- ----- 111 ----- 1010111'), + + # R5('pv.or.h', 'R', '010110- ----- ----- 000 ----- 1010111'), + # R5('pv.or.sc.h', 'R', '010110- ----- ----- 100 ----- 1010111'), + # R5('pv.or.sci.h', 'RRS', '010110- ----- ----- 110 ----- 1010111'), + # R5('pv.or.b', 'R', '010110- ----- ----- 001 ----- 1010111'), + # R5('pv.or.sc.b', 'R', '010110- ----- ----- 101 ----- 1010111'), + # R5('pv.or.sci.b', 'RRS', '010110- ----- ----- 111 ----- 1010111'), + + # R5('pv.xor.h', 'R', '011000- ----- ----- 000 ----- 1010111'), + # R5('pv.xor.sc.h', 'R', '011000- ----- ----- 100 ----- 1010111'), + # R5('pv.xor.sci.h', 'RRS', '011000- ----- ----- 110 ----- 1010111'), + # R5('pv.xor.b', 'R', '011000- ----- ----- 001 ----- 1010111'), + # R5('pv.xor.sc.b', 'R', '011000- ----- ----- 101 ----- 1010111'), + # R5('pv.xor.sci.b', 'RRS', '011000- ----- ----- 111 ----- 1010111'), + + # R5('pv.and.h', 'R', '011010- ----- ----- 000 ----- 1010111'), + # R5('pv.and.sc.h', 'R', '011010- ----- ----- 100 ----- 1010111'), + # R5('pv.and.sci.h', 'RRS', '011010- ----- ----- 110 ----- 1010111'), + # R5('pv.and.b', 'R', '011010- ----- ----- 001 ----- 1010111'), + # R5('pv.and.sc.b', 'R', '011010- ----- ----- 101 ----- 1010111'), + # R5('pv.and.sci.b', 'RRS', '011010- ----- ----- 111 ----- 1010111'), + + # R5('pv.abs.h', 'R1', '0111000 ----- ----- 000 ----- 1010111'), + # R5('pv.abs.b', 'R1', '0111000 ----- ----- 001 ----- 1010111'), + + # R5('pv.extract.h', 'RRU', '011110- ----- ----- 110 ----- 1010111'), + # R5('pv.extract.b', 'RRU', '011110- ----- ----- 111 ----- 1010111'), + # R5('pv.extractu.h', 'RRU', '100100- ----- ----- 110 ----- 1010111'), + # R5('pv.extractu.b', 'RRU', '100100- ----- ----- 111 ----- 1010111'), + + # R5('pv.insert.h', 'RRRU','101100- ----- ----- 110 ----- 1010111'), + # R5('pv.insert.b', 'RRRU','101100- ----- ----- 111 ----- 1010111'), + + # R5('pv.dotsp.h', 'R', '100110- ----- ----- 000 ----- 1010111'), + # R5('pv.dotsp.h.sc', 'R', '100110- ----- ----- 100 ----- 1010111'), + # R5('pv.dotsp.h.sci', 'RRS', '100110- ----- ----- 110 ----- 1010111'), + + # R5('pv.dotsp.b', 'R', '100110- ----- ----- 001 ----- 1010111'), + # R5('pv.dotsp.b.sc', 'R', '100110- ----- ----- 101 ----- 1010111'), + # R5('pv.dotsp.b.sci', 'RRS', '100110- ----- ----- 111 ----- 1010111'), + + # R5('pv.dotup.h', 'R', '100000- ----- ----- 000 ----- 1010111'), + # R5('pv.dotup.h.sc', 'R', '100000- ----- ----- 100 ----- 1010111'), + # R5('pv.dotup.h.sci', 'RRU', '100000- ----- ----- 110 ----- 1010111'), + + # R5('pv.dotup.b', 'R', '100000- ----- ----- 001 ----- 1010111'), + # R5('pv.dotup.b.sc', 'R', '100000- ----- ----- 101 ----- 1010111'), + # R5('pv.dotup.b.sci', 'RRU', '100000- ----- ----- 111 ----- 1010111'), + + # R5('pv.dotusp.h', 'R', '100010- ----- ----- 000 ----- 1010111'), + # R5('pv.dotusp.h.sc', 'R', '100010- ----- ----- 100 ----- 1010111'), + # R5('pv.dotusp.h.sci', 'RRS', '100010- ----- ----- 110 ----- 1010111'), + + # R5('pv.dotusp.b', 'R', '100010- ----- ----- 001 ----- 1010111'), + # R5('pv.dotusp.b.sc', 'R', '100010- ----- ----- 101 ----- 1010111'), + # R5('pv.dotusp.b.sci', 'RRS', '100010- ----- ----- 111 ----- 1010111'), + + + # R5('pv.sdotsp.h', 'RRRR','101110- ----- ----- 000 ----- 1010111'), + # R5('pv.sdotsp.h.sc', 'RRRR','101110- ----- ----- 100 ----- 1010111'), + # R5('pv.sdotsp.h.sci', 'RRRS','101110- ----- ----- 110 ----- 1010111'), + + # R5('pv.sdotsp.b', 'RRRR','101110- ----- ----- 001 ----- 1010111'), + # R5('pv.sdotsp.b.sc', 'RRRR','101110- ----- ----- 101 ----- 1010111'), + # R5('pv.sdotsp.b.sci', 'RRRS','101110- ----- ----- 111 ----- 1010111'), + + # R5('pv.sdotup.h', 'RRRR','101000- ----- ----- 000 ----- 1010111'), + # R5('pv.sdotup.h.sc', 'RRRR','101000- ----- ----- 100 ----- 1010111'), + # R5('pv.sdotup.h.sci', 'RRRU','101000- ----- ----- 110 ----- 1010111'), + + # R5('pv.sdotup.b', 'RRRR','101000- ----- ----- 001 ----- 1010111'), + # R5('pv.sdotup.b.sc', 'RRRR','101000- ----- ----- 101 ----- 1010111'), + # R5('pv.sdotup.b.sci', 'RRRU','101000- ----- ----- 111 ----- 1010111'), + + # R5('pv.sdotusp.h', 'RRRR','101010- ----- ----- 000 ----- 1010111'), + # R5('pv.sdotusp.h.sc', 'RRRR','101010- ----- ----- 100 ----- 1010111'), + # R5('pv.sdotusp.h.sci','RRRS','101010- ----- ----- 110 ----- 1010111'), + + # R5('pv.sdotusp.b', 'RRRR','101010- ----- ----- 001 ----- 1010111'), + # R5('pv.sdotusp.b.sc', 'RRRR','101010- ----- ----- 101 ----- 1010111'), + # R5('pv.sdotusp.b.sci','RRRS','101010- ----- ----- 111 ----- 1010111'), + + # R5('pv.shuffle.h', 'R', '110000- ----- ----- 000 ----- 1010111'), + # R5('pv.shuffle.h.sci','RRU', '110000- ----- ----- 110 ----- 1010111'), + + # R5('pv.shuffle.b', 'R', '110000- ----- ----- 001 ----- 1010111'), + # R5('pv.shufflei0.b.sci','RRU2','110000- ----- ----- 111 ----- 1010111'), + # R5('pv.shufflei1.b.sci','RRU2','111010- ----- ----- 111 ----- 1010111'), + # R5('pv.shufflei2.b.sci','RRU2','111100- ----- ----- 111 ----- 1010111'), + # R5('pv.shufflei3.b.sci','RRU2','111110- ----- ----- 111 ----- 1010111'), + + # R5('pv.shuffle2.h', 'RRRR','110010- ----- ----- 000 ----- 1010111'), + # R5('pv.shuffle2.b', 'RRRR','110010- ----- ----- 001 ----- 1010111'), + + # R5('pv.pack.h', 'RRRR','1101000 ----- ----- 000 ----- 1010111'), + # R5('pv.packhi.b', 'RRRR','110110- ----- ----- 001 ----- 1010111'), + # R5('pv.packlo.b', 'RRRR','111000- ----- ----- 001 ----- 1010111'), + + # R5('pv.cmpeq.h', 'R', '000001- ----- ----- 000 ----- 1010111'), + # R5('pv.cmpeq.sc.h', 'R', '000001- ----- ----- 100 ----- 1010111'), + # R5('pv.cmpeq.sci.h', 'RRS', '000001- ----- ----- 110 ----- 1010111'), + # R5('pv.cmpeq.b', 'R', '000001- ----- ----- 001 ----- 1010111'), + # R5('pv.cmpeq.sc.b', 'R', '000001- ----- ----- 101 ----- 1010111'), + # R5('pv.cmpeq.sci.b', 'RRS', '000001- ----- ----- 111 ----- 1010111'), + + # R5('pv.cmpne.h', 'R', '000011- ----- ----- 000 ----- 1010111'), + # R5('pv.cmpne.sc.h', 'R', '000011- ----- ----- 100 ----- 1010111'), + # R5('pv.cmpne.sci.h', 'RRS', '000011- ----- ----- 110 ----- 1010111'), + # R5('pv.cmpne.b', 'R', '000011- ----- ----- 001 ----- 1010111'), + # R5('pv.cmpne.sc.b', 'R', '000011- ----- ----- 101 ----- 1010111'), + # R5('pv.cmpne.sci.b', 'RRS', '000011- ----- ----- 111 ----- 1010111'), + + # R5('pv.cmpgt.h', 'R', '000101- ----- ----- 000 ----- 1010111'), + # R5('pv.cmpgt.sc.h', 'R', '000101- ----- ----- 100 ----- 1010111'), + # R5('pv.cmpgt.sci.h', 'RRS', '000101- ----- ----- 110 ----- 1010111'), + # R5('pv.cmpgt.b', 'R', '000101- ----- ----- 001 ----- 1010111'), + # R5('pv.cmpgt.sc.b', 'R', '000101- ----- ----- 101 ----- 1010111'), + # R5('pv.cmpgt.sci.b', 'RRS', '000101- ----- ----- 111 ----- 1010111'), + + # R5('pv.cmpge.h', 'R', '000111- ----- ----- 000 ----- 1010111'), + # R5('pv.cmpge.sc.h', 'R', '000111- ----- ----- 100 ----- 1010111'), + # R5('pv.cmpge.sci.h', 'RRS', '000111- ----- ----- 110 ----- 1010111'), + # R5('pv.cmpge.b', 'R', '000111- ----- ----- 001 ----- 1010111'), + # R5('pv.cmpge.sc.b', 'R', '000111- ----- ----- 101 ----- 1010111'), + # R5('pv.cmpge.sci.b', 'RRS', '000111- ----- ----- 111 ----- 1010111'), + + # R5('pv.cmplt.h', 'R', '001001- ----- ----- 000 ----- 1010111'), + # R5('pv.cmplt.sc.h', 'R', '001001- ----- ----- 100 ----- 1010111'), + # R5('pv.cmplt.sci.h', 'RRS', '001001- ----- ----- 110 ----- 1010111'), + # R5('pv.cmplt.b', 'R', '001001- ----- ----- 001 ----- 1010111'), + # R5('pv.cmplt.sc.b', 'R', '001001- ----- ----- 101 ----- 1010111'), + # R5('pv.cmplt.sci.b', 'RRS', '001001- ----- ----- 111 ----- 1010111'), + + # R5('pv.cmple.h', 'R', '001011- ----- ----- 000 ----- 1010111'), + # R5('pv.cmple.sc.h', 'R', '001011- ----- ----- 100 ----- 1010111'), + # R5('pv.cmple.sci.h', 'RRS', '001011- ----- ----- 110 ----- 1010111'), + # R5('pv.cmple.b', 'R', '001011- ----- ----- 001 ----- 1010111'), + # R5('pv.cmple.sc.b', 'R', '001011- ----- ----- 101 ----- 1010111'), + # R5('pv.cmple.sci.b', 'RRS', '001011- ----- ----- 111 ----- 1010111'), + + # R5('pv.cmpgtu.h', 'R', '001101- ----- ----- 000 ----- 1010111'), + # R5('pv.cmpgtu.sc.h', 'R', '001101- ----- ----- 100 ----- 1010111'), + # R5('pv.cmpgtu.sci.h', 'RRU', '001101- ----- ----- 110 ----- 1010111'), + # R5('pv.cmpgtu.b', 'R', '001101- ----- ----- 001 ----- 1010111'), + # R5('pv.cmpgtu.sc.b', 'R', '001101- ----- ----- 101 ----- 1010111'), + # R5('pv.cmpgtu.sci.b', 'RRU', '001101- ----- ----- 111 ----- 1010111'), + + # R5('pv.cmpgeu.h', 'R', '001111- ----- ----- 000 ----- 1010111'), + # R5('pv.cmpgeu.sc.h', 'R', '001111- ----- ----- 100 ----- 1010111'), + # R5('pv.cmpgeu.sci.h', 'RRU', '001111- ----- ----- 110 ----- 1010111'), + # R5('pv.cmpgeu.b', 'R', '001111- ----- ----- 001 ----- 1010111'), + # R5('pv.cmpgeu.sc.b', 'R', '001111- ----- ----- 101 ----- 1010111'), + # R5('pv.cmpgeu.sci.b', 'RRU', '001111- ----- ----- 111 ----- 1010111'), + + # R5('pv.cmpltu.h', 'R', '010001- ----- ----- 000 ----- 1010111'), + # R5('pv.cmpltu.sc.h', 'R', '010001- ----- ----- 100 ----- 1010111'), + # R5('pv.cmpltu.sci.h', 'RRU', '010001- ----- ----- 110 ----- 1010111'), + # R5('pv.cmpltu.b', 'R', '010001- ----- ----- 001 ----- 1010111'), + # R5('pv.cmpltu.sc.b', 'R', '010001- ----- ----- 101 ----- 1010111'), + # R5('pv.cmpltu.sci.b', 'RRU', '010001- ----- ----- 111 ----- 1010111'), + + # R5('pv.cmpleu.h', 'R', '010011- ----- ----- 000 ----- 1010111'), + # R5('pv.cmpleu.sc.h', 'R', '010011- ----- ----- 100 ----- 1010111'), + # R5('pv.cmpleu.sci.h', 'RRU', '010011- ----- ----- 110 ----- 1010111'), + # R5('pv.cmpleu.b', 'R', '010011- ----- ----- 001 ----- 1010111'), + # R5('pv.cmpleu.sc.b', 'R', '010011- ----- ----- 101 ----- 1010111'), + # R5('pv.cmpleu.sci.b', 'RRU', '010011- ----- ----- 111 ----- 1010111'), + +] + rnnext = [ R5('pl.sdotsp.h.0', 'LRRRR','101110- ----- ----- 000 ----- 1110111'), R5('pl.sdotsp.h.1', 'LRRRR','101111- ----- ----- 000 ----- 1110111'), @@ -1722,7 +2092,7 @@ def __init__(self, label, format, encoding, decode=None, N=None, L=None, mapTo=N R5('pl.sig', 'R1', '1111100 00000 ----- 001 ----- 1110111'), ] - +corev = IsaSubset('corev', corev_insns) pulp_v2 = IsaSubset('pulpv2', pulp_v2_insns + pulp_common_insns) pulp_v2_rnnext = IsaSubset('rnnext', rnnext) @@ -1877,6 +2247,7 @@ def __init__(self, label, format, encoding, decode=None, N=None, L=None, mapTo=N IsaDecodeTree('m', [rv32m]), IsaDecodeTree('c', [rv32c]), IsaDecodeTree('priv', [priv]), + IsaDecodeTree('corev', [pulp_v2]), IsaDecodeTree('pulp_v2', [pulp_v2]), IsaDecodeTree('rnnext', [pulp_v2_rnnext]), IsaDecodeTree('f', [rv32f]), @@ -1889,7 +2260,7 @@ def __init__(self, label, format, encoding, decode=None, N=None, L=None, mapTo=N #IsaTree('priv_pulp_v2', priv_pulp_v2), #IsaTree('priv_1_9', priv_1_9) #IsaTree('rv32a', rv32a), - #IsaTree('pulp_zeroriscy', pulp_zeroriscy), + #IsaTree('pulp_zeroriscy', pulp_zeroriscy), ] ) From 6f65bf314f8468477fc912ac1d8e61b4d8868723 Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Tue, 16 Feb 2021 19:25:36 +0100 Subject: [PATCH 04/22] Add corev instructions description --- .../models/cpu/iss/include/cv32e40p.hpp | 1638 +++++++++++++++++ .../common/models/cpu/iss/include/iss.hpp | 1 + .../models/cpu/iss/isa_gen/isa_riscv_gen.py | 4 +- 3 files changed, 1641 insertions(+), 2 deletions(-) create mode 100644 tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp diff --git a/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp b/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp new file mode 100644 index 00000000..9c027fd8 --- /dev/null +++ b/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp @@ -0,0 +1,1638 @@ +/* + * Copyright (C) 2020 GreenWaves Technologies, SAS, ETH Zurich and + * University of Bologna + * + * 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. + */ + +/* + * Authors: Nazareno Bruschi, UniBo + * Germain Haugou, GreenWaves Technologies (germain.haugou@greenwaves-technologies.com) + */ + +#ifndef __CPU_ISS_COREV_HPP +#define __CPU_ISS_COREV_HPP + +// static inline iss_insn_t *LB_RR_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load_signed(iss, insn, REG_GET(0) + REG_GET(1), 1, REG_OUT(0)); +// return insn->next; +// } + +// static inline iss_insn_t *LB_RR_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0) + REG_GET(1)); +// iss_lsu_check_stack_access(iss, REG_IN(1), REG_GET(0) + REG_GET(1)); +// iss_lsu_load_signed_perf(iss, insn, REG_GET(0) + REG_GET(1), 1, REG_OUT(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *LH_RR_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load_signed(iss, insn, REG_GET(0) + REG_GET(1), 2, REG_OUT(0)); +// return insn->next; +// } + +// static inline iss_insn_t *LH_RR_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0) + REG_GET(1)); +// iss_lsu_check_stack_access(iss, REG_IN(1), REG_GET(0) + REG_GET(1)); +// iss_lsu_load_signed_perf(iss, insn, REG_GET(0) + REG_GET(1), 2, REG_OUT(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *LW_RR_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load(iss, insn, REG_GET(0) + REG_GET(1), 4, REG_OUT(0)); +// return insn->next; +// } + +// static inline iss_insn_t *LW_RR_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0) + REG_GET(1)); +// iss_lsu_check_stack_access(iss, REG_IN(1), REG_GET(0) + REG_GET(1)); +// iss_lsu_load_perf(iss, insn, REG_GET(0) + REG_GET(1), 4, REG_OUT(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *LBU_RR_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load(iss, insn, REG_GET(0) + REG_GET(1), 1, REG_OUT(0)); +// return insn->next; +// } + +// static inline iss_insn_t *LBU_RR_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0) + REG_GET(1)); +// iss_lsu_check_stack_access(iss, REG_IN(1), REG_GET(0) + REG_GET(1)); +// iss_lsu_load_perf(iss, insn, REG_GET(0) + REG_GET(1), 1, REG_OUT(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *LHU_RR_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load(iss, insn, REG_GET(0) + REG_GET(1), 2, REG_OUT(0)); +// return insn->next; +// } + +// static inline iss_insn_t *LHU_RR_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0) + REG_GET(1)); +// iss_lsu_check_stack_access(iss, REG_IN(1), REG_GET(0) + REG_GET(1)); +// iss_lsu_load_perf(iss, insn, REG_GET(0) + REG_GET(1), 2, REG_OUT(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *LB_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load_signed(iss, insn, REG_GET(0), 1, REG_OUT(0)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + +// static inline iss_insn_t *LB_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load_signed_perf(iss, insn, REG_GET(0), 1, REG_OUT(0)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *LH_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load_signed(iss, insn, REG_GET(0), 2, REG_OUT(0)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + +// static inline iss_insn_t *LH_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load_signed_perf(iss, insn, REG_GET(0), 2, REG_OUT(0)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *LW_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load_signed(iss, insn, REG_GET(0), 4, REG_OUT(0)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + +// static inline iss_insn_t *LW_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load_signed_perf(iss, insn, REG_GET(0), 4, REG_OUT(0)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *LBU_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load(iss, insn, REG_GET(0), 1, REG_OUT(0)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + +// static inline iss_insn_t *LBU_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0)); +// iss_lsu_load_perf(iss, insn, REG_GET(0), 1, REG_OUT(0)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + + + + +// static inline iss_insn_t *LHU_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_load(iss, insn, REG_GET(0), 2, REG_OUT(0)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + +// static inline iss_insn_t *LHU_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0)); +// iss_lsu_load_perf(iss, insn, REG_GET(0), 2, REG_OUT(0)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *SB_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_store(iss, insn, REG_GET(0), 1, REG_IN(1)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + +// static inline iss_insn_t *SB_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0)); +// iss_lsu_store_perf(iss, insn, REG_GET(0), 1, REG_IN(1)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *SH_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_store(iss, insn, REG_GET(0), 2, REG_IN(1)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + +// static inline iss_insn_t *SH_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0)); +// iss_lsu_store_perf(iss, insn, REG_GET(0), 2, REG_IN(1)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *SW_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_store(iss, insn, REG_GET(0), 4, REG_IN(1)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + +// static inline iss_insn_t *SW_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0)); +// iss_lsu_store_perf(iss, insn, REG_GET(0), 4, REG_IN(1)); +// IN_REG_SET(0, REG_GET(0) + SIM_GET(0)); +// return insn->next; +// } + + + +// static inline iss_insn_t *LB_RR_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(1); +// iss_lsu_load_signed(iss, insn, REG_GET(0), 1, REG_OUT(0)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + +// static inline iss_insn_t *LB_RR_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(1); +// iss_lsu_load_signed_perf(iss, insn, REG_GET(0), 1, REG_OUT(0)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + + + +// static inline iss_insn_t *LH_RR_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(1); +// iss_lsu_load_signed(iss, insn, REG_GET(0), 2, REG_OUT(0)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + +// static inline iss_insn_t *LH_RR_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(1); +// iss_lsu_load_signed_perf(iss, insn, REG_GET(0), 2, REG_OUT(0)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + + + +// static inline iss_insn_t *LW_RR_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(1); +// iss_lsu_load_signed(iss, insn, REG_GET(0), 4, REG_OUT(0)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + +// static inline iss_insn_t *LW_RR_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(1); +// iss_lsu_load_signed_perf(iss, insn, REG_GET(0), 4, REG_OUT(0)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + + + +// static inline iss_insn_t *LBU_RR_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(1); +// iss_lsu_load(iss, insn, REG_GET(0), 1, REG_OUT(0)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + +// static inline iss_insn_t *LBU_RR_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(1); +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0)); +// iss_lsu_load_perf(iss, insn, REG_GET(0), 1, REG_OUT(0)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + + + +// static inline iss_insn_t *LHU_RR_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(1); +// iss_lsu_load(iss, insn, REG_GET(0), 2, REG_OUT(0)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + +// static inline iss_insn_t *LHU_RR_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(1); +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0)); +// iss_lsu_load_perf(iss, insn, REG_GET(0), 2, REG_OUT(0)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + + + +// static inline iss_insn_t *SB_RR_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(2); +// iss_lsu_store(iss, insn, REG_GET(0), 1, REG_IN(1)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + +// static inline iss_insn_t *SB_RR_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(2); +// iss_lsu_check_stack_access(iss, REG_OUT(0), REG_GET(0)); +// iss_lsu_store_perf(iss, insn, REG_GET(0), 1, REG_IN(1)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + + + +// static inline iss_insn_t *SH_RR_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(2); +// iss_lsu_store(iss, insn, REG_GET(0), 2, REG_IN(1)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + +// static inline iss_insn_t *SH_RR_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(2); +// iss_lsu_check_stack_access(iss, REG_OUT(0), REG_GET(0)); +// iss_lsu_store_perf(iss, insn, REG_GET(0), 2, REG_IN(1)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + + + +// static inline iss_insn_t *SW_RR_POSTINC_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(2); +// iss_lsu_store(iss, insn, REG_GET(0), 4, REG_IN(1)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + +// static inline iss_insn_t *SW_RR_POSTINC_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_reg_t new_val = REG_GET(0) + REG_GET(2); +// iss_lsu_check_stack_access(iss, REG_OUT(0), REG_GET(0)); +// iss_lsu_store_perf(iss, insn, REG_GET(0), 4, REG_IN(1)); +// IN_REG_SET(0, new_val); +// return insn->next; +// } + + + +static inline iss_insn_t *cv_avgu_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_AVGU, REG_GET(0), REG_GET(1))); + //setRegDelayed(cpu, pc->outReg[0], value, 2); + return insn->next; +} + + + +static inline iss_insn_t *cv_slet_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, (int32_t)REG_GET(0) <= (int32_t)REG_GET(1)); + return insn->next; +} + + + +static inline iss_insn_t *cv_sletu_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, REG_GET(0) <= REG_GET(1)); + return insn->next; +} + + + +static inline iss_insn_t *cv_min_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_MINS, REG_GET(0), REG_GET(1))); + //setRegDelayed(cpu, pc->outReg[0], value, 2); + return insn->next; +} + + + +static inline iss_insn_t *cv_minu_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_MINU, REG_GET(0), REG_GET(1))); + //setRegDelayed(cpu, pc->outReg[0], value, 2); + return insn->next; +} + + + +static inline iss_insn_t *cv_max_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_MAXS, REG_GET(0), REG_GET(1))); + //setRegDelayed(cpu, pc->outReg[0], value, 2); + return insn->next; +} + + + +static inline iss_insn_t *cv_maxu_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_MAXU, REG_GET(0), REG_GET(1))); + //setRegDelayed(cpu, pc->outReg[0], value, 2); + return insn->next; +} + + + +static inline iss_insn_t *cv_ror_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_ROR, REG_GET(0), REG_GET(1))); + //setRegDelayed(cpu, pc->outReg[0], value, 2); + return insn->next; +} + + + +static inline iss_insn_t *cv_ff1_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL1(lib_FF1, REG_GET(0))); + //setRegDelayed(cpu, pc->outReg[0], value, 2); + return insn->next; +} + + + +static inline iss_insn_t *cv_fl1_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL1(lib_FL1, REG_GET(0))); + //setRegDelayed(cpu, pc->outReg[0], value, 2); + return insn->next; +} + + + +static inline iss_insn_t *cv_clb_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL1(lib_CLB, REG_GET(0))); + //setRegDelayed(cpu, pc->outReg[0], value, 2); + return insn->next; +} + + + +static inline iss_insn_t *cv_cnt_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL1(lib_CNT, REG_GET(0))); + //setRegDelayed(cpu, pc->outReg[0], value, 2); + return insn->next; +} + + + +static inline iss_insn_t *cv_exths_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, iss_get_signed_value(REG_GET(0), 16)); + return insn->next; +} + + + +static inline iss_insn_t *cv_exthz_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, iss_get_field(REG_GET(0), 0, 16)); + return insn->next; +} + + + +static inline iss_insn_t *cv_extbs_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, iss_get_signed_value(REG_GET(0), 8)); + return insn->next; +} + + + +static inline iss_insn_t *cv_extbz_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, iss_get_field(REG_GET(0), 0, 8)); + return insn->next; +} + + +static inline iss_insn_t *cv_starti_exec(iss_t *iss, iss_insn_t *insn) +{ + hwloop_set_start(iss, insn, UIM_GET(0), insn->addr + (UIM_GET(1) << 1)); + return insn->next; +} + + + +static inline iss_insn_t *cv_endi_exec(iss_t *iss, iss_insn_t *insn) +{ + hwloop_set_end(iss, insn, UIM_GET(0), insn->addr + (UIM_GET(1) << 1)); + return insn->next; +} + + + +static inline iss_insn_t *cv_count_exec(iss_t *iss, iss_insn_t *insn) +{ + hwloop_set_count(iss, insn, UIM_GET(0), REG_GET(0)); + return insn->next; +} + + + +static inline iss_insn_t *cv_counti_exec(iss_t *iss, iss_insn_t *insn) +{ + hwloop_set_count(iss, insn, UIM_GET(0), UIM_GET(1)); + return insn->next; +} + + + +static inline iss_insn_t *cv_setup_exec(iss_t *iss, iss_insn_t *insn) +{ + int index = UIM_GET(0); + iss_reg_t count = REG_GET(0); + iss_reg_t start = insn->addr + insn->size; + iss_reg_t end = insn->addr + (UIM_GET(1) << 1); + + hwloop_set_all(iss, insn, index, start, end, count); + + return insn->next; +} + + +static inline iss_insn_t *cv_setupi_exec(iss_t *iss, iss_insn_t *insn) +{ + int index = UIM_GET(0); + iss_reg_t count = UIM_GET(1); + iss_reg_t start = insn->addr + insn->size; + iss_reg_t end = insn->addr + (UIM_GET(2) << 1); + + hwloop_set_all(iss, insn, index, start, end, count); + + return insn->next; +} + + + + + + + + + + + +static inline iss_insn_t *cv_abs_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL1(lib_ABS, REG_GET(0))); + return insn->next; +} + + + +// static inline iss_insn_t *SB_RR_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_store(iss, insn, REG_GET(0) + REG_GET(2), 1, REG_IN(1)); +// return insn->next; +// } + +// static inline iss_insn_t *SB_RR_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0) + REG_GET(2)); +// iss_lsu_check_stack_access(iss, REG_IN(2), REG_GET(0) + REG_GET(2)); +// iss_lsu_store_perf(iss, insn, REG_GET(0) + REG_GET(2), 1, REG_IN(1)); +// return insn->next; +// } + + + +// static inline iss_insn_t *SH_RR_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_store(iss, insn, REG_GET(0) + REG_GET(2), 2, REG_IN(1)); +// return insn->next; +// } + +// static inline iss_insn_t *SH_RR_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0) + REG_GET(2)); +// iss_lsu_check_stack_access(iss, REG_IN(2), REG_GET(0) + REG_GET(2)); +// iss_lsu_store_perf(iss, insn, REG_GET(0) + REG_GET(2), 2, REG_IN(1)); +// return insn->next; +// } + + + +// static inline iss_insn_t *SW_RR_exec_fast(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_store(iss, insn, REG_GET(0) + REG_GET(2), 4, REG_IN(1)); +// return insn->next; +// } + +// static inline iss_insn_t *SW_RR_exec(iss_t *iss, iss_insn_t *insn) +// { +// iss_lsu_check_stack_access(iss, REG_IN(0), REG_GET(0) + REG_GET(2)); +// iss_lsu_check_stack_access(iss, REG_IN(2), REG_GET(0) + REG_GET(2)); +// iss_lsu_store_perf(iss, insn, REG_GET(0) + REG_GET(2), 4, REG_IN(1)); +// return insn->next; +// } + + + +static inline iss_insn_t *cv_elw_exec(iss_t *iss, iss_insn_t *insn) +{ + uint32_t value = 0; + iss->cpu.state.elw_insn = insn; + iss_lsu_elw_perf(iss, insn, REG_GET(0) + SIM_GET(0), 4, REG_OUT(0)); + if (iss->cpu.state.insn_cycles != -1) + iss->cpu.state.elw_insn = NULL; + return insn->next; +} + + +#define CV_OP_RS_EXEC(insn_name,lib_name) \ +static inline iss_insn_t *cv_##insn_name##_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_int16_t_to_int32_t, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_sc_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_int16_t_to_int32_t, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_sci_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_int16_t_to_int32_t, REG_GET(0), SIM_GET(0))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_int8_t_to_int32_t, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_sc_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_int8_t_to_int32_t, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_sci_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_int8_t_to_int32_t, REG_GET(0), SIM_GET(0))); \ + return insn->next; \ +} + + + +#define CV_OP_RU_EXEC(insn_name,lib_name) \ +static inline iss_insn_t *cv_##insn_name##_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_uint16_t_to_uint32_t, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_sc_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_uint16_t_to_uint32_t, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_sci_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_uint16_t_to_uint32_t, REG_GET(0), UIM_GET(0))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_uint8_t_to_uint32_t, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_sc_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_uint8_t_to_uint32_t, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_sci_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_uint8_t_to_uint32_t, REG_GET(0), UIM_GET(0))); \ + return insn->next; \ +} + + +#define CV_OP_RS_EXEC2(insn_name,lib_name) \ +static inline iss_insn_t *cv_##insn_name##_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_16, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_h_sc_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_16, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_h_sci_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_16, REG_GET(0), SIM_GET(0))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_8, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_sc_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_8, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_sci_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_8, REG_GET(0), SIM_GET(0))); \ + return insn->next; \ +} + + + +#define CV_OP_RU_EXEC2(insn_name,lib_name) \ +static inline iss_insn_t *cv_##insn_name##_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_16, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_h_sc_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_16, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_h_sci_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_16, REG_GET(0), UIM_GET(0))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_8, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_sc_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_8, REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_sci_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL2(lib_VEC_##lib_name##_SC_8, REG_GET(0), UIM_GET(0))); \ + return insn->next; \ +} + + +#define CV_OP_RRS_EXEC2(insn_name,lib_name) \ +static inline iss_insn_t *cv_##insn_name##_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_16, REG_GET(2), REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_h_sc_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_SC_16, REG_GET(2), REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_h_sci_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_SC_16, REG_GET(0), REG_GET(1), SIM_GET(0))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_8, REG_GET(2), REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_sc_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_SC_8, REG_GET(2), REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_sci_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_SC_8, REG_GET(0), REG_GET(1), SIM_GET(0))); \ + return insn->next; \ +} + + + +#define CV_OP_RRU_EXEC2(insn_name,lib_name) \ +static inline iss_insn_t *cv_##insn_name##_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_16, REG_GET(2), REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_h_sc_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_SC_16, REG_GET(2), REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_h_sci_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_SC_16, REG_GET(0), REG_GET(1), UIM_GET(0))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_8, REG_GET(2), REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_sc_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_SC_8, REG_GET(2), REG_GET(0), REG_GET(1))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_sci_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL3(lib_VEC_##lib_name##_SC_8, REG_GET(0), REG_GET(1), UIM_GET(0))); \ + return insn->next; \ +} + + + + +#define CV_OP1_RS_EXEC(insn_name,lib_name) \ +static inline iss_insn_t *cv_##insn_name##_h_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL1(lib_VEC_##lib_name##_int16_t_to_int32_t, REG_GET(0))); \ + return insn->next; \ +} \ + \ +static inline iss_insn_t *cv_##insn_name##_b_exec(iss_t *iss, iss_insn_t *insn) \ +{ \ + REG_SET(0, LIB_CALL1(lib_VEC_##lib_name##_int8_t_to_int32_t, REG_GET(0))); \ + return insn->next; \ +} + + + +CV_OP_RS_EXEC(add,ADD) + +CV_OP_RS_EXEC(sub,SUB) + +CV_OP_RS_EXEC(avg,AVG) + +CV_OP_RU_EXEC(avgu,AVGU) + +CV_OP_RS_EXEC(min,MIN) + +CV_OP_RU_EXEC(minu,MINU) + +CV_OP_RS_EXEC(max,MAX) + +CV_OP_RU_EXEC(maxu,MAXU) + +CV_OP_RU_EXEC(srl,SRL) + +CV_OP_RS_EXEC(sra,SRA) + +CV_OP_RU_EXEC(sll,SLL) + +CV_OP_RS_EXEC(or,OR) + +CV_OP_RS_EXEC(xor,XOR) + +CV_OP_RS_EXEC(and,AND) + +CV_OP1_RS_EXEC(abs,ABS) + + + +// static inline iss_insn_t *cv_extract_h_exec(iss_t *iss, iss_insn_t *insn) +// { +// REG_SET(0, LIB_CALL2(lib_VEC_EXT_16, REG_GET(0), UIM_GET(0))); +// return insn->next; +// } + + + +// static inline iss_insn_t *cv_extract_b_exec(iss_t *iss, iss_insn_t *insn) +// { +// REG_SET(0, LIB_CALL2(lib_VEC_EXT_8, REG_GET(0), UIM_GET(0))); +// return insn->next; +// } + + + +// static inline iss_insn_t *cv_extractu_h_exec(iss_t *iss, iss_insn_t *insn) +// { +// REG_SET(0, LIB_CALL2(lib_VEC_EXTU_16, REG_GET(0), UIM_GET(0))); +// return insn->next; +// } + + + +// static inline iss_insn_t *cv_extractu_b_exec(iss_t *iss, iss_insn_t *insn) +// { +// REG_SET(0, LIB_CALL2(lib_VEC_EXTU_8, REG_GET(0), UIM_GET(0))); +// return insn->next; +// } + + + +// static inline iss_insn_t *cv_insert_h_exec(iss_t *iss, iss_insn_t *insn) +// { +// REG_SET(0, LIB_CALL3(lib_VEC_INS_16, REG_GET(0), REG_GET(1), UIM_GET(0))); +// return insn->next; +// } + + + +// static inline iss_insn_t *cv_insert_b_exec(iss_t *iss, iss_insn_t *insn) +// { +// REG_SET(0, LIB_CALL3(lib_VEC_INS_8, REG_GET(0), REG_GET(1), UIM_GET(0))); +// return insn->next; +// } + + +static inline iss_insn_t *cv_extractr_h_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_EXT_16, REG_GET(0), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_extractr_b_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_EXT_8, REG_GET(0), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_extractur_h_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_EXTU_16, REG_GET(0), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_extractur_b_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_EXTU_8, REG_GET(0), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_insertr_h_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_VEC_INS_16, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_insertr_b_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_VEC_INS_8, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + + +CV_OP_RS_EXEC2(dotsp,DOTSP) + +CV_OP_RU_EXEC2(dotup,DOTUP) + +CV_OP_RS_EXEC2(dotusp,DOTUSP) + +CV_OP_RRS_EXEC2(sdotsp,SDOTSP) + +CV_OP_RRU_EXEC2(sdotup,SDOTUP) + +CV_OP_RRS_EXEC2(sdotusp,SDOTUSP) + + + +static inline iss_insn_t *cv_shuffle_h_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_SHUFFLE_16, REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_shuffle_h_sci_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_SHUFFLE_SCI_16, REG_GET(0), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_shuffle_b_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_SHUFFLE_8, REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_shufflei0_b_sci_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_SHUFFLEI0_SCI_8, REG_GET(0), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_shufflei1_b_sci_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_SHUFFLEI1_SCI_8, REG_GET(0), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_shufflei2_b_sci_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_SHUFFLEI2_SCI_8, REG_GET(0), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_shufflei3_b_sci_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_SHUFFLEI3_SCI_8, REG_GET(0), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_shuffle2_h_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_VEC_SHUFFLE2_16, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + +static inline iss_insn_t *cv_shuffle2_b_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_VEC_SHUFFLE2_8, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + + + +static inline iss_insn_t *cv_pack_h_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_VEC_PACK_SC_16, REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_packhi_b_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_VEC_PACKHI_SC_8, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + +static inline iss_insn_t *cv_packlo_b_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_VEC_PACKLO_SC_8, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + + + +CV_OP_RS_EXEC(cmpeq,CMPEQ) + +CV_OP_RS_EXEC(cmpne,CMPNE) + +CV_OP_RS_EXEC(cmpgt,CMPGT) + +CV_OP_RS_EXEC(cmpge,CMPGE) + +CV_OP_RS_EXEC(cmplt,CMPLT) + +CV_OP_RS_EXEC(cmple,CMPLE) + +CV_OP_RU_EXEC(cmpgtu,CMPGTU) + +CV_OP_RU_EXEC(cmpgeu,CMPGEU) + +CV_OP_RU_EXEC(cmpltu,CMPLTU) + +CV_OP_RU_EXEC(cmpleu,CMPLEU) + + + +static inline iss_insn_t *cv_bneimm_exec_common(iss_t *iss, iss_insn_t *insn, int perf) +{ + if ((int32_t)REG_GET(0) != SIM_GET(1)) + { + if (perf) + { + iss_pccr_account_event(iss, CSR_PCER_BRANCH, 1); + iss_pccr_account_event(iss, CSR_PCER_TAKEN_BRANCH, 1); + } + iss_perf_account_taken_branch(iss); + return insn->branch; + } + else + { + if (perf) + { + iss_pccr_account_event(iss, CSR_PCER_BRANCH, 1); + } + return insn->next; + } +} + +static inline iss_insn_t *cv_bneimm_exec_fast(iss_t *iss, iss_insn_t *insn) +{ + return cv_bneimm_exec_common(iss, insn, 0); +} + +static inline iss_insn_t *cv_bneimm_exec(iss_t *iss, iss_insn_t *insn) +{ + return cv_bneimm_exec_common(iss, insn, 1); +} + + + +static inline iss_insn_t *cv_beqimm_exec_common(iss_t *iss, iss_insn_t *insn, int perf) +{ + if ((int32_t)REG_GET(0) == SIM_GET(1)) + { + if (perf) + { + iss_pccr_account_event(iss, CSR_PCER_BRANCH, 1); + iss_pccr_account_event(iss, CSR_PCER_TAKEN_BRANCH, 1); + } + iss_perf_account_taken_branch(iss); + return insn->branch; + } + else + { + if (perf) + { + iss_pccr_account_event(iss, CSR_PCER_BRANCH, 1); + } + return insn->next; + } +} + +static inline iss_insn_t *cv_beqimm_exec_fast(iss_t *iss, iss_insn_t *insn) +{ + return cv_beqimm_exec_common(iss, insn, 0); +} + +static inline iss_insn_t *cv_beqimm_exec(iss_t *iss, iss_insn_t *insn) +{ + return cv_beqimm_exec_common(iss, insn, 1); +} + + + + +static inline iss_insn_t *cv_mac_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MAC, REG_GET(2), REG_GET(0), REG_GET(1))); + return insn->next; +} + + + +static inline iss_insn_t *cv_msu_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MSU, REG_GET(2), REG_GET(0), REG_GET(1))); + return insn->next; +} + + + +static inline iss_insn_t *cv_mul_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_MULS, REG_GET(0), REG_GET(1))); + return insn->next; +} + + + +static inline iss_insn_t *cv_muls_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_MUL_SL_SL, REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_mulhhs_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_MUL_SH_SH, REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_mulsN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MUL_SL_SL_NR, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_mulhhsN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MUL_SH_SH_NR, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_mulsRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MUL_SL_SL_NR_R, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_mulhhsRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MUL_SH_SH_NR_R, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_mulu_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_MUL_ZL_ZL, REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_mulhhu_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL2(lib_MUL_ZH_ZH, REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_muluN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MUL_ZL_ZL_NR, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_mulhhuN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MUL_ZH_ZH_NR, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_muluRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MUL_ZL_ZL_NR_R, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_mulhhuRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MUL_ZH_ZH_NR_R, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_macs_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MAC_SL_SL, REG_GET(2), REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_machhs_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MAC_SH_SH, REG_GET(2), REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_macsN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL4(lib_MAC_SL_SL_NR, REG_GET(2), REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_machhsN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL4(lib_MAC_SH_SH_NR, REG_GET(2), REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_macsRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL4(lib_MAC_SL_SL_NR_R, REG_GET(2), REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_machhsRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL4(lib_MAC_SH_SH_NR_R, REG_GET(2), REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_macu_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MAC_ZL_ZL, REG_GET(2), REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_machhu_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_MAC_ZH_ZH, REG_GET(2), REG_GET(0), REG_GET(1))); + return insn->next; +} + +static inline iss_insn_t *cv_macuN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL4(lib_MAC_ZL_ZL_NR, REG_GET(2), REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_machhuN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL4(lib_MAC_ZH_ZH_NR, REG_GET(2), REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_macuRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL4(lib_MAC_ZL_ZL_NR_R, REG_GET(2), REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_machhuRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL4(lib_MAC_ZH_ZH_NR_R, REG_GET(2), REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_addN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_ADD_NR, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_adduN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_ADD_NRU, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_addRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_ADD_NR_R, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_adduRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_ADD_NR_RU, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_subN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_SUB_NR, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_subuN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_SUB_NRU, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_subRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_SUB_NR_R, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + +static inline iss_insn_t *cv_subuRN_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_SUB_NR_RU, REG_GET(0), REG_GET(1), UIM_GET(0))); + return insn->next; +} + + + +static inline iss_insn_t *cv_addNr_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_ADD_NR, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + +static inline iss_insn_t *cv_adduNr_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_ADD_NRU, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + +static inline iss_insn_t *cv_addRNr_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_ADD_NR_R, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + +static inline iss_insn_t *cv_adduRNr_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_ADD_NR_RU, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + + + +static inline iss_insn_t *cv_subNr_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_SUB_NR, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + +static inline iss_insn_t *cv_subuNr_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_SUB_NRU, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + +static inline iss_insn_t *cv_subRNr_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_SUB_NR_R, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + +static inline iss_insn_t *cv_subuRNr_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_SUB_NR_RU, REG_GET(0), REG_GET(1), REG_GET(2))); + return insn->next; +} + + + +static inline iss_insn_t *cv_clip_exec(iss_t *iss, iss_insn_t *insn) +{ + int low = (int) -(1 << MAX((int)UIM_GET(0)-1, 0)); + int high = (1 << MAX((int)UIM_GET(0)-1, 0)) - 1 ; + REG_SET(0, LIB_CALL3(lib_CLIP, REG_GET(0), low, high)); + return insn->next; +} + +static inline iss_insn_t *cv_clipu_exec(iss_t *iss, iss_insn_t *insn) +{ + int low = 0; + int high = (1 << MAX((int)UIM_GET(0)-1, 0)) - 1; + REG_SET(0, LIB_CALL3(lib_CLIP, REG_GET(0), low, high)); + return insn->next; +} + +static inline iss_insn_t *cv_clipr_exec(iss_t *iss, iss_insn_t *insn) +{ + int low = -REG_GET(1) - 1; + int high = REG_GET(1); + REG_SET(0, LIB_CALL3(lib_CLIP, REG_GET(0), low, high)); + return insn->next; +} + +static inline iss_insn_t *cv_clipur_exec(iss_t *iss, iss_insn_t *insn) +{ + int low = 0; + int high = REG_GET(1); + REG_SET(0, LIB_CALL3(lib_CLIP, REG_GET(0), low, high)); + return insn->next; +} + + + +static inline iss_insn_t *cv_bclr_exec(iss_t *iss, iss_insn_t *insn) +{ + int width = UIM_GET(0) + 1; + int shift = UIM_GET(1); + REG_SET(0, LIB_CALL2(lib_BCLR, REG_GET(0), ((1ULL<next; +} + + + +static inline iss_insn_t *cv_bclrr_exec(iss_t *iss, iss_insn_t *insn) +{ + int width = ((REG_GET(1) >> 5) & 0x1f) + 1; + int shift = REG_GET(1) & 0x1f; + REG_SET(0, LIB_CALL2(lib_BCLR, REG_GET(0), ((1ULL<next; +} + + +static inline iss_insn_t *cv_extract_exec(iss_t *iss, iss_insn_t *insn) +{ + int width = UIM_GET(0) + 1; + int shift = UIM_GET(1); + REG_SET(0, LIB_CALL3(lib_BEXTRACT, REG_GET(0), width, shift)); + return insn->next; +} + + + +static inline iss_insn_t *cv_extractu_exec(iss_t *iss, iss_insn_t *insn) +{ + int width = UIM_GET(0) + 1; + int shift = UIM_GET(1); + REG_SET(0, LIB_CALL3(lib_BEXTRACTU, REG_GET(0), ((1ULL << width) - 1) << shift, shift)); + return insn->next; +} + + + +static inline iss_insn_t *cv_extractr_exec(iss_t *iss, iss_insn_t *insn) +{ + int width = ((REG_GET(1) >> 5) & 0x1f) + 1; + int shift = REG_GET(1) & 0x1f; + REG_SET(0, LIB_CALL3(lib_BEXTRACT, REG_GET(0), width, shift)); + return insn->next; +} + + + +static inline iss_insn_t *cv_extractur_exec(iss_t *iss, iss_insn_t *insn) +{ + int width = ((REG_GET(1) >> 5) & 0x1f) + 1; + int shift = REG_GET(1) & 0x1f; + REG_SET(0, LIB_CALL3(lib_BEXTRACTU, REG_GET(0), ((1ULL << width) - 1) << shift, shift)); + return insn->next; +} + + + +static inline iss_insn_t *cv_insert_exec(iss_t *iss, iss_insn_t *insn) +{ + int width = UIM_GET(0) + 1; + int shift = UIM_GET(1); + REG_SET(0, LIB_CALL4(lib_BINSERT, REG_GET(0), REG_GET(1), ((1ULL << width) - 1) << shift, shift)); + return insn->next; +} + + + +static inline iss_insn_t *cv_insertr_exec(iss_t *iss, iss_insn_t *insn) +{ + int width = ((REG_GET(2) >> 5) & 0x1F) + 1; + int shift = REG_GET(2) & 0x1F; + REG_SET(0, LIB_CALL4(lib_BINSERT, REG_GET(0), REG_GET(1), ((1ULL << width) - 1) << shift, shift)); + return insn->next; +} + + + +static inline iss_insn_t *cv_bset_exec(iss_t *iss, iss_insn_t *insn) +{ + int width = UIM_GET(0) + 1; + int shift = UIM_GET(1); + REG_SET(0, LIB_CALL2(lib_BSET, REG_GET(0), ((1ULL << (width)) - 1) << shift)); + return insn->next; +} + + + +static inline iss_insn_t *cv_bsetr_exec(iss_t *iss, iss_insn_t *insn) +{ + int width = ((REG_GET(1) >> 5) & 0x1f) + 1; + int shift = REG_GET(1) & 0x1f; + REG_SET(0, LIB_CALL2(lib_BSET, REG_GET(0), ((1ULL << (width)) - 1) << shift)); + return insn->next; +} + + +static inline iss_insn_t *cv_bitrev_exec(iss_t *iss, iss_insn_t *insn) +{ + REG_SET(0, LIB_CALL3(lib_BITREV, REG_GET(0), UIM_GET(0), UIM_GET(1)+1)); + return insn->next; +} + +#endif diff --git a/tools/gvsoc/common/models/cpu/iss/include/iss.hpp b/tools/gvsoc/common/models/cpu/iss/include/iss.hpp index 2dd5c7ca..cf735351 100644 --- a/tools/gvsoc/common/models/cpu/iss/include/iss.hpp +++ b/tools/gvsoc/common/models/cpu/iss/include/iss.hpp @@ -34,6 +34,7 @@ #include "rv32Xfaux.hpp" #include "priv.hpp" #include "pulp_v2.hpp" +#include "cv32e40p.hpp" #include "rvXgap8.hpp" #include "rvXgap9.hpp" #include "rvXint64.hpp" diff --git a/tools/gvsoc/common/models/cpu/iss/isa_gen/isa_riscv_gen.py b/tools/gvsoc/common/models/cpu/iss/isa_gen/isa_riscv_gen.py index 3d8eac3d..10d03e83 100755 --- a/tools/gvsoc/common/models/cpu/iss/isa_gen/isa_riscv_gen.py +++ b/tools/gvsoc/common/models/cpu/iss/isa_gen/isa_riscv_gen.py @@ -1767,7 +1767,7 @@ def __init__(self, label, format, encoding, decode=None, N=None, L=None, mapTo=N R5('cv.insertr', 'I5U2', '1000000 ----- ----- 010 ----- 0110011'), R5('cv.bclrr', 'R', '1000000 ----- ----- 011 ----- 0110011'), R5('cv.bsetr', 'R', '1000000 ----- ----- 100 ----- 0110011'), - R5('cv.bitrev', 'BITREV', '11000-- ----- ----- 101 ----- 0110011', mapTo="gap9_BITREV"),#preso da gap9 + R5('cv.bitrev', 'BITREV', '11000-- ----- ----- 101 ----- 0110011', mapTo="cv_bitrev"),#preso da gap9 R5('cv.ror', 'R', '0000100 ----- ----- 101 ----- 0110011'), R5('cv.ff1', 'R1', '0001000 00000 ----- 000 ----- 0110011'), @@ -2247,7 +2247,7 @@ def __init__(self, label, format, encoding, decode=None, N=None, L=None, mapTo=N IsaDecodeTree('m', [rv32m]), IsaDecodeTree('c', [rv32c]), IsaDecodeTree('priv', [priv]), - IsaDecodeTree('corev', [pulp_v2]), + IsaDecodeTree('corev', [corev]), IsaDecodeTree('pulp_v2', [pulp_v2]), IsaDecodeTree('rnnext', [pulp_v2_rnnext]), IsaDecodeTree('f', [rv32f]), From b997a2a317be80c353cf3fb6032960ae4dc6b474 Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Wed, 17 Feb 2021 11:40:57 +0100 Subject: [PATCH 05/22] Add gcc, as, objdump and linker names --- rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk b/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk index 58472079..a183e55e 100644 --- a/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk +++ b/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk @@ -9,10 +9,10 @@ PULP_CFLAGS += -fdata-sections -ffunction-sections -include pos/chips/pulp/co PULP_OMP_CFLAGS += -fopenmp -mnativeomp PULP_LDFLAGS += -nostartfiles -nostdlib -Wl,--gc-sections -L$(PULPOS_PULP_HOME)/kernel -Tchips/pulp/link.ld -lgcc -PULP_CC = riscv32-unknown-elf-gcc -PULP_AR ?= riscv32-unknown-elf-ar -PULP_LD ?= riscv32-unknown-elf-gcc -PULP_OBJDUMP ?= riscv32-unknown-elf-objdump +PULP_CC = riscv32-corev-elf-gcc +PULP_AR ?= riscv32-corev-elf-ar +PULP_LD ?= riscv32-corev-elf-gcc +PULP_OBJDUMP ?= riscv32-corev-elf-objdump fc/archi=riscv pe/archi=riscv From 72c739fd13015bc64f8a46767665e841412e7eb3 Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Thu, 4 Mar 2021 16:06:03 +0100 Subject: [PATCH 06/22] Wrap pulp specific builtins on emulating library --- .../pulpos/common/include/pos/implem/implem.h | 6 +- rtos/pulpos/common/include/pos/implem/irq.h | 8 +- rtos/pulpos/common/kernel/soc_event_eu.S | 8 ++ rtos/pulpos/common/kernel/soc_event_v2_itc.S | 21 +++- rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S | 33 ++++++ .../pulp/include/pos/chips/pulp/config.h | 1 - .../rules/pulpos/targets/pulp_cv32e40p.mk | 6 +- .../pulp_archi/include/archi/gap_utils.h | 16 ++- rtos/pulpos/pulp_archi/include/archi/utils.h | 11 +- .../pulp_hal/include/hal/dma/mchan_v6.h | 4 +- .../pulp_hal/include/hal/dma/mchan_v7.h | 4 +- rtos/pulpos/pulp_hal/include/hal/pulp_io.h | 14 ++- .../pulp_hal/include/hal/riscv/riscv_v5.h | 2 +- .../models/cpu/iss/include/cv32e40p.hpp | 103 +++++++++++++++++- .../common/models/cpu/iss/include/types.hpp | 10 ++ tools/gvsoc/common/models/cpu/iss/src/iss.cpp | 5 + 16 files changed, 216 insertions(+), 36 deletions(-) diff --git a/rtos/pulpos/common/include/pos/implem/implem.h b/rtos/pulpos/common/include/pos/implem/implem.h index a56baf71..8de1fc21 100644 --- a/rtos/pulpos/common/include/pos/implem/implem.h +++ b/rtos/pulpos/common/include/pos/implem/implem.h @@ -52,15 +52,15 @@ static inline uint32_t pi_cluster_id() static inline int pi_cl_cluster_nb_cores() { #ifdef ARCHI_HAS_CC - return __builtin_pulp_CoreCount() + 1; + return __NCORE() + 1; #else - return __builtin_pulp_CoreCount(); + return __NCORE(); #endif } static inline uint32_t pi_cl_cluster_nb_pe_cores() { - return __builtin_pulp_CoreCount(); + return __NCORE(); } static inline uint32_t pi_is_fc() diff --git a/rtos/pulpos/common/include/pos/implem/irq.h b/rtos/pulpos/common/include/pos/implem/irq.h index db7d8131..80bde867 100644 --- a/rtos/pulpos/common/include/pos/implem/irq.h +++ b/rtos/pulpos/common/include/pos/implem/irq.h @@ -115,9 +115,9 @@ static inline unsigned int pos_irq_get_fc_vector_base() #if defined(__RISCV_GENERIC__) return hal_spr_read(0x305) & ~1; #elif defined(ARCHI_CORE_HAS_SECURITY) && !defined(ARCHI_CORE_HAS_1_10) - return __builtin_pulp_spr_read(SR_MTVEC); + return __SPRREAD(SR_MTVEC); #elif defined(ARCHI_CORE_HAS_1_10) - return __builtin_pulp_spr_read(SR_MTVEC) & ~1; + return __SPRREAD(SR_MTVEC) & ~1; #elif defined(APB_SOC_VERSION) && APB_SOC_VERSION >= 2 return apb_soc_ctrl_fc_boot_get(ARCHI_APB_SOC_CTRL_ADDR); #endif @@ -143,9 +143,9 @@ static inline void pos_irq_set_fc_vector_base(unsigned int base) #if defined(__RISCV_GENERIC__) hal_spr_write(0x305, base); #elif defined(ARCHI_CORE_HAS_SECURITY) && !defined(ARCHI_CORE_HAS_1_10) - __builtin_pulp_spr_write(SR_MTVEC, base); + __SPRWRITE(SR_MTVEC, base); #elif defined(ARCHI_CORE_HAS_1_10) - __builtin_pulp_spr_write(SR_MTVEC, base | 1); + __SPRWRITE(SR_MTVEC, base | 1); #elif defined(APB_SOC_VERSION) && APB_SOC_VERSION >= 2 apb_soc_ctrl_fc_boot_set(ARCHI_APB_SOC_CTRL_ADDR, base); #endif diff --git a/rtos/pulpos/common/kernel/soc_event_eu.S b/rtos/pulpos/common/kernel/soc_event_eu.S index c994f87d..39b8517c 100644 --- a/rtos/pulpos/common/kernel/soc_event_eu.S +++ b/rtos/pulpos/common/kernel/soc_event_eu.S @@ -59,7 +59,11 @@ pos_soc_event_handler_asm: sw x9, 0(x10) # Extract ID part + #ifdef ARCHI_HAS_COREV + cv.extractur x10, x8, EU_SOC_EVENTS_EVENT_MASK_BITS-1, EU_SOC_EVENTS_EVENT_MASK_OFFSET + #else p.extractu x10, x8, EU_SOC_EVENTS_EVENT_MASK_BITS-1, EU_SOC_EVENTS_EVENT_MASK_OFFSET + #endif @@ -135,7 +139,11 @@ pos_soc_event_store_asm: lw x12, %tiny(pos_soc_event_status)(x11) andi x10, x10, 0x1f + #ifdef ARCHI_HAS_COREV + cv.bsetr x12, x12, x10 + #else p.bsetr x12, x12, x10 + #endif sw x12, %tiny(pos_soc_event_status)(x11) diff --git a/rtos/pulpos/common/kernel/soc_event_v2_itc.S b/rtos/pulpos/common/kernel/soc_event_v2_itc.S index 36ec4daf..544a6c78 100644 --- a/rtos/pulpos/common/kernel/soc_event_v2_itc.S +++ b/rtos/pulpos/common/kernel/soc_event_v2_itc.S @@ -73,9 +73,17 @@ lw x11, %tiny(pos_soc_event_callback_arg)(x11) #else la t0, pos_soc_event_callback +#ifdef ARCHI_HAS_COREV + cv.lw x12, t0(x11) +#else p.lw x12, t0(x11) +#endif /*ARCHI_HAS_COREV*/ la t0, pos_soc_event_callback_arg +#ifdef ARCHI_HAS_COREV + cv.lw x11, t0(x11) +#else p.lw x11, t0(x11) +#endif /*ARCHI_HAS_COREV*/ #endif la x9, pos_soc_event_handler_end_asm j pos_irq_call_external_c_function @@ -100,17 +108,28 @@ pos_soc_event_store_asm: lw x12, %tiny(pos_soc_event_status)(x11) #else la t0, pos_soc_event_status +#ifdef ARCHI_HAS_COREV + cv.lw x12, t0(x11) +#else p.lw x12, t0(x11) +#endif /*ARCHI_HAS_COREV*/ #endif andi x10, x10, 0x1f - +#ifdef ARCHI_HAS_COREV + cv.bsetr x12, x12, x10 +#else p.bsetr x12, x12, x10 +#endif /*ARCHI_HAS_COREV*/ #ifdef CONFIG_NO_STD_RELOC sw x12, %tiny(pos_soc_event_status)(x11) #else la t0, pos_soc_event_status +#ifdef ARCHI_HAS_COREV + cv.sw x12, t0(x11) +#else p.sw x12, t0(x11) +#endif /*ARCHI_HAS_COREV*/ #endif diff --git a/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S b/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S index 90f3d4ec..271f7994 100644 --- a/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S +++ b/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S @@ -142,7 +142,11 @@ pos_master_no_slave_barrier: // Set stack on slaves // For that we push first the function for setting stack, then the stack size and the base +#ifdef ARCHI_HAS_COREV + cv.beqimm t5, 0, pos_master_loop_no_slave +#else p.beqimm t5, 0, pos_master_loop_no_slave +#endif sw s10, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s3) sw t2, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s3) sw sp, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s3) @@ -155,7 +159,11 @@ pos_master_loop_no_slave: pos_master_sleep: sw s4, EU_CORE_MASK_OR(s3) +#ifdef ARCHI_HAS_COREV + cv.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) +#else p.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) +#endif sw s4, EU_CORE_MASK_AND(s3) j pos_master_loop @@ -163,7 +171,11 @@ pos_master_sleep: pos_push_event_to_fc_wait: sw s4, EU_CORE_MASK_OR(s3) +#ifdef ARCHI_HAS_COREV + cv.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) +#else p.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) +#endif sw s4, EU_CORE_MASK_AND(s3) j pos_push_event_to_fc_retry @@ -210,16 +222,29 @@ pos_fork_return: #ifdef ARCHI_HAS_CC // When the cluster has a controller barrier 0 is used for normal team barrier // and barrier 1 is used for end of offload +#ifdef ARCHI_HAS_COREV + cv.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR + EU_BARRIER_SIZE(s2) +#else p.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR + EU_BARRIER_SIZE(s2) +#endif /*ARCHI_HAS_COREV*/ +#else +#ifdef ARCHI_HAS_COREV + cv.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR(s2) #else p.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR(s2) +#endif /*ARCHI_HAS_COREV*/ #endif pos_wait_for_dispatch: // Wait for PC + arg information from dispatcher +#ifdef ARCHI_HAS_COREV + cv.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) + cv.elw a0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) +#else p.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) p.elw a0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) +#endif // Check if this is an entry with a barrier at the end (fork entry) andi t1, t0, 1 @@ -243,7 +268,11 @@ pos_other_entry: pos_set_slave_stack: // Multiply the stack size by the core ID and add the stack base to get our stack +#ifdef ARCHI_HAS_COREV + cv.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) +#else p.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) +#endif #if defined(CONFIG_PULP) addi t5, s3, 0 #else @@ -251,7 +280,11 @@ pos_set_slave_stack: // and thus we need to take the next stack addi t5, s3, 1 #endif +#ifdef ARCHI_HAS_COREV + cv.mul t4, t5, a0 +#else p.mul t4, t5, a0 +#endif add sp, t4, t0 ret diff --git a/rtos/pulpos/pulp/include/pos/chips/pulp/config.h b/rtos/pulpos/pulp/include/pos/chips/pulp/config.h index 892a42ab..2b473db0 100644 --- a/rtos/pulpos/pulp/include/pos/chips/pulp/config.h +++ b/rtos/pulpos/pulp/include/pos/chips/pulp/config.h @@ -29,7 +29,6 @@ #define CONFIG_PULP 1 #define PULP_CHIP_STR pulp #define PULP_CHIP_FAMILY_STR pulp -#define ARCHI_CORE_HAS_PULPV2 1 #define ARCHI_CORE_HAS_1_10 1 diff --git a/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk b/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk index a183e55e..08c3d799 100644 --- a/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk +++ b/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk @@ -1,9 +1,9 @@ CONFIG_NB_CLUSTER_PE ?= 8 PULP_LDFLAGS += -PULP_CFLAGS += -D__riscv__ -PULP_ARCH_CFLAGS ?= -march=rv32imcxcorev -mPE=$(CONFIG_NB_CLUSTER_PE) -mFC=1 -PULP_ARCH_LDFLAGS ?= -march=rv32imcxcorev -mPE=$(CONFIG_NB_CLUSTER_PE) -mFC=1 +PULP_CFLAGS += -D__riscv__ -DARCHI_HAS_COREV -DPLP_NO_BUILTIN +PULP_ARCH_CFLAGS ?= -march=rv32imc_xcorev +PULP_ARCH_LDFLAGS ?= -march=rv32imc_xcorev PULP_ARCH_OBJDFLAGS ?= -Mmarch=rv32imcxcorev PULP_CFLAGS += -fdata-sections -ffunction-sections -include pos/chips/pulp/config.h -I$(PULPOS_PULP_HOME)/include/pos/chips/pulp PULP_OMP_CFLAGS += -fopenmp -mnativeomp diff --git a/rtos/pulpos/pulp_archi/include/archi/gap_utils.h b/rtos/pulpos/pulp_archi/include/archi/gap_utils.h index 2f6fa6c3..4826c4fe 100644 --- a/rtos/pulpos/pulp_archi/include/archi/gap_utils.h +++ b/rtos/pulpos/pulp_archi/include/archi/gap_utils.h @@ -22,18 +22,22 @@ static inline unsigned int __attribute__ ((always_inline)) ExtInsMaskFast_archi( static inline unsigned int __attribute__ ((always_inline)) ExtInsMaskSafe_archi(unsigned int Size, unsigned int Offset) { return ((((Size-1)&0x1F)<<5)|(Offset&0x1F)); } #if defined(__riscv__) && !defined(__LLVM__) && !defined(RV_ISA_RV32) +#if !defined(PLP_NO_BUILTIN) #define GAP_WRITE_VOL(base, offset, value) __builtin_pulp_write_base_off_v((value), (base), (offset)) -#define GAP_WRITE(base, offset, value) __builtin_pulp_OffsetedWrite((value), (int *)(base), (offset)) -#define GAP_READ(base, offset) __builtin_pulp_OffsetedRead((int *)(base), (offset)) +#else +#define GAP_WRITE_VOL(base, offset, value) archi_write32((base) + (offset), (value)) +#endif +#define GAP_WRITE(base, offset, value) __WRITE_BASE_OFF_VOL((value), (int *)(base), (offset)) +#define GAP_READ(base, offset) __READ_BASE_OFF_VOL((int *)(base), (offset)) #else #define GAP_WRITE_VOL(base, offset, value) archi_write32((base) + (offset), (value)) #define GAP_WRITE(base, offset, value) archi_write32((base) + (offset), (value)) #define GAP_READ(base, offset) archi_read32((base) + (offset)) #endif -#define GAP_BINSERT(dst,src,size,off) __builtin_pulp_binsert((dst), ~(((1UL<<(size))-1)<<(off)), (src), (((1UL<<(size))-1)<<(off)), (off)) -#define GAP_BINSERT_R(dst,src,size,off) __builtin_pulp_binsert_r((dst), (src), ExtInsMaskFast_archi((size), (off))) -#define GAP_BEXTRACTU(src,size,off) __builtin_pulp_bextractu((src), (size), (off)) -#define GAP_BEXTRACT(src,size,off) __builtin_pulp_bextract((src), (size), (off)) +#define GAP_BINSERT(dst,src,size,off) __BITINSERT(dst, src, size, off) +#define GAP_BINSERT_R(dst,src,size,off) __BITINSERT_R(dst, src, size, off) +#define GAP_BEXTRACTU(src,size,off) __BITEXTRACTU(src, size, off) +#define GAP_BEXTRACT(src,size,off) __BITEXTRACT(src, size, off) #endif \ No newline at end of file diff --git a/rtos/pulpos/pulp_archi/include/archi/utils.h b/rtos/pulpos/pulp_archi/include/archi/utils.h index 46af84ab..0a0ee2f8 100644 --- a/rtos/pulpos/pulp_archi/include/archi/utils.h +++ b/rtos/pulpos/pulp_archi/include/archi/utils.h @@ -51,16 +51,19 @@ #if defined(__riscv__) && !defined(__LLVM__) && !defined(RV_ISA_RV32) +#if !defined(PLP_NO_BUILTIN) #define ARCHI_WRITE_VOL(base, offset, value) __builtin_pulp_write_base_off_v((value), (base), (offset)) -#define ARCHI_WRITE(base, offset, value) __builtin_pulp_OffsetedWrite((value), (int *)(base), (offset)) -#define ARCHI_READ(base, offset) __builtin_pulp_OffsetedRead((int *)(base), (offset)) +#else +#define ARCHI_WRITE_VOL(base, offset, value) archi_write32((base) + (offset), (value)) +#endif +#define ARCHI_WRITE(base, offset, value) __WRITE_BASE_OFF_VOL((value), (int *)(base), (offset)) +#define ARCHI_READ(base, offset) __READ_BASE_OFF_VOL((int *)(base), (offset)) #else #define ARCHI_WRITE_VOL(base, offset, value) archi_write32((base) + (offset), (value)) #define ARCHI_WRITE(base, offset, value) archi_write32((base) + (offset), (value)) #define ARCHI_READ(base, offset) archi_read32((base) + (offset)) #endif - #include "archi/riscv/builtins_v2.h" #include "archi/riscv/builtins_v2_emu.h" @@ -71,4 +74,4 @@ #endif -#endif +#endif \ No newline at end of file diff --git a/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v6.h b/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v6.h index a239d426..b030c776 100644 --- a/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v6.h +++ b/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v6.h @@ -249,8 +249,8 @@ static inline unsigned int plp_dma_status(); /// @cond IMPLEM #if defined(__riscv__) && !defined(RV_ISA_RV32) && !defined(__LLVM__) -#define DMA_WRITE(value, offset) __builtin_pulp_OffsetedWrite((value), (int *)ARCHI_DEMUX_PERIPHERALS_ADDR, ARCHI_MCHAN_DEMUX_OFFSET + (offset)) -#define DMA_READ(offset) __builtin_pulp_OffsetedRead((int *)ARCHI_DEMUX_PERIPHERALS_ADDR, ARCHI_MCHAN_DEMUX_OFFSET + (offset)) +#define DMA_WRITE(value, offset) __WRITE_BASE_OFF_VOL((value), (int *)ARCHI_DEMUX_PERIPHERALS_ADDR, ARCHI_MCHAN_DEMUX_OFFSET + (offset)) +#define DMA_READ(offset) __READ_BASE_OFF_VOL((int *)ARCHI_DEMUX_PERIPHERALS_ADDR, ARCHI_MCHAN_DEMUX_OFFSET + (offset)) #else #define DMA_WRITE(value, offset) pulp_write32(ARCHI_DEMUX_PERIPHERALS_ADDR + ARCHI_MCHAN_DEMUX_OFFSET + (offset), (value)) #define DMA_READ(offset) pulp_read32(ARCHI_DEMUX_PERIPHERALS_ADDR + ARCHI_MCHAN_DEMUX_OFFSET + (offset)) diff --git a/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v7.h b/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v7.h index db1e20dc..f8dee791 100644 --- a/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v7.h +++ b/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v7.h @@ -259,8 +259,8 @@ static inline unsigned int plp_dma_status(); /// @cond IMPLEM #if defined(__riscv__) && !defined(RV_ISA_RV32) && !defined(__LLVM__) -#define DMA_WRITE(value, offset) __builtin_pulp_OffsetedWrite((value), (int *)ARCHI_MCHAN_EXT_ADDR, (offset)) -#define DMA_READ(offset) __builtin_pulp_OffsetedRead((int *)ARCHI_MCHAN_EXT_ADDR, (offset)) +#define DMA_WRITE(value, offset) __WRITE_BASE_OFF_VOL((value), (int *)ARCHI_MCHAN_EXT_ADDR, (offset)) +#define DMA_READ(offset) __READ_BASE_OFF_VOL((int *)ARCHI_MCHAN_EXT_ADDR, (offset)) #else #define DMA_WRITE(value, offset) pulp_write32(ARCHI_MCHAN_EXT_ADDR + (offset), (value)) #define DMA_READ(offset) pulp_read32(ARCHI_MCHAN_EXT_ADDR + (offset)) diff --git a/rtos/pulpos/pulp_hal/include/hal/pulp_io.h b/rtos/pulpos/pulp_hal/include/hal/pulp_io.h index ed31bef1..c10bf418 100644 --- a/rtos/pulpos/pulp_hal/include/hal/pulp_io.h +++ b/rtos/pulpos/pulp_hal/include/hal/pulp_io.h @@ -72,21 +72,29 @@ static inline uint32_t pulp_read(uint32_t add) #endif #if defined(__riscv__) && !defined(RV_ISA_RV32) && !defined(__LLVM__) +#if !defined(PLP_NO_BUILTIN) #define IP_WRITE_VOL(base, offset, value) __builtin_pulp_write_base_off_v((value), (base), (offset)) -#define IP_WRITE(base, offset, value) __builtin_pulp_OffsetedWrite((value), (int *)(base), (offset)) +#else +#define IP_WRITE_VOL(base, offset, value) pulp_write32((base) + (offset), (value)) +#endif +#define IP_WRITE(base, offset, value) __WRITE_BASE_OFF_VOL((value), (int *)(base), (offset)) #if !defined(CONFIG_PULP) #define IP_WRITE_PTR(base, offset, value) __builtin_pulp_OffsetedWritePtr((int *)(value), (int *)(base), (offset)) #else #define IP_WRITE_PTR(base, offset, value) do{asm volatile("":::"memory"); \ - __builtin_pulp_OffsetedWrite((value), (int *)(base), (offset)); \ + __WRITE_BASE_OFF_VOL((value), (int *)(base), (offset)); \ asm volatile("":::"memory"); \ }while(0) #endif -#define IP_READ(base, offset) __builtin_pulp_OffsetedRead((int *)(base), (offset)) +#define IP_READ(base, offset) __READ_BASE_OFF_VOL((int *)(base), (offset)) #else #define IP_WRITE_VOL(base, offset, value) pulp_write32((base) + (offset), (value)) #define IP_WRITE(base, offset, value) pulp_write32((base) + (offset), (value)) #define IP_READ(base, offset) pulp_read32((base) + (offset)) +#define IP_WRITE_PTR(base, offset, value) do{asm volatile("":::"memory"); \ + __WRITE_BASE_OFF_VOL((value), (int *)(base), (offset)); \ + asm volatile("":::"memory"); \ + }while(0) #endif #endif diff --git a/rtos/pulpos/pulp_hal/include/hal/riscv/riscv_v5.h b/rtos/pulpos/pulp_hal/include/hal/riscv/riscv_v5.h index 5048b7de..964365b6 100644 --- a/rtos/pulpos/pulp_hal/include/hal/riscv/riscv_v5.h +++ b/rtos/pulpos/pulp_hal/include/hal/riscv/riscv_v5.h @@ -442,7 +442,7 @@ static inline void cpu_stack_check_disable() -#if !defined(RV_ISA_RV32) +#if !defined(RV_ISA_RV32) && !defined(PLP_NO_BUILTIN) /* Packing of scalars into vectors */ #define __builtin_pack2(x, y) __builtin_pulp_pack2((signed short) (x), (signed short) (y)) diff --git a/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp b/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp index 9c027fd8..6166d5fa 100644 --- a/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp +++ b/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp @@ -23,6 +23,18 @@ #ifndef __CPU_ISS_COREV_HPP #define __CPU_ISS_COREV_HPP +#define COREV_HWLOOP_LPSTART0 0 +#define COREV_HWLOOP_LPEND0 1 +#define COREV_HWLOOP_LPCOUNT0 2 + +#define COREV_HWLOOP_LPSTART1 3 +#define COREV_HWLOOP_LPEND1 4 +#define COREV_HWLOOP_LPCOUNT1 5 + +#define COREV_HWLOOP_LPSTART(x) (COREV_HWLOOP_LPSTART0 + (x)*3) +#define COREV_HWLOOP_LPEND(x) (COREV_HWLOOP_LPEND0 + (x)*3) +#define COREV_HWLOOP_LPCOUNT(x) (COREV_HWLOOP_LPCOUNT0 + (x)*3) + // static inline iss_insn_t *LB_RR_exec_fast(iss_t *iss, iss_insn_t *insn) // { // iss_lsu_load_signed(iss, insn, REG_GET(0) + REG_GET(1), 1, REG_OUT(0)); @@ -385,6 +397,71 @@ // } +static inline iss_insn_t *corev_hwloop_check_exec(iss_t *iss, iss_insn_t *insn) +{ + iss_reg_t pc = insn->addr; + + // First execute the instructions as it is the last one of the loop body. + // The real handler has been saved when the loop was started. + iss_insn_t *insn_next = iss_exec_insn_handler(iss, insn, insn->hwloop_handler); + + // First check HW loop 0 as it has higher priority compared to HW loop 1 + if (iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT0] && iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPEND0] == pc) + { + iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT0]--; + iss_decoder_msg(iss, "Reached end of HW loop (index: 0, loop count: %d)\n", iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT0]); + + // If counter is not zero, we must jump back to beginning of the loop. + if (iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT0]) return iss->cpu.state.hwloop_start_insn[0]; + } + + // We get here either if HW loop 0 was not active or if the counter reached 0. + // In both cases, HW loop 1 can jump back to the beginning of the loop. + if (iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT1] && iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPEND1] == pc) + { + iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT1]--; + // If counter is not zero, we must jump back to beginning of the loop. + iss_decoder_msg(iss, "Reached end of HW loop (index: 1, loop count: %d)\n", iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT1]); + if (iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT1]) return iss->cpu.state.hwloop_start_insn[1]; + } + + // In case no HW loop jumped back, just continue with the next instruction. + return insn_next; +} + +static inline void corev_hwloop_set_start(iss_t *iss, iss_insn_t *insn, int index, iss_reg_t start) +{ + iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPSTART(index)] = start; + iss->cpu.state.hwloop_start_insn[index] = insn_cache_get(iss, start); +} + +static inline void corev_hwloop_set_end(iss_t *iss, iss_insn_t *insn, int index, iss_reg_t end) +{ + iss_insn_t *end_insn = insn_cache_get_decoded(iss, end); + + if (end_insn->hwloop_handler == NULL) + { + end_insn->hwloop_handler = end_insn->handler; + end_insn->handler = hwloop_check_exec; + end_insn->fast_handler = hwloop_check_exec; + } + + iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPEND(index)] = end; +} + +static inline void corev_hwloop_set_count(iss_t *iss, iss_insn_t *insn, int index, iss_reg_t count) +{ + iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT(index)] = count; +} + +static inline void corev_hwloop_set_all(iss_t *iss, iss_insn_t *insn, int index, iss_reg_t start, iss_reg_t end, iss_reg_t count) +{ + corev_hwloop_set_end(iss, insn, index, end); + corev_hwloop_set_start(iss, insn, index, start); + corev_hwloop_set_count(iss, insn, index, count); +} + + static inline iss_insn_t *cv_avgu_exec(iss_t *iss, iss_insn_t *insn) { @@ -525,7 +602,7 @@ static inline iss_insn_t *cv_extbz_exec(iss_t *iss, iss_insn_t *insn) static inline iss_insn_t *cv_starti_exec(iss_t *iss, iss_insn_t *insn) { - hwloop_set_start(iss, insn, UIM_GET(0), insn->addr + (UIM_GET(1) << 1)); + corev_hwloop_set_start(iss, insn, UIM_GET(0), insn->addr + (UIM_GET(1) << 1)); return insn->next; } @@ -533,7 +610,7 @@ static inline iss_insn_t *cv_starti_exec(iss_t *iss, iss_insn_t *insn) static inline iss_insn_t *cv_endi_exec(iss_t *iss, iss_insn_t *insn) { - hwloop_set_end(iss, insn, UIM_GET(0), insn->addr + (UIM_GET(1) << 1)); + corev_hwloop_set_end(iss, insn, UIM_GET(0), insn->addr + (UIM_GET(1) << 1)); return insn->next; } @@ -541,7 +618,7 @@ static inline iss_insn_t *cv_endi_exec(iss_t *iss, iss_insn_t *insn) static inline iss_insn_t *cv_count_exec(iss_t *iss, iss_insn_t *insn) { - hwloop_set_count(iss, insn, UIM_GET(0), REG_GET(0)); + corev_hwloop_set_count(iss, insn, UIM_GET(0), REG_GET(0)); return insn->next; } @@ -549,7 +626,7 @@ static inline iss_insn_t *cv_count_exec(iss_t *iss, iss_insn_t *insn) static inline iss_insn_t *cv_counti_exec(iss_t *iss, iss_insn_t *insn) { - hwloop_set_count(iss, insn, UIM_GET(0), UIM_GET(1)); + corev_hwloop_set_count(iss, insn, UIM_GET(0), UIM_GET(1)); return insn->next; } @@ -562,7 +639,7 @@ static inline iss_insn_t *cv_setup_exec(iss_t *iss, iss_insn_t *insn) iss_reg_t start = insn->addr + insn->size; iss_reg_t end = insn->addr + (UIM_GET(1) << 1); - hwloop_set_all(iss, insn, index, start, end, count); + corev_hwloop_set_all(iss, insn, index, start, end, count); return insn->next; } @@ -575,7 +652,7 @@ static inline iss_insn_t *cv_setupi_exec(iss_t *iss, iss_insn_t *insn) iss_reg_t start = insn->addr + insn->size; iss_reg_t end = insn->addr + (UIM_GET(2) << 1); - hwloop_set_all(iss, insn, index, start, end, count); + corev_hwloop_set_all(iss, insn, index, start, end, count); return insn->next; } @@ -1635,4 +1712,18 @@ static inline iss_insn_t *cv_bitrev_exec(iss_t *iss, iss_insn_t *insn) return insn->next; } + +static inline void iss_isa_corev_init(iss_t *iss) +{ + iss->cpu.corev.hwloop = false; +} + +static inline void iss_isa_corev_activate(iss_t *iss) +{ + iss->cpu.corev.hwloop = true; + iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT(0)] = 0; + iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPCOUNT(1)] = 0; + +} + #endif diff --git a/tools/gvsoc/common/models/cpu/iss/include/types.hpp b/tools/gvsoc/common/models/cpu/iss/include/types.hpp index f9c59e50..3ea7607e 100644 --- a/tools/gvsoc/common/models/cpu/iss/include/types.hpp +++ b/tools/gvsoc/common/models/cpu/iss/include/types.hpp @@ -430,6 +430,15 @@ typedef struct iss_pulpv2_s } iss_pulpv2_t; +#define COREV_HWLOOP_NB_REGS 7 + +typedef struct iss_corev_s +{ + bool hwloop; + iss_reg_t hwloop_regs[COREV_HWLOOP_NB_REGS]; +} iss_corev_t; + + typedef struct iss_rnnext_s { iss_insn_t *sdot_insn; @@ -451,6 +460,7 @@ typedef struct iss_cpu_s { iss_irq_t irq; iss_csr_t csr; iss_pulpv2_t pulpv2; + iss_pulpv2_t corev; iss_rnnext_t rnnext; } iss_cpu_t; diff --git a/tools/gvsoc/common/models/cpu/iss/src/iss.cpp b/tools/gvsoc/common/models/cpu/iss/src/iss.cpp index 9155dcd0..3ecb93ce 100644 --- a/tools/gvsoc/common/models/cpu/iss/src/iss.cpp +++ b/tools/gvsoc/common/models/cpu/iss/src/iss.cpp @@ -91,6 +91,10 @@ static int iss_parse_isa(iss_t *iss) { iss_isa_pulpv2_activate(iss); } + if (strcmp(token, "corev") == 0) + { + iss_isa_corev_activate(iss); + } else if (strcmp(token, "gap8") == 0) { iss_isa_pulpv2_activate(iss); @@ -276,6 +280,7 @@ void iss_reset(iss_t *iss, int active) int iss_open(iss_t *iss) { iss_isa_pulpv2_init(iss); + iss_isa_corev_init(iss); if (iss_parse_isa(iss)) return -1; From 7e1a16df307dfe396ad9d3bee9a7bbeee488c98a Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Thu, 4 Mar 2021 18:45:25 +0100 Subject: [PATCH 07/22] Fill builtins macro --- rtos/pulpos/common/kernel/soc_event_v2_itc.S | 5 +- rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S | 33 +- .../include/archi/riscv/builtins_v2_emu.h | 416 +++++++++--------- .../include/hal/riscv/builtins_v2_emu.h | 416 +++++++++--------- tools/gap-configs/.sconsign.dblite | Bin 275701 -> 0 bytes .../configs/config/pulp_cv32e40p.json | 14 +- 6 files changed, 451 insertions(+), 433 deletions(-) delete mode 100644 tools/gap-configs/.sconsign.dblite mode change 100644 => 120000 tools/gap-configs/configs/config/pulp_cv32e40p.json diff --git a/rtos/pulpos/common/kernel/soc_event_v2_itc.S b/rtos/pulpos/common/kernel/soc_event_v2_itc.S index 544a6c78..350fd57b 100644 --- a/rtos/pulpos/common/kernel/soc_event_v2_itc.S +++ b/rtos/pulpos/common/kernel/soc_event_v2_itc.S @@ -116,7 +116,10 @@ pos_soc_event_store_asm: #endif andi x10, x10, 0x1f #ifdef ARCHI_HAS_COREV - cv.bsetr x12, x12, x10 + li x11, 1 + sll x11, x11, x10 + or x12, x12, x11 + #cv.bsetr x12, x12, x10 #rD = rs1 | (((1 << (rs2[9:5]+1)) – 1) << rs2[4:0]) #else p.bsetr x12, x12, x10 #endif /*ARCHI_HAS_COREV*/ diff --git a/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S b/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S index 271f7994..fb946901 100644 --- a/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S +++ b/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S @@ -143,7 +143,8 @@ pos_master_no_slave_barrier: // Set stack on slaves // For that we push first the function for setting stack, then the stack size and the base #ifdef ARCHI_HAS_COREV - cv.beqimm t5, 0, pos_master_loop_no_slave + //cv.beqimm t5, 0, pos_master_loop_no_slave + beqz t5, pos_master_loop_no_slave #else p.beqimm t5, 0, pos_master_loop_no_slave #endif @@ -160,7 +161,9 @@ pos_master_loop_no_slave: pos_master_sleep: sw s4, EU_CORE_MASK_OR(s3) #ifdef ARCHI_HAS_COREV - cv.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) + //cv.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) + lw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) + beqz x0, hal_itc_wait_for_interrupt #else p.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) #endif @@ -172,7 +175,9 @@ pos_master_sleep: pos_push_event_to_fc_wait: sw s4, EU_CORE_MASK_OR(s3) #ifdef ARCHI_HAS_COREV - cv.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) + //cv.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) + lw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) + beqz x0, hal_itc_wait_for_interrupt #else p.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) #endif @@ -223,13 +228,17 @@ pos_fork_return: // When the cluster has a controller barrier 0 is used for normal team barrier // and barrier 1 is used for end of offload #ifdef ARCHI_HAS_COREV - cv.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR + EU_BARRIER_SIZE(s2) + //cv.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR + EU_BARRIER_SIZE(s2) + lw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR + EU_BARRIER_SIZE(s2) + beqz t0, hal_itc_wait_for_interrupt #else p.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR + EU_BARRIER_SIZE(s2) #endif /*ARCHI_HAS_COREV*/ #else #ifdef ARCHI_HAS_COREV - cv.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR(s2) + //cv.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR(s2) + lw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR(s2) + beqz t0, hal_itc_wait_for_interrupt #else p.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR(s2) #endif /*ARCHI_HAS_COREV*/ @@ -239,8 +248,12 @@ pos_wait_for_dispatch: // Wait for PC + arg information from dispatcher #ifdef ARCHI_HAS_COREV - cv.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) - cv.elw a0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) + //cv.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) + //cv.elw a0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) + lw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) + beqz t0, hal_itc_wait_for_interrupt + lw a0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) + beqz a0, hal_itc_wait_for_interrupt #else p.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) p.elw a0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) @@ -269,7 +282,9 @@ pos_set_slave_stack: // Multiply the stack size by the core ID and add the stack base to get our stack #ifdef ARCHI_HAS_COREV - cv.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) + //cv.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) + lw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) + beqz t0, hal_itc_wait_for_interrupt #else p.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) #endif @@ -281,7 +296,7 @@ pos_set_slave_stack: addi t5, s3, 1 #endif #ifdef ARCHI_HAS_COREV - cv.mul t4, t5, a0 + cv.muls t4, t5, a0 #else p.mul t4, t5, a0 #endif diff --git a/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h b/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h index b65aea75..15c97e80 100644 --- a/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h +++ b/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h @@ -47,301 +47,307 @@ typedef void * rt_pointerT; typedef unsigned int rt_pointerT; #endif /* Packing of scalars into vectors */ -#define __PACK2(x, y) ((v2s) {(signed short) (x), (signed short) (y)}) -#define __PACKU2(x, y) ((v2u) {(unsigned short) (x), (unsigned short) (y)}) +#define __PACK2(x, y) ((v2s) {(signed short) (x), (signed short) (y)}) +#define __PACKU2(x, y) ((v2u) {(unsigned short) (x), (unsigned short) (y)}) -#define __PACK4(x, y, z, t) ((v4s) {(signed char) (x), (signed char) (y), (signed char) (z), (signed char) (t)}) -#define __PACKU4(x, y, z, t) ((v4u) {(unsigned char) (x), (unsigned char) (y), (unsigned char) (z), (unsigned char) (t)}) +#define __PACK4(x, y, z, t) ((v4s) {(signed char) (x), (signed char) (y), (signed char) (z), (signed char) (t)}) +#define __PACKU4(x, y, z, t) ((v4u) {(unsigned char) (x), (unsigned char) (y), (unsigned char) (z), (unsigned char) (t)}) /* Max */ #define __MAX(x, y) ((x)>(y)?(x):(y)) -#define __MAX2(x, y) ((v2s) {((signed short)(x)[0]>(signed short)(y)[0])?((signed short)(x)[0]):((signed short)(y)[0]), \ - ((signed short)(x)[1]>(signed short)(y)[1])?((signed short)(x)[1]):((signed short)(y)[1])}) -#define __MAX4(x, y) ((v4s) {((signed char)(x)[0]>(signed char)(y)[0])?(signed char)(x)[0]:(signed char)(y)[0], \ - ((signed char)(x)[1]>(signed char)(y)[1])?(signed char)(x)[1]:(signed char)(y)[1], \ - ((signed char)(x)[2]>(signed char)(y)[2])?(signed char)(x)[2]:(signed char)(y)[2], \ - ((signed char)(x)[3]>(signed char)(y)[3])?(signed char)(x)[3]:(signed char)(y)[3]}) - -#define __MAXU2(x, y) ((v2u) {((unsigned short)(x)[0]>(unsigned short)(y)[0])?(unsigned short)(x)[0]:(unsigned short)(y)[0], \ - ((unsigned short)(x)[1]>(unsigned short)(y)[1])?(unsigned short)(x)[1]:(unsigned short)(y)[1]}) -#define __MAXU4(x, y) ((v4u) {((unsigned char)(x)[0]>(unsigned char)(y)[0])?(unsigned char)(x)[0]:(unsigned char)(y)[0], \ - ((unsigned char)(x)[1]>(unsigned char)(y)[1])?(unsigned char)(x)[1]:(unsigned char)(y)[1], \ - ((unsigned char)(x)[2]>(unsigned char)(y)[2])?(unsigned char)(x)[2]:(unsigned char)(y)[2], \ - ((unsigned char)(x)[3]>(unsigned char)(y)[3])?(unsigned char)(x)[3]:(unsigned char)(y)[3]}) +#define __MAX2(x, y) ((v2s) {((signed short)(x)[0]>(signed short)(y)[0])?((signed short)(x)[0]):((signed short)(y)[0]), \ + ((signed short)(x)[1]>(signed short)(y)[1])?((signed short)(x)[1]):((signed short)(y)[1])}) +#define __MAX4(x, y) ((v4s) {((signed char)(x)[0]>(signed char)(y)[0])?(signed char)(x)[0]:(signed char)(y)[0], \ + ((signed char)(x)[1]>(signed char)(y)[1])?(signed char)(x)[1]:(signed char)(y)[1], \ + ((signed char)(x)[2]>(signed char)(y)[2])?(signed char)(x)[2]:(signed char)(y)[2], \ + ((signed char)(x)[3]>(signed char)(y)[3])?(signed char)(x)[3]:(signed char)(y)[3]}) + +#define __MAXU2(x, y) ((v2u) {((unsigned short)(x)[0]>(unsigned short)(y)[0])?(unsigned short)(x)[0]:(unsigned short)(y)[0], \ + ((unsigned short)(x)[1]>(unsigned short)(y)[1])?(unsigned short)(x)[1]:(unsigned short)(y)[1]}) +#define __MAXU4(x, y) ((v4u) {((unsigned char)(x)[0]>(unsigned char)(y)[0])?(unsigned char)(x)[0]:(unsigned char)(y)[0], \ + ((unsigned char)(x)[1]>(unsigned char)(y)[1])?(unsigned char)(x)[1]:(unsigned char)(y)[1], \ + ((unsigned char)(x)[2]>(unsigned char)(y)[2])?(unsigned char)(x)[2]:(unsigned char)(y)[2], \ + ((unsigned char)(x)[3]>(unsigned char)(y)[3])?(unsigned char)(x)[3]:(unsigned char)(y)[3]}) /* Min */ -#define __MIN2(x, y) ((v2s) {((signed short)(x)[0]<(signed short)(y)[0])?((signed short)(x)[0]):((signed short)(y)[0]), \ - ((signed short)(x)[1]<(signed short)(y)[1])?((signed short)(x)[1]):((signed short)(y)[1])}) -#define __MIN4(x, y) ((v4s) {((signed char)(x)[0]<(signed char)(y)[0])?(signed char)(x)[0]:(signed char)(y)[0], \ - ((signed char)(x)[1]<(signed char)(y)[1])?(signed char)(x)[1]:(signed char)(y)[1], \ - ((signed char)(x)[2]<(signed char)(y)[2])?(signed char)(x)[2]:(signed char)(y)[2], \ - ((signed char)(x)[3]<(signed char)(y)[3])?(signed char)(x)[3]:(signed char)(y)[3]}) - -#define __MINU2(x, y) ((v2u) {((unsigned short)(x)[0]<(unsigned short)(y)[0])?(unsigned short)(x)[0]:(unsigned short)(y)[0], \ - ((unsigned short)(x)[1]<(unsigned short)(y)[1])?(unsigned short)(x)[1]:(unsigned short)(y)[1]}) -#define __MINU4(x, y) ((v4u) {((unsigned char)(x)[0]<(unsigned char)(y)[0])?(unsigned char)(x)[0]:(unsigned char)(y)[0], \ - ((unsigned char)(x)[1]<(unsigned char)(y)[1])?(unsigned char)(x)[1]:(unsigned char)(y)[1], \ - ((unsigned char)(x)[2]<(unsigned char)(y)[2])?(unsigned char)(x)[2]:(unsigned char)(y)[2], \ - ((unsigned char)(x)[3]<(unsigned char)(y)[3])?(unsigned char)(x)[3]:(unsigned char)(y)[3]}) +#define __MIN2(x, y) ((v2s) {((signed short)(x)[0]<(signed short)(y)[0])?((signed short)(x)[0]):((signed short)(y)[0]), \ + ((signed short)(x)[1]<(signed short)(y)[1])?((signed short)(x)[1]):((signed short)(y)[1])}) +#define __MIN4(x, y) ((v4s) {((signed char)(x)[0]<(signed char)(y)[0])?(signed char)(x)[0]:(signed char)(y)[0], \ + ((signed char)(x)[1]<(signed char)(y)[1])?(signed char)(x)[1]:(signed char)(y)[1], \ + ((signed char)(x)[2]<(signed char)(y)[2])?(signed char)(x)[2]:(signed char)(y)[2], \ + ((signed char)(x)[3]<(signed char)(y)[3])?(signed char)(x)[3]:(signed char)(y)[3]}) + +#define __MINU2(x, y) ((v2u) {((unsigned short)(x)[0]<(unsigned short)(y)[0])?(unsigned short)(x)[0]:(unsigned short)(y)[0], \ + ((unsigned short)(x)[1]<(unsigned short)(y)[1])?(unsigned short)(x)[1]:(unsigned short)(y)[1]}) +#define __MINU4(x, y) ((v4u) {((unsigned char)(x)[0]<(unsigned char)(y)[0])?(unsigned char)(x)[0]:(unsigned char)(y)[0], \ + ((unsigned char)(x)[1]<(unsigned char)(y)[1])?(unsigned char)(x)[1]:(unsigned char)(y)[1], \ + ((unsigned char)(x)[2]<(unsigned char)(y)[2])?(unsigned char)(x)[2]:(unsigned char)(y)[2], \ + ((unsigned char)(x)[3]<(unsigned char)(y)[3])?(unsigned char)(x)[3]:(unsigned char)(y)[3]}) /* Clip */ -#define __CLIP(x, precision) ((x)<(-(1<<(precision)))?(-(1<<(precision))):(((x)>((1<<(precision))-1))?((1<<(precision))-1):(x))) -#define __CLIP_R(x, bound) (((x)<=-((bound)+1))?(-((bound)+1)):(((x)>=(bound))?(bound):(x))) +#define __CLIP(x, precision) ((x)<(-(1<<(precision)))?(-(1<<(precision))):(((x)>((1<<(precision))-1))?((1<<(precision))-1):(x))) +#define __CLIP_R(x, bound) (((x)<=-((bound)+1))?(-((bound)+1)):(((x)>=(bound))?(bound):(x))) -#define __CLIPU(x, precision) ((x)<0)?0:(((x)>((1<<(precision))-1))?((1<<(precision))-1):(x)) -#define __CLIPU_R(x, bound) (((x)<=0)?(0):(((x)>=(bound))?(bound):(x))) +#define __CLIPU(x, precision) ((x)<0)?0:(((x)>((1<<(precision))-1))?((1<<(precision))-1):(x)) +#define __CLIPU_R(x, bound) (((x)<=0)?(0):(((x)>=(bound))?(bound):(x))) /* Abs */ -#define __ABS2(x) ((v2u) {((x)[0]<0)?-(x)[0]:(x)[0], ((x)[1]<0)?-(x)[1]:(x)[1]}) -#define __ABS4(x) ((v4u) {((x)[0]<0)?-(x)[0]:(x)[0], ((x)[1]<0)?-(x)[1]:(x)[1], \ - ((x)[2]<0)?-(x)[2]:(x)[2], ((x)[3]<0)?-(x)[3]:(x)[3]}) +#define __ABS2(x) ((v2u) {((x)[0]<0)?-(x)[0]:(x)[0], ((x)[1]<0)?-(x)[1]:(x)[1]}) +#define __ABS4(x) ((v4u) {((x)[0]<0)?-(x)[0]:(x)[0], ((x)[1]<0)?-(x)[1]:(x)[1], \ + ((x)[2]<0)?-(x)[2]:(x)[2], ((x)[3]<0)?-(x)[3]:(x)[3]}) /* Unary minus */ -#define __NEG2(x) ((v2s) {-(x)[0], -(x)[1]} -#define __NEG4(x) ((v4s) {-(x)[0], -(x)[1], -(x)[2], -(x)[3]} +#define __NEG2(x) ((v2s) {-(x)[0], -(x)[1]} +#define __NEG4(x) ((v4s) {-(x)[0], -(x)[1], -(x)[2], -(x)[3]} /* Addition */ -#define __ADD2(x, y) ((v2s) {(x)[0]+(y)[0], (x)[1]+(y)[1]} -#define __ADD4(x, y) ((v4s) {(x)[0]+(y)[0], (x)[1]+(y)[1], (x)[2]+(y)[2], (x)[3]+(y)[3]} +#define __ADD2(x, y) ((v2s) {(x)[0]+(y)[0], (x)[1]+(y)[1]} +#define __ADD4(x, y) ((v4s) {(x)[0]+(y)[0], (x)[1]+(y)[1], (x)[2]+(y)[2], (x)[3]+(y)[3]} /* Substraction */ -#define __SUB2(x, y) ((v2s) {(x)[0]-(y)[0], (x)[1]-(y)[1]} -#define __SUB4(x, y) ((v4s) {(x)[0]-(y)[0], (x)[1]-(y)[1], (x)[2]-(y)[2], (x)[3]-(y)[3]} +#define __SUB2(x, y) ((v2s) {(x)[0]-(y)[0], (x)[1]-(y)[1]} +#define __SUB4(x, y) ((v4s) {(x)[0]-(y)[0], (x)[1]-(y)[1], (x)[2]-(y)[2], (x)[3]-(y)[3]} /* Average */ -#define __AVG2(x, y) ((v2s) {((x)[0]+(y)[0])>>1, ((x)[1]+(y)[1])>>1} -#define __AVG4(x, y) ((v4s) {((x)[0]+(y)[0])>>1, ((x)[1]+(y)[1])>>1, ((x)[2]+(y)[2])>>1, ((x)[3]+(y)[3])>>1} +#define __AVG2(x, y) ((v2s) {((x)[0]+(y)[0])>>1, ((x)[1]+(y)[1])>>1} +#define __AVG4(x, y) ((v4s) {((x)[0]+(y)[0])>>1, ((x)[1]+(y)[1])>>1, ((x)[2]+(y)[2])>>1, ((x)[3]+(y)[3])>>1} /* Average unsigned */ -#define __AVGU2(x, y) ((v2u) {((unsigned short)(x)[0]+(unsigned short)(y)[0])>>1, ((unsigned short)(x)[1]+(unsigned short)(y)[1])>>1} -#define __AVGU4(x, y) ((v4u) {((unsigned char)(x)[0]+(unsigned char)(y)[0])>>1, ((unsigned char)(x)[1]+(unsigned char)(y)[1])>>1, \ - ((unsigned char)(x)[2]+(unsigned char)(y)[2])>>1, ((unsigned char)(x)[3]+(unsigned char)(y)[3])>>1} +#define __AVGU2(x, y) ((v2u) {((unsigned short)(x)[0]+(unsigned short)(y)[0])>>1, ((unsigned short)(x)[1]+(unsigned short)(y)[1])>>1} +#define __AVGU4(x, y) ((v4u) {((unsigned char)(x)[0]+(unsigned char)(y)[0])>>1, ((unsigned char)(x)[1]+(unsigned char)(y)[1])>>1, \ + ((unsigned char)(x)[2]+(unsigned char)(y)[2])>>1, ((unsigned char)(x)[3]+(unsigned char)(y)[3])>>1} /* Bitwise and */ -#define __AND2(x, y) ((v2s) {(x)[0]&(y)[0], (x)[1]&(y)[1]} -#define __AND4(x, y) ((v4s) {(x)[0]&(y)[0], (x)[1]&(y)[1], (x)[2]&(y)[2], (x)[3]&(y)[3]} +#define __AND2(x, y) ((v2s) {(x)[0]&(y)[0], (x)[1]&(y)[1]} +#define __AND4(x, y) ((v4s) {(x)[0]&(y)[0], (x)[1]&(y)[1], (x)[2]&(y)[2], (x)[3]&(y)[3]} /* Bitwise or */ -#define __OR2(x, y) ((v2s) {(x)[0]|(y)[0], (x)[1]|(y)[1]} -#define __OR4(x, y) ((v4s) {(x)[0]|(y)[0], (x)[1]|(y)[1], (x)[2]|(y)[2], (x)[3]|(y)[3]} +#define __OR2(x, y) ((v2s) {(x)[0]|(y)[0], (x)[1]|(y)[1]} +#define __OR4(x, y) ((v4s) {(x)[0]|(y)[0], (x)[1]|(y)[1], (x)[2]|(y)[2], (x)[3]|(y)[3]} /* Bitwise exor */ -#define __EXOR2(x, y) ((v2s) {(x)[0]^(y)[0], (x)[1]^(y)[1]} -#define __EXOR4(x, y) ((v4s) {(x)[0]^(y)[0], (x)[1]^(y)[1], (x)[2]^(y)[2], (x)[3]^(y)[3]} +#define __EXOR2(x, y) ((v2s) {(x)[0]^(y)[0], (x)[1]^(y)[1]} +#define __EXOR4(x, y) ((v4s) {(x)[0]^(y)[0], (x)[1]^(y)[1], (x)[2]^(y)[2], (x)[3]^(y)[3]} /* Logical shift right */ -#define __SRL2(x, y) ((v2u) {((unsigned short)(x)[0]>>(unsigned short)(y)[0]), ((unsigned short)(x)[1]>>(unsigned short)(y)[1])} -#define __SRL4(x, y) ((v4u) {((unsigned char)(x)[0]>>(unsigned char)(y)[0]), ((unsigned char)(x)[1]>>(unsigned char)(y)[1]), \ - ((unsigned char)(x)[2]>>(unsigned char)(y)[2]), ((unsigned char)(x)[3]>>(unsigned char)(y)[3])} +#define __SRL2(x, y) ((v2u) {((unsigned short)(x)[0]>>(unsigned short)(y)[0]), ((unsigned short)(x)[1]>>(unsigned short)(y)[1])} +#define __SRL4(x, y) ((v4u) {((unsigned char)(x)[0]>>(unsigned char)(y)[0]), ((unsigned char)(x)[1]>>(unsigned char)(y)[1]), \ + ((unsigned char)(x)[2]>>(unsigned char)(y)[2]), ((unsigned char)(x)[3]>>(unsigned char)(y)[3])} /* Arithmetic shift right */ -#define __SRA2(x, y) ((v2s) {((signed short)(x)[0]>>(signed short)(y)[0]), ((signed short)(x)[1]>>(signed short)(y)[1])} -#define __SRA4(x, y) ((v4s) {((signed char)(x)[0]>>(signed char)(y)[0]), ((signed char)(x)[1]>>(signed char)(y)[1]), \ - ((signed char)(x)[2]>>(signed char)(y)[2]), ((signed char)(x)[3]>>(signed char)(y)[3])} +#define __SRA2(x, y) ((v2s) {((signed short)(x)[0]>>(signed short)(y)[0]), ((signed short)(x)[1]>>(signed short)(y)[1])} +#define __SRA4(x, y) ((v4s) {((signed char)(x)[0]>>(signed char)(y)[0]), ((signed char)(x)[1]>>(signed char)(y)[1]), \ + ((signed char)(x)[2]>>(signed char)(y)[2]), ((signed char)(x)[3]>>(signed char)(y)[3])} /* Logical shift left */ -#define __SLL2(x, y) ((v2s) {(x)[0]<<(y)[0], (x)[1]<<(y)[1]} -#define __SLL4(x, y) ((v4s) {(x)[0]<<(y)[0], (x)[1]<<(y)[1], (x)[2]<<(y)[2], (x)[3]<<(y)[3]} +#define __SLL2(x, y) ((v2s) {(x)[0]<<(y)[0], (x)[1]<<(y)[1]} +#define __SLL4(x, y) ((v4s) {(x)[0]<<(y)[0], (x)[1]<<(y)[1], (x)[2]<<(y)[2], (x)[3]<<(y)[3]} /* Mac */ -#define __MAC(Acc, x, y) ((Acc) + ((x) * (y))) -#define __MSU(Acc, x, y) ((Acc) - ((x) * (y))) +#define __MAC(Acc, x, y) ((Acc) + ((x) * (y))) +#define __MSU(Acc, x, y) ((Acc) - ((x) * (y))) -#define __MACS(Acc, x, y) ((Acc) + ((short int) (x) * (short int) (y))) -#define __MACHHS(Acc, x, y) ((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) -#define __MACU(Acc, x, y) ((Acc) + ((unsigned short int) (x) * (unsigned short int) (y))) -#define __MACHHU(Acc, x, y) ((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) +#define __MACS(Acc, x, y) ((Acc) + ((short int) (x) * (short int) (y))) +#define __MACHHS(Acc, x, y) ((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) +#define __MACU(Acc, x, y) ((Acc) + ((unsigned short int) (x) * (unsigned short int) (y))) +#define __MACHHU(Acc, x, y) ((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) -#define __MACSN(Acc, x, y, n) (((Acc) + ((short int) (x) * (short int) (y)))>>(n)) -#define __MACUN(Acc, x, y, n) (((Acc) + ((unsigned short int) (x) * (unsigned short int) (y)))>>(n)) -#define __MACSRN(Acc, x, y, n) ((((Acc) + ((short int) (x) * (short int) (y))) + (1<<((n)-1))) >> (n)) -#define __MACURN(Acc, x, y, n) ((((Acc) + ((unsigned short int) (x) * (unsigned short int) (y))) + (1<<((n)-1))) >> (n)) +#define __MACSN(Acc, x, y, n) (((Acc) + ((short int) (x) * (short int) (y)))>>(n)) +#define __MACUN(Acc, x, y, n) (((Acc) + ((unsigned short int) (x) * (unsigned short int) (y)))>>(n)) +#define __MACSRN(Acc, x, y, n) ((((Acc) + ((short int) (x) * (short int) (y))) + (1<<((n)-1))) >> (n)) +#define __MACURN(Acc, x, y, n) ((((Acc) + ((unsigned short int) (x) * (unsigned short int) (y))) + (1<<((n)-1))) >> (n)) -#define __MACHHSN(Acc, x, y, n) (((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) >> (n)) -#define __MACHHUN(Acc, x, y, n) (((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) >> (n)) -#define __MACHHSRN(Acc, x, y, n) ((((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) + (1<<((n)-1))) >> (n)) -#define __MACHHURN(Acc, x, y, n) ((((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) + (n))) +#define __MACHHSN(Acc, x, y, n) (((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) >> (n)) +#define __MACHHUN(Acc, x, y, n) (((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) >> (n)) +#define __MACHHSRN(Acc, x, y, n) ((((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) + (1<<((n)-1))) >> (n)) +#define __MACHHURN(Acc, x, y, n) ((((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) + (n))) /* Multiplications */ -#define __MULS(x, y) (((short int) (x) * (short int) (y))) -#define __MULU(x, y) (((unsigned short int) (x) * (unsigned short int) (y))) -#define __MULHHS(x, y) (((short int) ((x)>>16) * (short int) ((y)>>16))) -#define __MULHHU(x, y) (((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) +#define __MULS(x, y) (((short int) (x) * (short int) (y))) +#define __MULU(x, y) (((unsigned short int) (x) * (unsigned short int) (y))) +#define __MULHHS(x, y) (((short int) ((x)>>16) * (short int) ((y)>>16))) +#define __MULHHU(x, y) (((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) -#define __MULSN(x, y, n) (((short int) (x) * (short int) (y))>>(n)) -#define __MULSRN(x, y, n) ((((short int) (x) * (short int) (y)) + (1<<((n)-1)))>>(n)) -#define __MULUN(x, y, n) (((unsigned short int) (x) * (unsigned short int) (y))>>(n)) -#define __MULURN(x, y, n) ((((unsigned short int) (x) * (unsigned short int) (y)) + (1<<((n)-1)))>>(n)) +#define __MULSN(x, y, n) (((short int) (x) * (short int) (y))>>(n)) +#define __MULSRN(x, y, n) ((((short int) (x) * (short int) (y)) + (1<<((n)-1)))>>(n)) +#define __MULUN(x, y, n) (((unsigned short int) (x) * (unsigned short int) (y))>>(n)) +#define __MULURN(x, y, n) ((((unsigned short int) (x) * (unsigned short int) (y)) + (1<<((n)-1)))>>(n)) -#define __MULHHSN(x, y, n) ((((short int) ((x)>>16) * (short int) ((y)>>16)))>>(n)) -#define __MULHHSRN(x, y, n) (((((short int) ((x)>>16) * (short int) ((y)>>16)))+(1<<((n)-1)))>>(n)) -#define __MULHHUN(x, y, n) ((((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16)))>>(n)) -#define __MULHHURN(x, y, n) (((((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16)))+(1<<((n)-1)))>>(n)) +#define __MULHHSN(x, y, n) ((((short int) ((x)>>16) * (short int) ((y)>>16)))>>(n)) +#define __MULHHSRN(x, y, n) (((((short int) ((x)>>16) * (short int) ((y)>>16)))+(1<<((n)-1)))>>(n)) +#define __MULHHUN(x, y, n) ((((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16)))>>(n)) +#define __MULHHURN(x, y, n) (((((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16)))+(1<<((n)-1)))>>(n)) /* Vectorial product and sum of products */ -#define __DOTP2(x, y) ( (short)(x)[0]*(short)(y)[0] + (short)(x)[1]*(short)(y)[1]) -#define __DOTPU2(x, y) ( (unsigned short)(x)[0]*(unsigned short)(y)[0] + (unsigned short)(x)[1]*(unsigned short)(y)[1]) -#define __DOTPUS2(x, y) ( (unsigned short)(x)[0]*(short)(y)[0] + (unsigned short)(x)[1]*(short)(y)[1]) - -#define __DOTPSC2(x, y) ( (short)(x)[0]*(short)(y) + (short)(x)[1]*(short)(y)) -#define __DOTPUSC2(x, y) ( (unsigned short)(x)[0]*(unsigned short)(y) + (unsigned short)(x)[1]*(unsigned short)(y)) -#define __DOTPUSSC2(x, y) ( (unsigned short)(x)[0]*(short)(y) + (unsigned short)(x)[1]*(short)(y)) - -#define __SUMDOTP2(x, y, z) ((z)+(short)(x)[0]*(short)(y)[0] + (short)(x)[1]*(short)(y)[1]) -#define __SUMDOTPU2(x, y, z) ((z)+(unsigned short)(x)[0]*(unsigned short)(y)[0] + (unsigned short)(x)[1]*(unsigned short)(y)[1]) -#define __SUMDOTPUS2(x, y, z) ((z)+(unsigned short)(x)[0]*(short)(y)[0] + (unsigned short)(x)[1]*(short)(y)[1]) - -#define __SUMDOTPSC2(x, y, z) ((z)+(short)(x)[0]*(short)(y) + (short)(x)[1]*(short)(y)) -#define __SUMDOTPUSC2(x, y, z) ((z)+(unsigned short)(x)[0]*(unsigned short)(y) + (unsigned short)(x)[1]*(unsigned short)(y)) -#define __SUMDOTPUSSC2(x, y, z) ((z)+(unsigned short)(x)[0]*(short)(y) + (unsigned short)(x)[1]*(short)(y)) - -#define __DOTP4(x, y) ( (char)(x)[0]*(char)(y)[0] + (char)(x)[1]*(char)(y)[1] + (char)(x)[2]*(char)(y)[2] + (char)(x)[3]*(char)(y)[3]) -#define __DOTPU4(x, y) ( (unsigned char)(x)[0]*(unsigned char)(y)[0] + (unsigned char)(x)[1]*(unsigned char)(y)[1] + \ - (unsigned char)(x)[2]*(unsigned char)(y)[2] + (unsigned char)(x)[3]*(unsigned char)(y)[3]) -#define __DOTPUS4(x, y) ( (unsigned char)(x)[0]*(char)(y)[0] + (unsigned char)(x)[1]*(char)(y)[1] + \ - (unsigned char)(x)[2]*(char)(y)[2] + (unsigned char)(x)[3]*(char)(y)[3]) - -#define __DOTPSC4(x, y) ( (char)(x)[0]*(char)(y) + (char)(x)[1]*(char)(y) + (char)(x)[2]*(char)(y) + (char)(x)[3]*(char)(y)) -#define __DOTPUSC4(x, y) ( (unsigned char)(x)[0]*(unsigned char)(y) + (unsigned char)(x)[1]*(unsigned char)(y) + \ - (unsigned char)(x)[2]*(unsigned char)(y) + (unsigned char)(x)[3]*(unsigned char)(y)) -#define __DOTPUSSC4(x, y) ( (unsigned char)(x)[0]*(char)(y) + (unsigned char)(x)[1]*(char)(y) + \ - (unsigned char)(x)[2]*(char)(y) + (unsigned char)(x)[3]*(char)(y)) - -#define __SUMDOTP4(x, y, z) ((z)+(char)(x)[0]*(char)(y)[0] + (char)(x)[1]*(char)(y)[1] + (char)(x)[2]*(char)(y)[2] + (char)(x)[3]*(char)(y)[3]) -#define __SUMDOTPU4(x, y, z) ((z)+(unsigned char)(x)[0]*(unsigned char)(y)[0] + (unsigned char)(x)[1]*(unsigned char)(y)[1] + \ - (unsigned char)(x)[2]*(unsigned char)(y)[2] + (unsigned char)(x)[3]*(unsigned char)(y)[3]) -#define __SUMDOTPUS4(x, y, z) ((z)+(unsigned char)(x)[0]*(char)(y)[0] + (unsigned char)(x)[1]*(char)(y)[1] + \ - (unsigned char)(x)[2]*(char)(y)[2] + (unsigned char)(x)[3]*(char)(y)[3]) - -#define __SUMDOTPSC4(x, y, z) ((z)+(char)(x)[0]*(char)(y) + (char)(x)[1]*(char)(y) + (char)(x)[2]*(char)(y) + (char)(x)[3]*(char)(y)) -#define __SUMDOTPUSC4(x, y, z) ((z)+(unsigned char)(x)[0]*(unsigned char)(y) + (unsigned char)(x)[1]*(unsigned char)(y) + \ - (unsigned char)(x)[2]*(unsigned char)(y) + (unsigned char)(x)[3]*(unsigned char)(y)) -#define __SUMDOTPUSSC4(x, y, z) ((z)+(unsigned char)(x)[0]*(char)(y) + (unsigned char)(x)[1]*(char)(y) + \ - (unsigned char)(x)[2]*(char)(y) + (unsigned char)(x)[3]*(char)(y)) +#define __DOTP2(x, y) ( (short)(x)[0]*(short)(y)[0] + (short)(x)[1]*(short)(y)[1]) +#define __DOTPU2(x, y) ( (unsigned short)(x)[0]*(unsigned short)(y)[0] + (unsigned short)(x)[1]*(unsigned short)(y)[1]) +#define __DOTPUS2(x, y) ( (unsigned short)(x)[0]*(short)(y)[0] + (unsigned short)(x)[1]*(short)(y)[1]) + +#define __DOTPSC2(x, y) ( (short)(x)[0]*(short)(y) + (short)(x)[1]*(short)(y)) +#define __DOTPUSC2(x, y) ( (unsigned short)(x)[0]*(unsigned short)(y) + (unsigned short)(x)[1]*(unsigned short)(y)) +#define __DOTPUSSC2(x, y) ( (unsigned short)(x)[0]*(short)(y) + (unsigned short)(x)[1]*(short)(y)) + +#define __SUMDOTP2(x, y, z) ((z)+(short)(x)[0]*(short)(y)[0] + (short)(x)[1]*(short)(y)[1]) +#define __SUMDOTPU2(x, y, z) ((z)+(unsigned short)(x)[0]*(unsigned short)(y)[0] + (unsigned short)(x)[1]*(unsigned short)(y)[1]) +#define __SUMDOTPUS2(x, y, z) ((z)+(unsigned short)(x)[0]*(short)(y)[0] + (unsigned short)(x)[1]*(short)(y)[1]) + +#define __SUMDOTPSC2(x, y, z) ((z)+(short)(x)[0]*(short)(y) + (short)(x)[1]*(short)(y)) +#define __SUMDOTPUSC2(x, y, z) ((z)+(unsigned short)(x)[0]*(unsigned short)(y) + (unsigned short)(x)[1]*(unsigned short)(y)) +#define __SUMDOTPUSSC2(x, y, z) ((z)+(unsigned short)(x)[0]*(short)(y) + (unsigned short)(x)[1]*(short)(y)) + +#define __DOTP4(x, y) ( (char)(x)[0]*(char)(y)[0] + (char)(x)[1]*(char)(y)[1] + (char)(x)[2]*(char)(y)[2] + (char)(x)[3]*(char)(y)[3]) +#define __DOTPU4(x, y) ( (unsigned char)(x)[0]*(unsigned char)(y)[0] + (unsigned char)(x)[1]*(unsigned char)(y)[1] + \ + (unsigned char)(x)[2]*(unsigned char)(y)[2] + (unsigned char)(x)[3]*(unsigned char)(y)[3]) +#define __DOTPUS4(x, y) ( (unsigned char)(x)[0]*(char)(y)[0] + (unsigned char)(x)[1]*(char)(y)[1] + \ + (unsigned char)(x)[2]*(char)(y)[2] + (unsigned char)(x)[3]*(char)(y)[3]) + +#define __DOTPSC4(x, y) ( (char)(x)[0]*(char)(y) + (char)(x)[1]*(char)(y) + (char)(x)[2]*(char)(y) + (char)(x)[3]*(char)(y)) +#define __DOTPUSC4(x, y) ( (unsigned char)(x)[0]*(unsigned char)(y) + (unsigned char)(x)[1]*(unsigned char)(y) + \ + (unsigned char)(x)[2]*(unsigned char)(y) + (unsigned char)(x)[3]*(unsigned char)(y)) +#define __DOTPUSSC4(x, y) ( (unsigned char)(x)[0]*(char)(y) + (unsigned char)(x)[1]*(char)(y) + \ + (unsigned char)(x)[2]*(char)(y) + (unsigned char)(x)[3]*(char)(y)) + +#define __SUMDOTP4(x, y, z) ((z)+(char)(x)[0]*(char)(y)[0] + (char)(x)[1]*(char)(y)[1] + (char)(x)[2]*(char)(y)[2] + (char)(x)[3]*(char)(y)[3]) +#define __SUMDOTPU4(x, y, z) ((z)+(unsigned char)(x)[0]*(unsigned char)(y)[0] + (unsigned char)(x)[1]*(unsigned char)(y)[1] + \ + (unsigned char)(x)[2]*(unsigned char)(y)[2] + (unsigned char)(x)[3]*(unsigned char)(y)[3]) +#define __SUMDOTPUS4(x, y, z) ((z)+(unsigned char)(x)[0]*(char)(y)[0] + (unsigned char)(x)[1]*(char)(y)[1] + \ + (unsigned char)(x)[2]*(char)(y)[2] + (unsigned char)(x)[3]*(char)(y)[3]) + +#define __SUMDOTPSC4(x, y, z) ((z)+(char)(x)[0]*(char)(y) + (char)(x)[1]*(char)(y) + (char)(x)[2]*(char)(y) + (char)(x)[3]*(char)(y)) +#define __SUMDOTPUSC4(x, y, z) ((z)+(unsigned char)(x)[0]*(unsigned char)(y) + (unsigned char)(x)[1]*(unsigned char)(y) + \ + (unsigned char)(x)[2]*(unsigned char)(y) + (unsigned char)(x)[3]*(unsigned char)(y)) +#define __SUMDOTPUSSC4(x, y, z) ((z)+(unsigned char)(x)[0]*(char)(y) + (unsigned char)(x)[1]*(char)(y) + \ + (unsigned char)(x)[2]*(char)(y) + (unsigned char)(x)[3]*(char)(y)) #ifdef ARCHI_CORE_HAS_CPLX /* Complex Multiplication, Q15x15 into Q15, with optional post scaling by 1 or 2 */ -#define __CPLXMULS(x, y) ((v2s) {(signed short) ((((long long) (x)[0]*(long long) (y)[0]) - ((long long) (x)[1]*(long long) (y)[1]))>>15), \ - (signed short) ((((long long) (x)[0]*(long long) (y)[1]) + ((long long) (x)[1]*(long long) (y)[0]))>>15)}) -#define __CPLXMULSDIV2(x, y) (__CPLXMULS(x, y)>>(v2s){1,1}) -#define __CPLXMULSDIV4(x, y) (__CPLXMULS(x, y)>>(v2s){2,2}) +#define __CPLXMULS(x, y) ((v2s) {(signed short) ((((long long) (x)[0]*(long long) (y)[0]) - ((long long) (x)[1]*(long long) (y)[1]))>>15), \ + (signed short) ((((long long) (x)[0]*(long long) (y)[1]) + ((long long) (x)[1]*(long long) (y)[0]))>>15)}) +#define __CPLXMULSDIV2(x, y) (__CPLXMULS(x, y)>>(v2s){1,1}) +#define __CPLXMULSDIV4(x, y) (__CPLXMULS(x, y)>>(v2s){2,2}) /* Complex conjugate */ -#define __CPLXCONJ(x) ((v2s) {(x)[0], -(x)[1]}) +#define __CPLXCONJ(x) ((v2s) {(x)[0], -(x)[1]}) /* Complex substration, result rotated by -pi/2 */ -#define __SUB2ROTMJ(x, y) ((v2s) {(x)[1]-(y)[1], (y)[0]-(x)[0]}) +#define __SUB2ROTMJ(x, y) ((v2s) {(x)[1]-(y)[1], (y)[0]-(x)[0]}) /* Complex addition with post scaling by 1 or 2 */ -#define __ADD2DIV2(x, y) (((x)+(y))>>(v2s) {1, 1}) -#define __ADD2DIV4(x, y) (((x)+(y))>>(v2s) {2, 2}) +#define __ADD2DIV2(x, y) (((x)+(y))>>(v2s) {1, 1}) +#define __ADD2DIV4(x, y) (((x)+(y))>>(v2s) {2, 2}) -#define __ADD4DIV2(x, y) (((x)+(y))>>(v4s) {1, 1, 1, 1}) -#define __ADD4DIV4(x, y) (((x)+(y))>>(v4s) {2, 2, 2, 2}) +#define __ADD4DIV2(x, y) (((x)+(y))>>(v4s) {1, 1, 1, 1}) +#define __ADD4DIV4(x, y) (((x)+(y))>>(v4s) {2, 2, 2, 2}) /* Complex substraction with post scaling by 1 or 2 */ -#define __SUB2DIV2(x, y) (((x)-(y))>>(v2s) {1, 1}) -#define __SUB2DIV4(x, y) (((x)-(y))>>(v2s) {2, 2}) +#define __SUB2DIV2(x, y) (((x)-(y))>>(v2s) {1, 1}) +#define __SUB2DIV4(x, y) (((x)-(y))>>(v2s) {2, 2}) -#define __SUB4DIV2(x, y) (((x)-(y))>>(v4s) {1, 1, 1, 1}) -#define __SUB4DIV4(x, y) (((x)-(y))>>(v4s) {2, 2, 2, 2}) +#define __SUB4DIV2(x, y) (((x)-(y))>>(v4s) {1, 1, 1, 1}) +#define __SUB4DIV4(x, y) (((x)-(y))>>(v4s) {2, 2, 2, 2}) /* Complex subtraction with post scaling by 1 or 2 */ -#define __SUB2DIV2(x, y) (((x)-(y))>>(v2s) {1, 1}) -#define __SUB2DIV4(x, y) (((x)-(y))>>(v2s) {2, 2}) +#define __SUB2DIV2(x, y) (((x)-(y))>>(v2s) {1, 1}) +#define __SUB2DIV4(x, y) (((x)-(y))>>(v2s) {2, 2}) /* Viterbi Max and Viterbi Select, pair of Q15 */ -#define __VITMAX(x, y) (_VitT1_Flag=((x)[1]<=(y)[1])?1:0, _VitT0_Flag=((x)[0]<=(y)[0])?1:0,\ - (v2s) {((x)[0]>(y)[0])?(x)[0]:(y)[0], ((x)[1]>(y)[1])?(x)[1]:(y)[1]}) -#define __VITSEL(x, y) (v2s) {(_VitT0_Flag?(y)[0]:(x)[0])<<1|_VitT0_Flag, (_VitT1_Flag?(y)[1]:(x)[1])<<1|_VitT1_Flag} +#define __VITMAX(x, y) (_VitT1_Flag=((x)[1]<=(y)[1])?1:0, _VitT0_Flag=((x)[0]<=(y)[0])?1:0,\ + (v2s) {((x)[0]>(y)[0])?(x)[0]:(y)[0], ((x)[1]>(y)[1])?(x)[1]:(y)[1]}) +#define __VITSEL(x, y) (v2s) {(_VitT0_Flag?(y)[0]:(x)[0])<<1|_VitT0_Flag, (_VitT1_Flag?(y)[1]:(x)[1])<<1|_VitT1_Flag} #endif /* Position of the most significant bit of x */ -#define __FL1(x) (31 - __builtin_clz((x))) +#define __FL1(x) (31 - __builtin_clz((x))) /* Number of sign bits */ -#define __CLB(x) (__builtin_clrsb((x))) +#define __CLB(x) (__builtin_clrsb((x))) /* Bit set */ -#define __BITSET(x, size, off) ((x) | (((1<<(size))-1)<<(off))) -#define __BITSET_R(x, size, off) ((x) | (((1<<(size))-1)<<(off))) -#define __BITSET_R_SAFE(x, size, off) ((x) | (((1<<((size)&0x1F))-1)<<((off)&0x1F))) +#define __BITSET(x, size, off) ((x) | (((1<<(size))-1)<<(off))) +#define __BITSET_R(x, size, off) ((x) | (((1<<(size))-1)<<(off))) +#define __BITSET_R_SAFE(x, size, off) ((x) | (((1<<((size)&0x1F))-1)<<((off)&0x1F))) /* Bit clr */ -#define __BITCLR(x, size, off) ((x) & ~(((1<<(size))-1)<<(off))) -#define __BITCLR_R(x, size, off) ((x) & ~(((1<<(size))-1)<<(off))) -#define __BITCLR_R_SAFE(x, size, off) ((x) & ~(((1<<((size)&0x1F))-1)<<((off)&0x1F))) +#define __BITCLR(x, size, off) ((x) & ~(((1<<(size))-1)<<(off))) +#define __BITCLR_R(x, size, off) ((x) & ~(((1<<(size))-1)<<(off))) +#define __BITCLR_R_SAFE(x, size, off) ((x) & ~(((1<<((size)&0x1F))-1)<<((off)&0x1F))) /* Bit Extraction */ -#define __BITEXTRACT(x, size, off) (((((x)>>(off))&((unsigned int)(1<<(size))-1))<<(32-(size)))>>(32-(size))) -#define __BITEXTRACTU(x, size, off) (((x)>>(off))&((unsigned int)(1<<(size))-1)) +#define __BITEXTRACT(x, size, off) (((((x)>>(off))&((unsigned int)(1<<(size))-1))<<(32-(size)))>>(32-(size))) +#define __BITEXTRACTU(x, size, off) (((x)>>(off))&((unsigned int)(1<<(size))-1)) -#define __BITEXTRACT_R(x, size, off) (((((x)>>(off))&((unsigned int)(1<<(size))-1))<<(32-(size)))>>(32-(size))) -#define __BITEXTRACTU_R(x, size, off) (((x)>>(off))&((unsigned int)(1<<(size))-1)) +#define __BITEXTRACT_R(x, size, off) (((((x)>>(off))&((unsigned int)(1<<(size))-1))<<(32-(size)))>>(32-(size))) +#define __BITEXTRACTU_R(x, size, off) (((x)>>(off))&((unsigned int)(1<<(size))-1)) -#define __BITEXTRACT_R_SAFE(x, size, off) (((((x)>>((off)&0x1F))&((unsigned int)(1<<((((size)>32)?32:(size))))-1))<<(32-((((size)>32)?32:(size)))))>>(32-((((size)>32)?32:(size))))) -#define __BITEXTRACTU_R_SAFE(x, size, off) (((x)>>((off)&0x1F))&((unsigned int)(1<<((((size)>32)?32:(size))))-1)) +#define __BITEXTRACT_R_SAFE(x, size, off) (((((x)>>((off)&0x1F))&((unsigned int)(1<<((((size)>32)?32:(size))))-1))<<(32-((((size)>32)?32:(size)))))>>(32-((((size)>32)?32:(size))))) +#define __BITEXTRACTU_R_SAFE(x, size, off) (((x)>>((off)&0x1F))&((unsigned int)(1<<((((size)>32)?32:(size))))-1)) /* Bit insertion */ -#define __BITINSERT(dst, src, size, off) (((dst) & ~(((1<<(size))-1)<<(off))) | (((src) & ((1<<(size))-1))<<(off))) -#define __BITINSERT_R(dst, src, size, off) (((dst) & ~(((1<<(size))-1)<<(off))) | (((src) & ((1<<(size))-1))<<(off))) -#define __BITINSERT_R_SAFE(dst, src, size, off) (((dst) & ~(((1<<(((size)>32)?32:(size)))-1)<<((off)&0x1F))) | (((src) & ((1<<(((size)>32)?32:(size)))-1))<<((off)&0x1F))) +#define __BITINSERT(dst, src, size, off) (((dst) & ~(((1<<(size))-1)<<(off))) | (((src) & ((1<<(size))-1))<<(off))) +#define __BITINSERT_R(dst, src, size, off) (((dst) & ~(((1<<(size))-1)<<(off))) | (((src) & ((1<<(size))-1))<<(off))) +#define __BITINSERT_R_SAFE(dst, src, size, off) (((dst) & ~(((1<<(((size)>32)?32:(size)))-1)<<((off)&0x1F))) | (((src) & ((1<<(((size)>32)?32:(size)))-1))<<((off)&0x1F))) /* 1 bit rotation to the right, 32 bits input */ -#define __ROTR(x) ((((x)>>1)&0x7FFFFFFF) | ((x)<<31)) +#define __ROTR(x) ((((x)>>1)&0x7FFFFFFF) | ((x)<<31)) /* Add with normalization */ -#define __ADDNORMU(x, y, scale) ((unsigned int)((x) + (y))>>(scale)) -#define __ADDNORMU_REG(x, y, scale) ((unsigned int)((x) + (y))>>(scale)) -#define __ADDNORM(x, y, scale) ((int)((x) + (y))>>(scale)) -#define __ADDNORM_REG(x, y, scale) ((int)((x) + (y))>>(scale)) +#define __ADDNORMU(x, y, scale) ((unsigned int)((x) + (y))>>(scale)) +#define __ADDNORMU_REG(x, y, scale) ((unsigned int)((x) + (y))>>(scale)) +#define __ADDNORM(x, y, scale) ((int)((x) + (y))>>(scale)) +#define __ADDNORM_REG(x, y, scale) ((int)((x) + (y))>>(scale)) /* Add with normalization and rounding */ -#define __ADDROUNDNORMU(x, y, scale) ((unsigned int)((x) + (y) + (1<<((scale)-1)))>>(scale)) -#define __ADDROUNDNORMU_REG(x, y, scale) ((unsigned int)((x) + (y) + (1<<((scale)-1)))>>(scale)) -#define __ADDROUNDNORM(x, y, scale) ((int)((x) + (y) + (1<<((scale)-1)))>>(scale)) -#define __ADDROUNDNORM_REG(x, y, scale) ((int)((x) + (y) + (1<<((scale)-1)))>>(scale)) +#define __ADDROUNDNORMU(x, y, scale) ((unsigned int)((x) + (y) + (1<<((scale)-1)))>>(scale)) +#define __ADDROUNDNORMU_REG(x, y, scale) ((unsigned int)((x) + (y) + (1<<((scale)-1)))>>(scale)) +#define __ADDROUNDNORM(x, y, scale) ((int)((x) + (y) + (1<<((scale)-1)))>>(scale)) +#define __ADDROUNDNORM_REG(x, y, scale) ((int)((x) + (y) + (1<<((scale)-1)))>>(scale)) /* Sub with normalization */ -#define __SUBNORMU(x, y, scale) ((unsigned int)((x) - (y))>>(scale)) -#define __SUBNORMU_REG(x, y, scale) ((unsigned int)((x) - (y))>>(scale)) -#define __SUBNORM(x, y, scale) ((int)((x) - (y))>>(scale)) -#define __SUBNORM_REG(x, y, scale) ((int)((x) - (y))>>(scale)) +#define __SUBNORMU(x, y, scale) ((unsigned int)((x) - (y))>>(scale)) +#define __SUBNORMU_REG(x, y, scale) ((unsigned int)((x) - (y))>>(scale)) +#define __SUBNORM(x, y, scale) ((int)((x) - (y))>>(scale)) +#define __SUBNORM_REG(x, y, scale) ((int)((x) - (y))>>(scale)) /* Sub with normalization and rounding */ -#define __SUBROUNDNORMU(x, y, scale) ((unsigned int)((x) - (y) + (1<<((scale)-1)))>>(scale)) -#define __SUBROUNDNORMU_REG(x, y, scale) ((unsigned int)((x) - (y) + (1<<((scale)-1)))>>(scale)) -#define __SUBROUNDNORM(x, y, scale) ((int)((x) - (y) + (1<<((scale)-1)))>>(scale)) -#define __SUBROUNDNORM_REG(x, y, scale) ((int)((x) - (y) + (1<<((scale)-1)))>>(scale)) +#define __SUBROUNDNORMU(x, y, scale) ((unsigned int)((x) - (y) + (1<<((scale)-1)))>>(scale)) +#define __SUBROUNDNORMU_REG(x, y, scale) ((unsigned int)((x) - (y) + (1<<((scale)-1)))>>(scale)) +#define __SUBROUNDNORM(x, y, scale) ((int)((x) - (y) + (1<<((scale)-1)))>>(scale)) +#define __SUBROUNDNORM_REG(x, y, scale) ((int)((x) - (y) + (1<<((scale)-1)))>>(scale)) /* Normalization and rounding */ -#define __ROUNDNORMU(x, scale) ((unsigned int)((x) + (1<<((scale)-1)))>>(scale)) -#define __ROUNDNORMU_REG(x, scale) ((unsigned int)((x) + (1<<((scale)-1)))>>(scale)) -#define __ROUNDNORM(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) -#define __ROUNDNORM_REG(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) -#define __COREID() 0 -#define __CLUSTERID() 0 -#define __NCORE() 1 - -#define __SPRWRITE(x, y) -#define __SPRREAD(x) ((int) 0) -#define __SPRREAD_VOL(x) ((int) 0) - -#define __READ_BASE_OFF(base, off) ((int) 0) -#define __WRITE_BASE_OFF(base, off, val) - -#define __READ_BASE_OFF_VOL(base, off) ((int) 0) -#define __READ_BASE_OFF_HALF_VOL(base, off) ((int) 0) -#define __READ_BASE_OFF_BYTE_VOL(base, off) ((int) 0) - -#define __WRITE_BASE_OFF_VOL(x, base, off) -#define __WRITE_BASE_OFF_HALF_VOL(x, base, off) -#define __WRITE_BASE_OFF_BYTE_VOL(x, base, off) +#define __ROUNDNORMU(x, scale) ((unsigned int)((x) + (1<<((scale)-1)))>>(scale)) +#define __ROUNDNORMU_REG(x, scale) ((unsigned int)((x) + (1<<((scale)-1)))>>(scale)) +#define __ROUNDNORM(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) +#define __ROUNDNORM_REG(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) + +#define __SPRWRITE(x, y) (*(volatile unsigned int *)(long)(x) = (y)) +#define __SPRREAD(x) (*(unsigned int *)(long)(x)) +#define __SPRREAD_VOL(x) (*(volatile unsigned int *)(long)(x)) + +#define __READ_BASE_OFF(base, off) (*(unsigned int *)(long)((base) + (off))) +#define __WRITE_BASE_OFF(base, off, val)(*(unsigned int *)(long)((base) + (off)) = (val)) + +#define __READ_BASE_OFF_VOL(base, off) (*(volatile unsigned int *)(long)((base) + (off))) +#define __READ_BASE_OFF_HALF_VOL(base, off) (*(volatile unsigned short int *)(long)((base) + (off))) +#define __READ_BASE_OFF_BYTE_VOL(base, off) (*(volatile unsigned char *)(long)((base) + (off))) + +#define __WRITE_BASE_OFF_VOL(x, base, off) (*(volatile unsigned int *)(long)((base) + (off)) = (x)) +#define __WRITE_BASE_OFF_HALF_VOL(x, base, off) (*(volatile unsigned short int *)(long)((base) + (off)) = (x)) +#define __WRITE_BASE_OFF_BYTE_VOL(x, base, off) (*(volatile unsigned char *)(long)((base) + (off)) = (x)) /* Utilities, Target independant */ -#define FIX2FP(Val, Precision) ((float) (Val) / (float) (1<<(Precision))) -#define FP2FIXR(Val, Precision) ((int)((Val)*((1 << (Precision))-1) + 0.5)) -#define FP2FIX(Val, Precision) ((int)((Val)*((1 << (Precision))-1))) +#define FIX2FP(Val, Precision) ((float) (Val) / (float) (1<<(Precision))) +#define FP2FIXR(Val, Precision) ((int)((Val)*((1 << (Precision))-1) + 0.5)) +#define FP2FIX(Val, Precision) ((int)((Val)*((1 << (Precision))-1))) + +#define HARTID_MASK 0xF +#define CLUSTERID_MASK 0x7E0 +#define CLUSTERID_SHIFT 5 + +#define __COREID() __SPRREAD_VOL(HARTID_MASK & RV_CSR_MHARTID) +#define __CLUSTERID() __SPRREAD_VOL((CLUSTERID_MASK & RV_CSR_MHARTID) >> CLUSTERID_SHIFT) +#define __NCORE() __SPRREAD_VOL(ARCHI_SOC_PERIPHERALS_ADDR + 0x3000 + 0x12) + #endif #endif diff --git a/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h b/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h index b65aea75..15c97e80 100644 --- a/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h +++ b/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h @@ -47,301 +47,307 @@ typedef void * rt_pointerT; typedef unsigned int rt_pointerT; #endif /* Packing of scalars into vectors */ -#define __PACK2(x, y) ((v2s) {(signed short) (x), (signed short) (y)}) -#define __PACKU2(x, y) ((v2u) {(unsigned short) (x), (unsigned short) (y)}) +#define __PACK2(x, y) ((v2s) {(signed short) (x), (signed short) (y)}) +#define __PACKU2(x, y) ((v2u) {(unsigned short) (x), (unsigned short) (y)}) -#define __PACK4(x, y, z, t) ((v4s) {(signed char) (x), (signed char) (y), (signed char) (z), (signed char) (t)}) -#define __PACKU4(x, y, z, t) ((v4u) {(unsigned char) (x), (unsigned char) (y), (unsigned char) (z), (unsigned char) (t)}) +#define __PACK4(x, y, z, t) ((v4s) {(signed char) (x), (signed char) (y), (signed char) (z), (signed char) (t)}) +#define __PACKU4(x, y, z, t) ((v4u) {(unsigned char) (x), (unsigned char) (y), (unsigned char) (z), (unsigned char) (t)}) /* Max */ #define __MAX(x, y) ((x)>(y)?(x):(y)) -#define __MAX2(x, y) ((v2s) {((signed short)(x)[0]>(signed short)(y)[0])?((signed short)(x)[0]):((signed short)(y)[0]), \ - ((signed short)(x)[1]>(signed short)(y)[1])?((signed short)(x)[1]):((signed short)(y)[1])}) -#define __MAX4(x, y) ((v4s) {((signed char)(x)[0]>(signed char)(y)[0])?(signed char)(x)[0]:(signed char)(y)[0], \ - ((signed char)(x)[1]>(signed char)(y)[1])?(signed char)(x)[1]:(signed char)(y)[1], \ - ((signed char)(x)[2]>(signed char)(y)[2])?(signed char)(x)[2]:(signed char)(y)[2], \ - ((signed char)(x)[3]>(signed char)(y)[3])?(signed char)(x)[3]:(signed char)(y)[3]}) - -#define __MAXU2(x, y) ((v2u) {((unsigned short)(x)[0]>(unsigned short)(y)[0])?(unsigned short)(x)[0]:(unsigned short)(y)[0], \ - ((unsigned short)(x)[1]>(unsigned short)(y)[1])?(unsigned short)(x)[1]:(unsigned short)(y)[1]}) -#define __MAXU4(x, y) ((v4u) {((unsigned char)(x)[0]>(unsigned char)(y)[0])?(unsigned char)(x)[0]:(unsigned char)(y)[0], \ - ((unsigned char)(x)[1]>(unsigned char)(y)[1])?(unsigned char)(x)[1]:(unsigned char)(y)[1], \ - ((unsigned char)(x)[2]>(unsigned char)(y)[2])?(unsigned char)(x)[2]:(unsigned char)(y)[2], \ - ((unsigned char)(x)[3]>(unsigned char)(y)[3])?(unsigned char)(x)[3]:(unsigned char)(y)[3]}) +#define __MAX2(x, y) ((v2s) {((signed short)(x)[0]>(signed short)(y)[0])?((signed short)(x)[0]):((signed short)(y)[0]), \ + ((signed short)(x)[1]>(signed short)(y)[1])?((signed short)(x)[1]):((signed short)(y)[1])}) +#define __MAX4(x, y) ((v4s) {((signed char)(x)[0]>(signed char)(y)[0])?(signed char)(x)[0]:(signed char)(y)[0], \ + ((signed char)(x)[1]>(signed char)(y)[1])?(signed char)(x)[1]:(signed char)(y)[1], \ + ((signed char)(x)[2]>(signed char)(y)[2])?(signed char)(x)[2]:(signed char)(y)[2], \ + ((signed char)(x)[3]>(signed char)(y)[3])?(signed char)(x)[3]:(signed char)(y)[3]}) + +#define __MAXU2(x, y) ((v2u) {((unsigned short)(x)[0]>(unsigned short)(y)[0])?(unsigned short)(x)[0]:(unsigned short)(y)[0], \ + ((unsigned short)(x)[1]>(unsigned short)(y)[1])?(unsigned short)(x)[1]:(unsigned short)(y)[1]}) +#define __MAXU4(x, y) ((v4u) {((unsigned char)(x)[0]>(unsigned char)(y)[0])?(unsigned char)(x)[0]:(unsigned char)(y)[0], \ + ((unsigned char)(x)[1]>(unsigned char)(y)[1])?(unsigned char)(x)[1]:(unsigned char)(y)[1], \ + ((unsigned char)(x)[2]>(unsigned char)(y)[2])?(unsigned char)(x)[2]:(unsigned char)(y)[2], \ + ((unsigned char)(x)[3]>(unsigned char)(y)[3])?(unsigned char)(x)[3]:(unsigned char)(y)[3]}) /* Min */ -#define __MIN2(x, y) ((v2s) {((signed short)(x)[0]<(signed short)(y)[0])?((signed short)(x)[0]):((signed short)(y)[0]), \ - ((signed short)(x)[1]<(signed short)(y)[1])?((signed short)(x)[1]):((signed short)(y)[1])}) -#define __MIN4(x, y) ((v4s) {((signed char)(x)[0]<(signed char)(y)[0])?(signed char)(x)[0]:(signed char)(y)[0], \ - ((signed char)(x)[1]<(signed char)(y)[1])?(signed char)(x)[1]:(signed char)(y)[1], \ - ((signed char)(x)[2]<(signed char)(y)[2])?(signed char)(x)[2]:(signed char)(y)[2], \ - ((signed char)(x)[3]<(signed char)(y)[3])?(signed char)(x)[3]:(signed char)(y)[3]}) - -#define __MINU2(x, y) ((v2u) {((unsigned short)(x)[0]<(unsigned short)(y)[0])?(unsigned short)(x)[0]:(unsigned short)(y)[0], \ - ((unsigned short)(x)[1]<(unsigned short)(y)[1])?(unsigned short)(x)[1]:(unsigned short)(y)[1]}) -#define __MINU4(x, y) ((v4u) {((unsigned char)(x)[0]<(unsigned char)(y)[0])?(unsigned char)(x)[0]:(unsigned char)(y)[0], \ - ((unsigned char)(x)[1]<(unsigned char)(y)[1])?(unsigned char)(x)[1]:(unsigned char)(y)[1], \ - ((unsigned char)(x)[2]<(unsigned char)(y)[2])?(unsigned char)(x)[2]:(unsigned char)(y)[2], \ - ((unsigned char)(x)[3]<(unsigned char)(y)[3])?(unsigned char)(x)[3]:(unsigned char)(y)[3]}) +#define __MIN2(x, y) ((v2s) {((signed short)(x)[0]<(signed short)(y)[0])?((signed short)(x)[0]):((signed short)(y)[0]), \ + ((signed short)(x)[1]<(signed short)(y)[1])?((signed short)(x)[1]):((signed short)(y)[1])}) +#define __MIN4(x, y) ((v4s) {((signed char)(x)[0]<(signed char)(y)[0])?(signed char)(x)[0]:(signed char)(y)[0], \ + ((signed char)(x)[1]<(signed char)(y)[1])?(signed char)(x)[1]:(signed char)(y)[1], \ + ((signed char)(x)[2]<(signed char)(y)[2])?(signed char)(x)[2]:(signed char)(y)[2], \ + ((signed char)(x)[3]<(signed char)(y)[3])?(signed char)(x)[3]:(signed char)(y)[3]}) + +#define __MINU2(x, y) ((v2u) {((unsigned short)(x)[0]<(unsigned short)(y)[0])?(unsigned short)(x)[0]:(unsigned short)(y)[0], \ + ((unsigned short)(x)[1]<(unsigned short)(y)[1])?(unsigned short)(x)[1]:(unsigned short)(y)[1]}) +#define __MINU4(x, y) ((v4u) {((unsigned char)(x)[0]<(unsigned char)(y)[0])?(unsigned char)(x)[0]:(unsigned char)(y)[0], \ + ((unsigned char)(x)[1]<(unsigned char)(y)[1])?(unsigned char)(x)[1]:(unsigned char)(y)[1], \ + ((unsigned char)(x)[2]<(unsigned char)(y)[2])?(unsigned char)(x)[2]:(unsigned char)(y)[2], \ + ((unsigned char)(x)[3]<(unsigned char)(y)[3])?(unsigned char)(x)[3]:(unsigned char)(y)[3]}) /* Clip */ -#define __CLIP(x, precision) ((x)<(-(1<<(precision)))?(-(1<<(precision))):(((x)>((1<<(precision))-1))?((1<<(precision))-1):(x))) -#define __CLIP_R(x, bound) (((x)<=-((bound)+1))?(-((bound)+1)):(((x)>=(bound))?(bound):(x))) +#define __CLIP(x, precision) ((x)<(-(1<<(precision)))?(-(1<<(precision))):(((x)>((1<<(precision))-1))?((1<<(precision))-1):(x))) +#define __CLIP_R(x, bound) (((x)<=-((bound)+1))?(-((bound)+1)):(((x)>=(bound))?(bound):(x))) -#define __CLIPU(x, precision) ((x)<0)?0:(((x)>((1<<(precision))-1))?((1<<(precision))-1):(x)) -#define __CLIPU_R(x, bound) (((x)<=0)?(0):(((x)>=(bound))?(bound):(x))) +#define __CLIPU(x, precision) ((x)<0)?0:(((x)>((1<<(precision))-1))?((1<<(precision))-1):(x)) +#define __CLIPU_R(x, bound) (((x)<=0)?(0):(((x)>=(bound))?(bound):(x))) /* Abs */ -#define __ABS2(x) ((v2u) {((x)[0]<0)?-(x)[0]:(x)[0], ((x)[1]<0)?-(x)[1]:(x)[1]}) -#define __ABS4(x) ((v4u) {((x)[0]<0)?-(x)[0]:(x)[0], ((x)[1]<0)?-(x)[1]:(x)[1], \ - ((x)[2]<0)?-(x)[2]:(x)[2], ((x)[3]<0)?-(x)[3]:(x)[3]}) +#define __ABS2(x) ((v2u) {((x)[0]<0)?-(x)[0]:(x)[0], ((x)[1]<0)?-(x)[1]:(x)[1]}) +#define __ABS4(x) ((v4u) {((x)[0]<0)?-(x)[0]:(x)[0], ((x)[1]<0)?-(x)[1]:(x)[1], \ + ((x)[2]<0)?-(x)[2]:(x)[2], ((x)[3]<0)?-(x)[3]:(x)[3]}) /* Unary minus */ -#define __NEG2(x) ((v2s) {-(x)[0], -(x)[1]} -#define __NEG4(x) ((v4s) {-(x)[0], -(x)[1], -(x)[2], -(x)[3]} +#define __NEG2(x) ((v2s) {-(x)[0], -(x)[1]} +#define __NEG4(x) ((v4s) {-(x)[0], -(x)[1], -(x)[2], -(x)[3]} /* Addition */ -#define __ADD2(x, y) ((v2s) {(x)[0]+(y)[0], (x)[1]+(y)[1]} -#define __ADD4(x, y) ((v4s) {(x)[0]+(y)[0], (x)[1]+(y)[1], (x)[2]+(y)[2], (x)[3]+(y)[3]} +#define __ADD2(x, y) ((v2s) {(x)[0]+(y)[0], (x)[1]+(y)[1]} +#define __ADD4(x, y) ((v4s) {(x)[0]+(y)[0], (x)[1]+(y)[1], (x)[2]+(y)[2], (x)[3]+(y)[3]} /* Substraction */ -#define __SUB2(x, y) ((v2s) {(x)[0]-(y)[0], (x)[1]-(y)[1]} -#define __SUB4(x, y) ((v4s) {(x)[0]-(y)[0], (x)[1]-(y)[1], (x)[2]-(y)[2], (x)[3]-(y)[3]} +#define __SUB2(x, y) ((v2s) {(x)[0]-(y)[0], (x)[1]-(y)[1]} +#define __SUB4(x, y) ((v4s) {(x)[0]-(y)[0], (x)[1]-(y)[1], (x)[2]-(y)[2], (x)[3]-(y)[3]} /* Average */ -#define __AVG2(x, y) ((v2s) {((x)[0]+(y)[0])>>1, ((x)[1]+(y)[1])>>1} -#define __AVG4(x, y) ((v4s) {((x)[0]+(y)[0])>>1, ((x)[1]+(y)[1])>>1, ((x)[2]+(y)[2])>>1, ((x)[3]+(y)[3])>>1} +#define __AVG2(x, y) ((v2s) {((x)[0]+(y)[0])>>1, ((x)[1]+(y)[1])>>1} +#define __AVG4(x, y) ((v4s) {((x)[0]+(y)[0])>>1, ((x)[1]+(y)[1])>>1, ((x)[2]+(y)[2])>>1, ((x)[3]+(y)[3])>>1} /* Average unsigned */ -#define __AVGU2(x, y) ((v2u) {((unsigned short)(x)[0]+(unsigned short)(y)[0])>>1, ((unsigned short)(x)[1]+(unsigned short)(y)[1])>>1} -#define __AVGU4(x, y) ((v4u) {((unsigned char)(x)[0]+(unsigned char)(y)[0])>>1, ((unsigned char)(x)[1]+(unsigned char)(y)[1])>>1, \ - ((unsigned char)(x)[2]+(unsigned char)(y)[2])>>1, ((unsigned char)(x)[3]+(unsigned char)(y)[3])>>1} +#define __AVGU2(x, y) ((v2u) {((unsigned short)(x)[0]+(unsigned short)(y)[0])>>1, ((unsigned short)(x)[1]+(unsigned short)(y)[1])>>1} +#define __AVGU4(x, y) ((v4u) {((unsigned char)(x)[0]+(unsigned char)(y)[0])>>1, ((unsigned char)(x)[1]+(unsigned char)(y)[1])>>1, \ + ((unsigned char)(x)[2]+(unsigned char)(y)[2])>>1, ((unsigned char)(x)[3]+(unsigned char)(y)[3])>>1} /* Bitwise and */ -#define __AND2(x, y) ((v2s) {(x)[0]&(y)[0], (x)[1]&(y)[1]} -#define __AND4(x, y) ((v4s) {(x)[0]&(y)[0], (x)[1]&(y)[1], (x)[2]&(y)[2], (x)[3]&(y)[3]} +#define __AND2(x, y) ((v2s) {(x)[0]&(y)[0], (x)[1]&(y)[1]} +#define __AND4(x, y) ((v4s) {(x)[0]&(y)[0], (x)[1]&(y)[1], (x)[2]&(y)[2], (x)[3]&(y)[3]} /* Bitwise or */ -#define __OR2(x, y) ((v2s) {(x)[0]|(y)[0], (x)[1]|(y)[1]} -#define __OR4(x, y) ((v4s) {(x)[0]|(y)[0], (x)[1]|(y)[1], (x)[2]|(y)[2], (x)[3]|(y)[3]} +#define __OR2(x, y) ((v2s) {(x)[0]|(y)[0], (x)[1]|(y)[1]} +#define __OR4(x, y) ((v4s) {(x)[0]|(y)[0], (x)[1]|(y)[1], (x)[2]|(y)[2], (x)[3]|(y)[3]} /* Bitwise exor */ -#define __EXOR2(x, y) ((v2s) {(x)[0]^(y)[0], (x)[1]^(y)[1]} -#define __EXOR4(x, y) ((v4s) {(x)[0]^(y)[0], (x)[1]^(y)[1], (x)[2]^(y)[2], (x)[3]^(y)[3]} +#define __EXOR2(x, y) ((v2s) {(x)[0]^(y)[0], (x)[1]^(y)[1]} +#define __EXOR4(x, y) ((v4s) {(x)[0]^(y)[0], (x)[1]^(y)[1], (x)[2]^(y)[2], (x)[3]^(y)[3]} /* Logical shift right */ -#define __SRL2(x, y) ((v2u) {((unsigned short)(x)[0]>>(unsigned short)(y)[0]), ((unsigned short)(x)[1]>>(unsigned short)(y)[1])} -#define __SRL4(x, y) ((v4u) {((unsigned char)(x)[0]>>(unsigned char)(y)[0]), ((unsigned char)(x)[1]>>(unsigned char)(y)[1]), \ - ((unsigned char)(x)[2]>>(unsigned char)(y)[2]), ((unsigned char)(x)[3]>>(unsigned char)(y)[3])} +#define __SRL2(x, y) ((v2u) {((unsigned short)(x)[0]>>(unsigned short)(y)[0]), ((unsigned short)(x)[1]>>(unsigned short)(y)[1])} +#define __SRL4(x, y) ((v4u) {((unsigned char)(x)[0]>>(unsigned char)(y)[0]), ((unsigned char)(x)[1]>>(unsigned char)(y)[1]), \ + ((unsigned char)(x)[2]>>(unsigned char)(y)[2]), ((unsigned char)(x)[3]>>(unsigned char)(y)[3])} /* Arithmetic shift right */ -#define __SRA2(x, y) ((v2s) {((signed short)(x)[0]>>(signed short)(y)[0]), ((signed short)(x)[1]>>(signed short)(y)[1])} -#define __SRA4(x, y) ((v4s) {((signed char)(x)[0]>>(signed char)(y)[0]), ((signed char)(x)[1]>>(signed char)(y)[1]), \ - ((signed char)(x)[2]>>(signed char)(y)[2]), ((signed char)(x)[3]>>(signed char)(y)[3])} +#define __SRA2(x, y) ((v2s) {((signed short)(x)[0]>>(signed short)(y)[0]), ((signed short)(x)[1]>>(signed short)(y)[1])} +#define __SRA4(x, y) ((v4s) {((signed char)(x)[0]>>(signed char)(y)[0]), ((signed char)(x)[1]>>(signed char)(y)[1]), \ + ((signed char)(x)[2]>>(signed char)(y)[2]), ((signed char)(x)[3]>>(signed char)(y)[3])} /* Logical shift left */ -#define __SLL2(x, y) ((v2s) {(x)[0]<<(y)[0], (x)[1]<<(y)[1]} -#define __SLL4(x, y) ((v4s) {(x)[0]<<(y)[0], (x)[1]<<(y)[1], (x)[2]<<(y)[2], (x)[3]<<(y)[3]} +#define __SLL2(x, y) ((v2s) {(x)[0]<<(y)[0], (x)[1]<<(y)[1]} +#define __SLL4(x, y) ((v4s) {(x)[0]<<(y)[0], (x)[1]<<(y)[1], (x)[2]<<(y)[2], (x)[3]<<(y)[3]} /* Mac */ -#define __MAC(Acc, x, y) ((Acc) + ((x) * (y))) -#define __MSU(Acc, x, y) ((Acc) - ((x) * (y))) +#define __MAC(Acc, x, y) ((Acc) + ((x) * (y))) +#define __MSU(Acc, x, y) ((Acc) - ((x) * (y))) -#define __MACS(Acc, x, y) ((Acc) + ((short int) (x) * (short int) (y))) -#define __MACHHS(Acc, x, y) ((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) -#define __MACU(Acc, x, y) ((Acc) + ((unsigned short int) (x) * (unsigned short int) (y))) -#define __MACHHU(Acc, x, y) ((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) +#define __MACS(Acc, x, y) ((Acc) + ((short int) (x) * (short int) (y))) +#define __MACHHS(Acc, x, y) ((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) +#define __MACU(Acc, x, y) ((Acc) + ((unsigned short int) (x) * (unsigned short int) (y))) +#define __MACHHU(Acc, x, y) ((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) -#define __MACSN(Acc, x, y, n) (((Acc) + ((short int) (x) * (short int) (y)))>>(n)) -#define __MACUN(Acc, x, y, n) (((Acc) + ((unsigned short int) (x) * (unsigned short int) (y)))>>(n)) -#define __MACSRN(Acc, x, y, n) ((((Acc) + ((short int) (x) * (short int) (y))) + (1<<((n)-1))) >> (n)) -#define __MACURN(Acc, x, y, n) ((((Acc) + ((unsigned short int) (x) * (unsigned short int) (y))) + (1<<((n)-1))) >> (n)) +#define __MACSN(Acc, x, y, n) (((Acc) + ((short int) (x) * (short int) (y)))>>(n)) +#define __MACUN(Acc, x, y, n) (((Acc) + ((unsigned short int) (x) * (unsigned short int) (y)))>>(n)) +#define __MACSRN(Acc, x, y, n) ((((Acc) + ((short int) (x) * (short int) (y))) + (1<<((n)-1))) >> (n)) +#define __MACURN(Acc, x, y, n) ((((Acc) + ((unsigned short int) (x) * (unsigned short int) (y))) + (1<<((n)-1))) >> (n)) -#define __MACHHSN(Acc, x, y, n) (((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) >> (n)) -#define __MACHHUN(Acc, x, y, n) (((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) >> (n)) -#define __MACHHSRN(Acc, x, y, n) ((((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) + (1<<((n)-1))) >> (n)) -#define __MACHHURN(Acc, x, y, n) ((((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) + (n))) +#define __MACHHSN(Acc, x, y, n) (((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) >> (n)) +#define __MACHHUN(Acc, x, y, n) (((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) >> (n)) +#define __MACHHSRN(Acc, x, y, n) ((((Acc) + ((short int) ((x)>>16) * (short int) ((y)>>16))) + (1<<((n)-1))) >> (n)) +#define __MACHHURN(Acc, x, y, n) ((((Acc) + ((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) + (n))) /* Multiplications */ -#define __MULS(x, y) (((short int) (x) * (short int) (y))) -#define __MULU(x, y) (((unsigned short int) (x) * (unsigned short int) (y))) -#define __MULHHS(x, y) (((short int) ((x)>>16) * (short int) ((y)>>16))) -#define __MULHHU(x, y) (((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) +#define __MULS(x, y) (((short int) (x) * (short int) (y))) +#define __MULU(x, y) (((unsigned short int) (x) * (unsigned short int) (y))) +#define __MULHHS(x, y) (((short int) ((x)>>16) * (short int) ((y)>>16))) +#define __MULHHU(x, y) (((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16))) -#define __MULSN(x, y, n) (((short int) (x) * (short int) (y))>>(n)) -#define __MULSRN(x, y, n) ((((short int) (x) * (short int) (y)) + (1<<((n)-1)))>>(n)) -#define __MULUN(x, y, n) (((unsigned short int) (x) * (unsigned short int) (y))>>(n)) -#define __MULURN(x, y, n) ((((unsigned short int) (x) * (unsigned short int) (y)) + (1<<((n)-1)))>>(n)) +#define __MULSN(x, y, n) (((short int) (x) * (short int) (y))>>(n)) +#define __MULSRN(x, y, n) ((((short int) (x) * (short int) (y)) + (1<<((n)-1)))>>(n)) +#define __MULUN(x, y, n) (((unsigned short int) (x) * (unsigned short int) (y))>>(n)) +#define __MULURN(x, y, n) ((((unsigned short int) (x) * (unsigned short int) (y)) + (1<<((n)-1)))>>(n)) -#define __MULHHSN(x, y, n) ((((short int) ((x)>>16) * (short int) ((y)>>16)))>>(n)) -#define __MULHHSRN(x, y, n) (((((short int) ((x)>>16) * (short int) ((y)>>16)))+(1<<((n)-1)))>>(n)) -#define __MULHHUN(x, y, n) ((((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16)))>>(n)) -#define __MULHHURN(x, y, n) (((((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16)))+(1<<((n)-1)))>>(n)) +#define __MULHHSN(x, y, n) ((((short int) ((x)>>16) * (short int) ((y)>>16)))>>(n)) +#define __MULHHSRN(x, y, n) (((((short int) ((x)>>16) * (short int) ((y)>>16)))+(1<<((n)-1)))>>(n)) +#define __MULHHUN(x, y, n) ((((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16)))>>(n)) +#define __MULHHURN(x, y, n) (((((unsigned short int) ((x)>>16) * (unsigned short int) ((y)>>16)))+(1<<((n)-1)))>>(n)) /* Vectorial product and sum of products */ -#define __DOTP2(x, y) ( (short)(x)[0]*(short)(y)[0] + (short)(x)[1]*(short)(y)[1]) -#define __DOTPU2(x, y) ( (unsigned short)(x)[0]*(unsigned short)(y)[0] + (unsigned short)(x)[1]*(unsigned short)(y)[1]) -#define __DOTPUS2(x, y) ( (unsigned short)(x)[0]*(short)(y)[0] + (unsigned short)(x)[1]*(short)(y)[1]) - -#define __DOTPSC2(x, y) ( (short)(x)[0]*(short)(y) + (short)(x)[1]*(short)(y)) -#define __DOTPUSC2(x, y) ( (unsigned short)(x)[0]*(unsigned short)(y) + (unsigned short)(x)[1]*(unsigned short)(y)) -#define __DOTPUSSC2(x, y) ( (unsigned short)(x)[0]*(short)(y) + (unsigned short)(x)[1]*(short)(y)) - -#define __SUMDOTP2(x, y, z) ((z)+(short)(x)[0]*(short)(y)[0] + (short)(x)[1]*(short)(y)[1]) -#define __SUMDOTPU2(x, y, z) ((z)+(unsigned short)(x)[0]*(unsigned short)(y)[0] + (unsigned short)(x)[1]*(unsigned short)(y)[1]) -#define __SUMDOTPUS2(x, y, z) ((z)+(unsigned short)(x)[0]*(short)(y)[0] + (unsigned short)(x)[1]*(short)(y)[1]) - -#define __SUMDOTPSC2(x, y, z) ((z)+(short)(x)[0]*(short)(y) + (short)(x)[1]*(short)(y)) -#define __SUMDOTPUSC2(x, y, z) ((z)+(unsigned short)(x)[0]*(unsigned short)(y) + (unsigned short)(x)[1]*(unsigned short)(y)) -#define __SUMDOTPUSSC2(x, y, z) ((z)+(unsigned short)(x)[0]*(short)(y) + (unsigned short)(x)[1]*(short)(y)) - -#define __DOTP4(x, y) ( (char)(x)[0]*(char)(y)[0] + (char)(x)[1]*(char)(y)[1] + (char)(x)[2]*(char)(y)[2] + (char)(x)[3]*(char)(y)[3]) -#define __DOTPU4(x, y) ( (unsigned char)(x)[0]*(unsigned char)(y)[0] + (unsigned char)(x)[1]*(unsigned char)(y)[1] + \ - (unsigned char)(x)[2]*(unsigned char)(y)[2] + (unsigned char)(x)[3]*(unsigned char)(y)[3]) -#define __DOTPUS4(x, y) ( (unsigned char)(x)[0]*(char)(y)[0] + (unsigned char)(x)[1]*(char)(y)[1] + \ - (unsigned char)(x)[2]*(char)(y)[2] + (unsigned char)(x)[3]*(char)(y)[3]) - -#define __DOTPSC4(x, y) ( (char)(x)[0]*(char)(y) + (char)(x)[1]*(char)(y) + (char)(x)[2]*(char)(y) + (char)(x)[3]*(char)(y)) -#define __DOTPUSC4(x, y) ( (unsigned char)(x)[0]*(unsigned char)(y) + (unsigned char)(x)[1]*(unsigned char)(y) + \ - (unsigned char)(x)[2]*(unsigned char)(y) + (unsigned char)(x)[3]*(unsigned char)(y)) -#define __DOTPUSSC4(x, y) ( (unsigned char)(x)[0]*(char)(y) + (unsigned char)(x)[1]*(char)(y) + \ - (unsigned char)(x)[2]*(char)(y) + (unsigned char)(x)[3]*(char)(y)) - -#define __SUMDOTP4(x, y, z) ((z)+(char)(x)[0]*(char)(y)[0] + (char)(x)[1]*(char)(y)[1] + (char)(x)[2]*(char)(y)[2] + (char)(x)[3]*(char)(y)[3]) -#define __SUMDOTPU4(x, y, z) ((z)+(unsigned char)(x)[0]*(unsigned char)(y)[0] + (unsigned char)(x)[1]*(unsigned char)(y)[1] + \ - (unsigned char)(x)[2]*(unsigned char)(y)[2] + (unsigned char)(x)[3]*(unsigned char)(y)[3]) -#define __SUMDOTPUS4(x, y, z) ((z)+(unsigned char)(x)[0]*(char)(y)[0] + (unsigned char)(x)[1]*(char)(y)[1] + \ - (unsigned char)(x)[2]*(char)(y)[2] + (unsigned char)(x)[3]*(char)(y)[3]) - -#define __SUMDOTPSC4(x, y, z) ((z)+(char)(x)[0]*(char)(y) + (char)(x)[1]*(char)(y) + (char)(x)[2]*(char)(y) + (char)(x)[3]*(char)(y)) -#define __SUMDOTPUSC4(x, y, z) ((z)+(unsigned char)(x)[0]*(unsigned char)(y) + (unsigned char)(x)[1]*(unsigned char)(y) + \ - (unsigned char)(x)[2]*(unsigned char)(y) + (unsigned char)(x)[3]*(unsigned char)(y)) -#define __SUMDOTPUSSC4(x, y, z) ((z)+(unsigned char)(x)[0]*(char)(y) + (unsigned char)(x)[1]*(char)(y) + \ - (unsigned char)(x)[2]*(char)(y) + (unsigned char)(x)[3]*(char)(y)) +#define __DOTP2(x, y) ( (short)(x)[0]*(short)(y)[0] + (short)(x)[1]*(short)(y)[1]) +#define __DOTPU2(x, y) ( (unsigned short)(x)[0]*(unsigned short)(y)[0] + (unsigned short)(x)[1]*(unsigned short)(y)[1]) +#define __DOTPUS2(x, y) ( (unsigned short)(x)[0]*(short)(y)[0] + (unsigned short)(x)[1]*(short)(y)[1]) + +#define __DOTPSC2(x, y) ( (short)(x)[0]*(short)(y) + (short)(x)[1]*(short)(y)) +#define __DOTPUSC2(x, y) ( (unsigned short)(x)[0]*(unsigned short)(y) + (unsigned short)(x)[1]*(unsigned short)(y)) +#define __DOTPUSSC2(x, y) ( (unsigned short)(x)[0]*(short)(y) + (unsigned short)(x)[1]*(short)(y)) + +#define __SUMDOTP2(x, y, z) ((z)+(short)(x)[0]*(short)(y)[0] + (short)(x)[1]*(short)(y)[1]) +#define __SUMDOTPU2(x, y, z) ((z)+(unsigned short)(x)[0]*(unsigned short)(y)[0] + (unsigned short)(x)[1]*(unsigned short)(y)[1]) +#define __SUMDOTPUS2(x, y, z) ((z)+(unsigned short)(x)[0]*(short)(y)[0] + (unsigned short)(x)[1]*(short)(y)[1]) + +#define __SUMDOTPSC2(x, y, z) ((z)+(short)(x)[0]*(short)(y) + (short)(x)[1]*(short)(y)) +#define __SUMDOTPUSC2(x, y, z) ((z)+(unsigned short)(x)[0]*(unsigned short)(y) + (unsigned short)(x)[1]*(unsigned short)(y)) +#define __SUMDOTPUSSC2(x, y, z) ((z)+(unsigned short)(x)[0]*(short)(y) + (unsigned short)(x)[1]*(short)(y)) + +#define __DOTP4(x, y) ( (char)(x)[0]*(char)(y)[0] + (char)(x)[1]*(char)(y)[1] + (char)(x)[2]*(char)(y)[2] + (char)(x)[3]*(char)(y)[3]) +#define __DOTPU4(x, y) ( (unsigned char)(x)[0]*(unsigned char)(y)[0] + (unsigned char)(x)[1]*(unsigned char)(y)[1] + \ + (unsigned char)(x)[2]*(unsigned char)(y)[2] + (unsigned char)(x)[3]*(unsigned char)(y)[3]) +#define __DOTPUS4(x, y) ( (unsigned char)(x)[0]*(char)(y)[0] + (unsigned char)(x)[1]*(char)(y)[1] + \ + (unsigned char)(x)[2]*(char)(y)[2] + (unsigned char)(x)[3]*(char)(y)[3]) + +#define __DOTPSC4(x, y) ( (char)(x)[0]*(char)(y) + (char)(x)[1]*(char)(y) + (char)(x)[2]*(char)(y) + (char)(x)[3]*(char)(y)) +#define __DOTPUSC4(x, y) ( (unsigned char)(x)[0]*(unsigned char)(y) + (unsigned char)(x)[1]*(unsigned char)(y) + \ + (unsigned char)(x)[2]*(unsigned char)(y) + (unsigned char)(x)[3]*(unsigned char)(y)) +#define __DOTPUSSC4(x, y) ( (unsigned char)(x)[0]*(char)(y) + (unsigned char)(x)[1]*(char)(y) + \ + (unsigned char)(x)[2]*(char)(y) + (unsigned char)(x)[3]*(char)(y)) + +#define __SUMDOTP4(x, y, z) ((z)+(char)(x)[0]*(char)(y)[0] + (char)(x)[1]*(char)(y)[1] + (char)(x)[2]*(char)(y)[2] + (char)(x)[3]*(char)(y)[3]) +#define __SUMDOTPU4(x, y, z) ((z)+(unsigned char)(x)[0]*(unsigned char)(y)[0] + (unsigned char)(x)[1]*(unsigned char)(y)[1] + \ + (unsigned char)(x)[2]*(unsigned char)(y)[2] + (unsigned char)(x)[3]*(unsigned char)(y)[3]) +#define __SUMDOTPUS4(x, y, z) ((z)+(unsigned char)(x)[0]*(char)(y)[0] + (unsigned char)(x)[1]*(char)(y)[1] + \ + (unsigned char)(x)[2]*(char)(y)[2] + (unsigned char)(x)[3]*(char)(y)[3]) + +#define __SUMDOTPSC4(x, y, z) ((z)+(char)(x)[0]*(char)(y) + (char)(x)[1]*(char)(y) + (char)(x)[2]*(char)(y) + (char)(x)[3]*(char)(y)) +#define __SUMDOTPUSC4(x, y, z) ((z)+(unsigned char)(x)[0]*(unsigned char)(y) + (unsigned char)(x)[1]*(unsigned char)(y) + \ + (unsigned char)(x)[2]*(unsigned char)(y) + (unsigned char)(x)[3]*(unsigned char)(y)) +#define __SUMDOTPUSSC4(x, y, z) ((z)+(unsigned char)(x)[0]*(char)(y) + (unsigned char)(x)[1]*(char)(y) + \ + (unsigned char)(x)[2]*(char)(y) + (unsigned char)(x)[3]*(char)(y)) #ifdef ARCHI_CORE_HAS_CPLX /* Complex Multiplication, Q15x15 into Q15, with optional post scaling by 1 or 2 */ -#define __CPLXMULS(x, y) ((v2s) {(signed short) ((((long long) (x)[0]*(long long) (y)[0]) - ((long long) (x)[1]*(long long) (y)[1]))>>15), \ - (signed short) ((((long long) (x)[0]*(long long) (y)[1]) + ((long long) (x)[1]*(long long) (y)[0]))>>15)}) -#define __CPLXMULSDIV2(x, y) (__CPLXMULS(x, y)>>(v2s){1,1}) -#define __CPLXMULSDIV4(x, y) (__CPLXMULS(x, y)>>(v2s){2,2}) +#define __CPLXMULS(x, y) ((v2s) {(signed short) ((((long long) (x)[0]*(long long) (y)[0]) - ((long long) (x)[1]*(long long) (y)[1]))>>15), \ + (signed short) ((((long long) (x)[0]*(long long) (y)[1]) + ((long long) (x)[1]*(long long) (y)[0]))>>15)}) +#define __CPLXMULSDIV2(x, y) (__CPLXMULS(x, y)>>(v2s){1,1}) +#define __CPLXMULSDIV4(x, y) (__CPLXMULS(x, y)>>(v2s){2,2}) /* Complex conjugate */ -#define __CPLXCONJ(x) ((v2s) {(x)[0], -(x)[1]}) +#define __CPLXCONJ(x) ((v2s) {(x)[0], -(x)[1]}) /* Complex substration, result rotated by -pi/2 */ -#define __SUB2ROTMJ(x, y) ((v2s) {(x)[1]-(y)[1], (y)[0]-(x)[0]}) +#define __SUB2ROTMJ(x, y) ((v2s) {(x)[1]-(y)[1], (y)[0]-(x)[0]}) /* Complex addition with post scaling by 1 or 2 */ -#define __ADD2DIV2(x, y) (((x)+(y))>>(v2s) {1, 1}) -#define __ADD2DIV4(x, y) (((x)+(y))>>(v2s) {2, 2}) +#define __ADD2DIV2(x, y) (((x)+(y))>>(v2s) {1, 1}) +#define __ADD2DIV4(x, y) (((x)+(y))>>(v2s) {2, 2}) -#define __ADD4DIV2(x, y) (((x)+(y))>>(v4s) {1, 1, 1, 1}) -#define __ADD4DIV4(x, y) (((x)+(y))>>(v4s) {2, 2, 2, 2}) +#define __ADD4DIV2(x, y) (((x)+(y))>>(v4s) {1, 1, 1, 1}) +#define __ADD4DIV4(x, y) (((x)+(y))>>(v4s) {2, 2, 2, 2}) /* Complex substraction with post scaling by 1 or 2 */ -#define __SUB2DIV2(x, y) (((x)-(y))>>(v2s) {1, 1}) -#define __SUB2DIV4(x, y) (((x)-(y))>>(v2s) {2, 2}) +#define __SUB2DIV2(x, y) (((x)-(y))>>(v2s) {1, 1}) +#define __SUB2DIV4(x, y) (((x)-(y))>>(v2s) {2, 2}) -#define __SUB4DIV2(x, y) (((x)-(y))>>(v4s) {1, 1, 1, 1}) -#define __SUB4DIV4(x, y) (((x)-(y))>>(v4s) {2, 2, 2, 2}) +#define __SUB4DIV2(x, y) (((x)-(y))>>(v4s) {1, 1, 1, 1}) +#define __SUB4DIV4(x, y) (((x)-(y))>>(v4s) {2, 2, 2, 2}) /* Complex subtraction with post scaling by 1 or 2 */ -#define __SUB2DIV2(x, y) (((x)-(y))>>(v2s) {1, 1}) -#define __SUB2DIV4(x, y) (((x)-(y))>>(v2s) {2, 2}) +#define __SUB2DIV2(x, y) (((x)-(y))>>(v2s) {1, 1}) +#define __SUB2DIV4(x, y) (((x)-(y))>>(v2s) {2, 2}) /* Viterbi Max and Viterbi Select, pair of Q15 */ -#define __VITMAX(x, y) (_VitT1_Flag=((x)[1]<=(y)[1])?1:0, _VitT0_Flag=((x)[0]<=(y)[0])?1:0,\ - (v2s) {((x)[0]>(y)[0])?(x)[0]:(y)[0], ((x)[1]>(y)[1])?(x)[1]:(y)[1]}) -#define __VITSEL(x, y) (v2s) {(_VitT0_Flag?(y)[0]:(x)[0])<<1|_VitT0_Flag, (_VitT1_Flag?(y)[1]:(x)[1])<<1|_VitT1_Flag} +#define __VITMAX(x, y) (_VitT1_Flag=((x)[1]<=(y)[1])?1:0, _VitT0_Flag=((x)[0]<=(y)[0])?1:0,\ + (v2s) {((x)[0]>(y)[0])?(x)[0]:(y)[0], ((x)[1]>(y)[1])?(x)[1]:(y)[1]}) +#define __VITSEL(x, y) (v2s) {(_VitT0_Flag?(y)[0]:(x)[0])<<1|_VitT0_Flag, (_VitT1_Flag?(y)[1]:(x)[1])<<1|_VitT1_Flag} #endif /* Position of the most significant bit of x */ -#define __FL1(x) (31 - __builtin_clz((x))) +#define __FL1(x) (31 - __builtin_clz((x))) /* Number of sign bits */ -#define __CLB(x) (__builtin_clrsb((x))) +#define __CLB(x) (__builtin_clrsb((x))) /* Bit set */ -#define __BITSET(x, size, off) ((x) | (((1<<(size))-1)<<(off))) -#define __BITSET_R(x, size, off) ((x) | (((1<<(size))-1)<<(off))) -#define __BITSET_R_SAFE(x, size, off) ((x) | (((1<<((size)&0x1F))-1)<<((off)&0x1F))) +#define __BITSET(x, size, off) ((x) | (((1<<(size))-1)<<(off))) +#define __BITSET_R(x, size, off) ((x) | (((1<<(size))-1)<<(off))) +#define __BITSET_R_SAFE(x, size, off) ((x) | (((1<<((size)&0x1F))-1)<<((off)&0x1F))) /* Bit clr */ -#define __BITCLR(x, size, off) ((x) & ~(((1<<(size))-1)<<(off))) -#define __BITCLR_R(x, size, off) ((x) & ~(((1<<(size))-1)<<(off))) -#define __BITCLR_R_SAFE(x, size, off) ((x) & ~(((1<<((size)&0x1F))-1)<<((off)&0x1F))) +#define __BITCLR(x, size, off) ((x) & ~(((1<<(size))-1)<<(off))) +#define __BITCLR_R(x, size, off) ((x) & ~(((1<<(size))-1)<<(off))) +#define __BITCLR_R_SAFE(x, size, off) ((x) & ~(((1<<((size)&0x1F))-1)<<((off)&0x1F))) /* Bit Extraction */ -#define __BITEXTRACT(x, size, off) (((((x)>>(off))&((unsigned int)(1<<(size))-1))<<(32-(size)))>>(32-(size))) -#define __BITEXTRACTU(x, size, off) (((x)>>(off))&((unsigned int)(1<<(size))-1)) +#define __BITEXTRACT(x, size, off) (((((x)>>(off))&((unsigned int)(1<<(size))-1))<<(32-(size)))>>(32-(size))) +#define __BITEXTRACTU(x, size, off) (((x)>>(off))&((unsigned int)(1<<(size))-1)) -#define __BITEXTRACT_R(x, size, off) (((((x)>>(off))&((unsigned int)(1<<(size))-1))<<(32-(size)))>>(32-(size))) -#define __BITEXTRACTU_R(x, size, off) (((x)>>(off))&((unsigned int)(1<<(size))-1)) +#define __BITEXTRACT_R(x, size, off) (((((x)>>(off))&((unsigned int)(1<<(size))-1))<<(32-(size)))>>(32-(size))) +#define __BITEXTRACTU_R(x, size, off) (((x)>>(off))&((unsigned int)(1<<(size))-1)) -#define __BITEXTRACT_R_SAFE(x, size, off) (((((x)>>((off)&0x1F))&((unsigned int)(1<<((((size)>32)?32:(size))))-1))<<(32-((((size)>32)?32:(size)))))>>(32-((((size)>32)?32:(size))))) -#define __BITEXTRACTU_R_SAFE(x, size, off) (((x)>>((off)&0x1F))&((unsigned int)(1<<((((size)>32)?32:(size))))-1)) +#define __BITEXTRACT_R_SAFE(x, size, off) (((((x)>>((off)&0x1F))&((unsigned int)(1<<((((size)>32)?32:(size))))-1))<<(32-((((size)>32)?32:(size)))))>>(32-((((size)>32)?32:(size))))) +#define __BITEXTRACTU_R_SAFE(x, size, off) (((x)>>((off)&0x1F))&((unsigned int)(1<<((((size)>32)?32:(size))))-1)) /* Bit insertion */ -#define __BITINSERT(dst, src, size, off) (((dst) & ~(((1<<(size))-1)<<(off))) | (((src) & ((1<<(size))-1))<<(off))) -#define __BITINSERT_R(dst, src, size, off) (((dst) & ~(((1<<(size))-1)<<(off))) | (((src) & ((1<<(size))-1))<<(off))) -#define __BITINSERT_R_SAFE(dst, src, size, off) (((dst) & ~(((1<<(((size)>32)?32:(size)))-1)<<((off)&0x1F))) | (((src) & ((1<<(((size)>32)?32:(size)))-1))<<((off)&0x1F))) +#define __BITINSERT(dst, src, size, off) (((dst) & ~(((1<<(size))-1)<<(off))) | (((src) & ((1<<(size))-1))<<(off))) +#define __BITINSERT_R(dst, src, size, off) (((dst) & ~(((1<<(size))-1)<<(off))) | (((src) & ((1<<(size))-1))<<(off))) +#define __BITINSERT_R_SAFE(dst, src, size, off) (((dst) & ~(((1<<(((size)>32)?32:(size)))-1)<<((off)&0x1F))) | (((src) & ((1<<(((size)>32)?32:(size)))-1))<<((off)&0x1F))) /* 1 bit rotation to the right, 32 bits input */ -#define __ROTR(x) ((((x)>>1)&0x7FFFFFFF) | ((x)<<31)) +#define __ROTR(x) ((((x)>>1)&0x7FFFFFFF) | ((x)<<31)) /* Add with normalization */ -#define __ADDNORMU(x, y, scale) ((unsigned int)((x) + (y))>>(scale)) -#define __ADDNORMU_REG(x, y, scale) ((unsigned int)((x) + (y))>>(scale)) -#define __ADDNORM(x, y, scale) ((int)((x) + (y))>>(scale)) -#define __ADDNORM_REG(x, y, scale) ((int)((x) + (y))>>(scale)) +#define __ADDNORMU(x, y, scale) ((unsigned int)((x) + (y))>>(scale)) +#define __ADDNORMU_REG(x, y, scale) ((unsigned int)((x) + (y))>>(scale)) +#define __ADDNORM(x, y, scale) ((int)((x) + (y))>>(scale)) +#define __ADDNORM_REG(x, y, scale) ((int)((x) + (y))>>(scale)) /* Add with normalization and rounding */ -#define __ADDROUNDNORMU(x, y, scale) ((unsigned int)((x) + (y) + (1<<((scale)-1)))>>(scale)) -#define __ADDROUNDNORMU_REG(x, y, scale) ((unsigned int)((x) + (y) + (1<<((scale)-1)))>>(scale)) -#define __ADDROUNDNORM(x, y, scale) ((int)((x) + (y) + (1<<((scale)-1)))>>(scale)) -#define __ADDROUNDNORM_REG(x, y, scale) ((int)((x) + (y) + (1<<((scale)-1)))>>(scale)) +#define __ADDROUNDNORMU(x, y, scale) ((unsigned int)((x) + (y) + (1<<((scale)-1)))>>(scale)) +#define __ADDROUNDNORMU_REG(x, y, scale) ((unsigned int)((x) + (y) + (1<<((scale)-1)))>>(scale)) +#define __ADDROUNDNORM(x, y, scale) ((int)((x) + (y) + (1<<((scale)-1)))>>(scale)) +#define __ADDROUNDNORM_REG(x, y, scale) ((int)((x) + (y) + (1<<((scale)-1)))>>(scale)) /* Sub with normalization */ -#define __SUBNORMU(x, y, scale) ((unsigned int)((x) - (y))>>(scale)) -#define __SUBNORMU_REG(x, y, scale) ((unsigned int)((x) - (y))>>(scale)) -#define __SUBNORM(x, y, scale) ((int)((x) - (y))>>(scale)) -#define __SUBNORM_REG(x, y, scale) ((int)((x) - (y))>>(scale)) +#define __SUBNORMU(x, y, scale) ((unsigned int)((x) - (y))>>(scale)) +#define __SUBNORMU_REG(x, y, scale) ((unsigned int)((x) - (y))>>(scale)) +#define __SUBNORM(x, y, scale) ((int)((x) - (y))>>(scale)) +#define __SUBNORM_REG(x, y, scale) ((int)((x) - (y))>>(scale)) /* Sub with normalization and rounding */ -#define __SUBROUNDNORMU(x, y, scale) ((unsigned int)((x) - (y) + (1<<((scale)-1)))>>(scale)) -#define __SUBROUNDNORMU_REG(x, y, scale) ((unsigned int)((x) - (y) + (1<<((scale)-1)))>>(scale)) -#define __SUBROUNDNORM(x, y, scale) ((int)((x) - (y) + (1<<((scale)-1)))>>(scale)) -#define __SUBROUNDNORM_REG(x, y, scale) ((int)((x) - (y) + (1<<((scale)-1)))>>(scale)) +#define __SUBROUNDNORMU(x, y, scale) ((unsigned int)((x) - (y) + (1<<((scale)-1)))>>(scale)) +#define __SUBROUNDNORMU_REG(x, y, scale) ((unsigned int)((x) - (y) + (1<<((scale)-1)))>>(scale)) +#define __SUBROUNDNORM(x, y, scale) ((int)((x) - (y) + (1<<((scale)-1)))>>(scale)) +#define __SUBROUNDNORM_REG(x, y, scale) ((int)((x) - (y) + (1<<((scale)-1)))>>(scale)) /* Normalization and rounding */ -#define __ROUNDNORMU(x, scale) ((unsigned int)((x) + (1<<((scale)-1)))>>(scale)) -#define __ROUNDNORMU_REG(x, scale) ((unsigned int)((x) + (1<<((scale)-1)))>>(scale)) -#define __ROUNDNORM(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) -#define __ROUNDNORM_REG(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) -#define __COREID() 0 -#define __CLUSTERID() 0 -#define __NCORE() 1 - -#define __SPRWRITE(x, y) -#define __SPRREAD(x) ((int) 0) -#define __SPRREAD_VOL(x) ((int) 0) - -#define __READ_BASE_OFF(base, off) ((int) 0) -#define __WRITE_BASE_OFF(base, off, val) - -#define __READ_BASE_OFF_VOL(base, off) ((int) 0) -#define __READ_BASE_OFF_HALF_VOL(base, off) ((int) 0) -#define __READ_BASE_OFF_BYTE_VOL(base, off) ((int) 0) - -#define __WRITE_BASE_OFF_VOL(x, base, off) -#define __WRITE_BASE_OFF_HALF_VOL(x, base, off) -#define __WRITE_BASE_OFF_BYTE_VOL(x, base, off) +#define __ROUNDNORMU(x, scale) ((unsigned int)((x) + (1<<((scale)-1)))>>(scale)) +#define __ROUNDNORMU_REG(x, scale) ((unsigned int)((x) + (1<<((scale)-1)))>>(scale)) +#define __ROUNDNORM(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) +#define __ROUNDNORM_REG(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) + +#define __SPRWRITE(x, y) (*(volatile unsigned int *)(long)(x) = (y)) +#define __SPRREAD(x) (*(unsigned int *)(long)(x)) +#define __SPRREAD_VOL(x) (*(volatile unsigned int *)(long)(x)) + +#define __READ_BASE_OFF(base, off) (*(unsigned int *)(long)((base) + (off))) +#define __WRITE_BASE_OFF(base, off, val)(*(unsigned int *)(long)((base) + (off)) = (val)) + +#define __READ_BASE_OFF_VOL(base, off) (*(volatile unsigned int *)(long)((base) + (off))) +#define __READ_BASE_OFF_HALF_VOL(base, off) (*(volatile unsigned short int *)(long)((base) + (off))) +#define __READ_BASE_OFF_BYTE_VOL(base, off) (*(volatile unsigned char *)(long)((base) + (off))) + +#define __WRITE_BASE_OFF_VOL(x, base, off) (*(volatile unsigned int *)(long)((base) + (off)) = (x)) +#define __WRITE_BASE_OFF_HALF_VOL(x, base, off) (*(volatile unsigned short int *)(long)((base) + (off)) = (x)) +#define __WRITE_BASE_OFF_BYTE_VOL(x, base, off) (*(volatile unsigned char *)(long)((base) + (off)) = (x)) /* Utilities, Target independant */ -#define FIX2FP(Val, Precision) ((float) (Val) / (float) (1<<(Precision))) -#define FP2FIXR(Val, Precision) ((int)((Val)*((1 << (Precision))-1) + 0.5)) -#define FP2FIX(Val, Precision) ((int)((Val)*((1 << (Precision))-1))) +#define FIX2FP(Val, Precision) ((float) (Val) / (float) (1<<(Precision))) +#define FP2FIXR(Val, Precision) ((int)((Val)*((1 << (Precision))-1) + 0.5)) +#define FP2FIX(Val, Precision) ((int)((Val)*((1 << (Precision))-1))) + +#define HARTID_MASK 0xF +#define CLUSTERID_MASK 0x7E0 +#define CLUSTERID_SHIFT 5 + +#define __COREID() __SPRREAD_VOL(HARTID_MASK & RV_CSR_MHARTID) +#define __CLUSTERID() __SPRREAD_VOL((CLUSTERID_MASK & RV_CSR_MHARTID) >> CLUSTERID_SHIFT) +#define __NCORE() __SPRREAD_VOL(ARCHI_SOC_PERIPHERALS_ADDR + 0x3000 + 0x12) + #endif #endif diff --git a/tools/gap-configs/.sconsign.dblite b/tools/gap-configs/.sconsign.dblite deleted file mode 100644 index 1984abc3250982f46cf045ab62cfacafd110b90d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 275701 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 ds;fw Date: Fri, 12 Mar 2021 10:45:54 +0100 Subject: [PATCH 08/22] Fixing *.S files --- rtos/pulpos/common/kernel/crt0.S | 2 + rtos/pulpos/common/kernel/irq_asm.S | 2 +- rtos/pulpos/common/kernel/soc_event_v2_itc.S | 1 + rtos/pulpos/common/kernel/task_asm.S | 2 + rtos/pulpos/common/kernel/time_asm.S | 2 + rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S | 47 +++++++++++++++----- 6 files changed, 43 insertions(+), 13 deletions(-) diff --git a/rtos/pulpos/common/kernel/crt0.S b/rtos/pulpos/common/kernel/crt0.S index 9441de1f..a597e2a5 100644 --- a/rtos/pulpos/common/kernel/crt0.S +++ b/rtos/pulpos/common/kernel/crt0.S @@ -21,6 +21,8 @@ #include "archi/pulp.h" .section .text_l2 + .attribute arch, "rv32imc_xcorev2p0" + .global pos_init_entry pos_init_entry: diff --git a/rtos/pulpos/common/kernel/irq_asm.S b/rtos/pulpos/common/kernel/irq_asm.S index 9631f701..0cacceed 100644 --- a/rtos/pulpos/common/kernel/irq_asm.S +++ b/rtos/pulpos/common/kernel/irq_asm.S @@ -20,7 +20,7 @@ .section .text_l2 - + .attribute arch, "rv32i2p0_m2p0_c2p0_xcorev2p0" .global pos_irq_call_external_c_function pos_irq_call_external_c_function: diff --git a/rtos/pulpos/common/kernel/soc_event_v2_itc.S b/rtos/pulpos/common/kernel/soc_event_v2_itc.S index 350fd57b..01a67a35 100644 --- a/rtos/pulpos/common/kernel/soc_event_v2_itc.S +++ b/rtos/pulpos/common/kernel/soc_event_v2_itc.S @@ -22,6 +22,7 @@ .section .text_l2 + .attribute arch, "rv32i2p0_m2p0_c2p0_xcorev2p0" # # SOC event handler entry diff --git a/rtos/pulpos/common/kernel/task_asm.S b/rtos/pulpos/common/kernel/task_asm.S index 7616ae61..d30c4a63 100644 --- a/rtos/pulpos/common/kernel/task_asm.S +++ b/rtos/pulpos/common/kernel/task_asm.S @@ -22,6 +22,8 @@ .section .text_l2 + .attribute arch, "rv32i2p0_m2p0_c2p0_xcorev2p0" + .global pos_task_push_asm pos_task_push_asm: diff --git a/rtos/pulpos/common/kernel/time_asm.S b/rtos/pulpos/common/kernel/time_asm.S index 692f3444..a321ecea 100644 --- a/rtos/pulpos/common/kernel/time_asm.S +++ b/rtos/pulpos/common/kernel/time_asm.S @@ -22,6 +22,8 @@ .section .text_l2 + .attribute arch, "rv32i2p0_m2p0_c2p0_xcorev2p0" + .global pos_time_timer_handler_asm pos_time_timer_handler_asm: add sp, sp, -8 diff --git a/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S b/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S index fb946901..0bc70972 100644 --- a/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S +++ b/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S @@ -22,6 +22,8 @@ .section .cluster.text , "ax" + .attribute arch, "rv32i2p0_m2p0_c2p0_xcorev2p0" + .global pos_pe_start pos_pe_start: csrr a0, 0xF14 @@ -163,7 +165,11 @@ pos_master_sleep: #ifdef ARCHI_HAS_COREV //cv.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) lw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) - beqz x0, hal_itc_wait_for_interrupt + //beqz x0, hal_itc_wait_for_interrupt + bnez x0, L10 + wfi +L10: + #else p.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) #endif @@ -177,7 +183,11 @@ pos_push_event_to_fc_wait: #ifdef ARCHI_HAS_COREV //cv.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) lw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) - beqz x0, hal_itc_wait_for_interrupt + //beqz x0, hal_itc_wait_for_interrupt + bnez x0, L11 + wfi +L11: + #else p.elw x0, EU_CORE_EVENT_WAIT_CLEAR(s3) #endif @@ -199,11 +209,6 @@ pos_push_event_to_fc_wait: - - - - - @@ -230,7 +235,10 @@ pos_fork_return: #ifdef ARCHI_HAS_COREV //cv.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR + EU_BARRIER_SIZE(s2) lw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR + EU_BARRIER_SIZE(s2) - beqz t0, hal_itc_wait_for_interrupt + //beqz t0, hal_itc_wait_for_interrupt + bnez t0, L0 + wfi +L0: #else p.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR + EU_BARRIER_SIZE(s2) #endif /*ARCHI_HAS_COREV*/ @@ -238,7 +246,10 @@ pos_fork_return: #ifdef ARCHI_HAS_COREV //cv.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR(s2) lw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR(s2) - beqz t0, hal_itc_wait_for_interrupt + //beqz t0, hal_itc_wait_for_interrupt + bnez t0, L1 + wfi +L1: #else p.elw t0, EU_BARRIER_DEMUX_OFFSET + EU_HW_BARR_TRIGGER_WAIT_CLEAR(s2) #endif /*ARCHI_HAS_COREV*/ @@ -251,9 +262,16 @@ pos_wait_for_dispatch: //cv.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) //cv.elw a0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) lw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) - beqz t0, hal_itc_wait_for_interrupt + //beqz t0, hal_itc_wait_for_interrupt + bnez t0, L2 + wfi +L2: + lw a0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) - beqz a0, hal_itc_wait_for_interrupt + //beqz a0, hal_itc_wait_for_interrupt + bnez a0, L3 + wfi +L3: #else p.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) p.elw a0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) @@ -284,7 +302,12 @@ pos_set_slave_stack: #ifdef ARCHI_HAS_COREV //cv.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) lw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) - beqz t0, hal_itc_wait_for_interrupt +// beqz t0, hal_itc_wait_for_interrupt + bnez t0, L4 + wfi +L4: + + #else p.elw t0, EU_DISPATCH_DEMUX_OFFSET + EU_DISPATCH_FIFO_ACCESS(s2) #endif From 975dc6cdcf158f110786ac6bdeba5d920ebd403d Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Fri, 12 Mar 2021 12:04:59 +0100 Subject: [PATCH 09/22] New boot stimuli --- .../gvsoc/pulp/models/pulp/chips/pulp/rom.bin | Bin 1092 -> 1220 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp/rom.bin b/tools/gvsoc/pulp/models/pulp/chips/pulp/rom.bin index a934779bada8edbc7355c841b56daad60cf50c52..a97074ae847cf9722c0b4453f8ad7cf1998c136b 100644 GIT binary patch literal 1220 zcmY+Ee@I(b6vxj?^4?XaYvj?{g%)gHn~V&`TsGc1NcW<~M@w6(8=c!=f9zGI9n6)k z?4JoQc|J`X-888h`(vaciG;0xRLW%oXJV}JcVyZbIvoWk*&iFMe?(cwaOX)G?2ixJ z@45G!d-$HqZ3nfa9V7&h6ln()kq|&q9BM>D07rgw#S&88oaV{vaNs__rb8|9YP4iuwY#l~i4=?bn(vQo zu}t|a-@Uy{S^1OGUp3ESUN1|i(`q}g80jX~-pp#IQI2MVD~}siSE6gYJ$S9ITzCrE~6YEo$QIl{a5i}|;-PG)Qn)_MrF~vzYdqbsLGMcT`f$CM38;pDm2HN5eXV|3M5W9m;A!+f{{+{X zs*?@MR5neCWpL0j7a?U3I?uNKaLvAOa^}qRxk+v*(rM}aesE?Xy1cL~3=@gE(1%1B zbiADmMG8(3Vo!j!S#7C|noVWZ`P96cOXbv+d|jjOr4aww&Q3m)ue)+1pZQi?KXo+| z!3pi6R2Sy_k`dOR|A9OT(Dc;KN`a1iGJ+Gtjy&Zp}H35ysHweZ@`?ilx{ z#1m4>G>9zj>v~w(|D4%)5p1gv%Z7PwpwTrzS+EIr#6Drz8R0ojis(l@X_w~A?kX3H zfRlvdQRM>5u$-5qsk`JJZ2nDlOX`w?*9YCjHQ1t5w%UGsUrWg4-pa#%Zkm-nAth9R zQdv<8rmLH}kMVVQo=>y>L)C`ELX7Oa&f#OkapIWRP`E@BU$i$5MzI)B6{l`SV=$93K z8v?4ky9inX0XNpsi2z3D$1a`uNx$HDcru^t{9uU>g}kAUTIW8V=6Jn~FfDfR&0`378y4tFV^-(s4&9_kMLP>ub%{l-{NkHnTgrjET>=`nj(TyN5A Z#0RwMFqnAeb~pYa@QOd68?Fv{{srHK$+G|e literal 1092 zcma))-D@0W6voeFcjnEPILWj`!d|R1u+>}?X&Q#6L}#|y9jmo8xk#X-B93H9poyWV z_z{@h*_mk~!DJ_!FC(N&$U=%fET|r z=Q#{K?|I&L9JKmzkPtvzG7cIdA%M6HYD7W+acR_ugaG1tQ6mxpi0eU(NC+Tq9cn~E z0C6eQh=c&*!Vv)1!2eR)^H9f8xxH~%>pm5Y8BUsQ7YTTUR7K;=l6EX;CkH@1puskk z*nALcbSZCJmbO%gY1iNq`y@LR*<94qrMWMEHzF@51wRcUA%2GdJCw7D2)-S${#k!! z(Rh@p5Gu26{4uoiaK^SCKNX4cZu8#5&xZE!?^%QR?l>pdcFrO!Ti`5(Jyx(yi|td; z(IloiAn_ zjs!i}iB0gUJ$*hn_IdQe{))cQ2x|EN=w`Vg>1-*Ub&^oup8?L>1D&V6d3^a)%2Yr3 r8^+9gM! Date: Fri, 12 Mar 2021 18:04:15 +0100 Subject: [PATCH 10/22] Fixing *.S files (define for COREV target) --- rtos/pulpos/common/kernel/crt0.S | 3 ++- rtos/pulpos/common/kernel/irq_asm.S | 4 +++- rtos/pulpos/common/kernel/soc_event_v2_itc.S | 5 ++++- rtos/pulpos/common/kernel/task_asm.S | 4 +++- rtos/pulpos/common/kernel/time_asm.S | 4 +++- rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S | 4 +++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/rtos/pulpos/common/kernel/crt0.S b/rtos/pulpos/common/kernel/crt0.S index a597e2a5..d22ce131 100644 --- a/rtos/pulpos/common/kernel/crt0.S +++ b/rtos/pulpos/common/kernel/crt0.S @@ -21,8 +21,9 @@ #include "archi/pulp.h" .section .text_l2 +#ifdef ARCHI_HAS_COREV .attribute arch, "rv32imc_xcorev2p0" - +#endif .global pos_init_entry pos_init_entry: diff --git a/rtos/pulpos/common/kernel/irq_asm.S b/rtos/pulpos/common/kernel/irq_asm.S index 0cacceed..2c520281 100644 --- a/rtos/pulpos/common/kernel/irq_asm.S +++ b/rtos/pulpos/common/kernel/irq_asm.S @@ -20,7 +20,9 @@ .section .text_l2 - .attribute arch, "rv32i2p0_m2p0_c2p0_xcorev2p0" +#ifdef ARCHI_HAS_COREV + .attribute arch, "rv32imc_xcorev2p0" +#endif .global pos_irq_call_external_c_function pos_irq_call_external_c_function: diff --git a/rtos/pulpos/common/kernel/soc_event_v2_itc.S b/rtos/pulpos/common/kernel/soc_event_v2_itc.S index 01a67a35..91e3684e 100644 --- a/rtos/pulpos/common/kernel/soc_event_v2_itc.S +++ b/rtos/pulpos/common/kernel/soc_event_v2_itc.S @@ -22,7 +22,10 @@ .section .text_l2 - .attribute arch, "rv32i2p0_m2p0_c2p0_xcorev2p0" +#ifdef ARCHI_HAS_COREV + .attribute arch, "rv32imc_xcorev2p0" +#endif + # # SOC event handler entry diff --git a/rtos/pulpos/common/kernel/task_asm.S b/rtos/pulpos/common/kernel/task_asm.S index d30c4a63..c3913548 100644 --- a/rtos/pulpos/common/kernel/task_asm.S +++ b/rtos/pulpos/common/kernel/task_asm.S @@ -22,7 +22,9 @@ .section .text_l2 - .attribute arch, "rv32i2p0_m2p0_c2p0_xcorev2p0" +#ifdef ARCHI_HAS_COREV + .attribute arch, "rv32imc_xcorev2p0" +#endif .global pos_task_push_asm pos_task_push_asm: diff --git a/rtos/pulpos/common/kernel/time_asm.S b/rtos/pulpos/common/kernel/time_asm.S index a321ecea..261db881 100644 --- a/rtos/pulpos/common/kernel/time_asm.S +++ b/rtos/pulpos/common/kernel/time_asm.S @@ -22,7 +22,9 @@ .section .text_l2 - .attribute arch, "rv32i2p0_m2p0_c2p0_xcorev2p0" +#ifdef ARCHI_HAS_COREV + .attribute arch, "rv32imc_xcorev2p0" +#endif .global pos_time_timer_handler_asm pos_time_timer_handler_asm: diff --git a/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S b/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S index 0bc70972..627e7556 100644 --- a/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S +++ b/rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S @@ -22,7 +22,9 @@ .section .cluster.text , "ax" - .attribute arch, "rv32i2p0_m2p0_c2p0_xcorev2p0" +#ifdef ARCHI_HAS_COREV + .attribute arch, "rv32imc_xcorev2p0" +#endif .global pos_pe_start pos_pe_start: From 67ed48068b2749d1d0f0d077eb85edd2195a9256 Mon Sep 17 00:00:00 2001 From: Giuseppe Tagliavini Date: Thu, 18 Mar 2021 20:02:18 +0100 Subject: [PATCH 11/22] Fixed emulated builtins --- .../include/archi/riscv/builtins_v2_emu.h | 49 +++++++++++++------ .../include/hal/riscv/builtins_v2_emu.h | 49 +++++++++++++------ 2 files changed, 68 insertions(+), 30 deletions(-) diff --git a/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h b/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h index 15c97e80..a04915be 100644 --- a/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h +++ b/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h @@ -322,32 +322,51 @@ typedef unsigned int rt_pointerT; #define __ROUNDNORM(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) #define __ROUNDNORM_REG(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) -#define __SPRWRITE(x, y) (*(volatile unsigned int *)(long)(x) = (y)) -#define __SPRREAD(x) (*(unsigned int *)(long)(x)) -#define __SPRREAD_VOL(x) (*(volatile unsigned int *)(long)(x)) +static inline unsigned int __SPRREAD(unsigned int spr) { + unsigned int val; + if(spr == 0x305) + asm ("csrr %0, 0x305" : "=r" (val)); + return val; +} -#define __READ_BASE_OFF(base, off) (*(unsigned int *)(long)((base) + (off))) -#define __WRITE_BASE_OFF(base, off, val)(*(unsigned int *)(long)((base) + (off)) = (val)) +static inline unsigned int __SPRREAD_VOL(unsigned int spr) { + unsigned int val; + if(spr == 0x305) + asm volatile("csrr %0, 0x305" : "=r" (val)); + return val; +} -#define __READ_BASE_OFF_VOL(base, off) (*(volatile unsigned int *)(long)((base) + (off))) -#define __READ_BASE_OFF_HALF_VOL(base, off) (*(volatile unsigned short int *)(long)((base) + (off))) -#define __READ_BASE_OFF_BYTE_VOL(base, off) (*(volatile unsigned char *)(long)((base) + (off))) -#define __WRITE_BASE_OFF_VOL(x, base, off) (*(volatile unsigned int *)(long)((base) + (off)) = (x)) -#define __WRITE_BASE_OFF_HALF_VOL(x, base, off) (*(volatile unsigned short int *)(long)((base) + (off)) = (x)) -#define __WRITE_BASE_OFF_BYTE_VOL(x, base, off) (*(volatile unsigned char *)(long)((base) + (off)) = (x)) +static inline void __SPRWRITE(unsigned int spr, unsigned int val) { + if(spr == 0x305) + asm volatile("csrw 0x305, %0" : : "r" (val)); +} + + + +#define __READ_BASE_OFF(base, off) (*(unsigned int *)((long)(base) + (long)(off))) +#define __WRITE_BASE_OFF(base, off, val)(*(unsigned int *)((long)(base) + (long)(off)) = (val)) + +#define __READ_BASE_OFF_VOL(base, off) (*(volatile unsigned int *)((long)(base) + (long)(off))) +#define __READ_BASE_OFF_HALF_VOL(base, off) (*(volatile unsigned short int *)((long)(base) + (long)(off))) +#define __READ_BASE_OFF_BYTE_VOL(base, off) (*(volatile unsigned char *)((long)(base) + (long)(off))) + +#define __WRITE_BASE_OFF_VOL(x, base, off) (*(volatile unsigned int *)((long)(base) + (long)(off)) = (x)) +#define __WRITE_BASE_OFF_HALF_VOL(x, base, off) (*(volatile unsigned short int *)((long)(base) + (long)(off)) = (x)) +#define __WRITE_BASE_OFF_BYTE_VOL(x, base, off) (*(volatile unsigned char *)((long)(base) + (long)(off)) = (x)) + /* Utilities, Target independant */ #define FIX2FP(Val, Precision) ((float) (Val) / (float) (1<<(Precision))) #define FP2FIXR(Val, Precision) ((int)((Val)*((1 << (Precision))-1) + 0.5)) #define FP2FIX(Val, Precision) ((int)((Val)*((1 << (Precision))-1))) #define HARTID_MASK 0xF -#define CLUSTERID_MASK 0x7E0 +#define CLUSTERID_MASK 0x3F #define CLUSTERID_SHIFT 5 -#define __COREID() __SPRREAD_VOL(HARTID_MASK & RV_CSR_MHARTID) -#define __CLUSTERID() __SPRREAD_VOL((CLUSTERID_MASK & RV_CSR_MHARTID) >> CLUSTERID_SHIFT) -#define __NCORE() __SPRREAD_VOL(ARCHI_SOC_PERIPHERALS_ADDR + 0x3000 + 0x12) +#define __COREID() hal_core_id() +#define __CLUSTERID() (0) +#define __NCORE() (1) #endif #endif diff --git a/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h b/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h index 15c97e80..a04915be 100644 --- a/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h +++ b/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h @@ -322,32 +322,51 @@ typedef unsigned int rt_pointerT; #define __ROUNDNORM(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) #define __ROUNDNORM_REG(x, scale) ((int)((x) + (1<<((scale)-1)))>>(scale)) -#define __SPRWRITE(x, y) (*(volatile unsigned int *)(long)(x) = (y)) -#define __SPRREAD(x) (*(unsigned int *)(long)(x)) -#define __SPRREAD_VOL(x) (*(volatile unsigned int *)(long)(x)) +static inline unsigned int __SPRREAD(unsigned int spr) { + unsigned int val; + if(spr == 0x305) + asm ("csrr %0, 0x305" : "=r" (val)); + return val; +} -#define __READ_BASE_OFF(base, off) (*(unsigned int *)(long)((base) + (off))) -#define __WRITE_BASE_OFF(base, off, val)(*(unsigned int *)(long)((base) + (off)) = (val)) +static inline unsigned int __SPRREAD_VOL(unsigned int spr) { + unsigned int val; + if(spr == 0x305) + asm volatile("csrr %0, 0x305" : "=r" (val)); + return val; +} -#define __READ_BASE_OFF_VOL(base, off) (*(volatile unsigned int *)(long)((base) + (off))) -#define __READ_BASE_OFF_HALF_VOL(base, off) (*(volatile unsigned short int *)(long)((base) + (off))) -#define __READ_BASE_OFF_BYTE_VOL(base, off) (*(volatile unsigned char *)(long)((base) + (off))) -#define __WRITE_BASE_OFF_VOL(x, base, off) (*(volatile unsigned int *)(long)((base) + (off)) = (x)) -#define __WRITE_BASE_OFF_HALF_VOL(x, base, off) (*(volatile unsigned short int *)(long)((base) + (off)) = (x)) -#define __WRITE_BASE_OFF_BYTE_VOL(x, base, off) (*(volatile unsigned char *)(long)((base) + (off)) = (x)) +static inline void __SPRWRITE(unsigned int spr, unsigned int val) { + if(spr == 0x305) + asm volatile("csrw 0x305, %0" : : "r" (val)); +} + + + +#define __READ_BASE_OFF(base, off) (*(unsigned int *)((long)(base) + (long)(off))) +#define __WRITE_BASE_OFF(base, off, val)(*(unsigned int *)((long)(base) + (long)(off)) = (val)) + +#define __READ_BASE_OFF_VOL(base, off) (*(volatile unsigned int *)((long)(base) + (long)(off))) +#define __READ_BASE_OFF_HALF_VOL(base, off) (*(volatile unsigned short int *)((long)(base) + (long)(off))) +#define __READ_BASE_OFF_BYTE_VOL(base, off) (*(volatile unsigned char *)((long)(base) + (long)(off))) + +#define __WRITE_BASE_OFF_VOL(x, base, off) (*(volatile unsigned int *)((long)(base) + (long)(off)) = (x)) +#define __WRITE_BASE_OFF_HALF_VOL(x, base, off) (*(volatile unsigned short int *)((long)(base) + (long)(off)) = (x)) +#define __WRITE_BASE_OFF_BYTE_VOL(x, base, off) (*(volatile unsigned char *)((long)(base) + (long)(off)) = (x)) + /* Utilities, Target independant */ #define FIX2FP(Val, Precision) ((float) (Val) / (float) (1<<(Precision))) #define FP2FIXR(Val, Precision) ((int)((Val)*((1 << (Precision))-1) + 0.5)) #define FP2FIX(Val, Precision) ((int)((Val)*((1 << (Precision))-1))) #define HARTID_MASK 0xF -#define CLUSTERID_MASK 0x7E0 +#define CLUSTERID_MASK 0x3F #define CLUSTERID_SHIFT 5 -#define __COREID() __SPRREAD_VOL(HARTID_MASK & RV_CSR_MHARTID) -#define __CLUSTERID() __SPRREAD_VOL((CLUSTERID_MASK & RV_CSR_MHARTID) >> CLUSTERID_SHIFT) -#define __NCORE() __SPRREAD_VOL(ARCHI_SOC_PERIPHERALS_ADDR + 0x3000 + 0x12) +#define __COREID() hal_core_id() +#define __CLUSTERID() (0) +#define __NCORE() (1) #endif #endif From 32abd503f964aa481a78f65c0ca5422e43ab7390 Mon Sep 17 00:00:00 2001 From: Giuseppe Tagliavini Date: Fri, 16 Apr 2021 14:41:31 +0200 Subject: [PATCH 12/22] Additional fixes for the pulp_cv32e40p target --- .../pulp/include/pos/chips/pulp/config.h | 1 + rtos/pulpos/pulp/rules/pulpos/targets/pulp.mk | 4 ++-- .../rules/pulpos/targets/pulp_cv32e40p.mk | 8 ++++---- .../include/archi/riscv/builtins_v2_emu.h | 5 ++++- .../include/hal/riscv/builtins_v2_emu.h | 5 ++++- .../configs/ips/riscv/cv32e40p.json | 4 ++-- tools/gapy/targets/pulp_cv32e40p.json | 7 ++++--- .../gvsoc/pulp/models/pulp/chips/pulp/rom.bin | Bin 1220 -> 1012 bytes 8 files changed, 21 insertions(+), 13 deletions(-) diff --git a/rtos/pulpos/pulp/include/pos/chips/pulp/config.h b/rtos/pulpos/pulp/include/pos/chips/pulp/config.h index 2b473db0..0acf21f6 100644 --- a/rtos/pulpos/pulp/include/pos/chips/pulp/config.h +++ b/rtos/pulpos/pulp/include/pos/chips/pulp/config.h @@ -29,6 +29,7 @@ #define CONFIG_PULP 1 #define PULP_CHIP_STR pulp #define PULP_CHIP_FAMILY_STR pulp +//#define ARCHI_CORE_HAS_PULPV2 1 #define ARCHI_CORE_HAS_1_10 1 diff --git a/rtos/pulpos/pulp/rules/pulpos/targets/pulp.mk b/rtos/pulpos/pulp/rules/pulpos/targets/pulp.mk index c5e988d5..d9a736eb 100644 --- a/rtos/pulpos/pulp/rules/pulpos/targets/pulp.mk +++ b/rtos/pulpos/pulp/rules/pulpos/targets/pulp.mk @@ -1,7 +1,7 @@ CONFIG_NB_CLUSTER_PE ?= 8 PULP_LDFLAGS += -PULP_CFLAGS += -D__riscv__ +PULP_CFLAGS += -D__riscv_ -DARCHI_CORE_HAS_PULPV2 PULP_ARCH_CFLAGS ?= -march=rv32imcxgap9 -mPE=$(CONFIG_NB_CLUSTER_PE) -mFC=1 PULP_ARCH_LDFLAGS ?= -march=rv32imcxgap9 -mPE=$(CONFIG_NB_CLUSTER_PE) -mFC=1 PULP_ARCH_OBJDFLAGS ?= -Mmarch=rv32imcxgap9 @@ -45,4 +45,4 @@ PULP_SRCS += kernel/chips/pulp/soc.c include $(PULPOS_HOME)/rules/pulpos/configs/default.mk -include $(PULPOS_HOME)/rules/pulpos/default_rules.mk \ No newline at end of file +include $(PULPOS_HOME)/rules/pulpos/default_rules.mk diff --git a/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk b/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk index 08c3d799..4d5da92d 100644 --- a/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk +++ b/rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk @@ -2,9 +2,9 @@ CONFIG_NB_CLUSTER_PE ?= 8 PULP_LDFLAGS += PULP_CFLAGS += -D__riscv__ -DARCHI_HAS_COREV -DPLP_NO_BUILTIN -PULP_ARCH_CFLAGS ?= -march=rv32imc_xcorev -PULP_ARCH_LDFLAGS ?= -march=rv32imc_xcorev -PULP_ARCH_OBJDFLAGS ?= -Mmarch=rv32imcxcorev +PULP_ARCH_CFLAGS ?= -march=rv32imfc_xcorev +PULP_ARCH_LDFLAGS ?= -march=rv32imfc_xcorev +PULP_ARCH_OBJDFLAGS ?= PULP_CFLAGS += -fdata-sections -ffunction-sections -include pos/chips/pulp/config.h -I$(PULPOS_PULP_HOME)/include/pos/chips/pulp PULP_OMP_CFLAGS += -fopenmp -mnativeomp PULP_LDFLAGS += -nostartfiles -nostdlib -Wl,--gc-sections -L$(PULPOS_PULP_HOME)/kernel -Tchips/pulp/link.ld -lgcc @@ -45,4 +45,4 @@ PULP_SRCS += kernel/chips/pulp/soc.c include $(PULPOS_HOME)/rules/pulpos/configs/default.mk -include $(PULPOS_HOME)/rules/pulpos/default_rules.mk \ No newline at end of file +include $(PULPOS_HOME)/rules/pulpos/default_rules.mk diff --git a/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h b/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h index a04915be..c116747c 100644 --- a/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h +++ b/rtos/pulpos/pulp_archi/include/archi/riscv/builtins_v2_emu.h @@ -326,6 +326,7 @@ static inline unsigned int __SPRREAD(unsigned int spr) { unsigned int val; if(spr == 0x305) asm ("csrr %0, 0x305" : "=r" (val)); + return val; } @@ -333,13 +334,15 @@ static inline unsigned int __SPRREAD_VOL(unsigned int spr) { unsigned int val; if(spr == 0x305) asm volatile("csrr %0, 0x305" : "=r" (val)); + return val; } static inline void __SPRWRITE(unsigned int spr, unsigned int val) { if(spr == 0x305) - asm volatile("csrw 0x305, %0" : : "r" (val)); + asm volatile("csrw 0x305, %0" : : "r" (val)); + } diff --git a/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h b/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h index a04915be..c116747c 100644 --- a/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h +++ b/rtos/pulpos/pulp_hal/include/hal/riscv/builtins_v2_emu.h @@ -326,6 +326,7 @@ static inline unsigned int __SPRREAD(unsigned int spr) { unsigned int val; if(spr == 0x305) asm ("csrr %0, 0x305" : "=r" (val)); + return val; } @@ -333,13 +334,15 @@ static inline unsigned int __SPRREAD_VOL(unsigned int spr) { unsigned int val; if(spr == 0x305) asm volatile("csrr %0, 0x305" : "=r" (val)); + return val; } static inline void __SPRWRITE(unsigned int spr, unsigned int val) { if(spr == 0x305) - asm volatile("csrw 0x305, %0" : : "r" (val)); + asm volatile("csrw 0x305, %0" : : "r" (val)); + } diff --git a/tools/gap-configs/configs/ips/riscv/cv32e40p.json b/tools/gap-configs/configs/ips/riscv/cv32e40p.json index 7d262bed..de98fd84 100644 --- a/tools/gap-configs/configs/ips/riscv/cv32e40p.json +++ b/tools/gap-configs/configs/ips/riscv/cv32e40p.json @@ -4,8 +4,8 @@ "archi" : "riscv", "implementation": "ri5cy", "gv_isa" : ["--pulp", "--rv32m", "--pulpv2", "--pulp-perf-counters", "--pulp-hw-loop", "--itc-external-req", "--fpu", "--fpud", "--shared-fpu"], - "isa" : "rv32imfcXpulpv2Xf8Xf16XfvecXfauxXf16altXgap9", - "march" : "imfcXpulpv2Xf8Xf16XfvecXfauxXf16alt", + "isa" : "rv32imfcXcorev", + "march" : "imfcXcorev", "priv_version" : 1.9, "perf_counters" : true, "first_ext_counter": 12, diff --git a/tools/gapy/targets/pulp_cv32e40p.json b/tools/gapy/targets/pulp_cv32e40p.json index 346389f6..e22d0549 100644 --- a/tools/gapy/targets/pulp_cv32e40p.json +++ b/tools/gapy/targets/pulp_cv32e40p.json @@ -15,11 +15,12 @@ "runner": { "flash_devices": [ - "target/board/devices/flash" + "target/board/devices/flash", + "target/board/devices/spiflash" ], "boot": { "mode": "flash", - "device": "target/board/devices/flash" + "device": "target/board/devices/spiflash" } } -} \ No newline at end of file +} diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp/rom.bin b/tools/gvsoc/pulp/models/pulp/chips/pulp/rom.bin index a97074ae847cf9722c0b4453f8ad7cf1998c136b..13bc0dd8ae469d3ab0101aebee36c0370b0e9958 100644 GIT binary patch delta 554 zcmV+_0@eM*3G@e$g8?;>gq&J}fB_LE%7tVThJ--DMkl&({E%&IM)@28KZ*mLi2>MDjq=M3M%tK9nP(sgfhE36LY8`H~|d1VA?@ z5E>jqs)SKRljkP@9K*L~5E^|~*u$8|*vFX5*vpvD*w2{L*h3QuiINj{_xISt*h6)j z*u$+i69ozYDK~?s36cXS#6UL}034Hu_y4m7@IVs>2Y*5VMmGfj9Agy*1BqvL)+8hl$p5eCMggCfMei;d5PgM~(8iPr!bM`MKN z0ENc^pgsXb((2Y_<`P!RRM1x4AnlePmy6vaD%O{STqo3{)A0fVQ>Kx6Oy0+S8|Bn$(mNkO+q z5E>((1^|;r1T0cdMlLGN^n6@gh?1*@Cxb`%w95k()*Kh&3qvLF&Sx0~BS z(4D&ZFV>KqjPipLM}fJ2ILq0D5=SWnBI`?0&=88SQ~J_}2VS1{eV>QteSe~4QJ({A z=y#5F(X>YAFmPTit3HUlRF&kWYmWf}4i-fGPY^YMR8&~Oq=JZoX9}i}3bH&lsiJBm zcVFT|BAqGc0+}W*kW%JB_A-3jvE6N>YLI(LHf!~9p@ncc88R8O8lA==JB!bU2I|f@ z!bxWK5XWa$!SFlSM&K?pIYbJiWt#C>e0YpCW1F)v>$*J6kQSfCjEN7i6J7eZA}R)+ z7TErJfJ~oTxHx|`j=vL~1%0pl3+cdedYK9#Mm8#`q+-x{CE^it20#UW0l8hHizaNT zXv(%2U9@GQ8Qc48V-tJCLp<2p$|kan9i!RAb9(K3Z$d;sv%8~QN6F--!aZ)tQNdi7 z*ThkwoST5fV8tX?fCFL=0*=WPBOCtJ67tas?`b6*TiY}EcZNWy0khIj=?j97FW;y3 zv`74~jS8lG1U}Sc9Fif>QB!n36*7nfjx(ZAYl;~ihSWBHD-V>%fYSl~Cd44#WkxZ% zzVHRu_>Hy;8zcS^e|>%xY|1jH(S5n$khl`Fw%U%*L(b&kJvmUs@wS|*cSB$?w$rpf z2JMwqvZSO6BHLYXNt2(mvyfevTl4?3W-UHXCiU?_j^CSC@hJ(EE(sh@qddTZ;~bCW zRhx6Wrk`YsM)&y|WJ^uNy#h^Vc8%%wW5FH6m&bR+zs?QP;y0D0?0UWZLU=elDwhDv zA-!SvjXwpVRb5jy)}WTon#B~-jK!%S4t8|K`|88ZO>^prJw86~1%&eyxDyDu_($Vd zv+JXv7b@$>5ps0K>t$fc4_Uv<6`dqqVWC;5qgUjLYM>&pr#-iU4IH(13p Date: Thu, 29 Apr 2021 19:36:29 +0200 Subject: [PATCH 13/22] Add core selector for boot binary code --- .../python/generators/v1/soc_gen.py | 3 +- tools/gvsoc/common/models/Makefile | 2 +- .../pulp/models/pulp/chips/pulp/Makefile | 10 +- .../gvsoc/pulp/models/pulp/chips/pulp/rom.bin | Bin 1012 -> 1092 bytes .../pulp/chips/pulp_cv32e40p/apb_soc_impl.cpp | 191 ++++++++++++++++++ .../pulp/chips/pulp_cv32e40p/debug_rom.bin | Bin 0 -> 148 bytes .../models/pulp/chips/pulp_cv32e40p/rom.bin | Bin 0 -> 1012 bytes 7 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/apb_soc_impl.cpp create mode 100644 tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/debug_rom.bin create mode 100644 tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/rom.bin diff --git a/tools/gap-configs/python/generators/v1/soc_gen.py b/tools/gap-configs/python/generators/v1/soc_gen.py index c3a2e92c..372439ea 100644 --- a/tools/gap-configs/python/generators/v1/soc_gen.py +++ b/tools/gap-configs/python/generators/v1/soc_gen.py @@ -368,7 +368,8 @@ def get_cluster_name(cid): ('cluster_id', tp.get_child_int("soc/fc/cluster_id")), ('core_id', tp.get_child_int("soc/fc/core_id")), ('fetch_enable', tp.get_child_bool("soc/fc/fetch_enable")), - ('boot_addr', tp.get_child_str("soc/fc/boot_addr")) + ('boot_addr', tp.get_child_str("soc/fc/boot_addr")), + ('core', tp.get_child_str("soc/fc/core")) ])) diff --git a/tools/gvsoc/common/models/Makefile b/tools/gvsoc/common/models/Makefile index e691049b..4552aa60 100644 --- a/tools/gvsoc/common/models/Makefile +++ b/tools/gvsoc/common/models/Makefile @@ -7,7 +7,7 @@ PULP_PROPERTIES += udma/version timer/version stdout/version soc_eu/version \ udma/cpi/version padframe/version pulp_chip_family pmu/version \ udma/hyper/version efuse/version udma/i2c/version rtc/version gpio/version \ fc/iss_class pe/iss_class udma/i2s/version udma/mram/version hwce/version \ - udma/tcdm/version hwacc/base udma/uart/version + udma/tcdm/version hwacc/base udma/uart/version core properties := $(foreach prop,$(PULP_PROPERTIES), --property=$(prop)) diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp/Makefile b/tools/gvsoc/pulp/models/pulp/chips/pulp/Makefile index 1b752eb7..ea587ecc 100644 --- a/tools/gvsoc/pulp/models/pulp/chips/pulp/Makefile +++ b/tools/gvsoc/pulp/models/pulp/chips/pulp/Makefile @@ -5,10 +5,18 @@ pulp/chips/pulp/apb_soc_impl_SRCS = pulp/chips/pulp/apb_soc_impl.cpp VP_INSTALL_TARGETS += $(INSTALL_DIR)/python/pulp/chips/pulp/rom.bin VP_INSTALL_TARGETS += $(INSTALL_DIR)/python/pulp/chips/pulp/debug_rom.bin +ifeq '$(core)' 'cv32e40p' +$(INSTALL_DIR)/python/pulp/chips/pulp/rom.bin: $(CURDIR)/pulp/chips/pulp_cv32e40p/rom.bin + install -D $^ $@ + +$(INSTALL_DIR)/python/pulp/chips/pulp/debug_rom.bin: $(CURDIR)/pulp/chips/pulp_cv32e40p/debug_rom.bin + install -D $^ $@ + +else $(INSTALL_DIR)/python/pulp/chips/pulp/rom.bin: $(CURDIR)/pulp/chips/pulp/rom.bin install -D $^ $@ $(INSTALL_DIR)/python/pulp/chips/pulp/debug_rom.bin: $(CURDIR)/pulp/chips/pulp/debug_rom.bin install -D $^ $@ - +endif endif diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp/rom.bin b/tools/gvsoc/pulp/models/pulp/chips/pulp/rom.bin index 13bc0dd8ae469d3ab0101aebee36c0370b0e9958..a934779bada8edbc7355c841b56daad60cf50c52 100644 GIT binary patch literal 1092 zcma))-D@0W6voeFcjnEPILWj`!d|R1u+>}?X&Q#6L}#|y9jmo8xk#X-B93H9poyWV z_z{@h*_mk~!DJ_!FC(N&$U=%fET|r z=Q#{K?|I&L9JKmzkPtvzG7cIdA%M6HYD7W+acR_ugaG1tQ6mxpi0eU(NC+Tq9cn~E z0C6eQh=c&*!Vv)1!2eR)^H9f8xxH~%>pm5Y8BUsQ7YTTUR7K;=l6EX;CkH@1puskk z*nALcbSZCJmbO%gY1iNq`y@LR*<94qrMWMEHzF@51wRcUA%2GdJCw7D2)-S${#k!! z(Rh@p5Gu26{4uoiaK^SCKNX4cZu8#5&xZE!?^%QR?l>pdcFrO!Ti`5(Jyx(yi|td; z(IloiAn_ zjs!i}iB0gUJ$*hn_IdQe{))cQ2x|EN=w`Vg>1-*Ub&^oup8?L>1D&V6d3^a)%2Yr3 r8^+9gM!M}*nBD%`585%mh3 zV|Pd*Fq_u$mFshlaG>{w&_#nW#fojHYhaN>Z@`fyu7=?xa%-gRaF;94L8Tgv!vjl& zU7E5~x%l1zq2-Uxd^u1x=0f8%A`aGej9=1D}Qto9ZB zJ{OBhi+<~KdYEHkZvG~Z2fa_E zO>dG}^wAfm$)bOUJdp;yn_l{2L~R0xtbr!)_2IBA8l|$Fhh}V~{}Bt$nu^g5p{)HS zu^l@3wbRQuz_qr6ImoQrSI(9T<`=2n31Q}TQn+It7qU;20^cf$23DtMHhSKdsym>w zNCK?w>IJ=+spyr=s=k`p&^I#OTe^ocCz9MJ`}=xP_k2917rzxbx*c8-wY)iLdbuQt z9nhJ&wtrAyewR3ePEvl6Hj+aMtqwl({OnF-4OY@(7zJWFDR}wXNURI)Yz!fS>`F49o ze-yn&=Rj&7vEe%H8|t~gK;p*gIBJWesq9dwPjkqY@M=?;kVVfPq_IHzejs64;r1Y+ zy5bGc{0LmA0T>C;T;Dx%_In((KRB(=M&DcK5(#(W((uZMW#%FOQ~&ncoA2BkT>D_z zKDqQ}_)KmxH$^N!Y5A(8{PuSXLFa(jS=%HMX7<`cS1&ZJ{?F-_DSygM3`QZ#fL=5- zx|`RUoTtF_su?tA0tkX%T!;Py>MfI_3d+#0fDgINBMYWm&n1<{7;w=OFp`;xDWhLg z{jmqwj(58GulT9L*qB6H9uxbs?C#Oyd@P6iv9<3$={O2495p?08B};J9KlV!!JdBs DouXu4 diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/apb_soc_impl.cpp b/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/apb_soc_impl.cpp new file mode 100644 index 00000000..16edfb94 --- /dev/null +++ b/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/apb_soc_impl.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2020 GreenWaves Technologies, SAS, ETH Zurich and + * University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, GreenWaves Technologies (germain.haugou@greenwaves-technologies.com) + */ + +#include +#include +#include +#include +#include + +#include "archi/chips/pulp/apb_soc.h" + +class apb_soc_ctrl : public vp::component +{ + +public: + + apb_soc_ctrl(js::config *config); + + int build(); + void start(); + + static vp::io_req_status_e req(void *__this, vp::io_req *req); + +private: + + void reset(bool active); + static void bootsel_sync(void *__this, int value); + static void confreg_ext_sync(void *__this, uint32_t value); + + vp::trace trace; + vp::io_slave in; + + vp::wire_master bootaddr_itf; + vp::wire_master event_itf; + vp::wire_slave bootsel_itf; + + vp::wire_master confreg_soc_itf; + vp::wire_slave confreg_ext_itf; + + uint32_t core_status; + uint32_t bootaddr; + int bootsel; + + vp::reg_32 jtag_reg_ext; +}; + +apb_soc_ctrl::apb_soc_ctrl(js::config *config) +: vp::component(config) +{ + +} + +vp::io_req_status_e apb_soc_ctrl::req(void *__this, vp::io_req *req) +{ + apb_soc_ctrl *_this = (apb_soc_ctrl *)__this; + + uint64_t offset = req->get_addr(); + uint8_t *data = req->get_data(); + uint64_t size = req->get_size(); + bool is_write = req->get_is_write(); + + _this->trace.msg("Apb_soc_ctrl access (offset: 0x%x, size: 0x%x, is_write: %d)\n", offset, size, is_write); + + if (size != 4) return vp::IO_REQ_INVALID; + + if (offset == APB_SOC_CORESTATUS_OFFSET) + { + if (!is_write) + { + *(uint32_t *)data = _this->core_status; + } + else + { + // We are writing to the status register, the 31 LSBs are the return value of the platform and the last bit + // makes the platform exit when set to 1 + _this->core_status = *(uint32_t *)data; + + if ((_this->core_status >> APB_SOC_STATUS_EOC_BIT) & 1) + { + _this->clock->stop_engine(_this->core_status & 0x7fffffff); + } + } + } + else if (offset == APB_SOC_PADS_CONFIG) + { + if (!is_write) + { + *(uint32_t *)data = _this->bootsel; + } + } + else if (offset == APB_SOC_BOOTADDR_OFFSET) + { + if (is_write) + { + _this->trace.msg("Setting boot address (addr: 0x%x)\n", *(uint32_t *)data); + if (_this->bootaddr_itf.is_bound()) + _this->bootaddr_itf.sync(*(uint32_t *)data); + + _this->bootaddr = *(uint32_t *)data; + } + else *(uint32_t *)data = _this->bootaddr; + } + else if (offset == APB_SOC_JTAG_REG) + { + if (is_write) + { + _this->confreg_soc_itf.sync(*(uint32_t *)data); + } + else + { + *(uint32_t *)data = _this->jtag_reg_ext.get() << APB_SOC_JTAG_REG_EXT_BIT; + } + } + else + { + + } + + + return vp::IO_REQ_OK; +} + +void apb_soc_ctrl::bootsel_sync(void *__this, int value) +{ + apb_soc_ctrl *_this = (apb_soc_ctrl *)__this; + _this->bootsel = value; +} + +void apb_soc_ctrl::confreg_ext_sync(void *__this, uint32_t value) +{ + apb_soc_ctrl *_this = (apb_soc_ctrl *)__this; + _this->jtag_reg_ext.set(value); +} + +int apb_soc_ctrl::build() +{ + traces.new_trace("trace", &trace, vp::DEBUG); + in.set_req_meth(&apb_soc_ctrl::req); + new_slave_port("input", &in); + + bootsel_itf.set_sync_meth(&apb_soc_ctrl::bootsel_sync); + new_slave_port("bootsel", &bootsel_itf); + + new_master_port("bootaddr", &this->bootaddr_itf); + + new_master_port("event", &event_itf); + + confreg_ext_itf.set_sync_meth(&apb_soc_ctrl::confreg_ext_sync); + this->new_slave_port("confreg_ext", &this->confreg_ext_itf); + + this->new_master_port("confreg_soc", &this->confreg_soc_itf); + + this->new_reg("jtag_reg_ext", &this->jtag_reg_ext, 0, false); + + core_status = 0; + this->bootsel = 0; + this->jtag_reg_ext.set(0); + + return 0; +} + +void apb_soc_ctrl::reset(bool active) +{ +} + +void apb_soc_ctrl::start() +{ +} + +extern "C" vp::component *vp_constructor(js::config *config) +{ + return new apb_soc_ctrl(config); +} diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/debug_rom.bin b/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/debug_rom.bin new file mode 100644 index 0000000000000000000000000000000000000000..4967365665d63c21d39bec8b1cd788ecf6f03d06 GIT binary patch literal 148 zcmd02IKYt4aDW|%S@;<~@D~fHR2K`FR*SPTFbIbpWe^rU%22H0@KITzRlt~K2?MhW zi-T~92t%?A3lm5jA*RCcScc_S{)hO#==zn_SOtnz4XTS71Q?W+fNT|oY7if&b_LjM Ml~%Y|F@r)i05wG?(f|Me literal 0 HcmV?d00001 diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/rom.bin b/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/rom.bin new file mode 100644 index 0000000000000000000000000000000000000000..13bc0dd8ae469d3ab0101aebee36c0370b0e9958 GIT binary patch literal 1012 zcmZA0Z)h8J7zgm@n%@1Inz58aIzwL6J54mcnHD)WX?t1zvEjzr7p^D>>)D1VSj!OK zba2V#nx&$4N!O$nAq_LZ!ulqZ->ce`v`zo5m9{hIv?9!sH-c`EmML1F4Dp38K5*ZM z`*F|n+}$9kO@kmKKt(bLDv}YPB8M5t2vCv5jAR6;*oYa)2vE_48OaDxu>muZ5uhT2 z8OaDx5eDJsSMeIILECAXx7s8EjbgT{YPqud__Of3mJ3A0nW`$@uBs9B8eF#S(|B+u zrRB>v=N{<6&Rb#|3wX*ZHIcU7MV`C{rm1o z_UbfU^zYM0a-Vm{%U+GB^?6%@^=WSogaZ;LWi=1~R{xt{F(tXGv-XyRdm- zRS)uw&0r33TaNWhD+Tj&Oy{_m{W>Aen#aV<?Ins+OA(Q zO6jsuPHz|+>1|^>y=!=Qb3DO+d~{@#49`bXM(K{kv(50Lq~*;C(+f+o)C%_GjiciN z_q)s!a*pv!EZCK&IaB>TumfVIoZz{MZr225!6wd zzPY7@=78ngWQ`Ws=a~n8(!VX9+0;s^y;gw(Dg~^z;kVr_+{&~jEX{xN>9(%vekERk zX97yJ3VNBzFusG1;U6R~-r1WxBGzz~_4Rk$Tc9!QGDeyrDJIk3r8xtZ@J4-;Vun2V zYHACt?|T{!DEuKr)b>~nG(UlMQUjfcP=ETFp-bQCA;5`HCg%XuLepk19ez!lSIrOHebI|-}V1D zZOWf_Q=CxDaA1^hyY70gCf9i|y<`T>6$eV-m-Z8X0P}{)xduxAub`iBn}-%mx1UNV zwNc=sXJIIv9Z`nAWO_#K>kfVYjPQjpRTv$WS<6G}cv8FjvM`d5x&536*6+5i9m literal 0 HcmV?d00001 From c620be7fdf50fbedc67b5dbf5738ba6e584d9c86 Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Fri, 30 Apr 2021 09:02:56 +0200 Subject: [PATCH 14/22] Delete chip specific folder for boot binary code --- .../pulp/models/pulp/chips/pulp/Makefile | 4 +- .../cv32e40p}/debug_rom.bin | Bin .../{pulp_cv32e40p => pulp/cv32e40p}/rom.bin | Bin .../pulp/chips/pulp_cv32e40p/apb_soc_impl.cpp | 191 ------------------ 4 files changed, 2 insertions(+), 193 deletions(-) rename tools/gvsoc/pulp/models/pulp/chips/{pulp_cv32e40p => pulp/cv32e40p}/debug_rom.bin (100%) rename tools/gvsoc/pulp/models/pulp/chips/{pulp_cv32e40p => pulp/cv32e40p}/rom.bin (100%) delete mode 100644 tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/apb_soc_impl.cpp diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp/Makefile b/tools/gvsoc/pulp/models/pulp/chips/pulp/Makefile index ea587ecc..a2f456c0 100644 --- a/tools/gvsoc/pulp/models/pulp/chips/pulp/Makefile +++ b/tools/gvsoc/pulp/models/pulp/chips/pulp/Makefile @@ -6,10 +6,10 @@ VP_INSTALL_TARGETS += $(INSTALL_DIR)/python/pulp/chips/pulp/rom.bin VP_INSTALL_TARGETS += $(INSTALL_DIR)/python/pulp/chips/pulp/debug_rom.bin ifeq '$(core)' 'cv32e40p' -$(INSTALL_DIR)/python/pulp/chips/pulp/rom.bin: $(CURDIR)/pulp/chips/pulp_cv32e40p/rom.bin +$(INSTALL_DIR)/python/pulp/chips/pulp/rom.bin: $(CURDIR)/pulp/chips/pulp/cv32e40p/rom.bin install -D $^ $@ -$(INSTALL_DIR)/python/pulp/chips/pulp/debug_rom.bin: $(CURDIR)/pulp/chips/pulp_cv32e40p/debug_rom.bin +$(INSTALL_DIR)/python/pulp/chips/pulp/debug_rom.bin: $(CURDIR)/pulp/chips/pulp/cv32e40p/debug_rom.bin install -D $^ $@ else diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/debug_rom.bin b/tools/gvsoc/pulp/models/pulp/chips/pulp/cv32e40p/debug_rom.bin similarity index 100% rename from tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/debug_rom.bin rename to tools/gvsoc/pulp/models/pulp/chips/pulp/cv32e40p/debug_rom.bin diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/rom.bin b/tools/gvsoc/pulp/models/pulp/chips/pulp/cv32e40p/rom.bin similarity index 100% rename from tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/rom.bin rename to tools/gvsoc/pulp/models/pulp/chips/pulp/cv32e40p/rom.bin diff --git a/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/apb_soc_impl.cpp b/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/apb_soc_impl.cpp deleted file mode 100644 index 16edfb94..00000000 --- a/tools/gvsoc/pulp/models/pulp/chips/pulp_cv32e40p/apb_soc_impl.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2020 GreenWaves Technologies, SAS, ETH Zurich and - * University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, GreenWaves Technologies (germain.haugou@greenwaves-technologies.com) - */ - -#include -#include -#include -#include -#include - -#include "archi/chips/pulp/apb_soc.h" - -class apb_soc_ctrl : public vp::component -{ - -public: - - apb_soc_ctrl(js::config *config); - - int build(); - void start(); - - static vp::io_req_status_e req(void *__this, vp::io_req *req); - -private: - - void reset(bool active); - static void bootsel_sync(void *__this, int value); - static void confreg_ext_sync(void *__this, uint32_t value); - - vp::trace trace; - vp::io_slave in; - - vp::wire_master bootaddr_itf; - vp::wire_master event_itf; - vp::wire_slave bootsel_itf; - - vp::wire_master confreg_soc_itf; - vp::wire_slave confreg_ext_itf; - - uint32_t core_status; - uint32_t bootaddr; - int bootsel; - - vp::reg_32 jtag_reg_ext; -}; - -apb_soc_ctrl::apb_soc_ctrl(js::config *config) -: vp::component(config) -{ - -} - -vp::io_req_status_e apb_soc_ctrl::req(void *__this, vp::io_req *req) -{ - apb_soc_ctrl *_this = (apb_soc_ctrl *)__this; - - uint64_t offset = req->get_addr(); - uint8_t *data = req->get_data(); - uint64_t size = req->get_size(); - bool is_write = req->get_is_write(); - - _this->trace.msg("Apb_soc_ctrl access (offset: 0x%x, size: 0x%x, is_write: %d)\n", offset, size, is_write); - - if (size != 4) return vp::IO_REQ_INVALID; - - if (offset == APB_SOC_CORESTATUS_OFFSET) - { - if (!is_write) - { - *(uint32_t *)data = _this->core_status; - } - else - { - // We are writing to the status register, the 31 LSBs are the return value of the platform and the last bit - // makes the platform exit when set to 1 - _this->core_status = *(uint32_t *)data; - - if ((_this->core_status >> APB_SOC_STATUS_EOC_BIT) & 1) - { - _this->clock->stop_engine(_this->core_status & 0x7fffffff); - } - } - } - else if (offset == APB_SOC_PADS_CONFIG) - { - if (!is_write) - { - *(uint32_t *)data = _this->bootsel; - } - } - else if (offset == APB_SOC_BOOTADDR_OFFSET) - { - if (is_write) - { - _this->trace.msg("Setting boot address (addr: 0x%x)\n", *(uint32_t *)data); - if (_this->bootaddr_itf.is_bound()) - _this->bootaddr_itf.sync(*(uint32_t *)data); - - _this->bootaddr = *(uint32_t *)data; - } - else *(uint32_t *)data = _this->bootaddr; - } - else if (offset == APB_SOC_JTAG_REG) - { - if (is_write) - { - _this->confreg_soc_itf.sync(*(uint32_t *)data); - } - else - { - *(uint32_t *)data = _this->jtag_reg_ext.get() << APB_SOC_JTAG_REG_EXT_BIT; - } - } - else - { - - } - - - return vp::IO_REQ_OK; -} - -void apb_soc_ctrl::bootsel_sync(void *__this, int value) -{ - apb_soc_ctrl *_this = (apb_soc_ctrl *)__this; - _this->bootsel = value; -} - -void apb_soc_ctrl::confreg_ext_sync(void *__this, uint32_t value) -{ - apb_soc_ctrl *_this = (apb_soc_ctrl *)__this; - _this->jtag_reg_ext.set(value); -} - -int apb_soc_ctrl::build() -{ - traces.new_trace("trace", &trace, vp::DEBUG); - in.set_req_meth(&apb_soc_ctrl::req); - new_slave_port("input", &in); - - bootsel_itf.set_sync_meth(&apb_soc_ctrl::bootsel_sync); - new_slave_port("bootsel", &bootsel_itf); - - new_master_port("bootaddr", &this->bootaddr_itf); - - new_master_port("event", &event_itf); - - confreg_ext_itf.set_sync_meth(&apb_soc_ctrl::confreg_ext_sync); - this->new_slave_port("confreg_ext", &this->confreg_ext_itf); - - this->new_master_port("confreg_soc", &this->confreg_soc_itf); - - this->new_reg("jtag_reg_ext", &this->jtag_reg_ext, 0, false); - - core_status = 0; - this->bootsel = 0; - this->jtag_reg_ext.set(0); - - return 0; -} - -void apb_soc_ctrl::reset(bool active) -{ -} - -void apb_soc_ctrl::start() -{ -} - -extern "C" vp::component *vp_constructor(js::config *config) -{ - return new apb_soc_ctrl(config); -} From 9934bd4a38dd9d1c0c96b6f250a7c1d8c3f0ba53 Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Fri, 30 Apr 2021 16:57:40 +0200 Subject: [PATCH 15/22] Add corev specification for csr (perf and hwloop) --- .../include/archi/chips/pulp/pulp.h | 4 + .../include/archi/riscv/priv_corev_1_1.h | 51 ++ .../pulp_hal/include/hal/chips/pulp/pulp.h | 4 + .../pulp_hal/include/hal/dma/mchan_v7.h | 14 +- rtos/pulpos/pulp_hal/include/hal/eu/eu_v3.h | 12 +- .../include/hal/riscv/riscv_corev_v1.h | 443 ++++++++++++++++++ tools/gvsoc/common/bin/pulp-pc-info | 18 +- .../models/cpu/iss/include/cv32e40p.hpp | 4 +- .../common/models/cpu/iss/include/types.hpp | 2 +- 9 files changed, 536 insertions(+), 16 deletions(-) create mode 100644 rtos/pulpos/pulp_archi/include/archi/riscv/priv_corev_1_1.h create mode 100644 rtos/pulpos/pulp_hal/include/hal/riscv/riscv_corev_v1.h diff --git a/rtos/pulpos/pulp_archi/include/archi/chips/pulp/pulp.h b/rtos/pulpos/pulp_archi/include/archi/chips/pulp/pulp.h index 058aa967..db4d88f0 100644 --- a/rtos/pulpos/pulp_archi/include/archi/chips/pulp/pulp.h +++ b/rtos/pulpos/pulp_archi/include/archi/chips/pulp/pulp.h @@ -22,7 +22,11 @@ #include "archi/chips/pulp/apb_soc_ctrl.h" #include "archi/gpio/gpio_v3.h" +#ifdef ARCHI_HAS_COREV +#include "archi/riscv/priv_corev_1_1.h" +#else #include "archi/riscv/priv_1_10.h" +#endif #include "archi/riscv/pcer_v2.h" #include "archi/itc/itc_v1.h" diff --git a/rtos/pulpos/pulp_archi/include/archi/riscv/priv_corev_1_1.h b/rtos/pulpos/pulp_archi/include/archi/riscv/priv_corev_1_1.h new file mode 100644 index 00000000..49752f54 --- /dev/null +++ b/rtos/pulpos/pulp_archi/include/archi/riscv/priv_corev_1_1.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* COREV has different addresses. Keep compatibility */ +#ifndef _ARCHI_RISCV_PRIV_1_11_H +#define _ARCHI_RISCV_PRIV_1_11_H + +#define RV_CSR_MSTATUS 0x300 +#define RV_CSR_MEPC 0x341 +#define RV_CSR_MCAUSE 0x342 +#define RV_CSR_MTVAL 0x343 + +#define RV_CSR_MISA 0x301 +#define RV_CSR_MIMPID 0xF13 +#define RV_CSR_MHARTID 0xF14 + +#define CSR_PCCR(N) (0x780 + (N)) +#define CSR_PCER 0xCC0 +#define CSR_PCMR 0xCC1 + +/* Not specified for COREV */ +#define CSR_STACK_CONF 0x7D0 +#define CSR_STACK_START 0x7D1 +#define CSR_STACK_END 0x7D2 + +/* Not specified for COREV */ +#define CSR_MESTATUS_INTEN_BIT 0 +#define CSR_MESTATUS_PRV_BIT 1 +#define CSR_MESTATUS_PRV_MACH 3 + +#define CSR_HWLOOP0_START 0x800 +#define CSR_HWLOOP0_END 0x801 +#define CSR_HWLOOP0_COUNTER 0x802 +#define CSR_HWLOOP1_START 0x804 +#define CSR_HWLOOP1_END 0x805 +#define CSR_HWLOOP1_COUNTER 0x806 + +#endif \ No newline at end of file diff --git a/rtos/pulpos/pulp_hal/include/hal/chips/pulp/pulp.h b/rtos/pulpos/pulp_hal/include/hal/chips/pulp/pulp.h index e2502b23..dba6dfe7 100644 --- a/rtos/pulpos/pulp_hal/include/hal/chips/pulp/pulp.h +++ b/rtos/pulpos/pulp_hal/include/hal/chips/pulp/pulp.h @@ -17,7 +17,11 @@ #ifndef __HAL_CHIPS_PULP_PULP_H__ #define __HAL_CHIPS_PULP_PULP_H__ +#ifdef ARCHI_HAS_COREV +#include "hal/riscv/riscv_corev_v1.h" +#else #include "hal/riscv/riscv_v5.h" +#endif #include "hal/eu/eu_v3.h" #include "hal/itc/itc_v1.h" #include "hal/dma/mchan_v7.h" diff --git a/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v7.h b/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v7.h index f8dee791..e7595abd 100644 --- a/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v7.h +++ b/rtos/pulpos/pulp_hal/include/hal/dma/mchan_v7.h @@ -277,13 +277,13 @@ static inline void plp_dma_counter_free(int counter) { static inline unsigned int plp_dma_getCmd(int ext2loc, unsigned int size, int is2D, int trigEvt, int trigIrq, int broadcast) { #if defined(__riscv__) unsigned int res; - res = __builtin_bitinsert(0, ext2loc, 1, MCHAN_CMD_CMD_TYPE_BIT); - res = __builtin_bitinsert(res, PLP_DMA_INC, 1, MCHAN_CMD_CMD_INC_BIT); - res = __builtin_bitinsert(res, is2D, 1, MCHAN_CMD_CMD__2D_EXT_BIT); - res = __builtin_bitinsert(res, size, MCHAN_CMD_CMD_LEN_WIDTH, MCHAN_CMD_CMD_LEN_BIT); - res = __builtin_bitinsert(res, trigEvt, 1, MCHAN_CMD_CMD_ELE_BIT); - res = __builtin_bitinsert(res, trigIrq, 1, MCHAN_CMD_CMD_ILE_BIT); - res = __builtin_bitinsert(res, broadcast, 1, MCHAN_CMD_CMD_BLE_BIT); + res = __BITINSERT(0, ext2loc, 1, MCHAN_CMD_CMD_TYPE_BIT); + res = __BITINSERT(res, PLP_DMA_INC, 1, MCHAN_CMD_CMD_INC_BIT); + res = __BITINSERT(res, is2D, 1, MCHAN_CMD_CMD__2D_EXT_BIT); + res = __BITINSERT(res, size, MCHAN_CMD_CMD_LEN_WIDTH, MCHAN_CMD_CMD_LEN_BIT); + res = __BITINSERT(res, trigEvt, 1, MCHAN_CMD_CMD_ELE_BIT); + res = __BITINSERT(res, trigIrq, 1, MCHAN_CMD_CMD_ILE_BIT); + res = __BITINSERT(res, broadcast, 1, MCHAN_CMD_CMD_BLE_BIT); return res; #else return (ext2loc << MCHAN_CMD_CMD_TYPE_BIT) | (PLP_DMA_INC << MCHAN_CMD_CMD_INC_BIT) | (is2D << MCHAN_CMD_CMD__2D_EXT_BIT) | (size << MCHAN_CMD_CMD_LEN_BIT) | (trigEvt<= 4 && !defined(RISCV_1_7) +#if PULP_CHIP_FAMILY == CHIP_GAP + asm("csrr %0, 0x014" : "=r" (hart_id) : ); +#else + asm("csrr %0, 0xF14" : "=r" (hart_id) : ); +#endif +#else + asm("csrr %0, 0xF10" : "=r" (hart_id) : ); +#endif + // in PULP the hart id is {22'b0, cluster_id, core_id} + return hart_id & 0x01f; +} + +static inline unsigned int cluster_id() { int hart_id; +#if RISCV_VERSION >= 4 && !defined(RISCV_1_7) +#if PULP_CHIP_FAMILY == CHIP_GAP + asm("csrr %0, 0x014" : "=r" (hart_id) : ); +#else + asm("csrr %0, 0xF14" : "=r" (hart_id) : ); +#endif +#else + asm("csrr %0, 0xF10" : "=r" (hart_id) : ); +#endif + // in PULP the hart id is {22'b0, cluster_id, core_id} + return (hart_id >> 5) & 0x3f; +} + +#ifndef PLP_NO_BUILTIN + +static inline unsigned int hal_core_id() { + return core_id(); + //return __builtin_pulp_CoreId(); +} + +static inline unsigned int hal_cluster_id() { + //return cluster_id(); + return __builtin_pulp_ClusterId(); +} + +// TODO replace by compiler builtin +static inline __attribute__((always_inline)) unsigned int hal_has_fc() { +#ifdef ARCHI_HAS_FC + return 1; +#else + return 0; +#endif +} + +static inline __attribute__((always_inline)) unsigned int hal_is_fc() { +#ifndef ARCHI_HAS_FC + return 0; +#else + if (hal_has_fc()) return hal_cluster_id() == ARCHI_FC_CID; + else return 0; +#endif +} + +#else + +static inline __attribute__((always_inline)) unsigned int hal_core_id() { + int hart_id; +#if RISCV_VERSION >= 4 && !defined(RISCV_1_7) +#if PULP_CHIP_FAMILY == CHIP_GAP + asm("csrr %0, 0x014" : "=r" (hart_id) : ); +#else + asm("csrr %0, 0xF14" : "=r" (hart_id) : ); +#endif +#else + asm("csrr %0, 0xF10" : "=r" (hart_id) : ); +#endif + // in PULP the hart id is {22'b0, cluster_id, core_id} + return hart_id & 0x01f; +} + +static inline __attribute__((always_inline)) unsigned int hal_cluster_id() { + int hart_id; +#if RISCV_VERSION >= 4 && !defined(RISCV_1_7) +#if PULP_CHIP_FAMILY == CHIP_GAP + asm("csrr %0, 0x014" : "=r" (hart_id) : ); +#else + asm("csrr %0, 0xF14" : "=r" (hart_id) : ); +#endif +#else + asm("csrr %0, 0xF10" : "=r" (hart_id) : ); +#endif + // in PULP the hart id is {22'b0, cluster_id, core_id} + return (hart_id >> 5) & 0x3f; +} + +static inline __attribute__((always_inline)) unsigned int hal_has_fc() { +#ifdef ARCHI_HAS_FC + return 1; +#else + return 0; +#endif +} + +static inline __attribute__((always_inline)) unsigned int hal_is_fc() { +#ifndef ARCHI_HAS_FC + return 0; +#else + if (hal_has_fc()) return hal_cluster_id() == ARCHI_FC_CID; + else return 0; +#endif +} + +#endif + + + +#if defined(__LLVM__) + +static inline int hal_irq_disable() +{ + return 0; +} + +static inline void hal_irq_restore(int state) +{ +} + +static inline void hal_irq_enable() +{ +} + +#else + +static inline int hal_irq_disable() +{ + int irq = hal_spr_read_then_clr(0x300, 0x1<<3); + // This memory barrier is needed to prevent the compiler to cross the irq barrier + __asm__ __volatile__ ("" : : : "memory"); + return irq; +} + +static inline void hal_irq_restore(int state) +{ + // This memory barrier is needed to prevent the compiler to cross the irq barrier + __asm__ __volatile__ ("" : : : "memory"); + hal_spr_write(0x300, state); +} + +static inline void hal_irq_enable() +{ + // This memory barrier is needed to prevent the compiler to cross the irq barrier + __asm__ __volatile__ ("" : : : "memory"); + hal_spr_read_then_set(0x300, 0x1<<3); +} + +#endif + +/* + * PERFORMANCE COUNTERS + * + * API for accessing performance counters registers. + * Have a look at file spr-defs.h to speficy registers through defines + * SPR_PCER_* and SPR_PCMR_* + */ + +#define PCER_NB_EVENTS CSR_PCER_NB_EVENTS +#define PCER_ALL_EVENTS_MASK CSR_PCER_ALL_EVENTS_MASK +#define PCMR_ACTIVE CSR_PCMR_ACTIVE +#define PCMR_SATURATE CSR_PCMR_SATURATE + +#define CSR_CONVERT(x) #x +#define CSR_WRITE(x, var) asm volatile ("csrw "CSR_CONVERT(x)", %0" :: "r" (var)) +#define CSR_READ(x, var) asm volatile ("csrr %0, "CSR_CONVERT(x)"" : "=r" (var) :) + +/* Configure the active events. eventMask is an OR of events got through SPR_PCER_EVENT_MASK */ +static inline void cpu_perf_conf_events(unsigned int eventMask) +{ +#ifndef PLP_NO_PERF_COUNTERS + CSR_WRITE(CSR_PCER, eventMask); +#endif +} + +/* Return events configuration */ +static inline unsigned int cpu_perf_conf_events_get() +{ +#ifndef PLP_NO_PERF_COUNTERS + unsigned int result; + CSR_READ(CSR_PCER, result); + return result; +#else + return 0; +#endif +} + +/* Configure the mode. confMask is an OR of all SPR_PCMR_* macros */ +static inline void cpu_perf_conf(unsigned int confMask) +{ +#ifndef PLP_NO_PERF_COUNTERS + CSR_WRITE(CSR_PCMR, confMask); +#endif +} + +/* Starts counting in all counters. As this is using the mode register, + * the rest of the config can be given through conf parameter */ +static inline void cpu_perf_start(unsigned int conf) { +#ifndef PLP_NO_PERF_COUNTERS + cpu_perf_conf(conf | CSR_PCMR_ACTIVE); // TODO +#endif +} + +/* Stops counting in all counters. As this is using the mode register, + * the rest of the config can be given through conf parameter */ +static inline void cpu_perf_stop(unsigned int conf) { +#ifndef PLP_NO_PERF_COUNTERS + cpu_perf_conf(conf); // TODO +#endif +} + +/* Set the specified counter to the specified value */ +static inline void cpu_perf_set(unsigned int counterId, unsigned int value) { + +} + +/* Set all counters to the specified value */ +static inline void cpu_perf_setall(unsigned int value) { +#ifndef PLP_NO_PERF_COUNTERS + asm volatile ("csrw 0x79F, %0" :: "r" (value)); +#endif +} + +/* Return the value of the specified counter */ +static inline unsigned int cpu_perf_get(const unsigned int counterId) { +#ifndef PLP_NO_PERF_COUNTERS + unsigned int value = 0; + + // This is stupid! But I really don't know how else we could do that + switch(counterId) { + case 0: CSR_READ(CSR_PCCR(0), value); break; + case 1: CSR_READ(CSR_PCCR(1), value); break; + case 2: CSR_READ(CSR_PCCR(2), value); break; + case 3: CSR_READ(CSR_PCCR(3), value); break; + case 4: CSR_READ(CSR_PCCR(4), value); break; + case 5: CSR_READ(CSR_PCCR(5), value); break; + case 6: CSR_READ(CSR_PCCR(6), value); break; + case 7: CSR_READ(CSR_PCCR(7), value); break; + case 8: CSR_READ(CSR_PCCR(8), value); break; + case 9: CSR_READ(CSR_PCCR(9), value); break; + case 10: CSR_READ(CSR_PCCR(10), value); break; + case 11: CSR_READ(CSR_PCCR(11), value); break; + case 12: CSR_READ(CSR_PCCR(12), value); break; + case 13: CSR_READ(CSR_PCCR(13), value); break; + case 14: CSR_READ(CSR_PCCR(14), value); break; + case 15: CSR_READ(CSR_PCCR(15), value); break; + case 16: CSR_READ(CSR_PCCR(16), value); break; + case 17: CSR_READ(CSR_PCCR(17), value); break; + case 18: CSR_READ(CSR_PCCR(18), value); break; + case 19: CSR_READ(CSR_PCCR(19), value); break; + case 20: CSR_READ(CSR_PCCR(20), value); break; + case 21: CSR_READ(CSR_PCCR(21), value); break; + case 22: CSR_READ(CSR_PCCR(22), value); break; + case 23: CSR_READ(CSR_PCCR(23), value); break; + case 24: CSR_READ(CSR_PCCR(24), value); break; + case 25: CSR_READ(CSR_PCCR(25), value); break; + case 26: CSR_READ(CSR_PCCR(26), value); break; + case 27: CSR_READ(CSR_PCCR(27), value); break; + case 28: CSR_READ(CSR_PCCR(28), value); break; + case 29: CSR_READ(CSR_PCCR(29), value); break; + case 30: CSR_READ(CSR_PCCR(30), value); break; + } + return value; +#else + return 0; +#endif +} + +static inline const char *cpu_perf_name(int event) { + switch (event) + { + case 0: return "CYCLES"; + case 1: return "INSTR"; + case 2: return "LD_STALL"; + case 3: return "JMP_STALL"; + case 4: return "IMISS"; + case 5: return "LD"; + case 6: return "ST"; + case 7: return "JUMP"; + case 8: return "BRANCH"; + case 9: return "TAKEN_BRANCH"; + case 10: return "RVC"; + case 11: return "LD_EXT"; + case 12: return "ST_EXT"; + case 13: return "LD_EXT_CYC"; + case 14: return "ST_EXT_CYC"; + case 15: return "TCDM_CONT"; + } + return (char *)0; +} + + + +/* + * Stack checking + */ + +static inline void cpu_stack_check_enable(unsigned int base, unsigned int end) +{ + asm volatile ("csrwi 0x7D0, 0" :: ); + asm volatile ("csrw 0x7D1, %0" :: "r" (base)); + asm volatile ("csrw 0x7D2, %0" :: "r" (end)); + asm volatile ("csrwi 0x7D0, 1" :: ); +} + +static inline void cpu_stack_check_disable() +{ + asm volatile ("csrwi 0x7D0, 0" :: ); +} + +#endif diff --git a/tools/gvsoc/common/bin/pulp-pc-info b/tools/gvsoc/common/bin/pulp-pc-info index b86a4b8d..a6b05912 100755 --- a/tools/gvsoc/common/bin/pulp-pc-info +++ b/tools/gvsoc/common/bin/pulp-pc-info @@ -63,8 +63,22 @@ if toolchain is None: toolchain = os.environ.get('PULP_RISCV_GCC_TOOLCHAIN') if toolchain is not None: - readelf = toolchain + '/bin/riscv32-unknown-elf-readelf' - addr2line = toolchain + '/bin/riscv32-unknown-elf-addr2line' + # To be compatible with other toolchains + readelf_tool = 'readelf' + addr2line_tool = 'addr2line' + + tools = os.listdir(toolchain + "/bin/") + + for tool in tools: + tool_name = tool.split('-')[-1] + if tool_name == readelf_tool: + readelf = toolchain + '/bin/' + tool + if tool_name == addr2line_tool: + addr2line = toolchain + '/bin/' + tool + + # Previous implementation. It doesn't work with corev toolchain + # readelf = toolchain + '/bin/riscv32-unknown-elf-readelf' + # addr2line = toolchain + '/bin/riscv32-unknown-elf-addr2line' else: readelf = 'riscv32-unknown-elf-readelf' addr2line = 'riscv32-unknown-elf-addr2line' diff --git a/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp b/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp index 6166d5fa..37101d53 100644 --- a/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp +++ b/tools/gvsoc/common/models/cpu/iss/include/cv32e40p.hpp @@ -442,8 +442,8 @@ static inline void corev_hwloop_set_end(iss_t *iss, iss_insn_t *insn, int index, if (end_insn->hwloop_handler == NULL) { end_insn->hwloop_handler = end_insn->handler; - end_insn->handler = hwloop_check_exec; - end_insn->fast_handler = hwloop_check_exec; + end_insn->handler = corev_hwloop_check_exec; + end_insn->fast_handler = corev_hwloop_check_exec; } iss->cpu.corev.hwloop_regs[COREV_HWLOOP_LPEND(index)] = end; diff --git a/tools/gvsoc/common/models/cpu/iss/include/types.hpp b/tools/gvsoc/common/models/cpu/iss/include/types.hpp index 3ea7607e..d23241df 100644 --- a/tools/gvsoc/common/models/cpu/iss/include/types.hpp +++ b/tools/gvsoc/common/models/cpu/iss/include/types.hpp @@ -460,7 +460,7 @@ typedef struct iss_cpu_s { iss_irq_t irq; iss_csr_t csr; iss_pulpv2_t pulpv2; - iss_pulpv2_t corev; + iss_corev_t corev; iss_rnnext_t rnnext; } iss_cpu_t; From 4ad626400078e94a609f5c16087e43800cbc1df3 Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Fri, 4 Jun 2021 09:56:52 +0200 Subject: [PATCH 16/22] [TEST] Add perf counters test --- tests/perf/vecmul/Makefile | 5 +++ tests/perf/vecmul/stats.h | 72 ++++++++++++++++++++++++++++++++++++++ tests/perf/vecmul/test.c | 61 ++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 tests/perf/vecmul/Makefile create mode 100644 tests/perf/vecmul/stats.h create mode 100644 tests/perf/vecmul/test.c diff --git a/tests/perf/vecmul/Makefile b/tests/perf/vecmul/Makefile new file mode 100644 index 00000000..1671ce58 --- /dev/null +++ b/tests/perf/vecmul/Makefile @@ -0,0 +1,5 @@ +APP = test +APP_SRCS += test.c +APP_CFLAGS += -O3 -g -DSTATS + +include $(RULES_DIR)/pmsis_rules.mk \ No newline at end of file diff --git a/tests/perf/vecmul/stats.h b/tests/perf/vecmul/stats.h new file mode 100644 index 00000000..c58680c2 --- /dev/null +++ b/tests/perf/vecmul/stats.h @@ -0,0 +1,72 @@ +#ifndef __STATS_BOARD_H__ +#define __STATS_BOARD_H__ + +#ifdef STATS + +#define HOTTING 2 /* Necessary Iterations to avoid cache cold effects */ +#define REPEAT 5 /* Averaging on 5 successive Iterations */ + + +#define INIT_STATS() \ + unsigned long _cycles = 0; \ + unsigned long _instr = 0; \ + unsigned long _ldstall = 0; \ + unsigned long _btaken = 0; \ + unsigned long _jrstall = 0; + + +#define ENTER_LOOP_STATS() \ + for(int _k = 0; _k < HOTTING + REPEAT; _k++) \ + { \ + pi_perf_conf( \ + (1<= HOTTING) \ + { \ + _cycles += pi_perf_read (PI_PERF_CYCLES); \ + _instr += pi_perf_read (PI_PERF_INSTR); \ + _ldstall += pi_perf_read (PI_PERF_LD_STALL); \ + _btaken += pi_perf_read (PI_PERF_BTAKEN); \ + _jrstall += pi_perf_read (PI_PERF_JR_STALL); \ + } + + +#define EXIT_LOOP_STATS() \ + } \ + _cycles = _cycles/REPEAT; \ + _instr = _instr/REPEAT; \ + _ldstall = _ldstall/REPEAT; \ + _btaken = _btaken/REPEAT; \ + _jrstall = _jrstall/REPEAT; \ + printf("Core Statistics\n"); \ + printf("Total Cycles = %lu\n", _cycles); \ + printf("Executed Instructions = %lu\n", _instr); \ + printf("Load Stalls = %lu\n", _ldstall); \ + printf("Branch Taken = %lu\n", _btaken); \ + printf("Jump Stalls = %lu\n", _jrstall); \ + +#else // ! STATS + +#define INIT_STATS() +#define ENTER_LOOP_STATS() +#define START_STATS() +#define STOP_STATS() +#define EXIT_LOOP_STATS() + +#endif + + +#endif \ No newline at end of file diff --git a/tests/perf/vecmul/test.c b/tests/perf/vecmul/test.c new file mode 100644 index 00000000..b1d5c462 --- /dev/null +++ b/tests/perf/vecmul/test.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 University of Bologna + * All rights reserved. + * + * This software may be modified and distributed under the terms + * of the BSD license. See the LICENSE file for details. + * + * Authors: Enrico Tabanelli, UniBO (enrico.tabanelli3@unibo.it) + */ + +#include "pmsis.h" +#include "stats.h" + +#define SIZE (128) + +int A[SIZE] = {0}; +int B[SIZE] = {0}; +int C[SIZE*SIZE] = {0}; + +void task_initMat() +{ + for(int i = 0; i < SIZE; i++) + { + A[i] = i; + B[i] = i; + } +} + +void task_vecmul(int* vecA, int* vecB, int * matC, int dim) +{ + for (int i = 0; i < dim; i++) + { + for(int j = 0; j < dim; j++) + { + matC[j + i*dim] = vecA[j] * vecB[j]; + } + } +} + +void task_VectProdScalar(int scalarA, int* matB, int * matC, int dim) +{ + for(int i = 0; i < dim; i++) + { + matC[i] = scalarA * matB[i]; + } +} + +int main() +{ + task_initMat(); + + INIT_STATS(); + ENTER_LOOP_STATS(); + START_STATS(); + + task_vecmul(A, B, C, SIZE); + + STOP_STATS(); + EXIT_LOOP_STATS(); + +} From 12f3db0bf4e7a6de784f23bce1d4dcb562bc3eca Mon Sep 17 00:00:00 2001 From: Nazareno Bruschi Date: Mon, 7 Jun 2021 10:17:01 +0200 Subject: [PATCH 17/22] [TEST] Add helloworld and cluster tests --- tests/cluster/call/Makefile | 5 ++ tests/cluster/call/test.c | 126 +++++++++++++++++++++++++++++++ tests/cluster/fork/Makefile | 5 ++ tests/cluster/fork/test.c | 145 ++++++++++++++++++++++++++++++++++++ tests/hello/Makefile | 16 ++++ tests/hello/test.c | 60 +++++++++++++++ 6 files changed, 357 insertions(+) create mode 100644 tests/cluster/call/Makefile create mode 100644 tests/cluster/call/test.c create mode 100644 tests/cluster/fork/Makefile create mode 100644 tests/cluster/fork/test.c create mode 100644 tests/hello/Makefile create mode 100644 tests/hello/test.c diff --git a/tests/cluster/call/Makefile b/tests/cluster/call/Makefile new file mode 100644 index 00000000..53f35100 --- /dev/null +++ b/tests/cluster/call/Makefile @@ -0,0 +1,5 @@ +APP = test +APP_SRCS += test.c +APP_CFLAGS += -O3 -g + +include $(RULES_DIR)/pmsis_rules.mk diff --git a/tests/cluster/call/test.c b/tests/cluster/call/test.c new file mode 100644 index 00000000..8659b82c --- /dev/null +++ b/tests/cluster/call/test.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017 ETH Zurich, University of Bologna and GreenWaves Technologies + * All rights reserved. + * + * This software may be modified and distributed under the terms + * of the BSD license. See the LICENSE file for details. + * + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ + +#include "pmsis.h" +#include "stdio.h" + +#define NB_CALL 1024 +#define NB_TASKS 32 + +static int nb_call; +static struct pi_cluster_task task[NB_TASKS]; +static struct pi_task events[NB_TASKS]; + +static void cluster_entry(void *arg) +{ + nb_call++; +} + +static int test_task_sync() +{ + struct pi_device cluster_dev; + struct pi_cluster_conf conf; + struct pi_cluster_task task; + int errors = 0; + + nb_call = 0; + + pi_cluster_conf_init(&conf); + conf.id = 0; + + pi_open_from_conf(&cluster_dev, &conf); + + pi_cluster_open(&cluster_dev); + + pi_cluster_task(&task, cluster_entry, NULL); + + for (int i=0; i Date: Tue, 29 Jun 2021 17:33:28 +0200 Subject: [PATCH 18/22] Add runtime library for gcc regression tests on gvsoc --- .gitignore | 6 +- Makefile | 2 +- configs/common.sh | 1 + configs/pulp-open-cv32e40p.sh | 2 + .../pulp/include/pos/chips/pulp/config.h | 1 - rules/pulpos.mk | 98 +++++++++++++++++++ tools/gvsoc/common/Makefile | 1 + tools/gvsoc/common/bin/gvsoc_sim | 4 + 8 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 tools/gvsoc/common/bin/gvsoc_sim diff --git a/.gitignore b/.gitignore index 11348367..49ebc59c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,7 @@ __pycache__ build/ install/ -log_build.txt -tools/gap-configs/.sconsign.dblite +.sconsign.dblite +libs/ +ext_libs/ +BUILD/ diff --git a/Makefile b/Makefile index a9f114d8..dedf689c 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,6 @@ include rules/dpi-models.mk include rules/gvsoc.mk include rules/pulpos.mk -build: gvsoc.build.all +build: gvsoc.build.all pulpos.libs clean: gvsoc.clean diff --git a/configs/common.sh b/configs/common.sh index 32cea6fa..9082af42 100644 --- a/configs/common.sh +++ b/configs/common.sh @@ -44,3 +44,4 @@ export GAP_PULPOS_ARCHI=$PULP_SDK_HOME/rtos/pulpos/gap_archi export PULPOS_ARCHI=$PULP_SDK_HOME/rtos/pulpos/pulp_archi export PULPOS_HAL=$PULP_SDK_HOME/rtos/pulpos/pulp_hal export PMSIS_API=$PULP_SDK_HOME/rtos/pmsis/pmsis_api +export PULP_LIBS_DIR=$PULP_SDK_HOME/libs diff --git a/configs/pulp-open-cv32e40p.sh b/configs/pulp-open-cv32e40p.sh index 21adcaa6..12f8d15c 100644 --- a/configs/pulp-open-cv32e40p.sh +++ b/configs/pulp-open-cv32e40p.sh @@ -24,6 +24,8 @@ export PULPOS_TARGET=pulp_cv32e40p export PULPOS_SYSTEM=pulp export GAPY_TARGET=pulp_cv32e40p +export CHIP_CORE_ISA=corev + export PULPOS_MODULES="$PULP_SDK_HOME/rtos/pulpos/pulp $PULP_SDK_HOME/rtos/pmsis/pmsis_bsp" export GVSOC_MODULES="$PULP_SDK_HOME/tools/gvsoc/common $PULP_SDK_HOME/tools/gvsoc/pulp/models" diff --git a/rtos/pulpos/pulp/include/pos/chips/pulp/config.h b/rtos/pulpos/pulp/include/pos/chips/pulp/config.h index 0acf21f6..2b473db0 100644 --- a/rtos/pulpos/pulp/include/pos/chips/pulp/config.h +++ b/rtos/pulpos/pulp/include/pos/chips/pulp/config.h @@ -29,7 +29,6 @@ #define CONFIG_PULP 1 #define PULP_CHIP_STR pulp #define PULP_CHIP_FAMILY_STR pulp -//#define ARCHI_CORE_HAS_PULPV2 1 #define ARCHI_CORE_HAS_1_10 1 diff --git a/rules/pulpos.mk b/rules/pulpos.mk index cc75ff2f..7f072bae 100644 --- a/rules/pulpos.mk +++ b/rules/pulpos.mk @@ -4,6 +4,99 @@ ifeq '$(TARGET_CHIP_FAMILY)' 'PULP' MODULES += rtos/pulpos/pulp rtos/pulpos/pulp_archi rtos/pulpos/pulp_hal endif +ifndef VERBOSE +V = @ +endif + + +define declare_runtime_lib + +$(eval PULP_RUNTIME_LIB_SRCS_$(1) += $(PULP_RUNTIME_LIB_SRCS)) +$(eval PULP_RUNTIME_LIB_ASM_SRCS_$(1) += $(PULP_RUNTIME_LIB_ASM_SRCS)) +$(eval PULP_RUNTIME_LIB_OBJS_$(1) += $(patsubst %.c,$(PULP_LIBS_DIR)/$(1)/%.o,$(PULP_RUNTIME_LIB_SRCS_$(1)))) +$(eval PULP_RUNTIME_LIB_OBJS_$(1) += $(patsubst %.S,$(PULP_LIBS_DIR)/$(1)/%.o,$(PULP_RUNTIME_LIB_ASM_SRCS_$(1)))) + +$(eval PULP_RUNTIME_LIB_CFLAGS_$(1) += $(PULP_RUNTIME_LIB_CFLAGS)) + +-include $(PULP_RUNTIME_LIB_OBJS_$(1):.o=.d) + +$(PULP_LIBS_DIR)/$(1)/%.o: %.c + @echo "CC $$<" + $(V)mkdir -p `dirname $$@` + $(V)$(PULP_RUNTIME_LIB_CC) -c $$< -o $$@ -MMD -MP $(PULP_RUNTIME_LIB_CFLAGS_$(1)) + +$(PULP_LIBS_DIR)/$(1)/%.o: %.cpp + @echo "CXX $$<" + $(V)mkdir -p `dirname $$@` + $(V)$(PULP_RUNTIME_LIB_CC) -c $< -o $@ -MMD -MP $(PULP_RUNTIME_LIB_CFLAGS_$(1)) + +$(PULP_LIBS_DIR)/$(1)/%.o: %.S + @echo "CC $$<" + $(V)mkdir -p `dirname $$@` + $(V)$(PULP_RUNTIME_LIB_CC) -c $$< -o $$@ -MMD -MP -DLANGUAGE_ASSEMBLY $(PULP_RUNTIME_LIB_CFLAGS_$(1)) + +$(PULP_LIBS_DIR)/lib$(1).a: $(PULP_RUNTIME_LIB_OBJS_$(1)) + @echo "AR $$@" + $(V)mkdir -p `dirname $$@` + $(V)$(PULP_RUNTIME_LIB_AR) -rcs $$@ $$^ + +RUNTIME_LIB_TARGETS += $(PULP_LIBS_DIR)/lib$(1).a + +endef + +# PULP-SPECIFIC RUNTIME LIBRARY. Generates a .a for basic runtime to launch an hello world on PULP +ifeq '$(TARGET_CHIP_FAMILY)' 'PULP' + +ifeq '$(CHIP_CORE_ISA)' '' +CHIP_CORE_ISA=pulpv2 +endif + +ifeq '$(CHIP_CORE_ISA)' 'pulpv2' +PULP_RUNTIME_LIB_CC = riscv32-unknown-elf-gcc +PULP_RUNTIME_LIB_AR = riscv32-unknown-elf-ar +PULP_RUNTIME_LIB_CFLAGS = -march=rv32imcxgap9 -DARCHI_CORE_HAS_PULPV2 +PULP_RUNTIME_LIB_LDFLAGS = -march=rv32imcxgap9 +else ifeq '$(CHIP_CORE_ISA)' 'corev' +PULP_RUNTIME_LIB_CC = riscv32-corev-elf-gcc +PULP_RUNTIME_LIB_AR = riscv32-corev-elf-ar +PULP_RUNTIME_LIB_CFLAGS = -march=rv32imfc_xcorev -DARCHI_HAS_COREV -DPLP_NO_BUILTIN +PULP_RUNTIME_LIB_LDFLAGS = -march=rv32imfc_xcorev +endif + +# from pulp.mk +runtime_lib/udma/uart/version=1 +runtime_lib/event_unit/version=3 +runtime_lib/fll/version=1 +runtime_lib/udma/archi=3 +runtime_lib/soc_eu/version=2 + +PULP_RUNTIME_LIB_SRCS = $(PULPOS_HOME)/kernel/fll-v$(runtime_lib/fll/version).c $(PULPOS_HOME)/kernel/freq-domains.c $(PULPOS_PULP_HOME)/kernel/chips/pulp/soc.c $(PULPOS_HOME)/lib/libc/minimal/io.c \ + $(PULPOS_HOME)/lib/libc/minimal/fprintf.c $(PULPOS_HOME)/lib/libc/minimal/prf.c $(PULPOS_HOME)/lib/libc/minimal/sprintf.c $(PULPOS_HOME)/lib/libc/minimal/semihost.c $(PULPOS_HOME)/kernel/init.c \ + $(PULPOS_HOME)/kernel/kernel.c $(PULPOS_HOME)/kernel/device.c $(PULPOS_HOME)/kernel/task.c $(PULPOS_HOME)/kernel/alloc.c $(PULPOS_HOME)/kernel/alloc_pool.c $(PULPOS_HOME)/kernel/irq.c $(PULPOS_HOME)/kernel/soc_event.c \ + $(PULPOS_HOME)/kernel/log.c $(PULPOS_HOME)/kernel/time.c $(PULPOS_PULP_HOME)/drivers/uart/uart-v$(runtime_lib/udma/uart/version).c $(PULPOS_PULP_HOME)/drivers/udma/udma-v$(runtime_lib/udma/archi).c $(PULPOS_PULP_HOME)/drivers/cluster/cluster.c \ + $(PULP_SDK_HOME)/rtos/pmsis/pmsis_bsp/bsp/pulp.c $(PULP_SDK_HOME)/rtos/pmsis/pmsis_bsp/fs/read_fs/read_fs.c $(PULP_SDK_HOME)/rtos/pmsis/pmsis_bsp/fs/host_fs/semihost.c $(PULP_SDK_HOME)/rtos/pmsis/pmsis_bsp/fs/host_fs/host_fs.c $(PULP_SDK_HOME)/rtos/pmsis/pmsis_bsp/fs/fs.c + +PULP_RUNTIME_LIB_ASM_SRCS = $(PULPOS_HOME)/kernel/crt0.S $(PULPOS_HOME)/kernel/irq_asm.S $(PULPOS_HOME)/kernel/task_asm.S $(PULPOS_HOME)/kernel/time_asm.S $(PULPOS_HOME)/kernel/soc_event_v$(runtime_lib/soc_eu/version)_itc.S \ + $(PULPOS_PULP_HOME)/drivers/cluster/pe-eu-v$(runtime_lib/event_unit/version).S + +PULP_RUNTIME_LIB_CFLAGS += -MMD -MP -D__riscv__ -fdata-sections -ffunction-sections -include pos/chips/pulp/config.h \ + -I$(PULP_SDK_HOME)/rtos/pulpos/pulp/include/pos/chips/pulp -I$(PULP_SDK_HOME)/ext_libs/include \ + -DCONFIG_PULP -DCONFIG_BOARD_VERSION_PULP -DCONFIG_PROFILE_PULP -D__CONFIG_UDMA__ -fno-jump-tables \ + -fno-tree-loop-distribute-patterns -D__PULPOS2__ -D__PLATFORM__=ARCHI_PLATFORM_GVSOC -DARCHI_CLUSTER_NB_PE=8 \ + -DPOS_CONFIG_IO_UART=0 -DPOS_CONFIG_IO_UART_BAUDRATE=115200 -DPOS_CONFIG_IO_UART_ITF=0 -D__TRACE_LEVEL__=3 -DPI_LOG_LOCAL_LEVEL=2 \ + -I$(PULP_SDK_HOME)/rtos/pulpos/common/lib/libc/minimal/include -I$(PULP_SDK_HOME)/rtos/pulpos/common/include \ + -I$(PULP_SDK_HOME)/rtos/pulpos/common/kernel -I$(PULP_SDK_HOME)/rtos/pulpos/pulp_archi/include \ + -I$(PULP_SDK_HOME)/rtos/pulpos/pulp_hal/include -I$(PULP_SDK_HOME)/rtos/pmsis/pmsis_api/include -I$(PULP_SDK_HOME)/rtos/pmsis/pmsis_bsp/include -I$(PULP_SDK_HOME)/rtos/pulpos/pulp/include + +PULP_RUNTIME_LIB_LDFLAGS += -nostartfiles -nostdlib -Wl,--gc-sections \ + -L$(PULP_SDK_HOME)/ext_libs -L$(PULP_SDK_HOME)/rtos/pulpos/pulp/kernel -Tchips/pulp/link.ld -lgcc + +endif + +PULP_RUNTIME_LIBS += basic-runtime + +$(foreach runtime_lib, $(PULP_RUNTIME_LIBS), $(eval $(call declare_runtime_lib,$(runtime_lib)))) + pulpos.checkout.deps: @@ -27,3 +120,8 @@ pulpos.all: pulpos.checkout.all pulpos.build.all pulpos.artifact: rsync -avR --exclude=".git*" --exclude=__pycache__ rtos/pulpos/common rtos/pulpos/pulp rtos/pulpos/pulp_archi rtos/pulpos/pulp_hal rtos/pmsis $(ARTIFACT_PATH) + +pulpos.libs: pulpos.build.libs + +pulpos.build.libs: $(RUNTIME_LIB_TARGETS) + $(V)rm -rf $(PULP_LIBS_DIR)/$(PULP_RUNTIME_LIBS) diff --git a/tools/gvsoc/common/Makefile b/tools/gvsoc/common/Makefile index 6f7f92e1..59ef8d8e 100644 --- a/tools/gvsoc/common/Makefile +++ b/tools/gvsoc/common/Makefile @@ -13,6 +13,7 @@ GV_MAKEFLAGS ?= -j 4 INSTALL_FILES += bin/pulp-pc-info INSTALL_FILES += bin/pulp-trace-extend +INSTALL_FILES += bin/gvsoc_sim $(foreach file, $(INSTALL_FILES), $(eval $(call declareInstallFile,$(file)))) clean: diff --git a/tools/gvsoc/common/bin/gvsoc_sim b/tools/gvsoc/common/bin/gvsoc_sim new file mode 100644 index 00000000..8cc9f98d --- /dev/null +++ b/tools/gvsoc/common/bin/gvsoc_sim @@ -0,0 +1,4 @@ +#!/bin/sh -x +gapy --target=pulp --platform=gvsoc --work-dir=./ --config-opt=cluster/nb_pe=8 --config-opt=**/runner/verbose=true -v run --image --binary=$1 +gapy --target=pulp --platform=gvsoc --work-dir=./ --config-opt=cluster/nb_pe=8 --config-opt=**/runner/verbose=true -v run --flash --binary=$1 +gapy --target=pulp --platform=gvsoc --work-dir=./ --config-opt=cluster/nb_pe=8 --config-opt=**/runner/verbose=true -v run --exec-prepare --exec --binary=$1 \ No newline at end of file From 4340fdb64a3fdc589cf1a55e4c8dd79cd5d28cd3 Mon Sep 17 00:00:00 2001 From: enrico Date: Fri, 16 Jul 2021 15:47:24 +0200 Subject: [PATCH 19/22] Add CI for GVSoC testing --- .github/workflows/regression-tests.yml | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/regression-tests.yml diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml new file mode 100644 index 00000000..6d928c24 --- /dev/null +++ b/.github/workflows/regression-tests.yml @@ -0,0 +1,67 @@ +name: PULP-SDK CI +on: [push,pull_request] +jobs: + Regression-Tests: + name: Regression Tests + runs-on: ubuntu-latest + steps: + - run: echo "The job was automatically triggered by a ${{ github.event_name }} event." + - run: echo "This job is now running on a ${{ runner.os }} server hosted by GitHub!" + - run: echo "The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." + - name: Check out repository code + uses: actions/checkout@v2 + - run: echo "The ${{ github.repository }} repository has been cloned to the runner." + - run: echo "The workflow is now ready to test your code on the runner." + - name: Build COREV Toolchain + run: | + cd ${{ github.workspace }} + cd .. + git clone https://github.com/EEESlab/corev-binutils-gdb.git + git clone https://github.com/riscv/riscv-newlib.git + git clone https://github.com/EEESlab/riscv-dejagnu.git + sudo apt-get update -y + sudo apt-get install -y expect + cd riscv-dejagnu + ./configure + aclocal + automake + sudo make all install + cd ${{ github.workspace }} + source build-corev-toolchain.sh --all + cd ${{ github.workspace }} + export -p > temp_env + - name: Install PULP-SDK Dependancies + run: | + cd ${{ github.workspace }} + source temp_env + sudo apt update + sudo apt-get install -y build-essential git libftdi-dev libftdi1 doxygen python3-pip libsdl2-dev curl cmake libusb-1.0-0-dev scons gtkwave libsndfile1-dev rsync autoconf automake texinfo libtool pkg-config libsdl2-ttf-dev + pip install --user argcomplete pyelftools + cd ${{ github.workspace }} + export -p > temp_env + - name: Build PULP-SDK + run: | + cd ${{ github.workspace }} + source temp_env + cd .. + git clone https://github.com/EEESlab/gvsoc.git + cd gvsoc/ + export PULP_RISCV_GCC_TOOLCHAIN=${INSTALL_PREFIX} + source configs/pulp-open-cv32e40p.sh + make build + cd ${{ github.workspace }} + export -p > temp_env + - name: Launch regression tests + run: | + source temp_env + cd tests/ + cd hello/ + make clean all run + cd ../perf/vecmul/ + make clean all run + cd ${{ github.workspace }} + export -p > temp_env + - name: List files in the repository + run: | + ls ${{ github.workspace }} + - run: echo "This job's status is ${{ job.status }}." From 8d6e67252719d52db565de5f8e298ca553d3bc8a Mon Sep 17 00:00:00 2001 From: enrico Date: Fri, 16 Jul 2021 18:50:08 +0200 Subject: [PATCH 20/22] Lightweight SDK Introduction --- .github/workflows/regression-tests.yml | 12 +- .gitignore | 2 - Makefile | 7 +- configs/pulp-open-cv32e40p.sh | 33 - configs/pulp-open-rnnext.sh | 27 - configs/pulp-open.sh | 31 - libs/libbasic-runtime.a | Bin 0 -> 177696 bytes rtos/pmsis/pmsis_api/LICENSE | 177 - rtos/pmsis/pmsis_api/docs/Makefile | 20 - rtos/pmsis/pmsis_api/docs/chips.rst | 21 - rtos/pmsis/pmsis_api/docs/conf.py | 66 - rtos/pmsis/pmsis_api/docs/dox.mk | 10 - rtos/pmsis/pmsis_api/docs/drivers.rst | 101 - rtos/pmsis/pmsis_api/docs/headers/groups.h | 13 - rtos/pmsis/pmsis_api/docs/index.rst | 19 - rtos/pmsis/pmsis_api/docs/mainpage.md | 13 - rtos/pmsis/pmsis_api/docs/make.bat | 35 - rtos/pmsis/pmsis_api/docs/pmsis.dxy.in | 2370 -------- rtos/pmsis/pmsis_api/docs/requirements.txt | 3 - rtos/pmsis/pmsis_api/docs/rtos.rst | 18 - .../pmsis_api/include/pmsis/chips/default.h | 94 - .../pmsis_api/include/pmsis/chips/gap8/gpio.h | 121 - .../pmsis_api/include/pmsis/chips/gap8/pad.h | 254 - .../pmsis_api/include/pmsis/chips/gap8/perf.h | 134 - .../pmsis_api/include/pmsis/chips/gap8/pmu.h | 67 - .../pmsis_api/include/pmsis/chips/vega/gpio.h | 71 - .../pmsis_api/include/pmsis/chips/vega/pad.h | 55 - .../pmsis_api/include/pmsis/chips/vega/perf.h | 126 - .../include/pmsis/cluster/cl_malloc.h | 107 - .../include/pmsis/cluster/cl_pmsis_api.h | 25 - .../include/pmsis/cluster/cl_pmsis_types.h | 123 - .../cluster/cluster_sync/cl_synchronisation.h | 71 - .../cluster/cluster_sync/cl_to_fc_delegate.h | 193 - .../cluster/cluster_sync/fc_to_cl_delegate.h | 285 - .../pmsis/cluster/cluster_team/cl_team.h | 165 - .../include/pmsis/cluster/dma/cl_dma.h | 209 - .../pmsis/cluster/dma/cl_dma_decompressor.h | 112 - rtos/pmsis/pmsis_api/include/pmsis/device.h | 42 - .../pmsis_api/include/pmsis/drivers/aes.h | 144 - .../pmsis_api/include/pmsis/drivers/asrc.h | 184 - .../pmsis_api/include/pmsis/drivers/cpi.h | 220 - .../pmsis_api/include/pmsis/drivers/dmacpy.h | 138 - .../pmsis_api/include/pmsis/drivers/gpio.h | 384 -- .../include/pmsis/drivers/hyperbus.h | 562 -- .../pmsis_api/include/pmsis/drivers/i2c.h | 310 -- .../include/pmsis/drivers/i2c_slave.h | 218 - .../pmsis_api/include/pmsis/drivers/i2s.h | 776 --- .../pmsis_api/include/pmsis/drivers/octospi.h | 594 --- .../pmsis_api/include/pmsis/drivers/pad.h | 150 - .../pmsis_api/include/pmsis/drivers/perf.h | 101 - .../pmsis_api/include/pmsis/drivers/pmu.h | 182 - .../pmsis_api/include/pmsis/drivers/pwm.h | 326 -- .../pmsis_api/include/pmsis/drivers/rtc.h | 228 - .../pmsis_api/include/pmsis/drivers/spi.h | 449 -- .../pmsis_api/include/pmsis/drivers/uart.h | 457 -- rtos/pmsis/pmsis_api/include/pmsis/errno.h | 46 - rtos/pmsis/pmsis_api/include/pmsis/mem_slab.h | 91 - .../pmsis_api/include/pmsis/pmsis_types.h | 162 - .../pmsis_api/include/pmsis/rtos/assert.h | 31 - .../pmsis/rtos/event_kernel/event_kernel.h | 47 - .../include/pmsis/rtos/malloc/cl_l1_malloc.h | 120 - .../include/pmsis/rtos/malloc/fc_l1_malloc.h | 144 - .../include/pmsis/rtos/malloc/l2_malloc.h | 107 - .../pmsis/rtos/malloc/malloc_internal.h | 171 - .../include/pmsis/rtos/malloc/pi_malloc.h | 86 - .../include/pmsis/rtos/os_frontend_api/freq.h | 48 - .../include/pmsis/rtos/os_frontend_api/os.h | 319 -- .../pmsis/rtos/os_frontend_api/pmsis_time.h | 22 - .../pmsis_api/include/pmsis/rtos/pi_log.h | 282 - .../pmsis_api/include/pmsis/rtos/pulpos.h | 42 - .../pmsis/pmsis_api/include/pmsis/rtos/rtos.h | 33 - rtos/pmsis/pmsis_api/include/pmsis/task.h | 198 - rtos/pmsis/pmsis_api/jenkins/sdk.mk | 34 - rtos/pmsis/pmsis_api/tools/export.mk | 16 - rtos/pmsis/pmsis_bsp/LICENSE | 177 - rtos/pmsis/pmsis_bsp/Makefile | 7 - rtos/pmsis/pmsis_bsp/ble/ble.c | 81 - .../pmsis/pmsis_bsp/ble/nina_b112/nina_b112.c | 555 -- .../ble/nina_b112/nina_b112_defines.h | 77 - .../pmsis_bsp/ble/nina_b112/nina_b112_old.c | 295 - .../pmsis_bsp/bootloader/bootloader_utility.c | 373 -- rtos/pmsis/pmsis_bsp/bsp/ai_deck.c | 149 - rtos/pmsis/pmsis_bsp/bsp/gapoc_a.c | 230 - rtos/pmsis/pmsis_bsp/bsp/gapoc_b.c | 183 - rtos/pmsis/pmsis_bsp/bsp/gapoc_b_v2.c | 225 - rtos/pmsis/pmsis_bsp/bsp/gapuino.c | 240 - rtos/pmsis/pmsis_bsp/bsp/pulp.c | 85 - rtos/pmsis/pmsis_bsp/bsp/vega.c | 130 - rtos/pmsis/pmsis_bsp/bsp/wolfe.c | 129 - rtos/pmsis/pmsis_bsp/camera/camera.c | 45 - rtos/pmsis/pmsis_bsp/camera/gc0308/gc0308.c | 611 --- rtos/pmsis/pmsis_bsp/camera/gc0308/gc0308.h | 27 - rtos/pmsis/pmsis_bsp/camera/himax/himax.c | 367 -- rtos/pmsis/pmsis_bsp/camera/himax/himax.h | 138 - rtos/pmsis/pmsis_bsp/camera/mt9v034/mt9v034.c | 372 -- rtos/pmsis/pmsis_bsp/camera/mt9v034/mt9v034.h | 274 - rtos/pmsis/pmsis_bsp/camera/ov5640/ov5640.c | 599 --- rtos/pmsis/pmsis_bsp/camera/ov5640/ov5640.h | 31 - rtos/pmsis/pmsis_bsp/camera/ov7670/ov7670.c | 415 -- rtos/pmsis/pmsis_bsp/camera/ov7670/ov7670.h | 231 - rtos/pmsis/pmsis_bsp/camera/pixart/pixart.c | 408 -- rtos/pmsis/pmsis_bsp/camera/pixart/pixart.h | 47 - .../pmsis_bsp/camera/thermeye/thermeye.c | 350 -- .../camera/thermeye/thermeye_defines.h | 167 - rtos/pmsis/pmsis_bsp/crc/md5.c | 291 - rtos/pmsis/pmsis_bsp/display/display.c | 47 - .../pmsis/pmsis_bsp/display/ili9341/ili9341.c | 577 -- .../pmsis/pmsis_bsp/display/ili9341/ili9341.h | 373 -- rtos/pmsis/pmsis_bsp/docs/Makefile | 20 - rtos/pmsis/pmsis_bsp/docs/ble.rst | 10 - rtos/pmsis/pmsis_bsp/docs/camera.rst | 26 - rtos/pmsis/pmsis_bsp/docs/conf.py | 62 - rtos/pmsis/pmsis_bsp/docs/display.rst | 10 - rtos/pmsis/pmsis_bsp/docs/dox.mk | 10 - rtos/pmsis/pmsis_bsp/docs/flash.rst | 18 - rtos/pmsis/pmsis_bsp/docs/fs.rst | 7 - rtos/pmsis/pmsis_bsp/docs/headers/groups.h | 0 rtos/pmsis/pmsis_bsp/docs/index.rst | 22 - rtos/pmsis/pmsis_bsp/docs/mainpage.md | 21 - rtos/pmsis/pmsis_bsp/docs/make.bat | 35 - rtos/pmsis/pmsis_bsp/docs/pmsis.dxy.in | 2361 -------- rtos/pmsis/pmsis_bsp/docs/ram.rst | 18 - rtos/pmsis/pmsis_bsp/docs/requirements.txt | 3 - rtos/pmsis/pmsis_bsp/eeprom/24XX1025.c | 225 - rtos/pmsis/pmsis_bsp/eeprom/virtual_eeprom.c | 240 - rtos/pmsis/pmsis_bsp/flash/flash.c | 35 - .../pmsis_bsp/flash/hyperflash/hyperflash.c | 833 --- rtos/pmsis/pmsis_bsp/flash/mram/mram-v2.c | 799 --- rtos/pmsis/pmsis_bsp/flash/spiflash/atxp032.c | 759 --- .../pmsis/pmsis_bsp/flash/spiflash/spiflash.c | 1008 ---- rtos/pmsis/pmsis_bsp/fs/fs.c | 440 -- rtos/pmsis/pmsis_bsp/fs/host_fs/host_fs.c | 177 - rtos/pmsis/pmsis_bsp/fs/host_fs/semihost.c | 70 - rtos/pmsis/pmsis_bsp/fs/host_fs/semihost.h | 120 - rtos/pmsis/pmsis_bsp/fs/lfs/lfs.c | 4742 ----------------- rtos/pmsis/pmsis_bsp/fs/lfs/lfs_util.c | 33 - rtos/pmsis/pmsis_bsp/fs/lfs/lfs_util.h | 220 - rtos/pmsis/pmsis_bsp/fs/lfs/pi_lfs.c | 465 -- rtos/pmsis/pmsis_bsp/fs/read_fs/read_fs.c | 683 --- rtos/pmsis/pmsis_bsp/include/bsp/ai_deck.h | 58 - rtos/pmsis/pmsis_bsp/include/bsp/ble.h | 194 - .../pmsis_bsp/include/bsp/ble/nina_b112.h | 93 - .../include/bsp/ble/nina_b112/nina_b112_old.h | 242 - .../include/bsp/bootloader_utility.h | 80 - rtos/pmsis/pmsis_bsp/include/bsp/bsp.h | 192 - rtos/pmsis/pmsis_bsp/include/bsp/buffer.h | 79 - rtos/pmsis/pmsis_bsp/include/bsp/camera.h | 264 - .../pmsis_bsp/include/bsp/camera/gc0308.h | 79 - .../pmsis_bsp/include/bsp/camera/himax.h | 76 - .../pmsis_bsp/include/bsp/camera/mt9v034.h | 82 - .../pmsis_bsp/include/bsp/camera/ov5640.h | 79 - .../pmsis_bsp/include/bsp/camera/ov7670.h | 76 - .../pmsis_bsp/include/bsp/camera/pixart.h | 88 - .../pmsis_bsp/include/bsp/camera/thermeye.h | 76 - rtos/pmsis/pmsis_bsp/include/bsp/crc/md5.h | 45 - rtos/pmsis/pmsis_bsp/include/bsp/debug.h | 28 - rtos/pmsis/pmsis_bsp/include/bsp/display.h | 142 - .../pmsis_bsp/include/bsp/display/ili9341.h | 87 - rtos/pmsis/pmsis_bsp/include/bsp/eeprom.h | 186 - .../pmsis_bsp/include/bsp/eeprom/24xx1025.h | 71 - .../include/bsp/eeprom/virtual_eeprom.h | 71 - .../pmsis_bsp/include/bsp/eeprom_slave.h | 97 - rtos/pmsis/pmsis_bsp/include/bsp/flash.h | 542 -- .../pmsis_bsp/include/bsp/flash/atxp032.h | 74 - .../pmsis_bsp/include/bsp/flash/hyperflash.h | 75 - rtos/pmsis/pmsis_bsp/include/bsp/flash/mram.h | 73 - .../pmsis_bsp/include/bsp/flash/spiflash.h | 75 - .../pmsis_bsp/include/bsp/flash_partition.h | 110 - rtos/pmsis/pmsis_bsp/include/bsp/fs.h | 647 --- .../pmsis_bsp/include/bsp/fs/LICENSE_LFS.md | 24 - rtos/pmsis/pmsis_bsp/include/bsp/fs/hostfs.h | 70 - rtos/pmsis/pmsis_bsp/include/bsp/fs/lfs.h | 651 --- rtos/pmsis/pmsis_bsp/include/bsp/fs/pi_lfs.h | 82 - rtos/pmsis/pmsis_bsp/include/bsp/fs/readfs.h | 70 - rtos/pmsis/pmsis_bsp/include/bsp/gapoc_a.h | 91 - rtos/pmsis/pmsis_bsp/include/bsp/gapoc_b.h | 78 - rtos/pmsis/pmsis_bsp/include/bsp/gapoc_b_v2.h | 84 - rtos/pmsis/pmsis_bsp/include/bsp/gapuino.h | 100 - rtos/pmsis/pmsis_bsp/include/bsp/ota.h | 96 - .../pmsis/pmsis_bsp/include/bsp/ota_utility.h | 64 - rtos/pmsis/pmsis_bsp/include/bsp/partition.h | 451 -- rtos/pmsis/pmsis_bsp/include/bsp/pulp.h | 31 - rtos/pmsis/pmsis_bsp/include/bsp/ram.h | 897 ---- .../pmsis_bsp/include/bsp/ram/aps25xxxn.h | 76 - .../pmsis_bsp/include/bsp/ram/hyperram.h | 77 - rtos/pmsis/pmsis_bsp/include/bsp/ram/spiram.h | 62 - rtos/pmsis/pmsis_bsp/include/bsp/transport.h | 97 - .../include/bsp/transport/nina_w10.h | 35 - rtos/pmsis/pmsis_bsp/include/bsp/updater.h | 34 - rtos/pmsis/pmsis_bsp/include/bsp/vega.h | 48 - rtos/pmsis/pmsis_bsp/include/bsp/wolfe.h | 48 - rtos/pmsis/pmsis_bsp/jenkins/sdk.mk | 34 - rtos/pmsis/pmsis_bsp/ota/ota.c | 374 -- rtos/pmsis/pmsis_bsp/ota/ota_utility.c | 202 - rtos/pmsis/pmsis_bsp/ota/updater.c | 128 - .../pmsis_bsp/partition/flash_partition.c | 243 - rtos/pmsis/pmsis_bsp/partition/partition.c | 89 - rtos/pmsis/pmsis_bsp/ram/alloc_extern.c | 240 - rtos/pmsis/pmsis_bsp/ram/extern_alloc.h | 53 - rtos/pmsis/pmsis_bsp/ram/hyperram/hyperram.c | 268 - rtos/pmsis/pmsis_bsp/ram/ram.c | 125 - rtos/pmsis/pmsis_bsp/ram/spiram/aps25xxxn.c | 200 - rtos/pmsis/pmsis_bsp/ram/spiram/spiram.c | 301 -- .../pmsis_bsp/rules/freertos_bsp_rules.mk | 46 - rtos/pmsis/pmsis_bsp/rules/gap_sdk.mk | 180 - rtos/pmsis/pmsis_bsp/rules/pulp_sdk.mk | 25 - rtos/pmsis/pmsis_bsp/rules/pulpos.mk | 70 - rtos/pmsis/pmsis_bsp/rules/pulpos/rules.mk | 3 - rtos/pmsis/pmsis_bsp/rules/pulpos/src.mk | 101 - rtos/pmsis/pmsis_bsp/src.mk | 142 - .../transport/nina_w10/firmware/Makefile | 4 - .../transport/nina_w10/firmware/README | 8 - .../nina_w10/firmware/main/component.mk | 8 - .../nina_w10/firmware/main/nina_w10_fw.c | 574 -- .../pmsis_bsp/transport/nina_w10/nina_w10.c | 376 -- rtos/pmsis/pmsis_bsp/transport/transport.c | 82 - rtos/pmsis/pmsis_bsp/zephyr/CMakeLists.txt | 23 - rtos/pulpos/common/Makefile | 5 - rtos/pulpos/common/bin/pos-size | 144 - rtos/pulpos/common/include/pmsis.h | 79 - rtos/pulpos/common/include/pos/data/alloc.h | 71 - rtos/pulpos/common/include/pos/data/cluster.h | 72 - rtos/pulpos/common/include/pos/data/data.h | 206 - rtos/pulpos/common/include/pos/data/kernel.h | 36 - rtos/pulpos/common/include/pos/data/lock.h | 35 - rtos/pulpos/common/include/pos/data/sched.h | 34 - .../common/include/pos/data/soc_event.h | 32 - rtos/pulpos/common/include/pos/implem/alloc.h | 55 - .../common/include/pos/implem/alloc_pool.h | 28 - .../common/include/pos/implem/cluster.h | 65 - rtos/pulpos/common/include/pos/implem/dma.h | 203 - rtos/pulpos/common/include/pos/implem/freq.h | 46 - .../pulpos/common/include/pos/implem/implem.h | 174 - rtos/pulpos/common/include/pos/implem/irq.h | 193 - .../pulpos/common/include/pos/implem/kernel.h | 43 - rtos/pulpos/common/include/pos/implem/link.h | 123 - rtos/pulpos/common/include/pos/implem/lock.h | 104 - rtos/pulpos/common/include/pos/implem/pe.h | 205 - rtos/pulpos/common/include/pos/implem/perf.h | 152 - .../common/include/pos/implem/soc_event.h | 44 - rtos/pulpos/common/include/pos/implem/task.h | 123 - rtos/pulpos/common/include/pos/implem/trace.h | 146 - rtos/pulpos/common/kernel/alloc.c | 344 -- rtos/pulpos/common/kernel/alloc_pool.c | 216 - rtos/pulpos/common/kernel/crt0.S | 148 - rtos/pulpos/common/kernel/device.c | 27 - rtos/pulpos/common/kernel/fll-v1.c | 190 - rtos/pulpos/common/kernel/freq-domains.c | 52 - rtos/pulpos/common/kernel/init.c | 99 - rtos/pulpos/common/kernel/irq.c | 96 - rtos/pulpos/common/kernel/irq_asm.S | 115 - rtos/pulpos/common/kernel/kernel.c | 85 - rtos/pulpos/common/kernel/log.c | 28 - rtos/pulpos/common/kernel/soc_event.c | 44 - rtos/pulpos/common/kernel/soc_event_eu.S | 163 - rtos/pulpos/common/kernel/soc_event_v2_itc.S | 155 - rtos/pulpos/common/kernel/task.c | 76 - rtos/pulpos/common/kernel/task_asm.S | 145 - rtos/pulpos/common/kernel/time.c | 205 - rtos/pulpos/common/kernel/time_asm.S | 44 - rtos/pulpos/common/lib/libc/minimal/fprintf.c | 60 - .../common/lib/libc/minimal/include/ctype.h | 72 - .../common/lib/libc/minimal/include/io.h | 32 - .../common/lib/libc/minimal/include/stdio.h | 58 - .../common/lib/libc/minimal/include/stdlib.h | 46 - .../common/lib/libc/minimal/include/string.h | 61 - rtos/pulpos/common/lib/libc/minimal/io.c | 638 --- rtos/pulpos/common/lib/libc/minimal/prf.c | 804 --- .../pulpos/common/lib/libc/minimal/semihost.c | 66 - .../pulpos/common/lib/libc/minimal/semihost.h | 120 - rtos/pulpos/common/lib/libc/minimal/sprintf.c | 106 - rtos/pulpos/common/rules/pulpos.mk | 9 - .../common/rules/pulpos/configs/default.mk | 16 - .../common/rules/pulpos/default_rules.mk | 274 - rtos/pulpos/common/rules/pulpos/src.mk | 182 - rtos/pulpos/pulp/drivers/cluster/cluster.c | 384 -- rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S | 330 -- .../pulp/drivers/hyperbus/hyperbus-v3.c | 351 -- rtos/pulpos/pulp/drivers/uart/uart-v1.c | 351 -- rtos/pulpos/pulp/drivers/udma/udma-v3.c | 102 - .../pulp/include/pos/chips/pulp/config.h | 35 - rtos/pulpos/pulp/include/pos/chips/pulp/soc.h | 54 - rtos/pulpos/pulp/include/pos/data/hyperbus.h | 48 - rtos/pulpos/pulp/include/pos/data/spim.h | 141 - rtos/pulpos/pulp/include/pos/data/uart.h | 67 - rtos/pulpos/pulp/include/pos/data/udma-v3.h | 48 - rtos/pulpos/pulp/include/pos/implem/udma-v3.h | 38 - rtos/pulpos/pulp/kernel/chips/pulp/soc.c | 34 - rtos/pulpos/pulp/rules/pulpos/targets/pulp.mk | 48 - .../rules/pulpos/targets/pulp_cv32e40p.mk | 48 - rules/gvsoc.mk | 6 +- rules/pulpos.mk | 6 +- configs/common.sh => sourceme.sh | 30 + tests/hello/Makefile | 87 +- tests/hello/test.c | 51 +- tests/perf/vecmul/Makefile | 82 +- tests/perf/vecmul/test.c | 2 +- tools/dpi-models/LICENSE | 177 - tools/dpi-models/Makefile | 99 - tools/dpi-models/dpi_rules.mk | 45 - tools/dpi-models/ext/nosv/svdpi.h | 27 - .../ext/sv/include/questa/dpiheader.h | 378 -- .../include/common/.telnet_proxy.hpp.swp | Bin 12288 -> 0 bytes .../include/common/telnet_proxy.hpp | 34 - tools/dpi-models/include/dpi/models.hpp | 202 - tools/dpi-models/include/dpi/tb_driver.h | 133 - tools/dpi-models/models/Makefile | 14 - tools/dpi-models/models/camera/Makefile | 12 - tools/dpi-models/models/camera/camera.cpp | 544 -- tools/dpi-models/models/eeprom/Makefile | 3 - tools/dpi-models/models/eeprom/eeprom.cpp | 188 - .../dpi-models/models/flash/spiflash/Makefile | 3 - .../models/flash/spiflash/spiflash.cpp | 706 --- tools/dpi-models/models/jtag/proxy/Makefile | 3 - tools/dpi-models/models/jtag/proxy/proxy.cpp | 422 -- tools/dpi-models/models/lcd/Makefile | 11 - tools/dpi-models/models/lcd/ili9341.cpp | 616 --- tools/dpi-models/models/microphone/Makefile | 11 - .../models/microphone/i2s_microphone.cpp | 538 -- tools/dpi-models/models/ram/spiram/Makefile | 3 - tools/dpi-models/models/ram/spiram/spiram.cpp | 424 -- .../models/test/spim_verif/Makefile | 3 - .../models/test/spim_verif/spim_verif.cpp | 513 -- tools/dpi-models/models/uart/uart/Makefile | 3 - tools/dpi-models/models/uart/uart/uart.cpp | 323 -- tools/dpi-models/models/wifi/Makefile | 3 - tools/dpi-models/models/wifi/nina_w10.cpp | 297 -- tools/dpi-models/src/cpi.cpp | 37 - tools/dpi-models/src/ctrl.cpp | 43 - tools/dpi-models/src/dpi.cpp | 375 -- tools/dpi-models/src/gpio.cpp | 43 - tools/dpi-models/src/i2c.cpp | 43 - tools/dpi-models/src/i2s.cpp | 43 - tools/dpi-models/src/jtag.cpp | 37 - tools/dpi-models/src/models.cpp | 440 -- tools/dpi-models/src/qspim.cpp | 62 - tools/dpi-models/src/telnet_proxy.cpp | 192 - tools/dpi-models/src/uart.cpp | 43 - tools/jenkins/sdk_version.txt | 1 - tools/pulp-debug-bridge/LICENSE | 177 - tools/pulp-debug-bridge/Makefile | 108 - tools/pulp-debug-bridge/README.md | 112 - tools/pulp-debug-bridge/bin/flasher-gap | Bin 768888 -> 0 bytes tools/pulp-debug-bridge/bin/plpbridge | 479 -- .../include/debug_bridge/debug_bridge.h | 215 - .../include/debug_bridge/proxy.hpp | 66 - .../python/bridge/chips/arnold.py | 108 - .../python/bridge/chips/fulmine.py | 33 - .../python/bridge/chips/gap.py | 217 - .../python/bridge/chips/gap8_revc.py | 237 - .../python/bridge/chips/gap_rev1.py | 237 - .../python/bridge/chips/pulp.py | 85 - .../python/bridge/chips/pulpissimo.py | 85 - .../python/bridge/chips/pulpissimo_v2.py | 108 - .../python/bridge/chips/usoc_v1.py | 90 - .../python/bridge/chips/vega.py | 182 - .../python/bridge/chips/vivosoc3.py | 36 - .../python/bridge/chips/wolfe.py | 90 - .../python/bridge/debug_bridge.py | 66 - .../python/bridge/default_debug_bridge.py | 576 -- tools/pulp-debug-bridge/python/bridge/jtag.py | 283 - tools/pulp-debug-bridge/src/cable.hpp | 90 - .../src/cables/adv_dbg_itf/adv_dbg_itf.cpp | 1423 ----- .../src/cables/adv_dbg_itf/adv_dbg_itf.hpp | 144 - .../src/cables/ftdi/ftdi.cpp | 941 ---- .../src/cables/ftdi/ftdi.hpp | 124 - .../src/cables/jtag-proxy/jtag-proxy.cpp | 167 - .../src/cables/jtag-proxy/jtag-proxy.hpp | 53 - tools/pulp-debug-bridge/src/cables/jtag.cpp | 128 - tools/pulp-debug-bridge/src/cables/log.h | 41 - .../src/gdb-server/breakpoints.cpp | 176 - .../src/gdb-server/gdb-server.cpp | 53 - .../src/gdb-server/gdb-server.h | 102 - .../src/gdb-server/gdb-server.hpp | 260 - .../pulp-debug-bridge/src/gdb-server/rsp.cpp | 1146 ---- .../src/gdb-server/target.cpp | 741 --- .../pulp-debug-bridge/src/python_wrapper.cpp | 295 - tools/pulp-debug-bridge/src/reqloop.cpp | 1181 ---- 378 files changed, 203 insertions(+), 72761 deletions(-) delete mode 100644 configs/pulp-open-cv32e40p.sh delete mode 100644 configs/pulp-open-rnnext.sh delete mode 100644 configs/pulp-open.sh create mode 100644 libs/libbasic-runtime.a delete mode 100644 rtos/pmsis/pmsis_api/LICENSE delete mode 100644 rtos/pmsis/pmsis_api/docs/Makefile delete mode 100644 rtos/pmsis/pmsis_api/docs/chips.rst delete mode 100644 rtos/pmsis/pmsis_api/docs/conf.py delete mode 100644 rtos/pmsis/pmsis_api/docs/dox.mk delete mode 100644 rtos/pmsis/pmsis_api/docs/drivers.rst delete mode 100755 rtos/pmsis/pmsis_api/docs/headers/groups.h delete mode 100644 rtos/pmsis/pmsis_api/docs/index.rst delete mode 100644 rtos/pmsis/pmsis_api/docs/mainpage.md delete mode 100644 rtos/pmsis/pmsis_api/docs/make.bat delete mode 100755 rtos/pmsis/pmsis_api/docs/pmsis.dxy.in delete mode 100644 rtos/pmsis/pmsis_api/docs/requirements.txt delete mode 100644 rtos/pmsis/pmsis_api/docs/rtos.rst delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/default.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/gap8/gpio.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/gap8/pad.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/gap8/perf.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/gap8/pmu.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/vega/gpio.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/vega/pad.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/vega/perf.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cl_malloc.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cl_pmsis_api.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cl_pmsis_types.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cluster_sync/cl_synchronisation.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cluster_sync/cl_to_fc_delegate.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cluster_sync/fc_to_cl_delegate.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cluster_team/cl_team.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/dma/cl_dma.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/dma/cl_dma_decompressor.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/device.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/aes.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/asrc.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/cpi.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/dmacpy.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/gpio.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/hyperbus.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/i2c.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/i2c_slave.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/i2s.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/octospi.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/pad.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/perf.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/pmu.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/pwm.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/rtc.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/spi.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/uart.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/errno.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/mem_slab.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/pmsis_types.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/assert.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/event_kernel/event_kernel.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/malloc/cl_l1_malloc.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/malloc/fc_l1_malloc.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/malloc/l2_malloc.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/malloc/malloc_internal.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/malloc/pi_malloc.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/os_frontend_api/freq.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/os_frontend_api/os.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/os_frontend_api/pmsis_time.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/pi_log.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/pulpos.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/rtos.h delete mode 100644 rtos/pmsis/pmsis_api/include/pmsis/task.h delete mode 100644 rtos/pmsis/pmsis_api/jenkins/sdk.mk delete mode 100644 rtos/pmsis/pmsis_api/tools/export.mk delete mode 100644 rtos/pmsis/pmsis_bsp/LICENSE delete mode 100644 rtos/pmsis/pmsis_bsp/Makefile delete mode 100644 rtos/pmsis/pmsis_bsp/ble/ble.c delete mode 100644 rtos/pmsis/pmsis_bsp/ble/nina_b112/nina_b112.c delete mode 100644 rtos/pmsis/pmsis_bsp/ble/nina_b112/nina_b112_defines.h delete mode 100644 rtos/pmsis/pmsis_bsp/ble/nina_b112/nina_b112_old.c delete mode 100644 rtos/pmsis/pmsis_bsp/bootloader/bootloader_utility.c delete mode 100644 rtos/pmsis/pmsis_bsp/bsp/ai_deck.c delete mode 100644 rtos/pmsis/pmsis_bsp/bsp/gapoc_a.c delete mode 100644 rtos/pmsis/pmsis_bsp/bsp/gapoc_b.c delete mode 100644 rtos/pmsis/pmsis_bsp/bsp/gapoc_b_v2.c delete mode 100644 rtos/pmsis/pmsis_bsp/bsp/gapuino.c delete mode 100644 rtos/pmsis/pmsis_bsp/bsp/pulp.c delete mode 100644 rtos/pmsis/pmsis_bsp/bsp/vega.c delete mode 100644 rtos/pmsis/pmsis_bsp/bsp/wolfe.c delete mode 100644 rtos/pmsis/pmsis_bsp/camera/camera.c delete mode 100755 rtos/pmsis/pmsis_bsp/camera/gc0308/gc0308.c delete mode 100755 rtos/pmsis/pmsis_bsp/camera/gc0308/gc0308.h delete mode 100644 rtos/pmsis/pmsis_bsp/camera/himax/himax.c delete mode 100644 rtos/pmsis/pmsis_bsp/camera/himax/himax.h delete mode 100644 rtos/pmsis/pmsis_bsp/camera/mt9v034/mt9v034.c delete mode 100644 rtos/pmsis/pmsis_bsp/camera/mt9v034/mt9v034.h delete mode 100755 rtos/pmsis/pmsis_bsp/camera/ov5640/ov5640.c delete mode 100755 rtos/pmsis/pmsis_bsp/camera/ov5640/ov5640.h delete mode 100755 rtos/pmsis/pmsis_bsp/camera/ov7670/ov7670.c delete mode 100755 rtos/pmsis/pmsis_bsp/camera/ov7670/ov7670.h delete mode 100644 rtos/pmsis/pmsis_bsp/camera/pixart/pixart.c delete mode 100644 rtos/pmsis/pmsis_bsp/camera/pixart/pixart.h delete mode 100644 rtos/pmsis/pmsis_bsp/camera/thermeye/thermeye.c delete mode 100644 rtos/pmsis/pmsis_bsp/camera/thermeye/thermeye_defines.h delete mode 100644 rtos/pmsis/pmsis_bsp/crc/md5.c delete mode 100644 rtos/pmsis/pmsis_bsp/display/display.c delete mode 100644 rtos/pmsis/pmsis_bsp/display/ili9341/ili9341.c delete mode 100644 rtos/pmsis/pmsis_bsp/display/ili9341/ili9341.h delete mode 100644 rtos/pmsis/pmsis_bsp/docs/Makefile delete mode 100644 rtos/pmsis/pmsis_bsp/docs/ble.rst delete mode 100644 rtos/pmsis/pmsis_bsp/docs/camera.rst delete mode 100644 rtos/pmsis/pmsis_bsp/docs/conf.py delete mode 100644 rtos/pmsis/pmsis_bsp/docs/display.rst delete mode 100644 rtos/pmsis/pmsis_bsp/docs/dox.mk delete mode 100644 rtos/pmsis/pmsis_bsp/docs/flash.rst delete mode 100644 rtos/pmsis/pmsis_bsp/docs/fs.rst delete mode 100755 rtos/pmsis/pmsis_bsp/docs/headers/groups.h delete mode 100644 rtos/pmsis/pmsis_bsp/docs/index.rst delete mode 100644 rtos/pmsis/pmsis_bsp/docs/mainpage.md delete mode 100644 rtos/pmsis/pmsis_bsp/docs/make.bat delete mode 100755 rtos/pmsis/pmsis_bsp/docs/pmsis.dxy.in delete mode 100644 rtos/pmsis/pmsis_bsp/docs/ram.rst delete mode 100644 rtos/pmsis/pmsis_bsp/docs/requirements.txt delete mode 100644 rtos/pmsis/pmsis_bsp/eeprom/24XX1025.c delete mode 100644 rtos/pmsis/pmsis_bsp/eeprom/virtual_eeprom.c delete mode 100644 rtos/pmsis/pmsis_bsp/flash/flash.c delete mode 100644 rtos/pmsis/pmsis_bsp/flash/hyperflash/hyperflash.c delete mode 100644 rtos/pmsis/pmsis_bsp/flash/mram/mram-v2.c delete mode 100644 rtos/pmsis/pmsis_bsp/flash/spiflash/atxp032.c delete mode 100644 rtos/pmsis/pmsis_bsp/flash/spiflash/spiflash.c delete mode 100644 rtos/pmsis/pmsis_bsp/fs/fs.c delete mode 100644 rtos/pmsis/pmsis_bsp/fs/host_fs/host_fs.c delete mode 100644 rtos/pmsis/pmsis_bsp/fs/host_fs/semihost.c delete mode 100644 rtos/pmsis/pmsis_bsp/fs/host_fs/semihost.h delete mode 100644 rtos/pmsis/pmsis_bsp/fs/lfs/lfs.c delete mode 100644 rtos/pmsis/pmsis_bsp/fs/lfs/lfs_util.c delete mode 100644 rtos/pmsis/pmsis_bsp/fs/lfs/lfs_util.h delete mode 100644 rtos/pmsis/pmsis_bsp/fs/lfs/pi_lfs.c delete mode 100644 rtos/pmsis/pmsis_bsp/fs/read_fs/read_fs.c delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/ai_deck.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/ble.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/ble/nina_b112.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/ble/nina_b112/nina_b112_old.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/bootloader_utility.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/bsp.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/buffer.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/camera.h delete mode 100755 rtos/pmsis/pmsis_bsp/include/bsp/camera/gc0308.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/camera/himax.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/camera/mt9v034.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/camera/ov5640.h delete mode 100755 rtos/pmsis/pmsis_bsp/include/bsp/camera/ov7670.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/camera/pixart.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/camera/thermeye.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/crc/md5.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/debug.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/display.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/display/ili9341.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/eeprom.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/eeprom/24xx1025.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/eeprom/virtual_eeprom.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/eeprom_slave.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/flash.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/flash/atxp032.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/flash/hyperflash.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/flash/mram.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/flash/spiflash.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/flash_partition.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/fs.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/fs/LICENSE_LFS.md delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/fs/hostfs.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/fs/lfs.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/fs/pi_lfs.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/fs/readfs.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/gapoc_a.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/gapoc_b.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/gapoc_b_v2.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/gapuino.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/ota.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/ota_utility.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/partition.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/pulp.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/ram.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/ram/aps25xxxn.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/ram/hyperram.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/ram/spiram.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/transport.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/transport/nina_w10.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/updater.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/vega.h delete mode 100644 rtos/pmsis/pmsis_bsp/include/bsp/wolfe.h delete mode 100644 rtos/pmsis/pmsis_bsp/jenkins/sdk.mk delete mode 100644 rtos/pmsis/pmsis_bsp/ota/ota.c delete mode 100644 rtos/pmsis/pmsis_bsp/ota/ota_utility.c delete mode 100644 rtos/pmsis/pmsis_bsp/ota/updater.c delete mode 100644 rtos/pmsis/pmsis_bsp/partition/flash_partition.c delete mode 100644 rtos/pmsis/pmsis_bsp/partition/partition.c delete mode 100644 rtos/pmsis/pmsis_bsp/ram/alloc_extern.c delete mode 100644 rtos/pmsis/pmsis_bsp/ram/extern_alloc.h delete mode 100644 rtos/pmsis/pmsis_bsp/ram/hyperram/hyperram.c delete mode 100644 rtos/pmsis/pmsis_bsp/ram/ram.c delete mode 100644 rtos/pmsis/pmsis_bsp/ram/spiram/aps25xxxn.c delete mode 100644 rtos/pmsis/pmsis_bsp/ram/spiram/spiram.c delete mode 100644 rtos/pmsis/pmsis_bsp/rules/freertos_bsp_rules.mk delete mode 100644 rtos/pmsis/pmsis_bsp/rules/gap_sdk.mk delete mode 100644 rtos/pmsis/pmsis_bsp/rules/pulp_sdk.mk delete mode 100644 rtos/pmsis/pmsis_bsp/rules/pulpos.mk delete mode 100644 rtos/pmsis/pmsis_bsp/rules/pulpos/rules.mk delete mode 100644 rtos/pmsis/pmsis_bsp/rules/pulpos/src.mk delete mode 100644 rtos/pmsis/pmsis_bsp/src.mk delete mode 100644 rtos/pmsis/pmsis_bsp/transport/nina_w10/firmware/Makefile delete mode 100644 rtos/pmsis/pmsis_bsp/transport/nina_w10/firmware/README delete mode 100644 rtos/pmsis/pmsis_bsp/transport/nina_w10/firmware/main/component.mk delete mode 100644 rtos/pmsis/pmsis_bsp/transport/nina_w10/firmware/main/nina_w10_fw.c delete mode 100644 rtos/pmsis/pmsis_bsp/transport/nina_w10/nina_w10.c delete mode 100644 rtos/pmsis/pmsis_bsp/transport/transport.c delete mode 100644 rtos/pmsis/pmsis_bsp/zephyr/CMakeLists.txt delete mode 100644 rtos/pulpos/common/Makefile delete mode 100755 rtos/pulpos/common/bin/pos-size delete mode 100644 rtos/pulpos/common/include/pmsis.h delete mode 100644 rtos/pulpos/common/include/pos/data/alloc.h delete mode 100644 rtos/pulpos/common/include/pos/data/cluster.h delete mode 100644 rtos/pulpos/common/include/pos/data/data.h delete mode 100644 rtos/pulpos/common/include/pos/data/kernel.h delete mode 100644 rtos/pulpos/common/include/pos/data/lock.h delete mode 100644 rtos/pulpos/common/include/pos/data/sched.h delete mode 100644 rtos/pulpos/common/include/pos/data/soc_event.h delete mode 100644 rtos/pulpos/common/include/pos/implem/alloc.h delete mode 100644 rtos/pulpos/common/include/pos/implem/alloc_pool.h delete mode 100644 rtos/pulpos/common/include/pos/implem/cluster.h delete mode 100644 rtos/pulpos/common/include/pos/implem/dma.h delete mode 100644 rtos/pulpos/common/include/pos/implem/freq.h delete mode 100644 rtos/pulpos/common/include/pos/implem/implem.h delete mode 100644 rtos/pulpos/common/include/pos/implem/irq.h delete mode 100644 rtos/pulpos/common/include/pos/implem/kernel.h delete mode 100644 rtos/pulpos/common/include/pos/implem/link.h delete mode 100644 rtos/pulpos/common/include/pos/implem/lock.h delete mode 100644 rtos/pulpos/common/include/pos/implem/pe.h delete mode 100644 rtos/pulpos/common/include/pos/implem/perf.h delete mode 100644 rtos/pulpos/common/include/pos/implem/soc_event.h delete mode 100644 rtos/pulpos/common/include/pos/implem/task.h delete mode 100644 rtos/pulpos/common/include/pos/implem/trace.h delete mode 100644 rtos/pulpos/common/kernel/alloc.c delete mode 100644 rtos/pulpos/common/kernel/alloc_pool.c delete mode 100644 rtos/pulpos/common/kernel/crt0.S delete mode 100644 rtos/pulpos/common/kernel/device.c delete mode 100644 rtos/pulpos/common/kernel/fll-v1.c delete mode 100644 rtos/pulpos/common/kernel/freq-domains.c delete mode 100644 rtos/pulpos/common/kernel/init.c delete mode 100644 rtos/pulpos/common/kernel/irq.c delete mode 100644 rtos/pulpos/common/kernel/irq_asm.S delete mode 100644 rtos/pulpos/common/kernel/kernel.c delete mode 100644 rtos/pulpos/common/kernel/log.c delete mode 100644 rtos/pulpos/common/kernel/soc_event.c delete mode 100644 rtos/pulpos/common/kernel/soc_event_eu.S delete mode 100644 rtos/pulpos/common/kernel/soc_event_v2_itc.S delete mode 100644 rtos/pulpos/common/kernel/task.c delete mode 100644 rtos/pulpos/common/kernel/task_asm.S delete mode 100644 rtos/pulpos/common/kernel/time.c delete mode 100644 rtos/pulpos/common/kernel/time_asm.S delete mode 100644 rtos/pulpos/common/lib/libc/minimal/fprintf.c delete mode 100644 rtos/pulpos/common/lib/libc/minimal/include/ctype.h delete mode 100644 rtos/pulpos/common/lib/libc/minimal/include/io.h delete mode 100644 rtos/pulpos/common/lib/libc/minimal/include/stdio.h delete mode 100644 rtos/pulpos/common/lib/libc/minimal/include/stdlib.h delete mode 100644 rtos/pulpos/common/lib/libc/minimal/include/string.h delete mode 100644 rtos/pulpos/common/lib/libc/minimal/io.c delete mode 100644 rtos/pulpos/common/lib/libc/minimal/prf.c delete mode 100644 rtos/pulpos/common/lib/libc/minimal/semihost.c delete mode 100644 rtos/pulpos/common/lib/libc/minimal/semihost.h delete mode 100644 rtos/pulpos/common/lib/libc/minimal/sprintf.c delete mode 100644 rtos/pulpos/common/rules/pulpos.mk delete mode 100644 rtos/pulpos/common/rules/pulpos/configs/default.mk delete mode 100644 rtos/pulpos/common/rules/pulpos/default_rules.mk delete mode 100644 rtos/pulpos/common/rules/pulpos/src.mk delete mode 100644 rtos/pulpos/pulp/drivers/cluster/cluster.c delete mode 100644 rtos/pulpos/pulp/drivers/cluster/pe-eu-v3.S delete mode 100644 rtos/pulpos/pulp/drivers/hyperbus/hyperbus-v3.c delete mode 100644 rtos/pulpos/pulp/drivers/uart/uart-v1.c delete mode 100644 rtos/pulpos/pulp/drivers/udma/udma-v3.c delete mode 100644 rtos/pulpos/pulp/include/pos/chips/pulp/config.h delete mode 100644 rtos/pulpos/pulp/include/pos/chips/pulp/soc.h delete mode 100644 rtos/pulpos/pulp/include/pos/data/hyperbus.h delete mode 100644 rtos/pulpos/pulp/include/pos/data/spim.h delete mode 100644 rtos/pulpos/pulp/include/pos/data/uart.h delete mode 100644 rtos/pulpos/pulp/include/pos/data/udma-v3.h delete mode 100644 rtos/pulpos/pulp/include/pos/implem/udma-v3.h delete mode 100644 rtos/pulpos/pulp/kernel/chips/pulp/soc.c delete mode 100644 rtos/pulpos/pulp/rules/pulpos/targets/pulp.mk delete mode 100644 rtos/pulpos/pulp/rules/pulpos/targets/pulp_cv32e40p.mk rename configs/common.sh => sourceme.sh (66%) mode change 100644 => 100755 delete mode 100644 tools/dpi-models/LICENSE delete mode 100644 tools/dpi-models/Makefile delete mode 100644 tools/dpi-models/dpi_rules.mk delete mode 100644 tools/dpi-models/ext/nosv/svdpi.h delete mode 100644 tools/dpi-models/ext/sv/include/questa/dpiheader.h delete mode 100644 tools/dpi-models/include/common/.telnet_proxy.hpp.swp delete mode 100644 tools/dpi-models/include/common/telnet_proxy.hpp delete mode 100644 tools/dpi-models/include/dpi/models.hpp delete mode 100644 tools/dpi-models/include/dpi/tb_driver.h delete mode 100644 tools/dpi-models/models/Makefile delete mode 100644 tools/dpi-models/models/camera/Makefile delete mode 100644 tools/dpi-models/models/camera/camera.cpp delete mode 100644 tools/dpi-models/models/eeprom/Makefile delete mode 100644 tools/dpi-models/models/eeprom/eeprom.cpp delete mode 100644 tools/dpi-models/models/flash/spiflash/Makefile delete mode 100644 tools/dpi-models/models/flash/spiflash/spiflash.cpp delete mode 100644 tools/dpi-models/models/jtag/proxy/Makefile delete mode 100644 tools/dpi-models/models/jtag/proxy/proxy.cpp delete mode 100644 tools/dpi-models/models/lcd/Makefile delete mode 100644 tools/dpi-models/models/lcd/ili9341.cpp delete mode 100644 tools/dpi-models/models/microphone/Makefile delete mode 100644 tools/dpi-models/models/microphone/i2s_microphone.cpp delete mode 100644 tools/dpi-models/models/ram/spiram/Makefile delete mode 100644 tools/dpi-models/models/ram/spiram/spiram.cpp delete mode 100644 tools/dpi-models/models/test/spim_verif/Makefile delete mode 100644 tools/dpi-models/models/test/spim_verif/spim_verif.cpp delete mode 100644 tools/dpi-models/models/uart/uart/Makefile delete mode 100644 tools/dpi-models/models/uart/uart/uart.cpp delete mode 100644 tools/dpi-models/models/wifi/Makefile delete mode 100644 tools/dpi-models/models/wifi/nina_w10.cpp delete mode 100644 tools/dpi-models/src/cpi.cpp delete mode 100644 tools/dpi-models/src/ctrl.cpp delete mode 100644 tools/dpi-models/src/dpi.cpp delete mode 100644 tools/dpi-models/src/gpio.cpp delete mode 100644 tools/dpi-models/src/i2c.cpp delete mode 100644 tools/dpi-models/src/i2s.cpp delete mode 100644 tools/dpi-models/src/jtag.cpp delete mode 100644 tools/dpi-models/src/models.cpp delete mode 100644 tools/dpi-models/src/qspim.cpp delete mode 100644 tools/dpi-models/src/telnet_proxy.cpp delete mode 100644 tools/dpi-models/src/uart.cpp delete mode 100644 tools/jenkins/sdk_version.txt delete mode 100644 tools/pulp-debug-bridge/LICENSE delete mode 100644 tools/pulp-debug-bridge/Makefile delete mode 100644 tools/pulp-debug-bridge/README.md delete mode 100755 tools/pulp-debug-bridge/bin/flasher-gap delete mode 100755 tools/pulp-debug-bridge/bin/plpbridge delete mode 100644 tools/pulp-debug-bridge/include/debug_bridge/debug_bridge.h delete mode 100644 tools/pulp-debug-bridge/include/debug_bridge/proxy.hpp delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/arnold.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/fulmine.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/gap.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/gap8_revc.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/gap_rev1.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/pulp.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/pulpissimo.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/pulpissimo_v2.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/usoc_v1.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/vega.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/vivosoc3.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/chips/wolfe.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/debug_bridge.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/default_debug_bridge.py delete mode 100644 tools/pulp-debug-bridge/python/bridge/jtag.py delete mode 100644 tools/pulp-debug-bridge/src/cable.hpp delete mode 100644 tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.cpp delete mode 100644 tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.hpp delete mode 100644 tools/pulp-debug-bridge/src/cables/ftdi/ftdi.cpp delete mode 100644 tools/pulp-debug-bridge/src/cables/ftdi/ftdi.hpp delete mode 100644 tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.cpp delete mode 100644 tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.hpp delete mode 100644 tools/pulp-debug-bridge/src/cables/jtag.cpp delete mode 100644 tools/pulp-debug-bridge/src/cables/log.h delete mode 100644 tools/pulp-debug-bridge/src/gdb-server/breakpoints.cpp delete mode 100644 tools/pulp-debug-bridge/src/gdb-server/gdb-server.cpp delete mode 100644 tools/pulp-debug-bridge/src/gdb-server/gdb-server.h delete mode 100644 tools/pulp-debug-bridge/src/gdb-server/gdb-server.hpp delete mode 100644 tools/pulp-debug-bridge/src/gdb-server/rsp.cpp delete mode 100644 tools/pulp-debug-bridge/src/gdb-server/target.cpp delete mode 100644 tools/pulp-debug-bridge/src/python_wrapper.cpp delete mode 100644 tools/pulp-debug-bridge/src/reqloop.cpp diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 6d928c24..8d0c9718 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -1,4 +1,4 @@ -name: PULP-SDK CI +name: GVSoC CI on: [push,pull_request] jobs: Regression-Tests: @@ -16,6 +16,7 @@ jobs: run: | cd ${{ github.workspace }} cd .. + git clone https://github.com/EEESlab/corev-gcc.git git clone https://github.com/EEESlab/corev-binutils-gdb.git git clone https://github.com/riscv/riscv-newlib.git git clone https://github.com/EEESlab/riscv-dejagnu.git @@ -26,7 +27,7 @@ jobs: aclocal automake sudo make all install - cd ${{ github.workspace }} + cd ../corev-gcc source build-corev-toolchain.sh --all cd ${{ github.workspace }} export -p > temp_env @@ -43,9 +44,6 @@ jobs: run: | cd ${{ github.workspace }} source temp_env - cd .. - git clone https://github.com/EEESlab/gvsoc.git - cd gvsoc/ export PULP_RISCV_GCC_TOOLCHAIN=${INSTALL_PREFIX} source configs/pulp-open-cv32e40p.sh make build @@ -53,9 +51,9 @@ jobs: export -p > temp_env - name: Launch regression tests run: | + cd ${{ github.workspace }} source temp_env - cd tests/ - cd hello/ + cd tests/hello/ make clean all run cd ../perf/vecmul/ make clean all run diff --git a/.gitignore b/.gitignore index 49ebc59c..c572b247 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,4 @@ __pycache__ build/ install/ .sconsign.dblite -libs/ -ext_libs/ BUILD/ diff --git a/Makefile b/Makefile index dedf689c..e5b16750 100644 --- a/Makefile +++ b/Makefile @@ -10,11 +10,12 @@ export BUILD_DIR include rules/json-tools.mk include rules/gap-configs.mk -include rules/pulp-debug-bridge.mk -include rules/dpi-models.mk include rules/gvsoc.mk include rules/pulpos.mk -build: gvsoc.build.all pulpos.libs +build: source gvsoc.build.all clean: gvsoc.clean + +source: + source sourceme.sh \ No newline at end of file diff --git a/configs/pulp-open-cv32e40p.sh b/configs/pulp-open-cv32e40p.sh deleted file mode 100644 index 12f8d15c..00000000 --- a/configs/pulp-open-cv32e40p.sh +++ /dev/null @@ -1,33 +0,0 @@ -#! /bin/bash - - -if [ -n "${ZSH_VERSION:-}" ]; then - DIR="$(readlink -f -- "${(%):-%x}")" - DIRNAME="$(dirname $DIR)" - PULP_SDK_HOME=$(dirname $DIRNAME) - export PULP_SDK_HOME - #echo $(dirname "$(readlink -f ${(%):-%N})") -else - export PULP_SDK_HOME="$(dirname $(dirname "$(readlink -f "${BASH_SOURCE[0]}")"))" -fi - -export TARGET_CHIP_FAMILY="PULP" -export TARGET_CHIP="PULP" -export TARGET_NAME="pulp" -export BOARD_NAME=pulp -export PULP_CURRENT_CONFIG=$BOARD_NAME@config_file=config/${BOARD_NAME}_cv32e40p.json - -export PULPOS_BOARD=pulp -export PULPOS_BOARD_VERSION=pulp -export PULPOS_BOARD_PROFILE=pulp -export PULPOS_TARGET=pulp_cv32e40p -export PULPOS_SYSTEM=pulp -export GAPY_TARGET=pulp_cv32e40p - -export CHIP_CORE_ISA=corev - -export PULPOS_MODULES="$PULP_SDK_HOME/rtos/pulpos/pulp $PULP_SDK_HOME/rtos/pmsis/pmsis_bsp" - -export GVSOC_MODULES="$PULP_SDK_HOME/tools/gvsoc/common $PULP_SDK_HOME/tools/gvsoc/pulp/models" - -source $PULP_SDK_HOME/configs/common.sh diff --git a/configs/pulp-open-rnnext.sh b/configs/pulp-open-rnnext.sh deleted file mode 100644 index cb296032..00000000 --- a/configs/pulp-open-rnnext.sh +++ /dev/null @@ -1,27 +0,0 @@ -#! /bin/bash - - -if [ -n "${ZSH_VERSION:-}" ]; then - DIR="$(readlink -f -- "${(%):-%x}")" - DIRNAME="$(dirname $DIR)" - PULP_SDK_HOME=$(dirname $DIRNAME) - export PULP_SDK_HOME - #echo $(dirname "$(readlink -f ${(%):-%N})") -else - export PULP_SDK_HOME="$(dirname $(dirname "$(readlink -f "${BASH_SOURCE[0]}")"))" -fi - -export TARGET_CHIP_FAMILY="PULP" -export TARGET_CHIP="PULP" -export TARGET_NAME="pulp" -export BOARD_NAME=pulp -export PULP_CURRENT_CONFIG=$BOARD_NAME@config_file=config/pulp_rnnext.json - -export PULPOS_BOARD=pulp -export PULPOS_BOARD_VERSION=pulp -export PULPOS_BOARD_PROFILE=pulp -export PULPOS_TARGET=pulp -export PULPOS_SYSTEM=pulp -export GAPY_TARGET=pulp_rnnext - -source $PULP_SDK_HOME/configs/common.sh diff --git a/configs/pulp-open.sh b/configs/pulp-open.sh deleted file mode 100644 index 23a25aba..00000000 --- a/configs/pulp-open.sh +++ /dev/null @@ -1,31 +0,0 @@ -#! /bin/bash - - -if [ -n "${ZSH_VERSION:-}" ]; then - DIR="$(readlink -f -- "${(%):-%x}")" - DIRNAME="$(dirname $DIR)" - PULP_SDK_HOME=$(dirname $DIRNAME) - export PULP_SDK_HOME - #echo $(dirname "$(readlink -f ${(%):-%N})") -else - export PULP_SDK_HOME="$(dirname $(dirname "$(readlink -f "${BASH_SOURCE[0]}")"))" -fi - -export TARGET_CHIP_FAMILY="PULP" -export TARGET_CHIP="PULP" -export TARGET_NAME="pulp" -export BOARD_NAME=pulp -export PULP_CURRENT_CONFIG=$BOARD_NAME@config_file=config/$BOARD_NAME.json - -export PULPOS_BOARD=pulp -export PULPOS_BOARD_VERSION=pulp -export PULPOS_BOARD_PROFILE=pulp -export PULPOS_TARGET=pulp -export PULPOS_SYSTEM=pulp -export GAPY_TARGET=pulp - -export PULPOS_MODULES="$PULP_SDK_HOME/rtos/pulpos/pulp $PULP_SDK_HOME/rtos/pmsis/pmsis_bsp" - -export GVSOC_MODULES="$PULP_SDK_HOME/tools/gvsoc/common $PULP_SDK_HOME/tools/gvsoc/pulp/models" - -source $PULP_SDK_HOME/configs/common.sh diff --git a/libs/libbasic-runtime.a b/libs/libbasic-runtime.a new file mode 100644 index 0000000000000000000000000000000000000000..83e6156b4ccdae0912168cf7396ba8a93d9c5683 GIT binary patch literal 177696 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ℑ>R(P*t;iSkH*=~2}%6!g20U* z-$@jEycHCCe5~lkZ~g%G9uL?nbL_DP5PLrl*yH<*Y-fceey>8{#*gDtiQkFVjrp-3 zhZ{e>Uzjfa-V4~{J4<4ZV@HW!HvGHx{xX2Q6CiivcZ*{$+p)*@U1^VbPa1^_OgCyq;%uvdEy9nf&8GLjF~weKH|7} z>h!;P+&ls^m}cDk{_=1UN0YKsv{D~fl93tazElVI7=zYu?ze)O%p3IX)wgrkJ?`WI z$#1&3i+pfY_qX^xYk1akd7WyojLZMlbB21fG_UNej2l`8vgm6Z%lL8z^2~l6rVub3 z9n`qGq`0QIp{Qadf0pql$)BtE9qrXX_~!f&R{0c?e5dfEn4cSLt)Gl@x-ll(MH1^& zhHh~+tADr;#8}H3_blHLh+|TzQ*28z(~MC+X?+-X=K+lNpL+QmvwjA0jzbJZ)Mo)0 zcS8|%5rFT0occU?-#uubEtXH*H=@F_=i|&VsC%!^`pfoSi2RMh;7tA-0sh+oetUr5 zY56>yW4ON80`5vz@T6K%SzA%lc!m9%x=J?JH{yHgP0gyxLmc_&?~7K|`zGQ~OQ1Zg{% zY)Na!%KO=2rvTD+Xc$C0vbJC1Lpk#|W^Zzc86Ohc_}FL-H5Pd;_Hdb1)DJHqRUv%r5*zQpeV*^b=J-U%5 z;$R*eNiPnaV7Pv}<Zpo090I$`zr!VVvq54^#-G0?-nJTMep+J z#=7cSesPJ9lEwJN+b8;X5IC z4qn#BU4^l0Eyj@8FSYNxn?e{#QWxK|(a+HJZs&a)%Q4T|w{a(TB*ad_+^Jl3tP=~5 zZ_r=E(DSc!zneRc>M`k04s%3mzC)2ZM~r9cefhbsYON6BVJw934E)V!Y2C!HO~;SB zOd`~sL3xPMXJypv&bRuSoxT1Qo`hY|;6qz`doK4a#$KGy_FiO1)LyKA_W{=e$ULsZ zJKQ&DlVexMEPs=EUaxnMn>Rn$xk2YMG<|-M@J_$lC{t{dBr9tXlv> zy0!Kt>7GHetH0J$${o1pB+giW$OlQ29%<+LS zRMD`qsivudlVKIBD{EJo30Gscs=8Kd8m_X+OuZVe`YNmrb(({q1@hjFdro>VzF5W zEMXCgoHj+DSmex8(I*x;qb2&pB4-~W`otp711_KdGz~|b9SDfmRlrUNbS{o~wuZXg z9gF=T@K!Lw_$|VT-?`RQ*mU*E=E|iQi#9i!^9n(n4pxO`RqoOz+{85Dx6sMxe&t0g zS1e0ei%k8bE}JYvfw8A?U~x+8pi5T3#guRkTYz1AoI7RP zC?xjohrqSh1AFr!WBhoXVs8g9?G@o5{S-{PG38FUAuo8@nz)DBRAF$Y4HC~12l7Q= zUV@NSoB79Z8HeDQk`CX5TuAH%$CPErEY2SbiC$Ip>Y8MAnlUB5XpJpTu#VFEoi+`l z$r)4Npy|kKE1yCj3`fS4LlAoz_iVxWBtmCWPRN-On4kaduyJ@^|SF&w0i%QZ65Q- zZEKlsLQ-GKAfTSq#l^ts>f(8jyLItP;E$#*J_fz@IM_Z(83&=F#sNWr?e z1P(HXNq5m}u57y8uZhlcf&uDc@wB3&zQ5!9fec6LqVt^KO7on6b&+dbcu(f9s>%DQ ziUyw~oHab}IW@RREsEkUOwYsfDhvf@1+>>D|qlRXn^SYJCuU zsoOigjwj4lvcC1g1_aa9qP%b23-9`P$lj4rmcOYBcch(t{4S=sd&GSt?pDPguQoKk ztnY>mB5^Iz=eFs4^%uHcOL-1r(!hieVf;-x9K)8=GjJsz#nT4A{4fVTiZ+a8oRo(n z-SY7r%&nD$b11Vte=LCSCs(l zvkh@($*2BnXrc-&ttR%d!!c8~8fRkR*J@XDe-}dwevjq1;>_m{k651R!RG@?S7iTr zKvhjs<f;PU}JLED_HxUv%WP|NVFpr)?5y0LLpgFnZg#>u-i<5;;#Pvx8YIG?Yr z#BCi}#-n0IWnEQmb;VLHVy>xM9(2&Wu_O0l%zxQ;iCFGCh)3Id0awm((HJWy7JZhV zblY35Q{t)Snkp00n2j{j-%zd2!j!u#Nu}du*8{INK}*d@Pl&$a-sq(iX_TJ)5HwywP)t>HoHI4eN@K zjBDmVz&_g+LH#ZO+ZZ9yy9feTuNHhvlyDZktE?WzK$=?cYpbE=+1`=9jKj6Zvdp$w zNbIeHfPPA?!vZku8#vyfUAnP7co>Ik?>_M3a0rRLmmqNUo&&#ClyDZk7l7SxtH4|e zj|`Wsy@dM*VAoy)m~`p)+km~NFl;Y^jP^JkB=%^G_UK09e&c5tsDJ6kgYOx-zcg?tB*}Usj)_d1ZjIi)!YOZ|N@6PqE>1xHdcm8oF zzN@649nc+55-?)((Ww~Um0@K{2%|IoRV+PbHD3gx3&wg{zpU?9`m5TvBa}ApYlY!n z@_37k&mM5!X`Ul|(o*hu)hp{8ug=wJgqO`5bMZj4xq4QvDDaHyo7Y%fojZqpYHnq7 z?)VuO$8(!%u7Te88CS(~7h*Py@Zwyks@%DsIsg0%7A-!zOexpQ5iwFO>-D@>{bw!a znF>dp*=r--7}%x^#>x-j+jja%OW%6?JYvVX?ft$r2z{gxHIrky`8BH)Yl$uXs&Aty zqwv?>59(rck2m_x#v6sH_w{@ZBTrBUcIjU(m{0tkl?~D6nYpGm{T(Gd)$zZl1f2ZB zUX1VYt(fk10ABm6#VTko$%&PJ|; z#{2Ic&{mDsHzn7UR+MeLgGo2mkhb1aZ$r7YS4g{js>QHai*OgCdHqFTq?^$z>l--C zvGZEThjH)Y#-glm_$K!$RWR*XcbNmOGXr{jNX`|t3iuMOYED(l_8;c5MS zE$F0#f$#L%x_Q^`HRUxZul$x4zns=PlDH(YtMuS)O!*M5n=bm>PC6aJeQSRv(o=6k z9Dc`pyE0u$q#Y>J>3}cJr=+im6wsZ5+{e^3Woq`i$5XUh5wicJE?pYXr_&~R zWeF+6(u=ob2P#W8Q!=vR;1suguBqGUPV%tTQD&`4wAU zvuQtxI&G==7D{LvN~kV#I)t*m?j+Tb@d0XY)L+1n9H8$&U+ zdrhe-OH-2hji^q2O{P5`SkrGi{&&s!$!Lu&Kl^PouMY8@5BBFIbH6K`k-6VpGVUDp z`;_kf*LP)j!S!;(_4}}sJf*OHHfj223d4H*#(NP%VTEo=-vbLjAAt8uhQj*Y-dlu) z|3w}q$kN|ok}(qbOCY_})5+3vUn?BG3ixJ^kLd3(@gCezM6d5#FD%lt3AotdIj;Sp z#~-i1tHk?3L&MY!0Dk<3sdn&j>&g)xucb%^N8oP^mw}CmwX=u1!qvEH>aK-U)mUn%_701;VJG}L%Py8;IOCdGF zON-5L;GFjdR)OilcaFFoTqp{_b>u?ix%aIQdG4n{T84x3FA2+^i8JS4_}wEC_wk6K zK788p+*=~XQN0-eo9ECPS5zjG^{cRGQgeDrBvrl)!#(FoIiHa8Nx4~|>2BUWm+z3z z)sM^lr{{W?b1kDsy#cz}fnRS2P6xBiyWR_2Zw1cX%k>uTdWZKCK7-dgy9f8@r(Ow- zI{WieS#Ra8cXG!T?wkrTms3->tlnX(R;|QB&sEJ>h1!V8r)9*+6_t&Sik=hl3^)#z zwKdDli&r{rMkBrB%p9J1@MZ+WaxQZmi+~Gk@nC(5kH8P81xMV@%d97k;RnQfwbAy5 zji(X{K@4k*l@p8nG~n1Q{D6o>J`Z^GNJ`C`4w19YjInZJk#o){_6hue-1#H6Q=@r< z3nCVMwq;|ioLJfCl+}TZ~+qr zv;;?#4{$)uI0|w!RJY<3Kh@weoT5xH(A_vY$2Xt@UL-_edYf|`6MGRL>xqi zs7W+LAiJVyE6PV^!pIGFJ967Pbr@?PQp|QUZC)tPoV&Etv4!R6S zG@+r6TY>GMo`fiF4ZTR6DMp8FtLz!C(|oFtAJe>K>LOvMi7HIL!JE<0t(KXn^cto> z13r+eX~xqBi`DdjnMfU6S|OTKwtfM9WBuR1!1=%Gc`LIY+3L+VGOIV+Y~?A_Viw^r zj#A5y8fxC-lQ}E8L8RBu(VqBw6ZlRXLewLD7Ka;d9hj}!#r%kWmR&boev6IYJ7c&U zWlOkhgI#-kZ_GA_aPi}_E}*CP$JQF=N5W;h?uX2c->aBBWI8Z@tgaHj zXMkOMkHQ|uEAHv&ZqAYwu5(A7uR$5__*e;M#lm1hbDD?eVTb z;`bL|*WO>?z_F8%*!v>{uD!)DBJpGQFZMFvm)>ntqqygmZ638ry73r-A4I)D3-R+M z=uH&{XX=o6mN<}q40=%n#v#EB91J&#BRFr`IRxcYjKEzW+KrsBh8!6EIRu2*ZWQ};h5%ilKLd>(o$#6I5W`E9tme}Sf263$XvX+7ujX*-?X|~U27jVm3wr#Tew@f)+gWOa@c)i-H+E!Si1qGv8#n8h3{j&Qa0zDL700rYc3`{f46Ii23kksVG;g71+uVI3X3%=`*U~@d@Y;IceD?i`rSHj{@g40Q-Q6M4yI)@m^xGm@AG2a! zjOahRyf7C>!zP=KD|0t`*udv z55a$G;zw9W9~_JHf3JG5eh-=|yw&Qz?&&ziaV#l@ zBh#BT)*_5ohNxBp^%|N?Z)K^t57qk=lQZ@?67`AioIGe*j+-*|j>N8eRDbh}5>5?d z>JgRe9@F!Tl#J8vjHOSU_xrhJIrh#3?-KVS`i&Ks_%Rkfg;G|nb9{yYl0zYm=%2lC}n31+MDD%0)?EnedrK!W|$3M$dgAQZ!~N~ zA!kOIl2G8yf)L*H7>4WhuX2d~zPumnA&TFZ_uYr;F|3_>fW@I|EZnD=;P9KPICtRl zd5ZgO6nXA(JtuC)qZ9_?&wA}s2rC@8FR>8IJlDetk$1=5_W8nh~>Wu97BHZ2)I9Q`6}(6tKPAET=PZh4=L_QS$mUFq}_`Z zzh_Rm8%GSKp5H4MV))Z7&wJMx%FBGq8~c;g#g=E9#!z0CS^iSZ#}&VGL;50)7}C4V z@|4j&zxPct_NJ(BTHeGDIqBS>BvtjQit5$P70rz`%PW$#tD3O3u%fc6s?l}OOA;|* z(NI$h*QJ$>jWzgiEoW8gmWn4XOxJ6&k}$utGHLe6(h^P%+x6-u0PVH5xB}D3tBYXI z?!)CtxI{v9nlRCfeF$77EAq7xj&q{Ilgs7Q)i>8Ht7xpgqN1w4u3GUk0?U$i4j=EU z={MK(+&ec@(BI6|pGws;>H5Q`m?5vI@M2e8S7lfm56dSiR#aCuU?&9bz5sRM$CB&ag($#t+C1mt&bRR!%JXv?*(0UHiF|vw;TX;t+iu z|56A*{jRrEi&Mg7`!@PhMouhtIBFPU<-{VN04)1?5Q|*5tvL{Yh(%ro9GgQa`XwA9 z=e5f><=k{&Sn`cKH{3kPN0;IUL@ag~hcTu20lD#+3|_(|7X4z%nIRxzk+Uesz9z&X zKNpzcvTr1&oOmG)hAUX)=R+>x5{n((cQWE2Vv$z?%f2SWB5$U=3_l=ZkzWri`9&=9 z8-R0cei4gY=hr+4KyLb=*Yehy6N|o%|7p}V&+bLetc}}AL5@gb^QmD*C!+H=*`z1X zr8wlW}&PK{@xWpnKP5C+a0TGLwMI`oVN>j^u5qQaWV$q)hEcKLF_B%Q&w_57au+;h&HduHz3nYnXk&YTH5 zmuW$!pBWE``pcZ~gs%Z#?h4R3 z7siw1J_6jA&UM+B@kNGWqw8!!FagB`YQkg5IFS+kjt*4LAMf&O!`ncbG zh0$dHq>uJ@yr$lT&G+>2xk%rhm&$?bw1eqs8kFYWpM9r(Pa-YIz9OCR%x zVJtV_PUtH{hF$Eg;Ys=T&4j)jCcIUi*;kOh|4it+6bkN4(Z~Ip{q${tKH6ch{IPsV zUw>Q=c6h(P0=+0jUmx&&eKaAR3pwh`;gG)fCiI<;2a~3+Dxq(#r?1S@H#?#45$M~B zZ~=l1oSD$K1NxMI6_8N+u1M(PzI!{J4xXg1IiW9yLM(m!Hj+M$|M~gXg#fnM0_pqf zguX4%HylFDztK3P?{5@qVQ5I|+S{LEmLYll_yv7ZUooK34k1dis8u&^Ht>y0rY;m(bS=eP=-r(^u>1 zD?<73pT`-{$8dqt_lJbO$2@&(k95DK34JSjN#DC6$7|{p5gHApZ-S?9VnW~Ndr4ny zLf=`QzDb_Gvzk0m&g zb~}sE*!?f>Lg`D|?c9$0ScHTKB!^|m-fG7q`_&jf_2~Bx+=~tYV9aIbeo;?WW!P|8V_nkn8#9ww~he-xh`C)^-oG zA?w>VeP&+4qs6cM_3v-riZ+pYzr zm4ouNylT6#cDTu`E;K7*t)~-XmC{zx-s4uoh0k_p$JQrpUfpR{NZQXCM__0JopwIOdNAW#aXskoSV6@H0nKe)3%*RofvPSG41_IE8 zBAhL21YFbeh$A1zxiDpoPbAHE0kuSs!5Z-AKt|slMaE8)_hO;1*(aEJYr*(R+*5t-k^t$sZT|7W9Qy82|Vn^B~RE;D8s- zcNA?^3GDA=F>-gJqxt=n#r<~ZfA-v&)-Md) zZCD>17+}E^Y)dQr!>?>U-e$jsl-^TslIdodSd{+0|kMs)RJ z$qq^(l!b(oSAI~`CuZH+|G6C#sY0iqc z`1R!TK>_MXUW|H+?RU}w5zs8T5?}b$h`u(%t+Wj$uS=PKDOgNsE_%=9)#}W zpMd-E7J^_uLLhyeVEFnThrapXQQx~<81wl*arpW!1Th~xf%N?x3}4^Kevv-Dk(Iu8 z<9c7;BOv@9hV*e=qOb2d=wsW;^sz@HeWxV!JrA1w8iDkklaSkp0KV@9l50%Fd%qX& z5C|*YB?*16L4kV>8I{u4k&ych0(c(+$=#5McQ4{S3qrhK^f8UJgSw&V0))Co`jl;d}2_lKhIDuzUU|3BCG>c3dO|FRf2$$RJTog8>42j0nn zcXHsJ9C#-O-pK(k2mJMOnaZ^Be7jmS=r<@_ea97Ko3QF|X~>z}Cn)-_jr6h~^ya~J?B++8_kFrF0hDLZm6+o!K!6pVvvq-2om>RfkWeY#5KOfys2?R z#HRr-A%kZe8eb$Xd5)!NJd(Khe75pKy|1FKf z8Jo_;;F@_ZCcxxd^3i&uOnKqc`U@Dhs>U-Zz@Zw<^yN-ug5{C7+}W@Sakhys~bR5A3oa+Jcs{WKS$t1 z9fVKg@Z9dfsW$+|rl2h&6x>@e?u z1{08|{~+Q(`jX=sui-v^A|ae5*RtaBw`8AChmAkX7CgBl@4@ppZiw+{Tw@?c>SE&& z^oU#89`obsW|Tc2s5a4k-pW+(+tC5=@`x#TJNf|V>KGV@|NK6HYOIyX-)E3dJ_4@6 z_QxSvHaIUrU|AW2T32T%`waLF4*0OmWkB1 zko=_n<;_2^+z8ZGRDpAw!QR588oYV9(>Fm;bMW^kvMkJ`0)Fm_XFLwa^?#wd3fZ)9m^v{ts$lXYm7lmM+tn%t=a?!#c0G*vl)n_k6+ZCJhh&cFzdx2TdRP*iB*^ zypcH_H-1A{#(2t~YJ8Isrzb0h2`@l7Zs=j0d~VKnRZ|9K`-Iyd2fM(1e|Yu4z617} z&hUDs^ozS=>1>Zto-JLJ3wxWaM>kp_`d+r;S8nPvr(ZNrxFSgX=3 zzhU$mX3Ns7Z=d_(`7d1f{Ka3t?73igTvHTB`Pl7d--Ky5t()~|<8i3v#TDZ_4i>I! ze5m+f;k3=-J=FJL;X_jo7EZeHq2kE}Pu#d^^YI0PFL^T<|BK5DHvP++!4GiPoXN_5 z_=XR!ob}^b=g)q6Hg}JhdS%}qmhD}OXS=-%bu_YV%xy>B7a6}u=GnutvAhiX0Q6G& zm`*&Kt~VPWnzz3Y^Jv)B0d=4ENz-q> zyhibv!!Lf5ucM*+)t_@8hz7Iw0=!c}!V+0jzZp^aOH*uKXHsG?c7nfg zrokYq5*&qnGB3=tNww|RpI|pW^;MXFx8r+7pXFuHm;b%eC$tYKHQ{|2Z3y&HHQG@* zob$&li^FdR8;)%EfZso7Iy#4QnqBLMW8wMVM;~?JHhmloi-3C>{FNgsa4s}GTt9q( z@y$<;4QCHC0*;A(&XMff=G(mv4E~o3A7jFOYYX;uBOg~*KYOhaWH3s~F$RLYo}+dV z(8tve9BIRcM1jC)2m3R}nT2y<>}!$X!-E4Zoa4ZS;nTEC`nc-r3Y2~R%*6=k?@09c$(EL^r4K##I@Sfmucm1j07J~L66O0P3 ziG`2ygAspug04)^Cne~U6SV)8!u+a9gr5@Jg!q{^7KR2~vlr#k(BM;`$0`{4a~Eiq z6$>K+t~sn1iE!G$oa50uK;IP7<-wOhU*XXYf?n;>kAmLe(T{_+-xOq)2Ty^nz+s_0 zcoz7%9{mmA{O+=FlGy|L5s|@3!FLhP?>P%61wR1&oJfTK2(-#%0Qxo1Tt_N-VLWHT z(Fx0?53+v`Wo?;wGeKty?YAk*(;$?elR*EsicT^m~IHj=t5AmMPlre|b6&ZITD^f3l=g<&=?Ty@Q= zrq&f7U%Ya~66_cOL)tvP-50{J^93yj&eQ#kFwA}zQ^AWIbRBV93(O$#iizB3VT?~CtP+5HojylHEw7(iTn>a0GMT%2*#yw9NK{ZW0I#ix;fy2VYr zya|9GFX!&E`~&bL{Z+tA$B-NMiA2^&$LC4%z3FG(N{^q<9E%>|^bnUG#+!8m)x^c` z54`j|`~irIZ}Qpk{d{IznxFLJHTvEC`ufj>eCY@A2Ouu}=K~+%_{7D(2zV(O0OI0b z3Vej)6BnQFW$7m_ek<^iPCs$+`QDa(;^JRNd8ePa_wLQ{YM`aq)i+T=5bYzXy0Whd%&u@wvuH z=^-wDDfxWh0OI10_T-6+&pxN*iHpyENp`f=LtOlEVAV{s{0niae!s<+kY8`{TXCwM zZt?9nm7HPm$H{kt%=CXz!wL-;jzjlCTya&9@A8ni_$B1`#UFsU_&MOpTjJvLTIH>; zX9D<&m$>BX$#?M*7ykv|(obCcmw~H{5f}egz@?wK_y@^%`iYC5Wu7ww0K~;-US)HJ zZT8m^f2=1@T>NR|yD~sr{8{ApvwDb&-wsyIOv~SjQ}vk^e-NjVMvLzv|166?i&M?n z7XKOfvn-y)Da#Dz8He{H;*#gE0f>uV3w(s*6BnQFsjU0RCN6#h`BnG>5EuVk;H6{9 zO=)8t|9pf=o>7*%&n)8NUkZGL;}aKuKJe_t_yf$xA^t~!moni1;^Hp{KEm;di@zFp zDKh{-T>Le_M>sxl@vj4}c!`S-G$@$If52Or^Nj3Yum~%CPjr0?*p*k#!5MqVmZ6UT zw=yj74JbzrZsy*KY0A;ylr6jIoccBdrzYVf7??ckZ}IC*_Wq=azeJAP605@e!| zrii2X69MY$>XXA$ry%?F;Rm3n8m_}j>T_Wx$2s-PkD|o2Oic7K(tBXuUQOmgdn@YN zr(Ny3@qSP*J$he6eRp-{{V-ha_w)TAU1m+7OpX(U(c8)e*98Bw=h9iF@j5W`*Q}}& zaJXifB*vC3@HOtsT?pDWqeeh-Y^!{^R?s;kVgDqz6r?YAJLsGhG5?ag0hBNI0O*{N zuz!;KEJ$DOMbJ4bV*VxfAShq%b4r%pO8Bra~XX$%?LLUp0$^l2&rH|vS{{4O)`Z(4q zkiLr(`f8zX3*?wSj?zot4GDb*@r~3VWjIUU^$C5?4YO-gsgI-l()amO;(06N!zK0X~Mnl;H?tVGTAbsD49NRfORs*j?AHxL1d3-aW zZ_n|zUF7|q;$+xP{?Mh5&x-In=<{be_-J_0CmG^wr3~Rc_!x1+VTPs*foF(@a1rDl zz%%Cil|ye#TM3S1aOfU@2K{hTzx6JZK2-mh+-taxH&f)U!e$6HSJ#Dht4CUkTUR$_ z;v{3VdSsi`)-rovya%=l=LEC=tZcCk<9o~6awTlFrVlE6Ht4r1n1h#Y+CF=*9_VG| zaAZdm$wZc1zBLd%Lc!Lcd+_)6Prv@=U7J7MVb>QMn*vy`;QH=O^Y%~wkG5%>ufN%N zxf%-_2yZTb=LTNir}3d-`vZEr`u%NBtoOrx>!!$(>yFr-co;{Y_LuIYPd0^5E}jz{Uz}!JXC;dJZ=JC|Ot=43WVKLU_2*-2JqMde@FYi^Yqud=B#_@+GjTxFqxH zOL1=k{&PMiw8k-ZLD)BMJ=zMUVXLvGayD??duCAY)5TATKM*9^;t%R?e zss7t-8CMr(fjP9PX+Pfk4>cy`g zq@AGrA|zw0*>S|!cE@9jX+QtM-sToFQw;NhVh zZ7lBt)?~&Kts(9HVT(pR8F(M0U1&r1))`-WmFK}7jfK>+p*U03+3nkD+0fzw8@g%3 zFjH367f;*ph_~0J)it<-q0kNM({@zT^PeiW(f zMjT;z^usch4WXSW_~BDDww%rq&3{IUx7_BUw@c&J8G}3a|Dg@Jh7=#BJV*mm&XVH4 zxngMy)6z@X#GA96yE=}3iO+#4an_zsA>Z4(3mUQiWOrMoZ-wWV4gapp?vtsy@SZ6= z8g0I{9xNdbV*ks8)!xpICpKiN8VnZo|iv%8Kv5Q+F~K z`kaaM9br|G{~p4t;-{uX@iKT z>9-h{zTQ<3w!`K{@E2eoc&vM)q05Z#0RPK`{rEn{7Z64GEuXfgS>GNx6ONBL4>Cb< zI0lgeKWz`ovz@#R=Zr}NTm#Ly140f*{u=21!3qWfI98M9_wptasx{fVTg%K~I$dE5r(8U+&EBnxJ2yy`JU1FvVVRmJ$7f?^bdjLFvp{~PopVfgrf<5Xva^!oyiZ=LVaPsGAc51xsIpB_91 z+K!{}q8VnKisL~RlFzeI0p^^6H#v@f33%O)6ZD}3J-`hz@fmWD(-R$SY&|fV_+Cd- zC-u#6G~4+s%JTC*-eo^tncUYH*{YS>LiK-A`kKGY_&T-;X+)?Lijh z`$b3d8E09(HL&$|nTUFROH<~B#WFL6OIECe=UeR4y7H>lqxG)AdD}c^446orNv9kN zS<-As`RIs+wXRGFqod)g7A;%QwD77`*BH0MhHAdtSfYlZpc%iRF&@akt6C#cv+7~^ z>NQTGn?u!1aeR7Mwy1f=sfHm7Rn8jwcuEdZ5%zU*j*wegXpJe_*mv?-hln-3L=w$j zvb>hTx+jk`Di$ur)3STnO3d&VEWz`G1?sqo{G)f|@ia&sPjb8==_h#t%~dDRTy+8i zCQQIzFLta5pmFR(UN({Tu9JA#Br2W6%O){&GPO)L7)7Q~{S=Z@rX&qtlXTn=*4)FQ z70VXD12ePB+*>nk2do{cq0FQ2nnll;Weux1rbYX^x#bTZCkE=H(BTP zMqR@C@49Ei#b;YC`yS%rQ&fE7;&Y5&c4NfF=Q}amfy8p@fe?i0H1H#MKS=yCEoyCK5dALPZia3@W(Tb znhzS(yyL6gi;AXvH5XVuzYt_|OgyG%u9X>zQ;D<9VqVo;X!-2J*37ebnTMNa#KZ#p zXT*L(>2kx4+1=*A8sYfF{WN4JfC1oriO+atFH2l}ipqYGxcKa=XZg?o#KmWJWXF;l z-_w-+3B^lX@{7o?#UFsU_$*g77m;hfi>g0taprS16#)2*YA!LT**CO=FGv9IE6aig z@b8g*6xr1gS6sZ`(o^vV@b8`ZKf>{eOP;E;u3Jf5e3K_tU;z9)VNt93h+*4uewoF2 zDrvI#AC(5zmG#qL;+k)D^6^wJu=u$+l`OP4^ER#i^4TdLi0i&sG?fp;#oq`#dnNt= z#Kr##aPf(Y{{!-wkpSZ2{|0zAhd%&u@q2*F{*t)(1Mz(#K5_Al&93W6`!>5(ly`YW zT=L_{?+3*I;^Mc0u5LDL^R=CpSFDOE1H^SL+gsTz5*MHOuX`shKAX#I4u1gR;;#X& zv=JAd&pNvre*pivvzUrcT=J#lSK|*rT>Npsv#ZFpZLjz&x7jg<9V^49fmO{43aH0X zvzVCG*{lAmtPJBiD*b#Z0qSwoTu#jCzdFLDhs8+gCoVqAzsfLi@rM9c8740NK;Si3 z;19t3ELmz$^H*@K!~e`|F1_xywTn~_JI@$X0Vy3vdu3X;+L?al1trWU^TNX(xo7X^ zgi<@_`C_U4beqtyKW=*K37+e-=W|VxjTLOzKezpNPm~>C_2Lt3_2s>kqN^8MbCweH zM;U;I{eE8|`gQsQNMWZv)X0Q2@58{?;C&;E#fGsoJk_oedHrGkI(0Nn)cTA_6L7g4 zPfqM3!hKxyaA2n=72@9dm}aF!gKb6@Uu&^a6e$=v{kFZTfGoP=zd_e0-s;MB(%2kE0-p0AHKjjqdVu9Uuc34NWOKH6_d z-zO6KxF4(Q0><>+nUJgMMUJ+j_=WZ349Ky+AW-_ANXT7?0QQ{(lKW0Vj<$eh62e(> zKTpU#iGZ}{o3!nutsTE*1kzUk#D5-pk$>|c#OJ{o5ar(}$ocm>8WnjG!UfVdGNJD@ zOvBVdi2B&3NZ+XmeV0LBnUvuyeG?PET)3-9A z?;hy8FeQD<68g46-+ajNen;U@`fg0!n)sIC-jXxCcV7;GNErH^v#DJ-tYT7ef^=>FVAaw(borZOhY{m!p}T?99mNP z$`blsfFvS$68h$Q`lfsO7A5pegT6BnE>QZK z68hFapW2rh5Js@6zt;m92prjMpdI=cCLqp(?GDfNI0#QdAL~D#$4ndyBYh(fJ`dJy z!ba$mJqT%r@wgLmh_jVE!b6Z_IT28Xz_ad&^e-Xz0P4sr;H;mFw*&_sQ+N+RgRSWN z{ZKarTKd?R@z}xhxR32r;y`kogs8cC{88JYPpGRq>V2W+Afp(MEmxyaZiadLLdj~W zJogf90i6#{Vs{uz;|BQ4BxOBp7WHh}S6mS73l+C3UEeWy^Tz{cQQXg1UiREs{1j}t zW%sK4{-1J=?vME-%UtHiO~u@8nHGMsi+yXdk^QfpKwD*d%wnvqvLQxCmg&ANC$6S_ zW>>kh_>(PXCoMLe|47&a8V{1u(=F=xBzyuE^=vVgu-2Dn2J{~DthdZ&*z@P{f9nP7 z&YZR(vW`7@8}GjS9@?fg^jPcA&hFkkQ+C2;Q=Ta)CC`)=r!CuWoMmO2C0zzFz;4v! z`zCnMi&A0I;OygUT977e4wyk6ZbsqSw-#&d;Exa%_i#rg`{~fa4;Gusb(Z_`v9Hl? z5;?a17d+eNYOY33VATQ2JS`Ss6IVGl5*r17qOVM=ySwed7Y=_0lVmbUH2Cun39`v2O!x!Nz*=eNUGpseHg?ntu* z#B0<5+xs+IKo@LIwEg4QG@%+tzU|*J!8{YHaJK3Z_`dz7ft~N$k6+ReaF1#qGVm_O z@q|x=%y$`EzYQLJFYpIEdI#{wJ^IVQ&kyNA!Gpl}c=V&dH+nR$-R03w0e{w`Llg-2 z#P)%{03~}70_gkw6`YBS?g1{~d+^;}0U*tB0>Vo;vKVRL8dw3tKa`*sIeHPovdF*d z9Q|WM{Mt+oG=gT|3FVFg|eF2C|59_b?iy{B6KXEAR){hC}@Az(+Vfaq(GarJuO?j{zU)^b;4K^-}tYi{D9k zr=Pg^4Zy1h;}38hj+!9`4L*-k^}8+p74nB#oOxAR7U9y918aoi6BoY$ITAAuDnA^>M75ZIM9wmVv=*>Rmuf zKl^>kYKu?F@Z0qs;J&_%z3BU#yQUsz!d6codn}A=0FHmg;p=-C#5C{(O5gKf`00Dz z)5rdo^mPIE_0eve?{$In?FYlx*9Cq3Aj|t@4@vq)g6HewnB*cU!&&-B*zl=qQHJqN)RqIIhL>&_i?2HJ-OzVtJvi^ z>am2n@pW}ayRQATlWlq?AU@7y)x#b+{@u&3mZRB?1OE35legp8 z6lajmv~TxE8*{-Osi6$n)Ui8*?`rq0tx=8eyOO^9z14=Bkw^R1!aD}OH+??yC|dWK zf7i&HqQRRlFL-nqV=R8f?x?^K2gR6wOgO7JVz<#_GsyK ze{dUKobU_4QjiAIjlTf-3b53AtmCu31Cq3#e}0}ttBY|Lf6`q`;`dW9NbCBUyZ=3F zy_4f1w#+DZ#%$p8=6Dp>4Q?7%6+E)puIG-{jqdDdt8DjImUl!e$^RJb(Kt$U{~SeO z%2Z<8s@i+jU*0|J_qRo5RplePidTfAHanxz=$8YoLpAq@ai5G=r%YMpePe|@*IgSK z;u!0ih;fp3jcH*dtD)!D!!!nVN3pH7?HJdz>$MwtUOoHu*ghRdSNGuk`;Sw;lKxCF zR$|_|54%D5sfbqTTbW&(KlLqMH5e%ohvQL4yvpq2!m+KxYk%pPJnhIg+->*X*=Z@h z7b(uW;4W+{Th^^$AzgN9*d%s^R*whUo?I9Zzu2T^f1h zN@knu4QP4hnRM`Jq2;lokXR><5ig8hAso5QQAXyl%C^o*N2anN9MiMAxIo^H{h4t` z=6CshHs4vQcpbx|RPXHI+V$vuK${io4c&Kq1F68e^eF5Yj?Tq6OsC5Cqs2Mg$NHI3 zEo(y^^Hb>Ku`6v`UcYN|_lKgZ=~dv}h%p|69XWrh*tGZ^CLfT0b{yXN4^ST2G0sG4|RO6P@WVx z{<#iUcJ}NwW1h_e_7{=Ds3=BE@!p0WhE3bBi~e20Rr};-&;r)|2m-MPv-R(tRMUP1RT?WXlz?-r3e;fb%xc&9UQl)`>!%GxU@2 zEtM3V%X79*0ACe0FJ@#Q%kgKEcNnjiBy?az{EmG*8yPPId3fGBuNGt~i^4kk$K~V^ z({1h^@xIOJj@lMK-AO+KagPLs+JNWt7QGkv?>F_wY;W?nC)NL2yL^4RW7GcqnMBKp zH{H9pMf;USZJVdZ?fdl4#am~*dB{FB_{aE0BpY2Hwfb*m#$XCw^$H+#12R2aL%8H7!Z#6^Lszvhrqz#MRz^F+X~G{tYhHBd5eE{ zWDe)Tym^e0;P))iE|Ot>4cbW-#wKp&O_TOKr6 zX?gT1u+4?r^kdTB1MX?`H5blmR2Z`aWcqT!*Il@cKj&S|@Uq}#7k-(!o~ei+0}IX{ zyKq|`SSupPz#{YaF5Je?S`h(#4)~CXS*$XC3<7vS9|Om_DoT6J!kCv+%2x-pLnPGV zD2$DoF`TK0AQO(jSYG}fKN@9LxIg+reUl?)!01C@9O931X;?5Bv>R@Q#IWEr&>Taz zaAGhWbWS9m&zYd3Ts+bE2#9hGdr%>~C6pgyd<2}~(ex3pC#1(39|1q}X!;1y+!rkZ z=_6p4dq$+`BjAiszRvgvxX`2NBS39TfHnE_5%5DV{07ip@o4%8_@zhFN5It~JwBk1 zfL$I<9|84Ydd3Iz5wP70-v;`39!(zs!##Q0GcF9nCmJ6C8$6mm0!|C*$;L;(IUY?P z0nznSjgNr(Fx(4?t(@oY-mIbW{O607=X~cgp9^>{fHseu0XO=NjOIEH}*0gldQS9Eu`((nKS1fB? z+Jfosg>bmO6s!2Lo~apZ7vu7UO|8fQbj`3b9<#fkT@VFh3!g+EX0xr%YE< zo)e(ftnR_utfnPNhXrn75|bMWf@r#WK?|4A<60$k_0r|n*c>9)_>fnSSPw1R+arV+{ zZ9L{~S1w+*0`*Z7kmi}9((ztwM!2GCqYiyD@P5tHaLh(luRVb)Co|_j7DG6s>RC}+1yyY0?Oooffv}6g#s;5ek2(y5PLRe6E)=c zV&mJyw)I&y$N-+yvl$Ltz8r{)&;GZ34-glB0Qvj_@FYI_IN7D-+WwCCQ$TAUG2-Gg zu9~y(2N;K=`fP(*pA8?4aLEq@YlPzy7oYbce*^w~QAYdV3!lVLvb0~hmRR{zK;aQ&w28US9*v`PX+K&Cr?~_=JN>0_w)Qz@TG^i zj55^?c)FQu#S2k`H!8uSRqCocI3 zz)M%-4?tXemY)%h@0TUAvhGuYxa2nim;Vpq;@<*X{y&I|e-Ci^DIqTY{lKM%xcCnM zmmcEcKjFy}7yoHbp1AmXfGb|&;{O=9;w3KrPk}35;^O~~@>k#wKwSI-z(+Vfaq$E8 znbf5PE`ERDikG%(I;^K3jT=K-l=iXS-Ph9+SJ$d5dpYO>N z7r%w_D{!L#;^MagAL;nS#jl5){KvEcFF6l?fPOeiuQI5?B|iYH5spt>eA8}RZRL~g zMz+TC^XdhSH~^z@NKZ9z9*Qt$?`!3Wi{A;B`~$rVT>gPL232#umA?si$psd_52x()5iUJXlm7|J zCocXN;MH?2|19DkvN&(4`a+9uz^P=O#cwB{1q!eSN44Yc1zz)Ea)V$Aj_h>?HMrus z)#DQv|9SGutvqq@Um|~iLD)vB=QGZK5_9+1xtQ_ z_ztbU#L8Ui;Vk0fuLfSjKtMZ=>W>)I+Dkf|WnMBrrOahkW&q-?rV$u`@hbhqm7X)m zclkhE{1#81xcGct(nDN))>GvJaq+j4@A84T_$(IH^YI6`7Ki-(n7+h&4cpigNxmAa z>|a|xaq&(1-LRdX{z;IjUTEcczctMkuf<940^-s$0l3;g;^G_qBf$Xp`p*F^UuDE4 zzlQuA{s6?qzX7=Rd?YUZG~hLh@CW!Pj_Sn*#n9NQYnH8Eun_0fEA9CTVRQ@<7z@u} z?7~&6oCgf`Z~?xN^Ozi9h%vo^ z2hZTsQ{#<6#4gMqiO36%VoDvAu=3_UR^x+a`GSQlcEBYb;GEXrW6VAa>>YOhCD6Ed+e4?oQJ#S(LM zVy-aR;<2TP->!C0B)V4>%4leW#d@qa65?_=%v_U$$**}oafmK|XeA+_XzeJAm)7X3 z*nF>h56wlH=dK0ONn7Fz-H$WU8-8uaKDCb^oBT3Ag1Gxg_ocjK#=qD9^6aK7Tr2e`T74aF$%s_e$&Wb}jVjkQsoZ z$epVb`c{MHJ5?ZkvlDVVK<6Zcv*bRKkb4d^zh4BB`>TZ9>!5QI!dY_kdyZdN4oVP^ zCZ~P+G=)$Lx#6BZ-Y?{AM)k8_wzYrPA249{&oa6Y$TIP~1!3proklc3p;5`Z*N1;p1k9EF*EDe8mjP+xMt zyf<)NnbBbXq%XN&-dWJce5O9mKr4Nh-~vB=!|`C!^7*e5a#fHUYoj#(iuYO|{(a0x zyr)5e@v`12Ud{>n@y><5w0z-QqOXs>D)`+)eK}9xg9&{%LEmKv7a-WcmlOJ4gFd!R z)K`W>>HDXIzMnx~TK(-z=(`k|tMpZP`hJ|ySA~eu@{hhv`r$f6XJ9Vmat`N-=_4Em z0p_1C$Lj==s-%rVoW=K7^>R8i({QZul#nYyCchyeoFzx!LH>O-jI!UBjF-dEiuaodeSMGd=k6nY&n5Id2Ypj; zztqR?9O-*Ap>IxqyCx1I29&F<&K;1TJ`USR-)JDrR%EY%e#fT!L@I|Iuc^0U z!4Bw~51jfq+#`LQ`}Or*27RAGxIpPUJE5;%MU=j&5SG5>PM^xsZ=lb8+8ULNi^s0?GY77=FC$cPn1Du!{EwzCWkiLHh!R)+wk3k=EZT z2Op2LRqj+0tPDhEgJ)S>q^|8P5L%pf;s{fZeDr;?* z{9a*2k6F37=NYUMGT0Wg9!M*{rfR=ftZDZ51TC*JzOt~xt5!jCjaFw@TUAjv_VmOq zqjq)E4Vxq1UPrHM>+B=x@^jKpR~y%7MQOSuEj?Q4c)0XnZKz%47V66E=-_%PtXj;! z%fc;YUr*=5>_~dc%Zj?ob3?F$t)JG+y}JwBMi+HuMmHelUgFv0$K$Ww-PC7oVWzS( zw)!gSqep#s#M$#T^Mt$3yTkPU51dEd&9O|;$U?B>D!TFZ*3&6--2C}BVJsd;g8R@`wbU$KZab$r`u0KV$pG^;hSdN%SBhbPzZg)`6H@b&do>s9wp-fpkmI^%Q2|Mpp| zmNoff{Pe+tKtm$M2k^X#y1UM6zO8tOxh|1HlcFwoUAT#%nZMoLr|skhqw(;mdSSz? z4d3=s_3529o^rG2GuA?PGzRpIhkAutQC?kyU51OgPQXgV&Tg)#ZR5(pMpC;rf7R_2 z-GKd-xw@9C4-ww=>a6`nZrv|3kfZ+c?fd$VGV2%VeXyPTgJX|w%Qxryiu;@MJ@(wy zh`7Q%g3HISM6@45*K}y;6(B#`MW zz8`+$>?N}nFSuc?-@Y+t?P}YMSHq*Vd$!(1^=+?dvj(G{>dwk5wxext@A>XC{4c-1 z80%jp|kVN3^3NV_ONw|Uq{KdFE#eRYu~!Ylkp5jAwQTW{uSNxrmbflF>gL{ z<%VbP>0H|n?aM*5{Rgqj8Wf4>v)gk>OF6V4im(u}q%gSFD$l_YI8sU6-uiM?GEh@ZReeJ-MXLv-u2= z<0ykB-}uZk;0!;{qzaVvCUa3E>GqzDyhE&>rp%P4@g4i>Nxyyx{~vF90xQ~8;>bB5 z{`%;Bl1xF=9)xX2)%ITeo1%w?{mF}%f9$Dw-a+`pBcC*(rbO=#X~J7tZBo1G$sCwR z?&IijHFQ7J@di8`pv<7eKjGa8cb-4`-d98S=WZTcytdy%#rx~2tNGAAw9P)(`ZkEt zVpG7{)VzE@TK@*~rZJ@mDcjW1(XaXSeQ5I<;NPR;50thw>3-&Kch|fO-c+0V;yG`~ zR7N!$VO{=i8aP^%0o>|yJRc-G=PaAcx6Fs1py6_f7i^VrbC*Gzm z|4u6EdaAMc`}>$)z9YN7cH8G}zU!Iwjf1wWbET=h=X1~Cc`@`*1}z<)UsqdYJG~5r zeT1DI-8+y=U7L_&&Cl>IJG&l78EoikZrNYR5Zrf{$qVpJUbOBnq}Ft8GjXpCU7x#I zEyh3W9@Gd<51)qNu?!t1J>|LW2uG_tV;$-=+7Z-{29&slF8FsaJsnc*q|mEy>9g8% zrXSRW9+7F!+Yhx>p|z`O$2~Xjp2PPD_4CQ#jFOdBm+^i;Zw0wO`X#p8w$$1h+U@SF z+tzioedq}{^z5B24<%@mW`onsJOjV6xyzKl9{Q}HoXH8}1IkLmo0E~;gO;ZqZ2?k^ zJ1k<#QCe89R91W+1J_(x_Hw|!viZJZ8rAmfZAAWbH(#?KrJkDLIpZ7aP%8K?95;6+ zS}p6J;J&Ya{ka#OU0YIKwf!I@4>H%vM;8(Aetg~+|C8sf7i93irfCn}<)qj*Q)(e$r@tcU#2v$etUJGJ*)%Zq7Hx# z>p%u2l$--U;y+9MmhWWn91IVb4SfvHqutaqUd!47O?Zwr7F&OSei?LQ)%J5-U5esV-L^5>x_!9&%1;}f$zkhuJiSL63xIT3-=p4x`Uh;0+Pfd>!xo?& zZDeOSzM|am-#SN%3q8Sb+(B*3VdLy&G$Ji-GeCA!fBvY2%`536#rnqcWK3?shfv`A zZGS7XeN&K@KW`r;Wk_M685NDPjdOl9yC87hEq~6hZ#YIf%Yy?3wqLmFeG=$= zA0++F-Wik~XTmw-69MN4FLory|I>VoERThAWIqDVDXw>91I~phbBv#H;TM^3e?;8Q zIqr1fSD0{)SVxe-2=Y^o9F22f%p#1}X9JGg6YTZ=NIJuZ1pj7(gL5$MobOX)s9Bpu zk!l=;#;V?3fKvt|{l~b0=hsa*NB$$o;Hiyt;d3z0eynZb>Wwq>vLWxuP-xes>s}nqcAqp!SITNd{wa4>HCV2uS$fE4mP>) z(@Z#XJp%gt_@WD+V(xcL6bOtyKWOhq*o7m_=f`(k_yZ<YE^is_jYR6@Q?49sqw44QS#Lf@bU^kpJZp6D|q(gH69 zpwIQf=`-Z(E{&Y+m=5|>lr0NGjL#6(1<|vq&y%Omkf(7d{6oOEiiAG;43Xao>Td#l znx~&WL!QN<>*+IOh)2_B2yHtRz6$gNFPuI@W_dJyhTQGZHvqrSqv%~u>At!n? zeTKZC=uls1=l5-orhVYc9=#3pPd%DGL-u$yeTHPi^p_f+A#B?$lm_$}5~aV?_zan& zU_4p+47uB*p88l86N65CuLPc-_=rtnIUX+23@~0v=3G}mGcs1xV!u+TR>Oj+W+CoJz33RJSSdg>q z^4()$bZ`dfsD6zO&H^2k|1rV2pjS&A?G5b;*Lw8DpvNFjEQ|@}gT|mwNH3whN4J2! z#iLh(F7&Qn1A3rGb5FJNr4H>`JLu?slI_)$P?89J4$)p!Py*!c1pQEgej-7?oS=V| zp!*g{yPaRHNYHf&dU}GMlc47%=*trHVn@@4G>h`F($Rb~48oIabM&d8vuJNOC$0}P zVcTpyns{rXlB=PT3wT63ZzRrFiFUrA$&I!0FflAxbq8l5&b zW7jWQx^i(dw_Jgnkd$CAe`A-$^BAsJV6R1gE92-M5+B}?HyWy#W&+*QWsTlsuF)=3cU>{cvU!nJU?3K7aN z3zB)5eQU7Jvt{w}VAl-pwb0dPSe);{?8XS!wd{{%Z?t^k;@?UB zO_onw{B7jlZ282+f0%q`DuB57PXm`8;^K47cB%VBA})Rj@DYwrTzvLRw7#0S_-0)? zGXUVPOQ+w8>@Ak>`>?nNe8o##dhP?Rc!`UDKXAoMT>K}=ckvPzpM84yARsROTHvLh zwCN!(emn3Hj!#_tn<#&)l_xI#t(13s;^MR4Fa5;DzX$k8r=Pg^{4S7w;^KdW@=iZ- z@gD=OIzn80_MvsZ#Kk`VT{ ze17LhKXLK7W=i)3)fe&w5%i!#)?b2_+2{XS-1{)8cG0N?36LXW*!DG7Ev1 z(C8Ats#Wbe^LGQUIh$OxGbl?nu2X*naQRny&g1`z_&HX7B=WH20~W6UUVW~``R!YC zp2atkKgZ%*$p4_lpCs<$>Ll*cfX;7F%?1Nd0=(t|i*r4aeCipM7|KHqWj*G61?mObeqF8(f$Ph9*`;MJGo4^WSz zhJ_Bm{M7q`xa7^d-aU8!U4IYdnQ#Dc$v+1?%R&txF8(;+B}>V*V_+p8wfNOIm3++N z593tRVsW0b)e$Z|=DwVM|Gv6BdE%0vkLRLv5*Od3XPMQ122Rz>EzWzaSz+<>$#1nd z(^+$+#n<9gveM$LBf{_V@J`^8e+hW?D*OQkvYf28_z>Wdxf?9`-6O7avU-R^R z>m5g2{I$TPpSbuNf$JSdT>LwM>m5g2{3n2yI)b?PPXiy}2;$;D2V8w5;^MzTJ{1FA z!6E)EmYKff+I5!VF9BcoOI-Yq0#|+#7r&MAEFb{l;&aSP`AJ-SwlmUCT>Oo|m7m1L zXImuw#KmWOtNbJ`{^x-!KZ%R~FmUB3aq%AmuKXk}{u98JpTxy~o_v>Y#Kr#^aNQSi z@qYtK2NOOt9Yk^m@asZYSzuw}X0ABM6i?g4v zYhR}f7p4%9hMKhoGd{BATWvd7O53TxOV(K#ra^5xamkprpyZ%q zxOyya=rIuzAAn2`bW7qm7U7m1hJ%rb3lhU+vHOVzV(fsSw~S2#g=P#Uh3}17CPU4D z9)?IXeA!#L9m(u1)GptPk{cfbOu7nEa!uTMzM5j8#!I#x^-RX?#)0gZ=px?@l&>L= zuN$^wq)DX;HN&O71%@N1y$KjCc9gU?k^J%0-Xy)D)x(N}BiUApjkSw0v9chT z51j7-zLFWn_IM2rU*9g!lW+*6ZxtB6zU!cmeNO6Q8!vsg0QdF14tg98f%M%AhA&qJ z;v5O#EV+M3$no8nCijDc+%Cv9r0Dx~LaqmL%qxN7rB5aN!sm7&0{FfYNUkO!$2s89 z62e(>qao+#-~DBF?34Ldf`C`gO<%!Iy9=xc!<>f<|6`kE8z;~XdLQ3Oig zWeI(?GTH?ST-}Hynrb{bNGkBgGi!kP^rVW%=;a*NTVs0C?0_>gn5?(AU4e9e;s{ z0qOf*LSGJ@i`yYVeeB05eY6L_FU-G}p)alea=#oueGfn%@00pY^z;o!0(^ZrEDXLB z;R2;^Fc4qgbDq8&gq6PcCG=efeS4)0XX%@i(Dw@T%||%XSB69SE>7s9Zxr@v1=2@b z6hD3S@3rNC`YLcp-^zr(m*3^@GZUq6Swdee3MutaANzyScY8u#`@r>R9YABTOUiJTKKhzs8tPrx6}8d* zLUpL`*9mH!Qga zm%LuE3img7PHR2wmF=FQNju;t)(;BT9Y$-4L!0KR?PetnbVj!NRol73gtq#$J;y2= zo?%1mR<60-U3=Ub)gG>k*i;NTV*MlbmOVbaUq2OZpZ<5J>c8g7zAu-(lvr_M(%c?e%iA19PU4=h z%Vp0K>ram9(i*O0j@8AaQrDj&Ul*iGy%pvLuv`FNkVGznx(64Ut43QLvTOi-Mtad zx~%s37m<%XGl96|`3)dGaq&$aIeFqo;!pAP5SM&!`g`NQ_3^c|C*utjg!$$kQd&@Q z2q?TJZ?q~5F^?}zSKio6cu8_xCJZpoH%*s$)PH&78r3Kt0`-GtgTZ&VD{{en;H;Md z$(;!XyC3x~U_c++KO6=cXxLv`0^HZP0dxxvf%Gi^!`D{@;p_Vi z=(O}*3%;*!u9rUcrKIn!gudZWJ|8@R()TGae0?pRK74Y8`W^)Cr*BO!`o5gd*Y4@V zCsL^I`Gmeb(02}a0;O*^7=HS;diwZA(Ea`dxSu}Sbfo3qD+zrMd-~YNl)j#XzFEEK zdo!W$c~9S9PhS>^WBT;YX@$PD{`Ck5pZa335~5(s7A8Uv>4UPrz@d+A?Ko;=mMo;r&Dbr%xP zcqW)gN5ida*?-CRRMa;&>ux)W1Fp5>N@VrNxdPn>o+4e_pJ?rf1Am#6H$@x zgtXo_SMEcP>(O^&iZr4R-+{HEU3cBaFM!NR?f4cc>f-9wBJ*XUxXN?< zYQc8_zD7*y@D$QK*Dyb)ZevJ>_Ykh{wYXVr?WG^zKUgLEo1_OS_thl-1OI>C#h>B{ zB;&A zT{^q(`c@k}4P@Zgqph+bAyv1d_prz($C3P2!0!`0E)6+UjyM*;+eG^z=56yE^oxK} zPR|yV?Mbf`ucEXc`s6e4f^Z1wiiH}FL5G?P_ZNPD+hM|o?C+mJ%|aPJWPL{Md`fhG_6cV_N>9q1%bVkcv%hw=M~?&j6_1__`u30>7@P)rt4B|dg_i`J+l#_W z0?r9;2<5ZE9ME@q^oKw*uPh7=E(X0?B*tHwKws?9i$HgV^zdLQ=rcT;bF8Q0urNGW z1^haZ$WMNQ-0jiVgRb}FZvZ~cqdS0e&cMQOvnG;ukD~7YUh2`G0gh%a4F4?fXwGQ3 z>2KfUg-eoWu50%p6TBx}H%>lFBg=(=^pgqtTM7CH3HoOV`aps%_C64(pSEKH()>me zkUk|ra}JKcd8I)puje@WRM1(J*ZB$gKpzK3Ugt>1(=fX*JDM9q40kDZ>BbU5yBJR_ zncu|ZqNRco^Og#V<=L9VF3vYFi3OV>7Jnt5^hFB=#qkVKVk#2GO6Szsb!%pM8vo2f zHM6db%hbkkRUDS5;g3ACoNpZ0teM4r$u)fpFw6NS@-nlWZ-QCBM+Fo4XO;&}pyml& z|7JFSpTNto#1Ez|(L%Fuh{%p8y15fKlxQW5-Bc)NhNZZQ?r_V~q$X|95lWqL5>o=y z3dJ(dEm&h7L!kEsYyD9UOZf-jNn?Tofsb%};^K3@Kl^_C0f>u#A@FQH{s6?qXWJtE z#Kr$SaOo#5eiiWQQTPL}9V{s`sAL7ujLaL|7zf+4VF(_{A+=aaD3w8uLWK@ z6MulUIK*!UKEm;di+?+C=_fA!7T_bDe&XWu-6Q?P#otbOr=Pg^UjZ(ASmNS;0(kXF z_ygREqlOI{;GO)j`C#(rWQ&{pVSxge{5dnim7jCKQu!e+ehY9tU*h7o0$2GVF1{&m z?m6a_H^=wO+XhcRap_^2Rep$ze<$Ug9^&HPO?k&BE_yZ6Zevvwy1e5EuUn z;JRPp;U*h7kuPOb+#lMsCD(4=bwraXx;*w|a zt*ONyfaR*B&Y)&~vzmnta50W*cLD2W$pmt(AC?+7ApIH8B~$=-9!H7$AnXEOJ%wD% zZ6MwnXCN|#_^Fo9Zx88d0bcFw=&vK61>8Zw0E-miR4~xu{fL)XoWTWIi<4F0>K9K1 zLoI(easB~D;wUIJXpkdL27q0`AM*1Nt!vI8eOg`|++t zyibCc!_gmy;_U?P>$@0)*Nlbv_mA$HdYlRMn8eh5F#Xc^W8l6%jzv7n3mhnY{|eeq zUn}(GAe#f9&87790S~{hTwMwUY5FMS>$|-Ky1=78prO7~z{4-pN1s`bgBB=#V<6<~ zdjJ!S(#Pkb^j(n9_e1ELB4s#B-?<5WFG2~M_8bnj`O?>#&^HtT?B@!k?_&vlKZCwC z5X#};>qGiBCG>p?0si=n^xc}!H-9kp_Cz4{F`d%)HK$MY@~pn;_Mlw%$ZP6x5NeSq zrH}Pa`o8P*L5+b{AnsLbXr=GPguWfn$9kE=!B?U5{ioB%vPswpeQER69CN}wv|6bk z_%7s*#vZf;mG(tMHVPN=lbrFgd?n|rUx5(<=gI|$&Oma$`gPm~*HQ{3=S@}9$n%Kv z)pZjmkAqn#;K=r%zeMIR9^3Yg##romZ_|7==fOBfv8kiDXz=FC3mzSo3xL03{RiJv zylZp*9Ja2(OcCaCG@~8OfopD!dMTs1<_0&{EI+hy&A&aV{@0vi;#HsS*tCCtM&aSS z9sGEQvp-dT&O1B#^Fixlv*9~C)_ME=MDsxY6<_JNw~x&S>@~U;^M8#GdoxPWHPZ+6 zeYUJK+IfSPt`EB1JAHkbN`E#!)pC?2VBfGHnzxE#iJmK;jGi6$F*UUIH{B8KYwABQ z$YA#wYqtx(?X&fSKluyJ6>gnzC6U)?usHg`Rz%=m0i#*AG$T#4H2q$6ttUsis)8AEi<)<$^m5BDO7$K>ZP^I`9d zy^YN08#brquWAHmo2~TL+1MSR2l|Z_x4qI>DmzPPf8QCT?f~pV4s0s!^mc^V* z?qo{s4ra3JKK{xLKfGseP{19$^klJv!h!Ot*Ych)v>kx9H&ITG`Z>QbXUW`Sd_VNP zq#O1I-LN;{{vs&7nR^?#Ls~a>NTXl*^!qS7q#ezDpuQg{FDrU8UJg-WH$wjb=s)mZ zxwB*mws)}iytc}AW0_%Vs_NlX?Xm}Z9Y}_jMyTh;GQSh85ZWc{)4t~m#j;=6d*0yv z`;VjUdpFVsqZ51o6mzeIp0?d575MwPspesSBWrhL_S?bR4mMWwXYbk|&nksl?&2P4 zo~48Ai^B4Tw;kj{3#;_>4({mSo@hO$^^EO_W>eZ&@M&WOMsN3>Jul+F0r$u~a59zA zJ^(vA{CoX}ZT8y9f5f%#qP2|Kh2Usoi(2H~pA?>tT}6T1-1O+M0KJ3b^u+Gkjq+qH zQ)pL09Hkj)*n`}+d}4TWnH{(rdY&@!F5kM+@~z#K0zFsmA7U(8+zamL~0< zrHRF9Y0~Lgn&1uVEXtDQS$0W`lXt>$M6A7M3oYJ&HDG54rB2Td_A@uKoZ`HZ?`E8D zvgdnmwCC&R*^*pjcCpH!Jx#m&qMkd7)B6F@PKSOwXjAmZrxJY}y#J}?&pVyU%|4Cy zx_thx<>ycy~wgIoNu#4)2Igw?|yQMx%qa#&`#$ zU;obgDr~u`qIwXsqqY4<)4H%Hi4g$2w}|1rh384E$h^z2bCc;~wRhV#6>rLWY(Lu8 zly>YD%6C@sNkGoD!R==MR9LsB?03a@;%_pwUq?!R)H5z(KiIy1!}}o5-Ul&zA4U$l zGT|*ox$Zr{8r;1VxqB<>cJJk0?g`=cz2fevDD|F-dby{+3_DVdDVAZZD{Y-8hTaTt zJTh(no+=m4ak9d2#Mm-vN9s&+f}9D*z@P`*-)FX?Ii8unzt1tDpNX6a=Llj18I00@ z)RCN`OS3b*)`d?p;T(mJfOe)E9XSr?d^^+Q0zvf7L{~WoDAcLLa zzv)PhzZROEU4Y_aV*hG#-ux_#UL$F?zw! zpYuu3Ol1Tatm5$@6IA*nI}`I-!}On=koQNS8P1uj2r{09C%Kux8A>>jOyDNF zLpq$THJnUrwH2?Q>?G}p`&;;s33s1T`00Kyu2<<+`1C~hj0Ei_@*Ou5G$z8&4*md6 zl*|2tA`C*a?pf$>_G`LOBtixvoMqcWX)qXcgGhuA2YtRrp8)zIkERVpIKU72lR)1W zhMyA9mZCkR>w-GauY2?)&`)~w`#?wWk2f~EbHecP!Aykz%A;q2e%7PU1O07}o(uYc zkbYnAVbGEO(}T-ENBU0>=!+=Qe|kV0j!6IM!7|X1{?h~6YQs5^!bNIM=$iYm6j>Y;4C9=(TJ)1{~t@C@!T)*Vos!f?mG5 zQfwzAvSpYA+ysy*3}qOV80sdBOAUi5lSu?63{WPe43jb`NkecZkf9kj3B*uF?)UBP zckk->$}*1(otA&@p8Ng({^#Gv*|TSN&z{|%Ilc+Hm8c(%I-d1lC9dy#A^m9)S-0PkYaQ`Dx8(bycVsSqsl1*=2@C(N`Kvs$RY~_ZuR;3s;HygZl@Oj=@!8wg>kI5;6amh4?j&e*k`!s9(6B59t6LcGn70WB5-8 z+>eL!I9w&_m)jg~!@DrJ-|>7dRib{;emeIWer52y^Z%9MR|UTqbAC(kj^js+|7U~K zN&rAF!Buj%JiIiUcnWvAuQlDDZOvxdw_w6>A9j7ov?e!g$^@PV=lG_Q9UU8!sq3-8 z0TvdZz$v$zJv7EFMG0TSkGI{8w(pRMuI_C6X6$USt#wm(S2{>_v>M3{YsJXivY4&y znQhpIr#IW3NgHe;g0&W~Z$~bM+3wcODNK{jPtRV=iPhdzY;H5s#JSvyInR12eV1~c z^-|8RHhZEh@>1Dg$tI(?0O`sWjNF2uR)zCAY#ec#;QqEYTH|quK+*G$%!Rr8=+=14j^L5*8f&X;P2AcUp z+FOHt4DFji?|RpKSnuI0iPzu&GS}{mJXTnyZQwdi#F8_fS`Xfj^GWS=Sog1 z;q}#AYhuMeL3s}jpah)cmjf$Z#F8(f+~p6kf zXP;Q|7pd>;5KGQHobBX(ejb5b^%Al4mjc(h?m;uK_PW_j`R8x|Jp!k_Zk__xUN<~f zYq1l{=1$<6FX8|qmYn%K%gOyb-vhbU;3t;;KFVV_fQTg@FWfIfN8u7ne*jqF5=(v) zu)-yl{I4jF;Q%6*{NoCD1>#W2LJcBTd^Q2Aejt{d_nqnoV#!Uq=0E`Q(`D|>8?4;F zH}8W;dl@nK8mB`nn=G2MoSay4Uf)mdFZ)cd`x9ga$lPD7_(A6Wy29c|@TYx~o&v7F z%E}kwPxlwG^7dNb8fGwvSn@XDSx!zYIm1#pCziaQ@)!;vV#(J6t6ULFzMk^gIDm*H z=RL0Hj(@-JrF<6-AY$qBUekV1OMz;EZP)-O3ZjX*_R#$i@@< zqc@(|c7zKK6JVUg}7`h;4>y~^8Q{_HL%ipk_btsV60A(4JOxeLo$UE zo6|W-_|hpDK0U|v;E2~J->0G8j<5m;ZGbC+DpbJxGsT9A9rg_?T5ON9lbX zoUg}cBHKD5=?z2R>y3hs!HJ~zj}Z8JbHH38CHzZ|&*2id1RSa2Q}#UvyL*%2=r`>;bHywvfFbmX9Cb@GE|Nw}@Y^oMvHQp|M~Ovd4Fle*CIqd3#Z)%HBO8 zd-uRVq3dxtq{saqb`}Vi-@y6dHX>Y>N#=76oWgxJWN$6rLDj)uq;&iwq?d)>MN-1Q z^nM-EI|RMj0(v|Fs=54oX&igp57^hMM8sXQ*H~4!Q2;-^M`HH-Ii?pdm)`o2y$+mI zC>>l^)(`jFc>ZC8dSK{c1?B8G41%yE7DS)(dxm7w(-Q zdt9fN{YnwM4LuyvdlPzFrG$U!9R%?6`MX}YoDo7l)<@ib*-!74sL>>nNv|AwetOp-+z$Bj z`f>(~!mSJ0n_6z)OGCww?D3tnZ|^bK8-WJxoew8_mxk<>O|tK`p<+n(_zv5*w>^%v z-HI$>p^&{-JbRz^>^&B;_dD2oyg>Z;j^59|D;L-~GfXd+zEJ+X60&y?h5T3n zd;b=)_cHA1dR*w)`|psw2d3GzUSv|&_wA6qbqn+P_s5>SGZ0a~JU2`a%zKEiz0;xR zr}rt?Q+ZzL*;^j6_dM+VO2+W7_$?0EYeXe*EBu*%tKb+GvMRVXWbaWl9t!2()ggN$ zu&4ZM@$7vmWba^wy^o+`Nb&0r*;{gMKL7Yy2Hqxq{BGROzb-UJ*ftZ%-Zw+`9`x*8 z3}xB-jR5&!gt!o zYS7-Grz6>8TMOalXkH!WIu+1MWwMRAMaA*;^@RKOG%ai5SK6oYoi-PUV0>&laUrh5 zl%ThwVrTFC#D5L-y;XGPt{c{*N`4S@AN`L}KF5Z3M}jCKT~&oDuU;r)w7r>2_ zfrm@L1#ly!;NcQ*0o+Ilc(?>y05?($9xeeFz>P$}!zJJXxRE08aEYQQo~~7;rNI{L zT>!tJH;6TIU!pAoL9mjK0k?4N9|4wT`WH8~H}y2OcJh$oVS5VGW%_}F@}kM8*~X`0 zlf!xc;xVO&Wxy{5U45&^qlzcWEIFi;(Z=(W~j7xbfmXnSDE%MY#3uCUEQ1j)w(s=wW%YW!Q`UG7-two z&bRuQrPYdALK#e=!3?6FbZc)mnZc5rSaisTm^c-{Cv|=PEUVa=Z0`!p&LU==NVgbd zdlzPjVI~-($9`Cp7Z*g#_5+Fiu4>dZBx1>9z|k{s0L9=Wp9frx_63Ppa+ZN8J5eBF z$=TnnW~UfLEIG?a^ejrpzMo)wz1o#}V(GICMcJtW5lhZGvYHPe5V7PeV^Jm)L@fDr zz}0vjlZYi}QfHvvB{jfROgGS6-zY0o&;XqD_W)O)jsu8Ta`H1=u`MA+JxF2}3p2XH zEQ7)(4o;s@mz>u{IOgGb9>$|Sr&H(QMh`FX@Ny4-#={8@ulDd-53lp^dJkXe;SC$?HQ<%C)zxLi#e?u~hd6ygevguxgajNujtu4iRl6AX?*M74Y{L7DJh2=B> zM|ud446Yf+Tby#bsc>wM@*HLD-N5(2iDZxWwQuirFyD3dc&_ZRJRvAvUPGL(o_~(y z<43!G+lXUm55ciY;cZEK6>yBN|Fq+L;X5D`(GH2{_9Mv>_IBo)M-#P; zmM_OPhlVCz!=@YwVmQ1eeELm7?J#p&*~s)KE1x>+>6&NeJh$}46)#=%qvn^_{5bPU z|4-i?8GZVf$obug+gH{$A8Kx1_2R1A|Frh%LsvI%cyYtV>wa@x+28=UOXqz>~LDn3nPB!?#sRb7hvH@Y!FB|1~a1^7g5S3&)2Z z-YK}f-BPWa`?^xu_U^6;6`;w-<@>2OSdrK&;{#U`A6Fg0dtc?9*~s#^kHC@qI5vQL zCU>ts1D?-Fk@WcQ->b*K55Vz}rhC88y}BJIvOW;WUie-efyZa1gn#Lox2fLEI?=t_ z)Yxd3n>vN{rdA{33`h4WzVylUMv5PZps!LIOnOfJ4@#y!dCoBT`0yd4$^YGZ;tjj7 zN9S#$@kM_#8ejMiqwxh#jFzsx6NPqYPf76n+7}WxK3m>A^VRrpNpS1hIEEzSv9dt^ zRc~Do%y>0@I3so0Ku|pIiQ|Lwb{#LCzw-Ft{G#KBOYw5g zpZF$^CvNljgTOmIegt^G#~;mMf)ex1RAo+Pc|U&C_v1(Yl92uvLcAYd&I^G3t+zY@ z>#WnfEabhx9X!$+A~?a>2KZKp6TVH{@_yGot>!s(p}s7 z(tT-kG%z(@Uv*^Co!wc=oF6|{Xzl3k?$O+b_o2!c{@M1fzVx`~AYL|{z8*BaJUvaI z823!`6@`sh7i(NM;lcK#a;Y)jT%aZ}W={5nZufhRDEIFfChtU&K46eS6 z$nFnN=fY)Mt!}arn>lxNuCK`cK)vgK?gpMwV|BiUzX~>BL65@KmmB#0p7FWK3m@)5 z|8YI%&qtEIYQoefkGX8%pZwS!V0lfzkv;>4ZFfGuDW_vru7LB~18;(Ff)mLe|05FO zKL!EY?z{xNRJ6xy9&WpH?M|){DiRF02achvtdkP{rI&5*O#6-ZiP{6_H!eBl_P`#* zjp6w1f#i&)kp)jiQCh3Y^3Uw~*&VQcx1aaCEm=j-L)> ziB~62e0I&xv!?x>7{dp_)gj(i=DZlZA{mW-@zUTsD|Ej(3TEo=f;!`P8S%*~X4iMol~h#_Cjw2j`a!akn!r1bqLo@v4>W^o(R_`%W-cPs!z^S*WUH;C#+AF zEM0!8V*z!DH1o@^PiAhbMSZg9sfMSUo>{iPqNb{(WMJ+3p)=>_dme9J6;q#M@GW3i zVLzh#=vUtxy6URn=2H0}1rxFXaDEA>Pznz1{AUU^deGHgzUh9fo}D7t@sIs>u9l?j1sb)5bKS^Cw8$n#^no z1teSiB3scoS=Aak9WNiUeZA(IMBRgf*GKi{9AMR(#FF!xMaOBs>RrSlPoLK`>e{cw zva=Le?;?mL=QWR3;s7F+d@ZovMG#BQG9i6p$*-fntKW$wUjaOWl?UnndA;9nbHN

;xqWI$i094uTu^`SWz>y zcj~T5>!Qf0s%g`Ls?M{6-t{Go=Nu?L_m-j*UN8^dHzi2j8YzztUtbg--dj|BZb^`u zHl<{+VM+vRnH3eEHzo4g-M_{N)1|>+?UbN+?kz!a{GWrt_!RhE8W}wA(xM*?)ZE4z zARZenir@Ffr1y2N4*U+-Kc{^6?)d(Ti;9PzEl!=mH{HUY7A$`9+2lEBqd8df&xT?uWrAsZ=H?4hkZfak|SQ)IHR4!aRe^U9(S82QI zx0ONa%;Ml-ye-(h`{4l|ea)(GP73~g`ri*M-gRBDd*GvOp?}clUxf-ON;S)AmkKZV zTHu>Jp7%=r{wp!>zTfryw*VjU`0c>o@c4be_j>$6;HN$Q3E&q!{wKgc_xQJgf9LTG zd$Je*QSc2OKOK05$IlD#T;phs=g;}X>pi|6nCl8zDhVzG-r(_U=k<6z*DP8nb>ts; zJ|`?${vxr?|Jo4$y%0Y;WS?oU#9Am%Y)^2FBoW&O$uT@$t%UfmfZya~)W1FCZ=b#$ z$$rfbVBe`TX;y)8tBm9Y9K$mwm@zRsd}h{KVb@C&4Y)*!@Abtbmy5|8>Fr3TtrZuH z85y*LcdZ}JG&L#&iup*bGl{MaoLwf z+x^VVw_qIfB~?#%cZV_Jj|cXqvo6~CX&5@8Yh!B?Z<%m$deXMsMdSABvka>3PzPLH zW97t>vs`infkZ4h+ZxpzI06w%&T<=_N2#4_EO|5d>e)tYzL%Apv7a+doj8)_h8$)5zCIfv4*-{kSWP#cO^`Z3^Y%-bgsOV0a7^HGQ;Ur7C!Q8#-n zOI{68h3lJl0N1kZZ2hS3wcfL>)ZCNF0-|aAS`gkFPt+%6b1+xH&6)Z(ZTWT2Izy49`e#rOSN# z^G)Jm3?xPSdc9MBjgnnZ@edbB~B3s(*|k>0t`35UyiP9(^p#u_vs4@8vT z2ZY; pmsis.dxy - doxygen pmsis.dxy - ln -s ${CURDIR}/doc/html/index.html pmsis.html diff --git a/rtos/pmsis/pmsis_api/docs/drivers.rst b/rtos/pmsis/pmsis_api/docs/drivers.rst deleted file mode 100644 index a48f36da..00000000 --- a/rtos/pmsis/pmsis_api/docs/drivers.rst +++ /dev/null @@ -1,101 +0,0 @@ -Drivers -------- - -Cluster -....... - -FC/cluster synchronization -========================== - -.. doxygengroup:: FcClusterSync - :members: - :private-members: - :protected-members: - -Cluster team synchronization -============================ - -.. doxygengroup:: ClusterTeam - :members: - :private-members: - :protected-members: - -Cluster DMA -=========== - -.. doxygengroup:: ClusterDMA - :members: - :private-members: - :protected-members: - -UART -.... - -.. doxygengroup:: UART - :members: - :private-members: - :protected-members: - -SPI -... - -.. doxygengroup:: SPI - :members: - :private-members: - :protected-members: - -Hyperbus -........ - -.. doxygengroup:: Hyperbus - :members: - :private-members: - :protected-members: - -CPI -... - -.. doxygengroup:: CPI - :members: - :private-members: - :protected-members: - -GPIO -.... - -.. doxygengroup:: GPIO - :members: - :private-members: - :protected-members: - -I2C -... - -.. doxygengroup:: I2C - :members: - :private-members: - :protected-members: - -I2S -... - -.. doxygengroup:: I2S - :members: - :private-members: - :protected-members: - -Padframe -........ - -.. doxygengroup:: Padframe - :members: - :private-members: - :protected-members: - -Performance counters -.................... - -.. doxygengroup:: Perf - :members: - :private-members: - :protected-members: diff --git a/rtos/pmsis/pmsis_api/docs/headers/groups.h b/rtos/pmsis/pmsis_api/docs/headers/groups.h deleted file mode 100755 index 42e70f6c..00000000 --- a/rtos/pmsis/pmsis_api/docs/headers/groups.h +++ /dev/null @@ -1,13 +0,0 @@ - - -/** - * @defgroup groupRTOS RTOS - */ - -/** - * @defgroup groupDrivers Drivers - */ - -/** - * @defgroup groupChips Chips - */ diff --git a/rtos/pmsis/pmsis_api/docs/index.rst b/rtos/pmsis/pmsis_api/docs/index.rst deleted file mode 100644 index b53dee42..00000000 --- a/rtos/pmsis/pmsis_api/docs/index.rst +++ /dev/null @@ -1,19 +0,0 @@ -PMSIS API documentation -======================= - -.. include:: mainpage.md - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - rtos - drivers - chips - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/rtos/pmsis/pmsis_api/docs/mainpage.md b/rtos/pmsis/pmsis_api/docs/mainpage.md deleted file mode 100644 index e6219c5c..00000000 --- a/rtos/pmsis/pmsis_api/docs/mainpage.md +++ /dev/null @@ -1,13 +0,0 @@ -Introduction -============ - -The PMSIS API is a set of low-level drivers which any operating system can implement to provide a common layer to upper layers. Together with the PMSIS BSP, it provides a full stack of drivers, allowing the development of applications portable across a wide range of operating systems. - -Conventions -=========== - -All functions prefixed by `pi_` can only be called from fabric-controller side while the ones prefixed by `pi_cl_` can only be called from cluster side. Any exception to these rules is documented where it applies. - -All functions on fabric-controller side are by default synchronous and are blocking the caller until the operation is done. All the functions suffixed by `_async` are asynchronous and are not blocking the caller. The termination of such operations is managed with a `pi_task_t` object, see PMSIS API documentation for more information. - -Functions on cluster-side are by default synchronous but can also be asynchronous if the documentation of the function mentions it. diff --git a/rtos/pmsis/pmsis_api/docs/make.bat b/rtos/pmsis/pmsis_api/docs/make.bat deleted file mode 100644 index 2119f510..00000000 --- a/rtos/pmsis/pmsis_api/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/rtos/pmsis/pmsis_api/docs/pmsis.dxy.in b/rtos/pmsis/pmsis_api/docs/pmsis.dxy.in deleted file mode 100755 index cab00d46..00000000 --- a/rtos/pmsis/pmsis_api/docs/pmsis.dxy.in +++ /dev/null @@ -1,2370 +0,0 @@ -# Doxyfile 1.8.6 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = PMSIS - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = "Version 0.1" - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "PULP Microcontrollers Software Interface Standard (PMSIS)" - -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = doc - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = NO - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = NO - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = YES - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = NO - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. -# -# Note For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = YES - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = YES - -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = NO - - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 0 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = YES - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. -# The default value is: NO. - -WARN_NO_PARAMDOC = YES - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. -# Note: If this tag is empty the current directory is searched. - -INPUT = ../include/pmsis/chips/gap8/perf.h \ - ../include/pmsis/drivers/spi.h \ - ../include/pmsis/drivers/uart.h \ - ../include/pmsis/drivers/perf.h \ - ../include/pmsis/drivers/pad.h \ - ../include/pmsis/drivers/i2c.h \ - ../include/pmsis/drivers/gpio.h \ - ../include/pmsis/drivers/hyperbus.h \ - ../include/pmsis/drivers/cpi.h \ - ../include/pmsis/drivers/i2s.h \ - ../include/pmsis/rtos/malloc/pmsis_l2_malloc.h \ - ../include/pmsis/rtos/malloc/pmsis_fc_tcdm_malloc.h \ - ../include/pmsis/rtos/malloc/pmsis_l1_malloc.h \ - ../include/pmsis/cluster/cl_malloc.h \ - ../include/pmsis/cluster/cluster_team/cl_team.h \ - ../include/pmsis/cluster/cl_pmsis_types.h \ - ../include/pmsis/cluster/cluster_sync/fc_to_cl_delegate.h \ - ../include/pmsis/cluster/cluster_sync/cl_to_fc_delegate.h \ - ../include/pmsis/cluster/dma/cl_dma.h \ - ../include/pmsis/task.h \ - headers - -#INPUT = ../include/pmsis/cluster/cluster_sync/fc_to_cl_delegate.h ../include/pmsis/pmsis_types.h - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. - -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py \ - *.f90 \ - *.f \ - *.for \ - *.vhd \ - *.vhdl \ - *.txt - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = math_helper.* - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = */RTE/* - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = * - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = NO - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = NO - -# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more acurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. -# Note: The availability of this option depends on whether or not doxygen was -# compiled with the --with-libclang option. -# The default value is: NO. - -CLANG_ASSISTED_PARSING = NO - -# If clang assisted parsing is enabled you can provide the compiler with command -# line options that you would normally use when invoking the compiler. Note that -# the include paths will already be set by doxygen for the files and directories -# specified with INPUT and INCLUDE_PATH. -# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. - -CLANG_OPTIONS = - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = NO - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = ignore_ - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = YES - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 0 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = http://www.mathjax.org/mathjax - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /

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

v`}M?1 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%0A-Q}hmORHx!ajKdWgJe1Ltaq^Ips0nVe-ULkW0M| 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

  • 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 diff --git a/tools/pulp-debug-bridge/bin/plpbridge b/tools/pulp-debug-bridge/bin/plpbridge deleted file mode 100755 index 4c75bd88..00000000 --- a/tools/pulp-debug-bridge/bin/plpbridge +++ /dev/null @@ -1,479 +0,0 @@ -#!/usr/bin/env python3 - -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - - -import bridge.debug_bridge as db -import argparse -import code -from importlib.machinery import SourceFileLoader -import os.path -import sys -import pulp_config as plpconf - -try: - import plptree -except: - pass - -import json_tools as js - -try: - from IPython import embed -except: - pass - - - -class bcolors: - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' - - -def fatal_error(error): - print (bcolors.FAIL + 'FATAL ERROR: ' + error + bcolors.ENDC) - - -def read(bridge): - if args.addr == None: - raise Exception('Access address must be given through option --addr when using command "read"') - - addr = int(args.addr, 0) - size = int(args.size, 0) - width = args.width - - if args.verbose >= 4: - print ('Read access command (addr: 0x%x, size: 0x%x)' % (addr, size)) - - data = bridge.read(addr=addr, size=size) - - while size > 0: - line = '%8.8x: ' % addr - for i in range(0, 16, width): - - line += '%*.*x ' % (width*2, width*2, int.from_bytes(b''.join(data[0:width]), byteorder='little')) - size -= width - addr += width - data = data[width:] - if size <= 0: - break - print (line) - - return 0 - - - -def write(bridge): - if args.addr == None: - raise Exception('Access address must be given through option --addr when using command "write"') - - if args.value == None: - raise Exception('Access value must be given through option --value when using command "write"') - - addr = int(args.addr, 0) - size = int(args.size, 0) - value = int(args.value, 0) - - if args.verbose >= 4: - print ('Write access command (addr: 0x%x, size: 0x%x, value: 0x%x)' % (addr, size, value)) - - bridge.write(addr=addr, size=size, buffer=value.to_bytes(size, byteorder='little')) - - return 0 - -def efuse_write(bridge): - addr = int(args.addr, 0) - value = int(args.value, 0) - mask = int(args.mask, 0) - - bridge.efuse_access(args.flasher_init, True, addr, value, mask) - - return 0 - -def eeprom_write(bridge): - addr = int(args.addr, 0) - - return bridge.eeprom_access(args.flasher_init, 0, 0, True, addr, args.file) - - -def get_flash_id(): - if args.flash_type == 'hyperflash': - return 1 - elif args.flash_type == 'mram': - return 2 - else: - return 0 - - -def flash_write(bridge): - addr = int(args.addr, 0) - - return bridge.flash_access(args.flasher_init, get_flash_id(), 0, 0, True, addr, 0, args.file) - - -def flash_read(bridge): - addr = int(args.addr, 0) - size = int(args.size, 0) - - return bridge.flash_access(args.flasher_init, get_flash_id(), 0, 0, False, addr, size, args.file) - - -def flash_erase(bridge): - addr = int(args.addr, 0) - size = int(args.size, 0) - - return bridge.flash_erase(args.flasher_init, get_flash_id(), 0, 0, addr, size) - - -def flash_erase_sector(bridge): - addr = int(args.addr, 0) - - return bridge.flash_erase_sector(args.flasher_init, get_flash_id(), 0, 0, addr) - - -def flash_erase_chip(bridge): - return bridge.flash_erase_chip(args.flasher_init, get_flash_id(), 0, 0) - - -def load(bridge): - - if args.verbose >= 4: - binaries = [] - binaries_conf = bridge.config.get('**/runner/binaries') - if binaries_conf is not None: - binaries = binaries_conf.get_dict() - print ('Loading ELF binaries (path: %s)' % ' '.join(binaries)) - - return bridge.load() - - -def ioloop(bridge): - if args.verbose >= 4: - print ('Lauching IO loop') - - return bridge.ioloop() - - -def reqloop(bridge): - if args.verbose >= 4: - print ('Lauching req loop') - - return bridge.reqloop() - - -def gdb(bridge): - if args.verbose >= 4: - print ('Lauching GDB server') - - return bridge.gdb(int(args.rsp_port)) - - -def start(bridge): - if args.verbose >= 4: - print ('Starting execution') - - return bridge.start() - - -def stop(bridge): - if args.verbose >= 4: - print ('Stopping execution') - - return bridge.stop() - - -def wait(bridge): - if args.verbose >= 4: - print ('Waiting termination') - - return bridge.wait() - -def flash(bridge): - # When using the flasher, the binaries should be the flasher - bridge.binaries= [os.environ["INSTALL_DIR"] + "/bin/flasher"] - if args.verbose >=4: - print("Flashing the flash image") - - load(bridge) - ioloop(bridge) - start(bridge) - return bridge.flash(args.fimages) - - -def reset(bridge): - if args.verbose >= 4: - print ('Chip reset') - - return bridge.reset() - - -def script(bridge): - - for script in args.scripts: - if args.verbose >= 4: - print ('Executing script: ' + script) - - if script.find('@') == -1: - script_name = script - script_entry = "debug_bridge_entry" - else: - script_entry, script_name = script.split('@') - - module = SourceFileLoader("user_script", script_name).load_module() - - entry = getattr(module, script_entry) - if entry(bridge): - return -1 - - return 0 - - - -commands = { - 'read' : ['Read data from the target', read], - 'write' : ['Write data to the target', write], - 'load' : ['Load a binary into the target', load], - 'ioloop' : ['Activate IO loop', ioloop], - 'reqloop' : ['Activate request loop', reqloop], - 'gdb' : ['Open RSP server for GDB connection',gdb], - 'start' : ['Start execution', start], - 'stop' : ['Stop execution', stop], - 'flash' : ['Flash the flash image', flash], - 'wait' : ['Wait termination', wait], - 'reset' : ['Chip reset', reset], - 'script' : ['Execute user scripts', script], - 'efuse_write' : ['Write to efuse', efuse_write], - 'eeprom_write': ['Write to eeprom', eeprom_write], - 'flash_write' : ['Write to flash', flash_write], - 'flash_read' : ['Read from flash', flash_read], - 'flash_erase_sector' : ['Erase flash sector', flash_erase_sector], - 'flash_erase' : ['Erase flash area', flash_erase], - 'flash_erase_chip' : ['Erase flash', flash_erase_chip], -} - - -command_help = """Available commands: -""" - -for name, cmd in commands.items(): - command_help += ' %-10s %s\n' % (name, cmd[0]) - -parser = argparse.ArgumentParser(description='Control a pulp target', - epilog=command_help, formatter_class=argparse.RawDescriptionHelpFormatter, add_help=False -) - -parser.add_argument('command', metavar='CMD', type=str, nargs='*', - help='a command to be executed (see the command help afterwards)') - -parser.add_argument("--script", dest="scripts", default=[], action="append", help="Specify a script to be executed with the script command") -parser.add_argument("--config", dest="config", default=None, help="Specify the system configuration") -parser.add_argument("--config-path", dest="config_path", default=None, help="Specify the system configuration") -parser.add_argument("--ipython", dest="ipython", action="store_true", help="Enter ipython shell") -parser.add_argument("--shell", dest="shell", action="store_true", help="Enter python shell") -parser.add_argument("--port", dest="port", type=int, default=None, help="Specify port for proxy mode") -parser.add_argument("--debug", dest="debug", action='store_true', default=False, help="Activate debug mode for this script") -parser.add_argument("--verbose", dest="verbose", type=int, default=3, help="Verbose mode") -parser.add_argument("--help", dest="help", action='store_true', default=False, help="Dump help") -parser.add_argument("--cable", dest="cable", default=None, help="Specify cable") -parser.add_argument("--chip", dest="chip", default=None, help="Specify target chip") -parser.add_argument("--binary", dest="binaries", default=[], action="append", help="Specify a binary to be loaded by the load command") -parser.add_argument("--flash-image", dest="fimages", default=[], action="append", help="Specify a flash image to be flashed") -parser.add_argument("--flash-type", dest="flash_type", default="hyperflash", help="Specify flash type") -parser.add_argument("--config-opt", dest="configOpt", default=[], action="append", help='specify configuration option') - -[args, otherArgs] = parser.parse_known_args() - -if 'read' in args.command or 'write' in args.command: - parser.add_argument("--addr", dest="addr", default=None, help="Specify the address of the access for read and write commands") - parser.add_argument("--size", dest="size", default="4", help="Specify the size of the access for read and write commands") - -opt_flasher_init = False - -if 'efuse_write' in args.command: - opt_flasher_init = True - parser.add_argument("--addr", dest="addr", default=None, help="Specify the address of the access for read and write commands") - parser.add_argument("--value", dest="value", default=None, help="Specify the value to be written for write command") - parser.add_argument("--mask", dest="mask", default="0xffffffff", help="Specify the mask used to select which bit is written") - -if 'read' in args.command: - parser.add_argument("--width", dest="width", default=1, type=int, help="Specify the word size in bytes used to display numbers for read commands") - -if 'write' in args.command: - parser.add_argument("--value", dest="value", default=None, help="Specify the value to be written for write command") - -if 'eeprom_write' in args.command or 'flash_write' in args.command or 'flash_read' in args.command: - parser.add_argument("--file", dest="file", default=None, help="The file to be written for eeprom_write command") - parser.add_argument("--addr", dest="addr", default=None, help="Specify the address of the access for read and write commands") - opt_flasher_init = True - -if 'flash_read' in args.command or 'flash_erase' in args.command: - parser.add_argument("--size", dest="size", default="4", help="Specify the size of the access for read and write commands") - -if 'flash_erase_sector' in args.command or 'flash_erase' in args.command: - parser.add_argument("--addr", dest="addr", default=None, help="Specify the address of the access for read and write commands") - opt_flasher_init = True - -if 'flash_erase_chip' in args.command: - opt_flasher_init = True - -if 'gdb' in args.command: - parser.add_argument("--rsp-port", dest="rsp_port", default=1234, help="Specify the port number that the RSP will use to open a socket for GDB connection") - -if opt_flasher_init: - parser.add_argument("--flasher-init", dest="flasher_init", action="store_true", default=True, help="Initialize flasher") - parser.add_argument("--no-flasher-init", dest="flasher_init", action="store_false", default=True, help="Initialize flasher") - - -parser.add_argument("--boot-mode", dest="boot_mode", default=None, help="Specify the boot mode") - -args = parser.parse_args() - -if args.help: - parser.print_help() - exit(0) - - - -config_path = args.config_path -config_name = args.config - - -if config_name is not None: - config_path = os.path.join( - os.path.dirname(os.path.dirname(sys.argv[0])), - 'configs', 'config', '%s.json' % config_name - ) - -elif args.chip is not None: - config_path = os.path.join( - os.path.dirname(os.path.dirname(sys.argv[0])), - 'configs', 'chips', args.chip, '%s.json' % args.chip - ) - - if not os.path.exists(config_path): - print ("ERROR, didn't find any configuration for specified chip (chip: %s, config: %s)" % (args.chip, config_path)) - exit(-1) - -elif config_path is None: - raise Exception('A chip or a config file must be specified') - -config = plpconf.get_config(config_path, config_opts=args.configOpt, interpret=True) - - -# And overloads it with the specified options -if args.boot_mode is not None: - if config.get('**/debug_bridge') is None: - config.set('debug_bridge', {}) - - config.get('**/debug_bridge').set('boot-mode', args.boot_mode) - -cable = os.environ.get('PLPBRIDGE_CABLE') -if cable is not None and args.cable is not None: - print ('Overwriting --cable option with PLPBRIDGE_CABLE: %s' % cable) - args.cable = cable - -if args.cable is not None: - if config.get('**/debug_bridge') is None: - config.set('debug_bridge', {}) - - config.get('**/debug_bridge').set('cable/type', args.cable) - -if args.port is not None: - if config.get('**/debug_bridge') is None: - config.set('debug_bridge', {}) - - if config.get('**/debug_bridge/cable') is None: - config.get('**/debug_bridge').set('cable', {}) - - if config.get('**/debug_bridge/cable/jtag-proxy') is None: - config.get('**/debug_bridge/cable').set('jtag-proxy', {}) - - config.get('**/debug_bridge/cable/jtag-proxy').set('port', args.port) - -for binary in args.binaries: - if config.get('**/runner') is None: - config.set('runner', {}) - - if config.get('**/runner/binaries') is None: - config.get('**/runner').set('binaries', []) - - config.get('**/runner').set('binaries', binary) - - - - -if len(args.command) == 0: - json_commands = config.get('**/debug_bridge/commands') - if json_commands is not None: - args.command = json_commands.get().split() - - -binaries = [] -binaries_conf = config.get('**/runner/binaries') -if binaries_conf is not None: - binaries = binaries_conf.get_dict() - -bridge = db.get_bridge(config=config, verbose=args.verbose, binaries=binaries) - -if args.ipython: - embed() - -if args.shell: - code.interact(local=locals()) - -#bridge.exec_config() - - - - - -def handle_commands(bridge): - if len(args.command) == 0: - parser.print_help() - exit(0) - - else: - for cmd in args.command: - - if commands.get(cmd) is None: - fatal_error('Unknown command: ' + cmd) - exit(1) - else: - try: - if commands.get(cmd)[1](bridge) != 0: - print () - fatal_error('the command \'%s\' has failed' % (cmd)) - exit(1) - except Exception as e: - fatal_error('the command \'%s\' has failed with an exception: %s' % (cmd, e)) - if args.debug: - raise - exit(1) - - - - - -handle_commands(bridge) diff --git a/tools/pulp-debug-bridge/include/debug_bridge/debug_bridge.h b/tools/pulp-debug-bridge/include/debug_bridge/debug_bridge.h deleted file mode 100644 index 54c48410..00000000 --- a/tools/pulp-debug-bridge/include/debug_bridge/debug_bridge.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - */ - -#ifndef __DEBUG_BRIDGE_DEBUG_BRIDGE_H__ -#define __DEBUG_BRIDGE_DEBUG_BRIDGE_H__ - -#define PROTOCOL_VERSION_0 0 // Initial version -#define PROTOCOL_VERSION_1 1 // Added runtime / bridge state synchronization -#define PROTOCOL_VERSION_2 2 // Added bridge to runtime requests -#define PROTOCOL_VERSION_3 3 // Added field "connected" in target state to allow bridge to reconnect several times -#define PROTOCOL_VERSION_4 4 // Added field "bridge_to_target" in requests as they are now released by the target - -#define HAL_PRINTF_BUF_SIZE 128 - -typedef enum { - HAL_BRIDGE_REQ_CONNECT = 0, - HAL_BRIDGE_REQ_DISCONNECT = 1, - HAL_BRIDGE_REQ_OPEN = 2, - HAL_BRIDGE_REQ_READ = 3, - HAL_BRIDGE_REQ_WRITE = 4, - HAL_BRIDGE_REQ_CLOSE = 5, - HAL_BRIDGE_REQ_FB_OPEN = 6, - HAL_BRIDGE_REQ_FB_UPDATE = 7, - HAL_BRIDGE_REQ_TARGET_STATUS_SYNC = 8, - HAL_BRIDGE_REQ_REPLY = 9, - HAL_BRIDGE_TARGET_REQ_EFUSE_ACCESS = 10, - HAL_BRIDGE_TARGET_REQ_EEPROM_ACCESS = 11, - HAL_BRIDGE_TARGET_REQ_BUFFER_ALLOC = 12, - HAL_BRIDGE_TARGET_REQ_BUFFER_FREE = 13, - HAL_BRIDGE_TARGET_REQ_FLASH_ACCESS = 14, - HAL_BRIDGE_TARGET_REQ_FLASH_ERASE_CHIP = 15, - HAL_BRIDGE_TARGET_REQ_FLASH_ERASE_SECTOR = 16, - HAL_BRIDGE_TARGET_REQ_FLASH_ERASE = 17, - HAL_BRIDGE_REQ_FIRST_USER = 18 -} hal_bridge_req_e; - -typedef enum { - HAL_BRIDGE_REQ_FB_FORMAT_GRAY = 1, - HAL_BRIDGE_REQ_FB_FORMAT_RGB = 2, - HAL_BRIDGE_REQ_FB_FORMAT_RAW = 3 -} hal_bridge_fb_format_e; - -typedef struct hal_bridge_req_s { - uint64_t bridge_data; - uint32_t next; - uint32_t size; - uint32_t type; - uint32_t done; - uint32_t popped; - uint32_t bridge_to_target; - union { - struct { - uint32_t name_len; - uint32_t name; - uint32_t flags; - uint32_t mode; - uint32_t retval; - } open; - struct { - uint32_t file; - uint32_t retval; - } close; - struct { - uint32_t file; - uint32_t ptr; - uint32_t len; - uint32_t retval; - } read; - struct { - uint32_t file; - uint32_t ptr; - uint32_t len; - uint32_t retval; - } write; - struct { - uint64_t screen; - uint32_t name_len; - uint32_t name; - uint32_t width; - uint32_t height; - uint32_t format; - } fb_open; - struct { - uint64_t screen; - uint32_t addr; - uint32_t posx; - uint32_t posy; - uint32_t width; - uint32_t height; - } fb_update; - struct { - uint32_t is_write; - uint32_t index; - uint32_t value; - uint32_t mask; - } efuse_access; - struct { - uint8_t itf; - uint8_t cs; - uint8_t is_write; - uint8_t padding; - uint32_t addr; - uint32_t buffer; - uint32_t size; - uint32_t retval; - } eeprom_access; - struct { - uint8_t type; - uint8_t itf; - uint8_t cs; - uint8_t is_write; - uint32_t addr; - uint32_t buffer; - uint32_t size; - uint32_t retval; - } flash_access; - struct { - uint8_t type; - uint8_t itf; - uint8_t cs; - uint8_t padding; - uint32_t retval; - } flash_erase_chip; - struct { - uint8_t type; - uint8_t itf; - uint8_t cs; - uint8_t padding; - uint32_t addr; - uint32_t retval; - } flash_erase_sector; - struct { - uint8_t type; - uint8_t itf; - uint8_t cs; - uint8_t padding; - uint32_t addr; - uint32_t size; - uint32_t retval; - } flash_erase; - struct { - uint32_t size; - uint32_t buffer; - } buffer_alloc; - struct { - uint32_t size; - uint32_t buffer; - } buffer_free; - struct { - } target_status_sync; - }; -} hal_bridge_req_t; - -typedef struct { - volatile int32_t connected; -} __attribute__((packed)) hal_target_state_t; - -typedef struct { - // The bridge will set it to one when it finds the debug struct to notify - // the target that the bridge is present. - volatile int32_t connected; -} __attribute__((packed)) hal_bridge_state_t; - -// This structure can be used to interact with the host loader -typedef struct { - - uint32_t protocol_version; - - hal_target_state_t target; - - hal_bridge_state_t bridge; - - // Used by external debug bridge to get exit status when using the board - uint32_t exit_status; - - // Printf - uint32_t use_internal_printf; - uint32_t pending_putchar; - uint32_t putc_current; - uint8_t putc_buffer[HAL_PRINTF_BUF_SIZE]; - - // Debug step, used for showing progress to host loader - uint32_t debug_step; - uint32_t debug_step_pending; - - // Requests - uint32_t first_req; - uint32_t last_req; - uint32_t first_bridge_req; - uint32_t first_bridge_free_req; - uint32_t target_req; - - uint32_t notif_req_addr; - uint32_t notif_req_value; - -} __attribute__((packed)) hal_debug_struct_t; - -#endif \ No newline at end of file diff --git a/tools/pulp-debug-bridge/include/debug_bridge/proxy.hpp b/tools/pulp-debug-bridge/include/debug_bridge/proxy.hpp deleted file mode 100644 index 2e8ea8c2..00000000 --- a/tools/pulp-debug-bridge/include/debug_bridge/proxy.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - */ - -#ifndef __DEBUG_BRIDGE_PROXY_HPP__ -#define __DEBUG_BRIDGE_PROXY_HPP__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - DEBUG_BRIDGE_JTAG_REQ = 0, - DEBUG_BRIDGE_RESET_REQ = 1, - DEBUG_BRIDGE_CONFIG_REQ = 2 -} proxy_req_type_e; - -typedef enum { - DEBUG_BRIDGE_JTAG_TDI = 0, - DEBUG_BRIDGE_JTAG_TMS = 1, - DEBUG_BRIDGE_JTAG_TRST = 2 -} proxy_req_jtag_cycle_e; - -typedef struct { - int32_t type; - - union { - struct { - int32_t bits; - int8_t tdo; - } jtag; - struct { - int32_t active; - int32_t duration; - } reset; - struct { - int32_t value; - } config; - }; - -} proxy_req_t; - - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/tools/pulp-debug-bridge/python/bridge/chips/arnold.py b/tools/pulp-debug-bridge/python/bridge/chips/arnold.py deleted file mode 100644 index 1f1aff3c..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/arnold.py +++ /dev/null @@ -1,108 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import time - -JTAG_SOC_AXIREG = 4 - -JTAG_SOC_CONFREG = 7 -JTAG_SOC_CONFREG_WIDTH = 9 - -BOOT_MODE_JTAG = 1 -BOOT_MODE_JTAG_HYPER = 11 -CONFREG_BOOT_WAIT = 1 -CONFREG_PGM_LOADED = 1 -CONFREG_INIT = 0 - -class arnold_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False, fimages=[]): - super(arnold_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - self.fimages = fimages - self.start_cores = False - - def stop(self): - - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - if self.verbose: - print ("Notifying to boot code that we are doing a JTAG boot") - self.get_cable().jtag_reset(True) - self.get_cable().jtag_reset(False) - self.get_cable().chip_reset(True) - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) - self.get_cable().chip_reset(False) - - - # Now wait until the boot code tells us we can load the code - if self.verbose: - print ("Waiting for notification from boot code") - while True: - reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) - if reg_value == CONFREG_BOOT_WAIT: - break - print ("Received for notification from boot code") - - # Stall the FC - self.write(0x1A110000, 4, [0, 0, 1, 0]) - - print ("Stopped core") - - return 0 - - def reset(self): - self.stop() - return 0 - - - def load_jtag(self, binaries): - - if self.verbose: - print ('Loading binary through jtag') - - if self.stop() != 0: - return -1 - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - # Be careful to set the new PC only after the code is loaded as the prefetch - # buffer is immediately fetching instructions and would get wrong instructions - self.write(0x1A112000, 4, [0x80, 0x80, 0x00, 0x1c]) - - self.start_cores = True - - return 0 - - - def start(self): - - if self.start_cores: - print ('Starting execution') - - # Unstall the FC so that it starts fetching instructions from the loaded binary - self.write(0x1A110000, 4, [0, 0, 0, 0]) - - return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/fulmine.py b/tools/pulp-debug-bridge/python/bridge/chips/fulmine.py deleted file mode 100644 index da7cc2b1..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/fulmine.py +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * - - -class fulmine_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False): - super(fulmine_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - # We have to use the soc tap on fulmine - if config.get('**/debug_bridge/cable/tap') is not None: - config.get('**/debug_bridge/cable').set('tap', 1) - - def start(self): - self.write_32(0x10200008, 0x1) - return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/gap.py b/tools/pulp-debug-bridge/python/bridge/chips/gap.py deleted file mode 100644 index 97bc0f41..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/gap.py +++ /dev/null @@ -1,217 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import time - -JTAG_SOC_AXIREG = 4 - -JTAG_SOC_CONFREG = 7 -JTAG_SOC_CONFREG_WIDTH = 4 - -BOOT_MODE_JTAG = 4 -BOOT_MODE_JTAG_HYPER = 11 -CONFREG_BOOT_WAIT = 1 -CONFREG_PGM_LOADED = 1 -CONFREG_INIT = 0 - -class gap_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False, fimages=[]): - super(gap_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - self.fimages = fimages - self.start_cores = False - - def stop(self): - - - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - if self.verbose: - print ("Notifying to boot code that we are doing a JTAG boot") - self.get_cable().chip_reset(True) - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG) - self.get_cable().chip_reset(False) - - # Removed synchronization with boot code due to HW bug, it is better - # to stop fc as soon as possible - -# # Now wait until the boot code tells us we can load the code -# if self.verbose: -# print ("Waiting for notification from boot code") -# while True: -# reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG) -# if reg_value == CONFREG_BOOT_WAIT: -# break -# print ("Received for notification from boot code") - - # Stall the FC - self.write(0x1B300000, 4, [0, 0, 1, 0]) - - # Configure FLL with no lock to avoid the HW bug with fll - #self.write_32(0x1a100004, 0x840005f5) - #self.write_32(0x1a100008, 0x8100410b) - - return 0 - - - def load_jtag(self, binaries): - - if self.verbose: - print ('Loading binary through jtag') - - if self.stop() != 0: - return -1 - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - with open(binaries[0], 'rb') as file: - entry = ELFFile(file).header['e_entry'] - # Be careful to set the new PC only after the code is loaded as the prefetch - # buffer is immediately fetching instructions and would get wrong instructions - self.write_32(0x1B302000, entry) - - self.start_cores = True - - return 0 - - - def start(self): - - if self.start_cores: - print ('Starting execution') - - # Unstall the FC so that it starts fetching instructions from the loaded binary - self.write(0x1B300000, 4, [0, 0, 0, 0]) - - return 0 - - - def load_jtag_hyper(self, binaries): - - if self.verbose: - print ('Loading binary through jtag_hyper') - - # Reset the chip and tell him we want to load from hyper - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - if self.verbose: - print ("Notifying to boot code that we are doing a JTAG boot from hyperflash") - self.get_cable().chip_reset(True) - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG_HYPER) - self.get_cable().chip_reset(False) - - return 0 - - - def flash(self, fimages): - MAX_BUFF_SIZE = (350*1024) - f_path = fimages[0] - addrHeader = self._get_binary_symbol_addr('flasherHeader') - addrImgRdy = addrHeader - addrFlasherRdy = addrHeader + 4 - addrFlashAddr = addrHeader + 8 - addrIterTime = addrHeader + 12 - addrBufSize = addrHeader + 16 - # open the file in read binary mode - f_img = open(f_path, 'rb') - f_size = os.path.getsize(f_path) - lastSize = f_size % MAX_BUFF_SIZE; - if(lastSize): - n_iter = f_size // MAX_BUFF_SIZE + 1; - else: - n_iter = f_size // MAX_BUFF_SIZE - - flasher_ready = self.read_32(addrFlasherRdy) - while(flasher_ready == 0): - flasher_ready = self.read_32(addrFlasherRdy) - flasher_ready = 0; - addrBuffer = self.read_32((addrHeader+20)) - indexAddr = 0 - self.write_32(addrFlashAddr, 0) - self.write_32(addrIterTime, n_iter) - for i in range(n_iter): - if (lastSize and i == (n_iter-1)): - buff_data = f_img.read(lastSize) - self.write(addrBuffer, lastSize, buff_data) - self.write_32(addrBufSize, ((lastSize + 3) & ~3)) - else: - buff_data = f_img.read(MAX_BUFF_SIZE) - self.write(addrBuffer, MAX_BUFF_SIZE, buff_data) - self.write_32(addrBufSize, MAX_BUFF_SIZE) - self.write_32(addrImgRdy, 1) - self.write_32(addrFlasherRdy, 0) - if (i!=(n_iter-1)): - flasher_ready = self.read_32(addrFlasherRdy) - while(flasher_ready == 0): - flasher_ready = self.read_32(addrFlasherRdy) - f_img.close() - return 0 - - - def load_jtag_old(self): - - if self.verbose: - print ('Loading binary through jtag') - - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - if self.verbose: - print ("Notifying to boot code that we are doing a JTAG boot") - self.get_cable().chip_reset(True) - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG) - self.get_cable().chip_reset(False) - - # Now wait until the boot code tells us we can load the code - if self.verbose: - print ("Waiting for notification from boot code") - while True: - reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG) - if reg_value == CONFREG_BOOT_WAIT: - break - print ("Received for notification from boot code") - - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in self.binaries: - if self.load_elf(binary=binary): - return 1 - - return 0 - - - def start_old(self): - - # And notify the boot code that the binary is ready - if self.verbose: - print ("Notifying to boot code that the binary is loaded") - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, CONFREG_PGM_LOADED) - - return 0 - - diff --git a/tools/pulp-debug-bridge/python/bridge/chips/gap8_revc.py b/tools/pulp-debug-bridge/python/bridge/chips/gap8_revc.py deleted file mode 100644 index d09c7e8a..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/gap8_revc.py +++ /dev/null @@ -1,237 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import time - -JTAG_SOC_AXIREG = 4 - -JTAG_SOC_CONFREG = 7 -JTAG_SOC_CONFREG_WIDTH = 4 - -BOOT_MODE_JTAG = 1 -BOOT_MODE_JTAG_HYPER = 11 -CONFREG_BOOT_WAIT = 1 -CONFREG_PGM_LOADED = 1 -CONFREG_INIT = 0 - -class gap_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False, fimages=[]): - super(gap_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - self.fimages = fimages - self.start_cores = False - self.boot_mode = None - self.stopped = False - - def reset(self, jtag_boot=True): - self.get_cable().jtag_reset(True) - self.get_cable().jtag_reset(False) - self.get_cable().chip_reset(True) - if jtag_boot: - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) - self.get_cable().chip_reset(False) - return 0 - - def set_boot_mode(self, boot_mode, reset=True): - if self.verbose: - print ("Notifying to boot code new boot mode (mode: %d)" % boot_mode) - if reset: - self.get_cable().chip_reset(True) - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (boot_mode << 1) | 1) - - if reset: - self.get_cable().chip_reset(False) - self.boot_mode = boot_mode - - def wait_available(self): - if self.verbose: - print ("Waiting for target to be available") - - boot_mode = 0 - if self.boot_mode is not None: - boot_mode = (self.boot_mode << 1) | 1 - - # Loop until we see bit 0 becoming 1, this will indicate that the - # target is ready to accept bridge requests - while True: - reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, boot_mode) - - rt_req = (reg_value >> 1) & 0x7 - - if rt_req == 4 or rt_req == 1: - break - - if self.verbose: - print ("Target is available") - - - def wait_ready(self, boot_mode=None): - if boot_mode is None: - boot_mode = self.boot_mode - - if boot_mode is None: - print ('Can not wait for boot code if the boot mode is unknown') - return -1 - - if self.verbose: - print ("Waiting for notification from boot code") - while True: - reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (boot_mode << 1) | 1) - - if reg_value == CONFREG_BOOT_WAIT: - break - print ("Received for notification from boot code") - - return 0 - - def stop(self): - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - self.set_boot_mode(BOOT_MODE_JTAG) - - # Now wait until the boot code tells us we can load the code - if self.wait_ready() != 0: - return -1 - - # Stall the FC - self.write(0x1B300000, 4, [0, 0, 1, 0]) - - self.stopped = True - - return 0 - - def pause_for_conf(self): - self.set_boot_mode(4) - if self.wait_ready() != 0: - return -1 - return 0 - - def resume_after_conf(self, boot_mode): - self.set_boot_mode(5, reset=False) - while True: - reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (5 << 1) | 1) - if (reg_value & 1) == 0: - break - - self.set_boot_mode(boot_mode, reset=False) - - return 0 - - def clear(self): - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, 0) - self.boot_mode = None - - - def load_jtag(self, binaries): - - if self.verbose: - print ('Loading binary through jtag') - - if self.stop() != 0: - return -1 - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - # Be careful to set the new PC only after the code is loaded as the prefetch - # buffer is immediately fetching instructions and would get wrong instructions - self.write(0x1B302000, 4, [0x80, 0x00, 0x00, 0x1c]) - - self.start_cores = True - - return 0 - - - def start(self): - - if self.start_cores: - print ('Starting execution') - - # Unstall the FC so that it starts fetching instructions from the loaded binary - self.write(0x1B300000, 4, [0, 0, 0, 0]) - - return 0 - - - def load_jtag_hyper(self, binaries): - - if self.verbose: - print ('Loading binary through jtag_hyper') - - # Reset the chip and tell him we want to load from hyper - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - if self.verbose: - print ("Notifying to boot code that we are doing a JTAG boot from hyperflash") - self.get_cable().chip_reset(True) - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG_HYPER) - self.get_cable().chip_reset(False) - - return 0 - - - def flash(self, fimages): - MAX_BUFF_SIZE = (350*1024) - f_path = fimages[0] - addrHeader = self._get_binary_symbol_addr('flasherHeader') - addrImgRdy = addrHeader - addrFlasherRdy = addrHeader + 4 - addrFlashAddr = addrHeader + 8 - addrIterTime = addrHeader + 12 - addrBufSize = addrHeader + 16 - # open the file in read binary mode - f_img = open(f_path, 'rb') - f_size = os.path.getsize(f_path) - lastSize = f_size % MAX_BUFF_SIZE; - if(lastSize): - n_iter = f_size // MAX_BUFF_SIZE + 1; - else: - n_iter = f_size // MAX_BUFF_SIZE - - flasher_ready = self.read_32(addrFlasherRdy) - while(flasher_ready == 0): - flasher_ready = self.read_32(addrFlasherRdy) - flasher_ready = 0; - addrBuffer = self.read_32((addrHeader+20)) - indexAddr = 0 - self.write_32(addrFlashAddr, 0) - self.write_32(addrIterTime, n_iter) - for i in range(n_iter): - if (lastSize and i == (n_iter-1)): - buff_data = f_img.read(lastSize) - self.write(addrBuffer, lastSize, buff_data) - self.write_32(addrBufSize, ((lastSize + 3) & ~3)) - else: - buff_data = f_img.read(MAX_BUFF_SIZE) - self.write(addrBuffer, MAX_BUFF_SIZE, buff_data) - self.write_32(addrBufSize, MAX_BUFF_SIZE) - self.write_32(addrImgRdy, 1) - self.write_32(addrFlasherRdy, 0) - if (i!=(n_iter-1)): - flasher_ready = self.read_32(addrFlasherRdy) - while(flasher_ready == 0): - flasher_ready = self.read_32(addrFlasherRdy) - f_img.close() - return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/gap_rev1.py b/tools/pulp-debug-bridge/python/bridge/chips/gap_rev1.py deleted file mode 100644 index d09c7e8a..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/gap_rev1.py +++ /dev/null @@ -1,237 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import time - -JTAG_SOC_AXIREG = 4 - -JTAG_SOC_CONFREG = 7 -JTAG_SOC_CONFREG_WIDTH = 4 - -BOOT_MODE_JTAG = 1 -BOOT_MODE_JTAG_HYPER = 11 -CONFREG_BOOT_WAIT = 1 -CONFREG_PGM_LOADED = 1 -CONFREG_INIT = 0 - -class gap_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False, fimages=[]): - super(gap_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - self.fimages = fimages - self.start_cores = False - self.boot_mode = None - self.stopped = False - - def reset(self, jtag_boot=True): - self.get_cable().jtag_reset(True) - self.get_cable().jtag_reset(False) - self.get_cable().chip_reset(True) - if jtag_boot: - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) - self.get_cable().chip_reset(False) - return 0 - - def set_boot_mode(self, boot_mode, reset=True): - if self.verbose: - print ("Notifying to boot code new boot mode (mode: %d)" % boot_mode) - if reset: - self.get_cable().chip_reset(True) - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (boot_mode << 1) | 1) - - if reset: - self.get_cable().chip_reset(False) - self.boot_mode = boot_mode - - def wait_available(self): - if self.verbose: - print ("Waiting for target to be available") - - boot_mode = 0 - if self.boot_mode is not None: - boot_mode = (self.boot_mode << 1) | 1 - - # Loop until we see bit 0 becoming 1, this will indicate that the - # target is ready to accept bridge requests - while True: - reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, boot_mode) - - rt_req = (reg_value >> 1) & 0x7 - - if rt_req == 4 or rt_req == 1: - break - - if self.verbose: - print ("Target is available") - - - def wait_ready(self, boot_mode=None): - if boot_mode is None: - boot_mode = self.boot_mode - - if boot_mode is None: - print ('Can not wait for boot code if the boot mode is unknown') - return -1 - - if self.verbose: - print ("Waiting for notification from boot code") - while True: - reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (boot_mode << 1) | 1) - - if reg_value == CONFREG_BOOT_WAIT: - break - print ("Received for notification from boot code") - - return 0 - - def stop(self): - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - self.set_boot_mode(BOOT_MODE_JTAG) - - # Now wait until the boot code tells us we can load the code - if self.wait_ready() != 0: - return -1 - - # Stall the FC - self.write(0x1B300000, 4, [0, 0, 1, 0]) - - self.stopped = True - - return 0 - - def pause_for_conf(self): - self.set_boot_mode(4) - if self.wait_ready() != 0: - return -1 - return 0 - - def resume_after_conf(self, boot_mode): - self.set_boot_mode(5, reset=False) - while True: - reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (5 << 1) | 1) - if (reg_value & 1) == 0: - break - - self.set_boot_mode(boot_mode, reset=False) - - return 0 - - def clear(self): - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, 0) - self.boot_mode = None - - - def load_jtag(self, binaries): - - if self.verbose: - print ('Loading binary through jtag') - - if self.stop() != 0: - return -1 - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - # Be careful to set the new PC only after the code is loaded as the prefetch - # buffer is immediately fetching instructions and would get wrong instructions - self.write(0x1B302000, 4, [0x80, 0x00, 0x00, 0x1c]) - - self.start_cores = True - - return 0 - - - def start(self): - - if self.start_cores: - print ('Starting execution') - - # Unstall the FC so that it starts fetching instructions from the loaded binary - self.write(0x1B300000, 4, [0, 0, 0, 0]) - - return 0 - - - def load_jtag_hyper(self, binaries): - - if self.verbose: - print ('Loading binary through jtag_hyper') - - # Reset the chip and tell him we want to load from hyper - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - if self.verbose: - print ("Notifying to boot code that we are doing a JTAG boot from hyperflash") - self.get_cable().chip_reset(True) - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG_HYPER) - self.get_cable().chip_reset(False) - - return 0 - - - def flash(self, fimages): - MAX_BUFF_SIZE = (350*1024) - f_path = fimages[0] - addrHeader = self._get_binary_symbol_addr('flasherHeader') - addrImgRdy = addrHeader - addrFlasherRdy = addrHeader + 4 - addrFlashAddr = addrHeader + 8 - addrIterTime = addrHeader + 12 - addrBufSize = addrHeader + 16 - # open the file in read binary mode - f_img = open(f_path, 'rb') - f_size = os.path.getsize(f_path) - lastSize = f_size % MAX_BUFF_SIZE; - if(lastSize): - n_iter = f_size // MAX_BUFF_SIZE + 1; - else: - n_iter = f_size // MAX_BUFF_SIZE - - flasher_ready = self.read_32(addrFlasherRdy) - while(flasher_ready == 0): - flasher_ready = self.read_32(addrFlasherRdy) - flasher_ready = 0; - addrBuffer = self.read_32((addrHeader+20)) - indexAddr = 0 - self.write_32(addrFlashAddr, 0) - self.write_32(addrIterTime, n_iter) - for i in range(n_iter): - if (lastSize and i == (n_iter-1)): - buff_data = f_img.read(lastSize) - self.write(addrBuffer, lastSize, buff_data) - self.write_32(addrBufSize, ((lastSize + 3) & ~3)) - else: - buff_data = f_img.read(MAX_BUFF_SIZE) - self.write(addrBuffer, MAX_BUFF_SIZE, buff_data) - self.write_32(addrBufSize, MAX_BUFF_SIZE) - self.write_32(addrImgRdy, 1) - self.write_32(addrFlasherRdy, 0) - if (i!=(n_iter-1)): - flasher_ready = self.read_32(addrFlasherRdy) - while(flasher_ready == 0): - flasher_ready = self.read_32(addrFlasherRdy) - f_img.close() - return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/pulp.py b/tools/pulp-debug-bridge/python/bridge/chips/pulp.py deleted file mode 100644 index 46aa79c8..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/pulp.py +++ /dev/null @@ -1,85 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import time - -JTAG_RISCV_IRLEN = 5 -JTAG_RISCV_BYPASS = 0x1f - -JTAG_SOC_CONFREG_ID = 6 -JTAG_SOC_CONFREG = (JTAG_SOC_CONFREG_ID << 0) | (JTAG_RISCV_BYPASS << 4) -JTAG_SOC_CONFREG_WIDTH = 8 + 1 -JTAG_SOC_IRLEN = 4 - -JTAG_IRLEN = JTAG_SOC_IRLEN + JTAG_RISCV_IRLEN - -class pulp_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False): - - super(pulp_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - def load_jtag(self, binaries): - - if self.verbose: - print ('Loading binary through jtag') - - #if self.stop(): - # return -1 - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - return 0 - - - def start(self): - # First stall the core - self.write_dmi(0x10, 0x00000001) # DMACTIVE - - self.write_dmi(0x10, 0x03E00001) # HART SEL - self.write_dmi(0x10, 0x83E00001) # HALT REQ - - # Wait until it is halted - while True: - status = self.read_dmi(0x11) - - if ((status >> 9) & 1) == 1: - break - - # Set PC - self.write_dmi(0x04, 0x1c008080) # PC into DATA0 - self.write_dmi(0x17, 0x00230000 | 0x7b1) # Abstract cmd to set DPC - - # Resume the core - self.write_dmi(0x10, 0x43E00001) - - return 0 - - def write_dmi(self, reg, value): - self.write_reg_int(reg, value, 4, 1) # DMACTIVE - - - - def read_dmi(self, reg): - return self.read_reg_int(reg, 4, 1) # DMACTIVE diff --git a/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo.py b/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo.py deleted file mode 100644 index 1ebe5fa9..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo.py +++ /dev/null @@ -1,85 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import time - -JTAG_RISCV_IRLEN = 5 -JTAG_RISCV_BYPASS = 0x1f - -JTAG_SOC_CONFREG_ID = 6 -JTAG_SOC_CONFREG = (JTAG_SOC_CONFREG_ID << 0) | (JTAG_RISCV_BYPASS << 4) -JTAG_SOC_CONFREG_WIDTH = 8 + 1 -JTAG_SOC_IRLEN = 4 - -JTAG_IRLEN = JTAG_SOC_IRLEN + JTAG_RISCV_IRLEN - -class pulpissimo_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False): - - super(pulpissimo_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - def load_jtag(self, binaries): - - if self.verbose: - print ('Loading binary through jtag') - - #if self.stop(): - # return -1 - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - return 0 - - - def start(self): - # First stall the core - self.write_dmi(0x10, 0x00000001) # DMACTIVE - - self.write_dmi(0x10, 0x03E00001) # HART SEL - self.write_dmi(0x10, 0x83E00001) # HALT REQ - - # Wait until it is halted - while True: - status = self.read_dmi(0x11) - - if ((status >> 9) & 1) == 1: - break - - # Set PC - self.write_dmi(0x04, 0x1c008080) # PC into DATA0 - self.write_dmi(0x17, 0x00230000 | 0x7b1) # Abstract cmd to set DPC - - # Resume the core - self.write_dmi(0x10, 0x43E00001) - - return 0 - - def write_dmi(self, reg, value): - self.write_reg_int(reg, value, 4, 1) # DMACTIVE - - - - def read_dmi(self, reg): - return self.read_reg_int(reg, 4, 1) # DMACTIVE diff --git a/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo_v2.py b/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo_v2.py deleted file mode 100644 index ab77c564..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo_v2.py +++ /dev/null @@ -1,108 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import time - -JTAG_SOC_AXIREG = 4 - -JTAG_SOC_CONFREG = 7 -JTAG_SOC_CONFREG_WIDTH = 9 - -BOOT_MODE_JTAG = 1 -BOOT_MODE_JTAG_HYPER = 11 -CONFREG_BOOT_WAIT = 1 -CONFREG_PGM_LOADED = 1 -CONFREG_INIT = 0 - -class pulpissimo_v2_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False, fimages=[]): - super(pulpissimo_v2_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - self.fimages = fimages - self.start_cores = False - - def stop(self): - - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - if self.verbose: - print ("Notifying to boot code that we are doing a JTAG boot") - self.get_cable().jtag_reset(True) - self.get_cable().jtag_reset(False) - self.get_cable().chip_reset(True) - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) - self.get_cable().chip_reset(False) - - - # Now wait until the boot code tells us we can load the code - if self.verbose: - print ("Waiting for notification from boot code") - while True: - reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) - if reg_value == CONFREG_BOOT_WAIT: - break - print ("Received for notification from boot code") - - # Stall the FC - self.write(0x1A110000, 4, [0, 0, 1, 0]) - - print ("Stopped core") - - return 0 - - def reset(self): - self.stop() - return 0 - - - def load_jtag(self, binaries): - - if self.verbose: - print ('Loading binary through jtag') - - if self.stop() != 0: - return -1 - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - # Be careful to set the new PC only after the code is loaded as the prefetch - # buffer is immediately fetching instructions and would get wrong instructions - self.write(0x1A112000, 4, [0x80, 0x80, 0x00, 0x1c]) - - self.start_cores = True - - return 0 - - - def start(self): - - if self.start_cores: - print ('Starting execution') - - # Unstall the FC so that it starts fetching instructions from the loaded binary - self.write(0x1A110000, 4, [0, 0, 0, 0]) - - return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/usoc_v1.py b/tools/pulp-debug-bridge/python/bridge/chips/usoc_v1.py deleted file mode 100644 index ec062f75..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/usoc_v1.py +++ /dev/null @@ -1,90 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import time - -class usoc_v1_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False): - - super(usoc_v1_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - self.start_cores = False - - - def load_jtag(self, binaries): - - if self.verbose: - print ('Loading binary through jtag') - - if self.stop(): - return -1 - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - # Be careful to set the new PC only after the code is loaded as the prefetch - # buffer is immediately fetching instructions and would get wrong instructions - self.write(0x1A112000, 4, [0x80, 0x80, 0x00, 0x1c]) - - self.start_cores = True - - return 0 - - - def start(self): - - if self.start_cores: - # Unstall the FC so that it starts fetching instructions from the loaded binary - self.write(0x1A110000, 4, [0, 0, 0, 0]) - - return 0 - - def stop(self): - - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - if self.verbose: - print ("Stalling the FC") - - self.get_cable().chip_reset(True) - self.get_cable().chip_reset(False) - - # Stall the FC as when the reset is released it just tries to load from flash - while True: - # on usoc_v1, the core will start only after a while when the reset - # is released, so the first accesses we do may not have any effect. - # As a aworkaround we have to try stopping it several time. - # Another issue on RTL simulation is that the core is not - # functional anymore if we let him execute a branch with X - # which happens if we let it run too long before we stop it. - # Due to that we cannot read the stall status before we stop it. - for i in range(0, 10): - self.write(0x1A110000, 4, [0, 0, 1, 0]) - - value = self.read_32(0x1A110000) - if (value >> 16) & 1 == 1: - break - - return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/vega.py b/tools/pulp-debug-bridge/python/bridge/chips/vega.py deleted file mode 100644 index 5a1e977d..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/vega.py +++ /dev/null @@ -1,182 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import time - -JTAG_RISCV_IRLEN = 5 -JTAG_RISCV_BYPASS = 0x1f - -JTAG_SOC_CONFREG_ID = 6 -JTAG_SOC_CONFREG = (JTAG_SOC_CONFREG_ID << 5) | (JTAG_RISCV_BYPASS << 0) -JTAG_SOC_CONFREG_WIDTH = 8 + 1 -JTAG_SOC_IRLEN = 4 - -JTAG_IRLEN = JTAG_SOC_IRLEN + JTAG_RISCV_IRLEN - -class vega_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False): - - super(vega_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - self.start_cores = False - self.first_reset = True - self.boot_mode = None - - - def reset(self, stop=True): - - - - - if self.first_reset: - # The first time, we need to wait enough time to let the voltage - # regulator converge - self.get_cable().chip_reset(True, 5000000) - self.first_reset = False - - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - - # Use bootsel pad to tell boot code to stop - if stop: - self.get_cable().chip_config(1) - - # Due to voltage convergence and so on we need to wait - # 200ms when the reset is low - #self.get_cable().chip_reset(True, 200000000) - self.get_cable().chip_reset(True, 100000000) - # It also takes some time before the JTAG is ready - self.get_cable().chip_reset(False, 4000000) - - #self.get_cable().jtag_reset(True) - self.get_cable().jtag_reset(False) - - return 0 - - - def wait_eoc(self): - - while True: - value = self.read_32(0x1a1040a0) - - if (value >> 31) == 1: - return value & 0x7fffffff - - time.sleep(0.1) - - - def jtag_hyper_boot(self): - - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, ((((2 << 0) | (1<<3)) << 1) | 1) << 1, JTAG_IRLEN) - - - def jtag_mram_boot(self): - - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, ((((2 << 0) | (2<<3)) << 1) | 1) << 1, JTAG_IRLEN) - - - def jtag_spim_boot(self): - - self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, ((((2 << 0) | (0<<3)) << 1) | 1) << 1, JTAG_IRLEN) - - - def load_jtag(self, binaries): - - if self.verbose: - print ('Loading binary through jtag') - - #if self.stop(): - # return -1 - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - return 0 - - - def start(self): - # First stall the core - self.write_dmi(0x10, 0x00000001) # DMACTIVE - - self.write_dmi(0x10, 0x03E00001) # HART SEL - self.write_dmi(0x10, 0x83E00001) # HALT REQ - - # Wait until it is halted - while True: - status = self.read_dmi(0x11) - - if ((status >> 9) & 1) == 1: - break - - # Set PC - self.write_dmi(0x04, 0x1c008080) # PC into DATA0 - self.write_dmi(0x17, 0x00230000 | 0x7b1) # Abstract cmd to set DPC - - # Resume the core - self.write_dmi(0x10, 0x43E00001) - - return 0 - - - - def clear(self): - self.get_cable().chip_config(0) - - - - def wait_available(self): - - boot_mode = 0 - if self.boot_mode is not None: - boot_mode = (self.boot_mode << 1) | 1 - - # Loop until we see bit 0 becoming 1, this will indicate that the - # target is ready to accept bridge requests - while True: - reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, boot_mode, JTAG_IRLEN) >> 1 - - rt_req = (reg_value >> 1) & 0x7 - - if rt_req == 4 or rt_req == 1: - break - - if self.verbose: - print ("Target is available") - - - - def write_dmi(self, reg, value): - self.write_reg_int(reg, value, 4, 0) # DMACTIVE - - - - def read_dmi(self, reg): - return self.read_reg_int(reg, 4, 0) # DMACTIVE - - - - def stop(self): - - return 0 \ No newline at end of file diff --git a/tools/pulp-debug-bridge/python/bridge/chips/vivosoc3.py b/tools/pulp-debug-bridge/python/bridge/chips/vivosoc3.py deleted file mode 100644 index 935db4a8..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/vivosoc3.py +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * - -class vivosoc3_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False): - - super(vivosoc3_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - - def reset(self, stop=True): - - self.get_cable().chip_config(1) - - self.get_cable().chip_reset(True, 1000000) - - self.get_cable().chip_reset(False) - - return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/wolfe.py b/tools/pulp-debug-bridge/python/bridge/chips/wolfe.py deleted file mode 100644 index c8c9fc5f..00000000 --- a/tools/pulp-debug-bridge/python/bridge/chips/wolfe.py +++ /dev/null @@ -1,90 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import time - -class wolfe_debug_bridge(debug_bridge): - - def __init__(self, config, binaries=[], verbose=False): - - super(wolfe_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) - - self.start_cores = False - - - def load_jtag(self, binaries): - - if self.verbose: - print ('Loading binary through jtag') - - if self.stop(): - return -1 - - # Load the binary through jtag - if self.verbose: - print ("Loading binaries") - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - # Be careful to set the new PC only after the code is loaded as the prefetch - # buffer is immediately fetching instructions and would get wrong instructions - self.write(0x1A112000, 4, [0x80, 0x80, 0x00, 0x1c]) - - self.start_cores = True - - return 0 - - - def start(self): - - if self.start_cores: - # Unstall the FC so that it starts fetching instructions from the loaded binary - self.write(0x1A110000, 4, [0, 0, 0, 0]) - - return 0 - - def stop(self): - - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - if self.verbose: - print ("Stalling the FC") - - self.get_cable().chip_reset(True) - self.get_cable().chip_reset(False) - - # Stall the FC as when the reset is released it just tries to load from flash - while True: - # on Wolfe, the core will start only after a while when the reset - # is released, so the first accesses we do may not have any effect. - # As a aworkaround we have to try stopping it several time. - # Another issue on RTL simulation is that the core is not - # functional anymore if we let him execute a branch with X - # which happens if we let it run too long before we stop it. - # Due to that we cannot read the stall status before we stop it. - for i in range(0, 10): - self.write(0x1A110000, 4, [0, 0, 1, 0]) - - value = self.read_32(0x1A110000) - if (value >> 16) & 1 == 1: - break - - return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/debug_bridge.py b/tools/pulp-debug-bridge/python/bridge/debug_bridge.py deleted file mode 100644 index e4c4918d..00000000 --- a/tools/pulp-debug-bridge/python/bridge/debug_bridge.py +++ /dev/null @@ -1,66 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -from bridge.default_debug_bridge import * -import bridge.chips.gap as gap -import bridge.chips.gap_rev1 as gap_rev1 -import bridge.chips.gap8_revc as gap8_revc -import bridge.chips.wolfe as wolfe -import bridge.chips.usoc_v1 as usoc_v1 -import bridge.chips.vega as vega -import bridge.chips.arnold as arnold -import bridge.chips.fulmine as fulmine -import bridge.chips.pulpissimo as pulpissimo -import bridge.chips.pulp as pulp -import bridge.chips.vivosoc3 as vivosoc3 - - -def get_bridge(config, binaries=[], verbose=False): - - chip_config = config.get('**/board/chip') - if chip_config is None: - raise Exception('Wrong JSON configuration, do not contain any chip information') - - chip = config.get('**/board/chip').get('name').get() - - if chip == 'gap': - bridge_class = gap.gap_debug_bridge - elif chip == 'gap_rev1': - bridge_class = gap_rev1.gap_debug_bridge - elif chip == 'gap8_revc': - bridge_class = gap8_revc.gap_debug_bridge - elif chip == 'fulmine' or chip == 'vivosoc2' or chip == 'vivosoc2_1': - bridge_class = fulmine.fulmine_debug_bridge - elif chip == 'wolfe': - bridge_class = wolfe.wolfe_debug_bridge - elif chip == 'usoc_v1': - bridge_class = usoc_v1.usoc_v1_debug_bridge - elif chip == 'vega': - bridge_class = vega.vega_debug_bridge - elif chip == 'arnold': - bridge_class = arnold.arnold_debug_bridge - elif chip == 'pulp': - bridge_class = pulp.pulp_debug_bridge - elif chip == 'pulpissimo': - bridge_class = pulpissimo.pulpissimo_debug_bridge - elif chip == 'vivosoc3': - bridge_class = vivosoc3.vivosoc3_debug_bridge - else: - bridge_class = debug_bridge - - return bridge_class(config=config, binaries=binaries, verbose=verbose) diff --git a/tools/pulp-debug-bridge/python/bridge/default_debug_bridge.py b/tools/pulp-debug-bridge/python/bridge/default_debug_bridge.py deleted file mode 100644 index 64b0b4e2..00000000 --- a/tools/pulp-debug-bridge/python/bridge/default_debug_bridge.py +++ /dev/null @@ -1,576 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -import ctypes -import os -import os.path -import json_tools as js -from elftools.elf.elffile import ELFFile -import time - - -class Ctype_cable(object): - - def __init__(self, module, config, system_config): - - self.module = module - self.gdb_handle = None - - # Register entry points with appropriate arguments - self.module.cable_new.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.module.cable_new.restype = ctypes.c_void_p - - self.module.cable_write.argtypes = \ - [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_char_p] - - self.module.cable_read.argtypes = \ - [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_char_p] - - self.module.cable_reg_write.argtypes = \ - [ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_int] - - self.module.cable_reg_read.argtypes = \ - [ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p] - - self.module.chip_reset.argtypes = \ - [ctypes.c_void_p, ctypes.c_bool, ctypes.c_int] - - self.module.chip_config.argtypes = \ - [ctypes.c_void_p, ctypes.c_int] - - self.module.jtag_reset.argtypes = \ - [ctypes.c_void_p, ctypes.c_bool] - - self.module.jtag_soft_reset.argtypes = \ - [ctypes.c_void_p] - - self.module.cable_jtag_set_reg.argtypes = \ - [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_int] - self.module.cable_jtag_set_reg.restype = ctypes.c_bool - - self.module.cable_jtag_get_reg.argtypes = \ - [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.c_int] - self.module.cable_jtag_get_reg.restype = ctypes.c_bool - - self.module.cable_lock.argtypes = \ - [ctypes.c_void_p] - - self.module.cable_unlock.argtypes = \ - [ctypes.c_void_p] - - self.module.bridge_get_error.argtypes = [] - self.module.bridge_get_error.restype = ctypes.c_char_p - - self.module.bridge_init.argtypes = [ctypes.c_char_p, ctypes.c_int] - - self.module.gdb_server_open.argtypes = [ctypes.c_void_p, ctypes.c_int] - self.module.gdb_server_open.restype = ctypes.c_void_p - - self.module.gdb_server_close.argtypes = [ctypes.c_void_p, ctypes.c_int] - - config_string = None - - if config is not None: - config_string = config.dump_to_string().encode('utf-8') - - self.instance = self.module.cable_new(config_string, system_config.dump_to_string().encode('utf-8')) - - if self.instance == None: - raise Exception('Failed to initialize cable with error: ' + self.module.bridge_get_error().decode('utf-8')) - - def get_instance(self): - return self.instance - - def write(self, addr, size, buffer): - data = (ctypes.c_char * size).from_buffer(bytearray(buffer)) - self.module.cable_write(self.instance, addr, size, data) - - def reg_write(self, addr, size, buffer, device=-1): - data = (ctypes.c_char * size).from_buffer(bytearray(buffer)) - self.module.cable_reg_write(self.instance, addr, data, device) - - def read(self, addr, size): - data = (ctypes.c_char * size)() - self.module.cable_read(self.instance, addr, size, data) - - result = [] - for elem in data: - result.append(elem) - - return result - - def reg_read(self, addr, size, device=-1): - data = (ctypes.c_char * size)() - self.module.cable_reg_read(self.instance, addr, data, device) - - result = [] - for elem in data: - result.append(elem) - - return result - - def chip_reset(self, value, duration=1000000): - self.module.chip_reset(self.instance, value, duration) - - def chip_config(self, value): - self.module.chip_config(self.instance, value) - - def jtag_reset(self, value): - self.module.jtag_reset(self.instance, value) - - def jtag_soft_reset(self): - self.module.jtag_soft_reset(self.instance) - - def jtag_set_reg(self, reg, width, value, ir_len=-1): - self.module.cable_jtag_set_reg(self.instance, reg, width, value, ir_len) - - def jtag_get_reg(self, reg, width, value, ir_len=-1): - out_value = ctypes.c_int() - self.module.cable_jtag_get_reg(self.instance, reg, width, ctypes.byref(out_value), value, ir_len) - return out_value.value - - def lock(self): - self.module.cable_lock(self.instance) - - def unlock(self): - self.module.cable_unlock(self.instance) - - - - -class debug_bridge(object): - - def __init__(self, config, binaries=[], verbose=False): - self.config = config - self.cable = None - self.cable_name = config.get_child_str('**/debug_bridge/cable/type') - if self.cable_name is None: - self.cable_name = 'ftdi' - self.binaries = binaries - self.reqloop_handle = None - self.verbose = verbose - self.gdb_handle = None - self.cable_config = config.get('**/debug_bridge/cable') - - - - # Load the library which provides generic services through - # python / C++ bindings - lib_path=os.path.join('libpulpdebugbridge.so') - self.module = ctypes.CDLL(lib_path) - - self.module.bridge_reqloop_open.argtypes = [ctypes.c_void_p, ctypes.c_uint] - self.module.bridge_reqloop_open.restype = ctypes.c_void_p - - self.module.bridge_reqloop_buffer_alloc.argtypes = [ctypes.c_void_p, ctypes.c_int] - self.module.bridge_reqloop_buffer_alloc.restype = ctypes.c_uint - - self.module.bridge_reqloop_buffer_free.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_int] - - self.module.bridge_reqloop_efuse_access.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_uint] - - self.module.bridge_reqloop_eeprom_access.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint] - self.module.bridge_reqloop_eeprom_access.restype = ctypes.c_int - - self.module.bridge_reqloop_flash_access.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint] - self.module.bridge_reqloop_flash_access.restype = ctypes.c_int - - self.module.bridge_reqloop_flash_erase.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_int] - self.module.bridge_reqloop_flash_erase.restype = ctypes.c_int - - self.module.bridge_reqloop_flash_erase_sector.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint] - self.module.bridge_reqloop_flash_erase_sector.restype = ctypes.c_int - - self.module.bridge_reqloop_flash_erase_chip.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_uint, ctypes.c_uint] - self.module.bridge_reqloop_flash_erase_chip.restype = ctypes.c_int - - self.module.bridge_reqloop_close.argtypes = [ctypes.c_void_p, ctypes.c_int] - - self.module.bridge_init(config.dump_to_string().encode('utf-8'), verbose) - - #self.module.jtag_shift.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_char_p)] - - def __mount_cable(self): - if self.cable_name is None: - raise Exception("Trying to mount cable while no cable was specified") - - if self.cable_name.split('@')[0] in ['ftdi', 'jtag-proxy']: - self.__mount_ctype_cable() - pass - else: - raise Exception('Unknown cable: ' + self.cable_name) - - def __mount_ctype_cable(self): - - self.cable = Ctype_cable( - module = self.module, - config = self.cable_config, - system_config = self.config - ) - - def get_cable(self): - if self.cable is None: - self.__mount_cable() - - return self.cable - - def load_jtag(self, binaries): - return self.load_default(binaries) - - def load_jtag_hyper(self, binaries): - raise Exception('JTAG boot is not supported on this target') - - def load_elf(self, binary): - if self.verbose: - print ('Loading %s' % binary) - - with open(binary, 'rb') as file: - elffile = ELFFile(file) - - for segment in elffile.iter_segments(): - - if segment['p_type'] == 'PT_LOAD': - - data = segment.data() - addr = segment['p_paddr'] - size = len(data) - - if self.verbose: - print ('Loading section (base: 0x%x, size: 0x%x)' % (addr, size)) - - self.write(addr, size, data) - - if segment['p_filesz'] < segment['p_memsz']: - addr = segment['p_paddr'] + segment['p_filesz'] - size = segment['p_memsz'] - segment['p_filesz'] - print ('Init section to 0 (base: 0x%x, size: 0x%x)' % (addr, size)) - self.write( - addr, - size, - [0] * size - ) - - - set_pc_addr_config = self.config.get('**/debug_bridge/set_pc_addr') - - if set_pc_addr_config is not None: - set_pc_offset_config = self.config.get('**/debug_bridge/set_pc_offset') - entry = elffile.header['e_entry'] - - if set_pc_offset_config is not None: - entry += set_pc_offset_config.get_int() - - if self.verbose: - print ('Setting PC (base: 0x%x, value: 0x%x)' % (set_pc_addr_config.get_int(), entry)) - - return self.write_32(set_pc_addr_config.get_int(), entry) - - return 0 - - def load(self, binaries=None): - if binaries is None: - binaries = self.binaries - - mode = self.config.get_child_str('**/debug_bridge/boot-mode') - if mode is None: - mode = 'jtag' - - if mode == 'jtag': - return self.load_jtag(binaries) - elif mode == 'jtag_hyper': - return self.load_jtag_hyper(binaries) - else: - return self.load_default(binaries) - - def load_default(self, binaries): - for binary in binaries: - if self.load_elf(binary=binary): - return 1 - - return 0 - - def start(self): - start_addr_config = self.config.get('**/debug_bridge/start_addr') - if start_addr_config is not None: - if self.verbose: - print ('Starting (base: 0x%x, value: 0x%x)' % (start_addr_config.get_int(), self.config.get('**/debug_bridge/start_value').get_int())) - - self.write_32(start_addr_config.get_int(), self.config.get('**/debug_bridge/start_value').get_int()) - return 0 - - def stop(self): - stop_addr_config = self.config.get('**/debug_bridge/stop_addr') - if stop_addr_config is not None: - self.write_32(stop_addr_config.get_int(), self.config.get('**/debug_bridge/stop_value').get_int()) != 0 - return 0 - - def read(self, addr, size): - return self.get_cable().read(addr, size) - - def write(self, addr, size, buffer): - return self.get_cable().write(addr, size, buffer) - - def write_int(self, addr, value, size): - return self.write(addr, size, value.to_bytes(size, byteorder='little')) - - def write_reg_int(self, addr, value, size, device=-1): - return self.get_cable().reg_write(addr, size, value.to_bytes(size, byteorder='little'), device) - - def write_32(self, addr, value): - return self.write_int(addr, value, 4) - - def write_16(self, addr, value): - return self.write_int(addr, value, 2) - - def write_8(self, addr, value): - return self.write_int(addr, value, 1) - - def read_int(self, addr, size): - byte_array = None - for byte in self.read(addr, size): - if byte_array == None: - byte_array = byte - else: - byte_array += byte - return int.from_bytes(byte_array, byteorder='little') - - def read_reg_int(self, addr, size, device=-1): - byte_array = None - for byte in self.get_cable().reg_read(addr, size, device): - if byte_array == None: - byte_array = byte - else: - byte_array += byte - return int.from_bytes(byte_array, byteorder='little') - - def read_32(self, addr): - return self.read_int(addr, 4) - - def read_16(self, addr): - return self.read_int(addr, 2) - - def read_8(self, addr): - return self.read_int(addr, 1) - - def _get_binary_symbol_addr(self, name, binaries=[]): - - binaries = binaries + self.binaries - - for binary in binaries: - with open(binary, 'rb') as file: - elf = ELFFile(file) - for section in elf.iter_sections(): - if section.header['sh_type'] == 'SHT_SYMTAB': - for symbol in section.iter_symbols(): - if symbol.name == name: - t_section=symbol.entry['st_shndx'] - t_vaddr=symbol.entry['st_value'] - return t_vaddr - return 0 - - def reset(self): - self.get_cable().jtag_reset(True) - self.get_cable().jtag_reset(False) - self.get_cable().chip_reset(True) - self.get_cable().chip_reset(False) - return 0 - - def ioloop(self): - - return 0 - - def reqloop(self, binaries=[]): - - # First get address of the structure used to communicate between - # the bridge and the runtime - addr = self._get_binary_symbol_addr('__rt_debug_struct_ptr', binaries) - if addr == 0: - addr = self._get_binary_symbol_addr('debugStruct_ptr', binaries) - - self.reqloop_handle = self.module.bridge_reqloop_open( - self.get_cable().get_instance(), addr) - - return 0 - - def reqloop_close(self, force=False): - if self.reqloop_handle is not None: - return self.module.bridge_reqloop_close(self.reqloop_handle, force) - return 0 - - - def __flasher_init(self, flasher_init): - chip = self.config.get('**/board/chip').get('name').get() - flasher_name = 'flasher-%s' % chip - flasher_path = os.path.join(os.environ.get('PULP_SDK_INSTALL'), 'bin', flasher_name) - - if flasher_init: - self.stop() - # TODO this breaks boot test using bridge, why this is needed ? - self.load([flasher_path]) - - self.reqloop([flasher_path]) - - if flasher_init: - self.start() - - - - def __flasher_deinit(self): - self.reqloop_close(force=True) - - - - def efuse_access(self, flasher_init, is_write, index, value, mask): - - self.__flasher_init(flasher_init) - - print ('efuse access') - self.module.bridge_reqloop_efuse_access(self.reqloop_handle, is_write, index, value, mask) - print ('efuse access done') - - self.__flasher_deinit() - - - - def __alloc_buffer(self, size): - - return self.module.bridge_reqloop_buffer_alloc(self.reqloop_handle, size) - - - - def eeprom_access(self, flasher_init, itf, cs, is_write,eeprom_addr, filepath): - self.__flasher_init(flasher_init) - - addr = self.__alloc_buffer(1024) - - with open(filepath, 'rb') as file: - while True: - buff = file.read(1024) - if buff: - self.write(addr, len(buff), buff) - if self.module.bridge_reqloop_eeprom_access(self.reqloop_handle, itf, cs, True, eeprom_addr, addr, len(buff)): - return -1 - eeprom_addr += 1024 - else: - break - - self.__flasher_deinit() - - return 0 - - - - def flash_access(self, flasher_init, type, itf, cs, is_write,flash_addr, size, filepath): - self.__flasher_init(flasher_init) - - addr = self.__alloc_buffer(1024) - - if is_write: - with open(filepath, 'rb') as file: - while True: - buff = file.read(1024) - if buff: - self.write(addr, len(buff), buff) - - buff_size = len(buff) - buff_size = (buff_size + 7) & ~0x7 - - if self.module.bridge_reqloop_flash_access(self.reqloop_handle, type, itf, cs, True, flash_addr, addr, buff_size): - return -1 - flash_addr += 1024 - else: - break - else: - with open(filepath, 'wb') as file: - while size > 0: - iter_size = 1024 - if iter_size > size: - iter_size = size - if self.module.bridge_reqloop_flash_access(self.reqloop_handle, type, itf, cs, False, flash_addr, addr, iter_size): - return -1 - buff = self.read(addr, iter_size) - for elem in buff: - file.write(elem) - size -= iter_size - flash_addr += iter_size - - self.__flasher_deinit() - - return 0 - - - - - def flash_erase_sector(self, flasher_init, type, itf, cs, flash_addr): - self.__flasher_init(flasher_init) - - if self.module.bridge_reqloop_flash_erase_sector(self.reqloop_handle, type, itf, cs, flash_addr): - return -1 - - self.__flasher_deinit() - - return 0 - - - - - def flash_erase_chip(self, flasher_init, type, itf, cs): - self.__flasher_init(flasher_init) - - if self.module.bridge_reqloop_flash_erase_chip(self.reqloop_handle, type, itf, cs): - return -1 - - self.__flasher_deinit() - - return 0 - - - def flash_erase(self, flasher_init, type, itf, cs, flash_addr, size): - self.__flasher_init(flasher_init) - - if self.module.bridge_reqloop_flash_erase(self.reqloop_handle, type, itf, cs, flash_addr, size): - return -1 - - self.__flasher_deinit() - - return 0 - - - def flash(self): - raise Exception('Flash is not supported on this target') - - def gdb(self, port): - self.gdb_handle = self.module.gdb_server_open(self.get_cable().get_instance(), port) - return 0 - - def wait(self): - if self.gdb_handle is not None: - self.module.gdb_server_close(self.gdb_handle, 0) - - # The wait function returns in case reqloop has been launched - # as it will check for end of application. - if self.reqloop_handle is not None: - return self.module.bridge_reqloop_close(self.reqloop_handle, 0) - - return 0 - - def lock(self): - self.get_cable().lock() - - def unlock(self): - self.get_cable().unlock() diff --git a/tools/pulp-debug-bridge/python/bridge/jtag.py b/tools/pulp-debug-bridge/python/bridge/jtag.py deleted file mode 100644 index b8ea9511..00000000 --- a/tools/pulp-debug-bridge/python/bridge/jtag.py +++ /dev/null @@ -1,283 +0,0 @@ -# -# Copyright (C) 2018 ETH Zurich and University of Bologna -# -# 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. -# - -# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - -import ctypes -import os -import os.path -from elftools.elf.elffile import ELFFile -import time -import sys -import json_tools as js - -BOOT_MODE_DEFAULT = 0 -BOOT_MODE_JTAG = 1 -BOOT_MODE_ROM_HYPER = 2 - -JTAG_SOC_INSTR_WIDTH = 0x4 -JTAG_SOC_IDCODE = 0x2 -JTAG_SOC_AXIREG = 0x4 -JTAG_SOC_BBMUXREG = 0x5 -JTAG_SOC_CLKGATEREG = 0x6 -JTAG_SOC_CONFREG = 0x7 -JTAG_SOC_TESTMODEREG = 0x8 -JTAG_SOC_BISTREG = 0x9 -JTAG_SOC_BYPASS = 0xf -JTAG_SOC_IDCODE_WIDTH = 32 -JTAG_SOC_AXIREG_WIDTH = 96 -JTAG_SOC_BBMUXREG_WIDTH = 21 -JTAG_SOC_CLKGATEREG_WIDTH = 11 -JTAG_SOC_CONFREG_WIDTH = 4 -JTAG_SOC_TESTMODEREG_WIDTH = 4 -JTAG_SOC_BISTREG_WIDTH = 20 - -JTAG_CLUSTER_INSTR_WIDTH = 4 -JTAG_CLUSTER_IDCODE = 2 -JTAG_CLUSTER_SAMPLE_PRELOAD = 1 -JTAG_CLUSTER_EXTEST = 0 -JTAG_CLUSTER_DEBUG = 8 -JTAG_CLUSTER_MBIST = 9 -JTAG_CLUSTER_BYPASS = 0xf -JTAG_CLUSTER_IDCODE_WIDTH = 32 - -JTAG_IDCODE_WIDTH = JTAG_CLUSTER_IDCODE_WIDTH + JTAG_SOC_IDCODE_WIDTH -JTAG_INSTR_WIDTH = JTAG_CLUSTER_INSTR_WIDTH + JTAG_SOC_INSTR_WIDTH - - -ADV_DBG_AXI4_MODULE = 0x20 -ADV_DBG_CPU_MODULE = 0x21 - -ADV_DBG_AXI4_NOP = 0x0 -ADV_DBG_AXI4_WRITE8 = 0x1 -ADV_DBG_AXI4_WRITE16 = 0x2 -ADV_DBG_AXI4_WRITE32 = 0x3 -ADV_DBG_AXI4_WRITE64 = 0x4 -ADV_DBG_AXI4_READ8 = 0x5 -ADV_DBG_AXI4_READ16 = 0x6 -ADV_DBG_AXI4_READ32 = 0x7 -ADV_DBG_AXI4_READ64 = 0x8 -ADV_DBG_AXI4_WREG = 0x9 -ADV_DBG_AXI4_SELREG = 0xD - -ADV_DBG_CPU_NOP = 0x0 -ADV_DBG_CPU_WRITE = 0x3 -ADV_DBG_CPU_READ = 0x7 -ADV_DBG_CPU_WREG = 0x9 -ADV_DBG_CPU_SELREG = 0xD - -ADV_DBG_CPU_REG_STATUS = 0x0 - -class debug_bridge(object): - """ Debug bridge doc. """ - - def __init__(self, config_path, cable_option=None): - self.config = js.import_config_from_file(config_path) - path = os.path.join(os.environ.get('PULP_SDK_HOME'), 'install', 'ws', 'lib', 'libdebugbridge.so') - self.module = ctypes.CDLL(path) - self.module.bridge_new.argtypes = [ctypes.c_char_p, ctypes.c_char_p] - self.module.jtag_write.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_char_p] - #self.module.jtag_shift.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_char_p)] - with open(config_path, 'r') as config: - self.instance = self.module.bridge_new(config.read().encode('utf-8'), cable_option.encode('utf-8')) - - def reset(self, active): - """ Debug bridge reset. - :param active: Reset target - """ - self.module.reset(self.instance, active) - - def exit(self, status): - self.module.plt_exit(self.instance, status) - - def jtag_write(self, addr, size, buffer): - data = (ctypes.c_char * size).from_buffer(bytearray(buffer)) - self.module.jtag_write(self.instance, addr, size, data) - - def jtag_read(self, addr, size): - data = (ctypes.c_ubyte * size)() - self.module.jtag_read(self.instance, addr, size, data) - - result = [] - for elem in data: - result.append(elem) - - return result - - def jtag_read32(self, addr): - data = self.jtag_read(addr, 4) - return int.from_bytes(bytearray(data), 'little') - - def jtag_reset(self): - self.module.jtag_reset(self.instance) - - def jtag_idle(self): - self.module.jtag_idle(self.instance) - - def jtag_shift_ir(self): - self.module.jtag_shift_ir(self.instance) - - def jtag_shift_dr(self): - self.module.jtag_shift_dr(self.instance) - - def jtag_shift_common(self, width, bufferin, noex): - size = int((width + 7) / 8) - if bufferin is not None: - datain = (ctypes.c_char * size).from_buffer(bytearray(bufferin)) - else: - datain = (ctypes.c_char * size)() - dataout = (ctypes.c_ubyte * size)() - self.module.jtag_shift(self.instance, width, datain, dataout, noex) - - result = [] - for elem in dataout: - result.append(elem) - - return bytearray(result) - - def jtag_shift_noex(self, width, bufferin): - return self.jtag_shift_common(width, bufferin, 1) - - def jtag_shift(self, width, bufferin): - return self.jtag_shift_common(width, bufferin, 0) - - def jtag_set_reg(self, reg, value): - # Set TAP in confreg mode - self.jtag_shift_ir() - self.jtag_shift(JTAG_SOC_INSTR_WIDTH, [reg]) - self.jtag_idle() - - # And push the new value - self.jtag_shift_dr() - self.jtag_shift(9, [value, 0]) - self.jtag_idle() - - def jtag_get_reg(self, reg, value): - # Set TAP in confreg mode - #self.jtag_shift_ir() - #self.jtag_shift(JTAG_SOC_INSTR_WIDTH, [reg]) - #self.jtag_idle() - #print (binascii.hexlify(bytearray(data))) - - # And push the new value - self.jtag_shift_dr() - result = self.jtag_shift(9, [value, 0]) - self.jtag_idle() - - return result - - def jtag_set_dbg_mode_soc(self): - - self.jtag_shift_ir() - self.jtag_shift_noex(JTAG_CLUSTER_INSTR_WIDTH, [JTAG_SOC_BYPASS]) - self.jtag_shift(JTAG_SOC_INSTR_WIDTH, [JTAG_SOC_AXIREG]) - self.jtag_idle() - - def wait_exit(self): - self.jtag_set_dbg_mode_soc() - - while True: - value = self.jtag_read32(0x1a1040a0) - - if (value >> 31) & 1: - return value & 0x7fffffff - - time.sleep(0.5) - - def wait_exit_and_stop(self): - status = self.wait_exit() - self.exit(status) - - def load(self, binary): - - print ('Loading ELF file (path: %s)' % binary) - - self.jtag_set_dbg_mode_soc() - - with open(binary, 'rb') as file: - elffile = ELFFile(file) - - for segment in elffile.iter_segments(): - if segment['p_type'] == 'PT_LOAD': - - data = segment.data() - addr = segment['p_paddr'] - size = len(data) - - print ('Loading section (base: 0x%x, size: 0x%x)' % (addr, size)) - - self.jtag_write(addr, size, data) - - def wait_and_exit(self): - - status = self.wait_exit_and_stop() - sys.exit(status) - - - def rom_boot(self): - - # This is the default mode so just let the core boot and behave in the - # default mode - self.jtag_reset() - self.reset(False) - - - def rom_hyper_boot(self, binary): - - self.jtag_reset() - - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - self.reset(True) - self.jtag_set_reg(JTAG_SOC_CONFREG, BOOT_MODE_ROM_HYPER << 1) - self.reset(False) - - # We don't have anything else to do as it reads the boot mode - # and does everything with the flash - - - def jtag_boot(self, binary): - - self.jtag_reset() - - # Reset the chip and tell him we want to load via jtag - # We keep the reset active until the end so that it sees - # the boot mode as soon as it boots from rom - self.reset(True) - self.jtag_set_reg(JTAG_SOC_CONFREG, BOOT_MODE_JTAG << 1) - self.reset(False) - - # Load the binary through jtag - self.load(binary) - - # And notify the boot code that the binary is ready - self.jtag_set_reg(JTAG_SOC_CONFREG, (BOOT_MODE_JTAG << 1) | 1) - - def exec_config(self): - - mode = self.config.get('runner/boot-mode').get() - - if mode == 'rom': - self.rom_boot() - elif mode == 'rom_hyper': - self.rom_hyper_boot() - elif mode == 'jtag': - self.jtag_boot(self.config.get('loader/binaries').get_elem(0).get()) - else: - raise Exception("Unsupported mode: " + mode) - - self.wait_and_exit() diff --git a/tools/pulp-debug-bridge/src/cable.hpp b/tools/pulp-debug-bridge/src/cable.hpp deleted file mode 100644 index 1302ba54..00000000 --- a/tools/pulp-debug-bridge/src/cable.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - */ - -#ifndef __CABLES_CABLE_HPP__ -#define __CABLES_CABLE_HPP__ - -#include "json.hpp" -#include "cables/log.h" - - -class Cable_jtag_itf -{ -public: - - virtual bool bit_inout(char* inbit, char outbit, bool last) { printf ("i am bit_inout virtual fct in cable class\n"); return false; } - - virtual bool stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last) { printf ("i am stream_inout virtual fct in cable class\n"); return false; } - - virtual int flush() { return -1; } - virtual bool jtag_reset(bool active) { printf("JTAG\n"); return false; } - - virtual void device_select(unsigned int i) {} - - bool jtag_soft_reset(); - bool jtag_write_tms(int val); - bool jtag_shift_ir(); - bool jtag_shift_dr(); - bool jtag_idle(); - bool jtag_shift(int width, char *bits); - bool jtag_shift_ir(unsigned int ir, int ir_len=-1); - bool jtag_set_reg(unsigned int reg, int width, unsigned int value, int ir_len=-1); - bool jtag_get_reg(unsigned int reg, int width, unsigned int *out_value, unsigned int value, int ir_len=-1); -}; - - - -class Cable_io_itf -{ -public: - virtual bool access(bool write, unsigned int addr, int size, char* buffer, int device=-1) { return false; } - virtual bool reg_access(bool write, unsigned int addr, char* buffer, int device=-1) { return false; } -}; - - - -class Cable_ctrl_itf -{ -public: - virtual bool chip_reset(bool active, int duration) { return false; } - virtual bool chip_config(uint32_t config) { return false; } -}; - - - -class Cable : public Cable_io_itf, public Cable_jtag_itf, public Cable_ctrl_itf -{ -public: - Cable(js::config *config) : config(config) {} - - virtual bool connect(js::config *config) { return true; } - - virtual void lock() { } - - virtual void unlock() { } - - js::config *get_config() { return this->config; } - -protected: - js::config *config; - -}; - -#endif diff --git a/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.cpp b/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.cpp deleted file mode 100644 index 9bab7129..00000000 --- a/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.cpp +++ /dev/null @@ -1,1423 +0,0 @@ -/* - * 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. - */ - -/* - * Authors: Andreas Traber - */ - -#include -#include -#include -#include -#include -#include - -#include "adv_dbg_itf.hpp" -#ifdef __USE_FTDI__ -#include "cables/ftdi/ftdi.hpp" -#endif - -#define JTAG_SOC_AXIREG 4 - - -Adv_dbg_itf::Adv_dbg_itf(js::config *system_config, js::config *config, Log* log, Cable *m_dev) : Cable(system_config), log(log), m_dev(m_dev), bridge_config(config) -{ - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&mutex, &attr); - - js::config *conf = system_config->get("**/adv_dbg_unit/debug_ir"); - - this->debug_ir = conf != NULL ? conf->get_int() : 0x4; - log->debug("Using debug IR: 0x%x\n", this->debug_ir); - - - conf = system_config->get("**/adv_dbg_unit/retry_count"); - - this->retry_count = conf != NULL ? conf->get_int() : 0; - log->debug("Using retry count: %d\n", this->retry_count); - - - conf = NULL; //system_config->get("**/adv_dbg_unit/check_errors"); - - this->check_errors = conf != NULL ? conf->get_bool() : false; - log->debug("Checking errors: %d\n", this->check_errors); - -} - - - -Adv_dbg_itf::~Adv_dbg_itf() -{ - delete m_dev; -} - - - -bool Adv_dbg_itf::connect() -{ - js::config *jtag_cable_config = NULL; - - access_timeout = bridge_config->get_int("**/access_timeout_us"); - if (access_timeout == 0) - access_timeout = 1000000; - - log->debug ("Using access timeout: %d us\n", access_timeout); - - this->check_cable(); - - m_dev->jtag_reset(true); - m_dev->jtag_reset(false); - - this->jtag_soft_reset(); - - // TODO once the bridge is working on Vega, changed that into a generic - // feature which reads the devices from json file - if (this->config->get("**/chip/name")->get_str() == "vega") - { - this->add_device(5, DEV_PROTOCOL_RISCV); - this->add_device(4, DEV_PROTOCOL_PULP); - } - else if (this->config->get("**/chip/name")->get_str() == "pulpissimo") - { - this->add_device(5, DEV_PROTOCOL_PULP); - this->add_device(5, DEV_PROTOCOL_RISCV); - } - - // now we can work with the chain - if (!jtag_auto_discovery()) { - log->error("Did not find an adv debug unit in the chain, exiting\n"); - return false; - } - - int tap = 0; - if (bridge_config->get("tap")) tap = bridge_config->get("tap")->get_int(); - this->m_jtag_device_default = tap; - - this->device_select(tap); - - return true; -} - - - -bool Adv_dbg_itf::jtag_reset(bool active) -{ - this->check_connection(); - - pthread_mutex_lock(&mutex); - - for (int i=0; i < m_jtag_devices.size(); i++) - { - m_jtag_devices[i].is_in_debug = false; - } - bool result = m_dev->jtag_reset(active); - - pthread_mutex_unlock(&mutex); - - return result; -} - -bool Adv_dbg_itf::check_connection() -{ - if (!this->connected) - { - this->connected = true; - - if (!this->connect()) - return false; - } - - return true; -} - -bool Adv_dbg_itf::check_cable() -{ - if (!this->cable_connected) - { - if (!this->m_dev->connect(this->bridge_config)) - return false; - - this->cable_connected = true; - } - - return true; -} - -bool Adv_dbg_itf::chip_reset(bool active, int duration) -{ - bool result = true; - - if (!this->check_cable()) - return false; - - pthread_mutex_lock(&mutex); - - if (!m_dev->chip_reset(active, duration)) { result = false; goto end; }; - // Wait some time so that we don't do any IO access after that while the chip - // has not finished booting - if (!active) usleep(10000); - -end: - pthread_mutex_unlock(&mutex); - - return result; -} - - - -bool Adv_dbg_itf::chip_config(uint32_t value) -{ - bool result = true; - - if (!this->check_cable()) - return false; - - pthread_mutex_lock(&mutex); - - if (!m_dev->chip_config(value)) { result = false; goto end; }; - -end: - pthread_mutex_unlock(&mutex); - - return result; -} - - - -void Adv_dbg_itf::device_select(unsigned int i) -{ - pthread_mutex_lock(&mutex); - - m_jtag_device_sel = i; - - if (i == m_jtag_devices.size() - 1) - m_tms_on_last = 1; - else - m_tms_on_last = 0; - - pthread_mutex_unlock(&mutex); -} - - -bool Adv_dbg_itf::reg_access(bool write, unsigned int addr, char* buffer, int device) -{ - bool result; - - this->check_connection(); - - pthread_mutex_lock(&mutex); - - if (device != -1) - this->device_select(device); - else if (m_jtag_device_default != m_jtag_device_sel) - this->device_select(m_jtag_device_default); - - jtag_device &dev = m_jtag_devices[m_jtag_device_sel]; - - if (dev.protocol == DEV_PROTOCOL_RISCV) - result = this->reg_access_riscv(write, addr, buffer); - else - result = this->reg_access_pulp(write, addr, buffer); - - pthread_mutex_unlock(&mutex); - - return result; -} - - -bool Adv_dbg_itf::reg_access_riscv(bool write, unsigned int addr, char* buffer) -{ - if (write) - return this->reg_access_write_riscv(write, addr, buffer); - else - return this->reg_access_read_riscv(write, addr, buffer); -} - -bool Adv_dbg_itf::reg_access_read_riscv(bool write, unsigned int addr, char* buffer) -{ - unsigned char buf[8]; - unsigned char recv[8]; - - uint32_t data = *(uint32_t *)buffer; - - jtag_soft_reset(); - jtag_set_selected_ir(0x11); - - buf[5] = (addr >> 6) & 0x1; - buf[4] = ((addr & 0x3f) << 2) | ((data >> 30) & 0x7); - buf[3] = (data >> 22) & 0xff; - buf[2] = (data >> 14) & 0xff; - buf[1] = (data >> 6) & 0xff; - buf[0] = ((data & 0x1f) << 2) | (0x1 << 0); - - m_dev->jtag_write_tms(1); - m_dev->jtag_write_tms(0); - m_dev->jtag_write_tms(0); - - jtag_pad_before(); - - if (!m_dev->stream_inout((char *)recv, (char *)buf, 41, m_tms_on_last)) { - log->warning("ft2232: failed to write opcode stream to device\n"); - return false; - } - - jtag_pad_after(!m_tms_on_last); - - // Go to UPDATE DR - m_dev->jtag_write_tms(1); - for (int i=0; i<50; i++) - m_dev->jtag_write_tms(0); - - // Go to CAPTURE DR - m_dev->jtag_write_tms(1); - m_dev->jtag_write_tms(0); - - // Go to IDLE - m_dev->jtag_write_tms(1); - m_dev->jtag_write_tms(1); - m_dev->jtag_write_tms(0); - - // SHIFT DR - m_dev->jtag_write_tms(1); - m_dev->jtag_write_tms(0); - m_dev->jtag_write_tms(0); - - jtag_pad_before(); - - buf[0] = 0; - - if (!m_dev->stream_inout((char *)recv, (char *)buf, 41, m_tms_on_last)) { - log->warning("ft2232: failed to write opcode stream to device\n"); - return false; - } - - jtag_pad_after(!m_tms_on_last); - - - // IDLE - m_dev->jtag_write_tms(1); // exit 1 DR - m_dev->jtag_write_tms(0); // run test idle - m_dev->jtag_write_tms(0); // run test idle - - *(uint32_t *)buffer = (recv[0] >> 2) | (recv[1] << 6) | (recv[2] << 14) | (recv[3] << 22) | ((recv[4] & 0x3) << 30); - - return false; -} - -bool Adv_dbg_itf::reg_access_write_riscv(bool write, unsigned int addr, char* buffer) -{ - char buf[8]; - char recv[8]; - - uint32_t data = *(uint32_t *)buffer; - - jtag_soft_reset(); - jtag_set_selected_ir(0x11); - - buf[5] = (addr >> 5) & 0x3; - buf[4] = ((addr & 0x1f) << 3) | ((data >> 29) & 0x7); - buf[3] = (data >> 21) & 0xff; - buf[2] = (data >> 13) & 0xff; - buf[1] = (data >> 5) & 0xff; - buf[0] = ((data & 0x1f) << 3) | (0x2 << 1); - - m_dev->jtag_write_tms(1); - m_dev->jtag_write_tms(0); - m_dev->jtag_write_tms(0); - - jtag_pad_before(); - - if (!m_dev->stream_inout(recv, buf, 42, m_tms_on_last)) { - log->warning("ft2232: failed to write opcode stream to device\n"); - return false; - } - - jtag_pad_after(!m_tms_on_last); - - m_dev->jtag_write_tms(1); // exit 1 DR - m_dev->jtag_write_tms(0); // run test idle - m_dev->jtag_write_tms(0); // run test idle - - return false; -} - - -bool Adv_dbg_itf::reg_access_pulp(bool write, unsigned int addr, char* buffer) -{ - return false; -} - - - -bool Adv_dbg_itf::access(bool wr, unsigned int addr, int size, char* buffer, int device) -{ - bool result; - - this->check_connection(); - - pthread_mutex_lock(&mutex); - - if (device != -1) - this->device_select(device); - else if (m_jtag_device_default != m_jtag_device_sel) - this->device_select(m_jtag_device_default); - - jtag_debug(); - - if (wr) - result = write(addr, size, buffer); - else - result = read(addr, size, buffer); - - pthread_mutex_unlock(&mutex); - - return result; -} - - - -bool Adv_dbg_itf::write(unsigned int _addr, int _size, char* _buffer) -{ - int count = 0; - do - { - unsigned int addr = _addr; - int size = _size; - char *buffer = _buffer; - - bool retval = true; - - if (addr & 0x1 && size >= 1) { - retval = retval && write_internal(8, addr, 1, buffer); - size -= 1; - buffer += 1; - addr += 1; - } - - if (addr & 0x2 && size >= 2) { - retval = retval && write_internal(16, addr, 2, buffer); - size -= 2; - buffer += 2; - addr += 2; - } - - #if 0 - - // TODO add 64 bits support and make sure it is not used on pulp targets - - if (addr & 0x4 && size >= 4) { - retval = retval && write_internal(AXI_WRITE32, addr, 4, buffer); - size -= 4; - buffer += 4; - addr += 4; - } - - if (size >= 8) { - int local_size = size & (~0x7); - retval = retval && write_internal(AXI_WRITE64, addr, local_size, buffer); - size -= local_size; - buffer += local_size; - addr += local_size; - } - - #else - - if (size >= 4) { - int local_size = size & (~0x3); - - while (local_size) - { - int iter_size = local_size; - if (iter_size > 1024) iter_size = 1024; - - retval = retval && write_internal(32, addr, iter_size, buffer); - local_size -= iter_size; - size -= iter_size; - buffer += iter_size; - addr += iter_size; - } - } - #endif - - if (size >= 2) { - retval = retval && write_internal(16, addr, 2, buffer); - size -= 2; - buffer += 2; - addr += 2; - } - - if (size >= 1) { - retval = retval && write_internal(8, addr, 1, buffer); - size -= 1; - buffer += 1; - addr += 1; - } - - if (this->check_errors) - { - uint32_t error_addr; - bool error = false; - retval = retval && read_error_reg(&error_addr, &error); - - if (error) { - log->debug("advdbg reports: Failed to write to addr %X\n", error_addr); - count++; - continue; - } - } - - return retval; - } while (count < this->retry_count); - - return false; -} - - - -bool Adv_dbg_itf::read(unsigned int _addr, int _size, char* _buffer) -{ - int count = 0; - do - { - unsigned int addr = _addr; - int size = _size; - char *buffer = _buffer; - - bool retval = true; - - if (addr & 0x1 && size >= 1) { - retval = retval && read_internal(8, addr, 1, buffer); - size -= 1; - buffer += 1; - addr += 1; - } - - if (addr & 0x2 && size >= 2) { - retval = retval && read_internal(16, addr, 2, buffer); - size -= 2; - buffer += 2; - addr += 2; - } - - #if 0 - - // TODO add 64 bits support and make sure it is not used on pulp targets - - if (addr & 0x4 && size >= 4) { - retval = retval && read_internal(AXI_READ32, addr, 4, buffer); - size -= 4; - buffer += 4; - addr += 4; - } - - if (size >= 8) { - int local_size = size & (~0x7); - retval = retval && read_internal(AXI_READ64, addr, local_size, buffer); - size -= local_size; - buffer += local_size; - addr += local_size; - } - - #else - - if (size >= 4) { - int local_size = size & (~0x3); - - while (local_size) - { - int iter_size = local_size; - if (iter_size > 2048) iter_size = 2048 ; - - retval = retval && read_internal(32, addr, iter_size, buffer); - local_size -= iter_size; - size -= iter_size; - buffer += iter_size; - addr += iter_size; - } - } - - #endif - - if (size >= 2) { - retval = retval && read_internal(16, addr, 2, buffer); - size -= 2; - buffer += 2; - addr += 2; - } - - if (size >= 1) { - retval = retval && read_internal(8, addr, 1, buffer); - size -= 1; - buffer += 1; - addr += 1; - } - - if (this->check_errors) - { - uint32_t error_addr; - bool error = false; - retval = retval && read_error_reg(&error_addr, &error); - - if (error) { - log->debug("advdbg reports: Failed to read from addr %X\n", error_addr); - count++; - continue; - } - } - - return retval; - } while (count < retry_count); - - return false; -} - - -bool Adv_dbg_itf::write_internal(int bitwidth, unsigned int addr, int size, char* buffer) -{ - if (m_jtag_device_sel >= m_jtag_devices.size()) - return false; - - jtag_device &dev = m_jtag_devices[m_jtag_device_sel]; - - if (dev.protocol == DEV_PROTOCOL_RISCV) - return this->write_internal_riscv(bitwidth, addr, size, buffer); - else - return this->write_internal_pulp(bitwidth, addr, size, buffer); -} - -bool Adv_dbg_itf::write_internal_riscv(int bitwidth, unsigned int addr, int size, char* buffer) -{ - return false; -} - -bool Adv_dbg_itf::write_internal_pulp(int bitwidth, unsigned int addr, int size, char* buffer) -{ - char buf[8]; - char recv[1]; - uint32_t crc; - ADBG_OPCODES opcode; - - switch(bitwidth) { - case 8: - opcode = AXI_WRITE8; - break; - - case 16: - opcode = AXI_WRITE16; - break; - - case 32: - opcode = AXI_WRITE32; - break; - - case 64: - opcode = AXI_WRITE64; - break; - - default: - log->warning("Invalid bitwidth: %d\n", bitwidth); - return false; - } - - if (size % (bitwidth/8) != 0) { - log->warning("Size is not aligned to selected bitwidth\n"); - return false; - } - - jtag_axi_select(); - - jtag_pad_before(); - - // build burst setup command - // bit 52: 0 - // bit 51-48: opcode - // bit 47:16: address - // bit 15:0: count - buf[6] = opcode; - buf[5] = addr >> 24; - buf[4] = addr >> 16; - buf[3] = addr >> 8; - buf[2] = addr >> 0; - buf[1] = ( size / (bitwidth / 8) ) >> 8; - buf[0] = ( size / (bitwidth / 8) ) >> 0; - - if (!m_dev->stream_inout(NULL, buf, 53, m_tms_on_last)) { - log->warning("ft2232: failed to write opcode stream to device\n"); - return false; - } - - jtag_pad_after(!m_tms_on_last); - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(0); // capture DR - m_dev->jtag_write_tms(0); // shift DR - - jtag_pad_before(); - - // send start bit - if (!m_dev->bit_inout(NULL, 0x1, false)) { - log->warning("ft2232: failed to write start bit to device\n"); - return false; - } - - // send data - if (!m_dev->stream_inout(NULL, buffer, size * 8, false)) { - log->warning("ft2232: failed to write data to device\n"); - return false; - } - - // send crc - crc = crc_compute(0xFFFFFFFF, buffer, size * 8); - buf[3] = crc >> 24; - buf[2] = crc >> 16; - buf[1] = crc >> 8; - buf[0] = crc >> 0; - if (!m_dev->stream_inout(NULL, buf, 32, false)) { - log->warning("ft2232: failed to write crc to device\n"); - return false; - } - - // push crc all the way in before we can expect to receive the match bit - jtag_pad_after(false); - - // receive match bit - recv[0] = 0; - - if (!m_dev->stream_inout(recv, buf, 2, false)) { - log->warning("ft2232: failed to read match bit from device\n"); - return false; - } - - - m_dev->jtag_write_tms(1); // exit 1 DR - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(0); // run test idle - - if (((recv[0] >> m_jtag_device_sel) & 0x1) != 0x1) { - // TODO some pulp targets like fulmine does not support CRC. - log->warning("ft2232: Match bit was not set. Transfer has probably failed; addr %08X, size %d\n", addr, size); - return false; - } - - return true; -} - - - -bool Adv_dbg_itf::read_internal(int bitwidth, unsigned int addr, int size, char* buffer) -{ - if (m_jtag_device_sel >= m_jtag_devices.size()) - return false; - - jtag_device &dev = m_jtag_devices[m_jtag_device_sel]; - - if (dev.protocol == DEV_PROTOCOL_RISCV) - return this->read_internal_riscv(bitwidth, addr, size, buffer); - else - return this->read_internal_pulp(bitwidth, addr, size, buffer); -} - -bool Adv_dbg_itf::read_internal_riscv(int bitwidth, unsigned int addr, int size, char* buffer) -{ - return false; -} - -bool Adv_dbg_itf::read_internal_pulp(int bitwidth, unsigned int addr, int size, char* buffer) -{ - char recv[size]; - char buf[size < 8 ? 8 : size]; - int nwords; - uint32_t crc = 0xFFFFFFFF; - ADBG_OPCODES opcode; - int bytewidth = bitwidth / 8; - - switch(bitwidth) { - case 8: - opcode = AXI_READ8; - break; - - case 16: - opcode = AXI_READ16; - break; - - case 32: - opcode = AXI_READ32; - break; - - case 64: - opcode = AXI_READ64; - break; - - default: - log->warning("Invalid bitwidth: %d\n", bitwidth); - return false; - } - - int factor = 1; - - // Increase the word size in case we have big burst with good multiple - if (size >= 256 && size % 256 == 0) - { - bytewidth = 256; - factor = bytewidth / 4; - } - - if (size % bytewidth != 0) { - log->warning("Size is not aligned to selected bitwidth\n"); - return false; - } - - nwords = size / bytewidth; - - jtag_axi_select(); - - jtag_pad_before(); - - // build burst setup command - // bit 52: 0 - // bit 51-48: opcode - // bit 47:16: address - // bit 15:0: count - buf[6] = opcode; - buf[5] = addr >> 24; - buf[4] = addr >> 16; - buf[3] = addr >> 8; - buf[2] = addr >> 0; - buf[1] = (nwords * factor) >> 8; - buf[0] = (nwords * factor) >> 0; - - if (!m_dev->stream_inout(NULL, buf, 53, m_tms_on_last)) { - log->warning("ft2232: failed to write opcode stream to device\n"); - return false; - } - - jtag_pad_after(!m_tms_on_last); - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(0); // capture DR - m_dev->jtag_write_tms(0); // shift DR - - // no need to do padding here, we just wait for a 1 - - // wait for a '1' from the AXI module - struct timeval start, now; - int retval = gettimeofday(&start, NULL); - assert(retval == 0); - - while (true) { - buf[0] = 0x0; - if (!m_dev->bit_inout(buf, 0x0, false)) { - log->warning("ft2232: failed to read start bit from device\n"); - return false; - } - - if (buf[0] & 0x1) - break; - - retval = gettimeofday(&now, NULL); - assert(retval == 0); - unsigned long usec_elapsed = (now.tv_sec - start.tv_sec) * 1000000 + (now.tv_usec - start.tv_usec); - - if (usec_elapsed > access_timeout) { - log->warning("ft2232: did not get a start bit from the AXI module in 1s\n"); - return false; - } - } - - // make sure we only send 0's to the device - memset(buf, 0, size); - - // receive data - crc = 0xFFFFFFFF; - for (int i = 0; i < nwords; i++) { - if (!m_dev->stream_inout(recv, buf, bytewidth*8, false)) { - log->warning("ft2232: failed to receive data from device\n"); - return false; - } - - memcpy(buffer, recv, bytewidth); - crc = crc_compute(crc, recv, bytewidth*8); - - buffer = buffer + bytewidth; - } - - // receive crc - if (!m_dev->stream_inout(recv, buf, 33, m_tms_on_last)) { - log->warning("ft2232: failed to read crc from device\n"); - return false; - } - - jtag_pad_after(!m_tms_on_last); - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(0); // run test idle - - uint32_t recv_crc; - memcpy(&recv_crc, recv, 4); - if (crc != recv_crc) { - log->warning ("ft2232: crc from adv dbg unit did not match for request to addr %08X\n", addr); - log->debug ("ft2232: Got %08X, expected %08X\n", recv_crc, crc); - return false; - } - - return true; -} - - - -bool Adv_dbg_itf::read_error_reg(uint32_t *addr, bool *error) -{ - char buf[5]; - assert (addr != NULL); - assert (error != NULL); - - jtag_axi_select(); - - jtag_pad_before(); - - // build internal register select - // 63 = 0 (module_cmd) - // 62:59 = 1101 (operation_in) - // 58 = 0 (does not matter) - // => 6 bits - buf[0] = 0x1A; - - if (!m_dev->stream_inout(NULL, buf, 6, m_tms_on_last)) { - log->warning("ft2232: failed to write internal register select to device\n"); - return false; - } - - jtag_pad_after(!m_tms_on_last); - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(0); // capture DR - m_dev->jtag_write_tms(0); // shift DR - - jtag_pad_before(); - - memset(buf, 0, 5); - - if (!m_dev->stream_inout(buf, buf, 33, m_tms_on_last)) { - log->warning("ft2232: failed to read AXI error register\n"); - return false; - } - - jtag_pad_after(!m_tms_on_last); - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(0); // run test idle - - *error = buf[0] & 0x1; - - // shift buffer by 1 bit to the right to get the address - for (int i = 0; i < 4; i++) - buf[i] = buf[i] >> 1 | ((buf[i+1] & 0x1) << 31); - memcpy(addr, buf, 4); - - if (*error) { - // there was an error, so we have to clear the internal error register on - // the adv dbg unit - return clear_error_reg(); - } - - return true; -} - - - -bool Adv_dbg_itf::clear_error_reg() -{ - char buf[5]; - - // IMPORTANT: since there is a bug in hardware, if the next access is a - // write, it will fail (or at least look like it failed). The reason for this - // is that the error register kind of keeps hanging in an error state. To - // mitigate this problem, we perform one valid dummy access - //read_internal(AXI_READ8, 0x10000000, 1, buf); - - jtag_axi_select(); - - jtag_pad_before(); - -#if 0 - // build internal register select - // 63 = 0 (module_cmd) - // 62:59 = 1001 (operation_in) - // 58 = 0 (does not matter) - // => 6 bits - // 55:46 = 1 (all 1) - // => +16 bits - buf[2] = 0x09; - buf[1] = 0x00; - buf[0] = 0x08; - - if (!m_dev->stream_inout(NULL, buf, 5+1+15, m_tms_on_last)) { - log->warning("ft2232: failed to write internal register write to device\n"); - return false; - } -#else - buf[0] = (0x9 << 1) | 1; - - if (!m_dev->stream_inout(NULL, buf, 5+1, m_tms_on_last)) { - log->warning("ft2232: failed to write internal register write to device\n"); - return false; - } -#endif - - jtag_pad_after(!m_tms_on_last); - - m_dev->jtag_write_tms(1); // update DR - - return true; -} - - - -bool Adv_dbg_itf::jtag_set_selected_ir(char ir) -{ - char buf[1]; - bool is_last; - unsigned int i; - - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(1); // select IR scan - m_dev->jtag_write_tms(0); // capture IR - m_dev->jtag_write_tms(0); // shift IR - - // put devices into bypass mode if they are before our device - for (i = 0; i < m_jtag_devices.size(); i++) { - - // set our device into debug, the rest into bypass - if(i == m_jtag_device_sel) - buf[0] = ir; - else - buf[0] = 0xFF; - - is_last = (m_jtag_devices.size() - 1 == i); - - if (!m_dev->stream_inout(NULL, buf, m_jtag_devices[i].ir_len, is_last)) { - log->warning ("ft2232: failed to set IR to bypass\n"); - return false; - } - } - - m_dev->jtag_write_tms(1); // update IR - m_dev->jtag_write_tms(0); // run test idle - - return true; -} - - - -bool Adv_dbg_itf::jtag_debug() -{ - if (m_jtag_device_sel >= m_jtag_devices.size()) - return false; - - jtag_device &dev = m_jtag_devices[m_jtag_device_sel]; - if (!dev.is_in_debug) - { - jtag_soft_reset(); - dev.is_in_debug = jtag_set_selected_ir(this->debug_ir); - return dev.is_in_debug; - } -} - - - -bool Adv_dbg_itf::jtag_dmi_select() -{ - char buf[1]; - buf[0] = 0x11; - - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(0); // capture DR scan - m_dev->jtag_write_tms(0); // shift DR - - jtag_pad_before(); - - if (!m_dev->stream_inout(NULL, buf, 6, m_tms_on_last)) { - log->warning("ft2232: failed to write AXI select to device\n"); - return false; - } - - jtag_pad_after(!m_tms_on_last); - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(0); // capture DR scan - m_dev->jtag_write_tms(0); // shift DR - - m_dev->flush(); - - return true; -} - - - -bool Adv_dbg_itf::jtag_axi_select() -{ - char buf[1]; - buf[0] = 0x20; - - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(0); // capture DR scan - m_dev->jtag_write_tms(0); // shift DR - - jtag_pad_before(); - - if (!m_dev->stream_inout(NULL, buf, 6, m_tms_on_last)) { - log->warning("ft2232: failed to write AXI select to device\n"); - return false; - } - - jtag_pad_after(!m_tms_on_last); - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(0); // capture DR scan - m_dev->jtag_write_tms(0); // shift DR - - m_dev->flush(); - - return true; -} - -#define MAX_CHAIN_LEN 128 - -void Adv_dbg_itf::add_device(int ir_len, int protocol) -{ - jtag_device device; - device.index = m_jtag_devices.size(); - device.is_in_debug = false; - device.ir_len = ir_len; - device.protocol = protocol; - m_jtag_devices.push_back(device); -} - -bool Adv_dbg_itf::jtag_auto_discovery() -{ - char recv_buf[MAX_CHAIN_LEN/8]; - char send_buf[MAX_CHAIN_LEN/8]; - int ir_len; - int dr_len; - - if (m_jtag_devices.size() == 0) - { - ir_len = ir_len_detect(); - - jtag_soft_reset(); - dr_len = dr_len_detect(); - - log->debug("JTAG IR len is %d, DR len is %d\n", ir_len, dr_len); - - std::string chip = this->config->get("**/chip/name")->get_str(); - - if (chip != "wolfe" && chip != "usoc_v1") - { - if (dr_len <= 0 || ir_len <= 0) { - log->error("JTAG sanity check failed\n"); - return false; - } - } - else - { - // On wolfe, due to a HW bug, it is not possible to guess the dr len - dr_len = 32; - } - - // since we now know how long the chain is, we can shift out the IDs - jtag_soft_reset(); - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(0); // capture DR scan - m_dev->jtag_write_tms(0); // shift DR scan - - m_dev->stream_inout(recv_buf, send_buf, dr_len, true); - - m_jtag_devices.clear(); - - // build 32-bit chunks for the IDs - for(int i = 0; i < dr_len/32; i++) { - jtag_device device; - device.id = (recv_buf[i*4 + 3] & 0xFF) << 24; - device.id |= (recv_buf[i*4 + 2] & 0xFF) << 16; - device.id |= (recv_buf[i*4 + 1] & 0xFF) << 8; - device.id |= (recv_buf[i*4 + 0] & 0xFF) << 0; - device.index = i; - device.is_in_debug = false; - device.protocol = DEV_PROTOCOL_PULP; - // TODO the detacted IR length is wrong when there are several taps - device.ir_len = 4; - - log->debug("Device %d ID: %08X\n", i, device.id); - - m_jtag_devices.push_back(device); - } - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(0); // run test idle - } - else - { - for(int i = 0; i < m_jtag_devices.size(); i++) - { - jtag_device &device = m_jtag_devices[i]; - - this->device_select(i); - - dr_len = dr_len_detect(); - - jtag_soft_reset(); - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(0); // capture DR scan - m_dev->jtag_write_tms(0); // shift DR scan - - m_dev->stream_inout(recv_buf, send_buf, dr_len, true); - - device.id = (recv_buf[i*4 + 3] & 0xFF) << 24; - device.id |= (recv_buf[i*4 + 2] & 0xFF) << 16; - device.id |= (recv_buf[i*4 + 1] & 0xFF) << 8; - device.id |= (recv_buf[i*4 + 0] & 0xFF) << 0; - - log->debug("Device %d ID: %08X\n", i, device.id); - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(0); // run test idle - - } - } - - return true; -} - - - -int Adv_dbg_itf::ir_len_detect() -{ - char recv_buf[MAX_CHAIN_LEN/8]; - char send_buf[MAX_CHAIN_LEN/8]; - int jtag_chainlen = -1; - - jtag_soft_reset(); - - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(1); // select IR scan - m_dev->jtag_write_tms(0); // capture IR - m_dev->jtag_write_tms(0); // shift IR - - // first poison with 0 - memset(send_buf, 0, MAX_CHAIN_LEN/8); - m_dev->stream_inout(recv_buf, send_buf, MAX_CHAIN_LEN, false); - - if (recv_buf[MAX_CHAIN_LEN/8-1] != 0) - log->warning("ft2232: Did not receive 0 that we sent, JTAG chain might be faulty\n"); - - // now we send all 1's and see how long it takes for them to get back to us - memset(send_buf, 0xFF, MAX_CHAIN_LEN/8); - - m_dev->stream_inout(recv_buf, send_buf, MAX_CHAIN_LEN, true); - - for(int i = 0; i < MAX_CHAIN_LEN; i++) { - if ((recv_buf[i/8] >> (i%8)) & 0x1) { - jtag_chainlen = i; - break; - } - } - log->debug("ft2232: jtag_chainlen = %d\n", jtag_chainlen); - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(0); // run test idle - - return jtag_chainlen; -} - - - -int Adv_dbg_itf::dr_len_detect() -{ - char recv_buf[MAX_CHAIN_LEN/8]; - char send_buf[MAX_CHAIN_LEN/8]; - int jtag_chainlen = -1; - - m_dev->jtag_write_tms(1); // select DR scan - m_dev->jtag_write_tms(0); // capture DR scan - m_dev->jtag_write_tms(0); // shift DR scan - - // first poison with 0 - memset(send_buf, 0, MAX_CHAIN_LEN/8); - - m_dev->stream_inout(recv_buf, send_buf, MAX_CHAIN_LEN, false); - - if (recv_buf[MAX_CHAIN_LEN/8-1] != 0) - log->warning("ft2232: Did not receive 0 that we sent, JTAG chain might be faulty\n"); - - // now we send all 1's and see how long it takes for them to get back to us - memset(send_buf, 0xFF, MAX_CHAIN_LEN/8); - - m_dev->stream_inout(recv_buf, send_buf, MAX_CHAIN_LEN, true); - - for(int i = 0; i < MAX_CHAIN_LEN; i++) { - if ((recv_buf[i/8] >> (i%8)) & 0x1) { - jtag_chainlen = i; - break; - } - } - - m_dev->jtag_write_tms(1); // update DR - m_dev->jtag_write_tms(0); // run test idle - - return jtag_chainlen; -} - - -bool Adv_dbg_itf::jtag_soft_reset() -{ - pthread_mutex_lock(&mutex); - - for (int i=0; i < m_jtag_devices.size(); i++) - { - m_jtag_devices[i].is_in_debug = false; - } - bool result = m_dev->jtag_soft_reset(); - - pthread_mutex_unlock(&mutex); - - return result; -} - - - - -#define ADBG_CRC_POLY 0xedb88320 - -uint32_t Adv_dbg_itf::crc_compute(uint32_t crc, char* data_in, int length_bits) -{ - uint32_t d, c; - - for(int i = 0; i < length_bits; i++) - { - d = ((data_in[i / 8] >> (i % 8)) & 0x1) ? 0xffffffff : 0; - c = (crc & 0x1) ? 0xffffffff : 0; - crc = crc >> 1; - crc = crc ^ ((d ^ c) & ADBG_CRC_POLY); - } - - return crc; -} - - - -bool Adv_dbg_itf::jtag_pad_before() -{ - if (m_jtag_device_sel == 0) { - return true; - } - - unsigned int pad_bits = m_jtag_device_sel; - unsigned int pad_bytes = (pad_bits + 7)/8; - char* buffer = (char*)malloc(pad_bytes); - memset(buffer, 0, pad_bytes); - - if (!m_dev->stream_inout(NULL, buffer, pad_bits, false)) { - log->warning("ft2232: failed to pad chain before our selected device\n"); - return false; - } - - free(buffer); - - return true; -} - - - -bool Adv_dbg_itf::jtag_pad_after(bool tms) -{ - if (m_jtag_device_sel == m_jtag_devices.size() - 1) { - return true; - } - - unsigned int pad_bits = m_jtag_devices.size() - m_jtag_device_sel - 1; - unsigned int pad_bytes = (pad_bits + 7)/8; - char* buffer = (char*)malloc(pad_bytes); - memset(buffer, 0, pad_bytes); - - if (!m_dev->stream_inout(NULL, buffer, pad_bits, tms)) { - log->warning("ft2232: failed to pad chain before our selected device\n"); - return false; - } - - free(buffer); - - return true; -} - -bool Adv_dbg_itf::bit_inout(char* inbit, char outbit, bool last) -{ - this->check_cable(); - - pthread_mutex_lock(&mutex); - - // Invalidate debug mode in case the caller is sending raw bitstream as it might - // change the IR - if (m_jtag_device_sel < m_jtag_devices.size()) - m_jtag_devices[m_jtag_device_sel].is_in_debug = false; - bool result = m_dev->bit_inout(inbit, outbit, last); - - pthread_mutex_unlock(&mutex); - - return result; -} - -bool Adv_dbg_itf::stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last) -{ - this->check_cable(); - - pthread_mutex_lock(&mutex); - - // Invalidate debug mode in case the caller is sending raw bitstream as it might - // change the IR - if (m_jtag_device_sel < m_jtag_devices.size()) - m_jtag_devices[m_jtag_device_sel].is_in_debug = false; - bool result = m_dev->stream_inout(instream, outstream, n_bits, last); - - pthread_mutex_unlock(&mutex); - - return result; -} - -int Adv_dbg_itf::flush() -{ - pthread_mutex_lock(&mutex); - - bool result = m_dev->flush(); - - pthread_mutex_unlock(&mutex); - - return result; -} - -void Adv_dbg_itf::lock() -{ - pthread_mutex_lock(&mutex); -} - -void Adv_dbg_itf::unlock() -{ - pthread_mutex_unlock(&mutex); -} diff --git a/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.hpp b/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.hpp deleted file mode 100644 index d1b95912..00000000 --- a/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.hpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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. - */ - -/* - * Authors: Andreas Traber - */ - -#ifndef __CABLES_ADV_DBG_ITF_ADV_DBG_ITF_HPP__ -#define __CABLES_ADV_DBG_ITF_ADV_DBG_ITF_HPP__ - -#include -#include - -#include "cables/log.h" -#include "cable.hpp" - -class FTDILL; - -#define DEV_PROTOCOL_PULP 0 -#define DEV_PROTOCOL_RISCV 1 - -struct jtag_device { - uint32_t id; - unsigned int index; - unsigned int ir_len; - bool is_in_debug; - int protocol; -}; - -class Adv_dbg_itf : public Cable { - public: - Adv_dbg_itf(js::config *system_config, js::config *config, Log* log, Cable *itf); - virtual ~Adv_dbg_itf(); - - bool connect(); - void lock(); - void unlock(); - - - bool access(bool write, unsigned int addr, int size, char* buffer, int device=-1); - bool reg_access(bool write, unsigned int addr, char* buffer, int device=-1); - - void device_select(unsigned int i); - - void add_device(int ir_len, int protocol=DEV_PROTOCOL_PULP); - - - bool chip_reset(bool active, int duration); - bool chip_config(uint32_t config); - bool jtag_reset(bool active); - bool jtag_soft_reset(); - - - bool bit_inout(char* inbit, char outbit, bool last); - bool stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last); - - int flush(); - - - private: - enum ADBG_OPCODES { - AXI_WRITE8 = 0x1, - AXI_WRITE16 = 0x2, - AXI_WRITE32 = 0x3, - AXI_WRITE64 = 0x4, - AXI_READ8 = 0x5, - AXI_READ16 = 0x6, - AXI_READ32 = 0x7, - AXI_READ64 = 0x8 - }; - - Cable* m_dev; - Log* log; - - bool cable_connected = false; - bool connected = false; - - pthread_mutex_t mutex; - unsigned int debug_ir; - int retry_count; - int check_errors; - int access_timeout; - - - std::vector m_jtag_devices; - unsigned int m_jtag_device_sel = 0; - unsigned int m_jtag_device_default = 0; - - bool m_tms_on_last; - - js::config *bridge_config; - - bool reg_access_pulp(bool write, unsigned int addr, char* buffer); - - bool reg_access_riscv(bool write, unsigned int addr, char* buffer); - bool reg_access_read_riscv(bool write, unsigned int addr, char* buffer); - bool reg_access_write_riscv(bool write, unsigned int addr, char* buffer); - - bool write(unsigned int addr, int size, char* buffer); - bool write_internal(int bitwidth, unsigned int addr, int size, char* buffer); - bool write_internal_pulp(int bitwidth, unsigned int addr, int size, char* buffer); - bool write_internal_riscv(int bitwidth, unsigned int addr, int size, char* buffer); - - bool read(unsigned int addr, int size, char* buffer); - bool read_internal(int bitwidth, unsigned int addr, int size, char* buffer); - bool read_internal_pulp(int bitwidth, unsigned int addr, int size, char* buffer); - bool read_internal_riscv(int bitwidth, unsigned int addr, int size, char* buffer); - - bool read_error_reg(uint32_t *addr, bool *error); - bool clear_error_reg(); - - bool jtag_pad_before(); - bool jtag_pad_after(bool tms); - - bool jtag_debug(); - - int ir_len_detect(); - int dr_len_detect(); - - bool jtag_axi_select(); - bool jtag_auto_discovery(); - bool jtag_idle(); - bool jtag_set_selected_ir(char ir); - - bool check_connection(); - bool check_cable(); - - bool jtag_dmi_select(); - - uint32_t crc_compute(uint32_t crc, char* data_in, int length_bits); -}; - -#endif diff --git a/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.cpp b/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.cpp deleted file mode 100644 index e82d446d..00000000 --- a/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.cpp +++ /dev/null @@ -1,941 +0,0 @@ -/* - * 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. - */ - -/* - * Authors: Andreas Traber - */ - -/* - * Olimex ARM-USB-OCD, ARM-USB-OCD-H, ARM-USB-TINY and ARM-USB-TINY-H pinout - * - * GPIO Function Comment - * 0 TCK Set as output but do not drive manually - * 1 TDI Set as output but do not drive manually - * 2 TDO Set as input, should not be driven - * 3 TMS Set as output but do not drive manually - * 4 ?? GPIOL0 of FT2232H, set as output - * 5 ?? GPIOL1 of FT2232H, set as input, should not be driven - * 6 ?? GPIOL2 of FT2232H, set as input, should not be driven - * 7 RTCK GPIOL3 of FT2232H, set as input, should not be driven - * 8 TRST*) Set as output - * 9 SRST**) Set as output - * 10 ?? GPIOH2 of FT2232H, set as output - * 11 Red LED Set as output - * 12 ?? GPIOH4 of FT2232H, set as input, should not be driven - * 13 ?? GPIOH5 of FT2232H, set as input, should not be driven - * 14 ?? GPIOH6 of FT2232H, set as input, should not be driven - * 15 ?? GPIOH7 of FT2232H, set as input, should not be driven - * - * *) Pin is driven by Olimex in an active-high way, i.e. 0: driven to GND by Olimex - * 1: driven to VRef by Olimex - * **) Pin is driven by Olimex in an active-low way, i.e. 0: not driven by Olimex -> needs to be driven to high from DUT side (V_reset) - * 1: driven to GND by Olimex - * - * Use command SET_BITS_LOW for GPIO 0 - 7 (byte0: value, byte1: direction) - * Use command SET_BITS_HIGH for GPIO 8 - 15 (byte0: value, byte1: direction) - */ - -#include -#include -#include -#include -#include - -#include "ftdi.hpp" -#include "cables/log.h" - - -#ifndef min -#define min(X,Y) ((X) < (Y) ? (X) : (Y)) -#endif - -//----------------------------------------------------------------------------- - -Ftdi::Ftdi(js::config *config, Log* log, FTDIDeviceID id) : Cable(config), log (log), m_id (id) -{ - if (config->get("**/vendor") != NULL && config->get("**/product") != NULL) - { - m_descriptors.push_back((struct device_desc){(unsigned int)config->get_child_int("**/vendor"), (unsigned int)config->get_child_int("**/product")}); - } - else - { - // add all our known devices to the map - m_descriptors.push_back((struct device_desc){0x15ba, 0x002a}); - m_descriptors.push_back((struct device_desc){0x15ba, 0x002b}); - - m_descriptors.push_back((struct device_desc){0x0403, 0x6010}); // ftdi2232 Gapuino - m_descriptors.push_back((struct device_desc){0x1d6b, 0x0002}); // ftdi2232 Gapuino - } -} - -Ftdi::~Ftdi() -{ - ftdi_usb_close(&m_ftdic); - ftdi_deinit(&m_ftdic); - - if (m_params.send_buf) free(m_params.send_buf); - if (m_params.recv_buf) free(m_params.recv_buf); -} - -bool -Ftdi::connect(js::config *config) -{ - char buf[256]; - std::list dev_desc = m_descriptors; - int error; - bool result; - int err; - const char *description = NULL; - js::config *user_gpios = config->get("user_gpios"); - - if (config && config->get("description") != NULL) - { - description = config->get("description")->get_str().c_str(); - } - - m_params.send_buf_len = FTDX_MAXSEND; - m_params.send_buffered = 0; - m_params.send_buf = (char*)malloc(m_params.send_buf_len); - m_params.recv_buf_len = FTDI_MAXRECV; - m_params.to_recv = 0; - m_params.recv_write_idx = 0; - m_params.recv_read_idx = 0; - m_params.recv_buf = (char*)malloc(m_params.recv_buf_len); - - if (!m_params.send_buf || !m_params.recv_buf) { - log->error("ftdi2232: Can't allocate memory for ftdi context structures\n"); - goto fail; - } - - ftdi_init(&m_ftdic); - - if (config->get("bus") != NULL) - { -#ifdef FTDI_1_4 - if (ftdi_usb_open_bus_addr(&m_ftdic, config->get_child_int("**/bus"), config->get_child_int("**/device"))) - { - error = 1; - } -#else - log->error("Specifying device through USB address is only supported with at least version 1.4 of libftdi\n"); - goto fail; -#endif - } - else - { - //--------------------------------------------------------------------------- - // Device Selection - if (description == NULL) { - std::list dev_available; - struct device_desc dev; - - for (std::list::iterator it = dev_desc.begin(); - it != dev_desc.end(); it++) { - struct ftdi_device_list* devlist; - int n = ftdi_usb_find_all(&m_ftdic, &devlist, it->vid, it->pid); - - if (n > 0) { - for(int i = 0; i < n; ++i) { - if (dev_try_open(it->vid, it->pid, i)) { - log->user("Found ftdi device i:0x%X:0x%X:%d\n", - it->vid, it->pid, i); - dev_available.push_back({.vid = it->vid, .pid = it->pid, .index = (unsigned int)i}); - break; - } - } - } - - ftdi_list_free2(devlist); - } - - if (dev_available.size() == 0) { - log->error("ft2232: Connection failed\n"); - goto fail; - } - - dev = dev_available.front(); - log->user("Connecting to ftdi device i:0x%X:0x%X:%d\n", - dev.vid, dev.pid, dev.index); - error = ftdi_usb_open_desc_index(&m_ftdic, dev.vid, dev.pid, NULL, NULL, dev.index); - } else { - log->user("Connecting to ftdi device %s\n", description); - error = ftdi_usb_open_string(&m_ftdic, description); - } - } - - if (error != 0) { - if (error == -1) log->debug("usb_find_busses() failed\n"); - else if (error == -2) log->debug("usb_find_devices() failed\n"); - else if (error == -3) log->debug("usb device not found\n"); - else if (error == -4) log->debug("unable to open device\n"); - else if (error == -5) log->debug("unable to claim device\n"); - else if (error == -6) log->debug("reset failed\n"); - else if (error == -7) log->debug("set baudrate failed\n"); - else if (error == -8) log->debug("get product description failed\n"); - else if (error == -9) log->debug("get serial number failed\n"); - else if (error == -10) log->debug("unable to close device\n"); - else if (error == -11) log->debug("ftdi context invalid\n"); - else if (error == -12) log->debug("libusb_get_device_list() failed\n"); - else if (error == -13) log->debug("libusb_get_device_descriptor() failed\n"); - - log->warning("ft2232: Connection failed\n"); - - goto fail; - } - - if(!ft2232_mpsse_open()) { - log->error("ft2232: Open MPSSE mode failed\n"); - goto fail; - } - - log->debug("Connected to libftdi driver.\n"); - - //--------------------------------------------------------------------------- - // Setup layout for different devices - - int buf_len; - this->reverse_reset = config->get_child_bool("reverse_reset"); - bits_value = 0; - bits_direction = 0x1b; - - if (user_gpios != NULL) - { - for (auto x:user_gpios->get_elems()) - { - this->user_gpios.push_back(x->get_int()); - set_bit_direction(x->get_int(), 1); - } - } - - if (config->get("system_reset_gpio") != NULL) - { - this->system_reset_gpio = config->get_int("system_reset_gpio"); - set_bit_direction(this->system_reset_gpio, 1); - if (reverse_reset) - bits_value |= 1<system_reset_gpio; - } - - if (config->get("jtag_reset_gpio") != NULL) - { - this->jtag_reset_gpio = config->get_int("jtag_reset_gpio"); - set_bit_direction(this->jtag_reset_gpio, 1); - bits_value |= 1<jtag_reset_gpio; - } - - - buf[0] = SET_BITS_LOW; // Set value & direction of ADBUS lines - buf[1] = bits_value & 0xff; // values - buf[2] = 0x1b; // direction (1 == output) - //buf[3] = 0x8a; // Activate this command to disabled the default divider by 5, otherwise by default we can just go up to 6MHz instead of 30MHz - buf[3] = TCK_DIVISOR; - buf[4] = 0x02; // The divisor has been put to 2 as is not reliable on silicon with less - // than that - buf[5] = 0x00; - buf[6] = SEND_IMMEDIATE; - - buf_len = 7; - - if(ft2232_write(buf, buf_len, 0) != buf_len) { - log->error("ft2232: Initial write failed\n"); - goto fail; - } - - flush(); - - return true; - -fail: - if (m_params.send_buf) free(m_params.send_buf); - if (m_params.recv_buf) free(m_params.recv_buf); - - return false; -} - - -bool Ftdi::chip_config(uint32_t config) -{ - for (auto x:this->user_gpios) - { - this->set_bit_value(x, config & 1); - config >>= 1; - } - -} - -bool Ftdi::chip_reset(bool active, int duration) -{ - if (this->system_reset_gpio != -1) - { - if (this->reverse_reset) - active = !active; - - if (!set_bit_value(this->system_reset_gpio, active)) - return false; - } - - if (duration > 0) - usleep(duration / 1000); - - return true; -} - -int -Ftdi::ft2232_seq_purge(int purge_rx, int purge_tx) { - int ret = 0; - unsigned char buf; - - ret = ftdi_usb_purge_buffers(&m_ftdic); - if (ret < 0) { - log->warning("ft2232: ftdi_usb_purge_buffers() failed\n"); - return -1; - } - - ret = ftdi_read_data(&m_ftdic, &buf, 1); - if (ret < 0) { - log->warning("ft2232: ftdi_read_data() failed\n"); - return -1; - } - - return 0; -} - -int -Ftdi::ft2232_seq_reset() { - if (ftdi_usb_reset(&m_ftdic) < 0) { - log->warning("ft2232: ftdi_usb_reset() failed\n"); - return -1; - } - - if(ft2232_seq_purge(1, 1) < 0) { - log->warning("ft2232: Could not purge\n"); - return -1; - } - - return 0; -} - -int -Ftdi::flush() { - unsigned int xferred; - unsigned int recvd = 0; - - if (m_params.send_buffered == 0) - return 0; - - if ((xferred = ftdi_write_data(&m_ftdic, (uint8_t*)m_params.send_buf, m_params.send_buffered)) < 0) { - log->warning("ft2232: ftdi_write_data() failed\n"); - return -1; - } - - if (xferred < m_params.send_buffered) { - log->warning("Written fewer bytes than requested.\n"); - return -1; - } - - m_params.send_buffered = 0; - - /* now read all scheduled receive bytes */ - if (m_params.to_recv) { - if (m_params.recv_write_idx + m_params.to_recv > m_params.recv_buf_len) { - /* extend receive buffer */ - m_params.recv_buf_len = m_params.recv_write_idx + m_params.to_recv; - if (m_params.recv_buf) - m_params.recv_buf = (char*)realloc(m_params.recv_buf, m_params.recv_buf_len); - } - - assert(m_params.recv_buf != NULL); - - while (recvd == 0) { - recvd = ftdi_read_data(&m_ftdic, (uint8_t*)&(m_params.recv_buf[m_params.recv_write_idx]), m_params.to_recv); - if (recvd < 0) - log->warning("Error from ftdi_read_data()\n"); - } - - if (recvd < m_params.to_recv) - log->warning("Received less bytes than requested.\n"); - - m_params.to_recv -= recvd; - m_params.recv_write_idx += recvd; - } - - return xferred < 0 ? -1 : xferred; -} - -int -Ftdi::ft2232_read(char* buf, int len) { - int cpy_len; - int recvd = 0; - - /* flush send buffer to get all scheduled receive bytes */ - if (flush() < 0) { - log->warning("ft2232: Could not read any data after a flush\n"); - return -1; - } - - if (len == 0) { - log->warning("ft2232: Please don't read 0 bits\n"); - return 0; - } - - /* check for number of remaining bytes in receive buffer */ - cpy_len = m_params.recv_write_idx - m_params.recv_read_idx; - - if (cpy_len > len) - cpy_len = len; - - len -= cpy_len; - - if (cpy_len > 0) { - // get data from the receive buffer - memcpy(buf, &(m_params.recv_buf[m_params.recv_read_idx]), cpy_len); - m_params.recv_read_idx += cpy_len; - - if (m_params.recv_read_idx == m_params.recv_write_idx) - m_params.recv_read_idx = m_params.recv_write_idx = 0; - } - - if (len > 0) { - /* need to get more data directly from the device */ - while (recvd == 0) { - recvd = ftdi_read_data(&m_ftdic, (uint8_t*)&(buf[cpy_len]), len); - if (recvd < 0) - log->warning("ft2232: Error from ftdi_read_data()\n"); - } - } - - return recvd < 0 ? -1 : (cpy_len + len); -} - -int Ftdi::ft2232_write(char *buf, int len, int recv) -{ - int xferred = 0; - - // this write function will try to buffer write data - // buffering will be ceased and a flush triggered in two cases. - - // Case A: max number of scheduled receive bytes will be exceeded - // with this write - // Case B: max number of scheduled send bytes has been reached - if ((m_params.to_recv + recv > FTDI_MAXRECV) || ((m_params.send_buffered > FTDX_MAXSEND) && (m_params.to_recv == 0))) - xferred = flush(); - - if (xferred < 0) { - log->warning("ft2232: Flush before write failed\n"); - return -1; - } - - // now buffer this write - if (m_params.send_buffered + len > m_params.send_buf_len) { - m_params.send_buf_len = m_params.send_buffered + len; - - if (m_params.send_buf) - m_params.send_buf = (char*)realloc(m_params.send_buf, m_params.send_buf_len); - } - - assert(m_params.send_buf); - - memcpy( &(m_params.send_buf[m_params.send_buffered]), buf, len); - m_params.send_buffered += len; - if (recv > 0) - m_params.to_recv += recv; - - if (recv < 0) { - // immediate write requested, so flush the buffered data - xferred = flush(); - } - - return xferred < 0 ? -1 : len; -} - -bool Ftdi::ft2232_mpsse_open() -{ - char buf[3]; - int ret; - - // This sequence might seem weird and containing superfluous stuff. - // However, it's built after the description of JTAG_InitDevice - // Ref. FTCJTAGPG10.pdf - // Intermittent problems will occur when certain steps are skipped. - - ret = ft2232_seq_reset(); - if (ret < 0) - goto fail; - - ret = ft2232_seq_purge(1, 0); - if (ret < 0) - goto fail; - - ret = ftdi_write_data_set_chunksize(&m_ftdic, FTDX_MAXSEND_MPSSE); - if (ret < 0) { - log->warning("ft2232: Got error %s\n", ftdi_get_error_string(&m_ftdic)); - goto fail; - } - - ret = ftdi_read_data_set_chunksize(&m_ftdic, FTDX_MAXSEND_MPSSE); - if (ret < 0) { - log->warning("ft2232: Got error %s\n", ftdi_get_error_string(&m_ftdic)); - goto fail; - } - - /* set a reasonable latency timer value - if this value is too low then the chip will send intermediate result data - in short packets (suboptimal performance) */ - ret = ftdi_set_latency_timer(&m_ftdic, 1); - if (ret < 0) { - log->warning("ft2232: ftdi_set_latency_timer() failed\n"); - goto fail; - } - - ret = ftdi_set_bitmode(&m_ftdic, 0x0b, BITMODE_MPSSE); - if (ret < 0) { - log->warning("ft2232: ftdi_set_bitmode() failed\n"); - goto fail; - } - - ret = ftdi_usb_reset(&m_ftdic); - if (ret < 0) { - log->warning("ft2232: ftdi_usb_reset() failed\n"); - goto fail; - } - - ret = ft2232_seq_purge(1, 0); - if (ret < 0) { - log->warning("ft2232: Could not purge\n"); - goto fail; - } - - // set TCK Divisor - buf[0] = TCK_DIVISOR; - buf[1] = 0x2; - buf[2] = 0x0; - ret = ft2232_write(buf, 3, 0 ); - if (ret < 0) { - log->warning("ft2232: Failed to set TCK divisor\n"); - goto fail; - } - - // switch off loopback - buf[0] = LOOPBACK_END; - ret = ft2232_write(buf, 1, 0); - if (ret < 0) { - log->warning("ft2232: Failed to switch off loopback\n"); - goto fail; - } - - ret = flush(); - if (ret < 0) { - goto fail; - } - - ret = ftdi_usb_reset(&m_ftdic); - if (ret < 0) { - log->warning("ft2232: ftdi_usb_reset() failed\n"); - goto fail; - } - - ret = ft2232_seq_purge(1, 0); - if (ret < 0) { - log->warning("ft2232: Could not purge\n"); - goto fail; - } - - return true; - -fail: - log->warning("ft2232: Opening device in mpsse mode failed\n"); - - ftdi_usb_close(&m_ftdic); - ftdi_deinit(&m_ftdic); - - return false; -} - -bool Ftdi::dev_try_open(unsigned int vid, unsigned int pid, unsigned int index) const -{ - struct ftdi_context ftdic; - int error; - - ftdi_init(&ftdic); - - error = ftdi_usb_open_desc_index(&ftdic, vid, pid, NULL, NULL, index); - if (error != 0) { - ftdi_deinit(&ftdic); - return false; - } - - ftdi_usb_close(&ftdic); - ftdi_deinit(&ftdic); - - return true; -} - -bool Ftdi::bit_out(char outbit, bool last) -{ - return stream_out_internal(&outbit, 1, false, last); -} - -bool Ftdi::bit_inout(char* inbit, char outbit, bool last) -{ - return stream_inout(inbit, &outbit, 1, last); -} - -bool Ftdi::stream_out(char* outstream, unsigned int n_bits, bool last) -{ - return stream_out_internal(outstream, n_bits, false, last); -} - -bool Ftdi::stream_out_internal(char* outstream, unsigned int n_bits, bool postread, bool last) -{ - unsigned int len_bytes; - unsigned int len_bits; - unsigned int len_tms_bits; - char buf; - - len_tms_bits = last ? 1 : 0; - len_bytes = (n_bits - len_tms_bits) / 8; - len_bits = (n_bits - len_tms_bits) % 8; - - if(len_bytes > 0) { - if (ft2232_write_bytes(outstream, len_bytes, postread) < 0) { - log->warning("ft2232: ftdi_stream_out has failed\n"); - return false; - } - } - - if(len_bits > 0) { - if (ft2232_write_bits(&(outstream[len_bytes]), len_bits, postread, 0) < 0) { - log->warning("ft2232: ftdi_stream_out has failed\n"); - return false; - } - } - - if(len_tms_bits > 0) { - buf = outstream[len_bytes] >> len_bits; - if (ft2232_write_bits(&buf, 1, postread, 1) < 0) { - log->warning("ft2232: ftdi_stream_out has failed\n"); - return false; - } - } - - return true; -} - -bool Ftdi::stream_in(char* instream, unsigned int n_bits, bool last) -{ - int len_bytes; - int len_bits; - int len_tms_bits; - - len_tms_bits = last ? 1 : 0; - len_bytes = (n_bits - len_tms_bits) / 8; - len_bits = (n_bits - len_tms_bits) % 8; - - if(len_bytes > 0) { - if (ft2232_read_packed_bits(instream, len_bytes, 8, 0) < 0) { - log->warning("ft2232: fdti_stream_in has failed\n"); - return false; - } - } - - if(len_bits > 0) { - if (ft2232_read_packed_bits(instream, 1, len_bits, len_bytes * 8) < 0) { - log->warning("ft2232: fdti_stream_in has failed\n"); - return false; - } - } - - if(len_tms_bits > 0) { - if (ft2232_read_packed_bits(instream, 1, 1, (len_bits + (len_bytes * 8))) < 0) { - log->warning("ft2232: fdti_stream_in has failed\n"); - return false; - } - } - - return true; -} - -bool Ftdi::stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last) -{ - if (outstream) - { - if (!stream_out_internal(outstream, n_bits, instream != NULL, last)) { - log->warning("ft2232: ftdi_stream_inout has failed\n"); - return false; - } - } - else - { - int bytes = (n_bits + 7) / 8; - char buffer[bytes]; - ::memset(buffer, 0, bytes); - if (!stream_out_internal(buffer, n_bits, instream != NULL, last)) { - log->warning("ft2232: ftdi_stream_inout has failed\n"); - return false; - } - } - - if (instream && !stream_in(instream, n_bits, last)) { - log->warning("ft2232: ftdi_stream_inout has failed\n"); - return false; - } - - return true; -} - -int Ftdi::ft2232_write_bytes(char *buf, int len, bool postread) -{ - int cur_command_size; - int max_command_size; - int cur_chunk_len; - int recv; - int xferred; - char *mybuf; - - if(len == 0) - return 0; - - recv = 0; - max_command_size = min(len, 65536)+3; - mybuf = (char*) malloc(max_command_size); - - /// Command OPCODE: write bytes - mybuf[0] = MPSSE_DO_WRITE | MPSSE_LSB | MPSSE_WRITE_NEG; - if(postread) // if postread is enabled it will buffer incoming bytes - mybuf[0] = mybuf[0] | MPSSE_DO_READ; - - // We divide the transmitting stream of bytes in chunks with a maximun length of 65536 bytes each. - while(len > 0) { - cur_chunk_len = min(len, 65536); - len = len - cur_chunk_len; - cur_command_size = cur_chunk_len + 3; - - /// Low and High bytes of the length field - mybuf[1] = (unsigned char) ( cur_chunk_len - 1); - mybuf[2] = (unsigned char) ((cur_chunk_len - 1) >> 8); - - /// The rest of the command is filled with the bytes that will be transferred - memcpy(&(mybuf[3]), buf, cur_chunk_len ); - buf = buf + cur_chunk_len; - - /// Finally we can transmit the command - xferred = ft2232_write(mybuf, cur_command_size, (postread ? cur_chunk_len : 0) ); - if(xferred != cur_command_size) { - log->warning("ft2232: could not transmit command\n"); - free(mybuf); - return -1; - } - - // If OK, the update the number of incoming bytes that are being buffered for a posterior read - if(postread) - recv = recv + cur_chunk_len; - } - - free(mybuf); - - // Returns the number of buffered incoming bytes - return recv; -} - -int Ftdi::ft2232_write_bits(char *buf, int len, bool postread, bool with_tms) -{ - int max_command_size; - int max_chunk_len; - int cur_chunk_len; - int recv; - int xferred; - int i; - char mybuf[3]; - - if(len == 0) - return 0; - - max_command_size = 3; - - if(!with_tms) { - /// Command OPCODE: write bits (can write up to 8 bits in a single command) - max_chunk_len = 8; - mybuf[0] = MPSSE_DO_WRITE | MPSSE_LSB | MPSSE_WRITE_NEG | MPSSE_BITMODE; - } - else { - /// Command OPCODE: 0x4B write bit with tms (can write up to 1 bits in a single command) - max_chunk_len = 1; - mybuf[0] = MPSSE_WRITE_TMS|MPSSE_LSB|MPSSE_BITMODE|MPSSE_WRITE_NEG; - } - - if(postread) // (OPCODE += 0x20) if postread is enabled it will buffer incoming bits - mybuf[0] = mybuf[0] | MPSSE_DO_READ; - - // We divide the transmitting stream of bytes in chunks with a maximun length of max_chunk_len bits each. - i = 0; - recv = 0; - while(len > 0) { - cur_chunk_len = min(len, max_chunk_len); - len = len - cur_chunk_len; - - // Bit length field - mybuf[1] = (char) (cur_chunk_len - 1); - - - if(!with_tms) { - /// The last byte of the command is filled with the bits that will be transferred - mybuf[2] = buf[i/8]; - i += 8; - } - else { - mybuf[2] = 0x01 | ((buf[(i/8)] >> (i%8)) << 7); - i++; - } - - // Finally we can transmit the command - xferred = ft2232_write(mybuf, max_command_size, (postread ? 1 : 0) ); - if(xferred != max_command_size) { - log->warning("ft2232: ftdi write has failed\n"); - return -1; - } - - // If OK, the update the number of incoming bytes that are being buffered for a posterior read - if(postread) - recv = recv + 1; - } - - if (flush() < 0) - return -1; - - return recv; -} - -int Ftdi::ft2232_read_packed_bits(char *buf, int packet_len, int bits_per_packet, int offset) -{ - char *mybuf; - unsigned char dst_mask; - unsigned char src_mask; - int row_offset; - int dst_row; - int dst_col; - int src_row; - int src_col; - int i; - - if(packet_len == 0 || bits_per_packet == 0) - return 0; - - if (offset == 0 && bits_per_packet == 8) - { - if(ft2232_read(buf, packet_len) < 0) { - log->warning("Read failed\n"); - return -1; - } - } - else - { - mybuf = (char*) malloc(packet_len); - if(ft2232_read(mybuf, packet_len) < 0) { - log->warning("Read failed\n"); - free(mybuf); - return -1; - } - - if(bits_per_packet < 8) { - for(i=0; i < packet_len; i++) { // rotate bits to the left side - mybuf[i] = (mybuf[i] >> (8-bits_per_packet)); - } - - for(i = offset; i < (packet_len*bits_per_packet+offset); i++) { - dst_row = i / 8; - dst_col = i % 8; - src_row = (i-offset) / bits_per_packet; - src_col = (i-offset) % bits_per_packet; - dst_mask = ~(1 << dst_col); - src_mask = (1 << src_col); - buf[dst_row] = (buf[dst_row] & dst_mask) | ((mybuf[src_row] & src_mask) >> (dst_col - src_col)); - } - } else if(bits_per_packet == 8) { - row_offset = offset / 8; - memcpy( &(buf[row_offset]), mybuf, packet_len); - } else { - free(mybuf); - return -1; - } - - free(mybuf); - } - - return 0; -} - -bool Ftdi::set_bit_value(int bit, int value) -{ - - char buf[4]; - - bits_value = (bits_value & ~(1<= 8) - { - buf[0] = SET_BITS_HIGH; - buf[1] = bits_value >> 8; - buf[2] = bits_direction >> 8; - buf[3] = SEND_IMMEDIATE; - } - else - { - buf[0] = SET_BITS_LOW; - buf[1] = bits_value; - buf[2] = bits_direction; - buf[3] = SEND_IMMEDIATE; - } - - if (ft2232_write(buf, 4, 0) != 4) return false; - flush(); - return true; -} - -bool Ftdi::set_bit_value_del(int bit, int value, int del) -{ - - char buf[4]; - - bits_value = (bits_value & ~(1<= 8) - { - buf[0] = SET_BITS_HIGH; - buf[1] = bits_value >> 8; - buf[2] = bits_direction >> 8; - buf[3] = SEND_IMMEDIATE; - } - else - { - buf[0] = SET_BITS_LOW; - buf[1] = bits_value; - buf[2] = bits_direction; - buf[3] = SEND_IMMEDIATE; - } - - if (ft2232_write(buf, 4, 0) != 4) return false; - usleep(del); - flush(); - return true; -} - -bool Ftdi::set_bit_direction(int bit, int isout) -{ - bits_direction = (bits_direction & ~(1<jtag_reset_gpio != -1) - return set_bit_value(this->jtag_reset_gpio, !active); - return true; -} - diff --git a/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.hpp b/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.hpp deleted file mode 100644 index 3cbba3f2..00000000 --- a/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.hpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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. - */ - -/* - * Authors: Andreas Traber - */ - -#ifndef MEM_FTDI_LL_H -#define MEM_FTDI_LL_H - -#include "cables/adv_dbg_itf/adv_dbg_itf.hpp" -#include "cable.hpp" - -#include -#include -#include - -extern "C" { -#include -} - -#define FTDX_MAXSEND 4096 -#define FTDX_MAXSEND_MPSSE (64 * 1024) -#define FTDI_MAXRECV ( 4 * 64) - -struct ftdi_param { - uint32_t send_buf_len; - uint32_t send_buffered; - char *send_buf; - uint32_t recv_buf_len; - uint32_t to_recv; - uint32_t recv_write_idx; - uint32_t recv_read_idx; - char *recv_buf; -}; - -class Log; - -class Ftdi : public Cable { - public: - - enum FTDIDeviceID { - Olimex, - Digilent, - Generic, - }; - - Ftdi(js::config *config, Log* log, FTDIDeviceID id); - ~Ftdi(); - - bool connect(js::config *config); - - bool bit_inout(char* inbit, char outbit, bool last); - - bool stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last); - - bool jtag_reset(bool active); - - int flush(); - - - - bool chip_reset(bool active, int duration); - bool chip_config(uint32_t config); - - private: - struct device_desc { - unsigned int vid; - unsigned int pid; - unsigned int index; - }; - - bool set_bit_value(int bit, int value); - bool set_bit_value_del(int bit, int value, int del); - bool set_bit_direction(int bit, int isout); - - bool bit_out(char outbit, bool tms); - - bool stream_out(char* outstream, unsigned int n_bits, bool last); - bool stream_in(char* instream, unsigned int n_bits, bool last); - - void state_trans_tms(int tms); - - bool stream_out_internal(char* outstream, unsigned int n_bits, bool postread, bool last); - - int ft2232_write_bytes(char *buf, int len, bool postread); - int ft2232_write_bits(char *buf, int len, bool postread, bool with_tms); - int ft2232_read_packed_bits(char *buf, int packet_len, int bits_per_packet, int offset); - - bool ft2232_common_open(); - bool ft2232_mpsse_open(); - int ft2232_seq_purge(int purge_rx, int purge_tx); - int ft2232_seq_reset(); - int ft2232_read(char* buf, int len); - int ft2232_write(char *buf, int len, int recv); - bool dev_try_open(unsigned int vid, unsigned int pid, unsigned int index) const; - - std::list m_descriptors; - std::vector user_gpios; - - Log* log; - FTDIDeviceID m_id; - struct ftdi_param m_params; - struct ftdi_context m_ftdic; - unsigned int bits_value; - unsigned int bits_direction; - int system_reset_gpio = -1; - int jtag_reset_gpio = -1; - bool reverse_reset = false; - -}; - -#endif diff --git a/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.cpp b/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.cpp deleted file mode 100644 index 189c79af..00000000 --- a/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cables/log.h" -#include "jtag-proxy.hpp" -#include "debug_bridge/proxy.hpp" - -Jtag_proxy::Jtag_proxy(Log* log) : Cable(NULL) -{ -} - -bool Jtag_proxy::connect(js::config *config) -{ - struct sockaddr_in addr; - struct hostent *he; - - js::config *proxy_config = config->get("jtag-proxy"); - - if (proxy_config == NULL || proxy_config->get("port") == NULL) - { - fprintf(stderr, "Didn't find any information for JTAG proxy\n"); - return false; - } - - int m_port = proxy_config->get("port")->get_int(); - char *m_server = (char *)"localhost"; - - if((m_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - fprintf(stderr, "Unable to create socket (%s)\n", strerror(errno)); - return false; - } - - if((he = gethostbyname(m_server)) == NULL) { - perror("gethostbyname"); - return false; - } - - addr.sin_family = AF_INET; - addr.sin_port = htons(m_port); - addr.sin_addr = *((struct in_addr *)he->h_addr_list[0]); - memset(addr.sin_zero, '\0', sizeof(addr.sin_zero)); - - if(::connect(m_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - fprintf(stderr, "Unable to connect to %s port %d (%s)\n", m_server, m_port, - strerror(errno)); - return false; - } - return true; -} - -bool Jtag_proxy::bit_inout(char* inbit, char outbit, bool last) -{ - return stream_inout(inbit, &outbit, 1, last); -} - -bool Jtag_proxy::proxy_stream(char* instream, char* outstream, unsigned int n_bits, bool last, int bit) -{ - proxy_req_t req = { .type=DEBUG_BRIDGE_JTAG_REQ }; - req.jtag.bits = n_bits; - req.jtag.tdo = instream != NULL; - - if (n_bits >= (1<<16)) return false; - - uint8_t buffer[n_bits]; - uint8_t value; - if (outstream) - { - for (int i=0; i>= 1; - } - } - else - { - ::memset(buffer, 0, n_bits); - } - - if (last) - { - buffer[n_bits-1] |= 1 << DEBUG_BRIDGE_JTAG_TMS; - } - - ::send(m_socket, (void *)&req, sizeof(req), 0); - ::send(m_socket, (void *)buffer, n_bits, 0); - if (instream != NULL) - { - - ::memset((void *)instream, 0, (n_bits + 7) / 8); - if (::recv(m_socket, (void *)instream, (n_bits + 7) / 8, 0) != (n_bits + 7) / 8) return false; - - } - return true; -} - -bool Jtag_proxy::stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last) -{ - return proxy_stream(instream, outstream, n_bits, last, DEBUG_BRIDGE_JTAG_TDI); -} - -bool Jtag_proxy::jtag_reset(bool active) -{ - int value = !active; - return proxy_stream(NULL, (char *)&value, 1, 0, DEBUG_BRIDGE_JTAG_TRST); -} - -int Jtag_proxy::flush() -{ - return true; -} - -bool Jtag_proxy::chip_reset(bool active, int duration) -{ - proxy_req_t req = { .type=DEBUG_BRIDGE_RESET_REQ }; - req.reset.active = active; - req.reset.duration = duration; - ::send(m_socket, (void *)&req, sizeof(req), 0); - - return true; -} - -bool Jtag_proxy::chip_config(uint32_t config) -{ - proxy_req_t req = { .type=DEBUG_BRIDGE_CONFIG_REQ }; - req.config.value = config; - ::send(m_socket, (void *)&req, sizeof(req), 0); - return true; -} diff --git a/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.hpp b/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.hpp deleted file mode 100644 index 97289aea..00000000 --- a/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - */ - - -#include "cables/adv_dbg_itf/adv_dbg_itf.hpp" -#include "cable.hpp" - -#include - -class Jtag_proxy : public Cable { - public: - - Jtag_proxy(Log* log); - - bool connect(js::config *config); - - bool bit_inout(char* inbit, char outbit, bool last); - - bool stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last); - - bool jtag_reset(bool active); - - int flush(); - - - - bool chip_reset(bool active, int duration); - bool chip_config(uint32_t config); - - private: - - int m_socket; - - bool proxy_stream(char* instream, char* outstream, unsigned int n_bits, bool last, int bit); - -}; \ No newline at end of file diff --git a/tools/pulp-debug-bridge/src/cables/jtag.cpp b/tools/pulp-debug-bridge/src/cables/jtag.cpp deleted file mode 100644 index ce571454..00000000 --- a/tools/pulp-debug-bridge/src/cables/jtag.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - */ -#include -#include -#include -#include -#include -#include "cable.hpp" - - -#define JTAG_SOC_INSTR_WIDTH 0x4 -#define JTAG_SOC_IDCODE 0x2 -#define JTAG_SOC_AXIREG 0x4 -#define JTAG_SOC_BBMUXREG 0x5 -#define JTAG_SOC_CLKGATEREG 0x6 -#define JTAG_SOC_CONFREG 0x7 -#define JTAG_SOC_TESTMODEREG 0x8 -#define JTAG_SOC_BISTREG 0x9 -#define JTAG_SOC_BYPASS 0xf -#define JTAG_SOC_IDCODE_WIDTH 32 -#define JTAG_SOC_AXIREG_WIDTH 96 -#define JTAG_SOC_BBMUXREG_WIDTH 21 -#define JTAG_SOC_CLKGATEREG_WIDTH 11 -#define JTAG_SOC_CONFREG_WIDTH 4 -#define JTAG_SOC_TESTMODEREG_WIDTH 4 -#define JTAG_SOC_BISTREG_WIDTH 20 - -#define JTAG_CLUSTER_INSTR_WIDTH 4 -#define JTAG_CLUSTER_IDCODE 2 -#define JTAG_CLUSTER_SAMPLE_PRELOAD 1 -#define JTAG_CLUSTER_EXTEST 0 -#define JTAG_CLUSTER_DEBUG 8 -#define JTAG_CLUSTER_MBIST 9 -#define JTAG_CLUSTER_BYPASS 0xf -#define JTAG_CLUSTER_IDCODE_WIDTH 32 - -#define JTAG_IDCODE_WIDTH JTAG_CLUSTER_IDCODE_WIDTH + JTAG_SOC_IDCODE_WIDTH -#define JTAG_INSTR_WIDTH JTAG_CLUSTER_INSTR_WIDTH + JTAG_SOC_INSTR_WIDTH - - -bool Cable_jtag_itf::jtag_soft_reset() { - for (int i = 0; i < 10; i++) - jtag_write_tms(1); - - jtag_write_tms(0); - - return true; -} - -bool Cable_jtag_itf::jtag_write_tms(int val) -{ - return bit_inout(NULL, 0x0, val != 0); -} - -bool Cable_jtag_itf::jtag_shift_ir() -{ - if (!jtag_write_tms(1)) return false; - if (!jtag_write_tms(1)) return false; - if (!jtag_write_tms(0)) return false; - if (!jtag_write_tms(0)) return false; - return true; -} - -bool Cable_jtag_itf::jtag_shift_dr() -{ - if (!jtag_write_tms(1)) return false; - if (!jtag_write_tms(0)) return false; - if (!jtag_write_tms(0)) return false; - return true; -} - -bool Cable_jtag_itf::jtag_idle() -{ - if (!jtag_write_tms(1)) return false; - if (!jtag_write_tms(0)) return false; - return true; -} - -bool Cable_jtag_itf::jtag_shift(int width, char *bits) -{ - return stream_inout(NULL, bits, width, 1); -} - -bool Cable_jtag_itf::jtag_shift_ir(unsigned int ir, int ir_len) -{ - if (ir_len == -1) - ir_len = JTAG_SOC_INSTR_WIDTH; - - if (!jtag_shift_ir()) return false; - if (!jtag_shift(ir_len, (char *)&ir)) return false; - if (!jtag_idle()) return false; - return true; -} - -bool Cable_jtag_itf::jtag_set_reg(unsigned int reg, int width, unsigned int value, int ir_len) -{ - if (!jtag_shift_ir(reg, ir_len)) return false; - if (!jtag_shift_dr()) return false; - if (!jtag_shift(width, (char *)&value)) return false; - if (!jtag_idle()) return false; - return true; -} - -bool Cable_jtag_itf::jtag_get_reg(unsigned int reg, int width, unsigned int *out_value, unsigned int value, int ir_len) -{ - if (!jtag_shift_ir(reg, ir_len)) return false; - if (!jtag_shift_dr()) return false; - if (!stream_inout((char *)out_value, (char *)&value, width, 1)) return false; - if (!jtag_idle()) return false; - return true; -} diff --git a/tools/pulp-debug-bridge/src/cables/log.h b/tools/pulp-debug-bridge/src/cables/log.h deleted file mode 100644 index aacedb37..00000000 --- a/tools/pulp-debug-bridge/src/cables/log.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - */ - -#ifndef LOG_H -#define LOG_H - -typedef enum -{ - LOG_ERROR = 0, - LOG_WARNING = 1, - LOG_INFO = 2, - LOG_DEBUG = 3 -} log_level_e; - -class Log { - public: - void print(log_level_e, const char *str, ...); - void error(const char *str, ...) ; - void warning(const char *str, ...) ; - void user(const char *str, ...) ; - void debug(const char *str, ...) ; -}; - -#endif diff --git a/tools/pulp-debug-bridge/src/gdb-server/breakpoints.cpp b/tools/pulp-debug-bridge/src/gdb-server/breakpoints.cpp deleted file mode 100644 index 1d046c55..00000000 --- a/tools/pulp-debug-bridge/src/gdb-server/breakpoints.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Andreas Traber - */ - - -#include "gdb-server.hpp" - -#include -#include - -#define INSN_IS_COMPRESSED(instr) ((instr & 0x3) != 0x3) -#define INSN_BP_COMPRESSED 0x9002 -#define INSN_BP 0x00100073 - -Breakpoints::Breakpoints(Gdb_server *top) -: top(top) { -} - -bool -Breakpoints::insert(unsigned int addr) { - bool retval; - uint32_t data_bp; - struct bp_insn bp; - - bp.addr = addr; - retval = top->cable->access(false, addr, 4, (char*)&bp.insn_orig); - bp.is_compressed = INSN_IS_COMPRESSED(bp.insn_orig); - - breakpoints.push_back(bp); - - if (bp.is_compressed) { - data_bp = INSN_BP_COMPRESSED; - retval = retval && top->cable->access(true, addr, 2, (char*)&data_bp); - } else { - data_bp = INSN_BP; - retval = retval && top->cable->access(true, addr, 4, (char*)&data_bp); - } - - this->top->target->flush(); - - return retval; -} - -bool -Breakpoints::remove(unsigned int addr) { - - bool retval; - bool is_compressed; - uint32_t data; - for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { - if (it->addr == addr) { - data = it->insn_orig; - is_compressed = it->is_compressed; - - breakpoints.erase(it); - - if (is_compressed) - retval = top->cable->access(true, addr, 2, (char*)&data); - else - retval = top->cable->access(true, addr, 4, (char*)&data); - - this->top->target->flush(); - - return retval; - } - } - - return false; -} - -bool -Breakpoints::clear() { - - bool retval = this->disable_all(); - - breakpoints.clear(); - - return retval; -} - - -bool -Breakpoints::at_addr(unsigned int addr) { - for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { - if (it->addr == addr) { - // we found our bp - return true; - } - } - - return false; -} - -bool -Breakpoints::enable(unsigned int addr) { - bool retval; - uint32_t data; - - for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { - if (it->addr == addr) { - if (it->is_compressed) { - data = INSN_BP_COMPRESSED; - retval = top->cable->access(1, addr, 2, (char*)&data); - } else { - data = INSN_BP; - retval = top->cable->access(1, addr, 4, (char*)&data); - } - - return true; - //return retval && m_cache->flush(); - } - } - - fprintf(stderr, "bp_enable: Did not find any bp at addr %08X\n", addr); - - return false; -} - -bool -Breakpoints::disable(unsigned int addr) { - bool retval; - - for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { - if (it->addr == addr) { - if (it->is_compressed) - retval = top->cable->access(1, addr, 2, (char*)&it->insn_orig); - else - retval = top->cable->access(1, addr, 4, (char*)&it->insn_orig); - - return true; - //return retval && m_cache->flush(); - } - } - - fprintf(stderr, "bp_enable: Did not find any bp at addr %08X\n", addr); - - return false; -} - -bool -Breakpoints::enable_all() { - bool retval = true; - - for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { - retval = retval && this->enable(it->addr); - } - - return retval; -} - -bool -Breakpoints::disable_all() { - bool retval = true; - - for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { - retval = retval && this->disable(it->addr); - } - - return retval; -} diff --git a/tools/pulp-debug-bridge/src/gdb-server/gdb-server.cpp b/tools/pulp-debug-bridge/src/gdb-server/gdb-server.cpp deleted file mode 100644 index d6f35cde..00000000 --- a/tools/pulp-debug-bridge/src/gdb-server/gdb-server.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Andreas Traber - */ - -#include "cable.hpp" -#include "gdb-server/gdb-server.hpp" -#include - - -Gdb_server::Gdb_server(Log *log, Cable *cable, js::config *config, int socket_port) -: log(log), cable(cable), config(config) -{ - target = new Target(this); - - bkp = new Breakpoints(this); - - rsp = new Rsp(this, socket_port); - - if (!rsp->open()) throw std::logic_error("Unable to open RSP server"); -} - -int Gdb_server::stop(bool kill) -{ - if (rsp != NULL) - { - rsp->close(kill); - rsp = NULL; - } -} - -void Gdb_server::print(const char *format, ...) -{ - va_list ap; - va_start(ap, format); - vprintf(format, ap); - va_end(ap); -} diff --git a/tools/pulp-debug-bridge/src/gdb-server/gdb-server.h b/tools/pulp-debug-bridge/src/gdb-server/gdb-server.h deleted file mode 100644 index defb494b..00000000 --- a/tools/pulp-debug-bridge/src/gdb-server/gdb-server.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Andreas Traber - */ - -#ifndef __GDB_SERVER_GDB_SERVER_H__ -#define __GDB_SERVER_GDB_SERVER_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -class Rsp { - public: - Rsp(int socket_ptr); - //Rsp(int socket_port, MemIF* mem, LogIF *log, std::list list_dbgif, std::list list_dbg_cluster_if, BreakPoints* bp, DbgIF *main_if); - - private: - bool open(); - void close(); - - bool wait_client(); - bool loop(); - - bool decode(char* data, size_t len); - - bool multithread(char* data, size_t len); - - bool cont(char* data, size_t len); // continue, reserved keyword, thus not used as function name - bool step(char* data, size_t len); - - bool query(char* data, size_t len); - bool v_packet(char* data, size_t len); - - bool regs_send(); - bool reg_read(char* data, size_t len); - bool reg_write(char* data, size_t len); - - bool get_packet(char* data, size_t* len); - - bool signal(); - - bool send(const char* data, size_t len); - bool send_str(const char* data); - - - // internal helper functions - bool pc_read(unsigned int* pc); - - bool waitStop(DbgIF* dbgif); - bool resume(bool step); - bool resume(int tid, bool step); - void resumeCoresPrepare(DbgIF *dbgif, bool step); - void resumeCores(); - - bool mem_read(char* data, size_t len); - bool mem_write_ascii(char* data, size_t len); - bool mem_write(char* data, size_t len); - - bool bp_insert(char* data, size_t len); - bool bp_remove(char* data, size_t len); - - DbgIF* get_dbgif(int thread_id); - - int m_socket_port; - int m_socket_in; - int m_socket_client; - - int m_thread_sel; - int m_thread_init; - MemIF* m_mem; - LogIF *log; - BreakPoints* m_bp; - std::list m_dbgifs; - std::list m_dbg_cluster_ifs; -}; - -#endif \ No newline at end of file diff --git a/tools/pulp-debug-bridge/src/gdb-server/gdb-server.hpp b/tools/pulp-debug-bridge/src/gdb-server/gdb-server.hpp deleted file mode 100644 index 7dded16c..00000000 --- a/tools/pulp-debug-bridge/src/gdb-server/gdb-server.hpp +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Andreas Traber - */ - -#ifndef __GDB_SERVER_GDB_SERVER_H__ -#define __GDB_SERVER_GDB_SERVER_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cable.hpp" -#include "json.hpp" - -#define DBG_CTRL_REG 0x0 -#define DBG_HIT_REG 0x4 -#define DBG_IE_REG 0x8 -#define DBG_CAUSE_REG 0xC -#define DBG_NPC_REG 0x2000 -#define DBG_PPC_REG 0x2004 - -#define DBG_CAUSE_BP 0x3 - - -class Rsp; -class Breakpoints; -class Target; -class Target_cluster_common; -class Target_cluster; -class Target_core; - -static int first_free_thread_id = 0; - -class Gdb_server -{ -public: - Gdb_server(Log *log, Cable *cable, js::config *config, int socket_port); - int stop(bool kill); - void print(const char *format, ...); - - Rsp *rsp; - Log *log; - Cable *cable; - Target *target; - Breakpoints *bkp; - - js::config *config; -}; - - - - -class Target_core -{ -public: - Target_core(Gdb_server *top, uint32_t dbg_unit_addr, int cluster_id, int core_id); - void set_power(bool is_on); - bool read(uint32_t addr, uint32_t* rdata); - bool write(uint32_t addr, uint32_t wdata); - bool csr_read(unsigned int i, uint32_t *data); - int get_thread_id() { return thread_id; } - void get_name(char* str, size_t len) { - snprintf(str, len, "Cluster %02d - Core %01d", this->cluster_id, this->core_id); - } - bool is_stopped(); - void read_ppc(uint32_t *ppc); - - bool stop(); - bool halt(); - void prepare_resume(bool step=false); - void commit_resume(); - void set_step_mode(bool new_step); - void commit_step_mode(); - void resume(); - void flush(); - - bool gpr_read_all(uint32_t *data); - bool gpr_read(unsigned int i, uint32_t *data); - bool gpr_write(unsigned int i, uint32_t data); - -private: - Gdb_server *top; - bool is_on = false; - uint32_t dbg_unit_addr; - uint32_t hartid; - int cluster_id; - int core_id; - int thread_id; - bool ppc_is_cached = false; - uint32_t ppc_cached; - bool stopped = false; - bool step = false; - bool commit_step = false; -}; - - - -class Target { -public: - Target(Gdb_server *top); - - inline int get_nb_threads() { return cores.size(); } - - - void halt(); - void resume(bool step=false, int tid=-1); - void resume_all(); - bool wait(int socket_client); - void flush(); - - void update_power(); - - std::vector get_threads() { return cores; } - Target_core *get_thread(int thread_id) { return cores_from_threadid[thread_id]; } - Target_core *get_thread_from_id(int id) { return cores[id]; } - - -private: - Gdb_server *top; - std::vector clusters; - std::vector cores; - std::map cores_from_threadid; -}; - - - -struct bp_insn { - uint32_t addr; - uint32_t insn_orig; - bool is_compressed; -}; - - - -class Breakpoints { - public: - Breakpoints(Gdb_server *top); - - bool insert(unsigned int addr); - bool remove(unsigned int addr); - - bool clear(); - - bool at_addr(unsigned int addr); - - bool enable_all(); - bool disable_all(); - - bool disable(unsigned int addr); - bool enable(unsigned int addr); - - private: - std::list breakpoints; - Gdb_server *top; -}; - -class Rsp { - public: - Rsp(Gdb_server *top, int socket_port); - //Rsp(int socket_port, MemIF* mem, LogIF *log, std::list list_dbgif, std::list list_dbg_cluster_if, BreakPoints* bp, DbgIF *main_if); - - bool open(); - void close(int kill); - - - private: - - bool decode(int socket_client, char* data, size_t len); - bool get_packet(int socket_client, char* data, size_t* len); - - void client_routine(int socket_client); - void listener_routine(); - - - bool regs_send(int socket_client); - bool signal(int socket_client); - - bool multithread(int socket_client, char* data, size_t len); - - bool v_packet(int socket_client, char* data, size_t len); - - bool query(int socket_client, char* data, size_t len); - - bool send(int socket_client, const char* data, size_t len); - bool send_str(int socket_client, const char* data); - - bool cont(int socket_client, char* data, size_t len); // continue, reserved keyword, thus not used as function name - bool resume(int socket_client, bool step); - bool resume(int socket_client, int tid, bool step); - bool wait(int socket_client, Target_core *core=NULL); - bool step(int socket_client, char* data, size_t len); - - // internal helper functions - bool pc_read(int socket_client, unsigned int* pc); - - bool reg_read(int socket_client, char* data, size_t len); - bool reg_write(int socket_client, char* data, size_t len); - - bool mem_read(int socket_client, char* data, size_t len); - bool mem_write_ascii(int socket_client, char* data, size_t len); - bool mem_write(int socket_client, char* data, size_t len); - - bool bp_insert(int socket_client, char* data, size_t len); - bool bp_remove(int socket_client, char* data, size_t len); - - Gdb_server *top; - int socket_port; - int socket_in; - std::thread *listener_thread; - - int thread_sel; - Target_core *main_core = NULL; - - - - bool wait_client(); - bool loop(); - - - - //void resumeCoresPrepare(DbgIF *dbgif, bool step); - void resumeCores(); - - //DbgIF* get_dbgif(int thread_id); - - - int m_thread_init; - //MemIF* m_mem; - //LogIF *log; - //BreakPoints* m_bp; - //std::list m_dbgifs; - //std::list m_dbg_cluster_ifs; -}; - -#endif \ No newline at end of file diff --git a/tools/pulp-debug-bridge/src/gdb-server/rsp.cpp b/tools/pulp-debug-bridge/src/gdb-server/rsp.cpp deleted file mode 100644 index fdf0b645..00000000 --- a/tools/pulp-debug-bridge/src/gdb-server/rsp.cpp +++ /dev/null @@ -1,1146 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Andreas Traber - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gdb-server.hpp" -#include - -enum mp_type { - BP_MEMORY = 0, - BP_HARDWARE = 1, - WP_WRITE = 2, - WP_READ = 3, - WP_ACCESS = 4 -}; - -enum target_signal { - TARGET_SIGNAL_NONE = 0, - TARGET_SIGNAL_INT = 2, - TARGET_SIGNAL_ILL = 4, - TARGET_SIGNAL_TRAP = 5, - TARGET_SIGNAL_FPE = 8, - TARGET_SIGNAL_BUS = 10, - TARGET_SIGNAL_SEGV = 11, - TARGET_SIGNAL_ALRM = 14, - TARGET_SIGNAL_STOP = 17, - TARGET_SIGNAL_USR2 = 31, - TARGET_SIGNAL_PWR = 32 -}; - -#define PACKET_MAX_LEN 4096 - - -Rsp::Rsp(Gdb_server *top, int socket_port) : top(top), socket_port(socket_port) -{ - main_core = top->target->get_threads().front(); - - m_thread_init = main_core->get_thread_id(); - thread_sel = m_thread_init; -} - -bool Rsp::v_packet(int socket_client, char* data, size_t len) -{ - if (strncmp ("vKill", data, strlen ("vKill")) == 0) - { - this->send_str(socket_client, "OK"); - return false; - } - else if (strncmp ("vCont?", data, strlen ("vCont?")) == 0) - { - return this->send_str(socket_client, "vCont;c;s;C;S"); - } - else if (strncmp ("vCont", data, strlen ("vCont")) == 0) - { - int nb_threads = top->target->get_nb_threads(); - bool thread_done[nb_threads]; - for (int i=0; ilog->print(LOG_ERROR, "Unsupported command in vCont packet: %s\n", str); - exit(-1); - } - - if (cont) { - if (tid == -1) { - for (int i=0; itop->target->get_thread_from_id(i)->prepare_resume(step); - } - } - } else { - if (!thread_done[tid]) - { - thread_done[tid] = true; - this->top->target->get_thread(tid)->prepare_resume(step); - } - } - } - - str = strtok(NULL, ";"); - } - - this->top->target->resume_all(); - - return this->wait(socket_client); - } - - return this->send_str(socket_client, ""); -} - -bool Rsp::query(int socket_client, char* data, size_t len) -{ - int ret; - char reply[256]; - - if (strncmp ("qSupported", data, strlen ("qSupported")) == 0) - { - return this->send_str(socket_client, "PacketSize=256"); - } - else if (strncmp ("qTStatus", data, strlen ("qTStatus")) == 0) - { - // not supported, send empty packet - return this->send_str(socket_client, ""); - } - else if (strncmp ("qfThreadInfo", data, strlen ("qfThreadInfo")) == 0) - { - reply[0] = 'm'; - ret = 1; - for (auto &thread : top->target->get_threads()) - { - ret += snprintf(&reply[ret], 256 - ret, "%u,", thread->get_thread_id()); - } - - return this->send(socket_client, reply, ret-1); - } - else if (strncmp ("qsThreadInfo", data, strlen ("qsThreadInfo")) == 0) - { - return this->send_str(socket_client, "l"); - } - else if (strncmp ("qThreadExtraInfo", data, strlen ("qThreadExtraInfo")) == 0) - { - const char* str_default = "Unknown Core"; - char str[256]; - unsigned int thread_id; - if (sscanf(data, "qThreadExtraInfo,%d", &thread_id) != 1) { - top->log->print(LOG_ERROR, "Could not parse qThreadExtraInfo packet\n"); - return this->send_str(socket_client, ""); - } - Target_core *thread = top->target->get_thread(thread_id); - { - if (thread != NULL) - thread->get_name(str, 256); - else - strcpy(str, str_default); - - ret = 0; - for(int i = 0; i < strlen(str); i++) - ret += snprintf(&reply[ret], 256 - ret, "%02X", str[i]); - } - - return this->send(socket_client, reply, ret); - } - else if (strncmp ("qAttached", data, strlen ("qAttached")) == 0) - { - return this->send_str(socket_client, "1"); - } - else if (strncmp ("qC", data, strlen ("qC")) == 0) - { - snprintf(reply, 64, "0.%u", this->top->target->get_thread(thread_sel)->get_thread_id()); - return this->send_str(socket_client, reply); - } - else if (strncmp ("qSymbol", data, strlen ("qSymbol")) == 0) - { - return this->send_str(socket_client, "OK"); - } - else if (strncmp ("qOffsets", data, strlen ("qOffsets")) == 0) - { - return this->send_str(socket_client, "Text=0;Data=0;Bss=0"); - } - else if (strncmp ("qT", data, strlen ("qT")) == 0) - { - // not supported, send empty packet - return this->send_str(socket_client, ""); - } - - top->log->print(LOG_ERROR, "Unknown query packet\n"); - - return false; -} - - - -// internal helper functions -bool Rsp::pc_read(int socket_client, unsigned int* pc) -{ - uint32_t npc; - uint32_t ppc; - uint32_t cause; - uint32_t hit; - Target_core *core; - - core = this->top->target->get_thread(thread_sel); - - core->read_ppc(&ppc); - core->read(DBG_NPC_REG, &npc); - - core->read(DBG_HIT_REG, &hit); - core->read(DBG_CAUSE_REG, &cause); - - if (hit & 0x1) - *pc = npc; - else if(cause & (1 << 31)) // interrupt - *pc = npc; - else if ((cause & 0x1f) == 3) // breakpoint - *pc = ppc; - else if ((cause & 0x1f) == 2) - *pc = ppc; - else if ((cause & 0x1f) == 5) - *pc = ppc; - else - *pc = npc; - - return true; -} - - - - -bool Rsp::mem_read(int socket_client, char* data, size_t len) -{ - unsigned char buffer[512]; - char reply[512]; - uint32_t addr; - uint32_t length; - uint32_t rdata; - int i; - - if (sscanf(data, "%x,%x", &addr, &length) != 2) { - top->log->print(LOG_ERROR, "Could not parse packet\n"); - return false; - } - - top->cable->access(false, addr, length, (char *)buffer); - - for(i = 0; i < length; i++) { - rdata = buffer[i]; - snprintf(&reply[i * 2], 3, "%02x", rdata); - } - - return this->send(socket_client, reply, length*2); -} - - - -bool Rsp::mem_write_ascii(int socket_client, char* data, size_t len) -{ - uint32_t addr; - int length; - uint32_t wdata; - int i, j; - - char* buffer; - int buffer_len; - - if (sscanf(data, "%x,%d:", &addr, &length) != 2) { - top->log->print(LOG_ERROR, "Could not parse packet\n"); - return false; - } - - for(i = 0; i < len; i++) { - if (data[i] == ':') { - break; - } - } - - if (i == len) - return false; - - // align to hex data - data = &data[i+1]; - len = len - i - 1; - - buffer_len = len/2; - buffer = (char*)malloc(buffer_len); - if (buffer == NULL) { - top->log->print(LOG_ERROR, "Failed to allocate buffer\n"); - return false; - } - - for(j = 0; j < len/2; j++) { - wdata = 0; - for(i = 0; i < 2; i++) { - char c = data[j * 2 + i]; - uint32_t hex = 0; - if (c >= '0' && c <= '9') - hex = c - '0'; - else if (c >= 'a' && c <= 'f') - hex = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - hex = c - 'A' + 10; - - wdata |= hex << (4 * i); - } - - buffer[j] = wdata; - } - - top->cable->access(true, addr, buffer_len, buffer); - - free(buffer); - - return this->send_str(socket_client, "OK"); -} - -bool Rsp::mem_write(int socket_client, char* data, size_t len) -{ - uint32_t addr; - int length; - uint32_t wdata; - int i, j; - - char* buffer; - int buffer_len; - - if (sscanf(data, "%x,%x:", &addr, &length) != 2) { - top->log->print(LOG_ERROR, "Could not parse packet\n"); - return false; - } - - for(i = 0; i < len; i++) { - if (data[i] == ':') { - break; - } - } - - if (i == len) - return false; - - // align to hex data - data = &data[i+1]; - len = len - i - 1; - - top->cable->access(write, addr, len, data); - - return this->send_str(socket_client, "OK"); -} - - - -bool Rsp::reg_read(int socket_client, char* data, size_t len) -{ - uint32_t addr; - uint32_t rdata; - char data_str[10]; - - if (sscanf(data, "%x", &addr) != 1) { - top->log->print(LOG_ERROR, "Could not parse packet\n"); - return false; - } - - if (addr < 32) - this->top->target->get_thread(thread_sel)->gpr_read(addr, &rdata); - else if (addr == 0x20) - this->pc_read(socket_client, &rdata); - else - return this->send_str(socket_client, ""); - - rdata = htonl(rdata); - snprintf(data_str, 9, "%08x", rdata); - - return this->send_str(socket_client, data_str); -} - - - -bool Rsp::reg_write(int socket_client, char* data, size_t len) -{ - uint32_t addr; - uint32_t wdata; - char data_str[10]; - Target_core *core; - - if (sscanf(data, "%x=%08x", &addr, &wdata) != 2) { - top->log->print(LOG_ERROR, "Could not parse packet\n"); - return false; - } - - wdata = ntohl(wdata); - - core = this->top->target->get_thread(thread_sel); - if (addr < 32) - core->gpr_write(addr, wdata); - else if (addr == 32) - core->write(DBG_NPC_REG, wdata); - else - return this->send_str(socket_client, "E01"); - - return this->send_str(socket_client, "OK"); -} - - - -bool Rsp::regs_send(int socket_client) -{ - uint32_t gpr[32]; - uint32_t npc; - uint32_t ppc; - char regs_str[512]; - int i; - - this->top->target->get_thread(thread_sel)->gpr_read_all(gpr); - - // now build the string to send back - for(i = 0; i < 32; i++) { - snprintf(®s_str[i * 8], 9, "%08x", htonl(gpr[i])); - } - - this->pc_read(socket_client, &npc); - snprintf(®s_str[32 * 8 + 0 * 8], 9, "%08x", htonl(npc)); - - return this->send_str(socket_client, regs_str); -} - - - -bool Rsp::signal(int socket_client) -{ - uint32_t cause; - uint32_t hit; - int signal; - char str[4]; - int len; - Target_core *core; - - core = this->top->target->get_thread(thread_sel); - - //dbgif->write(DBG_IE_REG, 0xFFFF); - - // figure out why we are stopped - if (core->is_stopped()) { - if (!core->read(DBG_HIT_REG, &hit)) - return false; - if (!core->read(DBG_CAUSE_REG, &cause)) - return false; - - if (hit & 0x1) - signal = TARGET_SIGNAL_TRAP; - else if(cause & (1 << 31)) - signal = TARGET_SIGNAL_INT; - else if ((cause & 0x1f) == 3) - signal = TARGET_SIGNAL_TRAP; - else if ((cause & 0x1f) == 2) - signal = TARGET_SIGNAL_ILL; - else if ((cause & 0x1f) == 5) - signal = TARGET_SIGNAL_BUS; - else - signal = TARGET_SIGNAL_STOP; - } else { - signal = TARGET_SIGNAL_NONE; - } - - len = snprintf(str, 4, "S%02x", signal); - - return this->send(socket_client, str, len); -} - - -bool Rsp::cont(int socket_client, char* data, size_t len) -{ - uint32_t sig; - uint32_t addr; - uint32_t npc; - int i; - bool npc_found = false; - Target_core *core; - - // strip signal first - if (data[0] == 'C') { - if (sscanf(data, "C%X;%X", &sig, &addr) == 2) - npc_found = true; - } else { - if (sscanf(data, "c%X", &addr) == 1) - npc_found = true; - } - - if (npc_found) { - core = this->top->target->get_thread(thread_sel); - // only when we have received an address - core->read(DBG_NPC_REG, &npc); - - if (npc != addr) - core->write(DBG_NPC_REG, addr); - } - - thread_sel = m_thread_init; - - return this->resume(socket_client, false); -} - - - -bool Rsp::resume(int socket_client, bool step) -{ - this->top->target->resume(step); - return this->wait(socket_client); -} - - - -bool Rsp::resume(int socket_client, int tid, bool step) -{ - this->top->target->resume(step, tid); - return this->wait(socket_client); -} - - - -bool Rsp::step(int socket_client, char* data, size_t len) -{ - uint32_t addr; - uint32_t npc; - int i; - Target_core *core; - - // strip signal first - if (data[0] == 'S') { - for (i = 0; i < len; i++) { - if (data[i] == ';') { - data = &data[i+1]; - break; - } - } - } - - if (sscanf(data, "%x", &addr) == 1) { - core = this->top->target->get_thread(thread_sel); - // only when we have received an address - core->read(DBG_NPC_REG, &npc); - - if (npc != addr) - core->write(DBG_NPC_REG, addr); - } - - thread_sel = m_thread_init; - - return this->resume(socket_client, true); -} - - - -bool Rsp::wait(int socket_client, Target_core *core) -{ - int ret; - char pkt; - - fd_set rfds; - struct timeval tv; - - while(1) { - - // Check if a cluster power state has changed - this->top->target->update_power(); - - //First check if one core has stopped - if (core) { - if (core->is_stopped()) { - this->top->target->halt(); - return this->signal(socket_client); - } - } else { - for (auto &core: this->top->target->get_threads()) { - if (core->is_stopped()) { - this->top->target->halt(); - return this->signal(socket_client); - } - } - } - - // Otherwise wait for a stop request from gdb side for a while - - FD_ZERO(&rfds); - FD_SET(socket_client, &rfds); - - tv.tv_sec = 0; - tv.tv_usec = 100 * 1000; - - if (select(socket_client+1, &rfds, NULL, NULL, &tv)) { - ret = recv(socket_client, &pkt, 1, 0); - if (ret == 1 && pkt == 0x3) { - if (core) { - core->halt(); - return this->signal(socket_client); - } else { - top->target->halt(); - } - } - } - } - - return true; -} - - - -bool Rsp::multithread(int socket_client, char* data, size_t len) -{ - int thread_id; - - switch (data[0]) { - case 'c': - case 'g': - if (sscanf(&data[1], "%d", &thread_id) != 1) - return false; - - if (thread_id == -1) // affects all threads - return this->send_str(socket_client, "OK"); - - // we got the thread id, now let's look for this thread in our list - if (this->top->target->get_thread(thread_id) != NULL) { - thread_sel = thread_id; - return this->send_str(socket_client, "OK"); - } - - return this->send_str(socket_client, "E01"); - } - - return false; -} - - - -bool Rsp::decode(int socket_client, char* data, size_t len) -{ - if (data[0] == 0x03) { - top->log->print(LOG_DEBUG, "Received break\n"); - return this->signal(socket_client); - } - - switch (data[0]) { - case 'q': - return this->query(socket_client, &data[0], len); - - case 'g': - return this->regs_send(socket_client); - - case 'p': - return this->reg_read(socket_client, &data[1], len-1); - - case 'P': - return this->reg_write(socket_client, &data[1], len-1); - - case 'c': - case 'C': - return this->cont(socket_client, &data[0], len); - - case 's': - case 'S': - return this->step(socket_client, &data[0], len); - - case 'H': - return this->multithread(socket_client, &data[1], len-1); - - case 'm': - return this->mem_read(socket_client, &data[1], len-1); - - case '?': - return this->signal(socket_client); - - case 'v': - return this->v_packet(socket_client, &data[0], len); - - case 'M': - return this->mem_write_ascii(socket_client, &data[1], len-1); - - case 'X': - return this->mem_write(socket_client, &data[1], len-1); - - case 'z': - return this->bp_remove(socket_client, &data[0], len); - - case 'Z': - return this->bp_insert(socket_client, &data[0], len); - - case 'T': - return this->send_str(socket_client, "OK"); // threads are always alive - - case 'D': - this->send_str(socket_client, "OK"); - return false; - - default: - top->log->print(LOG_ERROR, "Unknown packet: starts with %c\n", data[0]); - break; - } - - return false; -} - - - -bool -Rsp::get_packet(int socket_client, char* pkt, size_t* p_pkt_len) { - char c; - char check_chars[2]; - char buffer[PACKET_MAX_LEN]; - int buffer_len = 0; - int pkt_len; - bool escaped = false; - int ret; - // packets follow the format: $packet-data#checksum - // checksum is two-digit - - // poison packet - memset(pkt, 0, PACKET_MAX_LEN); - pkt_len = 0; - - // first look for start bit - do { - ret = recv(socket_client, &c, 1, 0); - - if((ret == -1 && errno != EWOULDBLOCK) || (ret == 0)) { - top->log->print(LOG_ERROR, "RSP: Error receiving\n"); - return false; - } - - if(ret == -1 && errno == EWOULDBLOCK) { - // no data available - continue; - } - - // special case for 0x03 (asynchronous break) - if (c == 0x03) { - pkt[0] = c; - *p_pkt_len = 1; - return true; - } - } while(c != '$'); - - buffer[0] = c; - - // now store data as long as we don't see # - do { - if (buffer_len >= PACKET_MAX_LEN || pkt_len >= PACKET_MAX_LEN) { - top->log->print(LOG_ERROR, "RSP: Too many characters received\n"); - return false; - } - - ret = recv(socket_client, &c, 1, 0); - - if((ret == -1 && errno != EWOULDBLOCK) || (ret == 0)) { - top->log->print(LOG_ERROR, "RSP: Error receiving\n"); - return false; - } - - if(ret == -1 && errno == EWOULDBLOCK) { - // no data available - continue; - } - - buffer[buffer_len++] = c; - - // check for 0x7d = '}' - if (c == 0x7d) { - escaped = true; - continue; - } - - if (escaped) - pkt[pkt_len++] = c ^ 0x20; - else - pkt[pkt_len++] = c; - - escaped = false; - } while(c != '#'); - - buffer_len--; - pkt_len--; - - // checksum, 2 bytes - ret = recv(socket_client, &check_chars[0], 1, 0); - if((ret == -1 && errno != EWOULDBLOCK) || (ret == 0)) { - top->log->print(LOG_ERROR, "RSP: Error receiving\n"); - return false; - } - - ret = recv(socket_client, &check_chars[1], 1, 0); - if((ret == -1 && errno != EWOULDBLOCK) || (ret == 0)) { - top->log->print(LOG_ERROR, "RSP: Error receiving\n"); - return false; - } - - // check the checksum - unsigned int checksum = 0; - for(int i = 0; i < buffer_len; i++) { - checksum += buffer[i]; - } - - checksum = checksum % 256; - char checksum_str[3]; - snprintf(checksum_str, 3, "%02x", checksum); - - if (check_chars[0] != checksum_str[0] || check_chars[1] != checksum_str[1]) { - top->log->print(LOG_ERROR, "RSP: Checksum failed; received %.*s; checksum should be %02x\n", pkt_len, pkt, checksum); - return false; - } - - // now send ACK - char ack = '+'; - if (::send(socket_client, &ack, 1, 0) != 1) { - top->log->print(LOG_ERROR, "RSP: Sending ACK failed\n"); - return false; - } - - // NULL terminate the string - pkt[pkt_len] = '\0'; - *p_pkt_len = pkt_len; - - return true; -} - -bool Rsp::send(int socket_client, const char* data, size_t len) -{ - int ret; - int i; - size_t raw_len = 0; - char* raw = (char*)malloc(len * 2 + 4); - unsigned int checksum = 0; - - raw[raw_len++] = '$'; - - for (i = 0; i < len; i++) { - char c = data[i]; - - // check if escaping needed - if (c == '#' || c == '%' || c == '}' || c == '*') { - raw[raw_len++] = '}'; - raw[raw_len++] = c; - checksum += '}'; - checksum += c; - } else { - raw[raw_len++] = c; - checksum += c; - } - } - - // add checksum - checksum = checksum % 256; - char checksum_str[3]; - snprintf(checksum_str, 3, "%02x", checksum); - - raw[raw_len++] = '#'; - raw[raw_len++] = checksum_str[0]; - raw[raw_len++] = checksum_str[1]; - - char ack; - do { - top->log->print(LOG_DEBUG, "Sending %.*s\n", raw_len, raw); - - if (::send(socket_client, raw, raw_len, 0) != raw_len) { - free(raw); - top->log->print(LOG_ERROR, "Unable to send data to client\n"); - return false; - } - - ret = recv(socket_client, &ack, 1, 0); - if((ret == -1 && errno != EWOULDBLOCK) || (ret == 0)) { - free(raw); - top->log->print(LOG_ERROR, "RSP: Error receiving\n"); - return false; - } - - if(ret == -1 && errno == EWOULDBLOCK) { - // no data available - continue; - } - - } while (ack != '+'); - - free(raw); - return true; -} - -bool Rsp::send_str(int socket_client, const char* data) -{ - return this->send(socket_client, data, strlen(data)); -} - -void Rsp::client_routine(int socket_client) -{ - while(1) - { - char pkt[PACKET_MAX_LEN]; - size_t len; - - fd_set rfds; - struct timeval tv; - - while (this->get_packet(socket_client, pkt, &len)) { - top->log->print(LOG_DEBUG, "Received $%.*s\n", len, pkt); - if (!this->decode(socket_client, pkt, len)) { - return; - } - } - } -} - -void Rsp::listener_routine() -{ - while(1) - { - int socket_client; - - if((socket_client = accept(socket_in, NULL, NULL)) == -1) - { - if(errno == EAGAIN) - continue; - - top->log->print(LOG_ERROR, "Unable to accept connection: %s\n", strerror(errno)); - return; - } - - top->log->print(LOG_INFO, "RSP: Client connected!\n"); - - std::thread *thread = new std::thread(&Rsp::client_routine, this, socket_client); - - } -} - -void Rsp::close(int kill) -{ - listener_thread->join(); -} - -bool -Rsp::open() { - struct sockaddr_in addr; - int yes = 1; - - addr.sin_family = AF_INET; - addr.sin_port = htons(socket_port); - addr.sin_addr.s_addr = INADDR_ANY; - memset(addr.sin_zero, '\0', sizeof(addr.sin_zero)); - - socket_in = socket(PF_INET, SOCK_STREAM, 0); - if(socket_in < 0) - { - top->log->print(LOG_ERROR, "Unable to create comm socket: %s\n", strerror(errno)); - return false; - } - - if(setsockopt(socket_in, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { - top->log->print(LOG_ERROR, "Unable to setsockopt on the socket: %s\n", strerror(errno)); - return false; - } - - if(bind(socket_in, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - top->log->print(LOG_ERROR, "Unable to bind the socket: %s\n", strerror(errno)); - return false; - } - - if(listen(socket_in, 1) == -1) { - top->log->print(LOG_ERROR, "Unable to listen: %s\n", strerror(errno)); - return false; - } - - this->top->target->halt(); - - listener_thread = new std::thread(&Rsp::listener_routine, this); - - top->log->print(LOG_INFO, "RSP server opened on port %d\n", socket_port); - - return true; -} - - - -bool Rsp::bp_insert(int socket_client, char* data, size_t len) -{ - enum mp_type type; - uint32_t addr; - uint32_t data_bp; - int bp_len; - - if (3 != sscanf(data, "Z%1d,%x,%1d", (int *)&type, &addr, &bp_len)) { - top->log->print(LOG_ERROR, "Could not get three arguments\n"); - return false; - } - - if (type != BP_MEMORY) { - top->log->print(LOG_ERROR, "ERROR: Not a memory bp\n"); - this->send_str(socket_client, ""); - return false; - } - - top->bkp->insert(addr); - - return this->send_str(socket_client, "OK"); -} - - - -bool Rsp::bp_remove(int socket_client, char* data, size_t len) -{ - enum mp_type type; - uint32_t addr; - uint32_t ppc; - int bp_len; - Target_core *core; - - core = this->top->target->get_thread(thread_sel); - - if (3 != sscanf(data, "z%1d,%x,%1d", (int *)&type, &addr, &bp_len)) { - top->log->print(LOG_ERROR, "Could not get three arguments\n"); - return false; - } - - if (type != BP_MEMORY) { - top->log->print(LOG_ERROR, "Not a memory bp\n"); - return false; - } - - top->bkp->remove(addr); - - // check if we are currently on this bp that is removed - core->read_ppc(&ppc); - - if (addr == ppc) { - core->write(DBG_NPC_REG, ppc); // re-execute this instruction - } - - return this->send_str(socket_client, "OK"); -} - - - -#if 0 - -Rsp::Rsp(int socket_port, MemIF* mem, LogIF *log, std::list list_dbgif, std::list list_dbg_cluster_ifs, BreakPoints* bp, DbgIF *main_if) { - socket_port = socket_port; - m_mem = mem; - m_dbgifs = list_dbgif; - m_dbg_cluster_ifs = list_dbg_cluster_ifs; - m_bp = bp; - this->log = log; - - // select one dbg if at random - if (m_dbgifs.size() == 0) { - top->log->print(LOG_ERROR, "No debug interface available! Exiting now\n"); - exit(1); - } - - if (main_if == NULL) main_if = m_dbgifs.front(); - - m_thread_init = main_if->get_thread_id(); - thread_sel = m_thread_init; -} - -void -Rsp::close() { - m_bp->clear(); - ::close(socket_in); -} - -void -Rsp::resumeCoresPrepare(DbgIF *dbgif, bool step) -{ - top->log->print(LOG_DEBUG, "Preparing core to resume (step: %d)\n", step); - - // now let's handle software breakpoints - uint32_t ppc; - dbgif->read_ppc(&ppc); - - // if there is a breakpoint at this address, let's remove it and single-step over it - bool hasStepped = false; - - if (m_bp->at_addr(ppc)) { - - top->log->print(LOG_DEBUG, "Core is stopped on a breakpoint, stepping to go over (addr: 0x%x)\n", ppc); - - m_bp->disable(ppc); - dbgif->write(DBG_NPC_REG, ppc); // re-execute this instruction - dbgif->write(DBG_CTRL_REG, 0x1); // single-step - while (1) { - uint32_t value; - dbgif->read(DBG_CTRL_REG, &value); - if ((value >> 16) & 1) break; - } - m_bp->enable(ppc); - hasStepped = true; - } - - dbgif->set_step_mode(step && !hasStepped); -} - -void -Rsp::resumeCores() { - for (std::list::iterator it = m_dbg_cluster_ifs.begin(); it != m_dbg_cluster_ifs.end(); it++) { - DbgIF_cluster *cluster = (DbgIF_cluster *)*it; - cluster->resume(); - } -} - -DbgIF* -Rsp::get_dbgif(int thread_id) { - for (std::list::iterator it = m_dbgifs.begin(); it != m_dbgifs.end(); it++) { - if ((*it)->get_thread_id() == thread_id) - return *it; - } - - return NULL; -} - -#endif \ No newline at end of file diff --git a/tools/pulp-debug-bridge/src/gdb-server/target.cpp b/tools/pulp-debug-bridge/src/gdb-server/target.cpp deleted file mode 100644 index 936f4ea7..00000000 --- a/tools/pulp-debug-bridge/src/gdb-server/target.cpp +++ /dev/null @@ -1,741 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - */ - -#include "gdb-server/gdb-server.hpp" -#include - - -class Target_cache -{ -public: - virtual void flush() { } -}; - -class Target_cluster_cache : public Target_cache -{ -public: - Target_cluster_cache(Gdb_server *top, uint32_t addr); - void flush(); -private: - Gdb_server *top; - uint32_t addr; -}; - - - -class Target_fc_cache : public Target_cache -{ -public: - Target_fc_cache(Gdb_server *top, uint32_t addr); - void flush(); -private: - Gdb_server *top; - uint32_t addr; -}; - - - - - -class Target_cluster_power -{ -public: - virtual bool is_on() { return true; } -}; - - - -class Target_cluster_power_bypass : public Target_cluster_power -{ -public: - Target_cluster_power_bypass(Gdb_server *top, uint32_t reg_addr, int bit); - bool is_on(); - -private: - Gdb_server *top; - uint32_t reg_addr; - int bit; -}; - - - - -class Target_cluster_ctrl -{ -public: - virtual bool init() {} -}; - - - -class Target_cluster_ctrl_xtrigger : public Target_cluster_ctrl -{ -public: - Target_cluster_ctrl_xtrigger(Gdb_server *top, uint32_t cluster_ctrl_addr); - bool init(); - -private: - Gdb_server *top; - uint32_t cluster_ctrl_addr; -}; - - -Target_cluster_cache::Target_cluster_cache(Gdb_server *top, uint32_t addr) -: top(top), addr(addr) -{ - -} - -void Target_cluster_cache::flush() -{ - this->top->log->debug("Flushing cluster cache (addr: 0x%x)\n", addr); - - uint32_t data = 0xFFFFFFFF; - top->cable->access(true, addr + 0x04, 4, (char*)&data); -} - - - -Target_fc_cache::Target_fc_cache(Gdb_server *top, uint32_t addr) -: top(top), addr(addr) -{ - -} - -void Target_fc_cache::flush() -{ - this->top->log->debug("Flushing FC cache (addr: 0x%x)\n", addr); - - uint32_t data = 0xFFFFFFFF; - top->cable->access(true, addr + 0x04, 4, (char*)&data); -} - - -Target_cluster_ctrl_xtrigger::Target_cluster_ctrl_xtrigger(Gdb_server *top, uint32_t cluster_ctrl_addr) -: top(top), cluster_ctrl_addr(cluster_ctrl_addr) -{ - -} - - - -bool Target_cluster_ctrl_xtrigger::init() -{ - uint32_t info; - // set all-stop mode, so that all cores go to debug when one enters debug mode - info = 0xFFFFFFFF; - top->cable->access(true, cluster_ctrl_addr + 0x000038, 4, (char*)&info); -} - - - -Target_cluster_power_bypass::Target_cluster_power_bypass(Gdb_server *top, uint32_t reg_addr, int bit) -: top(top), reg_addr(reg_addr), bit(bit) -{ -} - -bool Target_cluster_power_bypass::is_on() -{ - uint32_t info; - top->cable->access(false, reg_addr, 4, (char*)&info); - return (info >> bit) & 1; -} - - -class Target_cluster_common -{ - friend class Target_cluster; - friend class Target_fc; - -public: - Target_cluster_common(js::config *config, Gdb_server *top, uint32_t cluster_addr, uint32_t xtrigger_addr, int cluster_id); - int get_nb_core() { return nb_core; } - Target_core *get_core(int i) { return cores[i]; } - void update_power(); - void set_power(bool is_on); - void resume(); - void halt(); - void flush(); - -protected: - Gdb_server *top; - std::vector cores; - bool is_on = false; - Target_cluster_power *power; - Target_cluster_ctrl *ctrl; - int nb_on_cores = 0; - int nb_core = 0; - int cluster_id; - uint32_t cluster_addr; - uint32_t xtrigger_addr; - Target_cache *cache = NULL; -}; - -class Target_cluster : public Target_cluster_common -{ -public: - Target_cluster(js::config *system_config, js::config *config, Gdb_server *top, uint32_t cluster_base, uint32_t xtrigger_base, int cluster_id); -}; - -class Target_fc : public Target_cluster_common -{ -public: - Target_fc(js::config *config, Gdb_server *top, uint32_t fc_dbg_base, uint32_t fc_cache_base, int cluster_id); -}; - - - -Target_core::Target_core(Gdb_server *top, uint32_t dbg_unit_addr, int cluster_id, int core_id) -: top(top), dbg_unit_addr(dbg_unit_addr), cluster_id(cluster_id), core_id(core_id) -{ - top->log->print(LOG_DEBUG, "Instantiated core\n"); - this->thread_id = first_free_thread_id++; -} - - - -void Target_core::flush() -{ - this->top->log->debug("Flushing core cache (cluster: %d, core: %d)\n", cluster_id, core_id); - - // Write back the value of NPC so that it triggers a flush of the prefetch buffer - uint32_t npc; - this->read(DBG_NPC_REG, &npc); - this->write(DBG_NPC_REG, npc); -} - - - -void Target_core::read_ppc(uint32_t *ppc) -{ - if (!ppc_is_cached) { - read(DBG_PPC_REG, &ppc_cached); - ppc_is_cached = true; - } - *ppc = ppc_cached; -} - - - -bool Target_core::gpr_read_all(uint32_t *data) -{ - if (!is_on) return false; - this->top->log->debug("Reading all registers (cluster: %d, core: %d)\n", cluster_id, core_id); - - // Write back the valu - return top->cable->access(false, dbg_unit_addr + 0x0400, 32 * 4, (char*)data); -} - - - -bool Target_core::gpr_read(unsigned int i, uint32_t *data) -{ - if (!is_on) return false; - return this->read(0x0400 + i * 4, data); -} - - - -bool Target_core::gpr_write(unsigned int i, uint32_t data) -{ - if (!is_on) return false; - return this->write(0x0400 + i * 4, data); -} - - - -void Target_core::commit_resume() -{ - this->stopped = false; - - if (!this->is_on) return; - - this->ppc_is_cached = false; - this->commit_step_mode(); - this->write(DBG_HIT_REG, 0); -} - - - -void Target_core::set_power(bool is_on) -{ - if (is_on != this->is_on) - { - this->is_on = is_on; - if (is_on) { - top->log->print(LOG_DEBUG, "Setting-on core\n"); - is_on = true; - // let's discover core id and cluster id - this->stop(); - csr_read(0xF14, &hartid); - - cluster_id = hartid >> 5; - core_id = hartid & 0x1f; - - top->log->print(LOG_DEBUG, "Found a core with id %X (cluster: %d, core: %d)\n", hartid, cluster_id, core_id); - this->write(DBG_IE_REG, 1<<3); - if (!stopped) resume(); - } else { - top->log->print(LOG_DEBUG, "Setting-off core (cluster: %d, core: %d)\n", cluster_id, core_id); - is_on = false; - } - } -} - -bool Target_core::read(uint32_t addr, uint32_t* rdata) -{ - if (!is_on) return false; - top->log->print(LOG_DEBUG, "Reading register (addr: 0x%x)\n", dbg_unit_addr + addr); - return top->cable->access(false, dbg_unit_addr + addr, 4, (char*)rdata); -} - - - -bool Target_core::write(uint32_t addr, uint32_t wdata) -{ - if (!is_on) return false; - top->log->print(LOG_DEBUG, "Writing register (addr: 0x%x, value: 0x%x)\n", dbg_unit_addr + addr, wdata); - return top->cable->access(true, dbg_unit_addr + addr, 4, (char*)&wdata); -} - - - -bool Target_core::csr_read(unsigned int i, uint32_t *data) -{ - if (!is_on) return false; - return this->read(0x4000 + i * 4, data); -} - - - -bool Target_core::is_stopped() { - if (!is_on) return false; - - uint32_t data; - if (!this->read(DBG_CTRL_REG, &data)) { - fprintf(stderr, "debug_is_stopped: Reading from CTRL reg failed\n"); - return false; - } - - this->stopped = data & 0x10000; - - top->log->debug("Checking core status (cluster: %d, core: %d, stopped: %d)\n", cluster_id, core_id, this->stopped); - - return this->stopped; -} - - -bool Target_core::stop() -{ - if (!is_on) return false; - if (this->stopped) return false; - - this->top->log->debug("Halting core (cluster: %d, core: %d, is_on: %d)\n", cluster_id, core_id, is_on); - uint32_t data; - if (!this->read(DBG_CTRL_REG, &data)) { - fprintf(stderr, "debug_is_stopped: Reading from CTRL reg failed\n"); - return false; - } - - data |= 0x1 << 16; - return this->write(DBG_CTRL_REG, data); -} - - - -bool Target_core::halt() -{ - //stopped = true; - return stop(); -} - - - -void Target_core::set_step_mode(bool new_step) -{ - this->top->log->debug("Setting step mode (cluster: %d, core: %d, step: %d, new_step: %d)\n", cluster_id, core_id, step, new_step); - - if (new_step != step) { - this->step = new_step; - this->commit_step = true; - } -} - - -void Target_core::commit_step_mode() -{ - if (!is_on) return; - - if (commit_step) { - this->top->log->debug("Committing step mode (cluster: %d, core: %d, step: %d)\n", cluster_id, core_id, step); - this->write(DBG_CTRL_REG, (1<<16) | step); - this->commit_step = false; - } -} - - - -void Target_core::prepare_resume(bool step) -{ - - if (!is_on) return; - - top->log->debug("Preparing core to resume (step: %d)\n", step); - - // now let's handle software breakpoints - uint32_t ppc; - this->read_ppc(&ppc); - - // if there is a breakpoint at this address, let's remove it and single-step over it - bool has_stepped = false; - - if (this->top->bkp->at_addr(ppc)) { - - top->log->debug("Core is stopped on a breakpoint, stepping to go over (addr: 0x%x)\n", ppc); - - this->top->bkp->disable(ppc); - this->write(DBG_NPC_REG, ppc); // re-execute this instruction - this->write(DBG_CTRL_REG, 0x1); // single-step - while (1) { - uint32_t value; - this->read(DBG_CTRL_REG, &value); - if ((value >> 16) & 1) break; - } - this->top->bkp->enable(ppc); - has_stepped = true; - } - - this->set_step_mode(step && !has_stepped); -} - - - -void Target_core::resume() -{ - this->stopped = false; - - if (!is_on) return; - - this->top->log->debug("Resuming core and committing step mode (cluster: %d, core: %d, step: %d)\n", cluster_id, core_id, step); - - // clear hit register, has to be done before CTRL - this->write(DBG_HIT_REG, 0); - - this->write(DBG_CTRL_REG, step); - - this->commit_step = false; - this->ppc_is_cached = false; -} - - - -Target_cluster_common::Target_cluster_common(js::config *config, Gdb_server *top, uint32_t cluster_addr, uint32_t xtrigger_addr, int cluster_id) -: top(top), cluster_id(cluster_id), cluster_addr(cluster_addr), xtrigger_addr(xtrigger_addr) -{ -} - - - -Target_cluster::Target_cluster(js::config *system_config, js::config *config, Gdb_server *top, uint32_t cluster_base, uint32_t xtrigger_addr, int cluster_id) -: Target_cluster_common(config, top, cluster_base, xtrigger_addr, cluster_id) -{ - int nb_pe = config->get("nb_pe")->get_int(); - for (int i=0; iget("**/apb_soc_ctrl/regmap/power/bypass"); - if (bypass_config) - { - uint32_t addr = system_config->get("**/apb_soc_ctrl/base")->get_int() + - bypass_config->get("offset")->get_int(); - int bit = bypass_config->get("content/dbg1/bit")->get_int(); - - // Case where there is an apb soc ctrl register which tells if the cluster is on - power = new Target_cluster_power_bypass(top, addr, bit); - } - else - { - // Otherwise, the cluster will always be on - power = new Target_cluster_power(); - } - - ctrl = new Target_cluster_ctrl_xtrigger(top, cluster_base + 0x00200000); - - cache = new Target_cluster_cache(top, cluster_base + 0x00201400); - - this->update_power(); -} - - - -Target_fc::Target_fc(js::config *config, Gdb_server *top, uint32_t fc_dbg_base, uint32_t fc_cache_base, int cluster_id) -: Target_cluster_common(config, top, fc_dbg_base, -1, cluster_id) -{ - Target_core *core = new Target_core(top, fc_dbg_base, cluster_id, 0); - cores.push_back(core); - nb_core++; - - // the FC will always be on - power = new Target_cluster_power(); - - ctrl = new Target_cluster_ctrl(); - - if (fc_cache_base != -1) - cache = new Target_fc_cache(top, fc_cache_base); - - this->update_power(); -} - - - -void Target_cluster_common::flush() -{ - if (!is_on) return; - - this->top->log->debug("Flushing cluster caches (cluster: %d)\n", cluster_id); - - if (this->cache) - this->cache->flush(); - - for (auto &core: cores) - { - core->flush(); - } -} - - - -void Target_cluster_common::resume() -{ - this->top->log->debug("Resuming cluster (cluster: %d)\n", cluster_id); - - if (xtrigger_addr != -1) { - - // This cluster is a one with the cross-trigger matrix, use it to resume all cores - // As the step mode is cached and committed when writing to the individual core - // debug register, we have to commit it now before resuming the core through the - // global register - for (auto &core: cores) { - core->commit_resume(); - } - - if (is_on) { - this->top->log->debug("Resuming cluster through global register (cluster: %d)\n", cluster_id); - uint32_t info = 0xFFFFFFFF; - this->top->cable->access(true, xtrigger_addr + 0x00200000 + 0x28, 4, (char*)&info); - } - } else { - // Otherwise, just resume them individually - for (auto &core: cores) { - core->resume(); - } - } -} - - - -void Target_cluster_common::update_power() -{ - set_power(power->is_on()); -} - - -void Target_cluster_common::set_power(bool is_on) -{ - this->top->log->debug("Set cluster power (cluster: %d, is_on: %d)\n", cluster_id, is_on); - - if (is_on != this->is_on) { - this->is_on = is_on; - - ctrl->init(); - } - - if (is_on && nb_on_cores != nb_core) - { - for(auto const& core: cores) - { - core->set_power(is_on); - } - } else { - nb_on_cores = 0; - } -} - - - -void Target_cluster_common::halt() -{ - this->top->log->debug("Halting cluster (cluster: %d)\n", cluster_id); - // Either the core is alone (FC) or the cluster is using a cross-trigger matrix to stop all cores - // thus only stop the first one - cores.front()->halt(); -} - - -#if 0 -void Target_cluster::set_power(bool is_on) -{ - if (is_on != this->is_on) { - this->is_on = is_on; - - if (is_on && base_addr != -1) { - uint32_t info; - // set all-stop mode, so that all cores go to debug when one enters debug mode - info = 0xFFFFFFFF; - m_mem->access(1, base_addr + 0x000038, 4, (char*)&info); - } - } - - if (is_on && nb_on_cores != itfs.size()) { - uint32_t info = -1; - if (base_addr != -1) { - m_mem->access(0, base_addr + 0x000008, 4, (char*)&info); - } - int i = 0; - for (std::list::iterator it = itfs.begin(); it != itfs.end(); it++, i++) { - if ((info >> i) & 1) { - (*it)->set_power(is_on); - } - } - } else { - nb_on_cores = 0; - } -} -#endif - - - -Target::Target(Gdb_server *top) -: top(top) -{ - js::config *config = top->config; - - js::config *fc_config = config->get("**/soc/fc"); - if (fc_config != NULL) - { - unsigned int fc_dbg_addr = config->get("**/fc_dbg_unit/base")->get_int(); - js::config *cache_config = config->get("**/fc_icache/base"); - unsigned int fc_icache_addr = -1; - if (cache_config) - fc_icache_addr = cache_config->get_int(); - - Target_fc *cluster = new Target_fc(fc_config, top, fc_dbg_addr, fc_icache_addr, fc_config->get("cluster_id")->get_int()); - - clusters.push_back(cluster); - Target_core *core = cluster->get_core(0); - cores.push_back(core); - cores_from_threadid[core->get_thread_id()] = core; - } - - js::config *cluster_config = config->get("**/soc/cluster"); - if (cluster_config != NULL) - { - int nb_clusters = config->get("**/nb_cluster")->get_int(); - for (int i=0; iget("**/cluster/base"); - if (base_config != NULL) - cluster_base = base_config->get_int(); - - Target_cluster *cluster = new Target_cluster(config, cluster_config, top, cluster_base + 0x400000 * i, cluster_base + 0x400000 * i, i); - - clusters.push_back(cluster); - for (int j=0; jget_nb_core(); j++) - { - Target_core *core = cluster->get_core(j); - cores.push_back(core); - cores_from_threadid[core->get_thread_id()] = core; - } - } - } -} - - - -void Target::flush() -{ - for (auto &cluster : this->clusters) - { - cluster->flush(); - } -} - - - -void Target::resume_all() -{ - for (auto &cluster : this->clusters) - { - cluster->resume(); - } -} - - - -void Target::resume(bool step, int tid) -{ - if (tid == -1) - { - for (auto &thread : this->get_threads()) - { - thread->prepare_resume(step); - this->resume_all(); - } - } - else - { - auto *thread = this->get_thread(tid); - thread->prepare_resume(step); - thread->resume(); - } - -} - -bool Target::wait(int socket_client) -{ - printf("UNIMPLEMENTED AT %s %d\n", __FILE__, __LINE__); - exit(1); - return true; -} - - - -void Target::update_power() -{ - for (auto &cluster: clusters) { - cluster->update_power(); - } -} - - - -void Target::halt() -{ - for (auto &cluster: this->clusters) - { - cluster->halt(); - } -} \ No newline at end of file diff --git a/tools/pulp-debug-bridge/src/python_wrapper.cpp b/tools/pulp-debug-bridge/src/python_wrapper.cpp deleted file mode 100644 index 13b5e360..00000000 --- a/tools/pulp-debug-bridge/src/python_wrapper.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - */ - -#include -#include -#include -#include - -#include "json.hpp" -#include "cables/log.h" -#include "cables/adv_dbg_itf/adv_dbg_itf.hpp" -#include "cables/jtag-proxy/jtag-proxy.hpp" -#ifdef __USE_FTDI__ -#include "cables/ftdi/ftdi.hpp" -#endif -#include "gdb-server/gdb-server.hpp" - -using namespace std; - - -static int bridge_verbose = 0; -static const char *bridge_error = NULL; -static js::config *system_config = NULL; - -void Log::print(log_level_e level, const char *str, ...) -{ - if (bridge_verbose <= level) return; - va_list va; - va_start(va, str); - vprintf(str, va); - va_end(va); -} - - -void Log::user(const char *str, ...) -{ - if (bridge_verbose <= LOG_INFO) return; - va_list va; - va_start(va, str); - vprintf(str, va); - va_end(va); -} - -void Log::debug(const char *str, ...) -{ - if (bridge_verbose <= LOG_DEBUG) return; - va_list va; - va_start(va, str); - vprintf(str, va); - va_end(va); -} - -void Log::warning(const char *str, ...) -{ - if (bridge_verbose <= LOG_WARNING) return; - va_list va; - va_start(va, str); - vprintf(str, va); - va_end(va); -} - -void Log::error(const char *str, ...) -{ - char buff[1024]; - va_list va; - va_start(va, str); - vsnprintf(buff, 1024, str, va); - va_end(va); - bridge_error = strdup(buff); - - if (bridge_verbose <= LOG_ERROR) return; - va_start(va, str); - vprintf(str, va); - va_end(va); -} - -extern "C" void *cable_new(const char *config_string, const char *system_config_string) -{ - const char *cable_name = NULL; - js::config *config = NULL; - js::config *system_config = js::import_config_from_string(std::string(system_config_string)); - - if (config_string != NULL) - { - config = js::import_config_from_string(std::string(config_string)); - js::config *type_config = config->get("type"); - if (type_config != NULL) - { - cable_name = type_config->get_str().c_str(); - } - } - - if (cable_name == NULL) { - bridge_error = "No cable name specified"; - return NULL; - } - - if (strncmp(cable_name, "ftdi", 4) == 0) - { -#ifdef __USE_FTDI__ - Log *log = new Log(); - Ftdi::FTDIDeviceID id = Ftdi::Olimex; - if (strcmp(cable_name, "ftdi@digilent") == 0) id = Ftdi::Digilent; - if (strcmp(cable_name, "ftdi@generic") == 0) id = Ftdi::Generic; - Adv_dbg_itf *adu = new Adv_dbg_itf(system_config, config, log, new Ftdi(system_config, log, id)); - return (void *)static_cast(adu); -#else - fprintf(stderr, "Debug bridge has not been compiled with FTDI support\n"); - return NULL; -#endif - } - else if (strcmp(cable_name, "jtag-proxy") == 0) - { - Log *log = new Log(); - Adv_dbg_itf *adu = new Adv_dbg_itf(system_config, config, log, new Jtag_proxy(log)); - return (void *)static_cast(adu); - } - else - { - fprintf(stderr, "Unknown cable: %s\n", cable_name); - return NULL; - } - - return NULL; -} - -extern "C" void cable_write(void *cable, unsigned int addr, int size, const char *data) -{ - Adv_dbg_itf *adu = (Adv_dbg_itf *)cable; - adu->access(true, addr, size, (char *)data); -} - -extern "C" void cable_read(void *cable, unsigned int addr, int size, const char *data) -{ - Adv_dbg_itf *adu = (Adv_dbg_itf *)cable; - adu->access(false, addr, size, (char *)data); -} - -extern "C" void cable_reg_write(void *cable, unsigned int addr, const char *data, int device) -{ - Adv_dbg_itf *adu = (Adv_dbg_itf *)cable; - adu->reg_access(true, addr, (char *)data, device); -} - -extern "C" void cable_reg_read(void *cable, unsigned int addr, const char *data, int device) -{ - Adv_dbg_itf *adu = (Adv_dbg_itf *)cable; - adu->reg_access(false, addr, (char *)data, device); -} - -extern "C" void chip_reset(void *handler, bool active, int duration) -{ - Adv_dbg_itf *cable = (Adv_dbg_itf *)handler; - cable->chip_reset(active, duration); -} - -extern "C" void chip_config(void *handler, uint32_t value) -{ - Adv_dbg_itf *cable = (Adv_dbg_itf *)handler; - cable->chip_config(value); -} - -extern "C" void jtag_reset(void *handler, bool active) -{ - Adv_dbg_itf *cable = (Adv_dbg_itf *)handler; - cable->jtag_reset(active); -} - -extern "C" void jtag_soft_reset(void *handler) -{ - Adv_dbg_itf *cable = (Adv_dbg_itf *)handler; - cable->jtag_soft_reset(); -} - - -extern "C" bool cable_jtag_set_reg(void *handler, unsigned int reg, int width, unsigned int value, int ir_len) -{ - Cable *cable = (Cable *)handler; - return cable->jtag_set_reg(reg, width, value, ir_len); -} - -extern "C" bool cable_jtag_get_reg(void *handler, unsigned int reg, int width, unsigned int *out_value, unsigned int value, int ir_len) -{ - Cable *cable = (Cable *)handler; - return cable->jtag_get_reg(reg, width, out_value, value, ir_len); -} - -extern "C" void cable_lock(void *handler) -{ - Cable *cable = (Cable *)handler; - cable->lock(); -} - -extern "C" void cable_unlock(void *handler) -{ - Cable *cable = (Cable *)handler; - cable->unlock(); -} - - - - -static void init_sigint_handler(int s) { - raise(SIGTERM); -} - -extern "C" char * bridge_get_error() -{ - if (bridge_error == NULL) return strdup("unknown error"); - return strdup(bridge_error); -} - -extern "C" void bridge_init(const char *config_string, int verbose) -{ - system_config = js::import_config_from_string(std::string(config_string)); - bridge_verbose = verbose; - - // This should be the first C method called by python. - // As python is not catching SIGINT where we are in C world, we have to - // setup a sigint handler to exit in case control+C is hit. - signal (SIGINT, init_sigint_handler); - -} - - -extern "C" void *gdb_server_open(void *cable, int socket_port) -{ - return (void *)new Gdb_server(new Log(), (Cable *)cable, system_config, socket_port); -} - -extern "C" void gdb_server_close(void *arg, int kill) -{ - Gdb_server *server = (Gdb_server *)arg; - server->stop(kill); -} - - - - -#if 0 - -extern "C" void plt_exit(void *_bridge, bool status) -{ - Bridge *bridge = (Bridge *)_bridge; - bridge->getMemIF()->exit(status); -} - -extern "C" bool jtag_reset(void *_bridge) -{ - Bridge *bridge = (Bridge *)_bridge; - bridge->getJtagIF()->jtag_reset(); -} - -extern "C" bool jtag_idle(void *_bridge) -{ - Bridge *bridge = (Bridge *)_bridge; - bridge->getJtagIF()->jtag_idle(); -} - -extern "C" bool jtag_shift_ir(void *_bridge) -{ - Bridge *bridge = (Bridge *)_bridge; - bridge->getJtagIF()->jtag_shift_ir(); -} - -extern "C" bool jtag_shift_dr(void *_bridge) -{ - Bridge *bridge = (Bridge *)_bridge; - bridge->getJtagIF()->jtag_shift_dr(); -} - -extern "C" void jtag_shift(void *_bridge, int width, const char *datain, const char *dataout, int noex) -{ - Bridge *bridge = (Bridge *)_bridge; - bridge->getJtagIF()->jtag_shift(width, (unsigned char *)datain, (unsigned char *)dataout, noex); -} - -#endif diff --git a/tools/pulp-debug-bridge/src/reqloop.cpp b/tools/pulp-debug-bridge/src/reqloop.cpp deleted file mode 100644 index a651ce1c..00000000 --- a/tools/pulp-debug-bridge/src/reqloop.cpp +++ /dev/null @@ -1,1181 +0,0 @@ -/* - * Copyright (C) 2018 ETH Zurich and University of Bologna - * - * 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. - */ - -/* - * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) - */ - -#include -#include -#include "cable.hpp" -#include "cables/log.h" -#include "debug_bridge/debug_bridge.h" -#include -#include -#include -#include -#include -#include -#include - -#if defined(__USE_SDL__) -#include -#endif - -typedef enum -{ - TARGET_SYNC_FSM_STATE_INIT, - TARGET_SYNC_FSM_STATE_WAIT_INIT, - TARGET_SYNC_FSM_STATE_WAIT_AVAILABLE, - TARGET_SYNC_FSM_STATE_WAIT_REQUEST, - TARGET_SYNC_FSM_STATE_WAIT_ACK -} target_sync_fsm_state_e; - -class Target_req -{ -public: - bool done; - - hal_bridge_req_t target_req; -}; - -class Reqloop -{ -public: - Reqloop(Cable *cable, unsigned int debug_struct_addr); - void reqloop_routine(); - int stop(bool kill); - void activate(); - - void efuse_access(bool write, int id, uint32_t value, uint32_t mask); - int eeprom_access(uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size); - int flash_access(int32_t type, uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size); - int flash_erase(int32_t type, uint32_t itf, uint32_t cs, uint32_t addr, int32_t size); - int flash_erase_sector(int32_t type, uint32_t itf, uint32_t cs, uint32_t addr); - int flash_erase_chip(int32_t type, uint32_t itf, uint32_t cs); - void buffer_free(uint32_t addr, uint32_t size); - uint32_t buffer_alloc(uint32_t size); - -private: - void reply_req(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); - bool handle_req_connect(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); - bool handle_req_open(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); - bool handle_req_read(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); - bool handle_req_write(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); - bool handle_req_close(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); - bool handle_req_fb_open(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req); - bool handle_req_fb_update(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req); - bool handle_req_target_status_sync(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req); - bool handle_req_disconnect(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); - bool handle_req_reply(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); - bool handle_req(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); - void update_target_status(hal_debug_struct_t *debug_struct); - void wait_target_available(hal_debug_struct_t *debug_struct); - - void notif_target(hal_debug_struct_t *debug_struct); - void handle_target_req(hal_debug_struct_t *debug_struct, Target_req *target_req); - void handle_bridge_to_target_reqs(hal_debug_struct_t *debug_struct); - - bool wait_target_request(); - unsigned int get_target_state(); - void send_target_ack(); - void clear_target_ack(); - - Log *log; - std::thread *thread; - Cable *cable; - bool end = false; - unsigned int debug_struct_addr; - int status = 0; - bool connected = false; // Set to true once the applicatoin has sent the connect request - bool target_connected; - - hal_target_state_t target; - - std::queue target_reqs; - - std::mutex mutex; - std::condition_variable cond; - - target_sync_fsm_state_e target_sync_fsm_state; - unsigned int jtag_val; - - hal_debug_struct_t *debug_struct = NULL; - - bool target_jtag_sync; - - int confreg_instr; -}; - -class Framebuffer -{ -public: - Framebuffer(Cable *cable, std::string name, int width, int height, int format); - void update(uint32_t addr, int posx, int posy, int width, int height); - bool open(); - -private: - void fb_routine(); - - std::string name; - int width; - int height; - int format; - int pixel_size = 1; - Cable *cable; - std::thread *thread; - uint32_t *pixels; -#if defined(__USE_SDL__) - SDL_Surface *screen; - SDL_Texture * texture; - SDL_Renderer *renderer; - SDL_Window *window; -#endif -}; - -Framebuffer::Framebuffer(Cable *cable, std::string name, int width, int height, int format) -: name(name), width(width), height(height), format(format), cable(cable) -{ -} - -void Framebuffer::fb_routine() -{ -#if defined(__USE_SDL__) - bool quit = false; - SDL_Event event; - - while (!quit) - { - SDL_WaitEvent(&event); - switch (event.type) - { - case SDL_QUIT: - quit = true; - break; - } - } - - SDL_DestroyWindow(window); - SDL_Quit(); -#endif -} - - -bool Framebuffer::open() -{ -#if defined(__USE_SDL__) - - if (format == HAL_BRIDGE_REQ_FB_FORMAT_GRAY) - { - pixel_size = 1; - } - else if (format == HAL_BRIDGE_REQ_FB_FORMAT_RGB) - { - pixel_size = 4; - } - else if (format == HAL_BRIDGE_REQ_FB_FORMAT_RAW) - { - pixel_size = 1; - } - else - { - printf("Unsupported format: %d\n", format); - } - - pixels = new uint32_t[width*height]; - SDL_Init(SDL_INIT_VIDEO); - - window = SDL_CreateWindow(name.c_str(), - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 0); - - renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - - texture = SDL_CreateTexture(renderer, - SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height); - - memset(pixels, 255, width * height * sizeof(Uint32)); - - SDL_UpdateTexture(texture, NULL, pixels, width * sizeof(Uint32)); - - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); - - thread = new std::thread(&Framebuffer::fb_routine, this); - return true; - -#else - printf("Trying to open framebuffer while bridge has not been compiled with SDL support\n"); - return false; -#endif -} - -void Framebuffer::update(uint32_t addr, int posx, int posy, int width, int height) -{ -#if defined(__USE_SDL__) - - if (posx == -1) - { - posx = posy = 0; - width = this->width; - height = this->height; - } - - int size = width*height*pixel_size; - - if (this->format == HAL_BRIDGE_REQ_FB_FORMAT_GRAY) - { - uint8_t buffer[size]; - this->cable->access(false, addr, size, (char*)buffer); - -#if 0 - static int count = 0; - char name[64]; - sprintf(name, "fb%d", count++); - FILE *file = fopen(name, "w"); - fprintf(file, "P5\n%d %d\n255\n", this->width, this->height); - fwrite(buffer, 1, size, file); - fclose(file); -#endif - - for (int j=0; jwidth + i + posx] = (0xff << 24) | (value << 16) | (value << 8) | value; - } - } - } - else if (this->format == HAL_BRIDGE_REQ_FB_FORMAT_RAW) - { - uint8_t buffer[size]; - this->cable->access(false, addr, size, (char*)buffer); - - for (int j=0; jwidth + i + posx] = (0xff << 24) | (value << shift); - } - } - } - else - { - uint32_t buffer[size]; - this->cable->access(false, addr, size*4, (char*)buffer); - - for (int j=0; jwidth + i + posx] = (0xff << 24) | value; - } - } - } - - - SDL_UpdateTexture(texture, NULL, pixels, this->width * sizeof(Uint32)); - - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); -#endif -} - - - -int Reqloop::stop(bool kill) -{ - if (kill) end = true; - thread->join(); - return status; -} - -void Reqloop::activate() -{ - if (this->debug_struct == NULL) - { - cable->access(false, debug_struct_addr, 4, (char*)&this->debug_struct); - - if (this->debug_struct != NULL) - { - uint32_t protocol_version; - cable->access(false, (unsigned int)(long)&this->debug_struct->protocol_version, 4, (char*)&protocol_version); - - if (protocol_version != PROTOCOL_VERSION_4) - { - this->log->error("Protocol version mismatch between bridge and runtime (bridge: %d, runtime: %d)\n", PROTOCOL_VERSION_4, protocol_version); - throw std::logic_error("Unable to connect to runtime"); - } - - int32_t is_connected; - this->cable->access(false, (unsigned int)(long)&this->debug_struct->target.connected, 4, (char*)&is_connected); - this->connected = is_connected; - - // The binary has just started, we need to tell him we want to watch for requests - unsigned int value = 0; - - uint32_t connected = 1; - cable->access(true, (unsigned int)(long)&this->debug_struct->bridge.connected, 4, (char*)&connected); - cable->access(true, (unsigned int)(long)&this->debug_struct->use_internal_printf, 4, (char*)&value); - } - } -} - - - -void Reqloop::notif_target(hal_debug_struct_t *debug_struct) -{ - uint32_t notif_req_addr; - uint32_t notif_req_value; - cable->access(false, (unsigned int)(long)&debug_struct->notif_req_addr, 4, (char*)¬if_req_addr); - cable->access(false, (unsigned int)(long)&debug_struct->notif_req_value, 4, (char*)¬if_req_value); - - cable->access(true, (unsigned int)(long)notif_req_addr, 4, (char*)¬if_req_value); -} - -void Reqloop::reply_req(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req) -{ - uint32_t value = 1; - this->cable->access(true, (unsigned int)(long)&target_req->done, sizeof(target_req->done), (char*)&value); - - this->notif_target(debug_struct); -} - -static int transpose_code(int code) -{ - int alt = 0; - - if ((code & 0x0) == 0x0) alt |= O_RDONLY; - if ((code & 0x1) == 0x1) alt |= O_WRONLY; - if ((code & 0x2) == 0x2) alt |= O_RDWR; - if ((code & 0x8) == 0x8) alt |= O_APPEND; - if ((code & 0x200) == 0x200) alt |= O_CREAT; - if ((code & 0x400) == 0x400) alt |= O_TRUNC; - if ((code & 0x800) == 0x800) alt |= O_EXCL; - if ((code & 0x2000) == 0x2000) alt |= O_SYNC; - if ((code & 0x4000) == 0x4000) alt |= O_NONBLOCK; - if ((code & 0x8000) == 0x8000) alt |= O_NOCTTY; - - return alt; -} - -bool Reqloop::handle_req_connect(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ - this->connected = true; - this->reply_req(debug_struct, target_req, req); - return false; -} - -bool Reqloop::handle_req_reply(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ - Target_req *bridge_req = (Target_req *)req->bridge_data; - - this->mutex.lock(); - bridge_req->done = true; - this->notif_target(debug_struct); - this->cond.notify_all(); - - // Copy the target information to the local request in case some returned - // information are needed - memcpy((void *)&bridge_req->target_req, (void *)req, sizeof(hal_bridge_req_t)); - - this->mutex.unlock(); - return false; -} - -bool Reqloop::handle_req_disconnect(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ - this->connected = false; - this->reply_req(debug_struct, target_req, req); - return true; -} - -bool Reqloop::handle_req_open(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ - char name[req->open.name_len+1]; - cable->access(false, (unsigned int)(long)req->open.name, req->open.name_len+1, (char*)name); - - int res = open(name, req->open.flags, req->open.mode); - - cable->access(true, (unsigned int)(long)&target_req->open.retval, 4, (char*)&res); - - this->reply_req(debug_struct, target_req, req); - return false; -} - -bool Reqloop::handle_req_read(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ - char buffer[4096]; - int size = req->read.len; - char *ptr = (char *)(long)req->read.ptr; - int res = 0; - while (size) - { - int iter_size = size; - if (iter_size > 4096) - iter_size = 4096; - - iter_size = read(req->read.file, (void *)buffer, iter_size); - - if (iter_size <= 0) { - if (iter_size == -1 && res == 0) res = -1; - break; - } - - cable->access(true, (unsigned int)(long)ptr, iter_size, (char*)buffer); - - res += iter_size; - ptr += iter_size; - size -= iter_size; - } - - cable->access(true, (unsigned int)(long)&target_req->read.retval, 4, (char*)&res); - - this->reply_req(debug_struct, target_req, req); - return false; -} - -bool Reqloop::handle_req_write(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ - char buffer[4096]; - int size = req->write.len; - char *ptr = (char *)(long)req->write.ptr; - int res = 0; - while (size) - { - int iter_size = size; - if (iter_size > 4096) - iter_size = 4096; - - cable->access(false, (unsigned int)(long)ptr, iter_size, (char*)buffer); - - iter_size = write(req->write.file, (void *)buffer, iter_size); - - if (iter_size <= 0) - break; - - res += iter_size; - ptr += iter_size; - size -= iter_size; - } - - if (res == 0) - res = -1; - - cable->access(true, (unsigned int)(long)&target_req->write.retval, 4, (char*)&res); - - this->reply_req(debug_struct, target_req, req); - return false; -} - -bool Reqloop::handle_req_close(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ - int res = close(req->close.file); - cable->access(true, (unsigned int)(long)&target_req->write.retval, 4, (char*)&res); - this->reply_req(debug_struct, target_req, req); - return false; -} - -bool Reqloop::handle_req_fb_open(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ - char name[req->fb_open.name_len+1]; - cable->access(false, (unsigned int)(long)req->fb_open.name, req->fb_open.name_len+1, (char*)name); - - int res = 0; - Framebuffer *fb = new Framebuffer(cable, name, req->fb_open.width, req->fb_open.height, req->fb_open.format); - - - - if (!fb->open()) - { - res = -1; - delete fb; - fb = NULL; - } - - cable->access(true, (unsigned int)(long)&target_req->fb_open.screen, 8, (char*)&fb); - - this->reply_req(debug_struct, target_req, req); - return false; -} - -void Reqloop::update_target_status(hal_debug_struct_t *debug_struct) -{ - this->cable->access(false, (unsigned int)(long)&debug_struct->target, sizeof(this->target), (char *)&this->target); -} - -bool Reqloop::handle_req_target_status_sync(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ - this->update_target_status(debug_struct); - this->reply_req(debug_struct, target_req, req); - return false; -} - -bool Reqloop::handle_req_fb_update(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ -#if defined(__USE_SDL__) - Framebuffer *fb = (Framebuffer *)req->fb_update.screen; - - fb->update( - req->fb_update.addr, req->fb_update.posx, req->fb_update.posy, req->fb_update.width, req->fb_update.height - ); -#endif - - this->reply_req(debug_struct, target_req, req); - return false; -} - -bool Reqloop::handle_req(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) -{ - switch (req->type) - { - case HAL_BRIDGE_REQ_CONNECT: return this->handle_req_connect(debug_struct, req, target_req); - case HAL_BRIDGE_REQ_DISCONNECT: return this->handle_req_disconnect(debug_struct, req, target_req); - case HAL_BRIDGE_REQ_OPEN: return this->handle_req_open(debug_struct, req, target_req); - case HAL_BRIDGE_REQ_READ: return this->handle_req_read(debug_struct, req, target_req); - case HAL_BRIDGE_REQ_WRITE: return this->handle_req_write(debug_struct, req, target_req); - case HAL_BRIDGE_REQ_CLOSE: return this->handle_req_close(debug_struct, req, target_req); - case HAL_BRIDGE_REQ_FB_OPEN: return this->handle_req_fb_open(debug_struct, req, target_req); - case HAL_BRIDGE_REQ_FB_UPDATE: return this->handle_req_fb_update(debug_struct, req, target_req); - case HAL_BRIDGE_REQ_TARGET_STATUS_SYNC: return this->handle_req_target_status_sync(debug_struct, req, target_req); - case HAL_BRIDGE_REQ_REPLY: return this->handle_req_reply(debug_struct, req, target_req); - default: - this->log->print(LOG_ERROR, "Received unknown request from target (type: %d)\n", req->type); - } - return false; -} - - -unsigned int Reqloop::get_target_state() -{ - unsigned int value; - if (this->confreg_instr == 6) - { - this->cable->jtag_get_reg((6<<5)|(0x1f), 9, &value, this->jtag_val, 9); - value = (value >> 1) & 0xf; - } - else - { - this->cable->jtag_get_reg(7, 4, &value, this->jtag_val); - value &= 0xf; - } - return value >> 1; -} - -void Reqloop::send_target_ack() -{ - unsigned int value; - this->jtag_val = 0x7 << 1; - - if (this->confreg_instr == 6) - { - this->cable->jtag_set_reg((6<<5)|(0x1f), 9, 0x7 << 2, 9); - } - else - { - this->cable->jtag_set_reg(7, 4, 0x7 << 1); - } -} - -void Reqloop::clear_target_ack() -{ - unsigned int value; - this->jtag_val = 0x0 << 1; - - if (this->confreg_instr == 6) - { - this->cable->jtag_set_reg((6<<5)|(0x1f), 9, 0x0 << 2, 9); - } - else - { - this->cable->jtag_set_reg(7, 4, 0x0 << 1); - } -} - -bool Reqloop::wait_target_request() -{ - // In case the target does not support synchronization through the JTAG - // register, just consider that the target is always available. - if (!this->target_jtag_sync) - return true; - - switch (this->target_sync_fsm_state) - { - case TARGET_SYNC_FSM_STATE_INIT: { - //printf("STATE INIT\n"); - // This state is used in case the bridge is not yet connected to the - // target (e.g. if it boots from flash). - // Wait until the target becomes available and ask for the connection. - unsigned int state = this->get_target_state(); - if (state == 4) - { - //printf("RECEIVED CONNECTION REQ\n"); - // Target is asking for connection, acknowledge it and go to next step - this->send_target_ack(); - this->target_sync_fsm_state = TARGET_SYNC_FSM_STATE_WAIT_INIT; - } - return false; - } - - case TARGET_SYNC_FSM_STATE_WAIT_INIT: { - //printf("STATE WAIT INIT\n"); - // This state is used to wait for the target acknoledgment, after - // it asked for connection - unsigned int state = this->get_target_state(); - if (state != 4) - { - // Once the target acknowledged the connection, activate the bridge - // clear the acknowledge and wait for target availability - //printf("RECEIVED CONNECTION ACK\n"); - this->activate(); - this->clear_target_ack(); - this->target_sync_fsm_state = TARGET_SYNC_FSM_STATE_WAIT_REQUEST; - } - return true; - } - - case TARGET_SYNC_FSM_STATE_WAIT_AVAILABLE: { - //printf("STATE WAIT AVAILABLE\n"); - // We are in the state when the target cannot be accessed. - // Wait until we see something different from 0. - // We also need to clear the request acknowledgement to let the target - // know that we got it, in case we come from state - // TARGET_SYNC_FSM_STATE_WAIT_ACK. - this->clear_target_ack(); - unsigned int state = this->get_target_state(); - if (state == 0) - return false; - - //printf("TARGET AVAILABLE\n"); - this->target_sync_fsm_state = TARGET_SYNC_FSM_STATE_WAIT_REQUEST; - return false; - } - - case TARGET_SYNC_FSM_STATE_WAIT_REQUEST: { - //printf("STATE WAIT REQUEST\n"); - // The target became available, now wait for a request - unsigned int state = this->get_target_state(); - - if (state == 1) - { - // The target is still available with no request, stay in this state - return false; - } - - if (state == 2) - { - //printf("SHUTDOWN REQ\n"); - // The target is requesting a shutdown, acknowledge it and stop - // accessing the target - this->send_target_ack(); - this->target_sync_fsm_state = TARGET_SYNC_FSM_STATE_WAIT_ACK; - return false; - } - - //printf("OTHER REQ\n"); - // The target is requesting something else, just report true so that - // the caller now checks what is requested through JTAG and stay in - // this state. - return true; - } - - case TARGET_SYNC_FSM_STATE_WAIT_ACK: { - //printf("STATE WAIT ACK\n"); - // The chip is not accessible, just wait until it becomes available - // again - unsigned int state = this->get_target_state(); - if (state != 0) - return false; - - //printf("AVAILABLE AFTER SHUTDOWN\n"); - this->target_sync_fsm_state = TARGET_SYNC_FSM_STATE_WAIT_AVAILABLE; - return false; - } - } -} - - -void Reqloop::handle_target_req(hal_debug_struct_t *debug_struct, Target_req *target_req) -{ - // First get a request from the target - hal_bridge_req_t *req = NULL; - - this->cable->access(false, (unsigned int)(long)&debug_struct->first_bridge_free_req, 4, (char*)&req); - - if (req == NULL) - { - this->log->error("Unable to allocate bridge to target request"); - throw std::logic_error("Unable to allocate bridge to target request"); - } - - uint32_t next; - this->cable->access(false, (unsigned int)(long)&req->next, 4, (char*)&next); - this->cable->access(true, (unsigned int)(long)&debug_struct->first_bridge_free_req, 4, (char*)&next); - this->cable->access(true, (unsigned int)(long)req, sizeof(hal_bridge_req_t), (char*)&target_req->target_req); - this->cable->access(true, (unsigned int)(long)&req->bridge_data, sizeof(target_req), (char*)&target_req); - - - // Store it to the debug structure - this->cable->access(true, (unsigned int)(long)&debug_struct->target_req, 4, (char*)&req); - - // And notify the target so that it is processed - this->notif_target(debug_struct); -} - -void Reqloop::handle_bridge_to_target_reqs(hal_debug_struct_t *debug_struct) -{ - if (!this->connected) - return; - - while(this->target_reqs.size()) - { - // Runtime can only handle one request, first check if no request is already - // pushed. - uint32_t target_req; - this->cable->access(false, (unsigned int)(long)&debug_struct->target_req, 4, (char*)&target_req); - if (target_req) - break; - - this->mutex.lock(); - Target_req *bridge_target_req = this->target_reqs.front(); - this->target_reqs.pop(); - this->handle_target_req(debug_struct, bridge_target_req); - this->mutex.unlock(); - } -} - -void Reqloop::reqloop_routine() -{ - // In case the birdge is not yet connected, do extra init steps to - // connect once the target becomes available - this->target_sync_fsm_state = this->debug_struct ? TARGET_SYNC_FSM_STATE_WAIT_AVAILABLE : TARGET_SYNC_FSM_STATE_INIT; - - this->jtag_val = 0; - - if (debug_struct_addr) { - - // In case the debug struct pointer is found, iterate to receive IO requests - // from runtime - while(!end) - { - uint32_t value; - - // Wait until the target is available and has a request. - // This will poll the target through the JTAG register. - if (!this->wait_target_request()) - { - // If not, just wait a bit and retry - usleep(100); - continue; - } - - // First check if the application has exited - cable->access(false, (unsigned int)(long)&debug_struct->exit_status, 4, (char*)&value); - if (value >> 31) { - status = ((int)value << 1) >> 1; - printf("Detected end of application, exiting with status: %d\n", status); - return; - } - - // Check printf - // The target application should quickly dumps the characters, so we can loop on printf - // until we don't find anything - while(1) { - cable->access(false, (unsigned int)(long)&debug_struct->pending_putchar, 4, (char*)&value); - if (value == 0) break; - char buff[value+1]; - cable->access(false, (unsigned int)(long)&debug_struct->putc_buffer, value, (char*)buff); - unsigned int zero = 0; - cable->access(true, (unsigned int)(long)&debug_struct->pending_putchar, 4, (char*)&zero); - for (int i=0; iaccess(false, (unsigned int)(long)&debug_struct->first_bridge_req, 4, (char*)&first_bridge_req)) goto end; - - if (first_bridge_req == NULL) - break; - - hal_bridge_req_t req; - if (!this->cable->access(false, (unsigned int)(long)first_bridge_req, sizeof(hal_bridge_req_t), (char*)&req)) goto end; - - value = 1; - if (!cable->access(true, (unsigned int)(long)&first_bridge_req->popped, sizeof(first_bridge_req->popped), (char*)&value)) goto end; - if (!cable->access(true, (unsigned int)(long)&debug_struct->first_bridge_req, 4, (char*)&req.next)) goto end; - - if (this->handle_req(debug_struct, &req, first_bridge_req)) - return; - } - - // Handle bridge to target requests - this->handle_bridge_to_target_reqs(debug_struct); - - if (!this->target_jtag_sync) - { - // Small sleep to not poll too often - usleep(500); - } - } - } - else - { - log->warning("Trying to launch request loop (command reqloop) while no binary is provided\n"); - } - - return; - -end: - log->warning("Got access error in reqloop\n"); -} - -void Reqloop::efuse_access(bool write, int id, uint32_t value, uint32_t mask) -{ - Target_req *req = new Target_req(); - req->done = false; - - req->target_req.type = HAL_BRIDGE_TARGET_REQ_EFUSE_ACCESS; - req->target_req.efuse_access.is_write = write; - req->target_req.efuse_access.index = id; - req->target_req.efuse_access.value = value; - req->target_req.efuse_access.mask = mask; - - std::unique_lock lock(this->mutex); - - this->target_reqs.push(req); - - while(!req->done) - { - this->cond.wait(lock); - } - - lock.unlock(); -} - -int Reqloop::eeprom_access(uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size) -{ - Target_req *req = new Target_req(); - req->done = false; - - req->target_req.type = HAL_BRIDGE_TARGET_REQ_EEPROM_ACCESS; - req->target_req.eeprom_access.itf = itf; - req->target_req.eeprom_access.cs = cs; - req->target_req.eeprom_access.is_write = write; - req->target_req.eeprom_access.addr = addr; - req->target_req.eeprom_access.buffer = buffer; - req->target_req.eeprom_access.size = size; - - std::unique_lock lock(this->mutex); - this->target_reqs.push(req); - - while(!req->done) - { - this->cond.wait(lock); - } - - int retval = req->target_req.eeprom_access.retval; - - delete req; - - lock.unlock(); - - return retval; -} - -int Reqloop::flash_access(int type, uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size) -{ - Target_req *req = new Target_req(); - req->done = false; - - req->target_req.type = HAL_BRIDGE_TARGET_REQ_FLASH_ACCESS; - req->target_req.flash_access.type = type; - req->target_req.flash_access.itf = itf; - req->target_req.flash_access.cs = cs; - req->target_req.flash_access.is_write = write; - req->target_req.flash_access.addr = addr; - req->target_req.flash_access.buffer = buffer; - req->target_req.flash_access.size = size; - - std::unique_lock lock(this->mutex); - this->target_reqs.push(req); - - while(!req->done) - { - this->cond.wait(lock); - } - - int retval = req->target_req.flash_access.retval; - - delete req; - - lock.unlock(); - - return retval; -} - -int Reqloop::flash_erase(int type, uint32_t itf, uint32_t cs, uint32_t addr, int32_t size) -{ - Target_req *req = new Target_req(); - req->done = false; - - req->target_req.type = HAL_BRIDGE_TARGET_REQ_FLASH_ERASE; - req->target_req.flash_erase.type = type; - req->target_req.flash_erase.itf = itf; - req->target_req.flash_erase.cs = cs; - req->target_req.flash_erase.addr = addr; - req->target_req.flash_erase.size = size; - - std::unique_lock lock(this->mutex); - this->target_reqs.push(req); - - while(!req->done) - { - this->cond.wait(lock); - } - - int retval = req->target_req.flash_erase.retval; - - delete req; - - lock.unlock(); - - return retval; -} - -int Reqloop::flash_erase_sector(int type, uint32_t itf, uint32_t cs, uint32_t addr) -{ - Target_req *req = new Target_req(); - req->done = false; - - req->target_req.type = HAL_BRIDGE_TARGET_REQ_FLASH_ERASE_SECTOR; - req->target_req.flash_erase_sector.type = type; - req->target_req.flash_erase_sector.itf = itf; - req->target_req.flash_erase_sector.cs = cs; - req->target_req.flash_erase_sector.addr = addr; - - std::unique_lock lock(this->mutex); - this->target_reqs.push(req); - - while(!req->done) - { - this->cond.wait(lock); - } - - int retval = req->target_req.flash_erase_sector.retval; - - delete req; - - lock.unlock(); - - return retval; -} - -int Reqloop::flash_erase_chip(int type, uint32_t itf, uint32_t cs) -{ - Target_req *req = new Target_req(); - req->done = false; - - req->target_req.type = HAL_BRIDGE_TARGET_REQ_FLASH_ERASE_CHIP; - req->target_req.flash_erase_chip.type = type; - req->target_req.flash_erase_chip.itf = itf; - req->target_req.flash_erase_chip.cs = cs; - - std::unique_lock lock(this->mutex); - this->target_reqs.push(req); - - while(!req->done) - { - this->cond.wait(lock); - } - - int retval = req->target_req.flash_erase_chip.retval; - - delete req; - - lock.unlock(); - - return retval; -} - -void Reqloop::buffer_free(uint32_t addr, uint32_t size) -{ - Target_req *req = new Target_req(); - req->done = false; - - req->target_req.type = HAL_BRIDGE_TARGET_REQ_BUFFER_FREE; - req->target_req.buffer_free.size = size; - req->target_req.buffer_free.buffer = addr; - - std::unique_lock lock(this->mutex); - this->target_reqs.push(req); - - while(!req->done) - { - this->cond.wait(lock); - } - - delete req; - - lock.unlock(); -} - -uint32_t Reqloop::buffer_alloc(uint32_t size) -{ - Target_req *req = new Target_req(); - req->done = false; - - req->target_req.type = HAL_BRIDGE_TARGET_REQ_BUFFER_ALLOC; - req->target_req.buffer_alloc.size = size; - - std::unique_lock lock(this->mutex); - this->target_reqs.push(req); - - while(!req->done) - { - this->cond.wait(lock); - } - - uint32_t addr = req->target_req.buffer_alloc.buffer; - - delete req; - - lock.unlock(); - - return addr; -} - -Reqloop::Reqloop(Cable *cable, unsigned int debug_struct_addr) : cable(cable), debug_struct_addr(debug_struct_addr), target_connected(false) -{ - log = new Log(); - - js::config *config = cable->get_config(); - - this->target_jtag_sync = config->get_child_bool("**/debug_bridge/target_jtag_sync"); - - if (config->get("**/pulp_tap/confreg_instr") != NULL) - { - this->confreg_instr = config->get_int("**/pulp_tap/confreg_instr"); - } - else - { - this->confreg_instr = 7; - } - - std::string chip = config->get("**/chip/name")->get_str(); - - // Try to connect the bridge now before the execution is started so - // that the target sees the bridge as soon as it starts. - // Otherwise, the bridge will get connected later on when the target - // becomes available - this->activate(); - thread = new std::thread(&Reqloop::reqloop_routine, this); -} - -extern "C" void *bridge_reqloop_open(void *cable, unsigned int debug_struct_addr) -{ - return (void *)new Reqloop((Cable *)cable, debug_struct_addr); -} - -extern "C" void bridge_reqloop_close(void *arg, int kill) -{ - Reqloop *reqloop = (Reqloop *)arg; - reqloop->stop(kill); -} - -extern "C" void bridge_reqloop_efuse_access(void *arg, bool write, int id, uint32_t value, uint32_t mask) -{ - Reqloop *reqloop = (Reqloop *)arg; - reqloop->efuse_access(write, id, value, mask); -} - - -extern "C" int bridge_reqloop_eeprom_access(void *arg, uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size) -{ - Reqloop *reqloop = (Reqloop *)arg; - return reqloop->eeprom_access(itf, cs, write, addr, buffer, size); -} - - -extern "C" int bridge_reqloop_flash_access(void *arg, int type, uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size) -{ - Reqloop *reqloop = (Reqloop *)arg; - return reqloop->flash_access(type, itf, cs, write, addr, buffer, size); -} - - - -extern "C" int bridge_reqloop_flash_erase(void *arg, int type, uint32_t itf, uint32_t cs, uint32_t addr, int size) -{ - Reqloop *reqloop = (Reqloop *)arg; - return reqloop->flash_erase(type, itf, cs, addr, size); -} - - - -extern "C" int bridge_reqloop_flash_erase_sector(void *arg, int type, uint32_t itf, uint32_t cs, uint32_t addr) -{ - Reqloop *reqloop = (Reqloop *)arg; - return reqloop->flash_erase_sector(type, itf, cs, addr); -} - - - -extern "C" int bridge_reqloop_flash_erase_chip(void *arg, int type, uint32_t itf, uint32_t cs) -{ - Reqloop *reqloop = (Reqloop *)arg; - return reqloop->flash_erase_chip(type, itf, cs); -} - - -extern "C" uint32_t bridge_reqloop_buffer_alloc(void *arg, uint32_t size) -{ - Reqloop *reqloop = (Reqloop *)arg; - return reqloop->buffer_alloc(size); -} - - -extern "C" void bridge_reqloop_buffer_free(void *arg, uint32_t addr, uint32_t size) -{ - Reqloop *reqloop = (Reqloop *)arg; - reqloop->buffer_free(addr, size); -} - - From a7d1ea23a5000380d49b3e6fbb5d691251679e94 Mon Sep 17 00:00:00 2001 From: enrico Date: Wed, 21 Jul 2021 19:28:41 +0200 Subject: [PATCH 21/22] SDK upgrade with no runtime at compile time --- .github/workflows/regression-tests.yml | 7 +- Makefile | 5 +- rtos/pmsis/pmsis_api/LICENSE | 177 ++ rtos/pmsis/pmsis_api/docs/Makefile | 20 + rtos/pmsis/pmsis_api/docs/chips.rst | 21 + rtos/pmsis/pmsis_api/docs/conf.py | 66 + rtos/pmsis/pmsis_api/docs/dox.mk | 10 + rtos/pmsis/pmsis_api/docs/drivers.rst | 101 + rtos/pmsis/pmsis_api/docs/headers/groups.h | 13 + rtos/pmsis/pmsis_api/docs/index.rst | 19 + rtos/pmsis/pmsis_api/docs/mainpage.md | 13 + rtos/pmsis/pmsis_api/docs/make.bat | 35 + rtos/pmsis/pmsis_api/docs/pmsis.dxy.in | 2370 +++++++++++++++++ rtos/pmsis/pmsis_api/docs/requirements.txt | 3 + rtos/pmsis/pmsis_api/docs/rtos.rst | 18 + .../pmsis_api/include/pmsis/chips/default.h | 94 + .../pmsis_api/include/pmsis/chips/gap8/gpio.h | 121 + .../pmsis_api/include/pmsis/chips/gap8/pad.h | 254 ++ .../pmsis_api/include/pmsis/chips/gap8/perf.h | 134 + .../pmsis_api/include/pmsis/chips/gap8/pmu.h | 67 + .../pmsis_api/include/pmsis/chips/vega/gpio.h | 71 + .../pmsis_api/include/pmsis/chips/vega/pad.h} | 48 +- .../pmsis_api/include/pmsis/chips/vega/perf.h | 126 + .../include/pmsis/cluster/cl_malloc.h | 107 + .../include/pmsis/cluster/cl_pmsis_api.h} | 12 +- .../include/pmsis/cluster/cl_pmsis_types.h | 123 + .../cluster/cluster_sync/cl_synchronisation.h | 71 + .../cluster/cluster_sync/cl_to_fc_delegate.h | 193 ++ .../cluster/cluster_sync/fc_to_cl_delegate.h | 285 ++ .../pmsis/cluster/cluster_team/cl_team.h | 165 ++ .../include/pmsis/cluster/dma/cl_dma.h | 209 ++ .../pmsis/cluster/dma/cl_dma_decompressor.h | 112 + rtos/pmsis/pmsis_api/include/pmsis/device.h | 42 + .../pmsis_api/include/pmsis/drivers/aes.h | 144 + .../pmsis_api/include/pmsis/drivers/asrc.h | 184 ++ .../pmsis_api/include/pmsis/drivers/cpi.h | 220 ++ .../pmsis_api/include/pmsis/drivers/dmacpy.h | 138 + .../pmsis_api/include/pmsis/drivers/gpio.h | 384 +++ .../include/pmsis/drivers/hyperbus.h | 562 ++++ .../pmsis_api/include/pmsis/drivers/i2c.h | 310 +++ .../include/pmsis/drivers/i2c_slave.h | 218 ++ .../pmsis_api/include/pmsis/drivers/i2s.h | 776 ++++++ .../pmsis_api/include/pmsis/drivers/octospi.h | 594 +++++ .../pmsis_api/include/pmsis/drivers/pad.h | 150 ++ .../pmsis_api/include/pmsis/drivers/perf.h | 101 + .../pmsis_api/include/pmsis/drivers/pmu.h | 182 ++ .../pmsis_api/include/pmsis/drivers/pwm.h | 326 +++ .../pmsis_api/include/pmsis/drivers/rtc.h | 228 ++ .../pmsis_api/include/pmsis/drivers/spi.h | 449 ++++ .../pmsis_api/include/pmsis/drivers/uart.h | 457 ++++ rtos/pmsis/pmsis_api/include/pmsis/errno.h | 46 + rtos/pmsis/pmsis_api/include/pmsis/mem_slab.h | 91 + .../pmsis_api/include/pmsis/pmsis_types.h | 162 ++ .../pmsis_api/include/pmsis/rtos/assert.h | 31 + .../pmsis/rtos/event_kernel/event_kernel.h | 47 + .../include/pmsis/rtos/malloc/cl_l1_malloc.h | 120 + .../include/pmsis/rtos/malloc/fc_l1_malloc.h | 144 + .../include/pmsis/rtos/malloc/l2_malloc.h | 107 + .../pmsis/rtos/malloc/malloc_internal.h | 171 ++ .../include/pmsis/rtos/malloc/pi_malloc.h | 86 + .../include/pmsis/rtos/os_frontend_api/freq.h | 48 + .../include/pmsis/rtos/os_frontend_api/os.h | 319 +++ .../pmsis/rtos/os_frontend_api/pmsis_time.h} | 15 +- .../pmsis_api/include/pmsis/rtos/pi_log.h | 282 ++ .../pmsis_api/include/pmsis/rtos/pulpos.h} | 33 +- .../pmsis_api/include/pmsis/rtos/rtos.h} | 55 +- rtos/pmsis/pmsis_api/include/pmsis/task.h | 198 ++ rtos/pmsis/pmsis_api/tools/export.mk | 16 + rtos/pulpos/common/bin/pos-size | 144 + rtos/pulpos/common/include/pmsis.h | 79 + rtos/pulpos/common/include/pos/data/alloc.h | 71 + rtos/pulpos/common/include/pos/data/cluster.h | 72 + rtos/pulpos/common/include/pos/data/data.h | 206 ++ rtos/pulpos/common/include/pos/data/kernel.h | 36 + .../pulp.h => common/include/pos/data/lock.h} | 23 +- .../include/pos/data/sched.h} | 23 +- .../include/pos/data/soc_event.h} | 20 +- rtos/pulpos/common/include/pos/implem/alloc.h | 55 + .../include/pos/implem/alloc_pool.h} | 18 +- .../common/include/pos/implem/cluster.h | 65 + rtos/pulpos/common/include/pos/implem/dma.h | 203 ++ rtos/pulpos/common/include/pos/implem/freq.h | 46 + .../pulpos/common/include/pos/implem/implem.h | 174 ++ rtos/pulpos/common/include/pos/implem/irq.h | 193 ++ .../pulpos/common/include/pos/implem/kernel.h | 43 + rtos/pulpos/common/include/pos/implem/link.h | 123 + rtos/pulpos/common/include/pos/implem/lock.h | 104 + rtos/pulpos/common/include/pos/implem/pe.h | 205 ++ rtos/pulpos/common/include/pos/implem/perf.h | 152 ++ .../common/include/pos/implem/soc_event.h | 44 + rtos/pulpos/common/include/pos/implem/task.h | 123 + rtos/pulpos/common/include/pos/implem/trace.h | 146 + .../common/lib/libc/minimal/include/ctype.h | 72 + .../common/lib/libc/minimal/include/io.h | 32 + .../common/lib/libc/minimal/include/stdio.h | 58 + .../common/lib/libc/minimal/include/stdlib.h | 46 + .../common/lib/libc/minimal/include/string.h | 61 + .../pulpos/common/lib/libc/minimal/semihost.h | 120 + .../include/pos/chips/pulp/config.h} | 24 +- rtos/pulpos/pulp/include/pos/chips/pulp/soc.h | 54 + rtos/pulpos/pulp/include/pos/data/hyperbus.h | 48 + rtos/pulpos/pulp/include/pos/data/spim.h | 141 + rtos/pulpos/pulp/include/pos/data/uart.h | 67 + rtos/pulpos/pulp/include/pos/data/udma-v3.h | 48 + rtos/pulpos/pulp/include/pos/implem/udma-v3.h | 38 + .../chips/pulp/link_huge_stack_model.ld | 289 ++ .../include/archi/chips/arnold/apb_soc.h | 121 - .../include/archi/chips/arnold/memory_map.h | 94 - .../include/archi/chips/arnold/properties.h | 191 -- .../include/archi/chips/arnold/pulp.h | 38 - .../include/archi/chips/bigpulp/apb_soc.h | 122 - .../include/archi/chips/bigpulp/memory_map.h | 87 - .../include/archi/chips/bigpulp/properties.h | 145 - .../include/archi/chips/bigpulp/pulp.h | 34 - .../include/archi/chips/devchip/apb_soc.h | 121 - .../include/archi/chips/devchip/memory_map.h | 108 - .../include/archi/chips/devchip/properties.h | 223 -- .../include/archi/chips/devchip/pulp.h | 31 - .../include/archi/chips/fulmine/apb_soc.h | 33 - .../include/archi/chips/fulmine/memory_map.h | 92 - .../include/archi/chips/fulmine/properties.h | 123 - .../include/archi/chips/gap/apb_soc.h | 1588 ----------- .../include/archi/chips/gap/memory_map.h | 157 -- .../include/archi/chips/gap/padframe.h | 140 - .../include/archi/chips/gap/properties.h | 273 -- .../pulp_archi/include/archi/chips/gap/pulp.h | 48 - .../pulp_archi/include/archi/chips/gap8 | 1 - .../include/archi/chips/honey/apb_soc.h | 19 - .../include/archi/chips/honey/memory_map.h | 92 - .../include/archi/chips/honey/padframe.h | 199 -- .../include/archi/chips/honey/properties.h | 100 - .../include/archi/chips/multino/apb_soc.h | 122 - .../include/archi/chips/multino/memory_map.h | 117 - .../include/archi/chips/multino/properties.h | 114 - .../include/archi/chips/multino/pulp.h | 32 - .../include/archi/chips/neuraghe/memory_map.h | 79 - .../include/archi/chips/oprecompkw/apb_soc.h | 121 - .../archi/chips/oprecompkw/memory_map.h | 90 - .../archi/chips/oprecompkw/properties.h | 102 - .../include/archi/chips/oprecompkw/pulp.h | 31 - .../include/archi/chips/pulp_v1/apb_soc.h | 121 - .../include/archi/chips/pulp_v1/memory_map.h | 128 - .../include/archi/chips/pulp_v1/properties.h | 226 -- .../include/archi/chips/pulp_v1/pulp.h | 39 - .../include/archi/chips/pulpissimo/apb_soc.h | 121 - .../archi/chips/pulpissimo/apb_soc_ctrl.h | 116 - .../archi/chips/pulpissimo/memory_map.h | 81 - .../archi/chips/pulpissimo/properties.h | 236 -- .../include/archi/chips/pulpissimo/pulp.h | 39 - .../archi/chips/pulpissimo_v1/apb_soc.h | 121 - .../archi/chips/pulpissimo_v1/memory_map.h | 81 - .../archi/chips/pulpissimo_v1/properties.h | 200 -- .../include/archi/chips/quentin/apb_soc.h | 121 - .../include/archi/chips/quentin/memory_map.h | 95 - .../include/archi/chips/quentin/properties.h | 197 -- .../include/archi/chips/quentin/pulp.h | 32 - .../include/archi/chips/usoc_v1/apb_soc.h | 75 - .../include/archi/chips/usoc_v1/gpio.h | 50 - .../include/archi/chips/usoc_v1/memory_map.h | 123 - .../include/archi/chips/usoc_v1/properties.h | 168 -- .../include/archi/chips/vega/apb_soc.h | 110 - .../include/archi/chips/vega/apb_soc_ctrl.h | 1581 ----------- .../include/archi/chips/vega/memory_map.h.in | 175 -- .../pulp_archi/include/archi/chips/vega/pmu.h | 510 ---- .../include/archi/chips/vega/properties.h | 361 --- .../include/archi/chips/vega/pulp.h | 46 - .../include/archi/chips/vivosoc2/memory_map.h | 94 - .../include/archi/chips/vivosoc2/properties.h | 135 - .../archi/chips/vivosoc2_1/memory_map.h | 94 - .../archi/chips/vivosoc2_1/properties.h | 134 - .../include/archi/chips/vivosoc3/apb_soc.h | 121 - .../include/archi/chips/vivosoc3/fll.h | 125 - .../include/archi/chips/vivosoc3/freq.h | 27 - .../include/archi/chips/vivosoc3/memory_map.h | 127 - .../include/archi/chips/vivosoc3/properties.h | 212 -- .../include/archi/chips/vivosoc3/pulp.h | 41 - .../include/archi/chips/vivosoc3_1/apb_soc.h | 124 - .../include/archi/chips/vivosoc3_1/fll.h | 125 - .../include/archi/chips/vivosoc3_1/freq.h | 27 - .../archi/chips/vivosoc3_1/memory_map.h | 125 - .../archi/chips/vivosoc3_1/properties.h | 293 -- .../include/archi/chips/vivosoc3_1/pulp.h | 40 - .../include/archi/chips/vivosoc3_5/apb_soc.h | 121 - .../include/archi/chips/vivosoc3_5/fll.h | 125 - .../include/archi/chips/vivosoc3_5/freq.h | 27 - .../archi/chips/vivosoc3_5/memory_map.h | 127 - .../archi/chips/vivosoc3_5/properties.h | 212 -- .../include/archi/chips/vivosoc3_5/pulp.h | 39 - .../include/archi/chips/vivosoc4/apb_soc.h | 124 - .../include/archi/chips/vivosoc4/fll.h | 125 - .../include/archi/chips/vivosoc4/freq.h | 27 - .../include/archi/chips/vivosoc4/memory_map.h | 125 - .../include/archi/chips/vivosoc4/properties.h | 293 -- .../include/archi/chips/vivosoc4/pulp.h | 40 - .../include/archi/chips/wolfe/apb_soc.h | 138 - .../archi/chips/wolfe/apb_soc_ctrl_new.h | 271 -- .../include/archi/chips/wolfe/memory_map.h | 133 - .../include/archi/chips/wolfe/pmu.h | 330 --- .../include/archi/chips/wolfe/properties.h | 245 -- .../include/archi/chips/wolfe/pulp.h | 44 - .../pulp_hal/include/hal/chips/arnold/pulp.h | 36 - .../pulp_hal/include/hal/chips/bigpulp/pulp.h | 32 - .../pulp_hal/include/hal/chips/devchip/pulp.h | 29 - .../pulp_hal/include/hal/chips/fulmine/pulp.h | 28 - .../pulp_hal/include/hal/chips/gap/apb_soc.h | 120 - .../pulp_hal/include/hal/chips/gap/efuse.h | 492 ---- .../pulp_hal/include/hal/chips/gap/pulp.h | 47 - rtos/pulpos/pulp_hal/include/hal/chips/gap8 | 1 - .../include/hal/chips/gap_rev1/efuse.h | 160 -- .../include/hal/chips/gap_rev1/pulp.h | 45 - .../pulp_hal/include/hal/chips/honey/fll.h | 45 - .../pulp_hal/include/hal/chips/honey/pulp.h | 31 - .../pulp_hal/include/hal/chips/multino/pulp.h | 32 - .../include/hal/chips/neuraghe/pulp.h~ | 38 - .../include/hal/chips/oprecompkw/pulp.h | 29 - .../pulp_hal/include/hal/chips/pulp_v1/pulp.h | 40 - .../include/hal/chips/pulpissimo/pulp.h | 36 - .../include/hal/chips/pulpissimo_v1/pulp.h | 37 - .../pulp_hal/include/hal/chips/quentin/pulp.h | 31 - .../include/hal/chips/usoc_v1/apb_soc.h | 76 - .../pulp_hal/include/hal/chips/usoc_v1/gpio.h | 113 - .../pulp_hal/include/hal/chips/usoc_v1/pulp.h | 34 - .../pulp_hal/include/hal/chips/vega/efuse.h | 178 -- .../pulp_hal/include/hal/chips/vega/pulp.h | 45 - .../pulp_hal/include/hal/chips/vega/pulp.h~ | 29 - .../include/hal/chips/vivosoc2/pulp.h | 28 - .../include/hal/chips/vivosoc2_1/pulp.h | 28 - .../pulp_hal/include/hal/chips/vivosoc3/fll.h | 108 - .../include/hal/chips/vivosoc3/freq.h | 69 - .../include/hal/chips/vivosoc3/pulp.h | 40 - .../include/hal/chips/vivosoc3_1/fll.h | 108 - .../include/hal/chips/vivosoc3_1/freq.h | 69 - .../include/hal/chips/vivosoc3_1/pulp.h | 40 - .../include/hal/chips/vivosoc3_5/fll.h | 108 - .../include/hal/chips/vivosoc3_5/freq.h | 69 - .../include/hal/chips/vivosoc3_5/pulp.h | 40 - .../pulp_hal/include/hal/chips/vivosoc4/fll.h | 108 - .../include/hal/chips/vivosoc4/freq.h | 69 - .../include/hal/chips/vivosoc4/pulp.h | 40 - .../pulp_hal/include/hal/chips/wolfe/pulp.h | 43 - .../pulp_hal/src/hal/mailbox/mailbox_v0.c | 80 - rtos/pulpos/pulp_hal/src/hal/rab/rab_v1.c | 68 - rtos/pulpos/pulp_hal/src/hal/tryx/tryx_v1.c | 40 - rules/application.mk | 95 + rules/dpi-models.mk | 13 - rules/pulp-debug-bridge.mk | 13 - sourceme.sh | 8 +- tests/cluster/call/Makefile | 5 - tests/cluster/call/test.c | 126 - tests/cluster/fork/Makefile | 5 - tests/cluster/fork/test.c | 145 - tests/hello/Makefile | 82 +- tests/hello/test.c | 5 +- tests/perf/vecmul/Makefile | 82 +- tests/perf/vecmul/test.c | 3 +- tools/rules/pmsis_rules.mk | 178 -- 256 files changed, 15953 insertions(+), 18249 deletions(-) create mode 100644 rtos/pmsis/pmsis_api/LICENSE create mode 100644 rtos/pmsis/pmsis_api/docs/Makefile create mode 100644 rtos/pmsis/pmsis_api/docs/chips.rst create mode 100644 rtos/pmsis/pmsis_api/docs/conf.py create mode 100644 rtos/pmsis/pmsis_api/docs/dox.mk create mode 100644 rtos/pmsis/pmsis_api/docs/drivers.rst create mode 100755 rtos/pmsis/pmsis_api/docs/headers/groups.h create mode 100644 rtos/pmsis/pmsis_api/docs/index.rst create mode 100644 rtos/pmsis/pmsis_api/docs/mainpage.md create mode 100644 rtos/pmsis/pmsis_api/docs/make.bat create mode 100755 rtos/pmsis/pmsis_api/docs/pmsis.dxy.in create mode 100644 rtos/pmsis/pmsis_api/docs/requirements.txt create mode 100644 rtos/pmsis/pmsis_api/docs/rtos.rst create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/default.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/gap8/gpio.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/gap8/pad.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/gap8/perf.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/gap8/pmu.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/vega/gpio.h rename rtos/{pulpos/pulp_archi/include/archi/chips/pulpissimo_v1/pulp.h => pmsis/pmsis_api/include/pmsis/chips/vega/pad.h} (50%) create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/chips/vega/perf.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cl_malloc.h rename rtos/{pulpos/pulp_hal/include/hal/chips/neuraghe/pulp.h => pmsis/pmsis_api/include/pmsis/cluster/cl_pmsis_api.h} (75%) create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cl_pmsis_types.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cluster_sync/cl_synchronisation.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cluster_sync/cl_to_fc_delegate.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cluster_sync/fc_to_cl_delegate.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/cluster_team/cl_team.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/dma/cl_dma.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/cluster/dma/cl_dma_decompressor.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/device.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/aes.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/asrc.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/cpi.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/dmacpy.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/gpio.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/hyperbus.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/i2c.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/i2c_slave.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/i2s.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/octospi.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/pad.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/perf.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/pmu.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/pwm.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/rtc.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/spi.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/drivers/uart.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/errno.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/mem_slab.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/pmsis_types.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/assert.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/event_kernel/event_kernel.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/malloc/cl_l1_malloc.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/malloc/fc_l1_malloc.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/malloc/l2_malloc.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/malloc/malloc_internal.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/malloc/pi_malloc.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/os_frontend_api/freq.h create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/os_frontend_api/os.h rename rtos/{pulpos/pulp_archi/include/archi/chips/neuraghe/pulp.h => pmsis/pmsis_api/include/pmsis/rtos/os_frontend_api/pmsis_time.h} (68%) create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/rtos/pi_log.h rename rtos/{pulpos/pulp_archi/include/archi/chips/usoc_v1/pulp.h => pmsis/pmsis_api/include/pmsis/rtos/pulpos.h} (58%) rename rtos/{pulpos/pulp_archi/include/archi/chips/neuraghe/properties.h => pmsis/pmsis_api/include/pmsis/rtos/rtos.h} (53%) create mode 100644 rtos/pmsis/pmsis_api/include/pmsis/task.h create mode 100644 rtos/pmsis/pmsis_api/tools/export.mk create mode 100755 rtos/pulpos/common/bin/pos-size create mode 100644 rtos/pulpos/common/include/pmsis.h create mode 100644 rtos/pulpos/common/include/pos/data/alloc.h create mode 100644 rtos/pulpos/common/include/pos/data/cluster.h create mode 100644 rtos/pulpos/common/include/pos/data/data.h create mode 100644 rtos/pulpos/common/include/pos/data/kernel.h rename rtos/pulpos/{pulp_archi/include/archi/chips/vivosoc2/pulp.h => common/include/pos/data/lock.h} (63%) rename rtos/pulpos/{pulp_archi/include/archi/chips/fulmine/pulp.h => common/include/pos/data/sched.h} (61%) rename rtos/pulpos/{pulp_archi/include/archi/chips/vivosoc2_1/pulp.h => common/include/pos/data/soc_event.h} (61%) create mode 100644 rtos/pulpos/common/include/pos/implem/alloc.h rename rtos/pulpos/{pulp_hal/include/hal/chips/vivosoc2/pulp.h~ => common/include/pos/implem/alloc_pool.h} (65%) create mode 100644 rtos/pulpos/common/include/pos/implem/cluster.h create mode 100644 rtos/pulpos/common/include/pos/implem/dma.h create mode 100644 rtos/pulpos/common/include/pos/implem/freq.h create mode 100644 rtos/pulpos/common/include/pos/implem/implem.h create mode 100644 rtos/pulpos/common/include/pos/implem/irq.h create mode 100644 rtos/pulpos/common/include/pos/implem/kernel.h create mode 100644 rtos/pulpos/common/include/pos/implem/link.h create mode 100644 rtos/pulpos/common/include/pos/implem/lock.h create mode 100644 rtos/pulpos/common/include/pos/implem/pe.h create mode 100644 rtos/pulpos/common/include/pos/implem/perf.h create mode 100644 rtos/pulpos/common/include/pos/implem/soc_event.h create mode 100644 rtos/pulpos/common/include/pos/implem/task.h create mode 100644 rtos/pulpos/common/include/pos/implem/trace.h create mode 100644 rtos/pulpos/common/lib/libc/minimal/include/ctype.h create mode 100644 rtos/pulpos/common/lib/libc/minimal/include/io.h create mode 100644 rtos/pulpos/common/lib/libc/minimal/include/stdio.h create mode 100644 rtos/pulpos/common/lib/libc/minimal/include/stdlib.h create mode 100644 rtos/pulpos/common/lib/libc/minimal/include/string.h create mode 100644 rtos/pulpos/common/lib/libc/minimal/semihost.h rename rtos/pulpos/{pulp_archi/include/archi/chips/honey/pulp.h => pulp/include/pos/chips/pulp/config.h} (59%) create mode 100644 rtos/pulpos/pulp/include/pos/chips/pulp/soc.h create mode 100644 rtos/pulpos/pulp/include/pos/data/hyperbus.h create mode 100644 rtos/pulpos/pulp/include/pos/data/spim.h create mode 100644 rtos/pulpos/pulp/include/pos/data/uart.h create mode 100644 rtos/pulpos/pulp/include/pos/data/udma-v3.h create mode 100644 rtos/pulpos/pulp/include/pos/implem/udma-v3.h create mode 100644 rtos/pulpos/pulp/kernel/chips/pulp/link_huge_stack_model.ld delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/arnold/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/arnold/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/arnold/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/arnold/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/bigpulp/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/bigpulp/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/bigpulp/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/bigpulp/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/devchip/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/devchip/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/devchip/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/devchip/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/fulmine/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/fulmine/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/fulmine/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/gap/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/gap/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/gap/padframe.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/gap/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/gap/pulp.h delete mode 120000 rtos/pulpos/pulp_archi/include/archi/chips/gap8 delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/honey/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/honey/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/honey/padframe.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/honey/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/multino/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/multino/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/multino/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/multino/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/neuraghe/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/oprecompkw/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/oprecompkw/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/oprecompkw/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/oprecompkw/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulp_v1/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulp_v1/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulp_v1/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulp_v1/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulpissimo/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulpissimo/apb_soc_ctrl.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulpissimo/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulpissimo/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulpissimo/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulpissimo_v1/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulpissimo_v1/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/pulpissimo_v1/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/quentin/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/quentin/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/quentin/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/quentin/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/usoc_v1/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/usoc_v1/gpio.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/usoc_v1/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/usoc_v1/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vega/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vega/apb_soc_ctrl.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vega/memory_map.h.in delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vega/pmu.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vega/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vega/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc2/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc2/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc2_1/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc2_1/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3/fll.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3/freq.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_1/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_1/fll.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_1/freq.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_1/memory_map.h delete mode 100755 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_1/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_1/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_5/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_5/fll.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_5/freq.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_5/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_5/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc3_5/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc4/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc4/fll.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc4/freq.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc4/memory_map.h delete mode 100755 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc4/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/vivosoc4/pulp.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/wolfe/apb_soc.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/wolfe/apb_soc_ctrl_new.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/wolfe/memory_map.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/wolfe/pmu.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/wolfe/properties.h delete mode 100644 rtos/pulpos/pulp_archi/include/archi/chips/wolfe/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/arnold/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/bigpulp/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/devchip/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/fulmine/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/gap/apb_soc.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/gap/efuse.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/gap/pulp.h delete mode 120000 rtos/pulpos/pulp_hal/include/hal/chips/gap8 delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/gap_rev1/efuse.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/gap_rev1/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/honey/fll.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/honey/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/multino/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/neuraghe/pulp.h~ delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/oprecompkw/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/pulp_v1/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/pulpissimo/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/pulpissimo_v1/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/quentin/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/usoc_v1/apb_soc.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/usoc_v1/gpio.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/usoc_v1/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vega/efuse.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vega/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vega/pulp.h~ delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc2/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc2_1/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc3/fll.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc3/freq.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc3/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc3_1/fll.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc3_1/freq.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc3_1/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc3_5/fll.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc3_5/freq.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc3_5/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc4/fll.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc4/freq.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/vivosoc4/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/include/hal/chips/wolfe/pulp.h delete mode 100644 rtos/pulpos/pulp_hal/src/hal/mailbox/mailbox_v0.c delete mode 100644 rtos/pulpos/pulp_hal/src/hal/rab/rab_v1.c delete mode 100644 rtos/pulpos/pulp_hal/src/hal/tryx/tryx_v1.c create mode 100644 rules/application.mk delete mode 100644 rules/dpi-models.mk delete mode 100644 rules/pulp-debug-bridge.mk delete mode 100644 tests/cluster/call/Makefile delete mode 100644 tests/cluster/call/test.c delete mode 100644 tests/cluster/fork/Makefile delete mode 100644 tests/cluster/fork/test.c delete mode 100644 tools/rules/pmsis_rules.mk diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 8d0c9718..7661945f 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -3,7 +3,8 @@ on: [push,pull_request] jobs: Regression-Tests: name: Regression Tests - runs-on: ubuntu-latest + runs-on: self-hosted + timeout-minutes: 5000 steps: - run: echo "The job was automatically triggered by a ${{ github.event_name }} event." - run: echo "This job is now running on a ${{ runner.os }} server hosted by GitHub!" @@ -16,6 +17,7 @@ jobs: run: | cd ${{ github.workspace }} cd .. + rm -rf corev-gcc/ corev-binutils-gdb/ riscv-dejagnu/ riscv-newlib/ git clone https://github.com/EEESlab/corev-gcc.git git clone https://github.com/EEESlab/corev-binutils-gdb.git git clone https://github.com/riscv/riscv-newlib.git @@ -45,7 +47,7 @@ jobs: cd ${{ github.workspace }} source temp_env export PULP_RISCV_GCC_TOOLCHAIN=${INSTALL_PREFIX} - source configs/pulp-open-cv32e40p.sh + source sourceme.sh make build cd ${{ github.workspace }} export -p > temp_env @@ -62,4 +64,5 @@ jobs: - name: List files in the repository run: | ls ${{ github.workspace }} + cd .. - run: echo "This job's status is ${{ job.status }}." diff --git a/Makefile b/Makefile index e5b16750..b620671b 100644 --- a/Makefile +++ b/Makefile @@ -13,9 +13,6 @@ include rules/gap-configs.mk include rules/gvsoc.mk include rules/pulpos.mk -build: source gvsoc.build.all +build: gvsoc.build.all clean: gvsoc.clean - -source: - source sourceme.sh \ No newline at end of file diff --git a/rtos/pmsis/pmsis_api/LICENSE b/rtos/pmsis/pmsis_api/LICENSE new file mode 100644 index 00000000..f433b1a5 --- /dev/null +++ b/rtos/pmsis/pmsis_api/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/rtos/pmsis/pmsis_api/docs/Makefile b/rtos/pmsis/pmsis_api/docs/Makefile new file mode 100644 index 00000000..d4bb2cbb --- /dev/null +++ b/rtos/pmsis/pmsis_api/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/rtos/pmsis/pmsis_api/docs/chips.rst b/rtos/pmsis/pmsis_api/docs/chips.rst new file mode 100644 index 00000000..2291381d --- /dev/null +++ b/rtos/pmsis/pmsis_api/docs/chips.rst @@ -0,0 +1,21 @@ +Chips +----- + +GAP8 +........ + +Information +=========== + +.. doxygengroup:: GAP8_INFO + :members: + :private-members: + :protected-members: + +Performance counters +==================== + +.. doxygengroup:: GAP8_PERF + :members: + :private-members: + :protected-members: diff --git a/rtos/pmsis/pmsis_api/docs/conf.py b/rtos/pmsis/pmsis_api/docs/conf.py new file mode 100644 index 00000000..c73eefbb --- /dev/null +++ b/rtos/pmsis/pmsis_api/docs/conf.py @@ -0,0 +1,66 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +import subprocess, os + +read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True' + +if True: #read_the_docs_build: + + subprocess.call('make -f dox.mk clean all', shell=True) + +# -- Project information ----------------------------------------------------- + +project = 'PMSIS' +copyright = '2019, GreenWaves Technologies' +author = 'GreenWaves Technologies' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'breathe', + 'recommonmark' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +breathe_projects = { "pmsis_api": "doc/xml" } + +breathe_default_project = "pmsis_api" + diff --git a/rtos/pmsis/pmsis_api/docs/dox.mk b/rtos/pmsis/pmsis_api/docs/dox.mk new file mode 100644 index 00000000..986ab88f --- /dev/null +++ b/rtos/pmsis/pmsis_api/docs/dox.mk @@ -0,0 +1,10 @@ +CHIP ?= gap8 + +clean: + rm -rf ./doc + rm -rf pmsis.html + +all: + cat pmsis.dxy.in | sed s/@CHIP_CONFIG@/$(CHIP)/ > pmsis.dxy + doxygen pmsis.dxy + ln -s ${CURDIR}/doc/html/index.html pmsis.html diff --git a/rtos/pmsis/pmsis_api/docs/drivers.rst b/rtos/pmsis/pmsis_api/docs/drivers.rst new file mode 100644 index 00000000..a48f36da --- /dev/null +++ b/rtos/pmsis/pmsis_api/docs/drivers.rst @@ -0,0 +1,101 @@ +Drivers +------- + +Cluster +....... + +FC/cluster synchronization +========================== + +.. doxygengroup:: FcClusterSync + :members: + :private-members: + :protected-members: + +Cluster team synchronization +============================ + +.. doxygengroup:: ClusterTeam + :members: + :private-members: + :protected-members: + +Cluster DMA +=========== + +.. doxygengroup:: ClusterDMA + :members: + :private-members: + :protected-members: + +UART +.... + +.. doxygengroup:: UART + :members: + :private-members: + :protected-members: + +SPI +... + +.. doxygengroup:: SPI + :members: + :private-members: + :protected-members: + +Hyperbus +........ + +.. doxygengroup:: Hyperbus + :members: + :private-members: + :protected-members: + +CPI +... + +.. doxygengroup:: CPI + :members: + :private-members: + :protected-members: + +GPIO +.... + +.. doxygengroup:: GPIO + :members: + :private-members: + :protected-members: + +I2C +... + +.. doxygengroup:: I2C + :members: + :private-members: + :protected-members: + +I2S +... + +.. doxygengroup:: I2S + :members: + :private-members: + :protected-members: + +Padframe +........ + +.. doxygengroup:: Padframe + :members: + :private-members: + :protected-members: + +Performance counters +.................... + +.. doxygengroup:: Perf + :members: + :private-members: + :protected-members: diff --git a/rtos/pmsis/pmsis_api/docs/headers/groups.h b/rtos/pmsis/pmsis_api/docs/headers/groups.h new file mode 100755 index 00000000..42e70f6c --- /dev/null +++ b/rtos/pmsis/pmsis_api/docs/headers/groups.h @@ -0,0 +1,13 @@ + + +/** + * @defgroup groupRTOS RTOS + */ + +/** + * @defgroup groupDrivers Drivers + */ + +/** + * @defgroup groupChips Chips + */ diff --git a/rtos/pmsis/pmsis_api/docs/index.rst b/rtos/pmsis/pmsis_api/docs/index.rst new file mode 100644 index 00000000..b53dee42 --- /dev/null +++ b/rtos/pmsis/pmsis_api/docs/index.rst @@ -0,0 +1,19 @@ +PMSIS API documentation +======================= + +.. include:: mainpage.md + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + rtos + drivers + chips + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/rtos/pmsis/pmsis_api/docs/mainpage.md b/rtos/pmsis/pmsis_api/docs/mainpage.md new file mode 100644 index 00000000..e6219c5c --- /dev/null +++ b/rtos/pmsis/pmsis_api/docs/mainpage.md @@ -0,0 +1,13 @@ +Introduction +============ + +The PMSIS API is a set of low-level drivers which any operating system can implement to provide a common layer to upper layers. Together with the PMSIS BSP, it provides a full stack of drivers, allowing the development of applications portable across a wide range of operating systems. + +Conventions +=========== + +All functions prefixed by `pi_` can only be called from fabric-controller side while the ones prefixed by `pi_cl_` can only be called from cluster side. Any exception to these rules is documented where it applies. + +All functions on fabric-controller side are by default synchronous and are blocking the caller until the operation is done. All the functions suffixed by `_async` are asynchronous and are not blocking the caller. The termination of such operations is managed with a `pi_task_t` object, see PMSIS API documentation for more information. + +Functions on cluster-side are by default synchronous but can also be asynchronous if the documentation of the function mentions it. diff --git a/rtos/pmsis/pmsis_api/docs/make.bat b/rtos/pmsis/pmsis_api/docs/make.bat new file mode 100644 index 00000000..2119f510 --- /dev/null +++ b/rtos/pmsis/pmsis_api/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/rtos/pmsis/pmsis_api/docs/pmsis.dxy.in b/rtos/pmsis/pmsis_api/docs/pmsis.dxy.in new file mode 100755 index 00000000..cab00d46 --- /dev/null +++ b/rtos/pmsis/pmsis_api/docs/pmsis.dxy.in @@ -0,0 +1,2370 @@ +# Doxyfile 1.8.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = PMSIS + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = "Version 0.1" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "PULP Microcontrollers Software Interface Standard (PMSIS)" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = NO + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = NO + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = YES + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = NO + + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 0 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = ../include/pmsis/chips/gap8/perf.h \ + ../include/pmsis/drivers/spi.h \ + ../include/pmsis/drivers/uart.h \ + ../include/pmsis/drivers/perf.h \ + ../include/pmsis/drivers/pad.h \ + ../include/pmsis/drivers/i2c.h \ + ../include/pmsis/drivers/gpio.h \ + ../include/pmsis/drivers/hyperbus.h \ + ../include/pmsis/drivers/cpi.h \ + ../include/pmsis/drivers/i2s.h \ + ../include/pmsis/rtos/malloc/pmsis_l2_malloc.h \ + ../include/pmsis/rtos/malloc/pmsis_fc_tcdm_malloc.h \ + ../include/pmsis/rtos/malloc/pmsis_l1_malloc.h \ + ../include/pmsis/cluster/cl_malloc.h \ + ../include/pmsis/cluster/cluster_team/cl_team.h \ + ../include/pmsis/cluster/cl_pmsis_types.h \ + ../include/pmsis/cluster/cluster_sync/fc_to_cl_delegate.h \ + ../include/pmsis/cluster/cluster_sync/cl_to_fc_delegate.h \ + ../include/pmsis/cluster/dma/cl_dma.h \ + ../include/pmsis/task.h \ + headers + +#INPUT = ../include/pmsis/cluster/cluster_sync/fc_to_cl_delegate.h ../include/pmsis/pmsis_types.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.txt + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = math_helper.* + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = */RTE/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = NO + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more acurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = NO + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = ignore_ + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 0 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /

    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

    v`}M?1 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%0A-Q}hmORHx!ajKdWgJe1Ltaq^Ips0nVe-ULkW0M| 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

  • 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 literal 0 HcmV?d00001 diff --git a/tools/pulp-debug-bridge/bin/plpbridge b/tools/pulp-debug-bridge/bin/plpbridge new file mode 100755 index 00000000..4c75bd88 --- /dev/null +++ b/tools/pulp-debug-bridge/bin/plpbridge @@ -0,0 +1,479 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + + +import bridge.debug_bridge as db +import argparse +import code +from importlib.machinery import SourceFileLoader +import os.path +import sys +import pulp_config as plpconf + +try: + import plptree +except: + pass + +import json_tools as js + +try: + from IPython import embed +except: + pass + + + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + + +def fatal_error(error): + print (bcolors.FAIL + 'FATAL ERROR: ' + error + bcolors.ENDC) + + +def read(bridge): + if args.addr == None: + raise Exception('Access address must be given through option --addr when using command "read"') + + addr = int(args.addr, 0) + size = int(args.size, 0) + width = args.width + + if args.verbose >= 4: + print ('Read access command (addr: 0x%x, size: 0x%x)' % (addr, size)) + + data = bridge.read(addr=addr, size=size) + + while size > 0: + line = '%8.8x: ' % addr + for i in range(0, 16, width): + + line += '%*.*x ' % (width*2, width*2, int.from_bytes(b''.join(data[0:width]), byteorder='little')) + size -= width + addr += width + data = data[width:] + if size <= 0: + break + print (line) + + return 0 + + + +def write(bridge): + if args.addr == None: + raise Exception('Access address must be given through option --addr when using command "write"') + + if args.value == None: + raise Exception('Access value must be given through option --value when using command "write"') + + addr = int(args.addr, 0) + size = int(args.size, 0) + value = int(args.value, 0) + + if args.verbose >= 4: + print ('Write access command (addr: 0x%x, size: 0x%x, value: 0x%x)' % (addr, size, value)) + + bridge.write(addr=addr, size=size, buffer=value.to_bytes(size, byteorder='little')) + + return 0 + +def efuse_write(bridge): + addr = int(args.addr, 0) + value = int(args.value, 0) + mask = int(args.mask, 0) + + bridge.efuse_access(args.flasher_init, True, addr, value, mask) + + return 0 + +def eeprom_write(bridge): + addr = int(args.addr, 0) + + return bridge.eeprom_access(args.flasher_init, 0, 0, True, addr, args.file) + + +def get_flash_id(): + if args.flash_type == 'hyperflash': + return 1 + elif args.flash_type == 'mram': + return 2 + else: + return 0 + + +def flash_write(bridge): + addr = int(args.addr, 0) + + return bridge.flash_access(args.flasher_init, get_flash_id(), 0, 0, True, addr, 0, args.file) + + +def flash_read(bridge): + addr = int(args.addr, 0) + size = int(args.size, 0) + + return bridge.flash_access(args.flasher_init, get_flash_id(), 0, 0, False, addr, size, args.file) + + +def flash_erase(bridge): + addr = int(args.addr, 0) + size = int(args.size, 0) + + return bridge.flash_erase(args.flasher_init, get_flash_id(), 0, 0, addr, size) + + +def flash_erase_sector(bridge): + addr = int(args.addr, 0) + + return bridge.flash_erase_sector(args.flasher_init, get_flash_id(), 0, 0, addr) + + +def flash_erase_chip(bridge): + return bridge.flash_erase_chip(args.flasher_init, get_flash_id(), 0, 0) + + +def load(bridge): + + if args.verbose >= 4: + binaries = [] + binaries_conf = bridge.config.get('**/runner/binaries') + if binaries_conf is not None: + binaries = binaries_conf.get_dict() + print ('Loading ELF binaries (path: %s)' % ' '.join(binaries)) + + return bridge.load() + + +def ioloop(bridge): + if args.verbose >= 4: + print ('Lauching IO loop') + + return bridge.ioloop() + + +def reqloop(bridge): + if args.verbose >= 4: + print ('Lauching req loop') + + return bridge.reqloop() + + +def gdb(bridge): + if args.verbose >= 4: + print ('Lauching GDB server') + + return bridge.gdb(int(args.rsp_port)) + + +def start(bridge): + if args.verbose >= 4: + print ('Starting execution') + + return bridge.start() + + +def stop(bridge): + if args.verbose >= 4: + print ('Stopping execution') + + return bridge.stop() + + +def wait(bridge): + if args.verbose >= 4: + print ('Waiting termination') + + return bridge.wait() + +def flash(bridge): + # When using the flasher, the binaries should be the flasher + bridge.binaries= [os.environ["INSTALL_DIR"] + "/bin/flasher"] + if args.verbose >=4: + print("Flashing the flash image") + + load(bridge) + ioloop(bridge) + start(bridge) + return bridge.flash(args.fimages) + + +def reset(bridge): + if args.verbose >= 4: + print ('Chip reset') + + return bridge.reset() + + +def script(bridge): + + for script in args.scripts: + if args.verbose >= 4: + print ('Executing script: ' + script) + + if script.find('@') == -1: + script_name = script + script_entry = "debug_bridge_entry" + else: + script_entry, script_name = script.split('@') + + module = SourceFileLoader("user_script", script_name).load_module() + + entry = getattr(module, script_entry) + if entry(bridge): + return -1 + + return 0 + + + +commands = { + 'read' : ['Read data from the target', read], + 'write' : ['Write data to the target', write], + 'load' : ['Load a binary into the target', load], + 'ioloop' : ['Activate IO loop', ioloop], + 'reqloop' : ['Activate request loop', reqloop], + 'gdb' : ['Open RSP server for GDB connection',gdb], + 'start' : ['Start execution', start], + 'stop' : ['Stop execution', stop], + 'flash' : ['Flash the flash image', flash], + 'wait' : ['Wait termination', wait], + 'reset' : ['Chip reset', reset], + 'script' : ['Execute user scripts', script], + 'efuse_write' : ['Write to efuse', efuse_write], + 'eeprom_write': ['Write to eeprom', eeprom_write], + 'flash_write' : ['Write to flash', flash_write], + 'flash_read' : ['Read from flash', flash_read], + 'flash_erase_sector' : ['Erase flash sector', flash_erase_sector], + 'flash_erase' : ['Erase flash area', flash_erase], + 'flash_erase_chip' : ['Erase flash', flash_erase_chip], +} + + +command_help = """Available commands: +""" + +for name, cmd in commands.items(): + command_help += ' %-10s %s\n' % (name, cmd[0]) + +parser = argparse.ArgumentParser(description='Control a pulp target', + epilog=command_help, formatter_class=argparse.RawDescriptionHelpFormatter, add_help=False +) + +parser.add_argument('command', metavar='CMD', type=str, nargs='*', + help='a command to be executed (see the command help afterwards)') + +parser.add_argument("--script", dest="scripts", default=[], action="append", help="Specify a script to be executed with the script command") +parser.add_argument("--config", dest="config", default=None, help="Specify the system configuration") +parser.add_argument("--config-path", dest="config_path", default=None, help="Specify the system configuration") +parser.add_argument("--ipython", dest="ipython", action="store_true", help="Enter ipython shell") +parser.add_argument("--shell", dest="shell", action="store_true", help="Enter python shell") +parser.add_argument("--port", dest="port", type=int, default=None, help="Specify port for proxy mode") +parser.add_argument("--debug", dest="debug", action='store_true', default=False, help="Activate debug mode for this script") +parser.add_argument("--verbose", dest="verbose", type=int, default=3, help="Verbose mode") +parser.add_argument("--help", dest="help", action='store_true', default=False, help="Dump help") +parser.add_argument("--cable", dest="cable", default=None, help="Specify cable") +parser.add_argument("--chip", dest="chip", default=None, help="Specify target chip") +parser.add_argument("--binary", dest="binaries", default=[], action="append", help="Specify a binary to be loaded by the load command") +parser.add_argument("--flash-image", dest="fimages", default=[], action="append", help="Specify a flash image to be flashed") +parser.add_argument("--flash-type", dest="flash_type", default="hyperflash", help="Specify flash type") +parser.add_argument("--config-opt", dest="configOpt", default=[], action="append", help='specify configuration option') + +[args, otherArgs] = parser.parse_known_args() + +if 'read' in args.command or 'write' in args.command: + parser.add_argument("--addr", dest="addr", default=None, help="Specify the address of the access for read and write commands") + parser.add_argument("--size", dest="size", default="4", help="Specify the size of the access for read and write commands") + +opt_flasher_init = False + +if 'efuse_write' in args.command: + opt_flasher_init = True + parser.add_argument("--addr", dest="addr", default=None, help="Specify the address of the access for read and write commands") + parser.add_argument("--value", dest="value", default=None, help="Specify the value to be written for write command") + parser.add_argument("--mask", dest="mask", default="0xffffffff", help="Specify the mask used to select which bit is written") + +if 'read' in args.command: + parser.add_argument("--width", dest="width", default=1, type=int, help="Specify the word size in bytes used to display numbers for read commands") + +if 'write' in args.command: + parser.add_argument("--value", dest="value", default=None, help="Specify the value to be written for write command") + +if 'eeprom_write' in args.command or 'flash_write' in args.command or 'flash_read' in args.command: + parser.add_argument("--file", dest="file", default=None, help="The file to be written for eeprom_write command") + parser.add_argument("--addr", dest="addr", default=None, help="Specify the address of the access for read and write commands") + opt_flasher_init = True + +if 'flash_read' in args.command or 'flash_erase' in args.command: + parser.add_argument("--size", dest="size", default="4", help="Specify the size of the access for read and write commands") + +if 'flash_erase_sector' in args.command or 'flash_erase' in args.command: + parser.add_argument("--addr", dest="addr", default=None, help="Specify the address of the access for read and write commands") + opt_flasher_init = True + +if 'flash_erase_chip' in args.command: + opt_flasher_init = True + +if 'gdb' in args.command: + parser.add_argument("--rsp-port", dest="rsp_port", default=1234, help="Specify the port number that the RSP will use to open a socket for GDB connection") + +if opt_flasher_init: + parser.add_argument("--flasher-init", dest="flasher_init", action="store_true", default=True, help="Initialize flasher") + parser.add_argument("--no-flasher-init", dest="flasher_init", action="store_false", default=True, help="Initialize flasher") + + +parser.add_argument("--boot-mode", dest="boot_mode", default=None, help="Specify the boot mode") + +args = parser.parse_args() + +if args.help: + parser.print_help() + exit(0) + + + +config_path = args.config_path +config_name = args.config + + +if config_name is not None: + config_path = os.path.join( + os.path.dirname(os.path.dirname(sys.argv[0])), + 'configs', 'config', '%s.json' % config_name + ) + +elif args.chip is not None: + config_path = os.path.join( + os.path.dirname(os.path.dirname(sys.argv[0])), + 'configs', 'chips', args.chip, '%s.json' % args.chip + ) + + if not os.path.exists(config_path): + print ("ERROR, didn't find any configuration for specified chip (chip: %s, config: %s)" % (args.chip, config_path)) + exit(-1) + +elif config_path is None: + raise Exception('A chip or a config file must be specified') + +config = plpconf.get_config(config_path, config_opts=args.configOpt, interpret=True) + + +# And overloads it with the specified options +if args.boot_mode is not None: + if config.get('**/debug_bridge') is None: + config.set('debug_bridge', {}) + + config.get('**/debug_bridge').set('boot-mode', args.boot_mode) + +cable = os.environ.get('PLPBRIDGE_CABLE') +if cable is not None and args.cable is not None: + print ('Overwriting --cable option with PLPBRIDGE_CABLE: %s' % cable) + args.cable = cable + +if args.cable is not None: + if config.get('**/debug_bridge') is None: + config.set('debug_bridge', {}) + + config.get('**/debug_bridge').set('cable/type', args.cable) + +if args.port is not None: + if config.get('**/debug_bridge') is None: + config.set('debug_bridge', {}) + + if config.get('**/debug_bridge/cable') is None: + config.get('**/debug_bridge').set('cable', {}) + + if config.get('**/debug_bridge/cable/jtag-proxy') is None: + config.get('**/debug_bridge/cable').set('jtag-proxy', {}) + + config.get('**/debug_bridge/cable/jtag-proxy').set('port', args.port) + +for binary in args.binaries: + if config.get('**/runner') is None: + config.set('runner', {}) + + if config.get('**/runner/binaries') is None: + config.get('**/runner').set('binaries', []) + + config.get('**/runner').set('binaries', binary) + + + + +if len(args.command) == 0: + json_commands = config.get('**/debug_bridge/commands') + if json_commands is not None: + args.command = json_commands.get().split() + + +binaries = [] +binaries_conf = config.get('**/runner/binaries') +if binaries_conf is not None: + binaries = binaries_conf.get_dict() + +bridge = db.get_bridge(config=config, verbose=args.verbose, binaries=binaries) + +if args.ipython: + embed() + +if args.shell: + code.interact(local=locals()) + +#bridge.exec_config() + + + + + +def handle_commands(bridge): + if len(args.command) == 0: + parser.print_help() + exit(0) + + else: + for cmd in args.command: + + if commands.get(cmd) is None: + fatal_error('Unknown command: ' + cmd) + exit(1) + else: + try: + if commands.get(cmd)[1](bridge) != 0: + print () + fatal_error('the command \'%s\' has failed' % (cmd)) + exit(1) + except Exception as e: + fatal_error('the command \'%s\' has failed with an exception: %s' % (cmd, e)) + if args.debug: + raise + exit(1) + + + + + +handle_commands(bridge) diff --git a/tools/pulp-debug-bridge/include/debug_bridge/debug_bridge.h b/tools/pulp-debug-bridge/include/debug_bridge/debug_bridge.h new file mode 100644 index 00000000..54c48410 --- /dev/null +++ b/tools/pulp-debug-bridge/include/debug_bridge/debug_bridge.h @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ + +#ifndef __DEBUG_BRIDGE_DEBUG_BRIDGE_H__ +#define __DEBUG_BRIDGE_DEBUG_BRIDGE_H__ + +#define PROTOCOL_VERSION_0 0 // Initial version +#define PROTOCOL_VERSION_1 1 // Added runtime / bridge state synchronization +#define PROTOCOL_VERSION_2 2 // Added bridge to runtime requests +#define PROTOCOL_VERSION_3 3 // Added field "connected" in target state to allow bridge to reconnect several times +#define PROTOCOL_VERSION_4 4 // Added field "bridge_to_target" in requests as they are now released by the target + +#define HAL_PRINTF_BUF_SIZE 128 + +typedef enum { + HAL_BRIDGE_REQ_CONNECT = 0, + HAL_BRIDGE_REQ_DISCONNECT = 1, + HAL_BRIDGE_REQ_OPEN = 2, + HAL_BRIDGE_REQ_READ = 3, + HAL_BRIDGE_REQ_WRITE = 4, + HAL_BRIDGE_REQ_CLOSE = 5, + HAL_BRIDGE_REQ_FB_OPEN = 6, + HAL_BRIDGE_REQ_FB_UPDATE = 7, + HAL_BRIDGE_REQ_TARGET_STATUS_SYNC = 8, + HAL_BRIDGE_REQ_REPLY = 9, + HAL_BRIDGE_TARGET_REQ_EFUSE_ACCESS = 10, + HAL_BRIDGE_TARGET_REQ_EEPROM_ACCESS = 11, + HAL_BRIDGE_TARGET_REQ_BUFFER_ALLOC = 12, + HAL_BRIDGE_TARGET_REQ_BUFFER_FREE = 13, + HAL_BRIDGE_TARGET_REQ_FLASH_ACCESS = 14, + HAL_BRIDGE_TARGET_REQ_FLASH_ERASE_CHIP = 15, + HAL_BRIDGE_TARGET_REQ_FLASH_ERASE_SECTOR = 16, + HAL_BRIDGE_TARGET_REQ_FLASH_ERASE = 17, + HAL_BRIDGE_REQ_FIRST_USER = 18 +} hal_bridge_req_e; + +typedef enum { + HAL_BRIDGE_REQ_FB_FORMAT_GRAY = 1, + HAL_BRIDGE_REQ_FB_FORMAT_RGB = 2, + HAL_BRIDGE_REQ_FB_FORMAT_RAW = 3 +} hal_bridge_fb_format_e; + +typedef struct hal_bridge_req_s { + uint64_t bridge_data; + uint32_t next; + uint32_t size; + uint32_t type; + uint32_t done; + uint32_t popped; + uint32_t bridge_to_target; + union { + struct { + uint32_t name_len; + uint32_t name; + uint32_t flags; + uint32_t mode; + uint32_t retval; + } open; + struct { + uint32_t file; + uint32_t retval; + } close; + struct { + uint32_t file; + uint32_t ptr; + uint32_t len; + uint32_t retval; + } read; + struct { + uint32_t file; + uint32_t ptr; + uint32_t len; + uint32_t retval; + } write; + struct { + uint64_t screen; + uint32_t name_len; + uint32_t name; + uint32_t width; + uint32_t height; + uint32_t format; + } fb_open; + struct { + uint64_t screen; + uint32_t addr; + uint32_t posx; + uint32_t posy; + uint32_t width; + uint32_t height; + } fb_update; + struct { + uint32_t is_write; + uint32_t index; + uint32_t value; + uint32_t mask; + } efuse_access; + struct { + uint8_t itf; + uint8_t cs; + uint8_t is_write; + uint8_t padding; + uint32_t addr; + uint32_t buffer; + uint32_t size; + uint32_t retval; + } eeprom_access; + struct { + uint8_t type; + uint8_t itf; + uint8_t cs; + uint8_t is_write; + uint32_t addr; + uint32_t buffer; + uint32_t size; + uint32_t retval; + } flash_access; + struct { + uint8_t type; + uint8_t itf; + uint8_t cs; + uint8_t padding; + uint32_t retval; + } flash_erase_chip; + struct { + uint8_t type; + uint8_t itf; + uint8_t cs; + uint8_t padding; + uint32_t addr; + uint32_t retval; + } flash_erase_sector; + struct { + uint8_t type; + uint8_t itf; + uint8_t cs; + uint8_t padding; + uint32_t addr; + uint32_t size; + uint32_t retval; + } flash_erase; + struct { + uint32_t size; + uint32_t buffer; + } buffer_alloc; + struct { + uint32_t size; + uint32_t buffer; + } buffer_free; + struct { + } target_status_sync; + }; +} hal_bridge_req_t; + +typedef struct { + volatile int32_t connected; +} __attribute__((packed)) hal_target_state_t; + +typedef struct { + // The bridge will set it to one when it finds the debug struct to notify + // the target that the bridge is present. + volatile int32_t connected; +} __attribute__((packed)) hal_bridge_state_t; + +// This structure can be used to interact with the host loader +typedef struct { + + uint32_t protocol_version; + + hal_target_state_t target; + + hal_bridge_state_t bridge; + + // Used by external debug bridge to get exit status when using the board + uint32_t exit_status; + + // Printf + uint32_t use_internal_printf; + uint32_t pending_putchar; + uint32_t putc_current; + uint8_t putc_buffer[HAL_PRINTF_BUF_SIZE]; + + // Debug step, used for showing progress to host loader + uint32_t debug_step; + uint32_t debug_step_pending; + + // Requests + uint32_t first_req; + uint32_t last_req; + uint32_t first_bridge_req; + uint32_t first_bridge_free_req; + uint32_t target_req; + + uint32_t notif_req_addr; + uint32_t notif_req_value; + +} __attribute__((packed)) hal_debug_struct_t; + +#endif \ No newline at end of file diff --git a/tools/pulp-debug-bridge/include/debug_bridge/proxy.hpp b/tools/pulp-debug-bridge/include/debug_bridge/proxy.hpp new file mode 100644 index 00000000..2e8ea8c2 --- /dev/null +++ b/tools/pulp-debug-bridge/include/debug_bridge/proxy.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ + +#ifndef __DEBUG_BRIDGE_PROXY_HPP__ +#define __DEBUG_BRIDGE_PROXY_HPP__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + DEBUG_BRIDGE_JTAG_REQ = 0, + DEBUG_BRIDGE_RESET_REQ = 1, + DEBUG_BRIDGE_CONFIG_REQ = 2 +} proxy_req_type_e; + +typedef enum { + DEBUG_BRIDGE_JTAG_TDI = 0, + DEBUG_BRIDGE_JTAG_TMS = 1, + DEBUG_BRIDGE_JTAG_TRST = 2 +} proxy_req_jtag_cycle_e; + +typedef struct { + int32_t type; + + union { + struct { + int32_t bits; + int8_t tdo; + } jtag; + struct { + int32_t active; + int32_t duration; + } reset; + struct { + int32_t value; + } config; + }; + +} proxy_req_t; + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/tools/pulp-debug-bridge/python/bridge/chips/arnold.py b/tools/pulp-debug-bridge/python/bridge/chips/arnold.py new file mode 100644 index 00000000..1f1aff3c --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/arnold.py @@ -0,0 +1,108 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import time + +JTAG_SOC_AXIREG = 4 + +JTAG_SOC_CONFREG = 7 +JTAG_SOC_CONFREG_WIDTH = 9 + +BOOT_MODE_JTAG = 1 +BOOT_MODE_JTAG_HYPER = 11 +CONFREG_BOOT_WAIT = 1 +CONFREG_PGM_LOADED = 1 +CONFREG_INIT = 0 + +class arnold_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False, fimages=[]): + super(arnold_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + self.fimages = fimages + self.start_cores = False + + def stop(self): + + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + if self.verbose: + print ("Notifying to boot code that we are doing a JTAG boot") + self.get_cable().jtag_reset(True) + self.get_cable().jtag_reset(False) + self.get_cable().chip_reset(True) + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) + self.get_cable().chip_reset(False) + + + # Now wait until the boot code tells us we can load the code + if self.verbose: + print ("Waiting for notification from boot code") + while True: + reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) + if reg_value == CONFREG_BOOT_WAIT: + break + print ("Received for notification from boot code") + + # Stall the FC + self.write(0x1A110000, 4, [0, 0, 1, 0]) + + print ("Stopped core") + + return 0 + + def reset(self): + self.stop() + return 0 + + + def load_jtag(self, binaries): + + if self.verbose: + print ('Loading binary through jtag') + + if self.stop() != 0: + return -1 + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + # Be careful to set the new PC only after the code is loaded as the prefetch + # buffer is immediately fetching instructions and would get wrong instructions + self.write(0x1A112000, 4, [0x80, 0x80, 0x00, 0x1c]) + + self.start_cores = True + + return 0 + + + def start(self): + + if self.start_cores: + print ('Starting execution') + + # Unstall the FC so that it starts fetching instructions from the loaded binary + self.write(0x1A110000, 4, [0, 0, 0, 0]) + + return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/fulmine.py b/tools/pulp-debug-bridge/python/bridge/chips/fulmine.py new file mode 100644 index 00000000..da7cc2b1 --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/fulmine.py @@ -0,0 +1,33 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * + + +class fulmine_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False): + super(fulmine_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + # We have to use the soc tap on fulmine + if config.get('**/debug_bridge/cable/tap') is not None: + config.get('**/debug_bridge/cable').set('tap', 1) + + def start(self): + self.write_32(0x10200008, 0x1) + return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/gap.py b/tools/pulp-debug-bridge/python/bridge/chips/gap.py new file mode 100644 index 00000000..97bc0f41 --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/gap.py @@ -0,0 +1,217 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import time + +JTAG_SOC_AXIREG = 4 + +JTAG_SOC_CONFREG = 7 +JTAG_SOC_CONFREG_WIDTH = 4 + +BOOT_MODE_JTAG = 4 +BOOT_MODE_JTAG_HYPER = 11 +CONFREG_BOOT_WAIT = 1 +CONFREG_PGM_LOADED = 1 +CONFREG_INIT = 0 + +class gap_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False, fimages=[]): + super(gap_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + self.fimages = fimages + self.start_cores = False + + def stop(self): + + + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + if self.verbose: + print ("Notifying to boot code that we are doing a JTAG boot") + self.get_cable().chip_reset(True) + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG) + self.get_cable().chip_reset(False) + + # Removed synchronization with boot code due to HW bug, it is better + # to stop fc as soon as possible + +# # Now wait until the boot code tells us we can load the code +# if self.verbose: +# print ("Waiting for notification from boot code") +# while True: +# reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG) +# if reg_value == CONFREG_BOOT_WAIT: +# break +# print ("Received for notification from boot code") + + # Stall the FC + self.write(0x1B300000, 4, [0, 0, 1, 0]) + + # Configure FLL with no lock to avoid the HW bug with fll + #self.write_32(0x1a100004, 0x840005f5) + #self.write_32(0x1a100008, 0x8100410b) + + return 0 + + + def load_jtag(self, binaries): + + if self.verbose: + print ('Loading binary through jtag') + + if self.stop() != 0: + return -1 + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + with open(binaries[0], 'rb') as file: + entry = ELFFile(file).header['e_entry'] + # Be careful to set the new PC only after the code is loaded as the prefetch + # buffer is immediately fetching instructions and would get wrong instructions + self.write_32(0x1B302000, entry) + + self.start_cores = True + + return 0 + + + def start(self): + + if self.start_cores: + print ('Starting execution') + + # Unstall the FC so that it starts fetching instructions from the loaded binary + self.write(0x1B300000, 4, [0, 0, 0, 0]) + + return 0 + + + def load_jtag_hyper(self, binaries): + + if self.verbose: + print ('Loading binary through jtag_hyper') + + # Reset the chip and tell him we want to load from hyper + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + if self.verbose: + print ("Notifying to boot code that we are doing a JTAG boot from hyperflash") + self.get_cable().chip_reset(True) + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG_HYPER) + self.get_cable().chip_reset(False) + + return 0 + + + def flash(self, fimages): + MAX_BUFF_SIZE = (350*1024) + f_path = fimages[0] + addrHeader = self._get_binary_symbol_addr('flasherHeader') + addrImgRdy = addrHeader + addrFlasherRdy = addrHeader + 4 + addrFlashAddr = addrHeader + 8 + addrIterTime = addrHeader + 12 + addrBufSize = addrHeader + 16 + # open the file in read binary mode + f_img = open(f_path, 'rb') + f_size = os.path.getsize(f_path) + lastSize = f_size % MAX_BUFF_SIZE; + if(lastSize): + n_iter = f_size // MAX_BUFF_SIZE + 1; + else: + n_iter = f_size // MAX_BUFF_SIZE + + flasher_ready = self.read_32(addrFlasherRdy) + while(flasher_ready == 0): + flasher_ready = self.read_32(addrFlasherRdy) + flasher_ready = 0; + addrBuffer = self.read_32((addrHeader+20)) + indexAddr = 0 + self.write_32(addrFlashAddr, 0) + self.write_32(addrIterTime, n_iter) + for i in range(n_iter): + if (lastSize and i == (n_iter-1)): + buff_data = f_img.read(lastSize) + self.write(addrBuffer, lastSize, buff_data) + self.write_32(addrBufSize, ((lastSize + 3) & ~3)) + else: + buff_data = f_img.read(MAX_BUFF_SIZE) + self.write(addrBuffer, MAX_BUFF_SIZE, buff_data) + self.write_32(addrBufSize, MAX_BUFF_SIZE) + self.write_32(addrImgRdy, 1) + self.write_32(addrFlasherRdy, 0) + if (i!=(n_iter-1)): + flasher_ready = self.read_32(addrFlasherRdy) + while(flasher_ready == 0): + flasher_ready = self.read_32(addrFlasherRdy) + f_img.close() + return 0 + + + def load_jtag_old(self): + + if self.verbose: + print ('Loading binary through jtag') + + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + if self.verbose: + print ("Notifying to boot code that we are doing a JTAG boot") + self.get_cable().chip_reset(True) + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG) + self.get_cable().chip_reset(False) + + # Now wait until the boot code tells us we can load the code + if self.verbose: + print ("Waiting for notification from boot code") + while True: + reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG) + if reg_value == CONFREG_BOOT_WAIT: + break + print ("Received for notification from boot code") + + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in self.binaries: + if self.load_elf(binary=binary): + return 1 + + return 0 + + + def start_old(self): + + # And notify the boot code that the binary is ready + if self.verbose: + print ("Notifying to boot code that the binary is loaded") + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, CONFREG_PGM_LOADED) + + return 0 + + diff --git a/tools/pulp-debug-bridge/python/bridge/chips/gap8_revc.py b/tools/pulp-debug-bridge/python/bridge/chips/gap8_revc.py new file mode 100644 index 00000000..d09c7e8a --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/gap8_revc.py @@ -0,0 +1,237 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import time + +JTAG_SOC_AXIREG = 4 + +JTAG_SOC_CONFREG = 7 +JTAG_SOC_CONFREG_WIDTH = 4 + +BOOT_MODE_JTAG = 1 +BOOT_MODE_JTAG_HYPER = 11 +CONFREG_BOOT_WAIT = 1 +CONFREG_PGM_LOADED = 1 +CONFREG_INIT = 0 + +class gap_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False, fimages=[]): + super(gap_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + self.fimages = fimages + self.start_cores = False + self.boot_mode = None + self.stopped = False + + def reset(self, jtag_boot=True): + self.get_cable().jtag_reset(True) + self.get_cable().jtag_reset(False) + self.get_cable().chip_reset(True) + if jtag_boot: + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) + self.get_cable().chip_reset(False) + return 0 + + def set_boot_mode(self, boot_mode, reset=True): + if self.verbose: + print ("Notifying to boot code new boot mode (mode: %d)" % boot_mode) + if reset: + self.get_cable().chip_reset(True) + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (boot_mode << 1) | 1) + + if reset: + self.get_cable().chip_reset(False) + self.boot_mode = boot_mode + + def wait_available(self): + if self.verbose: + print ("Waiting for target to be available") + + boot_mode = 0 + if self.boot_mode is not None: + boot_mode = (self.boot_mode << 1) | 1 + + # Loop until we see bit 0 becoming 1, this will indicate that the + # target is ready to accept bridge requests + while True: + reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, boot_mode) + + rt_req = (reg_value >> 1) & 0x7 + + if rt_req == 4 or rt_req == 1: + break + + if self.verbose: + print ("Target is available") + + + def wait_ready(self, boot_mode=None): + if boot_mode is None: + boot_mode = self.boot_mode + + if boot_mode is None: + print ('Can not wait for boot code if the boot mode is unknown') + return -1 + + if self.verbose: + print ("Waiting for notification from boot code") + while True: + reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (boot_mode << 1) | 1) + + if reg_value == CONFREG_BOOT_WAIT: + break + print ("Received for notification from boot code") + + return 0 + + def stop(self): + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + self.set_boot_mode(BOOT_MODE_JTAG) + + # Now wait until the boot code tells us we can load the code + if self.wait_ready() != 0: + return -1 + + # Stall the FC + self.write(0x1B300000, 4, [0, 0, 1, 0]) + + self.stopped = True + + return 0 + + def pause_for_conf(self): + self.set_boot_mode(4) + if self.wait_ready() != 0: + return -1 + return 0 + + def resume_after_conf(self, boot_mode): + self.set_boot_mode(5, reset=False) + while True: + reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (5 << 1) | 1) + if (reg_value & 1) == 0: + break + + self.set_boot_mode(boot_mode, reset=False) + + return 0 + + def clear(self): + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, 0) + self.boot_mode = None + + + def load_jtag(self, binaries): + + if self.verbose: + print ('Loading binary through jtag') + + if self.stop() != 0: + return -1 + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + # Be careful to set the new PC only after the code is loaded as the prefetch + # buffer is immediately fetching instructions and would get wrong instructions + self.write(0x1B302000, 4, [0x80, 0x00, 0x00, 0x1c]) + + self.start_cores = True + + return 0 + + + def start(self): + + if self.start_cores: + print ('Starting execution') + + # Unstall the FC so that it starts fetching instructions from the loaded binary + self.write(0x1B300000, 4, [0, 0, 0, 0]) + + return 0 + + + def load_jtag_hyper(self, binaries): + + if self.verbose: + print ('Loading binary through jtag_hyper') + + # Reset the chip and tell him we want to load from hyper + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + if self.verbose: + print ("Notifying to boot code that we are doing a JTAG boot from hyperflash") + self.get_cable().chip_reset(True) + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG_HYPER) + self.get_cable().chip_reset(False) + + return 0 + + + def flash(self, fimages): + MAX_BUFF_SIZE = (350*1024) + f_path = fimages[0] + addrHeader = self._get_binary_symbol_addr('flasherHeader') + addrImgRdy = addrHeader + addrFlasherRdy = addrHeader + 4 + addrFlashAddr = addrHeader + 8 + addrIterTime = addrHeader + 12 + addrBufSize = addrHeader + 16 + # open the file in read binary mode + f_img = open(f_path, 'rb') + f_size = os.path.getsize(f_path) + lastSize = f_size % MAX_BUFF_SIZE; + if(lastSize): + n_iter = f_size // MAX_BUFF_SIZE + 1; + else: + n_iter = f_size // MAX_BUFF_SIZE + + flasher_ready = self.read_32(addrFlasherRdy) + while(flasher_ready == 0): + flasher_ready = self.read_32(addrFlasherRdy) + flasher_ready = 0; + addrBuffer = self.read_32((addrHeader+20)) + indexAddr = 0 + self.write_32(addrFlashAddr, 0) + self.write_32(addrIterTime, n_iter) + for i in range(n_iter): + if (lastSize and i == (n_iter-1)): + buff_data = f_img.read(lastSize) + self.write(addrBuffer, lastSize, buff_data) + self.write_32(addrBufSize, ((lastSize + 3) & ~3)) + else: + buff_data = f_img.read(MAX_BUFF_SIZE) + self.write(addrBuffer, MAX_BUFF_SIZE, buff_data) + self.write_32(addrBufSize, MAX_BUFF_SIZE) + self.write_32(addrImgRdy, 1) + self.write_32(addrFlasherRdy, 0) + if (i!=(n_iter-1)): + flasher_ready = self.read_32(addrFlasherRdy) + while(flasher_ready == 0): + flasher_ready = self.read_32(addrFlasherRdy) + f_img.close() + return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/gap_rev1.py b/tools/pulp-debug-bridge/python/bridge/chips/gap_rev1.py new file mode 100644 index 00000000..d09c7e8a --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/gap_rev1.py @@ -0,0 +1,237 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import time + +JTAG_SOC_AXIREG = 4 + +JTAG_SOC_CONFREG = 7 +JTAG_SOC_CONFREG_WIDTH = 4 + +BOOT_MODE_JTAG = 1 +BOOT_MODE_JTAG_HYPER = 11 +CONFREG_BOOT_WAIT = 1 +CONFREG_PGM_LOADED = 1 +CONFREG_INIT = 0 + +class gap_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False, fimages=[]): + super(gap_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + self.fimages = fimages + self.start_cores = False + self.boot_mode = None + self.stopped = False + + def reset(self, jtag_boot=True): + self.get_cable().jtag_reset(True) + self.get_cable().jtag_reset(False) + self.get_cable().chip_reset(True) + if jtag_boot: + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) + self.get_cable().chip_reset(False) + return 0 + + def set_boot_mode(self, boot_mode, reset=True): + if self.verbose: + print ("Notifying to boot code new boot mode (mode: %d)" % boot_mode) + if reset: + self.get_cable().chip_reset(True) + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (boot_mode << 1) | 1) + + if reset: + self.get_cable().chip_reset(False) + self.boot_mode = boot_mode + + def wait_available(self): + if self.verbose: + print ("Waiting for target to be available") + + boot_mode = 0 + if self.boot_mode is not None: + boot_mode = (self.boot_mode << 1) | 1 + + # Loop until we see bit 0 becoming 1, this will indicate that the + # target is ready to accept bridge requests + while True: + reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, boot_mode) + + rt_req = (reg_value >> 1) & 0x7 + + if rt_req == 4 or rt_req == 1: + break + + if self.verbose: + print ("Target is available") + + + def wait_ready(self, boot_mode=None): + if boot_mode is None: + boot_mode = self.boot_mode + + if boot_mode is None: + print ('Can not wait for boot code if the boot mode is unknown') + return -1 + + if self.verbose: + print ("Waiting for notification from boot code") + while True: + reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (boot_mode << 1) | 1) + + if reg_value == CONFREG_BOOT_WAIT: + break + print ("Received for notification from boot code") + + return 0 + + def stop(self): + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + self.set_boot_mode(BOOT_MODE_JTAG) + + # Now wait until the boot code tells us we can load the code + if self.wait_ready() != 0: + return -1 + + # Stall the FC + self.write(0x1B300000, 4, [0, 0, 1, 0]) + + self.stopped = True + + return 0 + + def pause_for_conf(self): + self.set_boot_mode(4) + if self.wait_ready() != 0: + return -1 + return 0 + + def resume_after_conf(self, boot_mode): + self.set_boot_mode(5, reset=False) + while True: + reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (5 << 1) | 1) + if (reg_value & 1) == 0: + break + + self.set_boot_mode(boot_mode, reset=False) + + return 0 + + def clear(self): + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, 0) + self.boot_mode = None + + + def load_jtag(self, binaries): + + if self.verbose: + print ('Loading binary through jtag') + + if self.stop() != 0: + return -1 + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + # Be careful to set the new PC only after the code is loaded as the prefetch + # buffer is immediately fetching instructions and would get wrong instructions + self.write(0x1B302000, 4, [0x80, 0x00, 0x00, 0x1c]) + + self.start_cores = True + + return 0 + + + def start(self): + + if self.start_cores: + print ('Starting execution') + + # Unstall the FC so that it starts fetching instructions from the loaded binary + self.write(0x1B300000, 4, [0, 0, 0, 0]) + + return 0 + + + def load_jtag_hyper(self, binaries): + + if self.verbose: + print ('Loading binary through jtag_hyper') + + # Reset the chip and tell him we want to load from hyper + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + if self.verbose: + print ("Notifying to boot code that we are doing a JTAG boot from hyperflash") + self.get_cable().chip_reset(True) + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, BOOT_MODE_JTAG_HYPER) + self.get_cable().chip_reset(False) + + return 0 + + + def flash(self, fimages): + MAX_BUFF_SIZE = (350*1024) + f_path = fimages[0] + addrHeader = self._get_binary_symbol_addr('flasherHeader') + addrImgRdy = addrHeader + addrFlasherRdy = addrHeader + 4 + addrFlashAddr = addrHeader + 8 + addrIterTime = addrHeader + 12 + addrBufSize = addrHeader + 16 + # open the file in read binary mode + f_img = open(f_path, 'rb') + f_size = os.path.getsize(f_path) + lastSize = f_size % MAX_BUFF_SIZE; + if(lastSize): + n_iter = f_size // MAX_BUFF_SIZE + 1; + else: + n_iter = f_size // MAX_BUFF_SIZE + + flasher_ready = self.read_32(addrFlasherRdy) + while(flasher_ready == 0): + flasher_ready = self.read_32(addrFlasherRdy) + flasher_ready = 0; + addrBuffer = self.read_32((addrHeader+20)) + indexAddr = 0 + self.write_32(addrFlashAddr, 0) + self.write_32(addrIterTime, n_iter) + for i in range(n_iter): + if (lastSize and i == (n_iter-1)): + buff_data = f_img.read(lastSize) + self.write(addrBuffer, lastSize, buff_data) + self.write_32(addrBufSize, ((lastSize + 3) & ~3)) + else: + buff_data = f_img.read(MAX_BUFF_SIZE) + self.write(addrBuffer, MAX_BUFF_SIZE, buff_data) + self.write_32(addrBufSize, MAX_BUFF_SIZE) + self.write_32(addrImgRdy, 1) + self.write_32(addrFlasherRdy, 0) + if (i!=(n_iter-1)): + flasher_ready = self.read_32(addrFlasherRdy) + while(flasher_ready == 0): + flasher_ready = self.read_32(addrFlasherRdy) + f_img.close() + return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/pulp.py b/tools/pulp-debug-bridge/python/bridge/chips/pulp.py new file mode 100644 index 00000000..46aa79c8 --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/pulp.py @@ -0,0 +1,85 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import time + +JTAG_RISCV_IRLEN = 5 +JTAG_RISCV_BYPASS = 0x1f + +JTAG_SOC_CONFREG_ID = 6 +JTAG_SOC_CONFREG = (JTAG_SOC_CONFREG_ID << 0) | (JTAG_RISCV_BYPASS << 4) +JTAG_SOC_CONFREG_WIDTH = 8 + 1 +JTAG_SOC_IRLEN = 4 + +JTAG_IRLEN = JTAG_SOC_IRLEN + JTAG_RISCV_IRLEN + +class pulp_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False): + + super(pulp_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + def load_jtag(self, binaries): + + if self.verbose: + print ('Loading binary through jtag') + + #if self.stop(): + # return -1 + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + return 0 + + + def start(self): + # First stall the core + self.write_dmi(0x10, 0x00000001) # DMACTIVE + + self.write_dmi(0x10, 0x03E00001) # HART SEL + self.write_dmi(0x10, 0x83E00001) # HALT REQ + + # Wait until it is halted + while True: + status = self.read_dmi(0x11) + + if ((status >> 9) & 1) == 1: + break + + # Set PC + self.write_dmi(0x04, 0x1c008080) # PC into DATA0 + self.write_dmi(0x17, 0x00230000 | 0x7b1) # Abstract cmd to set DPC + + # Resume the core + self.write_dmi(0x10, 0x43E00001) + + return 0 + + def write_dmi(self, reg, value): + self.write_reg_int(reg, value, 4, 1) # DMACTIVE + + + + def read_dmi(self, reg): + return self.read_reg_int(reg, 4, 1) # DMACTIVE diff --git a/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo.py b/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo.py new file mode 100644 index 00000000..1ebe5fa9 --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo.py @@ -0,0 +1,85 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import time + +JTAG_RISCV_IRLEN = 5 +JTAG_RISCV_BYPASS = 0x1f + +JTAG_SOC_CONFREG_ID = 6 +JTAG_SOC_CONFREG = (JTAG_SOC_CONFREG_ID << 0) | (JTAG_RISCV_BYPASS << 4) +JTAG_SOC_CONFREG_WIDTH = 8 + 1 +JTAG_SOC_IRLEN = 4 + +JTAG_IRLEN = JTAG_SOC_IRLEN + JTAG_RISCV_IRLEN + +class pulpissimo_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False): + + super(pulpissimo_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + def load_jtag(self, binaries): + + if self.verbose: + print ('Loading binary through jtag') + + #if self.stop(): + # return -1 + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + return 0 + + + def start(self): + # First stall the core + self.write_dmi(0x10, 0x00000001) # DMACTIVE + + self.write_dmi(0x10, 0x03E00001) # HART SEL + self.write_dmi(0x10, 0x83E00001) # HALT REQ + + # Wait until it is halted + while True: + status = self.read_dmi(0x11) + + if ((status >> 9) & 1) == 1: + break + + # Set PC + self.write_dmi(0x04, 0x1c008080) # PC into DATA0 + self.write_dmi(0x17, 0x00230000 | 0x7b1) # Abstract cmd to set DPC + + # Resume the core + self.write_dmi(0x10, 0x43E00001) + + return 0 + + def write_dmi(self, reg, value): + self.write_reg_int(reg, value, 4, 1) # DMACTIVE + + + + def read_dmi(self, reg): + return self.read_reg_int(reg, 4, 1) # DMACTIVE diff --git a/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo_v2.py b/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo_v2.py new file mode 100644 index 00000000..ab77c564 --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/pulpissimo_v2.py @@ -0,0 +1,108 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import time + +JTAG_SOC_AXIREG = 4 + +JTAG_SOC_CONFREG = 7 +JTAG_SOC_CONFREG_WIDTH = 9 + +BOOT_MODE_JTAG = 1 +BOOT_MODE_JTAG_HYPER = 11 +CONFREG_BOOT_WAIT = 1 +CONFREG_PGM_LOADED = 1 +CONFREG_INIT = 0 + +class pulpissimo_v2_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False, fimages=[]): + super(pulpissimo_v2_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + self.fimages = fimages + self.start_cores = False + + def stop(self): + + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + if self.verbose: + print ("Notifying to boot code that we are doing a JTAG boot") + self.get_cable().jtag_reset(True) + self.get_cable().jtag_reset(False) + self.get_cable().chip_reset(True) + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) + self.get_cable().chip_reset(False) + + + # Now wait until the boot code tells us we can load the code + if self.verbose: + print ("Waiting for notification from boot code") + while True: + reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, (BOOT_MODE_JTAG << 1) | 1) + if reg_value == CONFREG_BOOT_WAIT: + break + print ("Received for notification from boot code") + + # Stall the FC + self.write(0x1A110000, 4, [0, 0, 1, 0]) + + print ("Stopped core") + + return 0 + + def reset(self): + self.stop() + return 0 + + + def load_jtag(self, binaries): + + if self.verbose: + print ('Loading binary through jtag') + + if self.stop() != 0: + return -1 + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + # Be careful to set the new PC only after the code is loaded as the prefetch + # buffer is immediately fetching instructions and would get wrong instructions + self.write(0x1A112000, 4, [0x80, 0x80, 0x00, 0x1c]) + + self.start_cores = True + + return 0 + + + def start(self): + + if self.start_cores: + print ('Starting execution') + + # Unstall the FC so that it starts fetching instructions from the loaded binary + self.write(0x1A110000, 4, [0, 0, 0, 0]) + + return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/usoc_v1.py b/tools/pulp-debug-bridge/python/bridge/chips/usoc_v1.py new file mode 100644 index 00000000..ec062f75 --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/usoc_v1.py @@ -0,0 +1,90 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import time + +class usoc_v1_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False): + + super(usoc_v1_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + self.start_cores = False + + + def load_jtag(self, binaries): + + if self.verbose: + print ('Loading binary through jtag') + + if self.stop(): + return -1 + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + # Be careful to set the new PC only after the code is loaded as the prefetch + # buffer is immediately fetching instructions and would get wrong instructions + self.write(0x1A112000, 4, [0x80, 0x80, 0x00, 0x1c]) + + self.start_cores = True + + return 0 + + + def start(self): + + if self.start_cores: + # Unstall the FC so that it starts fetching instructions from the loaded binary + self.write(0x1A110000, 4, [0, 0, 0, 0]) + + return 0 + + def stop(self): + + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + if self.verbose: + print ("Stalling the FC") + + self.get_cable().chip_reset(True) + self.get_cable().chip_reset(False) + + # Stall the FC as when the reset is released it just tries to load from flash + while True: + # on usoc_v1, the core will start only after a while when the reset + # is released, so the first accesses we do may not have any effect. + # As a aworkaround we have to try stopping it several time. + # Another issue on RTL simulation is that the core is not + # functional anymore if we let him execute a branch with X + # which happens if we let it run too long before we stop it. + # Due to that we cannot read the stall status before we stop it. + for i in range(0, 10): + self.write(0x1A110000, 4, [0, 0, 1, 0]) + + value = self.read_32(0x1A110000) + if (value >> 16) & 1 == 1: + break + + return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/vega.py b/tools/pulp-debug-bridge/python/bridge/chips/vega.py new file mode 100644 index 00000000..5a1e977d --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/vega.py @@ -0,0 +1,182 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import time + +JTAG_RISCV_IRLEN = 5 +JTAG_RISCV_BYPASS = 0x1f + +JTAG_SOC_CONFREG_ID = 6 +JTAG_SOC_CONFREG = (JTAG_SOC_CONFREG_ID << 5) | (JTAG_RISCV_BYPASS << 0) +JTAG_SOC_CONFREG_WIDTH = 8 + 1 +JTAG_SOC_IRLEN = 4 + +JTAG_IRLEN = JTAG_SOC_IRLEN + JTAG_RISCV_IRLEN + +class vega_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False): + + super(vega_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + self.start_cores = False + self.first_reset = True + self.boot_mode = None + + + def reset(self, stop=True): + + + + + if self.first_reset: + # The first time, we need to wait enough time to let the voltage + # regulator converge + self.get_cable().chip_reset(True, 5000000) + self.first_reset = False + + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + + # Use bootsel pad to tell boot code to stop + if stop: + self.get_cable().chip_config(1) + + # Due to voltage convergence and so on we need to wait + # 200ms when the reset is low + #self.get_cable().chip_reset(True, 200000000) + self.get_cable().chip_reset(True, 100000000) + # It also takes some time before the JTAG is ready + self.get_cable().chip_reset(False, 4000000) + + #self.get_cable().jtag_reset(True) + self.get_cable().jtag_reset(False) + + return 0 + + + def wait_eoc(self): + + while True: + value = self.read_32(0x1a1040a0) + + if (value >> 31) == 1: + return value & 0x7fffffff + + time.sleep(0.1) + + + def jtag_hyper_boot(self): + + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, ((((2 << 0) | (1<<3)) << 1) | 1) << 1, JTAG_IRLEN) + + + def jtag_mram_boot(self): + + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, ((((2 << 0) | (2<<3)) << 1) | 1) << 1, JTAG_IRLEN) + + + def jtag_spim_boot(self): + + self.get_cable().jtag_set_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, ((((2 << 0) | (0<<3)) << 1) | 1) << 1, JTAG_IRLEN) + + + def load_jtag(self, binaries): + + if self.verbose: + print ('Loading binary through jtag') + + #if self.stop(): + # return -1 + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + return 0 + + + def start(self): + # First stall the core + self.write_dmi(0x10, 0x00000001) # DMACTIVE + + self.write_dmi(0x10, 0x03E00001) # HART SEL + self.write_dmi(0x10, 0x83E00001) # HALT REQ + + # Wait until it is halted + while True: + status = self.read_dmi(0x11) + + if ((status >> 9) & 1) == 1: + break + + # Set PC + self.write_dmi(0x04, 0x1c008080) # PC into DATA0 + self.write_dmi(0x17, 0x00230000 | 0x7b1) # Abstract cmd to set DPC + + # Resume the core + self.write_dmi(0x10, 0x43E00001) + + return 0 + + + + def clear(self): + self.get_cable().chip_config(0) + + + + def wait_available(self): + + boot_mode = 0 + if self.boot_mode is not None: + boot_mode = (self.boot_mode << 1) | 1 + + # Loop until we see bit 0 becoming 1, this will indicate that the + # target is ready to accept bridge requests + while True: + reg_value = self.get_cable().jtag_get_reg(JTAG_SOC_CONFREG, JTAG_SOC_CONFREG_WIDTH, boot_mode, JTAG_IRLEN) >> 1 + + rt_req = (reg_value >> 1) & 0x7 + + if rt_req == 4 or rt_req == 1: + break + + if self.verbose: + print ("Target is available") + + + + def write_dmi(self, reg, value): + self.write_reg_int(reg, value, 4, 0) # DMACTIVE + + + + def read_dmi(self, reg): + return self.read_reg_int(reg, 4, 0) # DMACTIVE + + + + def stop(self): + + return 0 \ No newline at end of file diff --git a/tools/pulp-debug-bridge/python/bridge/chips/vivosoc3.py b/tools/pulp-debug-bridge/python/bridge/chips/vivosoc3.py new file mode 100644 index 00000000..935db4a8 --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/vivosoc3.py @@ -0,0 +1,36 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * + +class vivosoc3_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False): + + super(vivosoc3_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + + def reset(self, stop=True): + + self.get_cable().chip_config(1) + + self.get_cable().chip_reset(True, 1000000) + + self.get_cable().chip_reset(False) + + return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/chips/wolfe.py b/tools/pulp-debug-bridge/python/bridge/chips/wolfe.py new file mode 100644 index 00000000..c8c9fc5f --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/chips/wolfe.py @@ -0,0 +1,90 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import time + +class wolfe_debug_bridge(debug_bridge): + + def __init__(self, config, binaries=[], verbose=False): + + super(wolfe_debug_bridge, self).__init__(config=config, binaries=binaries, verbose=verbose) + + self.start_cores = False + + + def load_jtag(self, binaries): + + if self.verbose: + print ('Loading binary through jtag') + + if self.stop(): + return -1 + + # Load the binary through jtag + if self.verbose: + print ("Loading binaries") + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + # Be careful to set the new PC only after the code is loaded as the prefetch + # buffer is immediately fetching instructions and would get wrong instructions + self.write(0x1A112000, 4, [0x80, 0x80, 0x00, 0x1c]) + + self.start_cores = True + + return 0 + + + def start(self): + + if self.start_cores: + # Unstall the FC so that it starts fetching instructions from the loaded binary + self.write(0x1A110000, 4, [0, 0, 0, 0]) + + return 0 + + def stop(self): + + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + if self.verbose: + print ("Stalling the FC") + + self.get_cable().chip_reset(True) + self.get_cable().chip_reset(False) + + # Stall the FC as when the reset is released it just tries to load from flash + while True: + # on Wolfe, the core will start only after a while when the reset + # is released, so the first accesses we do may not have any effect. + # As a aworkaround we have to try stopping it several time. + # Another issue on RTL simulation is that the core is not + # functional anymore if we let him execute a branch with X + # which happens if we let it run too long before we stop it. + # Due to that we cannot read the stall status before we stop it. + for i in range(0, 10): + self.write(0x1A110000, 4, [0, 0, 1, 0]) + + value = self.read_32(0x1A110000) + if (value >> 16) & 1 == 1: + break + + return 0 diff --git a/tools/pulp-debug-bridge/python/bridge/debug_bridge.py b/tools/pulp-debug-bridge/python/bridge/debug_bridge.py new file mode 100644 index 00000000..e4c4918d --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/debug_bridge.py @@ -0,0 +1,66 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +from bridge.default_debug_bridge import * +import bridge.chips.gap as gap +import bridge.chips.gap_rev1 as gap_rev1 +import bridge.chips.gap8_revc as gap8_revc +import bridge.chips.wolfe as wolfe +import bridge.chips.usoc_v1 as usoc_v1 +import bridge.chips.vega as vega +import bridge.chips.arnold as arnold +import bridge.chips.fulmine as fulmine +import bridge.chips.pulpissimo as pulpissimo +import bridge.chips.pulp as pulp +import bridge.chips.vivosoc3 as vivosoc3 + + +def get_bridge(config, binaries=[], verbose=False): + + chip_config = config.get('**/board/chip') + if chip_config is None: + raise Exception('Wrong JSON configuration, do not contain any chip information') + + chip = config.get('**/board/chip').get('name').get() + + if chip == 'gap': + bridge_class = gap.gap_debug_bridge + elif chip == 'gap_rev1': + bridge_class = gap_rev1.gap_debug_bridge + elif chip == 'gap8_revc': + bridge_class = gap8_revc.gap_debug_bridge + elif chip == 'fulmine' or chip == 'vivosoc2' or chip == 'vivosoc2_1': + bridge_class = fulmine.fulmine_debug_bridge + elif chip == 'wolfe': + bridge_class = wolfe.wolfe_debug_bridge + elif chip == 'usoc_v1': + bridge_class = usoc_v1.usoc_v1_debug_bridge + elif chip == 'vega': + bridge_class = vega.vega_debug_bridge + elif chip == 'arnold': + bridge_class = arnold.arnold_debug_bridge + elif chip == 'pulp': + bridge_class = pulp.pulp_debug_bridge + elif chip == 'pulpissimo': + bridge_class = pulpissimo.pulpissimo_debug_bridge + elif chip == 'vivosoc3': + bridge_class = vivosoc3.vivosoc3_debug_bridge + else: + bridge_class = debug_bridge + + return bridge_class(config=config, binaries=binaries, verbose=verbose) diff --git a/tools/pulp-debug-bridge/python/bridge/default_debug_bridge.py b/tools/pulp-debug-bridge/python/bridge/default_debug_bridge.py new file mode 100644 index 00000000..64b0b4e2 --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/default_debug_bridge.py @@ -0,0 +1,576 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +import ctypes +import os +import os.path +import json_tools as js +from elftools.elf.elffile import ELFFile +import time + + +class Ctype_cable(object): + + def __init__(self, module, config, system_config): + + self.module = module + self.gdb_handle = None + + # Register entry points with appropriate arguments + self.module.cable_new.argtypes = [ctypes.c_char_p, ctypes.c_char_p] + self.module.cable_new.restype = ctypes.c_void_p + + self.module.cable_write.argtypes = \ + [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_char_p] + + self.module.cable_read.argtypes = \ + [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_char_p] + + self.module.cable_reg_write.argtypes = \ + [ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_int] + + self.module.cable_reg_read.argtypes = \ + [ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p] + + self.module.chip_reset.argtypes = \ + [ctypes.c_void_p, ctypes.c_bool, ctypes.c_int] + + self.module.chip_config.argtypes = \ + [ctypes.c_void_p, ctypes.c_int] + + self.module.jtag_reset.argtypes = \ + [ctypes.c_void_p, ctypes.c_bool] + + self.module.jtag_soft_reset.argtypes = \ + [ctypes.c_void_p] + + self.module.cable_jtag_set_reg.argtypes = \ + [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_int] + self.module.cable_jtag_set_reg.restype = ctypes.c_bool + + self.module.cable_jtag_get_reg.argtypes = \ + [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.c_int] + self.module.cable_jtag_get_reg.restype = ctypes.c_bool + + self.module.cable_lock.argtypes = \ + [ctypes.c_void_p] + + self.module.cable_unlock.argtypes = \ + [ctypes.c_void_p] + + self.module.bridge_get_error.argtypes = [] + self.module.bridge_get_error.restype = ctypes.c_char_p + + self.module.bridge_init.argtypes = [ctypes.c_char_p, ctypes.c_int] + + self.module.gdb_server_open.argtypes = [ctypes.c_void_p, ctypes.c_int] + self.module.gdb_server_open.restype = ctypes.c_void_p + + self.module.gdb_server_close.argtypes = [ctypes.c_void_p, ctypes.c_int] + + config_string = None + + if config is not None: + config_string = config.dump_to_string().encode('utf-8') + + self.instance = self.module.cable_new(config_string, system_config.dump_to_string().encode('utf-8')) + + if self.instance == None: + raise Exception('Failed to initialize cable with error: ' + self.module.bridge_get_error().decode('utf-8')) + + def get_instance(self): + return self.instance + + def write(self, addr, size, buffer): + data = (ctypes.c_char * size).from_buffer(bytearray(buffer)) + self.module.cable_write(self.instance, addr, size, data) + + def reg_write(self, addr, size, buffer, device=-1): + data = (ctypes.c_char * size).from_buffer(bytearray(buffer)) + self.module.cable_reg_write(self.instance, addr, data, device) + + def read(self, addr, size): + data = (ctypes.c_char * size)() + self.module.cable_read(self.instance, addr, size, data) + + result = [] + for elem in data: + result.append(elem) + + return result + + def reg_read(self, addr, size, device=-1): + data = (ctypes.c_char * size)() + self.module.cable_reg_read(self.instance, addr, data, device) + + result = [] + for elem in data: + result.append(elem) + + return result + + def chip_reset(self, value, duration=1000000): + self.module.chip_reset(self.instance, value, duration) + + def chip_config(self, value): + self.module.chip_config(self.instance, value) + + def jtag_reset(self, value): + self.module.jtag_reset(self.instance, value) + + def jtag_soft_reset(self): + self.module.jtag_soft_reset(self.instance) + + def jtag_set_reg(self, reg, width, value, ir_len=-1): + self.module.cable_jtag_set_reg(self.instance, reg, width, value, ir_len) + + def jtag_get_reg(self, reg, width, value, ir_len=-1): + out_value = ctypes.c_int() + self.module.cable_jtag_get_reg(self.instance, reg, width, ctypes.byref(out_value), value, ir_len) + return out_value.value + + def lock(self): + self.module.cable_lock(self.instance) + + def unlock(self): + self.module.cable_unlock(self.instance) + + + + +class debug_bridge(object): + + def __init__(self, config, binaries=[], verbose=False): + self.config = config + self.cable = None + self.cable_name = config.get_child_str('**/debug_bridge/cable/type') + if self.cable_name is None: + self.cable_name = 'ftdi' + self.binaries = binaries + self.reqloop_handle = None + self.verbose = verbose + self.gdb_handle = None + self.cable_config = config.get('**/debug_bridge/cable') + + + + # Load the library which provides generic services through + # python / C++ bindings + lib_path=os.path.join('libpulpdebugbridge.so') + self.module = ctypes.CDLL(lib_path) + + self.module.bridge_reqloop_open.argtypes = [ctypes.c_void_p, ctypes.c_uint] + self.module.bridge_reqloop_open.restype = ctypes.c_void_p + + self.module.bridge_reqloop_buffer_alloc.argtypes = [ctypes.c_void_p, ctypes.c_int] + self.module.bridge_reqloop_buffer_alloc.restype = ctypes.c_uint + + self.module.bridge_reqloop_buffer_free.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_int] + + self.module.bridge_reqloop_efuse_access.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_uint] + + self.module.bridge_reqloop_eeprom_access.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint] + self.module.bridge_reqloop_eeprom_access.restype = ctypes.c_int + + self.module.bridge_reqloop_flash_access.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint] + self.module.bridge_reqloop_flash_access.restype = ctypes.c_int + + self.module.bridge_reqloop_flash_erase.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_int] + self.module.bridge_reqloop_flash_erase.restype = ctypes.c_int + + self.module.bridge_reqloop_flash_erase_sector.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint] + self.module.bridge_reqloop_flash_erase_sector.restype = ctypes.c_int + + self.module.bridge_reqloop_flash_erase_chip.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_uint, ctypes.c_uint] + self.module.bridge_reqloop_flash_erase_chip.restype = ctypes.c_int + + self.module.bridge_reqloop_close.argtypes = [ctypes.c_void_p, ctypes.c_int] + + self.module.bridge_init(config.dump_to_string().encode('utf-8'), verbose) + + #self.module.jtag_shift.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_char_p)] + + def __mount_cable(self): + if self.cable_name is None: + raise Exception("Trying to mount cable while no cable was specified") + + if self.cable_name.split('@')[0] in ['ftdi', 'jtag-proxy']: + self.__mount_ctype_cable() + pass + else: + raise Exception('Unknown cable: ' + self.cable_name) + + def __mount_ctype_cable(self): + + self.cable = Ctype_cable( + module = self.module, + config = self.cable_config, + system_config = self.config + ) + + def get_cable(self): + if self.cable is None: + self.__mount_cable() + + return self.cable + + def load_jtag(self, binaries): + return self.load_default(binaries) + + def load_jtag_hyper(self, binaries): + raise Exception('JTAG boot is not supported on this target') + + def load_elf(self, binary): + if self.verbose: + print ('Loading %s' % binary) + + with open(binary, 'rb') as file: + elffile = ELFFile(file) + + for segment in elffile.iter_segments(): + + if segment['p_type'] == 'PT_LOAD': + + data = segment.data() + addr = segment['p_paddr'] + size = len(data) + + if self.verbose: + print ('Loading section (base: 0x%x, size: 0x%x)' % (addr, size)) + + self.write(addr, size, data) + + if segment['p_filesz'] < segment['p_memsz']: + addr = segment['p_paddr'] + segment['p_filesz'] + size = segment['p_memsz'] - segment['p_filesz'] + print ('Init section to 0 (base: 0x%x, size: 0x%x)' % (addr, size)) + self.write( + addr, + size, + [0] * size + ) + + + set_pc_addr_config = self.config.get('**/debug_bridge/set_pc_addr') + + if set_pc_addr_config is not None: + set_pc_offset_config = self.config.get('**/debug_bridge/set_pc_offset') + entry = elffile.header['e_entry'] + + if set_pc_offset_config is not None: + entry += set_pc_offset_config.get_int() + + if self.verbose: + print ('Setting PC (base: 0x%x, value: 0x%x)' % (set_pc_addr_config.get_int(), entry)) + + return self.write_32(set_pc_addr_config.get_int(), entry) + + return 0 + + def load(self, binaries=None): + if binaries is None: + binaries = self.binaries + + mode = self.config.get_child_str('**/debug_bridge/boot-mode') + if mode is None: + mode = 'jtag' + + if mode == 'jtag': + return self.load_jtag(binaries) + elif mode == 'jtag_hyper': + return self.load_jtag_hyper(binaries) + else: + return self.load_default(binaries) + + def load_default(self, binaries): + for binary in binaries: + if self.load_elf(binary=binary): + return 1 + + return 0 + + def start(self): + start_addr_config = self.config.get('**/debug_bridge/start_addr') + if start_addr_config is not None: + if self.verbose: + print ('Starting (base: 0x%x, value: 0x%x)' % (start_addr_config.get_int(), self.config.get('**/debug_bridge/start_value').get_int())) + + self.write_32(start_addr_config.get_int(), self.config.get('**/debug_bridge/start_value').get_int()) + return 0 + + def stop(self): + stop_addr_config = self.config.get('**/debug_bridge/stop_addr') + if stop_addr_config is not None: + self.write_32(stop_addr_config.get_int(), self.config.get('**/debug_bridge/stop_value').get_int()) != 0 + return 0 + + def read(self, addr, size): + return self.get_cable().read(addr, size) + + def write(self, addr, size, buffer): + return self.get_cable().write(addr, size, buffer) + + def write_int(self, addr, value, size): + return self.write(addr, size, value.to_bytes(size, byteorder='little')) + + def write_reg_int(self, addr, value, size, device=-1): + return self.get_cable().reg_write(addr, size, value.to_bytes(size, byteorder='little'), device) + + def write_32(self, addr, value): + return self.write_int(addr, value, 4) + + def write_16(self, addr, value): + return self.write_int(addr, value, 2) + + def write_8(self, addr, value): + return self.write_int(addr, value, 1) + + def read_int(self, addr, size): + byte_array = None + for byte in self.read(addr, size): + if byte_array == None: + byte_array = byte + else: + byte_array += byte + return int.from_bytes(byte_array, byteorder='little') + + def read_reg_int(self, addr, size, device=-1): + byte_array = None + for byte in self.get_cable().reg_read(addr, size, device): + if byte_array == None: + byte_array = byte + else: + byte_array += byte + return int.from_bytes(byte_array, byteorder='little') + + def read_32(self, addr): + return self.read_int(addr, 4) + + def read_16(self, addr): + return self.read_int(addr, 2) + + def read_8(self, addr): + return self.read_int(addr, 1) + + def _get_binary_symbol_addr(self, name, binaries=[]): + + binaries = binaries + self.binaries + + for binary in binaries: + with open(binary, 'rb') as file: + elf = ELFFile(file) + for section in elf.iter_sections(): + if section.header['sh_type'] == 'SHT_SYMTAB': + for symbol in section.iter_symbols(): + if symbol.name == name: + t_section=symbol.entry['st_shndx'] + t_vaddr=symbol.entry['st_value'] + return t_vaddr + return 0 + + def reset(self): + self.get_cable().jtag_reset(True) + self.get_cable().jtag_reset(False) + self.get_cable().chip_reset(True) + self.get_cable().chip_reset(False) + return 0 + + def ioloop(self): + + return 0 + + def reqloop(self, binaries=[]): + + # First get address of the structure used to communicate between + # the bridge and the runtime + addr = self._get_binary_symbol_addr('__rt_debug_struct_ptr', binaries) + if addr == 0: + addr = self._get_binary_symbol_addr('debugStruct_ptr', binaries) + + self.reqloop_handle = self.module.bridge_reqloop_open( + self.get_cable().get_instance(), addr) + + return 0 + + def reqloop_close(self, force=False): + if self.reqloop_handle is not None: + return self.module.bridge_reqloop_close(self.reqloop_handle, force) + return 0 + + + def __flasher_init(self, flasher_init): + chip = self.config.get('**/board/chip').get('name').get() + flasher_name = 'flasher-%s' % chip + flasher_path = os.path.join(os.environ.get('PULP_SDK_INSTALL'), 'bin', flasher_name) + + if flasher_init: + self.stop() + # TODO this breaks boot test using bridge, why this is needed ? + self.load([flasher_path]) + + self.reqloop([flasher_path]) + + if flasher_init: + self.start() + + + + def __flasher_deinit(self): + self.reqloop_close(force=True) + + + + def efuse_access(self, flasher_init, is_write, index, value, mask): + + self.__flasher_init(flasher_init) + + print ('efuse access') + self.module.bridge_reqloop_efuse_access(self.reqloop_handle, is_write, index, value, mask) + print ('efuse access done') + + self.__flasher_deinit() + + + + def __alloc_buffer(self, size): + + return self.module.bridge_reqloop_buffer_alloc(self.reqloop_handle, size) + + + + def eeprom_access(self, flasher_init, itf, cs, is_write,eeprom_addr, filepath): + self.__flasher_init(flasher_init) + + addr = self.__alloc_buffer(1024) + + with open(filepath, 'rb') as file: + while True: + buff = file.read(1024) + if buff: + self.write(addr, len(buff), buff) + if self.module.bridge_reqloop_eeprom_access(self.reqloop_handle, itf, cs, True, eeprom_addr, addr, len(buff)): + return -1 + eeprom_addr += 1024 + else: + break + + self.__flasher_deinit() + + return 0 + + + + def flash_access(self, flasher_init, type, itf, cs, is_write,flash_addr, size, filepath): + self.__flasher_init(flasher_init) + + addr = self.__alloc_buffer(1024) + + if is_write: + with open(filepath, 'rb') as file: + while True: + buff = file.read(1024) + if buff: + self.write(addr, len(buff), buff) + + buff_size = len(buff) + buff_size = (buff_size + 7) & ~0x7 + + if self.module.bridge_reqloop_flash_access(self.reqloop_handle, type, itf, cs, True, flash_addr, addr, buff_size): + return -1 + flash_addr += 1024 + else: + break + else: + with open(filepath, 'wb') as file: + while size > 0: + iter_size = 1024 + if iter_size > size: + iter_size = size + if self.module.bridge_reqloop_flash_access(self.reqloop_handle, type, itf, cs, False, flash_addr, addr, iter_size): + return -1 + buff = self.read(addr, iter_size) + for elem in buff: + file.write(elem) + size -= iter_size + flash_addr += iter_size + + self.__flasher_deinit() + + return 0 + + + + + def flash_erase_sector(self, flasher_init, type, itf, cs, flash_addr): + self.__flasher_init(flasher_init) + + if self.module.bridge_reqloop_flash_erase_sector(self.reqloop_handle, type, itf, cs, flash_addr): + return -1 + + self.__flasher_deinit() + + return 0 + + + + + def flash_erase_chip(self, flasher_init, type, itf, cs): + self.__flasher_init(flasher_init) + + if self.module.bridge_reqloop_flash_erase_chip(self.reqloop_handle, type, itf, cs): + return -1 + + self.__flasher_deinit() + + return 0 + + + def flash_erase(self, flasher_init, type, itf, cs, flash_addr, size): + self.__flasher_init(flasher_init) + + if self.module.bridge_reqloop_flash_erase(self.reqloop_handle, type, itf, cs, flash_addr, size): + return -1 + + self.__flasher_deinit() + + return 0 + + + def flash(self): + raise Exception('Flash is not supported on this target') + + def gdb(self, port): + self.gdb_handle = self.module.gdb_server_open(self.get_cable().get_instance(), port) + return 0 + + def wait(self): + if self.gdb_handle is not None: + self.module.gdb_server_close(self.gdb_handle, 0) + + # The wait function returns in case reqloop has been launched + # as it will check for end of application. + if self.reqloop_handle is not None: + return self.module.bridge_reqloop_close(self.reqloop_handle, 0) + + return 0 + + def lock(self): + self.get_cable().lock() + + def unlock(self): + self.get_cable().unlock() diff --git a/tools/pulp-debug-bridge/python/bridge/jtag.py b/tools/pulp-debug-bridge/python/bridge/jtag.py new file mode 100644 index 00000000..b8ea9511 --- /dev/null +++ b/tools/pulp-debug-bridge/python/bridge/jtag.py @@ -0,0 +1,283 @@ +# +# Copyright (C) 2018 ETH Zurich and University of Bologna +# +# 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. +# + +# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + +import ctypes +import os +import os.path +from elftools.elf.elffile import ELFFile +import time +import sys +import json_tools as js + +BOOT_MODE_DEFAULT = 0 +BOOT_MODE_JTAG = 1 +BOOT_MODE_ROM_HYPER = 2 + +JTAG_SOC_INSTR_WIDTH = 0x4 +JTAG_SOC_IDCODE = 0x2 +JTAG_SOC_AXIREG = 0x4 +JTAG_SOC_BBMUXREG = 0x5 +JTAG_SOC_CLKGATEREG = 0x6 +JTAG_SOC_CONFREG = 0x7 +JTAG_SOC_TESTMODEREG = 0x8 +JTAG_SOC_BISTREG = 0x9 +JTAG_SOC_BYPASS = 0xf +JTAG_SOC_IDCODE_WIDTH = 32 +JTAG_SOC_AXIREG_WIDTH = 96 +JTAG_SOC_BBMUXREG_WIDTH = 21 +JTAG_SOC_CLKGATEREG_WIDTH = 11 +JTAG_SOC_CONFREG_WIDTH = 4 +JTAG_SOC_TESTMODEREG_WIDTH = 4 +JTAG_SOC_BISTREG_WIDTH = 20 + +JTAG_CLUSTER_INSTR_WIDTH = 4 +JTAG_CLUSTER_IDCODE = 2 +JTAG_CLUSTER_SAMPLE_PRELOAD = 1 +JTAG_CLUSTER_EXTEST = 0 +JTAG_CLUSTER_DEBUG = 8 +JTAG_CLUSTER_MBIST = 9 +JTAG_CLUSTER_BYPASS = 0xf +JTAG_CLUSTER_IDCODE_WIDTH = 32 + +JTAG_IDCODE_WIDTH = JTAG_CLUSTER_IDCODE_WIDTH + JTAG_SOC_IDCODE_WIDTH +JTAG_INSTR_WIDTH = JTAG_CLUSTER_INSTR_WIDTH + JTAG_SOC_INSTR_WIDTH + + +ADV_DBG_AXI4_MODULE = 0x20 +ADV_DBG_CPU_MODULE = 0x21 + +ADV_DBG_AXI4_NOP = 0x0 +ADV_DBG_AXI4_WRITE8 = 0x1 +ADV_DBG_AXI4_WRITE16 = 0x2 +ADV_DBG_AXI4_WRITE32 = 0x3 +ADV_DBG_AXI4_WRITE64 = 0x4 +ADV_DBG_AXI4_READ8 = 0x5 +ADV_DBG_AXI4_READ16 = 0x6 +ADV_DBG_AXI4_READ32 = 0x7 +ADV_DBG_AXI4_READ64 = 0x8 +ADV_DBG_AXI4_WREG = 0x9 +ADV_DBG_AXI4_SELREG = 0xD + +ADV_DBG_CPU_NOP = 0x0 +ADV_DBG_CPU_WRITE = 0x3 +ADV_DBG_CPU_READ = 0x7 +ADV_DBG_CPU_WREG = 0x9 +ADV_DBG_CPU_SELREG = 0xD + +ADV_DBG_CPU_REG_STATUS = 0x0 + +class debug_bridge(object): + """ Debug bridge doc. """ + + def __init__(self, config_path, cable_option=None): + self.config = js.import_config_from_file(config_path) + path = os.path.join(os.environ.get('PULP_SDK_HOME'), 'install', 'ws', 'lib', 'libdebugbridge.so') + self.module = ctypes.CDLL(path) + self.module.bridge_new.argtypes = [ctypes.c_char_p, ctypes.c_char_p] + self.module.jtag_write.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_char_p] + #self.module.jtag_shift.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_char_p)] + with open(config_path, 'r') as config: + self.instance = self.module.bridge_new(config.read().encode('utf-8'), cable_option.encode('utf-8')) + + def reset(self, active): + """ Debug bridge reset. + :param active: Reset target + """ + self.module.reset(self.instance, active) + + def exit(self, status): + self.module.plt_exit(self.instance, status) + + def jtag_write(self, addr, size, buffer): + data = (ctypes.c_char * size).from_buffer(bytearray(buffer)) + self.module.jtag_write(self.instance, addr, size, data) + + def jtag_read(self, addr, size): + data = (ctypes.c_ubyte * size)() + self.module.jtag_read(self.instance, addr, size, data) + + result = [] + for elem in data: + result.append(elem) + + return result + + def jtag_read32(self, addr): + data = self.jtag_read(addr, 4) + return int.from_bytes(bytearray(data), 'little') + + def jtag_reset(self): + self.module.jtag_reset(self.instance) + + def jtag_idle(self): + self.module.jtag_idle(self.instance) + + def jtag_shift_ir(self): + self.module.jtag_shift_ir(self.instance) + + def jtag_shift_dr(self): + self.module.jtag_shift_dr(self.instance) + + def jtag_shift_common(self, width, bufferin, noex): + size = int((width + 7) / 8) + if bufferin is not None: + datain = (ctypes.c_char * size).from_buffer(bytearray(bufferin)) + else: + datain = (ctypes.c_char * size)() + dataout = (ctypes.c_ubyte * size)() + self.module.jtag_shift(self.instance, width, datain, dataout, noex) + + result = [] + for elem in dataout: + result.append(elem) + + return bytearray(result) + + def jtag_shift_noex(self, width, bufferin): + return self.jtag_shift_common(width, bufferin, 1) + + def jtag_shift(self, width, bufferin): + return self.jtag_shift_common(width, bufferin, 0) + + def jtag_set_reg(self, reg, value): + # Set TAP in confreg mode + self.jtag_shift_ir() + self.jtag_shift(JTAG_SOC_INSTR_WIDTH, [reg]) + self.jtag_idle() + + # And push the new value + self.jtag_shift_dr() + self.jtag_shift(9, [value, 0]) + self.jtag_idle() + + def jtag_get_reg(self, reg, value): + # Set TAP in confreg mode + #self.jtag_shift_ir() + #self.jtag_shift(JTAG_SOC_INSTR_WIDTH, [reg]) + #self.jtag_idle() + #print (binascii.hexlify(bytearray(data))) + + # And push the new value + self.jtag_shift_dr() + result = self.jtag_shift(9, [value, 0]) + self.jtag_idle() + + return result + + def jtag_set_dbg_mode_soc(self): + + self.jtag_shift_ir() + self.jtag_shift_noex(JTAG_CLUSTER_INSTR_WIDTH, [JTAG_SOC_BYPASS]) + self.jtag_shift(JTAG_SOC_INSTR_WIDTH, [JTAG_SOC_AXIREG]) + self.jtag_idle() + + def wait_exit(self): + self.jtag_set_dbg_mode_soc() + + while True: + value = self.jtag_read32(0x1a1040a0) + + if (value >> 31) & 1: + return value & 0x7fffffff + + time.sleep(0.5) + + def wait_exit_and_stop(self): + status = self.wait_exit() + self.exit(status) + + def load(self, binary): + + print ('Loading ELF file (path: %s)' % binary) + + self.jtag_set_dbg_mode_soc() + + with open(binary, 'rb') as file: + elffile = ELFFile(file) + + for segment in elffile.iter_segments(): + if segment['p_type'] == 'PT_LOAD': + + data = segment.data() + addr = segment['p_paddr'] + size = len(data) + + print ('Loading section (base: 0x%x, size: 0x%x)' % (addr, size)) + + self.jtag_write(addr, size, data) + + def wait_and_exit(self): + + status = self.wait_exit_and_stop() + sys.exit(status) + + + def rom_boot(self): + + # This is the default mode so just let the core boot and behave in the + # default mode + self.jtag_reset() + self.reset(False) + + + def rom_hyper_boot(self, binary): + + self.jtag_reset() + + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + self.reset(True) + self.jtag_set_reg(JTAG_SOC_CONFREG, BOOT_MODE_ROM_HYPER << 1) + self.reset(False) + + # We don't have anything else to do as it reads the boot mode + # and does everything with the flash + + + def jtag_boot(self, binary): + + self.jtag_reset() + + # Reset the chip and tell him we want to load via jtag + # We keep the reset active until the end so that it sees + # the boot mode as soon as it boots from rom + self.reset(True) + self.jtag_set_reg(JTAG_SOC_CONFREG, BOOT_MODE_JTAG << 1) + self.reset(False) + + # Load the binary through jtag + self.load(binary) + + # And notify the boot code that the binary is ready + self.jtag_set_reg(JTAG_SOC_CONFREG, (BOOT_MODE_JTAG << 1) | 1) + + def exec_config(self): + + mode = self.config.get('runner/boot-mode').get() + + if mode == 'rom': + self.rom_boot() + elif mode == 'rom_hyper': + self.rom_hyper_boot() + elif mode == 'jtag': + self.jtag_boot(self.config.get('loader/binaries').get_elem(0).get()) + else: + raise Exception("Unsupported mode: " + mode) + + self.wait_and_exit() diff --git a/tools/pulp-debug-bridge/src/cable.hpp b/tools/pulp-debug-bridge/src/cable.hpp new file mode 100644 index 00000000..1302ba54 --- /dev/null +++ b/tools/pulp-debug-bridge/src/cable.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ + +#ifndef __CABLES_CABLE_HPP__ +#define __CABLES_CABLE_HPP__ + +#include "json.hpp" +#include "cables/log.h" + + +class Cable_jtag_itf +{ +public: + + virtual bool bit_inout(char* inbit, char outbit, bool last) { printf ("i am bit_inout virtual fct in cable class\n"); return false; } + + virtual bool stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last) { printf ("i am stream_inout virtual fct in cable class\n"); return false; } + + virtual int flush() { return -1; } + virtual bool jtag_reset(bool active) { printf("JTAG\n"); return false; } + + virtual void device_select(unsigned int i) {} + + bool jtag_soft_reset(); + bool jtag_write_tms(int val); + bool jtag_shift_ir(); + bool jtag_shift_dr(); + bool jtag_idle(); + bool jtag_shift(int width, char *bits); + bool jtag_shift_ir(unsigned int ir, int ir_len=-1); + bool jtag_set_reg(unsigned int reg, int width, unsigned int value, int ir_len=-1); + bool jtag_get_reg(unsigned int reg, int width, unsigned int *out_value, unsigned int value, int ir_len=-1); +}; + + + +class Cable_io_itf +{ +public: + virtual bool access(bool write, unsigned int addr, int size, char* buffer, int device=-1) { return false; } + virtual bool reg_access(bool write, unsigned int addr, char* buffer, int device=-1) { return false; } +}; + + + +class Cable_ctrl_itf +{ +public: + virtual bool chip_reset(bool active, int duration) { return false; } + virtual bool chip_config(uint32_t config) { return false; } +}; + + + +class Cable : public Cable_io_itf, public Cable_jtag_itf, public Cable_ctrl_itf +{ +public: + Cable(js::config *config) : config(config) {} + + virtual bool connect(js::config *config) { return true; } + + virtual void lock() { } + + virtual void unlock() { } + + js::config *get_config() { return this->config; } + +protected: + js::config *config; + +}; + +#endif diff --git a/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.cpp b/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.cpp new file mode 100644 index 00000000..9bab7129 --- /dev/null +++ b/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.cpp @@ -0,0 +1,1423 @@ +/* + * 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. + */ + +/* + * Authors: Andreas Traber + */ + +#include +#include +#include +#include +#include +#include + +#include "adv_dbg_itf.hpp" +#ifdef __USE_FTDI__ +#include "cables/ftdi/ftdi.hpp" +#endif + +#define JTAG_SOC_AXIREG 4 + + +Adv_dbg_itf::Adv_dbg_itf(js::config *system_config, js::config *config, Log* log, Cable *m_dev) : Cable(system_config), log(log), m_dev(m_dev), bridge_config(config) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex, &attr); + + js::config *conf = system_config->get("**/adv_dbg_unit/debug_ir"); + + this->debug_ir = conf != NULL ? conf->get_int() : 0x4; + log->debug("Using debug IR: 0x%x\n", this->debug_ir); + + + conf = system_config->get("**/adv_dbg_unit/retry_count"); + + this->retry_count = conf != NULL ? conf->get_int() : 0; + log->debug("Using retry count: %d\n", this->retry_count); + + + conf = NULL; //system_config->get("**/adv_dbg_unit/check_errors"); + + this->check_errors = conf != NULL ? conf->get_bool() : false; + log->debug("Checking errors: %d\n", this->check_errors); + +} + + + +Adv_dbg_itf::~Adv_dbg_itf() +{ + delete m_dev; +} + + + +bool Adv_dbg_itf::connect() +{ + js::config *jtag_cable_config = NULL; + + access_timeout = bridge_config->get_int("**/access_timeout_us"); + if (access_timeout == 0) + access_timeout = 1000000; + + log->debug ("Using access timeout: %d us\n", access_timeout); + + this->check_cable(); + + m_dev->jtag_reset(true); + m_dev->jtag_reset(false); + + this->jtag_soft_reset(); + + // TODO once the bridge is working on Vega, changed that into a generic + // feature which reads the devices from json file + if (this->config->get("**/chip/name")->get_str() == "vega") + { + this->add_device(5, DEV_PROTOCOL_RISCV); + this->add_device(4, DEV_PROTOCOL_PULP); + } + else if (this->config->get("**/chip/name")->get_str() == "pulpissimo") + { + this->add_device(5, DEV_PROTOCOL_PULP); + this->add_device(5, DEV_PROTOCOL_RISCV); + } + + // now we can work with the chain + if (!jtag_auto_discovery()) { + log->error("Did not find an adv debug unit in the chain, exiting\n"); + return false; + } + + int tap = 0; + if (bridge_config->get("tap")) tap = bridge_config->get("tap")->get_int(); + this->m_jtag_device_default = tap; + + this->device_select(tap); + + return true; +} + + + +bool Adv_dbg_itf::jtag_reset(bool active) +{ + this->check_connection(); + + pthread_mutex_lock(&mutex); + + for (int i=0; i < m_jtag_devices.size(); i++) + { + m_jtag_devices[i].is_in_debug = false; + } + bool result = m_dev->jtag_reset(active); + + pthread_mutex_unlock(&mutex); + + return result; +} + +bool Adv_dbg_itf::check_connection() +{ + if (!this->connected) + { + this->connected = true; + + if (!this->connect()) + return false; + } + + return true; +} + +bool Adv_dbg_itf::check_cable() +{ + if (!this->cable_connected) + { + if (!this->m_dev->connect(this->bridge_config)) + return false; + + this->cable_connected = true; + } + + return true; +} + +bool Adv_dbg_itf::chip_reset(bool active, int duration) +{ + bool result = true; + + if (!this->check_cable()) + return false; + + pthread_mutex_lock(&mutex); + + if (!m_dev->chip_reset(active, duration)) { result = false; goto end; }; + // Wait some time so that we don't do any IO access after that while the chip + // has not finished booting + if (!active) usleep(10000); + +end: + pthread_mutex_unlock(&mutex); + + return result; +} + + + +bool Adv_dbg_itf::chip_config(uint32_t value) +{ + bool result = true; + + if (!this->check_cable()) + return false; + + pthread_mutex_lock(&mutex); + + if (!m_dev->chip_config(value)) { result = false; goto end; }; + +end: + pthread_mutex_unlock(&mutex); + + return result; +} + + + +void Adv_dbg_itf::device_select(unsigned int i) +{ + pthread_mutex_lock(&mutex); + + m_jtag_device_sel = i; + + if (i == m_jtag_devices.size() - 1) + m_tms_on_last = 1; + else + m_tms_on_last = 0; + + pthread_mutex_unlock(&mutex); +} + + +bool Adv_dbg_itf::reg_access(bool write, unsigned int addr, char* buffer, int device) +{ + bool result; + + this->check_connection(); + + pthread_mutex_lock(&mutex); + + if (device != -1) + this->device_select(device); + else if (m_jtag_device_default != m_jtag_device_sel) + this->device_select(m_jtag_device_default); + + jtag_device &dev = m_jtag_devices[m_jtag_device_sel]; + + if (dev.protocol == DEV_PROTOCOL_RISCV) + result = this->reg_access_riscv(write, addr, buffer); + else + result = this->reg_access_pulp(write, addr, buffer); + + pthread_mutex_unlock(&mutex); + + return result; +} + + +bool Adv_dbg_itf::reg_access_riscv(bool write, unsigned int addr, char* buffer) +{ + if (write) + return this->reg_access_write_riscv(write, addr, buffer); + else + return this->reg_access_read_riscv(write, addr, buffer); +} + +bool Adv_dbg_itf::reg_access_read_riscv(bool write, unsigned int addr, char* buffer) +{ + unsigned char buf[8]; + unsigned char recv[8]; + + uint32_t data = *(uint32_t *)buffer; + + jtag_soft_reset(); + jtag_set_selected_ir(0x11); + + buf[5] = (addr >> 6) & 0x1; + buf[4] = ((addr & 0x3f) << 2) | ((data >> 30) & 0x7); + buf[3] = (data >> 22) & 0xff; + buf[2] = (data >> 14) & 0xff; + buf[1] = (data >> 6) & 0xff; + buf[0] = ((data & 0x1f) << 2) | (0x1 << 0); + + m_dev->jtag_write_tms(1); + m_dev->jtag_write_tms(0); + m_dev->jtag_write_tms(0); + + jtag_pad_before(); + + if (!m_dev->stream_inout((char *)recv, (char *)buf, 41, m_tms_on_last)) { + log->warning("ft2232: failed to write opcode stream to device\n"); + return false; + } + + jtag_pad_after(!m_tms_on_last); + + // Go to UPDATE DR + m_dev->jtag_write_tms(1); + for (int i=0; i<50; i++) + m_dev->jtag_write_tms(0); + + // Go to CAPTURE DR + m_dev->jtag_write_tms(1); + m_dev->jtag_write_tms(0); + + // Go to IDLE + m_dev->jtag_write_tms(1); + m_dev->jtag_write_tms(1); + m_dev->jtag_write_tms(0); + + // SHIFT DR + m_dev->jtag_write_tms(1); + m_dev->jtag_write_tms(0); + m_dev->jtag_write_tms(0); + + jtag_pad_before(); + + buf[0] = 0; + + if (!m_dev->stream_inout((char *)recv, (char *)buf, 41, m_tms_on_last)) { + log->warning("ft2232: failed to write opcode stream to device\n"); + return false; + } + + jtag_pad_after(!m_tms_on_last); + + + // IDLE + m_dev->jtag_write_tms(1); // exit 1 DR + m_dev->jtag_write_tms(0); // run test idle + m_dev->jtag_write_tms(0); // run test idle + + *(uint32_t *)buffer = (recv[0] >> 2) | (recv[1] << 6) | (recv[2] << 14) | (recv[3] << 22) | ((recv[4] & 0x3) << 30); + + return false; +} + +bool Adv_dbg_itf::reg_access_write_riscv(bool write, unsigned int addr, char* buffer) +{ + char buf[8]; + char recv[8]; + + uint32_t data = *(uint32_t *)buffer; + + jtag_soft_reset(); + jtag_set_selected_ir(0x11); + + buf[5] = (addr >> 5) & 0x3; + buf[4] = ((addr & 0x1f) << 3) | ((data >> 29) & 0x7); + buf[3] = (data >> 21) & 0xff; + buf[2] = (data >> 13) & 0xff; + buf[1] = (data >> 5) & 0xff; + buf[0] = ((data & 0x1f) << 3) | (0x2 << 1); + + m_dev->jtag_write_tms(1); + m_dev->jtag_write_tms(0); + m_dev->jtag_write_tms(0); + + jtag_pad_before(); + + if (!m_dev->stream_inout(recv, buf, 42, m_tms_on_last)) { + log->warning("ft2232: failed to write opcode stream to device\n"); + return false; + } + + jtag_pad_after(!m_tms_on_last); + + m_dev->jtag_write_tms(1); // exit 1 DR + m_dev->jtag_write_tms(0); // run test idle + m_dev->jtag_write_tms(0); // run test idle + + return false; +} + + +bool Adv_dbg_itf::reg_access_pulp(bool write, unsigned int addr, char* buffer) +{ + return false; +} + + + +bool Adv_dbg_itf::access(bool wr, unsigned int addr, int size, char* buffer, int device) +{ + bool result; + + this->check_connection(); + + pthread_mutex_lock(&mutex); + + if (device != -1) + this->device_select(device); + else if (m_jtag_device_default != m_jtag_device_sel) + this->device_select(m_jtag_device_default); + + jtag_debug(); + + if (wr) + result = write(addr, size, buffer); + else + result = read(addr, size, buffer); + + pthread_mutex_unlock(&mutex); + + return result; +} + + + +bool Adv_dbg_itf::write(unsigned int _addr, int _size, char* _buffer) +{ + int count = 0; + do + { + unsigned int addr = _addr; + int size = _size; + char *buffer = _buffer; + + bool retval = true; + + if (addr & 0x1 && size >= 1) { + retval = retval && write_internal(8, addr, 1, buffer); + size -= 1; + buffer += 1; + addr += 1; + } + + if (addr & 0x2 && size >= 2) { + retval = retval && write_internal(16, addr, 2, buffer); + size -= 2; + buffer += 2; + addr += 2; + } + + #if 0 + + // TODO add 64 bits support and make sure it is not used on pulp targets + + if (addr & 0x4 && size >= 4) { + retval = retval && write_internal(AXI_WRITE32, addr, 4, buffer); + size -= 4; + buffer += 4; + addr += 4; + } + + if (size >= 8) { + int local_size = size & (~0x7); + retval = retval && write_internal(AXI_WRITE64, addr, local_size, buffer); + size -= local_size; + buffer += local_size; + addr += local_size; + } + + #else + + if (size >= 4) { + int local_size = size & (~0x3); + + while (local_size) + { + int iter_size = local_size; + if (iter_size > 1024) iter_size = 1024; + + retval = retval && write_internal(32, addr, iter_size, buffer); + local_size -= iter_size; + size -= iter_size; + buffer += iter_size; + addr += iter_size; + } + } + #endif + + if (size >= 2) { + retval = retval && write_internal(16, addr, 2, buffer); + size -= 2; + buffer += 2; + addr += 2; + } + + if (size >= 1) { + retval = retval && write_internal(8, addr, 1, buffer); + size -= 1; + buffer += 1; + addr += 1; + } + + if (this->check_errors) + { + uint32_t error_addr; + bool error = false; + retval = retval && read_error_reg(&error_addr, &error); + + if (error) { + log->debug("advdbg reports: Failed to write to addr %X\n", error_addr); + count++; + continue; + } + } + + return retval; + } while (count < this->retry_count); + + return false; +} + + + +bool Adv_dbg_itf::read(unsigned int _addr, int _size, char* _buffer) +{ + int count = 0; + do + { + unsigned int addr = _addr; + int size = _size; + char *buffer = _buffer; + + bool retval = true; + + if (addr & 0x1 && size >= 1) { + retval = retval && read_internal(8, addr, 1, buffer); + size -= 1; + buffer += 1; + addr += 1; + } + + if (addr & 0x2 && size >= 2) { + retval = retval && read_internal(16, addr, 2, buffer); + size -= 2; + buffer += 2; + addr += 2; + } + + #if 0 + + // TODO add 64 bits support and make sure it is not used on pulp targets + + if (addr & 0x4 && size >= 4) { + retval = retval && read_internal(AXI_READ32, addr, 4, buffer); + size -= 4; + buffer += 4; + addr += 4; + } + + if (size >= 8) { + int local_size = size & (~0x7); + retval = retval && read_internal(AXI_READ64, addr, local_size, buffer); + size -= local_size; + buffer += local_size; + addr += local_size; + } + + #else + + if (size >= 4) { + int local_size = size & (~0x3); + + while (local_size) + { + int iter_size = local_size; + if (iter_size > 2048) iter_size = 2048 ; + + retval = retval && read_internal(32, addr, iter_size, buffer); + local_size -= iter_size; + size -= iter_size; + buffer += iter_size; + addr += iter_size; + } + } + + #endif + + if (size >= 2) { + retval = retval && read_internal(16, addr, 2, buffer); + size -= 2; + buffer += 2; + addr += 2; + } + + if (size >= 1) { + retval = retval && read_internal(8, addr, 1, buffer); + size -= 1; + buffer += 1; + addr += 1; + } + + if (this->check_errors) + { + uint32_t error_addr; + bool error = false; + retval = retval && read_error_reg(&error_addr, &error); + + if (error) { + log->debug("advdbg reports: Failed to read from addr %X\n", error_addr); + count++; + continue; + } + } + + return retval; + } while (count < retry_count); + + return false; +} + + +bool Adv_dbg_itf::write_internal(int bitwidth, unsigned int addr, int size, char* buffer) +{ + if (m_jtag_device_sel >= m_jtag_devices.size()) + return false; + + jtag_device &dev = m_jtag_devices[m_jtag_device_sel]; + + if (dev.protocol == DEV_PROTOCOL_RISCV) + return this->write_internal_riscv(bitwidth, addr, size, buffer); + else + return this->write_internal_pulp(bitwidth, addr, size, buffer); +} + +bool Adv_dbg_itf::write_internal_riscv(int bitwidth, unsigned int addr, int size, char* buffer) +{ + return false; +} + +bool Adv_dbg_itf::write_internal_pulp(int bitwidth, unsigned int addr, int size, char* buffer) +{ + char buf[8]; + char recv[1]; + uint32_t crc; + ADBG_OPCODES opcode; + + switch(bitwidth) { + case 8: + opcode = AXI_WRITE8; + break; + + case 16: + opcode = AXI_WRITE16; + break; + + case 32: + opcode = AXI_WRITE32; + break; + + case 64: + opcode = AXI_WRITE64; + break; + + default: + log->warning("Invalid bitwidth: %d\n", bitwidth); + return false; + } + + if (size % (bitwidth/8) != 0) { + log->warning("Size is not aligned to selected bitwidth\n"); + return false; + } + + jtag_axi_select(); + + jtag_pad_before(); + + // build burst setup command + // bit 52: 0 + // bit 51-48: opcode + // bit 47:16: address + // bit 15:0: count + buf[6] = opcode; + buf[5] = addr >> 24; + buf[4] = addr >> 16; + buf[3] = addr >> 8; + buf[2] = addr >> 0; + buf[1] = ( size / (bitwidth / 8) ) >> 8; + buf[0] = ( size / (bitwidth / 8) ) >> 0; + + if (!m_dev->stream_inout(NULL, buf, 53, m_tms_on_last)) { + log->warning("ft2232: failed to write opcode stream to device\n"); + return false; + } + + jtag_pad_after(!m_tms_on_last); + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(0); // capture DR + m_dev->jtag_write_tms(0); // shift DR + + jtag_pad_before(); + + // send start bit + if (!m_dev->bit_inout(NULL, 0x1, false)) { + log->warning("ft2232: failed to write start bit to device\n"); + return false; + } + + // send data + if (!m_dev->stream_inout(NULL, buffer, size * 8, false)) { + log->warning("ft2232: failed to write data to device\n"); + return false; + } + + // send crc + crc = crc_compute(0xFFFFFFFF, buffer, size * 8); + buf[3] = crc >> 24; + buf[2] = crc >> 16; + buf[1] = crc >> 8; + buf[0] = crc >> 0; + if (!m_dev->stream_inout(NULL, buf, 32, false)) { + log->warning("ft2232: failed to write crc to device\n"); + return false; + } + + // push crc all the way in before we can expect to receive the match bit + jtag_pad_after(false); + + // receive match bit + recv[0] = 0; + + if (!m_dev->stream_inout(recv, buf, 2, false)) { + log->warning("ft2232: failed to read match bit from device\n"); + return false; + } + + + m_dev->jtag_write_tms(1); // exit 1 DR + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(0); // run test idle + + if (((recv[0] >> m_jtag_device_sel) & 0x1) != 0x1) { + // TODO some pulp targets like fulmine does not support CRC. + log->warning("ft2232: Match bit was not set. Transfer has probably failed; addr %08X, size %d\n", addr, size); + return false; + } + + return true; +} + + + +bool Adv_dbg_itf::read_internal(int bitwidth, unsigned int addr, int size, char* buffer) +{ + if (m_jtag_device_sel >= m_jtag_devices.size()) + return false; + + jtag_device &dev = m_jtag_devices[m_jtag_device_sel]; + + if (dev.protocol == DEV_PROTOCOL_RISCV) + return this->read_internal_riscv(bitwidth, addr, size, buffer); + else + return this->read_internal_pulp(bitwidth, addr, size, buffer); +} + +bool Adv_dbg_itf::read_internal_riscv(int bitwidth, unsigned int addr, int size, char* buffer) +{ + return false; +} + +bool Adv_dbg_itf::read_internal_pulp(int bitwidth, unsigned int addr, int size, char* buffer) +{ + char recv[size]; + char buf[size < 8 ? 8 : size]; + int nwords; + uint32_t crc = 0xFFFFFFFF; + ADBG_OPCODES opcode; + int bytewidth = bitwidth / 8; + + switch(bitwidth) { + case 8: + opcode = AXI_READ8; + break; + + case 16: + opcode = AXI_READ16; + break; + + case 32: + opcode = AXI_READ32; + break; + + case 64: + opcode = AXI_READ64; + break; + + default: + log->warning("Invalid bitwidth: %d\n", bitwidth); + return false; + } + + int factor = 1; + + // Increase the word size in case we have big burst with good multiple + if (size >= 256 && size % 256 == 0) + { + bytewidth = 256; + factor = bytewidth / 4; + } + + if (size % bytewidth != 0) { + log->warning("Size is not aligned to selected bitwidth\n"); + return false; + } + + nwords = size / bytewidth; + + jtag_axi_select(); + + jtag_pad_before(); + + // build burst setup command + // bit 52: 0 + // bit 51-48: opcode + // bit 47:16: address + // bit 15:0: count + buf[6] = opcode; + buf[5] = addr >> 24; + buf[4] = addr >> 16; + buf[3] = addr >> 8; + buf[2] = addr >> 0; + buf[1] = (nwords * factor) >> 8; + buf[0] = (nwords * factor) >> 0; + + if (!m_dev->stream_inout(NULL, buf, 53, m_tms_on_last)) { + log->warning("ft2232: failed to write opcode stream to device\n"); + return false; + } + + jtag_pad_after(!m_tms_on_last); + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(0); // capture DR + m_dev->jtag_write_tms(0); // shift DR + + // no need to do padding here, we just wait for a 1 + + // wait for a '1' from the AXI module + struct timeval start, now; + int retval = gettimeofday(&start, NULL); + assert(retval == 0); + + while (true) { + buf[0] = 0x0; + if (!m_dev->bit_inout(buf, 0x0, false)) { + log->warning("ft2232: failed to read start bit from device\n"); + return false; + } + + if (buf[0] & 0x1) + break; + + retval = gettimeofday(&now, NULL); + assert(retval == 0); + unsigned long usec_elapsed = (now.tv_sec - start.tv_sec) * 1000000 + (now.tv_usec - start.tv_usec); + + if (usec_elapsed > access_timeout) { + log->warning("ft2232: did not get a start bit from the AXI module in 1s\n"); + return false; + } + } + + // make sure we only send 0's to the device + memset(buf, 0, size); + + // receive data + crc = 0xFFFFFFFF; + for (int i = 0; i < nwords; i++) { + if (!m_dev->stream_inout(recv, buf, bytewidth*8, false)) { + log->warning("ft2232: failed to receive data from device\n"); + return false; + } + + memcpy(buffer, recv, bytewidth); + crc = crc_compute(crc, recv, bytewidth*8); + + buffer = buffer + bytewidth; + } + + // receive crc + if (!m_dev->stream_inout(recv, buf, 33, m_tms_on_last)) { + log->warning("ft2232: failed to read crc from device\n"); + return false; + } + + jtag_pad_after(!m_tms_on_last); + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(0); // run test idle + + uint32_t recv_crc; + memcpy(&recv_crc, recv, 4); + if (crc != recv_crc) { + log->warning ("ft2232: crc from adv dbg unit did not match for request to addr %08X\n", addr); + log->debug ("ft2232: Got %08X, expected %08X\n", recv_crc, crc); + return false; + } + + return true; +} + + + +bool Adv_dbg_itf::read_error_reg(uint32_t *addr, bool *error) +{ + char buf[5]; + assert (addr != NULL); + assert (error != NULL); + + jtag_axi_select(); + + jtag_pad_before(); + + // build internal register select + // 63 = 0 (module_cmd) + // 62:59 = 1101 (operation_in) + // 58 = 0 (does not matter) + // => 6 bits + buf[0] = 0x1A; + + if (!m_dev->stream_inout(NULL, buf, 6, m_tms_on_last)) { + log->warning("ft2232: failed to write internal register select to device\n"); + return false; + } + + jtag_pad_after(!m_tms_on_last); + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(0); // capture DR + m_dev->jtag_write_tms(0); // shift DR + + jtag_pad_before(); + + memset(buf, 0, 5); + + if (!m_dev->stream_inout(buf, buf, 33, m_tms_on_last)) { + log->warning("ft2232: failed to read AXI error register\n"); + return false; + } + + jtag_pad_after(!m_tms_on_last); + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(0); // run test idle + + *error = buf[0] & 0x1; + + // shift buffer by 1 bit to the right to get the address + for (int i = 0; i < 4; i++) + buf[i] = buf[i] >> 1 | ((buf[i+1] & 0x1) << 31); + memcpy(addr, buf, 4); + + if (*error) { + // there was an error, so we have to clear the internal error register on + // the adv dbg unit + return clear_error_reg(); + } + + return true; +} + + + +bool Adv_dbg_itf::clear_error_reg() +{ + char buf[5]; + + // IMPORTANT: since there is a bug in hardware, if the next access is a + // write, it will fail (or at least look like it failed). The reason for this + // is that the error register kind of keeps hanging in an error state. To + // mitigate this problem, we perform one valid dummy access + //read_internal(AXI_READ8, 0x10000000, 1, buf); + + jtag_axi_select(); + + jtag_pad_before(); + +#if 0 + // build internal register select + // 63 = 0 (module_cmd) + // 62:59 = 1001 (operation_in) + // 58 = 0 (does not matter) + // => 6 bits + // 55:46 = 1 (all 1) + // => +16 bits + buf[2] = 0x09; + buf[1] = 0x00; + buf[0] = 0x08; + + if (!m_dev->stream_inout(NULL, buf, 5+1+15, m_tms_on_last)) { + log->warning("ft2232: failed to write internal register write to device\n"); + return false; + } +#else + buf[0] = (0x9 << 1) | 1; + + if (!m_dev->stream_inout(NULL, buf, 5+1, m_tms_on_last)) { + log->warning("ft2232: failed to write internal register write to device\n"); + return false; + } +#endif + + jtag_pad_after(!m_tms_on_last); + + m_dev->jtag_write_tms(1); // update DR + + return true; +} + + + +bool Adv_dbg_itf::jtag_set_selected_ir(char ir) +{ + char buf[1]; + bool is_last; + unsigned int i; + + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(1); // select IR scan + m_dev->jtag_write_tms(0); // capture IR + m_dev->jtag_write_tms(0); // shift IR + + // put devices into bypass mode if they are before our device + for (i = 0; i < m_jtag_devices.size(); i++) { + + // set our device into debug, the rest into bypass + if(i == m_jtag_device_sel) + buf[0] = ir; + else + buf[0] = 0xFF; + + is_last = (m_jtag_devices.size() - 1 == i); + + if (!m_dev->stream_inout(NULL, buf, m_jtag_devices[i].ir_len, is_last)) { + log->warning ("ft2232: failed to set IR to bypass\n"); + return false; + } + } + + m_dev->jtag_write_tms(1); // update IR + m_dev->jtag_write_tms(0); // run test idle + + return true; +} + + + +bool Adv_dbg_itf::jtag_debug() +{ + if (m_jtag_device_sel >= m_jtag_devices.size()) + return false; + + jtag_device &dev = m_jtag_devices[m_jtag_device_sel]; + if (!dev.is_in_debug) + { + jtag_soft_reset(); + dev.is_in_debug = jtag_set_selected_ir(this->debug_ir); + return dev.is_in_debug; + } +} + + + +bool Adv_dbg_itf::jtag_dmi_select() +{ + char buf[1]; + buf[0] = 0x11; + + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(0); // capture DR scan + m_dev->jtag_write_tms(0); // shift DR + + jtag_pad_before(); + + if (!m_dev->stream_inout(NULL, buf, 6, m_tms_on_last)) { + log->warning("ft2232: failed to write AXI select to device\n"); + return false; + } + + jtag_pad_after(!m_tms_on_last); + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(0); // capture DR scan + m_dev->jtag_write_tms(0); // shift DR + + m_dev->flush(); + + return true; +} + + + +bool Adv_dbg_itf::jtag_axi_select() +{ + char buf[1]; + buf[0] = 0x20; + + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(0); // capture DR scan + m_dev->jtag_write_tms(0); // shift DR + + jtag_pad_before(); + + if (!m_dev->stream_inout(NULL, buf, 6, m_tms_on_last)) { + log->warning("ft2232: failed to write AXI select to device\n"); + return false; + } + + jtag_pad_after(!m_tms_on_last); + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(0); // capture DR scan + m_dev->jtag_write_tms(0); // shift DR + + m_dev->flush(); + + return true; +} + +#define MAX_CHAIN_LEN 128 + +void Adv_dbg_itf::add_device(int ir_len, int protocol) +{ + jtag_device device; + device.index = m_jtag_devices.size(); + device.is_in_debug = false; + device.ir_len = ir_len; + device.protocol = protocol; + m_jtag_devices.push_back(device); +} + +bool Adv_dbg_itf::jtag_auto_discovery() +{ + char recv_buf[MAX_CHAIN_LEN/8]; + char send_buf[MAX_CHAIN_LEN/8]; + int ir_len; + int dr_len; + + if (m_jtag_devices.size() == 0) + { + ir_len = ir_len_detect(); + + jtag_soft_reset(); + dr_len = dr_len_detect(); + + log->debug("JTAG IR len is %d, DR len is %d\n", ir_len, dr_len); + + std::string chip = this->config->get("**/chip/name")->get_str(); + + if (chip != "wolfe" && chip != "usoc_v1") + { + if (dr_len <= 0 || ir_len <= 0) { + log->error("JTAG sanity check failed\n"); + return false; + } + } + else + { + // On wolfe, due to a HW bug, it is not possible to guess the dr len + dr_len = 32; + } + + // since we now know how long the chain is, we can shift out the IDs + jtag_soft_reset(); + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(0); // capture DR scan + m_dev->jtag_write_tms(0); // shift DR scan + + m_dev->stream_inout(recv_buf, send_buf, dr_len, true); + + m_jtag_devices.clear(); + + // build 32-bit chunks for the IDs + for(int i = 0; i < dr_len/32; i++) { + jtag_device device; + device.id = (recv_buf[i*4 + 3] & 0xFF) << 24; + device.id |= (recv_buf[i*4 + 2] & 0xFF) << 16; + device.id |= (recv_buf[i*4 + 1] & 0xFF) << 8; + device.id |= (recv_buf[i*4 + 0] & 0xFF) << 0; + device.index = i; + device.is_in_debug = false; + device.protocol = DEV_PROTOCOL_PULP; + // TODO the detacted IR length is wrong when there are several taps + device.ir_len = 4; + + log->debug("Device %d ID: %08X\n", i, device.id); + + m_jtag_devices.push_back(device); + } + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(0); // run test idle + } + else + { + for(int i = 0; i < m_jtag_devices.size(); i++) + { + jtag_device &device = m_jtag_devices[i]; + + this->device_select(i); + + dr_len = dr_len_detect(); + + jtag_soft_reset(); + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(0); // capture DR scan + m_dev->jtag_write_tms(0); // shift DR scan + + m_dev->stream_inout(recv_buf, send_buf, dr_len, true); + + device.id = (recv_buf[i*4 + 3] & 0xFF) << 24; + device.id |= (recv_buf[i*4 + 2] & 0xFF) << 16; + device.id |= (recv_buf[i*4 + 1] & 0xFF) << 8; + device.id |= (recv_buf[i*4 + 0] & 0xFF) << 0; + + log->debug("Device %d ID: %08X\n", i, device.id); + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(0); // run test idle + + } + } + + return true; +} + + + +int Adv_dbg_itf::ir_len_detect() +{ + char recv_buf[MAX_CHAIN_LEN/8]; + char send_buf[MAX_CHAIN_LEN/8]; + int jtag_chainlen = -1; + + jtag_soft_reset(); + + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(1); // select IR scan + m_dev->jtag_write_tms(0); // capture IR + m_dev->jtag_write_tms(0); // shift IR + + // first poison with 0 + memset(send_buf, 0, MAX_CHAIN_LEN/8); + m_dev->stream_inout(recv_buf, send_buf, MAX_CHAIN_LEN, false); + + if (recv_buf[MAX_CHAIN_LEN/8-1] != 0) + log->warning("ft2232: Did not receive 0 that we sent, JTAG chain might be faulty\n"); + + // now we send all 1's and see how long it takes for them to get back to us + memset(send_buf, 0xFF, MAX_CHAIN_LEN/8); + + m_dev->stream_inout(recv_buf, send_buf, MAX_CHAIN_LEN, true); + + for(int i = 0; i < MAX_CHAIN_LEN; i++) { + if ((recv_buf[i/8] >> (i%8)) & 0x1) { + jtag_chainlen = i; + break; + } + } + log->debug("ft2232: jtag_chainlen = %d\n", jtag_chainlen); + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(0); // run test idle + + return jtag_chainlen; +} + + + +int Adv_dbg_itf::dr_len_detect() +{ + char recv_buf[MAX_CHAIN_LEN/8]; + char send_buf[MAX_CHAIN_LEN/8]; + int jtag_chainlen = -1; + + m_dev->jtag_write_tms(1); // select DR scan + m_dev->jtag_write_tms(0); // capture DR scan + m_dev->jtag_write_tms(0); // shift DR scan + + // first poison with 0 + memset(send_buf, 0, MAX_CHAIN_LEN/8); + + m_dev->stream_inout(recv_buf, send_buf, MAX_CHAIN_LEN, false); + + if (recv_buf[MAX_CHAIN_LEN/8-1] != 0) + log->warning("ft2232: Did not receive 0 that we sent, JTAG chain might be faulty\n"); + + // now we send all 1's and see how long it takes for them to get back to us + memset(send_buf, 0xFF, MAX_CHAIN_LEN/8); + + m_dev->stream_inout(recv_buf, send_buf, MAX_CHAIN_LEN, true); + + for(int i = 0; i < MAX_CHAIN_LEN; i++) { + if ((recv_buf[i/8] >> (i%8)) & 0x1) { + jtag_chainlen = i; + break; + } + } + + m_dev->jtag_write_tms(1); // update DR + m_dev->jtag_write_tms(0); // run test idle + + return jtag_chainlen; +} + + +bool Adv_dbg_itf::jtag_soft_reset() +{ + pthread_mutex_lock(&mutex); + + for (int i=0; i < m_jtag_devices.size(); i++) + { + m_jtag_devices[i].is_in_debug = false; + } + bool result = m_dev->jtag_soft_reset(); + + pthread_mutex_unlock(&mutex); + + return result; +} + + + + +#define ADBG_CRC_POLY 0xedb88320 + +uint32_t Adv_dbg_itf::crc_compute(uint32_t crc, char* data_in, int length_bits) +{ + uint32_t d, c; + + for(int i = 0; i < length_bits; i++) + { + d = ((data_in[i / 8] >> (i % 8)) & 0x1) ? 0xffffffff : 0; + c = (crc & 0x1) ? 0xffffffff : 0; + crc = crc >> 1; + crc = crc ^ ((d ^ c) & ADBG_CRC_POLY); + } + + return crc; +} + + + +bool Adv_dbg_itf::jtag_pad_before() +{ + if (m_jtag_device_sel == 0) { + return true; + } + + unsigned int pad_bits = m_jtag_device_sel; + unsigned int pad_bytes = (pad_bits + 7)/8; + char* buffer = (char*)malloc(pad_bytes); + memset(buffer, 0, pad_bytes); + + if (!m_dev->stream_inout(NULL, buffer, pad_bits, false)) { + log->warning("ft2232: failed to pad chain before our selected device\n"); + return false; + } + + free(buffer); + + return true; +} + + + +bool Adv_dbg_itf::jtag_pad_after(bool tms) +{ + if (m_jtag_device_sel == m_jtag_devices.size() - 1) { + return true; + } + + unsigned int pad_bits = m_jtag_devices.size() - m_jtag_device_sel - 1; + unsigned int pad_bytes = (pad_bits + 7)/8; + char* buffer = (char*)malloc(pad_bytes); + memset(buffer, 0, pad_bytes); + + if (!m_dev->stream_inout(NULL, buffer, pad_bits, tms)) { + log->warning("ft2232: failed to pad chain before our selected device\n"); + return false; + } + + free(buffer); + + return true; +} + +bool Adv_dbg_itf::bit_inout(char* inbit, char outbit, bool last) +{ + this->check_cable(); + + pthread_mutex_lock(&mutex); + + // Invalidate debug mode in case the caller is sending raw bitstream as it might + // change the IR + if (m_jtag_device_sel < m_jtag_devices.size()) + m_jtag_devices[m_jtag_device_sel].is_in_debug = false; + bool result = m_dev->bit_inout(inbit, outbit, last); + + pthread_mutex_unlock(&mutex); + + return result; +} + +bool Adv_dbg_itf::stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last) +{ + this->check_cable(); + + pthread_mutex_lock(&mutex); + + // Invalidate debug mode in case the caller is sending raw bitstream as it might + // change the IR + if (m_jtag_device_sel < m_jtag_devices.size()) + m_jtag_devices[m_jtag_device_sel].is_in_debug = false; + bool result = m_dev->stream_inout(instream, outstream, n_bits, last); + + pthread_mutex_unlock(&mutex); + + return result; +} + +int Adv_dbg_itf::flush() +{ + pthread_mutex_lock(&mutex); + + bool result = m_dev->flush(); + + pthread_mutex_unlock(&mutex); + + return result; +} + +void Adv_dbg_itf::lock() +{ + pthread_mutex_lock(&mutex); +} + +void Adv_dbg_itf::unlock() +{ + pthread_mutex_unlock(&mutex); +} diff --git a/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.hpp b/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.hpp new file mode 100644 index 00000000..d1b95912 --- /dev/null +++ b/tools/pulp-debug-bridge/src/cables/adv_dbg_itf/adv_dbg_itf.hpp @@ -0,0 +1,144 @@ +/* + * 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. + */ + +/* + * Authors: Andreas Traber + */ + +#ifndef __CABLES_ADV_DBG_ITF_ADV_DBG_ITF_HPP__ +#define __CABLES_ADV_DBG_ITF_ADV_DBG_ITF_HPP__ + +#include +#include + +#include "cables/log.h" +#include "cable.hpp" + +class FTDILL; + +#define DEV_PROTOCOL_PULP 0 +#define DEV_PROTOCOL_RISCV 1 + +struct jtag_device { + uint32_t id; + unsigned int index; + unsigned int ir_len; + bool is_in_debug; + int protocol; +}; + +class Adv_dbg_itf : public Cable { + public: + Adv_dbg_itf(js::config *system_config, js::config *config, Log* log, Cable *itf); + virtual ~Adv_dbg_itf(); + + bool connect(); + void lock(); + void unlock(); + + + bool access(bool write, unsigned int addr, int size, char* buffer, int device=-1); + bool reg_access(bool write, unsigned int addr, char* buffer, int device=-1); + + void device_select(unsigned int i); + + void add_device(int ir_len, int protocol=DEV_PROTOCOL_PULP); + + + bool chip_reset(bool active, int duration); + bool chip_config(uint32_t config); + bool jtag_reset(bool active); + bool jtag_soft_reset(); + + + bool bit_inout(char* inbit, char outbit, bool last); + bool stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last); + + int flush(); + + + private: + enum ADBG_OPCODES { + AXI_WRITE8 = 0x1, + AXI_WRITE16 = 0x2, + AXI_WRITE32 = 0x3, + AXI_WRITE64 = 0x4, + AXI_READ8 = 0x5, + AXI_READ16 = 0x6, + AXI_READ32 = 0x7, + AXI_READ64 = 0x8 + }; + + Cable* m_dev; + Log* log; + + bool cable_connected = false; + bool connected = false; + + pthread_mutex_t mutex; + unsigned int debug_ir; + int retry_count; + int check_errors; + int access_timeout; + + + std::vector m_jtag_devices; + unsigned int m_jtag_device_sel = 0; + unsigned int m_jtag_device_default = 0; + + bool m_tms_on_last; + + js::config *bridge_config; + + bool reg_access_pulp(bool write, unsigned int addr, char* buffer); + + bool reg_access_riscv(bool write, unsigned int addr, char* buffer); + bool reg_access_read_riscv(bool write, unsigned int addr, char* buffer); + bool reg_access_write_riscv(bool write, unsigned int addr, char* buffer); + + bool write(unsigned int addr, int size, char* buffer); + bool write_internal(int bitwidth, unsigned int addr, int size, char* buffer); + bool write_internal_pulp(int bitwidth, unsigned int addr, int size, char* buffer); + bool write_internal_riscv(int bitwidth, unsigned int addr, int size, char* buffer); + + bool read(unsigned int addr, int size, char* buffer); + bool read_internal(int bitwidth, unsigned int addr, int size, char* buffer); + bool read_internal_pulp(int bitwidth, unsigned int addr, int size, char* buffer); + bool read_internal_riscv(int bitwidth, unsigned int addr, int size, char* buffer); + + bool read_error_reg(uint32_t *addr, bool *error); + bool clear_error_reg(); + + bool jtag_pad_before(); + bool jtag_pad_after(bool tms); + + bool jtag_debug(); + + int ir_len_detect(); + int dr_len_detect(); + + bool jtag_axi_select(); + bool jtag_auto_discovery(); + bool jtag_idle(); + bool jtag_set_selected_ir(char ir); + + bool check_connection(); + bool check_cable(); + + bool jtag_dmi_select(); + + uint32_t crc_compute(uint32_t crc, char* data_in, int length_bits); +}; + +#endif diff --git a/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.cpp b/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.cpp new file mode 100644 index 00000000..e82d446d --- /dev/null +++ b/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.cpp @@ -0,0 +1,941 @@ +/* + * 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. + */ + +/* + * Authors: Andreas Traber + */ + +/* + * Olimex ARM-USB-OCD, ARM-USB-OCD-H, ARM-USB-TINY and ARM-USB-TINY-H pinout + * + * GPIO Function Comment + * 0 TCK Set as output but do not drive manually + * 1 TDI Set as output but do not drive manually + * 2 TDO Set as input, should not be driven + * 3 TMS Set as output but do not drive manually + * 4 ?? GPIOL0 of FT2232H, set as output + * 5 ?? GPIOL1 of FT2232H, set as input, should not be driven + * 6 ?? GPIOL2 of FT2232H, set as input, should not be driven + * 7 RTCK GPIOL3 of FT2232H, set as input, should not be driven + * 8 TRST*) Set as output + * 9 SRST**) Set as output + * 10 ?? GPIOH2 of FT2232H, set as output + * 11 Red LED Set as output + * 12 ?? GPIOH4 of FT2232H, set as input, should not be driven + * 13 ?? GPIOH5 of FT2232H, set as input, should not be driven + * 14 ?? GPIOH6 of FT2232H, set as input, should not be driven + * 15 ?? GPIOH7 of FT2232H, set as input, should not be driven + * + * *) Pin is driven by Olimex in an active-high way, i.e. 0: driven to GND by Olimex + * 1: driven to VRef by Olimex + * **) Pin is driven by Olimex in an active-low way, i.e. 0: not driven by Olimex -> needs to be driven to high from DUT side (V_reset) + * 1: driven to GND by Olimex + * + * Use command SET_BITS_LOW for GPIO 0 - 7 (byte0: value, byte1: direction) + * Use command SET_BITS_HIGH for GPIO 8 - 15 (byte0: value, byte1: direction) + */ + +#include +#include +#include +#include +#include + +#include "ftdi.hpp" +#include "cables/log.h" + + +#ifndef min +#define min(X,Y) ((X) < (Y) ? (X) : (Y)) +#endif + +//----------------------------------------------------------------------------- + +Ftdi::Ftdi(js::config *config, Log* log, FTDIDeviceID id) : Cable(config), log (log), m_id (id) +{ + if (config->get("**/vendor") != NULL && config->get("**/product") != NULL) + { + m_descriptors.push_back((struct device_desc){(unsigned int)config->get_child_int("**/vendor"), (unsigned int)config->get_child_int("**/product")}); + } + else + { + // add all our known devices to the map + m_descriptors.push_back((struct device_desc){0x15ba, 0x002a}); + m_descriptors.push_back((struct device_desc){0x15ba, 0x002b}); + + m_descriptors.push_back((struct device_desc){0x0403, 0x6010}); // ftdi2232 Gapuino + m_descriptors.push_back((struct device_desc){0x1d6b, 0x0002}); // ftdi2232 Gapuino + } +} + +Ftdi::~Ftdi() +{ + ftdi_usb_close(&m_ftdic); + ftdi_deinit(&m_ftdic); + + if (m_params.send_buf) free(m_params.send_buf); + if (m_params.recv_buf) free(m_params.recv_buf); +} + +bool +Ftdi::connect(js::config *config) +{ + char buf[256]; + std::list dev_desc = m_descriptors; + int error; + bool result; + int err; + const char *description = NULL; + js::config *user_gpios = config->get("user_gpios"); + + if (config && config->get("description") != NULL) + { + description = config->get("description")->get_str().c_str(); + } + + m_params.send_buf_len = FTDX_MAXSEND; + m_params.send_buffered = 0; + m_params.send_buf = (char*)malloc(m_params.send_buf_len); + m_params.recv_buf_len = FTDI_MAXRECV; + m_params.to_recv = 0; + m_params.recv_write_idx = 0; + m_params.recv_read_idx = 0; + m_params.recv_buf = (char*)malloc(m_params.recv_buf_len); + + if (!m_params.send_buf || !m_params.recv_buf) { + log->error("ftdi2232: Can't allocate memory for ftdi context structures\n"); + goto fail; + } + + ftdi_init(&m_ftdic); + + if (config->get("bus") != NULL) + { +#ifdef FTDI_1_4 + if (ftdi_usb_open_bus_addr(&m_ftdic, config->get_child_int("**/bus"), config->get_child_int("**/device"))) + { + error = 1; + } +#else + log->error("Specifying device through USB address is only supported with at least version 1.4 of libftdi\n"); + goto fail; +#endif + } + else + { + //--------------------------------------------------------------------------- + // Device Selection + if (description == NULL) { + std::list dev_available; + struct device_desc dev; + + for (std::list::iterator it = dev_desc.begin(); + it != dev_desc.end(); it++) { + struct ftdi_device_list* devlist; + int n = ftdi_usb_find_all(&m_ftdic, &devlist, it->vid, it->pid); + + if (n > 0) { + for(int i = 0; i < n; ++i) { + if (dev_try_open(it->vid, it->pid, i)) { + log->user("Found ftdi device i:0x%X:0x%X:%d\n", + it->vid, it->pid, i); + dev_available.push_back({.vid = it->vid, .pid = it->pid, .index = (unsigned int)i}); + break; + } + } + } + + ftdi_list_free2(devlist); + } + + if (dev_available.size() == 0) { + log->error("ft2232: Connection failed\n"); + goto fail; + } + + dev = dev_available.front(); + log->user("Connecting to ftdi device i:0x%X:0x%X:%d\n", + dev.vid, dev.pid, dev.index); + error = ftdi_usb_open_desc_index(&m_ftdic, dev.vid, dev.pid, NULL, NULL, dev.index); + } else { + log->user("Connecting to ftdi device %s\n", description); + error = ftdi_usb_open_string(&m_ftdic, description); + } + } + + if (error != 0) { + if (error == -1) log->debug("usb_find_busses() failed\n"); + else if (error == -2) log->debug("usb_find_devices() failed\n"); + else if (error == -3) log->debug("usb device not found\n"); + else if (error == -4) log->debug("unable to open device\n"); + else if (error == -5) log->debug("unable to claim device\n"); + else if (error == -6) log->debug("reset failed\n"); + else if (error == -7) log->debug("set baudrate failed\n"); + else if (error == -8) log->debug("get product description failed\n"); + else if (error == -9) log->debug("get serial number failed\n"); + else if (error == -10) log->debug("unable to close device\n"); + else if (error == -11) log->debug("ftdi context invalid\n"); + else if (error == -12) log->debug("libusb_get_device_list() failed\n"); + else if (error == -13) log->debug("libusb_get_device_descriptor() failed\n"); + + log->warning("ft2232: Connection failed\n"); + + goto fail; + } + + if(!ft2232_mpsse_open()) { + log->error("ft2232: Open MPSSE mode failed\n"); + goto fail; + } + + log->debug("Connected to libftdi driver.\n"); + + //--------------------------------------------------------------------------- + // Setup layout for different devices + + int buf_len; + this->reverse_reset = config->get_child_bool("reverse_reset"); + bits_value = 0; + bits_direction = 0x1b; + + if (user_gpios != NULL) + { + for (auto x:user_gpios->get_elems()) + { + this->user_gpios.push_back(x->get_int()); + set_bit_direction(x->get_int(), 1); + } + } + + if (config->get("system_reset_gpio") != NULL) + { + this->system_reset_gpio = config->get_int("system_reset_gpio"); + set_bit_direction(this->system_reset_gpio, 1); + if (reverse_reset) + bits_value |= 1<system_reset_gpio; + } + + if (config->get("jtag_reset_gpio") != NULL) + { + this->jtag_reset_gpio = config->get_int("jtag_reset_gpio"); + set_bit_direction(this->jtag_reset_gpio, 1); + bits_value |= 1<jtag_reset_gpio; + } + + + buf[0] = SET_BITS_LOW; // Set value & direction of ADBUS lines + buf[1] = bits_value & 0xff; // values + buf[2] = 0x1b; // direction (1 == output) + //buf[3] = 0x8a; // Activate this command to disabled the default divider by 5, otherwise by default we can just go up to 6MHz instead of 30MHz + buf[3] = TCK_DIVISOR; + buf[4] = 0x02; // The divisor has been put to 2 as is not reliable on silicon with less + // than that + buf[5] = 0x00; + buf[6] = SEND_IMMEDIATE; + + buf_len = 7; + + if(ft2232_write(buf, buf_len, 0) != buf_len) { + log->error("ft2232: Initial write failed\n"); + goto fail; + } + + flush(); + + return true; + +fail: + if (m_params.send_buf) free(m_params.send_buf); + if (m_params.recv_buf) free(m_params.recv_buf); + + return false; +} + + +bool Ftdi::chip_config(uint32_t config) +{ + for (auto x:this->user_gpios) + { + this->set_bit_value(x, config & 1); + config >>= 1; + } + +} + +bool Ftdi::chip_reset(bool active, int duration) +{ + if (this->system_reset_gpio != -1) + { + if (this->reverse_reset) + active = !active; + + if (!set_bit_value(this->system_reset_gpio, active)) + return false; + } + + if (duration > 0) + usleep(duration / 1000); + + return true; +} + +int +Ftdi::ft2232_seq_purge(int purge_rx, int purge_tx) { + int ret = 0; + unsigned char buf; + + ret = ftdi_usb_purge_buffers(&m_ftdic); + if (ret < 0) { + log->warning("ft2232: ftdi_usb_purge_buffers() failed\n"); + return -1; + } + + ret = ftdi_read_data(&m_ftdic, &buf, 1); + if (ret < 0) { + log->warning("ft2232: ftdi_read_data() failed\n"); + return -1; + } + + return 0; +} + +int +Ftdi::ft2232_seq_reset() { + if (ftdi_usb_reset(&m_ftdic) < 0) { + log->warning("ft2232: ftdi_usb_reset() failed\n"); + return -1; + } + + if(ft2232_seq_purge(1, 1) < 0) { + log->warning("ft2232: Could not purge\n"); + return -1; + } + + return 0; +} + +int +Ftdi::flush() { + unsigned int xferred; + unsigned int recvd = 0; + + if (m_params.send_buffered == 0) + return 0; + + if ((xferred = ftdi_write_data(&m_ftdic, (uint8_t*)m_params.send_buf, m_params.send_buffered)) < 0) { + log->warning("ft2232: ftdi_write_data() failed\n"); + return -1; + } + + if (xferred < m_params.send_buffered) { + log->warning("Written fewer bytes than requested.\n"); + return -1; + } + + m_params.send_buffered = 0; + + /* now read all scheduled receive bytes */ + if (m_params.to_recv) { + if (m_params.recv_write_idx + m_params.to_recv > m_params.recv_buf_len) { + /* extend receive buffer */ + m_params.recv_buf_len = m_params.recv_write_idx + m_params.to_recv; + if (m_params.recv_buf) + m_params.recv_buf = (char*)realloc(m_params.recv_buf, m_params.recv_buf_len); + } + + assert(m_params.recv_buf != NULL); + + while (recvd == 0) { + recvd = ftdi_read_data(&m_ftdic, (uint8_t*)&(m_params.recv_buf[m_params.recv_write_idx]), m_params.to_recv); + if (recvd < 0) + log->warning("Error from ftdi_read_data()\n"); + } + + if (recvd < m_params.to_recv) + log->warning("Received less bytes than requested.\n"); + + m_params.to_recv -= recvd; + m_params.recv_write_idx += recvd; + } + + return xferred < 0 ? -1 : xferred; +} + +int +Ftdi::ft2232_read(char* buf, int len) { + int cpy_len; + int recvd = 0; + + /* flush send buffer to get all scheduled receive bytes */ + if (flush() < 0) { + log->warning("ft2232: Could not read any data after a flush\n"); + return -1; + } + + if (len == 0) { + log->warning("ft2232: Please don't read 0 bits\n"); + return 0; + } + + /* check for number of remaining bytes in receive buffer */ + cpy_len = m_params.recv_write_idx - m_params.recv_read_idx; + + if (cpy_len > len) + cpy_len = len; + + len -= cpy_len; + + if (cpy_len > 0) { + // get data from the receive buffer + memcpy(buf, &(m_params.recv_buf[m_params.recv_read_idx]), cpy_len); + m_params.recv_read_idx += cpy_len; + + if (m_params.recv_read_idx == m_params.recv_write_idx) + m_params.recv_read_idx = m_params.recv_write_idx = 0; + } + + if (len > 0) { + /* need to get more data directly from the device */ + while (recvd == 0) { + recvd = ftdi_read_data(&m_ftdic, (uint8_t*)&(buf[cpy_len]), len); + if (recvd < 0) + log->warning("ft2232: Error from ftdi_read_data()\n"); + } + } + + return recvd < 0 ? -1 : (cpy_len + len); +} + +int Ftdi::ft2232_write(char *buf, int len, int recv) +{ + int xferred = 0; + + // this write function will try to buffer write data + // buffering will be ceased and a flush triggered in two cases. + + // Case A: max number of scheduled receive bytes will be exceeded + // with this write + // Case B: max number of scheduled send bytes has been reached + if ((m_params.to_recv + recv > FTDI_MAXRECV) || ((m_params.send_buffered > FTDX_MAXSEND) && (m_params.to_recv == 0))) + xferred = flush(); + + if (xferred < 0) { + log->warning("ft2232: Flush before write failed\n"); + return -1; + } + + // now buffer this write + if (m_params.send_buffered + len > m_params.send_buf_len) { + m_params.send_buf_len = m_params.send_buffered + len; + + if (m_params.send_buf) + m_params.send_buf = (char*)realloc(m_params.send_buf, m_params.send_buf_len); + } + + assert(m_params.send_buf); + + memcpy( &(m_params.send_buf[m_params.send_buffered]), buf, len); + m_params.send_buffered += len; + if (recv > 0) + m_params.to_recv += recv; + + if (recv < 0) { + // immediate write requested, so flush the buffered data + xferred = flush(); + } + + return xferred < 0 ? -1 : len; +} + +bool Ftdi::ft2232_mpsse_open() +{ + char buf[3]; + int ret; + + // This sequence might seem weird and containing superfluous stuff. + // However, it's built after the description of JTAG_InitDevice + // Ref. FTCJTAGPG10.pdf + // Intermittent problems will occur when certain steps are skipped. + + ret = ft2232_seq_reset(); + if (ret < 0) + goto fail; + + ret = ft2232_seq_purge(1, 0); + if (ret < 0) + goto fail; + + ret = ftdi_write_data_set_chunksize(&m_ftdic, FTDX_MAXSEND_MPSSE); + if (ret < 0) { + log->warning("ft2232: Got error %s\n", ftdi_get_error_string(&m_ftdic)); + goto fail; + } + + ret = ftdi_read_data_set_chunksize(&m_ftdic, FTDX_MAXSEND_MPSSE); + if (ret < 0) { + log->warning("ft2232: Got error %s\n", ftdi_get_error_string(&m_ftdic)); + goto fail; + } + + /* set a reasonable latency timer value + if this value is too low then the chip will send intermediate result data + in short packets (suboptimal performance) */ + ret = ftdi_set_latency_timer(&m_ftdic, 1); + if (ret < 0) { + log->warning("ft2232: ftdi_set_latency_timer() failed\n"); + goto fail; + } + + ret = ftdi_set_bitmode(&m_ftdic, 0x0b, BITMODE_MPSSE); + if (ret < 0) { + log->warning("ft2232: ftdi_set_bitmode() failed\n"); + goto fail; + } + + ret = ftdi_usb_reset(&m_ftdic); + if (ret < 0) { + log->warning("ft2232: ftdi_usb_reset() failed\n"); + goto fail; + } + + ret = ft2232_seq_purge(1, 0); + if (ret < 0) { + log->warning("ft2232: Could not purge\n"); + goto fail; + } + + // set TCK Divisor + buf[0] = TCK_DIVISOR; + buf[1] = 0x2; + buf[2] = 0x0; + ret = ft2232_write(buf, 3, 0 ); + if (ret < 0) { + log->warning("ft2232: Failed to set TCK divisor\n"); + goto fail; + } + + // switch off loopback + buf[0] = LOOPBACK_END; + ret = ft2232_write(buf, 1, 0); + if (ret < 0) { + log->warning("ft2232: Failed to switch off loopback\n"); + goto fail; + } + + ret = flush(); + if (ret < 0) { + goto fail; + } + + ret = ftdi_usb_reset(&m_ftdic); + if (ret < 0) { + log->warning("ft2232: ftdi_usb_reset() failed\n"); + goto fail; + } + + ret = ft2232_seq_purge(1, 0); + if (ret < 0) { + log->warning("ft2232: Could not purge\n"); + goto fail; + } + + return true; + +fail: + log->warning("ft2232: Opening device in mpsse mode failed\n"); + + ftdi_usb_close(&m_ftdic); + ftdi_deinit(&m_ftdic); + + return false; +} + +bool Ftdi::dev_try_open(unsigned int vid, unsigned int pid, unsigned int index) const +{ + struct ftdi_context ftdic; + int error; + + ftdi_init(&ftdic); + + error = ftdi_usb_open_desc_index(&ftdic, vid, pid, NULL, NULL, index); + if (error != 0) { + ftdi_deinit(&ftdic); + return false; + } + + ftdi_usb_close(&ftdic); + ftdi_deinit(&ftdic); + + return true; +} + +bool Ftdi::bit_out(char outbit, bool last) +{ + return stream_out_internal(&outbit, 1, false, last); +} + +bool Ftdi::bit_inout(char* inbit, char outbit, bool last) +{ + return stream_inout(inbit, &outbit, 1, last); +} + +bool Ftdi::stream_out(char* outstream, unsigned int n_bits, bool last) +{ + return stream_out_internal(outstream, n_bits, false, last); +} + +bool Ftdi::stream_out_internal(char* outstream, unsigned int n_bits, bool postread, bool last) +{ + unsigned int len_bytes; + unsigned int len_bits; + unsigned int len_tms_bits; + char buf; + + len_tms_bits = last ? 1 : 0; + len_bytes = (n_bits - len_tms_bits) / 8; + len_bits = (n_bits - len_tms_bits) % 8; + + if(len_bytes > 0) { + if (ft2232_write_bytes(outstream, len_bytes, postread) < 0) { + log->warning("ft2232: ftdi_stream_out has failed\n"); + return false; + } + } + + if(len_bits > 0) { + if (ft2232_write_bits(&(outstream[len_bytes]), len_bits, postread, 0) < 0) { + log->warning("ft2232: ftdi_stream_out has failed\n"); + return false; + } + } + + if(len_tms_bits > 0) { + buf = outstream[len_bytes] >> len_bits; + if (ft2232_write_bits(&buf, 1, postread, 1) < 0) { + log->warning("ft2232: ftdi_stream_out has failed\n"); + return false; + } + } + + return true; +} + +bool Ftdi::stream_in(char* instream, unsigned int n_bits, bool last) +{ + int len_bytes; + int len_bits; + int len_tms_bits; + + len_tms_bits = last ? 1 : 0; + len_bytes = (n_bits - len_tms_bits) / 8; + len_bits = (n_bits - len_tms_bits) % 8; + + if(len_bytes > 0) { + if (ft2232_read_packed_bits(instream, len_bytes, 8, 0) < 0) { + log->warning("ft2232: fdti_stream_in has failed\n"); + return false; + } + } + + if(len_bits > 0) { + if (ft2232_read_packed_bits(instream, 1, len_bits, len_bytes * 8) < 0) { + log->warning("ft2232: fdti_stream_in has failed\n"); + return false; + } + } + + if(len_tms_bits > 0) { + if (ft2232_read_packed_bits(instream, 1, 1, (len_bits + (len_bytes * 8))) < 0) { + log->warning("ft2232: fdti_stream_in has failed\n"); + return false; + } + } + + return true; +} + +bool Ftdi::stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last) +{ + if (outstream) + { + if (!stream_out_internal(outstream, n_bits, instream != NULL, last)) { + log->warning("ft2232: ftdi_stream_inout has failed\n"); + return false; + } + } + else + { + int bytes = (n_bits + 7) / 8; + char buffer[bytes]; + ::memset(buffer, 0, bytes); + if (!stream_out_internal(buffer, n_bits, instream != NULL, last)) { + log->warning("ft2232: ftdi_stream_inout has failed\n"); + return false; + } + } + + if (instream && !stream_in(instream, n_bits, last)) { + log->warning("ft2232: ftdi_stream_inout has failed\n"); + return false; + } + + return true; +} + +int Ftdi::ft2232_write_bytes(char *buf, int len, bool postread) +{ + int cur_command_size; + int max_command_size; + int cur_chunk_len; + int recv; + int xferred; + char *mybuf; + + if(len == 0) + return 0; + + recv = 0; + max_command_size = min(len, 65536)+3; + mybuf = (char*) malloc(max_command_size); + + /// Command OPCODE: write bytes + mybuf[0] = MPSSE_DO_WRITE | MPSSE_LSB | MPSSE_WRITE_NEG; + if(postread) // if postread is enabled it will buffer incoming bytes + mybuf[0] = mybuf[0] | MPSSE_DO_READ; + + // We divide the transmitting stream of bytes in chunks with a maximun length of 65536 bytes each. + while(len > 0) { + cur_chunk_len = min(len, 65536); + len = len - cur_chunk_len; + cur_command_size = cur_chunk_len + 3; + + /// Low and High bytes of the length field + mybuf[1] = (unsigned char) ( cur_chunk_len - 1); + mybuf[2] = (unsigned char) ((cur_chunk_len - 1) >> 8); + + /// The rest of the command is filled with the bytes that will be transferred + memcpy(&(mybuf[3]), buf, cur_chunk_len ); + buf = buf + cur_chunk_len; + + /// Finally we can transmit the command + xferred = ft2232_write(mybuf, cur_command_size, (postread ? cur_chunk_len : 0) ); + if(xferred != cur_command_size) { + log->warning("ft2232: could not transmit command\n"); + free(mybuf); + return -1; + } + + // If OK, the update the number of incoming bytes that are being buffered for a posterior read + if(postread) + recv = recv + cur_chunk_len; + } + + free(mybuf); + + // Returns the number of buffered incoming bytes + return recv; +} + +int Ftdi::ft2232_write_bits(char *buf, int len, bool postread, bool with_tms) +{ + int max_command_size; + int max_chunk_len; + int cur_chunk_len; + int recv; + int xferred; + int i; + char mybuf[3]; + + if(len == 0) + return 0; + + max_command_size = 3; + + if(!with_tms) { + /// Command OPCODE: write bits (can write up to 8 bits in a single command) + max_chunk_len = 8; + mybuf[0] = MPSSE_DO_WRITE | MPSSE_LSB | MPSSE_WRITE_NEG | MPSSE_BITMODE; + } + else { + /// Command OPCODE: 0x4B write bit with tms (can write up to 1 bits in a single command) + max_chunk_len = 1; + mybuf[0] = MPSSE_WRITE_TMS|MPSSE_LSB|MPSSE_BITMODE|MPSSE_WRITE_NEG; + } + + if(postread) // (OPCODE += 0x20) if postread is enabled it will buffer incoming bits + mybuf[0] = mybuf[0] | MPSSE_DO_READ; + + // We divide the transmitting stream of bytes in chunks with a maximun length of max_chunk_len bits each. + i = 0; + recv = 0; + while(len > 0) { + cur_chunk_len = min(len, max_chunk_len); + len = len - cur_chunk_len; + + // Bit length field + mybuf[1] = (char) (cur_chunk_len - 1); + + + if(!with_tms) { + /// The last byte of the command is filled with the bits that will be transferred + mybuf[2] = buf[i/8]; + i += 8; + } + else { + mybuf[2] = 0x01 | ((buf[(i/8)] >> (i%8)) << 7); + i++; + } + + // Finally we can transmit the command + xferred = ft2232_write(mybuf, max_command_size, (postread ? 1 : 0) ); + if(xferred != max_command_size) { + log->warning("ft2232: ftdi write has failed\n"); + return -1; + } + + // If OK, the update the number of incoming bytes that are being buffered for a posterior read + if(postread) + recv = recv + 1; + } + + if (flush() < 0) + return -1; + + return recv; +} + +int Ftdi::ft2232_read_packed_bits(char *buf, int packet_len, int bits_per_packet, int offset) +{ + char *mybuf; + unsigned char dst_mask; + unsigned char src_mask; + int row_offset; + int dst_row; + int dst_col; + int src_row; + int src_col; + int i; + + if(packet_len == 0 || bits_per_packet == 0) + return 0; + + if (offset == 0 && bits_per_packet == 8) + { + if(ft2232_read(buf, packet_len) < 0) { + log->warning("Read failed\n"); + return -1; + } + } + else + { + mybuf = (char*) malloc(packet_len); + if(ft2232_read(mybuf, packet_len) < 0) { + log->warning("Read failed\n"); + free(mybuf); + return -1; + } + + if(bits_per_packet < 8) { + for(i=0; i < packet_len; i++) { // rotate bits to the left side + mybuf[i] = (mybuf[i] >> (8-bits_per_packet)); + } + + for(i = offset; i < (packet_len*bits_per_packet+offset); i++) { + dst_row = i / 8; + dst_col = i % 8; + src_row = (i-offset) / bits_per_packet; + src_col = (i-offset) % bits_per_packet; + dst_mask = ~(1 << dst_col); + src_mask = (1 << src_col); + buf[dst_row] = (buf[dst_row] & dst_mask) | ((mybuf[src_row] & src_mask) >> (dst_col - src_col)); + } + } else if(bits_per_packet == 8) { + row_offset = offset / 8; + memcpy( &(buf[row_offset]), mybuf, packet_len); + } else { + free(mybuf); + return -1; + } + + free(mybuf); + } + + return 0; +} + +bool Ftdi::set_bit_value(int bit, int value) +{ + + char buf[4]; + + bits_value = (bits_value & ~(1<= 8) + { + buf[0] = SET_BITS_HIGH; + buf[1] = bits_value >> 8; + buf[2] = bits_direction >> 8; + buf[3] = SEND_IMMEDIATE; + } + else + { + buf[0] = SET_BITS_LOW; + buf[1] = bits_value; + buf[2] = bits_direction; + buf[3] = SEND_IMMEDIATE; + } + + if (ft2232_write(buf, 4, 0) != 4) return false; + flush(); + return true; +} + +bool Ftdi::set_bit_value_del(int bit, int value, int del) +{ + + char buf[4]; + + bits_value = (bits_value & ~(1<= 8) + { + buf[0] = SET_BITS_HIGH; + buf[1] = bits_value >> 8; + buf[2] = bits_direction >> 8; + buf[3] = SEND_IMMEDIATE; + } + else + { + buf[0] = SET_BITS_LOW; + buf[1] = bits_value; + buf[2] = bits_direction; + buf[3] = SEND_IMMEDIATE; + } + + if (ft2232_write(buf, 4, 0) != 4) return false; + usleep(del); + flush(); + return true; +} + +bool Ftdi::set_bit_direction(int bit, int isout) +{ + bits_direction = (bits_direction & ~(1<jtag_reset_gpio != -1) + return set_bit_value(this->jtag_reset_gpio, !active); + return true; +} + diff --git a/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.hpp b/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.hpp new file mode 100644 index 00000000..3cbba3f2 --- /dev/null +++ b/tools/pulp-debug-bridge/src/cables/ftdi/ftdi.hpp @@ -0,0 +1,124 @@ +/* + * 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. + */ + +/* + * Authors: Andreas Traber + */ + +#ifndef MEM_FTDI_LL_H +#define MEM_FTDI_LL_H + +#include "cables/adv_dbg_itf/adv_dbg_itf.hpp" +#include "cable.hpp" + +#include +#include +#include + +extern "C" { +#include +} + +#define FTDX_MAXSEND 4096 +#define FTDX_MAXSEND_MPSSE (64 * 1024) +#define FTDI_MAXRECV ( 4 * 64) + +struct ftdi_param { + uint32_t send_buf_len; + uint32_t send_buffered; + char *send_buf; + uint32_t recv_buf_len; + uint32_t to_recv; + uint32_t recv_write_idx; + uint32_t recv_read_idx; + char *recv_buf; +}; + +class Log; + +class Ftdi : public Cable { + public: + + enum FTDIDeviceID { + Olimex, + Digilent, + Generic, + }; + + Ftdi(js::config *config, Log* log, FTDIDeviceID id); + ~Ftdi(); + + bool connect(js::config *config); + + bool bit_inout(char* inbit, char outbit, bool last); + + bool stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last); + + bool jtag_reset(bool active); + + int flush(); + + + + bool chip_reset(bool active, int duration); + bool chip_config(uint32_t config); + + private: + struct device_desc { + unsigned int vid; + unsigned int pid; + unsigned int index; + }; + + bool set_bit_value(int bit, int value); + bool set_bit_value_del(int bit, int value, int del); + bool set_bit_direction(int bit, int isout); + + bool bit_out(char outbit, bool tms); + + bool stream_out(char* outstream, unsigned int n_bits, bool last); + bool stream_in(char* instream, unsigned int n_bits, bool last); + + void state_trans_tms(int tms); + + bool stream_out_internal(char* outstream, unsigned int n_bits, bool postread, bool last); + + int ft2232_write_bytes(char *buf, int len, bool postread); + int ft2232_write_bits(char *buf, int len, bool postread, bool with_tms); + int ft2232_read_packed_bits(char *buf, int packet_len, int bits_per_packet, int offset); + + bool ft2232_common_open(); + bool ft2232_mpsse_open(); + int ft2232_seq_purge(int purge_rx, int purge_tx); + int ft2232_seq_reset(); + int ft2232_read(char* buf, int len); + int ft2232_write(char *buf, int len, int recv); + bool dev_try_open(unsigned int vid, unsigned int pid, unsigned int index) const; + + std::list m_descriptors; + std::vector user_gpios; + + Log* log; + FTDIDeviceID m_id; + struct ftdi_param m_params; + struct ftdi_context m_ftdic; + unsigned int bits_value; + unsigned int bits_direction; + int system_reset_gpio = -1; + int jtag_reset_gpio = -1; + bool reverse_reset = false; + +}; + +#endif diff --git a/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.cpp b/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.cpp new file mode 100644 index 00000000..189c79af --- /dev/null +++ b/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cables/log.h" +#include "jtag-proxy.hpp" +#include "debug_bridge/proxy.hpp" + +Jtag_proxy::Jtag_proxy(Log* log) : Cable(NULL) +{ +} + +bool Jtag_proxy::connect(js::config *config) +{ + struct sockaddr_in addr; + struct hostent *he; + + js::config *proxy_config = config->get("jtag-proxy"); + + if (proxy_config == NULL || proxy_config->get("port") == NULL) + { + fprintf(stderr, "Didn't find any information for JTAG proxy\n"); + return false; + } + + int m_port = proxy_config->get("port")->get_int(); + char *m_server = (char *)"localhost"; + + if((m_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "Unable to create socket (%s)\n", strerror(errno)); + return false; + } + + if((he = gethostbyname(m_server)) == NULL) { + perror("gethostbyname"); + return false; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr = *((struct in_addr *)he->h_addr_list[0]); + memset(addr.sin_zero, '\0', sizeof(addr.sin_zero)); + + if(::connect(m_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + fprintf(stderr, "Unable to connect to %s port %d (%s)\n", m_server, m_port, + strerror(errno)); + return false; + } + return true; +} + +bool Jtag_proxy::bit_inout(char* inbit, char outbit, bool last) +{ + return stream_inout(inbit, &outbit, 1, last); +} + +bool Jtag_proxy::proxy_stream(char* instream, char* outstream, unsigned int n_bits, bool last, int bit) +{ + proxy_req_t req = { .type=DEBUG_BRIDGE_JTAG_REQ }; + req.jtag.bits = n_bits; + req.jtag.tdo = instream != NULL; + + if (n_bits >= (1<<16)) return false; + + uint8_t buffer[n_bits]; + uint8_t value; + if (outstream) + { + for (int i=0; i>= 1; + } + } + else + { + ::memset(buffer, 0, n_bits); + } + + if (last) + { + buffer[n_bits-1] |= 1 << DEBUG_BRIDGE_JTAG_TMS; + } + + ::send(m_socket, (void *)&req, sizeof(req), 0); + ::send(m_socket, (void *)buffer, n_bits, 0); + if (instream != NULL) + { + + ::memset((void *)instream, 0, (n_bits + 7) / 8); + if (::recv(m_socket, (void *)instream, (n_bits + 7) / 8, 0) != (n_bits + 7) / 8) return false; + + } + return true; +} + +bool Jtag_proxy::stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last) +{ + return proxy_stream(instream, outstream, n_bits, last, DEBUG_BRIDGE_JTAG_TDI); +} + +bool Jtag_proxy::jtag_reset(bool active) +{ + int value = !active; + return proxy_stream(NULL, (char *)&value, 1, 0, DEBUG_BRIDGE_JTAG_TRST); +} + +int Jtag_proxy::flush() +{ + return true; +} + +bool Jtag_proxy::chip_reset(bool active, int duration) +{ + proxy_req_t req = { .type=DEBUG_BRIDGE_RESET_REQ }; + req.reset.active = active; + req.reset.duration = duration; + ::send(m_socket, (void *)&req, sizeof(req), 0); + + return true; +} + +bool Jtag_proxy::chip_config(uint32_t config) +{ + proxy_req_t req = { .type=DEBUG_BRIDGE_CONFIG_REQ }; + req.config.value = config; + ::send(m_socket, (void *)&req, sizeof(req), 0); + return true; +} diff --git a/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.hpp b/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.hpp new file mode 100644 index 00000000..97289aea --- /dev/null +++ b/tools/pulp-debug-bridge/src/cables/jtag-proxy/jtag-proxy.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ + + +#include "cables/adv_dbg_itf/adv_dbg_itf.hpp" +#include "cable.hpp" + +#include + +class Jtag_proxy : public Cable { + public: + + Jtag_proxy(Log* log); + + bool connect(js::config *config); + + bool bit_inout(char* inbit, char outbit, bool last); + + bool stream_inout(char* instream, char* outstream, unsigned int n_bits, bool last); + + bool jtag_reset(bool active); + + int flush(); + + + + bool chip_reset(bool active, int duration); + bool chip_config(uint32_t config); + + private: + + int m_socket; + + bool proxy_stream(char* instream, char* outstream, unsigned int n_bits, bool last, int bit); + +}; \ No newline at end of file diff --git a/tools/pulp-debug-bridge/src/cables/jtag.cpp b/tools/pulp-debug-bridge/src/cables/jtag.cpp new file mode 100644 index 00000000..ce571454 --- /dev/null +++ b/tools/pulp-debug-bridge/src/cables/jtag.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ +#include +#include +#include +#include +#include +#include "cable.hpp" + + +#define JTAG_SOC_INSTR_WIDTH 0x4 +#define JTAG_SOC_IDCODE 0x2 +#define JTAG_SOC_AXIREG 0x4 +#define JTAG_SOC_BBMUXREG 0x5 +#define JTAG_SOC_CLKGATEREG 0x6 +#define JTAG_SOC_CONFREG 0x7 +#define JTAG_SOC_TESTMODEREG 0x8 +#define JTAG_SOC_BISTREG 0x9 +#define JTAG_SOC_BYPASS 0xf +#define JTAG_SOC_IDCODE_WIDTH 32 +#define JTAG_SOC_AXIREG_WIDTH 96 +#define JTAG_SOC_BBMUXREG_WIDTH 21 +#define JTAG_SOC_CLKGATEREG_WIDTH 11 +#define JTAG_SOC_CONFREG_WIDTH 4 +#define JTAG_SOC_TESTMODEREG_WIDTH 4 +#define JTAG_SOC_BISTREG_WIDTH 20 + +#define JTAG_CLUSTER_INSTR_WIDTH 4 +#define JTAG_CLUSTER_IDCODE 2 +#define JTAG_CLUSTER_SAMPLE_PRELOAD 1 +#define JTAG_CLUSTER_EXTEST 0 +#define JTAG_CLUSTER_DEBUG 8 +#define JTAG_CLUSTER_MBIST 9 +#define JTAG_CLUSTER_BYPASS 0xf +#define JTAG_CLUSTER_IDCODE_WIDTH 32 + +#define JTAG_IDCODE_WIDTH JTAG_CLUSTER_IDCODE_WIDTH + JTAG_SOC_IDCODE_WIDTH +#define JTAG_INSTR_WIDTH JTAG_CLUSTER_INSTR_WIDTH + JTAG_SOC_INSTR_WIDTH + + +bool Cable_jtag_itf::jtag_soft_reset() { + for (int i = 0; i < 10; i++) + jtag_write_tms(1); + + jtag_write_tms(0); + + return true; +} + +bool Cable_jtag_itf::jtag_write_tms(int val) +{ + return bit_inout(NULL, 0x0, val != 0); +} + +bool Cable_jtag_itf::jtag_shift_ir() +{ + if (!jtag_write_tms(1)) return false; + if (!jtag_write_tms(1)) return false; + if (!jtag_write_tms(0)) return false; + if (!jtag_write_tms(0)) return false; + return true; +} + +bool Cable_jtag_itf::jtag_shift_dr() +{ + if (!jtag_write_tms(1)) return false; + if (!jtag_write_tms(0)) return false; + if (!jtag_write_tms(0)) return false; + return true; +} + +bool Cable_jtag_itf::jtag_idle() +{ + if (!jtag_write_tms(1)) return false; + if (!jtag_write_tms(0)) return false; + return true; +} + +bool Cable_jtag_itf::jtag_shift(int width, char *bits) +{ + return stream_inout(NULL, bits, width, 1); +} + +bool Cable_jtag_itf::jtag_shift_ir(unsigned int ir, int ir_len) +{ + if (ir_len == -1) + ir_len = JTAG_SOC_INSTR_WIDTH; + + if (!jtag_shift_ir()) return false; + if (!jtag_shift(ir_len, (char *)&ir)) return false; + if (!jtag_idle()) return false; + return true; +} + +bool Cable_jtag_itf::jtag_set_reg(unsigned int reg, int width, unsigned int value, int ir_len) +{ + if (!jtag_shift_ir(reg, ir_len)) return false; + if (!jtag_shift_dr()) return false; + if (!jtag_shift(width, (char *)&value)) return false; + if (!jtag_idle()) return false; + return true; +} + +bool Cable_jtag_itf::jtag_get_reg(unsigned int reg, int width, unsigned int *out_value, unsigned int value, int ir_len) +{ + if (!jtag_shift_ir(reg, ir_len)) return false; + if (!jtag_shift_dr()) return false; + if (!stream_inout((char *)out_value, (char *)&value, width, 1)) return false; + if (!jtag_idle()) return false; + return true; +} diff --git a/tools/pulp-debug-bridge/src/cables/log.h b/tools/pulp-debug-bridge/src/cables/log.h new file mode 100644 index 00000000..aacedb37 --- /dev/null +++ b/tools/pulp-debug-bridge/src/cables/log.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ + +#ifndef LOG_H +#define LOG_H + +typedef enum +{ + LOG_ERROR = 0, + LOG_WARNING = 1, + LOG_INFO = 2, + LOG_DEBUG = 3 +} log_level_e; + +class Log { + public: + void print(log_level_e, const char *str, ...); + void error(const char *str, ...) ; + void warning(const char *str, ...) ; + void user(const char *str, ...) ; + void debug(const char *str, ...) ; +}; + +#endif diff --git a/tools/pulp-debug-bridge/src/gdb-server/breakpoints.cpp b/tools/pulp-debug-bridge/src/gdb-server/breakpoints.cpp new file mode 100644 index 00000000..1d046c55 --- /dev/null +++ b/tools/pulp-debug-bridge/src/gdb-server/breakpoints.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Andreas Traber + */ + + +#include "gdb-server.hpp" + +#include +#include + +#define INSN_IS_COMPRESSED(instr) ((instr & 0x3) != 0x3) +#define INSN_BP_COMPRESSED 0x9002 +#define INSN_BP 0x00100073 + +Breakpoints::Breakpoints(Gdb_server *top) +: top(top) { +} + +bool +Breakpoints::insert(unsigned int addr) { + bool retval; + uint32_t data_bp; + struct bp_insn bp; + + bp.addr = addr; + retval = top->cable->access(false, addr, 4, (char*)&bp.insn_orig); + bp.is_compressed = INSN_IS_COMPRESSED(bp.insn_orig); + + breakpoints.push_back(bp); + + if (bp.is_compressed) { + data_bp = INSN_BP_COMPRESSED; + retval = retval && top->cable->access(true, addr, 2, (char*)&data_bp); + } else { + data_bp = INSN_BP; + retval = retval && top->cable->access(true, addr, 4, (char*)&data_bp); + } + + this->top->target->flush(); + + return retval; +} + +bool +Breakpoints::remove(unsigned int addr) { + + bool retval; + bool is_compressed; + uint32_t data; + for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { + if (it->addr == addr) { + data = it->insn_orig; + is_compressed = it->is_compressed; + + breakpoints.erase(it); + + if (is_compressed) + retval = top->cable->access(true, addr, 2, (char*)&data); + else + retval = top->cable->access(true, addr, 4, (char*)&data); + + this->top->target->flush(); + + return retval; + } + } + + return false; +} + +bool +Breakpoints::clear() { + + bool retval = this->disable_all(); + + breakpoints.clear(); + + return retval; +} + + +bool +Breakpoints::at_addr(unsigned int addr) { + for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { + if (it->addr == addr) { + // we found our bp + return true; + } + } + + return false; +} + +bool +Breakpoints::enable(unsigned int addr) { + bool retval; + uint32_t data; + + for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { + if (it->addr == addr) { + if (it->is_compressed) { + data = INSN_BP_COMPRESSED; + retval = top->cable->access(1, addr, 2, (char*)&data); + } else { + data = INSN_BP; + retval = top->cable->access(1, addr, 4, (char*)&data); + } + + return true; + //return retval && m_cache->flush(); + } + } + + fprintf(stderr, "bp_enable: Did not find any bp at addr %08X\n", addr); + + return false; +} + +bool +Breakpoints::disable(unsigned int addr) { + bool retval; + + for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { + if (it->addr == addr) { + if (it->is_compressed) + retval = top->cable->access(1, addr, 2, (char*)&it->insn_orig); + else + retval = top->cable->access(1, addr, 4, (char*)&it->insn_orig); + + return true; + //return retval && m_cache->flush(); + } + } + + fprintf(stderr, "bp_enable: Did not find any bp at addr %08X\n", addr); + + return false; +} + +bool +Breakpoints::enable_all() { + bool retval = true; + + for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { + retval = retval && this->enable(it->addr); + } + + return retval; +} + +bool +Breakpoints::disable_all() { + bool retval = true; + + for (std::list::iterator it = breakpoints.begin(); it != breakpoints.end(); it++) { + retval = retval && this->disable(it->addr); + } + + return retval; +} diff --git a/tools/pulp-debug-bridge/src/gdb-server/gdb-server.cpp b/tools/pulp-debug-bridge/src/gdb-server/gdb-server.cpp new file mode 100644 index 00000000..d6f35cde --- /dev/null +++ b/tools/pulp-debug-bridge/src/gdb-server/gdb-server.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Andreas Traber + */ + +#include "cable.hpp" +#include "gdb-server/gdb-server.hpp" +#include + + +Gdb_server::Gdb_server(Log *log, Cable *cable, js::config *config, int socket_port) +: log(log), cable(cable), config(config) +{ + target = new Target(this); + + bkp = new Breakpoints(this); + + rsp = new Rsp(this, socket_port); + + if (!rsp->open()) throw std::logic_error("Unable to open RSP server"); +} + +int Gdb_server::stop(bool kill) +{ + if (rsp != NULL) + { + rsp->close(kill); + rsp = NULL; + } +} + +void Gdb_server::print(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vprintf(format, ap); + va_end(ap); +} diff --git a/tools/pulp-debug-bridge/src/gdb-server/gdb-server.h b/tools/pulp-debug-bridge/src/gdb-server/gdb-server.h new file mode 100644 index 00000000..defb494b --- /dev/null +++ b/tools/pulp-debug-bridge/src/gdb-server/gdb-server.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Andreas Traber + */ + +#ifndef __GDB_SERVER_GDB_SERVER_H__ +#define __GDB_SERVER_GDB_SERVER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class Rsp { + public: + Rsp(int socket_ptr); + //Rsp(int socket_port, MemIF* mem, LogIF *log, std::list list_dbgif, std::list list_dbg_cluster_if, BreakPoints* bp, DbgIF *main_if); + + private: + bool open(); + void close(); + + bool wait_client(); + bool loop(); + + bool decode(char* data, size_t len); + + bool multithread(char* data, size_t len); + + bool cont(char* data, size_t len); // continue, reserved keyword, thus not used as function name + bool step(char* data, size_t len); + + bool query(char* data, size_t len); + bool v_packet(char* data, size_t len); + + bool regs_send(); + bool reg_read(char* data, size_t len); + bool reg_write(char* data, size_t len); + + bool get_packet(char* data, size_t* len); + + bool signal(); + + bool send(const char* data, size_t len); + bool send_str(const char* data); + + + // internal helper functions + bool pc_read(unsigned int* pc); + + bool waitStop(DbgIF* dbgif); + bool resume(bool step); + bool resume(int tid, bool step); + void resumeCoresPrepare(DbgIF *dbgif, bool step); + void resumeCores(); + + bool mem_read(char* data, size_t len); + bool mem_write_ascii(char* data, size_t len); + bool mem_write(char* data, size_t len); + + bool bp_insert(char* data, size_t len); + bool bp_remove(char* data, size_t len); + + DbgIF* get_dbgif(int thread_id); + + int m_socket_port; + int m_socket_in; + int m_socket_client; + + int m_thread_sel; + int m_thread_init; + MemIF* m_mem; + LogIF *log; + BreakPoints* m_bp; + std::list m_dbgifs; + std::list m_dbg_cluster_ifs; +}; + +#endif \ No newline at end of file diff --git a/tools/pulp-debug-bridge/src/gdb-server/gdb-server.hpp b/tools/pulp-debug-bridge/src/gdb-server/gdb-server.hpp new file mode 100644 index 00000000..7dded16c --- /dev/null +++ b/tools/pulp-debug-bridge/src/gdb-server/gdb-server.hpp @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Andreas Traber + */ + +#ifndef __GDB_SERVER_GDB_SERVER_H__ +#define __GDB_SERVER_GDB_SERVER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cable.hpp" +#include "json.hpp" + +#define DBG_CTRL_REG 0x0 +#define DBG_HIT_REG 0x4 +#define DBG_IE_REG 0x8 +#define DBG_CAUSE_REG 0xC +#define DBG_NPC_REG 0x2000 +#define DBG_PPC_REG 0x2004 + +#define DBG_CAUSE_BP 0x3 + + +class Rsp; +class Breakpoints; +class Target; +class Target_cluster_common; +class Target_cluster; +class Target_core; + +static int first_free_thread_id = 0; + +class Gdb_server +{ +public: + Gdb_server(Log *log, Cable *cable, js::config *config, int socket_port); + int stop(bool kill); + void print(const char *format, ...); + + Rsp *rsp; + Log *log; + Cable *cable; + Target *target; + Breakpoints *bkp; + + js::config *config; +}; + + + + +class Target_core +{ +public: + Target_core(Gdb_server *top, uint32_t dbg_unit_addr, int cluster_id, int core_id); + void set_power(bool is_on); + bool read(uint32_t addr, uint32_t* rdata); + bool write(uint32_t addr, uint32_t wdata); + bool csr_read(unsigned int i, uint32_t *data); + int get_thread_id() { return thread_id; } + void get_name(char* str, size_t len) { + snprintf(str, len, "Cluster %02d - Core %01d", this->cluster_id, this->core_id); + } + bool is_stopped(); + void read_ppc(uint32_t *ppc); + + bool stop(); + bool halt(); + void prepare_resume(bool step=false); + void commit_resume(); + void set_step_mode(bool new_step); + void commit_step_mode(); + void resume(); + void flush(); + + bool gpr_read_all(uint32_t *data); + bool gpr_read(unsigned int i, uint32_t *data); + bool gpr_write(unsigned int i, uint32_t data); + +private: + Gdb_server *top; + bool is_on = false; + uint32_t dbg_unit_addr; + uint32_t hartid; + int cluster_id; + int core_id; + int thread_id; + bool ppc_is_cached = false; + uint32_t ppc_cached; + bool stopped = false; + bool step = false; + bool commit_step = false; +}; + + + +class Target { +public: + Target(Gdb_server *top); + + inline int get_nb_threads() { return cores.size(); } + + + void halt(); + void resume(bool step=false, int tid=-1); + void resume_all(); + bool wait(int socket_client); + void flush(); + + void update_power(); + + std::vector get_threads() { return cores; } + Target_core *get_thread(int thread_id) { return cores_from_threadid[thread_id]; } + Target_core *get_thread_from_id(int id) { return cores[id]; } + + +private: + Gdb_server *top; + std::vector clusters; + std::vector cores; + std::map cores_from_threadid; +}; + + + +struct bp_insn { + uint32_t addr; + uint32_t insn_orig; + bool is_compressed; +}; + + + +class Breakpoints { + public: + Breakpoints(Gdb_server *top); + + bool insert(unsigned int addr); + bool remove(unsigned int addr); + + bool clear(); + + bool at_addr(unsigned int addr); + + bool enable_all(); + bool disable_all(); + + bool disable(unsigned int addr); + bool enable(unsigned int addr); + + private: + std::list breakpoints; + Gdb_server *top; +}; + +class Rsp { + public: + Rsp(Gdb_server *top, int socket_port); + //Rsp(int socket_port, MemIF* mem, LogIF *log, std::list list_dbgif, std::list list_dbg_cluster_if, BreakPoints* bp, DbgIF *main_if); + + bool open(); + void close(int kill); + + + private: + + bool decode(int socket_client, char* data, size_t len); + bool get_packet(int socket_client, char* data, size_t* len); + + void client_routine(int socket_client); + void listener_routine(); + + + bool regs_send(int socket_client); + bool signal(int socket_client); + + bool multithread(int socket_client, char* data, size_t len); + + bool v_packet(int socket_client, char* data, size_t len); + + bool query(int socket_client, char* data, size_t len); + + bool send(int socket_client, const char* data, size_t len); + bool send_str(int socket_client, const char* data); + + bool cont(int socket_client, char* data, size_t len); // continue, reserved keyword, thus not used as function name + bool resume(int socket_client, bool step); + bool resume(int socket_client, int tid, bool step); + bool wait(int socket_client, Target_core *core=NULL); + bool step(int socket_client, char* data, size_t len); + + // internal helper functions + bool pc_read(int socket_client, unsigned int* pc); + + bool reg_read(int socket_client, char* data, size_t len); + bool reg_write(int socket_client, char* data, size_t len); + + bool mem_read(int socket_client, char* data, size_t len); + bool mem_write_ascii(int socket_client, char* data, size_t len); + bool mem_write(int socket_client, char* data, size_t len); + + bool bp_insert(int socket_client, char* data, size_t len); + bool bp_remove(int socket_client, char* data, size_t len); + + Gdb_server *top; + int socket_port; + int socket_in; + std::thread *listener_thread; + + int thread_sel; + Target_core *main_core = NULL; + + + + bool wait_client(); + bool loop(); + + + + //void resumeCoresPrepare(DbgIF *dbgif, bool step); + void resumeCores(); + + //DbgIF* get_dbgif(int thread_id); + + + int m_thread_init; + //MemIF* m_mem; + //LogIF *log; + //BreakPoints* m_bp; + //std::list m_dbgifs; + //std::list m_dbg_cluster_ifs; +}; + +#endif \ No newline at end of file diff --git a/tools/pulp-debug-bridge/src/gdb-server/rsp.cpp b/tools/pulp-debug-bridge/src/gdb-server/rsp.cpp new file mode 100644 index 00000000..fdf0b645 --- /dev/null +++ b/tools/pulp-debug-bridge/src/gdb-server/rsp.cpp @@ -0,0 +1,1146 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Andreas Traber + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gdb-server.hpp" +#include + +enum mp_type { + BP_MEMORY = 0, + BP_HARDWARE = 1, + WP_WRITE = 2, + WP_READ = 3, + WP_ACCESS = 4 +}; + +enum target_signal { + TARGET_SIGNAL_NONE = 0, + TARGET_SIGNAL_INT = 2, + TARGET_SIGNAL_ILL = 4, + TARGET_SIGNAL_TRAP = 5, + TARGET_SIGNAL_FPE = 8, + TARGET_SIGNAL_BUS = 10, + TARGET_SIGNAL_SEGV = 11, + TARGET_SIGNAL_ALRM = 14, + TARGET_SIGNAL_STOP = 17, + TARGET_SIGNAL_USR2 = 31, + TARGET_SIGNAL_PWR = 32 +}; + +#define PACKET_MAX_LEN 4096 + + +Rsp::Rsp(Gdb_server *top, int socket_port) : top(top), socket_port(socket_port) +{ + main_core = top->target->get_threads().front(); + + m_thread_init = main_core->get_thread_id(); + thread_sel = m_thread_init; +} + +bool Rsp::v_packet(int socket_client, char* data, size_t len) +{ + if (strncmp ("vKill", data, strlen ("vKill")) == 0) + { + this->send_str(socket_client, "OK"); + return false; + } + else if (strncmp ("vCont?", data, strlen ("vCont?")) == 0) + { + return this->send_str(socket_client, "vCont;c;s;C;S"); + } + else if (strncmp ("vCont", data, strlen ("vCont")) == 0) + { + int nb_threads = top->target->get_nb_threads(); + bool thread_done[nb_threads]; + for (int i=0; ilog->print(LOG_ERROR, "Unsupported command in vCont packet: %s\n", str); + exit(-1); + } + + if (cont) { + if (tid == -1) { + for (int i=0; itop->target->get_thread_from_id(i)->prepare_resume(step); + } + } + } else { + if (!thread_done[tid]) + { + thread_done[tid] = true; + this->top->target->get_thread(tid)->prepare_resume(step); + } + } + } + + str = strtok(NULL, ";"); + } + + this->top->target->resume_all(); + + return this->wait(socket_client); + } + + return this->send_str(socket_client, ""); +} + +bool Rsp::query(int socket_client, char* data, size_t len) +{ + int ret; + char reply[256]; + + if (strncmp ("qSupported", data, strlen ("qSupported")) == 0) + { + return this->send_str(socket_client, "PacketSize=256"); + } + else if (strncmp ("qTStatus", data, strlen ("qTStatus")) == 0) + { + // not supported, send empty packet + return this->send_str(socket_client, ""); + } + else if (strncmp ("qfThreadInfo", data, strlen ("qfThreadInfo")) == 0) + { + reply[0] = 'm'; + ret = 1; + for (auto &thread : top->target->get_threads()) + { + ret += snprintf(&reply[ret], 256 - ret, "%u,", thread->get_thread_id()); + } + + return this->send(socket_client, reply, ret-1); + } + else if (strncmp ("qsThreadInfo", data, strlen ("qsThreadInfo")) == 0) + { + return this->send_str(socket_client, "l"); + } + else if (strncmp ("qThreadExtraInfo", data, strlen ("qThreadExtraInfo")) == 0) + { + const char* str_default = "Unknown Core"; + char str[256]; + unsigned int thread_id; + if (sscanf(data, "qThreadExtraInfo,%d", &thread_id) != 1) { + top->log->print(LOG_ERROR, "Could not parse qThreadExtraInfo packet\n"); + return this->send_str(socket_client, ""); + } + Target_core *thread = top->target->get_thread(thread_id); + { + if (thread != NULL) + thread->get_name(str, 256); + else + strcpy(str, str_default); + + ret = 0; + for(int i = 0; i < strlen(str); i++) + ret += snprintf(&reply[ret], 256 - ret, "%02X", str[i]); + } + + return this->send(socket_client, reply, ret); + } + else if (strncmp ("qAttached", data, strlen ("qAttached")) == 0) + { + return this->send_str(socket_client, "1"); + } + else if (strncmp ("qC", data, strlen ("qC")) == 0) + { + snprintf(reply, 64, "0.%u", this->top->target->get_thread(thread_sel)->get_thread_id()); + return this->send_str(socket_client, reply); + } + else if (strncmp ("qSymbol", data, strlen ("qSymbol")) == 0) + { + return this->send_str(socket_client, "OK"); + } + else if (strncmp ("qOffsets", data, strlen ("qOffsets")) == 0) + { + return this->send_str(socket_client, "Text=0;Data=0;Bss=0"); + } + else if (strncmp ("qT", data, strlen ("qT")) == 0) + { + // not supported, send empty packet + return this->send_str(socket_client, ""); + } + + top->log->print(LOG_ERROR, "Unknown query packet\n"); + + return false; +} + + + +// internal helper functions +bool Rsp::pc_read(int socket_client, unsigned int* pc) +{ + uint32_t npc; + uint32_t ppc; + uint32_t cause; + uint32_t hit; + Target_core *core; + + core = this->top->target->get_thread(thread_sel); + + core->read_ppc(&ppc); + core->read(DBG_NPC_REG, &npc); + + core->read(DBG_HIT_REG, &hit); + core->read(DBG_CAUSE_REG, &cause); + + if (hit & 0x1) + *pc = npc; + else if(cause & (1 << 31)) // interrupt + *pc = npc; + else if ((cause & 0x1f) == 3) // breakpoint + *pc = ppc; + else if ((cause & 0x1f) == 2) + *pc = ppc; + else if ((cause & 0x1f) == 5) + *pc = ppc; + else + *pc = npc; + + return true; +} + + + + +bool Rsp::mem_read(int socket_client, char* data, size_t len) +{ + unsigned char buffer[512]; + char reply[512]; + uint32_t addr; + uint32_t length; + uint32_t rdata; + int i; + + if (sscanf(data, "%x,%x", &addr, &length) != 2) { + top->log->print(LOG_ERROR, "Could not parse packet\n"); + return false; + } + + top->cable->access(false, addr, length, (char *)buffer); + + for(i = 0; i < length; i++) { + rdata = buffer[i]; + snprintf(&reply[i * 2], 3, "%02x", rdata); + } + + return this->send(socket_client, reply, length*2); +} + + + +bool Rsp::mem_write_ascii(int socket_client, char* data, size_t len) +{ + uint32_t addr; + int length; + uint32_t wdata; + int i, j; + + char* buffer; + int buffer_len; + + if (sscanf(data, "%x,%d:", &addr, &length) != 2) { + top->log->print(LOG_ERROR, "Could not parse packet\n"); + return false; + } + + for(i = 0; i < len; i++) { + if (data[i] == ':') { + break; + } + } + + if (i == len) + return false; + + // align to hex data + data = &data[i+1]; + len = len - i - 1; + + buffer_len = len/2; + buffer = (char*)malloc(buffer_len); + if (buffer == NULL) { + top->log->print(LOG_ERROR, "Failed to allocate buffer\n"); + return false; + } + + for(j = 0; j < len/2; j++) { + wdata = 0; + for(i = 0; i < 2; i++) { + char c = data[j * 2 + i]; + uint32_t hex = 0; + if (c >= '0' && c <= '9') + hex = c - '0'; + else if (c >= 'a' && c <= 'f') + hex = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + hex = c - 'A' + 10; + + wdata |= hex << (4 * i); + } + + buffer[j] = wdata; + } + + top->cable->access(true, addr, buffer_len, buffer); + + free(buffer); + + return this->send_str(socket_client, "OK"); +} + +bool Rsp::mem_write(int socket_client, char* data, size_t len) +{ + uint32_t addr; + int length; + uint32_t wdata; + int i, j; + + char* buffer; + int buffer_len; + + if (sscanf(data, "%x,%x:", &addr, &length) != 2) { + top->log->print(LOG_ERROR, "Could not parse packet\n"); + return false; + } + + for(i = 0; i < len; i++) { + if (data[i] == ':') { + break; + } + } + + if (i == len) + return false; + + // align to hex data + data = &data[i+1]; + len = len - i - 1; + + top->cable->access(write, addr, len, data); + + return this->send_str(socket_client, "OK"); +} + + + +bool Rsp::reg_read(int socket_client, char* data, size_t len) +{ + uint32_t addr; + uint32_t rdata; + char data_str[10]; + + if (sscanf(data, "%x", &addr) != 1) { + top->log->print(LOG_ERROR, "Could not parse packet\n"); + return false; + } + + if (addr < 32) + this->top->target->get_thread(thread_sel)->gpr_read(addr, &rdata); + else if (addr == 0x20) + this->pc_read(socket_client, &rdata); + else + return this->send_str(socket_client, ""); + + rdata = htonl(rdata); + snprintf(data_str, 9, "%08x", rdata); + + return this->send_str(socket_client, data_str); +} + + + +bool Rsp::reg_write(int socket_client, char* data, size_t len) +{ + uint32_t addr; + uint32_t wdata; + char data_str[10]; + Target_core *core; + + if (sscanf(data, "%x=%08x", &addr, &wdata) != 2) { + top->log->print(LOG_ERROR, "Could not parse packet\n"); + return false; + } + + wdata = ntohl(wdata); + + core = this->top->target->get_thread(thread_sel); + if (addr < 32) + core->gpr_write(addr, wdata); + else if (addr == 32) + core->write(DBG_NPC_REG, wdata); + else + return this->send_str(socket_client, "E01"); + + return this->send_str(socket_client, "OK"); +} + + + +bool Rsp::regs_send(int socket_client) +{ + uint32_t gpr[32]; + uint32_t npc; + uint32_t ppc; + char regs_str[512]; + int i; + + this->top->target->get_thread(thread_sel)->gpr_read_all(gpr); + + // now build the string to send back + for(i = 0; i < 32; i++) { + snprintf(®s_str[i * 8], 9, "%08x", htonl(gpr[i])); + } + + this->pc_read(socket_client, &npc); + snprintf(®s_str[32 * 8 + 0 * 8], 9, "%08x", htonl(npc)); + + return this->send_str(socket_client, regs_str); +} + + + +bool Rsp::signal(int socket_client) +{ + uint32_t cause; + uint32_t hit; + int signal; + char str[4]; + int len; + Target_core *core; + + core = this->top->target->get_thread(thread_sel); + + //dbgif->write(DBG_IE_REG, 0xFFFF); + + // figure out why we are stopped + if (core->is_stopped()) { + if (!core->read(DBG_HIT_REG, &hit)) + return false; + if (!core->read(DBG_CAUSE_REG, &cause)) + return false; + + if (hit & 0x1) + signal = TARGET_SIGNAL_TRAP; + else if(cause & (1 << 31)) + signal = TARGET_SIGNAL_INT; + else if ((cause & 0x1f) == 3) + signal = TARGET_SIGNAL_TRAP; + else if ((cause & 0x1f) == 2) + signal = TARGET_SIGNAL_ILL; + else if ((cause & 0x1f) == 5) + signal = TARGET_SIGNAL_BUS; + else + signal = TARGET_SIGNAL_STOP; + } else { + signal = TARGET_SIGNAL_NONE; + } + + len = snprintf(str, 4, "S%02x", signal); + + return this->send(socket_client, str, len); +} + + +bool Rsp::cont(int socket_client, char* data, size_t len) +{ + uint32_t sig; + uint32_t addr; + uint32_t npc; + int i; + bool npc_found = false; + Target_core *core; + + // strip signal first + if (data[0] == 'C') { + if (sscanf(data, "C%X;%X", &sig, &addr) == 2) + npc_found = true; + } else { + if (sscanf(data, "c%X", &addr) == 1) + npc_found = true; + } + + if (npc_found) { + core = this->top->target->get_thread(thread_sel); + // only when we have received an address + core->read(DBG_NPC_REG, &npc); + + if (npc != addr) + core->write(DBG_NPC_REG, addr); + } + + thread_sel = m_thread_init; + + return this->resume(socket_client, false); +} + + + +bool Rsp::resume(int socket_client, bool step) +{ + this->top->target->resume(step); + return this->wait(socket_client); +} + + + +bool Rsp::resume(int socket_client, int tid, bool step) +{ + this->top->target->resume(step, tid); + return this->wait(socket_client); +} + + + +bool Rsp::step(int socket_client, char* data, size_t len) +{ + uint32_t addr; + uint32_t npc; + int i; + Target_core *core; + + // strip signal first + if (data[0] == 'S') { + for (i = 0; i < len; i++) { + if (data[i] == ';') { + data = &data[i+1]; + break; + } + } + } + + if (sscanf(data, "%x", &addr) == 1) { + core = this->top->target->get_thread(thread_sel); + // only when we have received an address + core->read(DBG_NPC_REG, &npc); + + if (npc != addr) + core->write(DBG_NPC_REG, addr); + } + + thread_sel = m_thread_init; + + return this->resume(socket_client, true); +} + + + +bool Rsp::wait(int socket_client, Target_core *core) +{ + int ret; + char pkt; + + fd_set rfds; + struct timeval tv; + + while(1) { + + // Check if a cluster power state has changed + this->top->target->update_power(); + + //First check if one core has stopped + if (core) { + if (core->is_stopped()) { + this->top->target->halt(); + return this->signal(socket_client); + } + } else { + for (auto &core: this->top->target->get_threads()) { + if (core->is_stopped()) { + this->top->target->halt(); + return this->signal(socket_client); + } + } + } + + // Otherwise wait for a stop request from gdb side for a while + + FD_ZERO(&rfds); + FD_SET(socket_client, &rfds); + + tv.tv_sec = 0; + tv.tv_usec = 100 * 1000; + + if (select(socket_client+1, &rfds, NULL, NULL, &tv)) { + ret = recv(socket_client, &pkt, 1, 0); + if (ret == 1 && pkt == 0x3) { + if (core) { + core->halt(); + return this->signal(socket_client); + } else { + top->target->halt(); + } + } + } + } + + return true; +} + + + +bool Rsp::multithread(int socket_client, char* data, size_t len) +{ + int thread_id; + + switch (data[0]) { + case 'c': + case 'g': + if (sscanf(&data[1], "%d", &thread_id) != 1) + return false; + + if (thread_id == -1) // affects all threads + return this->send_str(socket_client, "OK"); + + // we got the thread id, now let's look for this thread in our list + if (this->top->target->get_thread(thread_id) != NULL) { + thread_sel = thread_id; + return this->send_str(socket_client, "OK"); + } + + return this->send_str(socket_client, "E01"); + } + + return false; +} + + + +bool Rsp::decode(int socket_client, char* data, size_t len) +{ + if (data[0] == 0x03) { + top->log->print(LOG_DEBUG, "Received break\n"); + return this->signal(socket_client); + } + + switch (data[0]) { + case 'q': + return this->query(socket_client, &data[0], len); + + case 'g': + return this->regs_send(socket_client); + + case 'p': + return this->reg_read(socket_client, &data[1], len-1); + + case 'P': + return this->reg_write(socket_client, &data[1], len-1); + + case 'c': + case 'C': + return this->cont(socket_client, &data[0], len); + + case 's': + case 'S': + return this->step(socket_client, &data[0], len); + + case 'H': + return this->multithread(socket_client, &data[1], len-1); + + case 'm': + return this->mem_read(socket_client, &data[1], len-1); + + case '?': + return this->signal(socket_client); + + case 'v': + return this->v_packet(socket_client, &data[0], len); + + case 'M': + return this->mem_write_ascii(socket_client, &data[1], len-1); + + case 'X': + return this->mem_write(socket_client, &data[1], len-1); + + case 'z': + return this->bp_remove(socket_client, &data[0], len); + + case 'Z': + return this->bp_insert(socket_client, &data[0], len); + + case 'T': + return this->send_str(socket_client, "OK"); // threads are always alive + + case 'D': + this->send_str(socket_client, "OK"); + return false; + + default: + top->log->print(LOG_ERROR, "Unknown packet: starts with %c\n", data[0]); + break; + } + + return false; +} + + + +bool +Rsp::get_packet(int socket_client, char* pkt, size_t* p_pkt_len) { + char c; + char check_chars[2]; + char buffer[PACKET_MAX_LEN]; + int buffer_len = 0; + int pkt_len; + bool escaped = false; + int ret; + // packets follow the format: $packet-data#checksum + // checksum is two-digit + + // poison packet + memset(pkt, 0, PACKET_MAX_LEN); + pkt_len = 0; + + // first look for start bit + do { + ret = recv(socket_client, &c, 1, 0); + + if((ret == -1 && errno != EWOULDBLOCK) || (ret == 0)) { + top->log->print(LOG_ERROR, "RSP: Error receiving\n"); + return false; + } + + if(ret == -1 && errno == EWOULDBLOCK) { + // no data available + continue; + } + + // special case for 0x03 (asynchronous break) + if (c == 0x03) { + pkt[0] = c; + *p_pkt_len = 1; + return true; + } + } while(c != '$'); + + buffer[0] = c; + + // now store data as long as we don't see # + do { + if (buffer_len >= PACKET_MAX_LEN || pkt_len >= PACKET_MAX_LEN) { + top->log->print(LOG_ERROR, "RSP: Too many characters received\n"); + return false; + } + + ret = recv(socket_client, &c, 1, 0); + + if((ret == -1 && errno != EWOULDBLOCK) || (ret == 0)) { + top->log->print(LOG_ERROR, "RSP: Error receiving\n"); + return false; + } + + if(ret == -1 && errno == EWOULDBLOCK) { + // no data available + continue; + } + + buffer[buffer_len++] = c; + + // check for 0x7d = '}' + if (c == 0x7d) { + escaped = true; + continue; + } + + if (escaped) + pkt[pkt_len++] = c ^ 0x20; + else + pkt[pkt_len++] = c; + + escaped = false; + } while(c != '#'); + + buffer_len--; + pkt_len--; + + // checksum, 2 bytes + ret = recv(socket_client, &check_chars[0], 1, 0); + if((ret == -1 && errno != EWOULDBLOCK) || (ret == 0)) { + top->log->print(LOG_ERROR, "RSP: Error receiving\n"); + return false; + } + + ret = recv(socket_client, &check_chars[1], 1, 0); + if((ret == -1 && errno != EWOULDBLOCK) || (ret == 0)) { + top->log->print(LOG_ERROR, "RSP: Error receiving\n"); + return false; + } + + // check the checksum + unsigned int checksum = 0; + for(int i = 0; i < buffer_len; i++) { + checksum += buffer[i]; + } + + checksum = checksum % 256; + char checksum_str[3]; + snprintf(checksum_str, 3, "%02x", checksum); + + if (check_chars[0] != checksum_str[0] || check_chars[1] != checksum_str[1]) { + top->log->print(LOG_ERROR, "RSP: Checksum failed; received %.*s; checksum should be %02x\n", pkt_len, pkt, checksum); + return false; + } + + // now send ACK + char ack = '+'; + if (::send(socket_client, &ack, 1, 0) != 1) { + top->log->print(LOG_ERROR, "RSP: Sending ACK failed\n"); + return false; + } + + // NULL terminate the string + pkt[pkt_len] = '\0'; + *p_pkt_len = pkt_len; + + return true; +} + +bool Rsp::send(int socket_client, const char* data, size_t len) +{ + int ret; + int i; + size_t raw_len = 0; + char* raw = (char*)malloc(len * 2 + 4); + unsigned int checksum = 0; + + raw[raw_len++] = '$'; + + for (i = 0; i < len; i++) { + char c = data[i]; + + // check if escaping needed + if (c == '#' || c == '%' || c == '}' || c == '*') { + raw[raw_len++] = '}'; + raw[raw_len++] = c; + checksum += '}'; + checksum += c; + } else { + raw[raw_len++] = c; + checksum += c; + } + } + + // add checksum + checksum = checksum % 256; + char checksum_str[3]; + snprintf(checksum_str, 3, "%02x", checksum); + + raw[raw_len++] = '#'; + raw[raw_len++] = checksum_str[0]; + raw[raw_len++] = checksum_str[1]; + + char ack; + do { + top->log->print(LOG_DEBUG, "Sending %.*s\n", raw_len, raw); + + if (::send(socket_client, raw, raw_len, 0) != raw_len) { + free(raw); + top->log->print(LOG_ERROR, "Unable to send data to client\n"); + return false; + } + + ret = recv(socket_client, &ack, 1, 0); + if((ret == -1 && errno != EWOULDBLOCK) || (ret == 0)) { + free(raw); + top->log->print(LOG_ERROR, "RSP: Error receiving\n"); + return false; + } + + if(ret == -1 && errno == EWOULDBLOCK) { + // no data available + continue; + } + + } while (ack != '+'); + + free(raw); + return true; +} + +bool Rsp::send_str(int socket_client, const char* data) +{ + return this->send(socket_client, data, strlen(data)); +} + +void Rsp::client_routine(int socket_client) +{ + while(1) + { + char pkt[PACKET_MAX_LEN]; + size_t len; + + fd_set rfds; + struct timeval tv; + + while (this->get_packet(socket_client, pkt, &len)) { + top->log->print(LOG_DEBUG, "Received $%.*s\n", len, pkt); + if (!this->decode(socket_client, pkt, len)) { + return; + } + } + } +} + +void Rsp::listener_routine() +{ + while(1) + { + int socket_client; + + if((socket_client = accept(socket_in, NULL, NULL)) == -1) + { + if(errno == EAGAIN) + continue; + + top->log->print(LOG_ERROR, "Unable to accept connection: %s\n", strerror(errno)); + return; + } + + top->log->print(LOG_INFO, "RSP: Client connected!\n"); + + std::thread *thread = new std::thread(&Rsp::client_routine, this, socket_client); + + } +} + +void Rsp::close(int kill) +{ + listener_thread->join(); +} + +bool +Rsp::open() { + struct sockaddr_in addr; + int yes = 1; + + addr.sin_family = AF_INET; + addr.sin_port = htons(socket_port); + addr.sin_addr.s_addr = INADDR_ANY; + memset(addr.sin_zero, '\0', sizeof(addr.sin_zero)); + + socket_in = socket(PF_INET, SOCK_STREAM, 0); + if(socket_in < 0) + { + top->log->print(LOG_ERROR, "Unable to create comm socket: %s\n", strerror(errno)); + return false; + } + + if(setsockopt(socket_in, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + top->log->print(LOG_ERROR, "Unable to setsockopt on the socket: %s\n", strerror(errno)); + return false; + } + + if(bind(socket_in, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + top->log->print(LOG_ERROR, "Unable to bind the socket: %s\n", strerror(errno)); + return false; + } + + if(listen(socket_in, 1) == -1) { + top->log->print(LOG_ERROR, "Unable to listen: %s\n", strerror(errno)); + return false; + } + + this->top->target->halt(); + + listener_thread = new std::thread(&Rsp::listener_routine, this); + + top->log->print(LOG_INFO, "RSP server opened on port %d\n", socket_port); + + return true; +} + + + +bool Rsp::bp_insert(int socket_client, char* data, size_t len) +{ + enum mp_type type; + uint32_t addr; + uint32_t data_bp; + int bp_len; + + if (3 != sscanf(data, "Z%1d,%x,%1d", (int *)&type, &addr, &bp_len)) { + top->log->print(LOG_ERROR, "Could not get three arguments\n"); + return false; + } + + if (type != BP_MEMORY) { + top->log->print(LOG_ERROR, "ERROR: Not a memory bp\n"); + this->send_str(socket_client, ""); + return false; + } + + top->bkp->insert(addr); + + return this->send_str(socket_client, "OK"); +} + + + +bool Rsp::bp_remove(int socket_client, char* data, size_t len) +{ + enum mp_type type; + uint32_t addr; + uint32_t ppc; + int bp_len; + Target_core *core; + + core = this->top->target->get_thread(thread_sel); + + if (3 != sscanf(data, "z%1d,%x,%1d", (int *)&type, &addr, &bp_len)) { + top->log->print(LOG_ERROR, "Could not get three arguments\n"); + return false; + } + + if (type != BP_MEMORY) { + top->log->print(LOG_ERROR, "Not a memory bp\n"); + return false; + } + + top->bkp->remove(addr); + + // check if we are currently on this bp that is removed + core->read_ppc(&ppc); + + if (addr == ppc) { + core->write(DBG_NPC_REG, ppc); // re-execute this instruction + } + + return this->send_str(socket_client, "OK"); +} + + + +#if 0 + +Rsp::Rsp(int socket_port, MemIF* mem, LogIF *log, std::list list_dbgif, std::list list_dbg_cluster_ifs, BreakPoints* bp, DbgIF *main_if) { + socket_port = socket_port; + m_mem = mem; + m_dbgifs = list_dbgif; + m_dbg_cluster_ifs = list_dbg_cluster_ifs; + m_bp = bp; + this->log = log; + + // select one dbg if at random + if (m_dbgifs.size() == 0) { + top->log->print(LOG_ERROR, "No debug interface available! Exiting now\n"); + exit(1); + } + + if (main_if == NULL) main_if = m_dbgifs.front(); + + m_thread_init = main_if->get_thread_id(); + thread_sel = m_thread_init; +} + +void +Rsp::close() { + m_bp->clear(); + ::close(socket_in); +} + +void +Rsp::resumeCoresPrepare(DbgIF *dbgif, bool step) +{ + top->log->print(LOG_DEBUG, "Preparing core to resume (step: %d)\n", step); + + // now let's handle software breakpoints + uint32_t ppc; + dbgif->read_ppc(&ppc); + + // if there is a breakpoint at this address, let's remove it and single-step over it + bool hasStepped = false; + + if (m_bp->at_addr(ppc)) { + + top->log->print(LOG_DEBUG, "Core is stopped on a breakpoint, stepping to go over (addr: 0x%x)\n", ppc); + + m_bp->disable(ppc); + dbgif->write(DBG_NPC_REG, ppc); // re-execute this instruction + dbgif->write(DBG_CTRL_REG, 0x1); // single-step + while (1) { + uint32_t value; + dbgif->read(DBG_CTRL_REG, &value); + if ((value >> 16) & 1) break; + } + m_bp->enable(ppc); + hasStepped = true; + } + + dbgif->set_step_mode(step && !hasStepped); +} + +void +Rsp::resumeCores() { + for (std::list::iterator it = m_dbg_cluster_ifs.begin(); it != m_dbg_cluster_ifs.end(); it++) { + DbgIF_cluster *cluster = (DbgIF_cluster *)*it; + cluster->resume(); + } +} + +DbgIF* +Rsp::get_dbgif(int thread_id) { + for (std::list::iterator it = m_dbgifs.begin(); it != m_dbgifs.end(); it++) { + if ((*it)->get_thread_id() == thread_id) + return *it; + } + + return NULL; +} + +#endif \ No newline at end of file diff --git a/tools/pulp-debug-bridge/src/gdb-server/target.cpp b/tools/pulp-debug-bridge/src/gdb-server/target.cpp new file mode 100644 index 00000000..936f4ea7 --- /dev/null +++ b/tools/pulp-debug-bridge/src/gdb-server/target.cpp @@ -0,0 +1,741 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ + +#include "gdb-server/gdb-server.hpp" +#include + + +class Target_cache +{ +public: + virtual void flush() { } +}; + +class Target_cluster_cache : public Target_cache +{ +public: + Target_cluster_cache(Gdb_server *top, uint32_t addr); + void flush(); +private: + Gdb_server *top; + uint32_t addr; +}; + + + +class Target_fc_cache : public Target_cache +{ +public: + Target_fc_cache(Gdb_server *top, uint32_t addr); + void flush(); +private: + Gdb_server *top; + uint32_t addr; +}; + + + + + +class Target_cluster_power +{ +public: + virtual bool is_on() { return true; } +}; + + + +class Target_cluster_power_bypass : public Target_cluster_power +{ +public: + Target_cluster_power_bypass(Gdb_server *top, uint32_t reg_addr, int bit); + bool is_on(); + +private: + Gdb_server *top; + uint32_t reg_addr; + int bit; +}; + + + + +class Target_cluster_ctrl +{ +public: + virtual bool init() {} +}; + + + +class Target_cluster_ctrl_xtrigger : public Target_cluster_ctrl +{ +public: + Target_cluster_ctrl_xtrigger(Gdb_server *top, uint32_t cluster_ctrl_addr); + bool init(); + +private: + Gdb_server *top; + uint32_t cluster_ctrl_addr; +}; + + +Target_cluster_cache::Target_cluster_cache(Gdb_server *top, uint32_t addr) +: top(top), addr(addr) +{ + +} + +void Target_cluster_cache::flush() +{ + this->top->log->debug("Flushing cluster cache (addr: 0x%x)\n", addr); + + uint32_t data = 0xFFFFFFFF; + top->cable->access(true, addr + 0x04, 4, (char*)&data); +} + + + +Target_fc_cache::Target_fc_cache(Gdb_server *top, uint32_t addr) +: top(top), addr(addr) +{ + +} + +void Target_fc_cache::flush() +{ + this->top->log->debug("Flushing FC cache (addr: 0x%x)\n", addr); + + uint32_t data = 0xFFFFFFFF; + top->cable->access(true, addr + 0x04, 4, (char*)&data); +} + + +Target_cluster_ctrl_xtrigger::Target_cluster_ctrl_xtrigger(Gdb_server *top, uint32_t cluster_ctrl_addr) +: top(top), cluster_ctrl_addr(cluster_ctrl_addr) +{ + +} + + + +bool Target_cluster_ctrl_xtrigger::init() +{ + uint32_t info; + // set all-stop mode, so that all cores go to debug when one enters debug mode + info = 0xFFFFFFFF; + top->cable->access(true, cluster_ctrl_addr + 0x000038, 4, (char*)&info); +} + + + +Target_cluster_power_bypass::Target_cluster_power_bypass(Gdb_server *top, uint32_t reg_addr, int bit) +: top(top), reg_addr(reg_addr), bit(bit) +{ +} + +bool Target_cluster_power_bypass::is_on() +{ + uint32_t info; + top->cable->access(false, reg_addr, 4, (char*)&info); + return (info >> bit) & 1; +} + + +class Target_cluster_common +{ + friend class Target_cluster; + friend class Target_fc; + +public: + Target_cluster_common(js::config *config, Gdb_server *top, uint32_t cluster_addr, uint32_t xtrigger_addr, int cluster_id); + int get_nb_core() { return nb_core; } + Target_core *get_core(int i) { return cores[i]; } + void update_power(); + void set_power(bool is_on); + void resume(); + void halt(); + void flush(); + +protected: + Gdb_server *top; + std::vector cores; + bool is_on = false; + Target_cluster_power *power; + Target_cluster_ctrl *ctrl; + int nb_on_cores = 0; + int nb_core = 0; + int cluster_id; + uint32_t cluster_addr; + uint32_t xtrigger_addr; + Target_cache *cache = NULL; +}; + +class Target_cluster : public Target_cluster_common +{ +public: + Target_cluster(js::config *system_config, js::config *config, Gdb_server *top, uint32_t cluster_base, uint32_t xtrigger_base, int cluster_id); +}; + +class Target_fc : public Target_cluster_common +{ +public: + Target_fc(js::config *config, Gdb_server *top, uint32_t fc_dbg_base, uint32_t fc_cache_base, int cluster_id); +}; + + + +Target_core::Target_core(Gdb_server *top, uint32_t dbg_unit_addr, int cluster_id, int core_id) +: top(top), dbg_unit_addr(dbg_unit_addr), cluster_id(cluster_id), core_id(core_id) +{ + top->log->print(LOG_DEBUG, "Instantiated core\n"); + this->thread_id = first_free_thread_id++; +} + + + +void Target_core::flush() +{ + this->top->log->debug("Flushing core cache (cluster: %d, core: %d)\n", cluster_id, core_id); + + // Write back the value of NPC so that it triggers a flush of the prefetch buffer + uint32_t npc; + this->read(DBG_NPC_REG, &npc); + this->write(DBG_NPC_REG, npc); +} + + + +void Target_core::read_ppc(uint32_t *ppc) +{ + if (!ppc_is_cached) { + read(DBG_PPC_REG, &ppc_cached); + ppc_is_cached = true; + } + *ppc = ppc_cached; +} + + + +bool Target_core::gpr_read_all(uint32_t *data) +{ + if (!is_on) return false; + this->top->log->debug("Reading all registers (cluster: %d, core: %d)\n", cluster_id, core_id); + + // Write back the valu + return top->cable->access(false, dbg_unit_addr + 0x0400, 32 * 4, (char*)data); +} + + + +bool Target_core::gpr_read(unsigned int i, uint32_t *data) +{ + if (!is_on) return false; + return this->read(0x0400 + i * 4, data); +} + + + +bool Target_core::gpr_write(unsigned int i, uint32_t data) +{ + if (!is_on) return false; + return this->write(0x0400 + i * 4, data); +} + + + +void Target_core::commit_resume() +{ + this->stopped = false; + + if (!this->is_on) return; + + this->ppc_is_cached = false; + this->commit_step_mode(); + this->write(DBG_HIT_REG, 0); +} + + + +void Target_core::set_power(bool is_on) +{ + if (is_on != this->is_on) + { + this->is_on = is_on; + if (is_on) { + top->log->print(LOG_DEBUG, "Setting-on core\n"); + is_on = true; + // let's discover core id and cluster id + this->stop(); + csr_read(0xF14, &hartid); + + cluster_id = hartid >> 5; + core_id = hartid & 0x1f; + + top->log->print(LOG_DEBUG, "Found a core with id %X (cluster: %d, core: %d)\n", hartid, cluster_id, core_id); + this->write(DBG_IE_REG, 1<<3); + if (!stopped) resume(); + } else { + top->log->print(LOG_DEBUG, "Setting-off core (cluster: %d, core: %d)\n", cluster_id, core_id); + is_on = false; + } + } +} + +bool Target_core::read(uint32_t addr, uint32_t* rdata) +{ + if (!is_on) return false; + top->log->print(LOG_DEBUG, "Reading register (addr: 0x%x)\n", dbg_unit_addr + addr); + return top->cable->access(false, dbg_unit_addr + addr, 4, (char*)rdata); +} + + + +bool Target_core::write(uint32_t addr, uint32_t wdata) +{ + if (!is_on) return false; + top->log->print(LOG_DEBUG, "Writing register (addr: 0x%x, value: 0x%x)\n", dbg_unit_addr + addr, wdata); + return top->cable->access(true, dbg_unit_addr + addr, 4, (char*)&wdata); +} + + + +bool Target_core::csr_read(unsigned int i, uint32_t *data) +{ + if (!is_on) return false; + return this->read(0x4000 + i * 4, data); +} + + + +bool Target_core::is_stopped() { + if (!is_on) return false; + + uint32_t data; + if (!this->read(DBG_CTRL_REG, &data)) { + fprintf(stderr, "debug_is_stopped: Reading from CTRL reg failed\n"); + return false; + } + + this->stopped = data & 0x10000; + + top->log->debug("Checking core status (cluster: %d, core: %d, stopped: %d)\n", cluster_id, core_id, this->stopped); + + return this->stopped; +} + + +bool Target_core::stop() +{ + if (!is_on) return false; + if (this->stopped) return false; + + this->top->log->debug("Halting core (cluster: %d, core: %d, is_on: %d)\n", cluster_id, core_id, is_on); + uint32_t data; + if (!this->read(DBG_CTRL_REG, &data)) { + fprintf(stderr, "debug_is_stopped: Reading from CTRL reg failed\n"); + return false; + } + + data |= 0x1 << 16; + return this->write(DBG_CTRL_REG, data); +} + + + +bool Target_core::halt() +{ + //stopped = true; + return stop(); +} + + + +void Target_core::set_step_mode(bool new_step) +{ + this->top->log->debug("Setting step mode (cluster: %d, core: %d, step: %d, new_step: %d)\n", cluster_id, core_id, step, new_step); + + if (new_step != step) { + this->step = new_step; + this->commit_step = true; + } +} + + +void Target_core::commit_step_mode() +{ + if (!is_on) return; + + if (commit_step) { + this->top->log->debug("Committing step mode (cluster: %d, core: %d, step: %d)\n", cluster_id, core_id, step); + this->write(DBG_CTRL_REG, (1<<16) | step); + this->commit_step = false; + } +} + + + +void Target_core::prepare_resume(bool step) +{ + + if (!is_on) return; + + top->log->debug("Preparing core to resume (step: %d)\n", step); + + // now let's handle software breakpoints + uint32_t ppc; + this->read_ppc(&ppc); + + // if there is a breakpoint at this address, let's remove it and single-step over it + bool has_stepped = false; + + if (this->top->bkp->at_addr(ppc)) { + + top->log->debug("Core is stopped on a breakpoint, stepping to go over (addr: 0x%x)\n", ppc); + + this->top->bkp->disable(ppc); + this->write(DBG_NPC_REG, ppc); // re-execute this instruction + this->write(DBG_CTRL_REG, 0x1); // single-step + while (1) { + uint32_t value; + this->read(DBG_CTRL_REG, &value); + if ((value >> 16) & 1) break; + } + this->top->bkp->enable(ppc); + has_stepped = true; + } + + this->set_step_mode(step && !has_stepped); +} + + + +void Target_core::resume() +{ + this->stopped = false; + + if (!is_on) return; + + this->top->log->debug("Resuming core and committing step mode (cluster: %d, core: %d, step: %d)\n", cluster_id, core_id, step); + + // clear hit register, has to be done before CTRL + this->write(DBG_HIT_REG, 0); + + this->write(DBG_CTRL_REG, step); + + this->commit_step = false; + this->ppc_is_cached = false; +} + + + +Target_cluster_common::Target_cluster_common(js::config *config, Gdb_server *top, uint32_t cluster_addr, uint32_t xtrigger_addr, int cluster_id) +: top(top), cluster_id(cluster_id), cluster_addr(cluster_addr), xtrigger_addr(xtrigger_addr) +{ +} + + + +Target_cluster::Target_cluster(js::config *system_config, js::config *config, Gdb_server *top, uint32_t cluster_base, uint32_t xtrigger_addr, int cluster_id) +: Target_cluster_common(config, top, cluster_base, xtrigger_addr, cluster_id) +{ + int nb_pe = config->get("nb_pe")->get_int(); + for (int i=0; iget("**/apb_soc_ctrl/regmap/power/bypass"); + if (bypass_config) + { + uint32_t addr = system_config->get("**/apb_soc_ctrl/base")->get_int() + + bypass_config->get("offset")->get_int(); + int bit = bypass_config->get("content/dbg1/bit")->get_int(); + + // Case where there is an apb soc ctrl register which tells if the cluster is on + power = new Target_cluster_power_bypass(top, addr, bit); + } + else + { + // Otherwise, the cluster will always be on + power = new Target_cluster_power(); + } + + ctrl = new Target_cluster_ctrl_xtrigger(top, cluster_base + 0x00200000); + + cache = new Target_cluster_cache(top, cluster_base + 0x00201400); + + this->update_power(); +} + + + +Target_fc::Target_fc(js::config *config, Gdb_server *top, uint32_t fc_dbg_base, uint32_t fc_cache_base, int cluster_id) +: Target_cluster_common(config, top, fc_dbg_base, -1, cluster_id) +{ + Target_core *core = new Target_core(top, fc_dbg_base, cluster_id, 0); + cores.push_back(core); + nb_core++; + + // the FC will always be on + power = new Target_cluster_power(); + + ctrl = new Target_cluster_ctrl(); + + if (fc_cache_base != -1) + cache = new Target_fc_cache(top, fc_cache_base); + + this->update_power(); +} + + + +void Target_cluster_common::flush() +{ + if (!is_on) return; + + this->top->log->debug("Flushing cluster caches (cluster: %d)\n", cluster_id); + + if (this->cache) + this->cache->flush(); + + for (auto &core: cores) + { + core->flush(); + } +} + + + +void Target_cluster_common::resume() +{ + this->top->log->debug("Resuming cluster (cluster: %d)\n", cluster_id); + + if (xtrigger_addr != -1) { + + // This cluster is a one with the cross-trigger matrix, use it to resume all cores + // As the step mode is cached and committed when writing to the individual core + // debug register, we have to commit it now before resuming the core through the + // global register + for (auto &core: cores) { + core->commit_resume(); + } + + if (is_on) { + this->top->log->debug("Resuming cluster through global register (cluster: %d)\n", cluster_id); + uint32_t info = 0xFFFFFFFF; + this->top->cable->access(true, xtrigger_addr + 0x00200000 + 0x28, 4, (char*)&info); + } + } else { + // Otherwise, just resume them individually + for (auto &core: cores) { + core->resume(); + } + } +} + + + +void Target_cluster_common::update_power() +{ + set_power(power->is_on()); +} + + +void Target_cluster_common::set_power(bool is_on) +{ + this->top->log->debug("Set cluster power (cluster: %d, is_on: %d)\n", cluster_id, is_on); + + if (is_on != this->is_on) { + this->is_on = is_on; + + ctrl->init(); + } + + if (is_on && nb_on_cores != nb_core) + { + for(auto const& core: cores) + { + core->set_power(is_on); + } + } else { + nb_on_cores = 0; + } +} + + + +void Target_cluster_common::halt() +{ + this->top->log->debug("Halting cluster (cluster: %d)\n", cluster_id); + // Either the core is alone (FC) or the cluster is using a cross-trigger matrix to stop all cores + // thus only stop the first one + cores.front()->halt(); +} + + +#if 0 +void Target_cluster::set_power(bool is_on) +{ + if (is_on != this->is_on) { + this->is_on = is_on; + + if (is_on && base_addr != -1) { + uint32_t info; + // set all-stop mode, so that all cores go to debug when one enters debug mode + info = 0xFFFFFFFF; + m_mem->access(1, base_addr + 0x000038, 4, (char*)&info); + } + } + + if (is_on && nb_on_cores != itfs.size()) { + uint32_t info = -1; + if (base_addr != -1) { + m_mem->access(0, base_addr + 0x000008, 4, (char*)&info); + } + int i = 0; + for (std::list::iterator it = itfs.begin(); it != itfs.end(); it++, i++) { + if ((info >> i) & 1) { + (*it)->set_power(is_on); + } + } + } else { + nb_on_cores = 0; + } +} +#endif + + + +Target::Target(Gdb_server *top) +: top(top) +{ + js::config *config = top->config; + + js::config *fc_config = config->get("**/soc/fc"); + if (fc_config != NULL) + { + unsigned int fc_dbg_addr = config->get("**/fc_dbg_unit/base")->get_int(); + js::config *cache_config = config->get("**/fc_icache/base"); + unsigned int fc_icache_addr = -1; + if (cache_config) + fc_icache_addr = cache_config->get_int(); + + Target_fc *cluster = new Target_fc(fc_config, top, fc_dbg_addr, fc_icache_addr, fc_config->get("cluster_id")->get_int()); + + clusters.push_back(cluster); + Target_core *core = cluster->get_core(0); + cores.push_back(core); + cores_from_threadid[core->get_thread_id()] = core; + } + + js::config *cluster_config = config->get("**/soc/cluster"); + if (cluster_config != NULL) + { + int nb_clusters = config->get("**/nb_cluster")->get_int(); + for (int i=0; iget("**/cluster/base"); + if (base_config != NULL) + cluster_base = base_config->get_int(); + + Target_cluster *cluster = new Target_cluster(config, cluster_config, top, cluster_base + 0x400000 * i, cluster_base + 0x400000 * i, i); + + clusters.push_back(cluster); + for (int j=0; jget_nb_core(); j++) + { + Target_core *core = cluster->get_core(j); + cores.push_back(core); + cores_from_threadid[core->get_thread_id()] = core; + } + } + } +} + + + +void Target::flush() +{ + for (auto &cluster : this->clusters) + { + cluster->flush(); + } +} + + + +void Target::resume_all() +{ + for (auto &cluster : this->clusters) + { + cluster->resume(); + } +} + + + +void Target::resume(bool step, int tid) +{ + if (tid == -1) + { + for (auto &thread : this->get_threads()) + { + thread->prepare_resume(step); + this->resume_all(); + } + } + else + { + auto *thread = this->get_thread(tid); + thread->prepare_resume(step); + thread->resume(); + } + +} + +bool Target::wait(int socket_client) +{ + printf("UNIMPLEMENTED AT %s %d\n", __FILE__, __LINE__); + exit(1); + return true; +} + + + +void Target::update_power() +{ + for (auto &cluster: clusters) { + cluster->update_power(); + } +} + + + +void Target::halt() +{ + for (auto &cluster: this->clusters) + { + cluster->halt(); + } +} \ No newline at end of file diff --git a/tools/pulp-debug-bridge/src/python_wrapper.cpp b/tools/pulp-debug-bridge/src/python_wrapper.cpp new file mode 100644 index 00000000..13b5e360 --- /dev/null +++ b/tools/pulp-debug-bridge/src/python_wrapper.cpp @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ + +#include +#include +#include +#include + +#include "json.hpp" +#include "cables/log.h" +#include "cables/adv_dbg_itf/adv_dbg_itf.hpp" +#include "cables/jtag-proxy/jtag-proxy.hpp" +#ifdef __USE_FTDI__ +#include "cables/ftdi/ftdi.hpp" +#endif +#include "gdb-server/gdb-server.hpp" + +using namespace std; + + +static int bridge_verbose = 0; +static const char *bridge_error = NULL; +static js::config *system_config = NULL; + +void Log::print(log_level_e level, const char *str, ...) +{ + if (bridge_verbose <= level) return; + va_list va; + va_start(va, str); + vprintf(str, va); + va_end(va); +} + + +void Log::user(const char *str, ...) +{ + if (bridge_verbose <= LOG_INFO) return; + va_list va; + va_start(va, str); + vprintf(str, va); + va_end(va); +} + +void Log::debug(const char *str, ...) +{ + if (bridge_verbose <= LOG_DEBUG) return; + va_list va; + va_start(va, str); + vprintf(str, va); + va_end(va); +} + +void Log::warning(const char *str, ...) +{ + if (bridge_verbose <= LOG_WARNING) return; + va_list va; + va_start(va, str); + vprintf(str, va); + va_end(va); +} + +void Log::error(const char *str, ...) +{ + char buff[1024]; + va_list va; + va_start(va, str); + vsnprintf(buff, 1024, str, va); + va_end(va); + bridge_error = strdup(buff); + + if (bridge_verbose <= LOG_ERROR) return; + va_start(va, str); + vprintf(str, va); + va_end(va); +} + +extern "C" void *cable_new(const char *config_string, const char *system_config_string) +{ + const char *cable_name = NULL; + js::config *config = NULL; + js::config *system_config = js::import_config_from_string(std::string(system_config_string)); + + if (config_string != NULL) + { + config = js::import_config_from_string(std::string(config_string)); + js::config *type_config = config->get("type"); + if (type_config != NULL) + { + cable_name = type_config->get_str().c_str(); + } + } + + if (cable_name == NULL) { + bridge_error = "No cable name specified"; + return NULL; + } + + if (strncmp(cable_name, "ftdi", 4) == 0) + { +#ifdef __USE_FTDI__ + Log *log = new Log(); + Ftdi::FTDIDeviceID id = Ftdi::Olimex; + if (strcmp(cable_name, "ftdi@digilent") == 0) id = Ftdi::Digilent; + if (strcmp(cable_name, "ftdi@generic") == 0) id = Ftdi::Generic; + Adv_dbg_itf *adu = new Adv_dbg_itf(system_config, config, log, new Ftdi(system_config, log, id)); + return (void *)static_cast(adu); +#else + fprintf(stderr, "Debug bridge has not been compiled with FTDI support\n"); + return NULL; +#endif + } + else if (strcmp(cable_name, "jtag-proxy") == 0) + { + Log *log = new Log(); + Adv_dbg_itf *adu = new Adv_dbg_itf(system_config, config, log, new Jtag_proxy(log)); + return (void *)static_cast(adu); + } + else + { + fprintf(stderr, "Unknown cable: %s\n", cable_name); + return NULL; + } + + return NULL; +} + +extern "C" void cable_write(void *cable, unsigned int addr, int size, const char *data) +{ + Adv_dbg_itf *adu = (Adv_dbg_itf *)cable; + adu->access(true, addr, size, (char *)data); +} + +extern "C" void cable_read(void *cable, unsigned int addr, int size, const char *data) +{ + Adv_dbg_itf *adu = (Adv_dbg_itf *)cable; + adu->access(false, addr, size, (char *)data); +} + +extern "C" void cable_reg_write(void *cable, unsigned int addr, const char *data, int device) +{ + Adv_dbg_itf *adu = (Adv_dbg_itf *)cable; + adu->reg_access(true, addr, (char *)data, device); +} + +extern "C" void cable_reg_read(void *cable, unsigned int addr, const char *data, int device) +{ + Adv_dbg_itf *adu = (Adv_dbg_itf *)cable; + adu->reg_access(false, addr, (char *)data, device); +} + +extern "C" void chip_reset(void *handler, bool active, int duration) +{ + Adv_dbg_itf *cable = (Adv_dbg_itf *)handler; + cable->chip_reset(active, duration); +} + +extern "C" void chip_config(void *handler, uint32_t value) +{ + Adv_dbg_itf *cable = (Adv_dbg_itf *)handler; + cable->chip_config(value); +} + +extern "C" void jtag_reset(void *handler, bool active) +{ + Adv_dbg_itf *cable = (Adv_dbg_itf *)handler; + cable->jtag_reset(active); +} + +extern "C" void jtag_soft_reset(void *handler) +{ + Adv_dbg_itf *cable = (Adv_dbg_itf *)handler; + cable->jtag_soft_reset(); +} + + +extern "C" bool cable_jtag_set_reg(void *handler, unsigned int reg, int width, unsigned int value, int ir_len) +{ + Cable *cable = (Cable *)handler; + return cable->jtag_set_reg(reg, width, value, ir_len); +} + +extern "C" bool cable_jtag_get_reg(void *handler, unsigned int reg, int width, unsigned int *out_value, unsigned int value, int ir_len) +{ + Cable *cable = (Cable *)handler; + return cable->jtag_get_reg(reg, width, out_value, value, ir_len); +} + +extern "C" void cable_lock(void *handler) +{ + Cable *cable = (Cable *)handler; + cable->lock(); +} + +extern "C" void cable_unlock(void *handler) +{ + Cable *cable = (Cable *)handler; + cable->unlock(); +} + + + + +static void init_sigint_handler(int s) { + raise(SIGTERM); +} + +extern "C" char * bridge_get_error() +{ + if (bridge_error == NULL) return strdup("unknown error"); + return strdup(bridge_error); +} + +extern "C" void bridge_init(const char *config_string, int verbose) +{ + system_config = js::import_config_from_string(std::string(config_string)); + bridge_verbose = verbose; + + // This should be the first C method called by python. + // As python is not catching SIGINT where we are in C world, we have to + // setup a sigint handler to exit in case control+C is hit. + signal (SIGINT, init_sigint_handler); + +} + + +extern "C" void *gdb_server_open(void *cable, int socket_port) +{ + return (void *)new Gdb_server(new Log(), (Cable *)cable, system_config, socket_port); +} + +extern "C" void gdb_server_close(void *arg, int kill) +{ + Gdb_server *server = (Gdb_server *)arg; + server->stop(kill); +} + + + + +#if 0 + +extern "C" void plt_exit(void *_bridge, bool status) +{ + Bridge *bridge = (Bridge *)_bridge; + bridge->getMemIF()->exit(status); +} + +extern "C" bool jtag_reset(void *_bridge) +{ + Bridge *bridge = (Bridge *)_bridge; + bridge->getJtagIF()->jtag_reset(); +} + +extern "C" bool jtag_idle(void *_bridge) +{ + Bridge *bridge = (Bridge *)_bridge; + bridge->getJtagIF()->jtag_idle(); +} + +extern "C" bool jtag_shift_ir(void *_bridge) +{ + Bridge *bridge = (Bridge *)_bridge; + bridge->getJtagIF()->jtag_shift_ir(); +} + +extern "C" bool jtag_shift_dr(void *_bridge) +{ + Bridge *bridge = (Bridge *)_bridge; + bridge->getJtagIF()->jtag_shift_dr(); +} + +extern "C" void jtag_shift(void *_bridge, int width, const char *datain, const char *dataout, int noex) +{ + Bridge *bridge = (Bridge *)_bridge; + bridge->getJtagIF()->jtag_shift(width, (unsigned char *)datain, (unsigned char *)dataout, noex); +} + +#endif diff --git a/tools/pulp-debug-bridge/src/reqloop.cpp b/tools/pulp-debug-bridge/src/reqloop.cpp new file mode 100644 index 00000000..a651ce1c --- /dev/null +++ b/tools/pulp-debug-bridge/src/reqloop.cpp @@ -0,0 +1,1181 @@ +/* + * Copyright (C) 2018 ETH Zurich and University of Bologna + * + * 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. + */ + +/* + * Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch) + */ + +#include +#include +#include "cable.hpp" +#include "cables/log.h" +#include "debug_bridge/debug_bridge.h" +#include +#include +#include +#include +#include +#include +#include + +#if defined(__USE_SDL__) +#include +#endif + +typedef enum +{ + TARGET_SYNC_FSM_STATE_INIT, + TARGET_SYNC_FSM_STATE_WAIT_INIT, + TARGET_SYNC_FSM_STATE_WAIT_AVAILABLE, + TARGET_SYNC_FSM_STATE_WAIT_REQUEST, + TARGET_SYNC_FSM_STATE_WAIT_ACK +} target_sync_fsm_state_e; + +class Target_req +{ +public: + bool done; + + hal_bridge_req_t target_req; +}; + +class Reqloop +{ +public: + Reqloop(Cable *cable, unsigned int debug_struct_addr); + void reqloop_routine(); + int stop(bool kill); + void activate(); + + void efuse_access(bool write, int id, uint32_t value, uint32_t mask); + int eeprom_access(uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size); + int flash_access(int32_t type, uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size); + int flash_erase(int32_t type, uint32_t itf, uint32_t cs, uint32_t addr, int32_t size); + int flash_erase_sector(int32_t type, uint32_t itf, uint32_t cs, uint32_t addr); + int flash_erase_chip(int32_t type, uint32_t itf, uint32_t cs); + void buffer_free(uint32_t addr, uint32_t size); + uint32_t buffer_alloc(uint32_t size); + +private: + void reply_req(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); + bool handle_req_connect(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); + bool handle_req_open(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); + bool handle_req_read(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); + bool handle_req_write(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); + bool handle_req_close(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); + bool handle_req_fb_open(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req); + bool handle_req_fb_update(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req); + bool handle_req_target_status_sync(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req); + bool handle_req_disconnect(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); + bool handle_req_reply(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); + bool handle_req(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req); + void update_target_status(hal_debug_struct_t *debug_struct); + void wait_target_available(hal_debug_struct_t *debug_struct); + + void notif_target(hal_debug_struct_t *debug_struct); + void handle_target_req(hal_debug_struct_t *debug_struct, Target_req *target_req); + void handle_bridge_to_target_reqs(hal_debug_struct_t *debug_struct); + + bool wait_target_request(); + unsigned int get_target_state(); + void send_target_ack(); + void clear_target_ack(); + + Log *log; + std::thread *thread; + Cable *cable; + bool end = false; + unsigned int debug_struct_addr; + int status = 0; + bool connected = false; // Set to true once the applicatoin has sent the connect request + bool target_connected; + + hal_target_state_t target; + + std::queue target_reqs; + + std::mutex mutex; + std::condition_variable cond; + + target_sync_fsm_state_e target_sync_fsm_state; + unsigned int jtag_val; + + hal_debug_struct_t *debug_struct = NULL; + + bool target_jtag_sync; + + int confreg_instr; +}; + +class Framebuffer +{ +public: + Framebuffer(Cable *cable, std::string name, int width, int height, int format); + void update(uint32_t addr, int posx, int posy, int width, int height); + bool open(); + +private: + void fb_routine(); + + std::string name; + int width; + int height; + int format; + int pixel_size = 1; + Cable *cable; + std::thread *thread; + uint32_t *pixels; +#if defined(__USE_SDL__) + SDL_Surface *screen; + SDL_Texture * texture; + SDL_Renderer *renderer; + SDL_Window *window; +#endif +}; + +Framebuffer::Framebuffer(Cable *cable, std::string name, int width, int height, int format) +: name(name), width(width), height(height), format(format), cable(cable) +{ +} + +void Framebuffer::fb_routine() +{ +#if defined(__USE_SDL__) + bool quit = false; + SDL_Event event; + + while (!quit) + { + SDL_WaitEvent(&event); + switch (event.type) + { + case SDL_QUIT: + quit = true; + break; + } + } + + SDL_DestroyWindow(window); + SDL_Quit(); +#endif +} + + +bool Framebuffer::open() +{ +#if defined(__USE_SDL__) + + if (format == HAL_BRIDGE_REQ_FB_FORMAT_GRAY) + { + pixel_size = 1; + } + else if (format == HAL_BRIDGE_REQ_FB_FORMAT_RGB) + { + pixel_size = 4; + } + else if (format == HAL_BRIDGE_REQ_FB_FORMAT_RAW) + { + pixel_size = 1; + } + else + { + printf("Unsupported format: %d\n", format); + } + + pixels = new uint32_t[width*height]; + SDL_Init(SDL_INIT_VIDEO); + + window = SDL_CreateWindow(name.c_str(), + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 0); + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + + texture = SDL_CreateTexture(renderer, + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height); + + memset(pixels, 255, width * height * sizeof(Uint32)); + + SDL_UpdateTexture(texture, NULL, pixels, width * sizeof(Uint32)); + + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + + thread = new std::thread(&Framebuffer::fb_routine, this); + return true; + +#else + printf("Trying to open framebuffer while bridge has not been compiled with SDL support\n"); + return false; +#endif +} + +void Framebuffer::update(uint32_t addr, int posx, int posy, int width, int height) +{ +#if defined(__USE_SDL__) + + if (posx == -1) + { + posx = posy = 0; + width = this->width; + height = this->height; + } + + int size = width*height*pixel_size; + + if (this->format == HAL_BRIDGE_REQ_FB_FORMAT_GRAY) + { + uint8_t buffer[size]; + this->cable->access(false, addr, size, (char*)buffer); + +#if 0 + static int count = 0; + char name[64]; + sprintf(name, "fb%d", count++); + FILE *file = fopen(name, "w"); + fprintf(file, "P5\n%d %d\n255\n", this->width, this->height); + fwrite(buffer, 1, size, file); + fclose(file); +#endif + + for (int j=0; jwidth + i + posx] = (0xff << 24) | (value << 16) | (value << 8) | value; + } + } + } + else if (this->format == HAL_BRIDGE_REQ_FB_FORMAT_RAW) + { + uint8_t buffer[size]; + this->cable->access(false, addr, size, (char*)buffer); + + for (int j=0; jwidth + i + posx] = (0xff << 24) | (value << shift); + } + } + } + else + { + uint32_t buffer[size]; + this->cable->access(false, addr, size*4, (char*)buffer); + + for (int j=0; jwidth + i + posx] = (0xff << 24) | value; + } + } + } + + + SDL_UpdateTexture(texture, NULL, pixels, this->width * sizeof(Uint32)); + + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); +#endif +} + + + +int Reqloop::stop(bool kill) +{ + if (kill) end = true; + thread->join(); + return status; +} + +void Reqloop::activate() +{ + if (this->debug_struct == NULL) + { + cable->access(false, debug_struct_addr, 4, (char*)&this->debug_struct); + + if (this->debug_struct != NULL) + { + uint32_t protocol_version; + cable->access(false, (unsigned int)(long)&this->debug_struct->protocol_version, 4, (char*)&protocol_version); + + if (protocol_version != PROTOCOL_VERSION_4) + { + this->log->error("Protocol version mismatch between bridge and runtime (bridge: %d, runtime: %d)\n", PROTOCOL_VERSION_4, protocol_version); + throw std::logic_error("Unable to connect to runtime"); + } + + int32_t is_connected; + this->cable->access(false, (unsigned int)(long)&this->debug_struct->target.connected, 4, (char*)&is_connected); + this->connected = is_connected; + + // The binary has just started, we need to tell him we want to watch for requests + unsigned int value = 0; + + uint32_t connected = 1; + cable->access(true, (unsigned int)(long)&this->debug_struct->bridge.connected, 4, (char*)&connected); + cable->access(true, (unsigned int)(long)&this->debug_struct->use_internal_printf, 4, (char*)&value); + } + } +} + + + +void Reqloop::notif_target(hal_debug_struct_t *debug_struct) +{ + uint32_t notif_req_addr; + uint32_t notif_req_value; + cable->access(false, (unsigned int)(long)&debug_struct->notif_req_addr, 4, (char*)¬if_req_addr); + cable->access(false, (unsigned int)(long)&debug_struct->notif_req_value, 4, (char*)¬if_req_value); + + cable->access(true, (unsigned int)(long)notif_req_addr, 4, (char*)¬if_req_value); +} + +void Reqloop::reply_req(hal_debug_struct_t *debug_struct, hal_bridge_req_t *target_req, hal_bridge_req_t *req) +{ + uint32_t value = 1; + this->cable->access(true, (unsigned int)(long)&target_req->done, sizeof(target_req->done), (char*)&value); + + this->notif_target(debug_struct); +} + +static int transpose_code(int code) +{ + int alt = 0; + + if ((code & 0x0) == 0x0) alt |= O_RDONLY; + if ((code & 0x1) == 0x1) alt |= O_WRONLY; + if ((code & 0x2) == 0x2) alt |= O_RDWR; + if ((code & 0x8) == 0x8) alt |= O_APPEND; + if ((code & 0x200) == 0x200) alt |= O_CREAT; + if ((code & 0x400) == 0x400) alt |= O_TRUNC; + if ((code & 0x800) == 0x800) alt |= O_EXCL; + if ((code & 0x2000) == 0x2000) alt |= O_SYNC; + if ((code & 0x4000) == 0x4000) alt |= O_NONBLOCK; + if ((code & 0x8000) == 0x8000) alt |= O_NOCTTY; + + return alt; +} + +bool Reqloop::handle_req_connect(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ + this->connected = true; + this->reply_req(debug_struct, target_req, req); + return false; +} + +bool Reqloop::handle_req_reply(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ + Target_req *bridge_req = (Target_req *)req->bridge_data; + + this->mutex.lock(); + bridge_req->done = true; + this->notif_target(debug_struct); + this->cond.notify_all(); + + // Copy the target information to the local request in case some returned + // information are needed + memcpy((void *)&bridge_req->target_req, (void *)req, sizeof(hal_bridge_req_t)); + + this->mutex.unlock(); + return false; +} + +bool Reqloop::handle_req_disconnect(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ + this->connected = false; + this->reply_req(debug_struct, target_req, req); + return true; +} + +bool Reqloop::handle_req_open(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ + char name[req->open.name_len+1]; + cable->access(false, (unsigned int)(long)req->open.name, req->open.name_len+1, (char*)name); + + int res = open(name, req->open.flags, req->open.mode); + + cable->access(true, (unsigned int)(long)&target_req->open.retval, 4, (char*)&res); + + this->reply_req(debug_struct, target_req, req); + return false; +} + +bool Reqloop::handle_req_read(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ + char buffer[4096]; + int size = req->read.len; + char *ptr = (char *)(long)req->read.ptr; + int res = 0; + while (size) + { + int iter_size = size; + if (iter_size > 4096) + iter_size = 4096; + + iter_size = read(req->read.file, (void *)buffer, iter_size); + + if (iter_size <= 0) { + if (iter_size == -1 && res == 0) res = -1; + break; + } + + cable->access(true, (unsigned int)(long)ptr, iter_size, (char*)buffer); + + res += iter_size; + ptr += iter_size; + size -= iter_size; + } + + cable->access(true, (unsigned int)(long)&target_req->read.retval, 4, (char*)&res); + + this->reply_req(debug_struct, target_req, req); + return false; +} + +bool Reqloop::handle_req_write(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ + char buffer[4096]; + int size = req->write.len; + char *ptr = (char *)(long)req->write.ptr; + int res = 0; + while (size) + { + int iter_size = size; + if (iter_size > 4096) + iter_size = 4096; + + cable->access(false, (unsigned int)(long)ptr, iter_size, (char*)buffer); + + iter_size = write(req->write.file, (void *)buffer, iter_size); + + if (iter_size <= 0) + break; + + res += iter_size; + ptr += iter_size; + size -= iter_size; + } + + if (res == 0) + res = -1; + + cable->access(true, (unsigned int)(long)&target_req->write.retval, 4, (char*)&res); + + this->reply_req(debug_struct, target_req, req); + return false; +} + +bool Reqloop::handle_req_close(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ + int res = close(req->close.file); + cable->access(true, (unsigned int)(long)&target_req->write.retval, 4, (char*)&res); + this->reply_req(debug_struct, target_req, req); + return false; +} + +bool Reqloop::handle_req_fb_open(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ + char name[req->fb_open.name_len+1]; + cable->access(false, (unsigned int)(long)req->fb_open.name, req->fb_open.name_len+1, (char*)name); + + int res = 0; + Framebuffer *fb = new Framebuffer(cable, name, req->fb_open.width, req->fb_open.height, req->fb_open.format); + + + + if (!fb->open()) + { + res = -1; + delete fb; + fb = NULL; + } + + cable->access(true, (unsigned int)(long)&target_req->fb_open.screen, 8, (char*)&fb); + + this->reply_req(debug_struct, target_req, req); + return false; +} + +void Reqloop::update_target_status(hal_debug_struct_t *debug_struct) +{ + this->cable->access(false, (unsigned int)(long)&debug_struct->target, sizeof(this->target), (char *)&this->target); +} + +bool Reqloop::handle_req_target_status_sync(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ + this->update_target_status(debug_struct); + this->reply_req(debug_struct, target_req, req); + return false; +} + +bool Reqloop::handle_req_fb_update(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ +#if defined(__USE_SDL__) + Framebuffer *fb = (Framebuffer *)req->fb_update.screen; + + fb->update( + req->fb_update.addr, req->fb_update.posx, req->fb_update.posy, req->fb_update.width, req->fb_update.height + ); +#endif + + this->reply_req(debug_struct, target_req, req); + return false; +} + +bool Reqloop::handle_req(hal_debug_struct_t *debug_struct, hal_bridge_req_t *req, hal_bridge_req_t *target_req) +{ + switch (req->type) + { + case HAL_BRIDGE_REQ_CONNECT: return this->handle_req_connect(debug_struct, req, target_req); + case HAL_BRIDGE_REQ_DISCONNECT: return this->handle_req_disconnect(debug_struct, req, target_req); + case HAL_BRIDGE_REQ_OPEN: return this->handle_req_open(debug_struct, req, target_req); + case HAL_BRIDGE_REQ_READ: return this->handle_req_read(debug_struct, req, target_req); + case HAL_BRIDGE_REQ_WRITE: return this->handle_req_write(debug_struct, req, target_req); + case HAL_BRIDGE_REQ_CLOSE: return this->handle_req_close(debug_struct, req, target_req); + case HAL_BRIDGE_REQ_FB_OPEN: return this->handle_req_fb_open(debug_struct, req, target_req); + case HAL_BRIDGE_REQ_FB_UPDATE: return this->handle_req_fb_update(debug_struct, req, target_req); + case HAL_BRIDGE_REQ_TARGET_STATUS_SYNC: return this->handle_req_target_status_sync(debug_struct, req, target_req); + case HAL_BRIDGE_REQ_REPLY: return this->handle_req_reply(debug_struct, req, target_req); + default: + this->log->print(LOG_ERROR, "Received unknown request from target (type: %d)\n", req->type); + } + return false; +} + + +unsigned int Reqloop::get_target_state() +{ + unsigned int value; + if (this->confreg_instr == 6) + { + this->cable->jtag_get_reg((6<<5)|(0x1f), 9, &value, this->jtag_val, 9); + value = (value >> 1) & 0xf; + } + else + { + this->cable->jtag_get_reg(7, 4, &value, this->jtag_val); + value &= 0xf; + } + return value >> 1; +} + +void Reqloop::send_target_ack() +{ + unsigned int value; + this->jtag_val = 0x7 << 1; + + if (this->confreg_instr == 6) + { + this->cable->jtag_set_reg((6<<5)|(0x1f), 9, 0x7 << 2, 9); + } + else + { + this->cable->jtag_set_reg(7, 4, 0x7 << 1); + } +} + +void Reqloop::clear_target_ack() +{ + unsigned int value; + this->jtag_val = 0x0 << 1; + + if (this->confreg_instr == 6) + { + this->cable->jtag_set_reg((6<<5)|(0x1f), 9, 0x0 << 2, 9); + } + else + { + this->cable->jtag_set_reg(7, 4, 0x0 << 1); + } +} + +bool Reqloop::wait_target_request() +{ + // In case the target does not support synchronization through the JTAG + // register, just consider that the target is always available. + if (!this->target_jtag_sync) + return true; + + switch (this->target_sync_fsm_state) + { + case TARGET_SYNC_FSM_STATE_INIT: { + //printf("STATE INIT\n"); + // This state is used in case the bridge is not yet connected to the + // target (e.g. if it boots from flash). + // Wait until the target becomes available and ask for the connection. + unsigned int state = this->get_target_state(); + if (state == 4) + { + //printf("RECEIVED CONNECTION REQ\n"); + // Target is asking for connection, acknowledge it and go to next step + this->send_target_ack(); + this->target_sync_fsm_state = TARGET_SYNC_FSM_STATE_WAIT_INIT; + } + return false; + } + + case TARGET_SYNC_FSM_STATE_WAIT_INIT: { + //printf("STATE WAIT INIT\n"); + // This state is used to wait for the target acknoledgment, after + // it asked for connection + unsigned int state = this->get_target_state(); + if (state != 4) + { + // Once the target acknowledged the connection, activate the bridge + // clear the acknowledge and wait for target availability + //printf("RECEIVED CONNECTION ACK\n"); + this->activate(); + this->clear_target_ack(); + this->target_sync_fsm_state = TARGET_SYNC_FSM_STATE_WAIT_REQUEST; + } + return true; + } + + case TARGET_SYNC_FSM_STATE_WAIT_AVAILABLE: { + //printf("STATE WAIT AVAILABLE\n"); + // We are in the state when the target cannot be accessed. + // Wait until we see something different from 0. + // We also need to clear the request acknowledgement to let the target + // know that we got it, in case we come from state + // TARGET_SYNC_FSM_STATE_WAIT_ACK. + this->clear_target_ack(); + unsigned int state = this->get_target_state(); + if (state == 0) + return false; + + //printf("TARGET AVAILABLE\n"); + this->target_sync_fsm_state = TARGET_SYNC_FSM_STATE_WAIT_REQUEST; + return false; + } + + case TARGET_SYNC_FSM_STATE_WAIT_REQUEST: { + //printf("STATE WAIT REQUEST\n"); + // The target became available, now wait for a request + unsigned int state = this->get_target_state(); + + if (state == 1) + { + // The target is still available with no request, stay in this state + return false; + } + + if (state == 2) + { + //printf("SHUTDOWN REQ\n"); + // The target is requesting a shutdown, acknowledge it and stop + // accessing the target + this->send_target_ack(); + this->target_sync_fsm_state = TARGET_SYNC_FSM_STATE_WAIT_ACK; + return false; + } + + //printf("OTHER REQ\n"); + // The target is requesting something else, just report true so that + // the caller now checks what is requested through JTAG and stay in + // this state. + return true; + } + + case TARGET_SYNC_FSM_STATE_WAIT_ACK: { + //printf("STATE WAIT ACK\n"); + // The chip is not accessible, just wait until it becomes available + // again + unsigned int state = this->get_target_state(); + if (state != 0) + return false; + + //printf("AVAILABLE AFTER SHUTDOWN\n"); + this->target_sync_fsm_state = TARGET_SYNC_FSM_STATE_WAIT_AVAILABLE; + return false; + } + } +} + + +void Reqloop::handle_target_req(hal_debug_struct_t *debug_struct, Target_req *target_req) +{ + // First get a request from the target + hal_bridge_req_t *req = NULL; + + this->cable->access(false, (unsigned int)(long)&debug_struct->first_bridge_free_req, 4, (char*)&req); + + if (req == NULL) + { + this->log->error("Unable to allocate bridge to target request"); + throw std::logic_error("Unable to allocate bridge to target request"); + } + + uint32_t next; + this->cable->access(false, (unsigned int)(long)&req->next, 4, (char*)&next); + this->cable->access(true, (unsigned int)(long)&debug_struct->first_bridge_free_req, 4, (char*)&next); + this->cable->access(true, (unsigned int)(long)req, sizeof(hal_bridge_req_t), (char*)&target_req->target_req); + this->cable->access(true, (unsigned int)(long)&req->bridge_data, sizeof(target_req), (char*)&target_req); + + + // Store it to the debug structure + this->cable->access(true, (unsigned int)(long)&debug_struct->target_req, 4, (char*)&req); + + // And notify the target so that it is processed + this->notif_target(debug_struct); +} + +void Reqloop::handle_bridge_to_target_reqs(hal_debug_struct_t *debug_struct) +{ + if (!this->connected) + return; + + while(this->target_reqs.size()) + { + // Runtime can only handle one request, first check if no request is already + // pushed. + uint32_t target_req; + this->cable->access(false, (unsigned int)(long)&debug_struct->target_req, 4, (char*)&target_req); + if (target_req) + break; + + this->mutex.lock(); + Target_req *bridge_target_req = this->target_reqs.front(); + this->target_reqs.pop(); + this->handle_target_req(debug_struct, bridge_target_req); + this->mutex.unlock(); + } +} + +void Reqloop::reqloop_routine() +{ + // In case the birdge is not yet connected, do extra init steps to + // connect once the target becomes available + this->target_sync_fsm_state = this->debug_struct ? TARGET_SYNC_FSM_STATE_WAIT_AVAILABLE : TARGET_SYNC_FSM_STATE_INIT; + + this->jtag_val = 0; + + if (debug_struct_addr) { + + // In case the debug struct pointer is found, iterate to receive IO requests + // from runtime + while(!end) + { + uint32_t value; + + // Wait until the target is available and has a request. + // This will poll the target through the JTAG register. + if (!this->wait_target_request()) + { + // If not, just wait a bit and retry + usleep(100); + continue; + } + + // First check if the application has exited + cable->access(false, (unsigned int)(long)&debug_struct->exit_status, 4, (char*)&value); + if (value >> 31) { + status = ((int)value << 1) >> 1; + printf("Detected end of application, exiting with status: %d\n", status); + return; + } + + // Check printf + // The target application should quickly dumps the characters, so we can loop on printf + // until we don't find anything + while(1) { + cable->access(false, (unsigned int)(long)&debug_struct->pending_putchar, 4, (char*)&value); + if (value == 0) break; + char buff[value+1]; + cable->access(false, (unsigned int)(long)&debug_struct->putc_buffer, value, (char*)buff); + unsigned int zero = 0; + cable->access(true, (unsigned int)(long)&debug_struct->pending_putchar, 4, (char*)&zero); + for (int i=0; iaccess(false, (unsigned int)(long)&debug_struct->first_bridge_req, 4, (char*)&first_bridge_req)) goto end; + + if (first_bridge_req == NULL) + break; + + hal_bridge_req_t req; + if (!this->cable->access(false, (unsigned int)(long)first_bridge_req, sizeof(hal_bridge_req_t), (char*)&req)) goto end; + + value = 1; + if (!cable->access(true, (unsigned int)(long)&first_bridge_req->popped, sizeof(first_bridge_req->popped), (char*)&value)) goto end; + if (!cable->access(true, (unsigned int)(long)&debug_struct->first_bridge_req, 4, (char*)&req.next)) goto end; + + if (this->handle_req(debug_struct, &req, first_bridge_req)) + return; + } + + // Handle bridge to target requests + this->handle_bridge_to_target_reqs(debug_struct); + + if (!this->target_jtag_sync) + { + // Small sleep to not poll too often + usleep(500); + } + } + } + else + { + log->warning("Trying to launch request loop (command reqloop) while no binary is provided\n"); + } + + return; + +end: + log->warning("Got access error in reqloop\n"); +} + +void Reqloop::efuse_access(bool write, int id, uint32_t value, uint32_t mask) +{ + Target_req *req = new Target_req(); + req->done = false; + + req->target_req.type = HAL_BRIDGE_TARGET_REQ_EFUSE_ACCESS; + req->target_req.efuse_access.is_write = write; + req->target_req.efuse_access.index = id; + req->target_req.efuse_access.value = value; + req->target_req.efuse_access.mask = mask; + + std::unique_lock lock(this->mutex); + + this->target_reqs.push(req); + + while(!req->done) + { + this->cond.wait(lock); + } + + lock.unlock(); +} + +int Reqloop::eeprom_access(uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size) +{ + Target_req *req = new Target_req(); + req->done = false; + + req->target_req.type = HAL_BRIDGE_TARGET_REQ_EEPROM_ACCESS; + req->target_req.eeprom_access.itf = itf; + req->target_req.eeprom_access.cs = cs; + req->target_req.eeprom_access.is_write = write; + req->target_req.eeprom_access.addr = addr; + req->target_req.eeprom_access.buffer = buffer; + req->target_req.eeprom_access.size = size; + + std::unique_lock lock(this->mutex); + this->target_reqs.push(req); + + while(!req->done) + { + this->cond.wait(lock); + } + + int retval = req->target_req.eeprom_access.retval; + + delete req; + + lock.unlock(); + + return retval; +} + +int Reqloop::flash_access(int type, uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size) +{ + Target_req *req = new Target_req(); + req->done = false; + + req->target_req.type = HAL_BRIDGE_TARGET_REQ_FLASH_ACCESS; + req->target_req.flash_access.type = type; + req->target_req.flash_access.itf = itf; + req->target_req.flash_access.cs = cs; + req->target_req.flash_access.is_write = write; + req->target_req.flash_access.addr = addr; + req->target_req.flash_access.buffer = buffer; + req->target_req.flash_access.size = size; + + std::unique_lock lock(this->mutex); + this->target_reqs.push(req); + + while(!req->done) + { + this->cond.wait(lock); + } + + int retval = req->target_req.flash_access.retval; + + delete req; + + lock.unlock(); + + return retval; +} + +int Reqloop::flash_erase(int type, uint32_t itf, uint32_t cs, uint32_t addr, int32_t size) +{ + Target_req *req = new Target_req(); + req->done = false; + + req->target_req.type = HAL_BRIDGE_TARGET_REQ_FLASH_ERASE; + req->target_req.flash_erase.type = type; + req->target_req.flash_erase.itf = itf; + req->target_req.flash_erase.cs = cs; + req->target_req.flash_erase.addr = addr; + req->target_req.flash_erase.size = size; + + std::unique_lock lock(this->mutex); + this->target_reqs.push(req); + + while(!req->done) + { + this->cond.wait(lock); + } + + int retval = req->target_req.flash_erase.retval; + + delete req; + + lock.unlock(); + + return retval; +} + +int Reqloop::flash_erase_sector(int type, uint32_t itf, uint32_t cs, uint32_t addr) +{ + Target_req *req = new Target_req(); + req->done = false; + + req->target_req.type = HAL_BRIDGE_TARGET_REQ_FLASH_ERASE_SECTOR; + req->target_req.flash_erase_sector.type = type; + req->target_req.flash_erase_sector.itf = itf; + req->target_req.flash_erase_sector.cs = cs; + req->target_req.flash_erase_sector.addr = addr; + + std::unique_lock lock(this->mutex); + this->target_reqs.push(req); + + while(!req->done) + { + this->cond.wait(lock); + } + + int retval = req->target_req.flash_erase_sector.retval; + + delete req; + + lock.unlock(); + + return retval; +} + +int Reqloop::flash_erase_chip(int type, uint32_t itf, uint32_t cs) +{ + Target_req *req = new Target_req(); + req->done = false; + + req->target_req.type = HAL_BRIDGE_TARGET_REQ_FLASH_ERASE_CHIP; + req->target_req.flash_erase_chip.type = type; + req->target_req.flash_erase_chip.itf = itf; + req->target_req.flash_erase_chip.cs = cs; + + std::unique_lock lock(this->mutex); + this->target_reqs.push(req); + + while(!req->done) + { + this->cond.wait(lock); + } + + int retval = req->target_req.flash_erase_chip.retval; + + delete req; + + lock.unlock(); + + return retval; +} + +void Reqloop::buffer_free(uint32_t addr, uint32_t size) +{ + Target_req *req = new Target_req(); + req->done = false; + + req->target_req.type = HAL_BRIDGE_TARGET_REQ_BUFFER_FREE; + req->target_req.buffer_free.size = size; + req->target_req.buffer_free.buffer = addr; + + std::unique_lock lock(this->mutex); + this->target_reqs.push(req); + + while(!req->done) + { + this->cond.wait(lock); + } + + delete req; + + lock.unlock(); +} + +uint32_t Reqloop::buffer_alloc(uint32_t size) +{ + Target_req *req = new Target_req(); + req->done = false; + + req->target_req.type = HAL_BRIDGE_TARGET_REQ_BUFFER_ALLOC; + req->target_req.buffer_alloc.size = size; + + std::unique_lock lock(this->mutex); + this->target_reqs.push(req); + + while(!req->done) + { + this->cond.wait(lock); + } + + uint32_t addr = req->target_req.buffer_alloc.buffer; + + delete req; + + lock.unlock(); + + return addr; +} + +Reqloop::Reqloop(Cable *cable, unsigned int debug_struct_addr) : cable(cable), debug_struct_addr(debug_struct_addr), target_connected(false) +{ + log = new Log(); + + js::config *config = cable->get_config(); + + this->target_jtag_sync = config->get_child_bool("**/debug_bridge/target_jtag_sync"); + + if (config->get("**/pulp_tap/confreg_instr") != NULL) + { + this->confreg_instr = config->get_int("**/pulp_tap/confreg_instr"); + } + else + { + this->confreg_instr = 7; + } + + std::string chip = config->get("**/chip/name")->get_str(); + + // Try to connect the bridge now before the execution is started so + // that the target sees the bridge as soon as it starts. + // Otherwise, the bridge will get connected later on when the target + // becomes available + this->activate(); + thread = new std::thread(&Reqloop::reqloop_routine, this); +} + +extern "C" void *bridge_reqloop_open(void *cable, unsigned int debug_struct_addr) +{ + return (void *)new Reqloop((Cable *)cable, debug_struct_addr); +} + +extern "C" void bridge_reqloop_close(void *arg, int kill) +{ + Reqloop *reqloop = (Reqloop *)arg; + reqloop->stop(kill); +} + +extern "C" void bridge_reqloop_efuse_access(void *arg, bool write, int id, uint32_t value, uint32_t mask) +{ + Reqloop *reqloop = (Reqloop *)arg; + reqloop->efuse_access(write, id, value, mask); +} + + +extern "C" int bridge_reqloop_eeprom_access(void *arg, uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size) +{ + Reqloop *reqloop = (Reqloop *)arg; + return reqloop->eeprom_access(itf, cs, write, addr, buffer, size); +} + + +extern "C" int bridge_reqloop_flash_access(void *arg, int type, uint32_t itf, uint32_t cs, bool write, uint32_t addr, uint32_t buffer, uint32_t size) +{ + Reqloop *reqloop = (Reqloop *)arg; + return reqloop->flash_access(type, itf, cs, write, addr, buffer, size); +} + + + +extern "C" int bridge_reqloop_flash_erase(void *arg, int type, uint32_t itf, uint32_t cs, uint32_t addr, int size) +{ + Reqloop *reqloop = (Reqloop *)arg; + return reqloop->flash_erase(type, itf, cs, addr, size); +} + + + +extern "C" int bridge_reqloop_flash_erase_sector(void *arg, int type, uint32_t itf, uint32_t cs, uint32_t addr) +{ + Reqloop *reqloop = (Reqloop *)arg; + return reqloop->flash_erase_sector(type, itf, cs, addr); +} + + + +extern "C" int bridge_reqloop_flash_erase_chip(void *arg, int type, uint32_t itf, uint32_t cs) +{ + Reqloop *reqloop = (Reqloop *)arg; + return reqloop->flash_erase_chip(type, itf, cs); +} + + +extern "C" uint32_t bridge_reqloop_buffer_alloc(void *arg, uint32_t size) +{ + Reqloop *reqloop = (Reqloop *)arg; + return reqloop->buffer_alloc(size); +} + + +extern "C" void bridge_reqloop_buffer_free(void *arg, uint32_t addr, uint32_t size) +{ + Reqloop *reqloop = (Reqloop *)arg; + reqloop->buffer_free(addr, size); +} + +