From 3c7fb7a456e2879993c6e0d9a44aa978e14ddead Mon Sep 17 00:00:00 2001 From: rdi Date: Thu, 7 Mar 2024 13:54:51 +0100 Subject: [PATCH] Add fuzz testing system --- build-system/build-system.gyp | 10 + build-system/completion/__init__.py | 10 +- build-system/erbb/__init__.py | 156 +++++++ build-system/erbb/generators/action/action.py | 1 + .../generators/action/action_fuzz_template.py | 52 +++ .../erbb/generators/fuzz/Makefile_template | 126 +++++ build-system/erbb/generators/fuzz/__init__.py | 1 + build-system/erbb/generators/fuzz/make.py | 433 ++++++++++++++++++ build-system/erbui/__init__.py | 13 + build-system/erbui/generators/fuzz/code.py | 190 ++++++++ .../erbui/generators/fuzz/code_template.cpp | 291 ++++++++++++ build-system/scripts/erbb | 80 ++++ 12 files changed, 1360 insertions(+), 3 deletions(-) create mode 100755 build-system/erbb/generators/action/action_fuzz_template.py create mode 100644 build-system/erbb/generators/fuzz/Makefile_template create mode 100755 build-system/erbb/generators/fuzz/__init__.py create mode 100755 build-system/erbb/generators/fuzz/make.py create mode 100755 build-system/erbui/generators/fuzz/code.py create mode 100644 build-system/erbui/generators/fuzz/code_template.cpp diff --git a/build-system/build-system.gyp b/build-system/build-system.gyp index 9813e5e8b..0962a0bee 100644 --- a/build-system/build-system.gyp +++ b/build-system/build-system.gyp @@ -42,7 +42,9 @@ 'erbb/generators/action/action_daisy_template.py', 'erbb/generators/action/action_data_template.py', 'erbb/generators/action/action_faust_template.py', + 'erbb/generators/action/action_fuzz_template.py', 'erbb/generators/action/action_max_template.py', + 'erbb/generators/action/action_perf_template.py', 'erbb/generators/action/action_ui_template.py', 'erbb/generators/action/action_vcvrack_install_template.py', 'erbb/generators/action/action_vcvrack_template.py', @@ -98,6 +100,10 @@ 'erbb/generators/perf/Makefile_template', 'erbb/generators/perf/make.py', + # generators/fuzz + 'erbb/generators/fuzz/Makefile_template', + 'erbb/generators/fuzz/make.py', + # generators/vscode 'erbb/generators/vscode/c_cpp_properties_template.json', 'erbb/generators/vscode/c_cpp_properties.py', @@ -161,6 +167,10 @@ 'erbui/generators/perf/code_template.cpp', 'erbui/generators/perf/code.py', + # generators/fuzz + 'erbui/generators/fuzz/code_template.cpp', + 'erbui/generators/fuzz/code.py', + # generators/tayda 'erbui/generators/tayda/drill.py', 'erbui/generators/tayda/drill_template.py', diff --git a/build-system/completion/__init__.py b/build-system/completion/__init__.py index a8c009b17..42553875c 100755 --- a/build-system/completion/__init__.py +++ b/build-system/completion/__init__.py @@ -32,14 +32,17 @@ def with_apt (): return '--with-apt' def build_simulator (): return 'simulator', ZeroOrMore ([configuration, xcode]) def build_firmware (): return 'firmware', ZeroOrMore ([configuration, semihosting]) def build_performance (): return 'performance', ZeroOrMore ([logger]) +def build_fuzz (): return 'fuzz', ZeroOrMore ([logger]) def build_hardware (): return 'hardware', ZeroOrMore ([only_gerber]) def install_firmware (): return 'firmware', ZeroOrMore ([configuration, programmer]) def install_performance (): return 'performance' +def install_fuzz (): return 'fuzz' def install_bootloader (): return 'bootloader', ZeroOrMore ([fast_boot]) def install_simulator (): return 'simulator', ZeroOrMore ([configuration]) def run_performance (): return 'performance' +def run_fuzz (): return 'fuzz' def setup (): if platform.system () == 'Darwin': @@ -51,9 +54,9 @@ def setup (): def init (): return 'init', ZeroOrMore ([name, language]) def configure (): return 'configure' -def build (): return 'build', [build_simulator, build_firmware, build_performance, build_hardware] -def install (): return 'install', [install_firmware, install_performance, install_bootloader, install_simulator] -def run (): return 'run ', run_performance +def build (): return 'build', [build_simulator, build_firmware, build_performance, build_fuzz, build_hardware] +def install (): return 'install', [install_firmware, install_performance, install_fuzz, install_bootloader, install_simulator] +def run (): return 'run ', [run_performance, run_fuzz] def commands (): return [setup, init, configure, build, install, run] @@ -86,6 +89,7 @@ def erbb_cli (): return 'erbb', commands 'install': 'install the firmware or bootloader', '--programmer': 'the programmer to use, defaults to automatic selection', 'performance': 'the performance analysis firmware', + 'fuzz': 'the fuzz testing firmware', '--logger': 'the logger to use, defaults to automatic selection', 'bootloader': 'the bootloader', 'debug': 'for debugging', diff --git a/build-system/erbb/__init__.py b/build-system/erbb/__init__.py index 802692f85..d605faae8 100755 --- a/build-system/erbb/__init__.py +++ b/build-system/erbb/__init__.py @@ -59,6 +59,7 @@ from .generators.simulator.make import Make as simulatorMake from .generators.daisy.make import Make as daisyMake from .generators.perf.make import Make as perfMake +from .generators.fuzz.make import Make as fuzzMake from .generators.vcvrack.project import Project as vcvrackProject from .generators.vscode.c_cpp_properties import CCppProperties as vscodeCCppProperties from .generators.vscode.extensions import Extensions as vscodeExtensions @@ -232,6 +233,7 @@ def configure (path, ast): configure_simulator_make (path, ast) configure_daisy (path, ast) configure_perf (path, ast) + configure_fuzz (path, ast) configure_vscode (path, ast) @@ -285,6 +287,18 @@ def configure_perf (path, ast): +""" +============================================================================== +Name: configure_fuzz +============================================================================== +""" + +def configure_fuzz (path, ast): + generator = fuzzMake () + generator.generate (path, ast) + + + """ ============================================================================== Name: configure_vscode @@ -435,6 +449,38 @@ def build_performance_target (target, path, logger): +""" +============================================================================== +Name : build_fuzz_target +============================================================================== +""" + +def build_fuzz_target (target, path, logger): + path_artifacts = os.path.join (path, 'artifacts') + + build_libdaisy () + + os.environ ['CONFIGURATION'] = 'Release' + + if logger == 'auto': + if stlink_plugged (): + logger = 'semihosting' + else: + logger = 'usb' + + if logger == 'semihosting': + os.environ ['SEMIHOSTING'] = '1' + + cmd = [ + MAKE_CMD, + '--jobs', + '--directory=%s' % os.path.join (path_artifacts, 'fuzz') + ] + + subprocess.check_call (cmd) + + + """ ============================================================================== Name : build_libdaisy @@ -686,6 +732,38 @@ def deploy_performance (name, section, path, programmer): +""" +============================================================================== +Name : deploy_fuzz +============================================================================== +""" + +def deploy_fuzz (name, section, path, programmer): + path_artifacts = os.path.join (path, 'artifacts') + + if programmer == 'auto': + if section != 'flash': + programmer = 'dfu' + elif stlink_plugged (): + programmer = 'stlink' + else: + programmer = 'dfu' + + if programmer == 'stlink': + if section != 'flash': + print ('Install option \'stlink\' doesn\'t support programming to %s.' % section) + print ('Please use option \'dfu\' instead.') + sys.exit () + + file_elf = os.path.join (path_artifacts, 'fuzz', 'Release', '%s.elf' % name) + deploy_openocd (name, file_elf) + + elif programmer == 'dfu': + file_bin = os.path.join (path_artifacts, 'fuzz', 'Release', '%s.bin' % name) + deploy_dfu_util (name, section, file_bin) + + + """ ============================================================================== Name : deploy_bootloader @@ -929,3 +1007,81 @@ def run_performance_usb (): while True: nbr = daisy.in_waiting print (daisy.read (nbr).decode ('utf-8'), end='') + + + +""" +============================================================================== +Name : run_fuzz +============================================================================== +""" + +def run_fuzz (): + if stlink_plugged (): + run_fuzz_semihosting () + else: + run_fuzz_usb () + + + +""" +============================================================================== +Name : run_fuzz_semihosting +============================================================================== +""" + +def run_fuzz_semihosting (): + openocd_cmd = [ + OPENOCD_CMD, + '--search', OPENOCD_SCRIPTS, + '--file', 'interface/stlink.cfg', + '--file', 'target/stm32h7x.cfg', + ] + + gdb_cmd = [ + GDB_CMD, + '--ex', 'target extended-remote localhost:3333', # connect to openocd gdb-server + '--ex', 'monitor reset halt', # restart device and stop execution + '--ex', 'monitor arm semihosting enable', # activate semihosting + '--ex', 'continue', # run + ] + + proc_openocd = subprocess.Popen (openocd_cmd) + proc_gdb = subprocess.Popen (gdb_cmd) + + try: + while True: + pass + except KeyboardInterrupt: + proc_gdb.kill () + proc_openocd.kill () + + + +""" +============================================================================== +Name : run_fuzz_usb +============================================================================== +""" + +def run_fuzz_usb (): + print ('Waiting for Daisy...') + print ('(Press the RESET button if it doesn\'t connect)') + + while not daisy_plugged (): + print ('.', end='', flush=True) + time.sleep (0.5) + + print ('') + + device = None + for port in serial.tools.list_ports.comports (): + if (port.vid, port.pid) == (0x0483, 0x5740): # Daisy Seed Built In + device = port.device + + assert device + daisy = serial.Serial (device, 115200, timeout=0) + + while True: + nbr = daisy.in_waiting + print (daisy.read (nbr).decode ('utf-8'), end='') diff --git a/build-system/erbb/generators/action/action.py b/build-system/erbb/generators/action/action.py index aa5d78466..9a093b22f 100755 --- a/build-system/erbb/generators/action/action.py +++ b/build-system/erbb/generators/action/action.py @@ -31,6 +31,7 @@ def generate (self, path, root): def generate_module (self, path, module): self.generate_module_action (path, module, 'daisy') self.generate_module_action (path, module, 'perf') + self.generate_module_action (path, module, 'fuzz') self.generate_module_action (path, module, 'ui') self.generate_module_action (path, module, 'vcvrack') self.generate_module_action (path, module, 'data') diff --git a/build-system/erbb/generators/action/action_fuzz_template.py b/build-system/erbb/generators/action/action_fuzz_template.py new file mode 100755 index 000000000..0e06971c6 --- /dev/null +++ b/build-system/erbb/generators/action/action_fuzz_template.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# action_daisy.py +# Copyright (c) 2020 Raphael Dinge +# +#Tab=3######################################################################## + + +##### IMPORT ################################################################# + +from __future__ import print_function +import os +import subprocess +import sys + +PATH_ROOT = '%PATH_ROOT%' +PATH_PROJECT = '%PATH_PROJECT%' + +sys.path.insert (0, os.path.join (PATH_ROOT, 'build-system')) +import erbui + + + +############################################################################## + +if sys.version_info < (3, 7): + print ('This script requires python 3.7 or greater.', file = sys.stderr) + sys.exit (1) + + + +############################################################################## + +def find_erbui (): + files = os.listdir (PATH_PROJECT) + for file in files: + if file.endswith ('.erbui'): + return os.path.join (PATH_PROJECT, file) + + return None + +if __name__ == '__main__': + try: + project_path = os.path.abspath (PATH_PROJECT) + artifacts_path = os.path.join (project_path, 'artifacts') + + ast = erbui.parse (find_erbui ()) + erbui.generate_fuzz (artifacts_path, ast) + + except subprocess.CalledProcessError as error: + print ('Action exited with %d' % error.returncode, file = sys.stderr) + sys.exit (1) diff --git a/build-system/erbb/generators/fuzz/Makefile_template b/build-system/erbb/generators/fuzz/Makefile_template new file mode 100644 index 000000000..c6189cca6 --- /dev/null +++ b/build-system/erbb/generators/fuzz/Makefile_template @@ -0,0 +1,126 @@ +############################################################################## +# +# Makefile +# Copyright (c) 2020 Raphael DINGE +# +#Tab=3######################################################################## + + + +# !!! THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY !!! + + + +%define_PATH_ROOT% +%define_PATH_LIBDAISY% +TARGET = %module.name% + +CONFIGURATION ?= Release + + +# Toolchain +%define_CC% +%define_CXX% +%define_GDB% +%define_AS% +%define_CP% +%define_SZ% +%define_HEX% +%define_BIN% + + +# C standard +CFLAGS += -std=gnu11 + +# C++ standard +CXXFLAGS += -std=gnu++2a + +FLAGS += -I$(PATH_ROOT)/include + +FLAGS += \ +-I$(LIBDAISY_DIR) \ +-I$(LIBDAISY_DIR)/src/ \ +-I$(LIBDAISY_DIR)/src/sys \ +-I$(LIBDAISY_DIR)/src/usbd \ +-I$(LIBDAISY_DIR)/src/usbh \ +-I$(LIBDAISY_DIR)/Drivers/CMSIS/Include/ \ +-I$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Include \ +-I$(LIBDAISY_DIR)/Drivers/CMSIS/Device/ST/STM32H7xx/Include \ +-I$(LIBDAISY_DIR)/Drivers/STM32H7xx_HAL_Driver/Inc/ \ +-I$(LIBDAISY_DIR)/Middlewares/ST/STM32_USB_Device_Library/Core/Inc \ +-I$(LIBDAISY_DIR)/Middlewares/ST/STM32_USB_Host_Library/Core/Inc \ +-I$(LIBDAISY_DIR)/Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Inc \ +-I$(LIBDAISY_DIR)/Middlewares/Third_Party/FatFs/src + +FLAGS += -mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard + +FLAGS += -DUSE_HAL_DRIVER -DSTM32H750xx -DHSE_VALUE=16000000 +FLAGS += -DCORE_CM7 -DSTM32H750IB -DARM_MATH_CM7 -DUSE_FULL_LL_DRIVER +FLAGS += -D__FPU_PRESENT +FLAGS += -fasm -fdata-sections -ffunction-sections +FLAGS += -finline -finline-functions-called-once -fshort-enums -fno-move-loop-invariants + +# Optimization +ifeq ($(CONFIGURATION),Debug) +FLAGS += -O0 -g -ggdb -funsafe-math-optimizations +endif + +ifeq ($(CONFIGURATION),Release) +FLAGS += -O3 -DNDEBUG=1 -funsafe-math-optimizations +endif + +ifdef SEMIHOSTING + FLAGS += -Derb_SEMIHOSTING=1 +endif + +FLAGS += -Derb_FUZZ=1 + +%warnings% + +FLAGS += -Derb_TARGET_DAISY +%defines.entities% +%bases.entities% + +# Apply FLAGS to language-specific flags +CFLAGS += $(FLAGS) +CXXFLAGS += $(FLAGS) + +CXXFLAGS += -fno-exceptions -fno-rtti -fno-unwind-tables +CXXFLAGS += -Wno-register -Wno-volatile + +LDFLAGS += -mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard +LDFLAGS += --specs=nano.specs +ifdef SEMIHOSTING + LDFLAGS += --specs=rdimon.specs +else + LDFLAGS += --specs=nosys.specs +endif +LDFLAGS += -T%LDS_PATH% +LDFLAGS += -lc -lm +ifdef SEMIHOSTING + LDFLAGS += -lrdimon +else + LDFLAGS += -lnosys +endif +LDFLAGS += -ldaisy -L$(LIBDAISY_DIR)/build +LDFLAGS += -Wl,-Map=$(CONFIGURATION)/$(TARGET).map,--cref +LDFLAGS += -Wl,--gc-sections +LDFLAGS += -Wl,--print-memory-usage + + +all: $(CONFIGURATION)/$(TARGET).elf $(CONFIGURATION)/$(TARGET).hex $(CONFIGURATION)/$(TARGET).bin + +%target_actions% +%sources% +$(CONFIGURATION)/$(TARGET).hex: $(CONFIGURATION)/$(TARGET).elf | $(CONFIGURATION) + @echo "OBJCOPY $(CONFIGURATION)/$(TARGET).hex" + @$(HEX) $< $@ + +$(CONFIGURATION)/$(TARGET).bin: $(CONFIGURATION)/$(TARGET).elf | $(CONFIGURATION) + @echo "OBJCOPY $(CONFIGURATION)/$(TARGET).bin" + @$(BIN) $< $@ + +$(CONFIGURATION): + mkdir $@ + +.DEFAULT_GOAL := all diff --git a/build-system/erbb/generators/fuzz/__init__.py b/build-system/erbb/generators/fuzz/__init__.py new file mode 100755 index 000000000..8b1378917 --- /dev/null +++ b/build-system/erbb/generators/fuzz/__init__.py @@ -0,0 +1 @@ + diff --git a/build-system/erbb/generators/fuzz/make.py b/build-system/erbb/generators/fuzz/make.py new file mode 100755 index 000000000..6d4e6afe2 --- /dev/null +++ b/build-system/erbb/generators/fuzz/make.py @@ -0,0 +1,433 @@ +############################################################################## +# +# make.py +# Copyright (c) 2020 Raphael DINGE +# +#Tab=3######################################################################## + + + +import os +import platform +import sys + +PATH_THIS = os.path.abspath (os.path.dirname (__file__)) +PATH_ROOT = os.path.abspath (os.path.dirname (os.path.dirname (os.path.dirname (os.path.dirname (PATH_THIS))))) +PATH_BUILD_SYSTEM = os.path.join (PATH_ROOT, 'build-system') +PATH_TOOLCHAIN = os.path.join (PATH_BUILD_SYSTEM, 'toolchain') +PATH_ERBB_GENS = os.path.join (PATH_BUILD_SYSTEM, 'erbb', 'generators') +PATH_ERBUI_GENS = os.path.join (PATH_BUILD_SYSTEM, 'erbui', 'generators') +PATH_LIBDAISY = os.path.join (PATH_ROOT, 'submodules', 'libDaisy') + + + +class Make: + def __init__ (self): + pass + + + #-------------------------------------------------------------------------- + + def generate (self, path, root): + for module in root.modules: + self.generate_module (path, module, root.strict) + + + #-------------------------------------------------------------------------- + + def generate_module (self, path, module, strict): + path_template = os.path.join (PATH_THIS, 'Makefile_template') + path_fuzz = os.path.join (path, 'artifacts', 'fuzz') + path_makefile = os.path.join (path_fuzz, 'Makefile') + + if not os.path.exists (path_fuzz): + os.makedirs (path_fuzz) + + with open (path_template, 'r', encoding='utf-8') as file: + template = file.read () + + path_root = os.path.relpath (PATH_ROOT, path_fuzz) + path_libdaisy = os.path.relpath (PATH_LIBDAISY, path_fuzz) + + if module.section.name == 'flash': + lds_path = os.path.join (PATH_LIBDAISY, 'core', 'STM32H750IB_flash.lds') + elif module.section.name == 'qspi': + lds_path = os.path.join (PATH_LIBDAISY, 'core', 'STM32H750IB_qspi.lds') + elif module.section.name == 'sram': + lds_path = os.path.join (PATH_ERBB_GENS, 'linker', 'STM32H750IB_sram_ramd2.lds') + else: + assert False + + path_lds = os.path.relpath (lds_path, path_fuzz) + + template = template.replace ('%module.name%', module.name) + template = template.replace ('%define_PATH_ROOT%', 'PATH_ROOT ?= %s' % path_root.replace ('\\', '/')) + template = template.replace ('%define_PATH_LIBDAISY%', 'LIBDAISY_DIR ?= %s' % path_libdaisy.replace ('\\', '/')) + + if platform.system () == 'Darwin': + PATH_ARM_BIN = os.path.join (PATH_TOOLCHAIN, 'gcc-arm-none-eabi-10.3-2021.10', 'bin') + template = template.replace ('%define_CC%', 'CC = %s' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-gcc')) + template = template.replace ('%define_CXX%', 'CXX = %s' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-g++')) + template = template.replace ('%define_GDB%', 'GDB = %s' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-gdb')) + template = template.replace ('%define_AS%', 'AS = %s -x assembler-with-cpp' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-gcc')) + template = template.replace ('%define_CP%', 'CP = %s' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-objcopy')) + template = template.replace ('%define_SZ%', 'SZ = %s' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-size')) + template = template.replace ('%define_HEX%', 'HEX = %s -O ihex' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-objcopy')) + template = template.replace ('%define_BIN%', 'BIN = %s -O binary -S' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-objcopy')) + + elif platform.system () == 'Windows': + PATH_ARM_BIN = os.path.join (PATH_TOOLCHAIN, 'gcc-arm-none-eabi-10.3-2021.10', 'bin') + template = template.replace ('%define_CC%', 'CC = %s' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-gcc').replace ('\\', '/')) + template = template.replace ('%define_CXX%', 'CXX = %s' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-g++').replace ('\\', '/')) + template = template.replace ('%define_GDB%', 'GDB = %s' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-gdb').replace ('\\', '/')) + template = template.replace ('%define_AS%', 'AS = %s -x assembler-with-cpp' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-gcc').replace ('\\', '/')) + template = template.replace ('%define_CP%', 'CP = %s' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-objcopy').replace ('\\', '/')) + template = template.replace ('%define_SZ%', 'SZ = %s' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-size').replace ('\\', '/')) + template = template.replace ('%define_HEX%', 'HEX = %s -O ihex' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-objcopy').replace ('\\', '/')) + template = template.replace ('%define_BIN%', 'BIN = %s -O binary -S' % os.path.join (PATH_ARM_BIN, 'arm-none-eabi-objcopy').replace ('\\', '/')) + + else: + template = template.replace ('%define_CC%', 'CC = arm-none-eabi-gcc') + template = template.replace ('%define_CXX%', 'CXX = arm-none-eabi-g++') + template = template.replace ('%define_GDB%', 'GDB = arm-none-eabi-gdb') + template = template.replace ('%define_AS%', 'AS = arm-none-eabi-gcc -x assembler-with-cpp') + template = template.replace ('%define_CP%', 'CP = arm-none-eabi-objcopy') + template = template.replace ('%define_SZ%', 'SZ = arm-none-eabi-size') + template = template.replace ('%define_HEX%', 'HEX = arm-none-eabi-objcopy -O ihex') + template = template.replace ('%define_BIN%', 'BIN = arm-none-eabi-objcopy -O binary -S') + + template = template.replace ('%LDS_PATH%', path_lds) + template = self.replace_warnings (template, strict) + template = self.replace_defines (template, module, module.defines) + template = self.replace_bases (template, module, module.bases, path_fuzz); + template = self.replace_sources (template, module, module.sources, path_fuzz) + template = self.replace_actions (template, module, path_fuzz) + + with open (path_makefile, 'w', encoding='utf-8') as file: + file.write (template) + + + #-------------------------------------------------------------------------- + + def replace_warnings (self, template, strict): + lines = '' + + if strict: + lines += 'FLAGS += -Wall -Wextra -Wpedantic -Werror\n' + + return template.replace ('%warnings%', lines) + + + #-------------------------------------------------------------------------- + + def replace_defines (self, template, module, defines): + lines = '' + + if module.section.name != 'flash': + lines += 'FLAGS += -DBOOT_APP=1\n' + + if module.source_language == 'max': + lines += 'FLAGS += -DGENLIB_USE_FLOAT32\n' + lines += 'FLAGS += -DGENLIB_NO_JSON\n' + lines += 'FLAGS += -DGEN_NO_STDLIB\n' + + define_map = { + 'erb_BUFFER_SIZE': '48', + 'erb_SAMPLE_RATE': '48014', + } + + for define in defines: + define_map [define.key] = define.value + + for key, value in define_map.items (): + lines += 'FLAGS += -D%s=%s\n' % (key, value) + + return template.replace ('%defines.entities%', lines) + + + #-------------------------------------------------------------------------- + + def replace_bases (self, template, module, bases, path_fuzz): + lines = '' + + lines += 'FLAGS += -I../..\n' + + if module.source_language == 'max': + path_gen_dsp = os.path.join (PATH_ROOT, 'include', 'gen_dsp') + lines += 'FLAGS += -I%s\n' % os.path.relpath (path_gen_dsp, path_fuzz).replace ('\\', '/') + lines += 'FLAGS += -I..\n' + + elif module.source_language == 'faust': + lines += 'FLAGS += -I..\n' + + for base in bases: + path_base = os.path.relpath (base.path, path_fuzz) + lines += 'FLAGS += -I%s\n' % path_base.replace ('\\', '/') + + return template.replace ('%bases.entities%', lines) + + + #-------------------------------------------------------------------------- + + def replace_sources (self, template, module, sources, path_fuzz): + lines = '' + + source_paths = self.include_sources_erb (module) + + def add_source_path (path): + source_paths.append (os.path.abspath (path)) + + add_source_path (os.path.join (path_fuzz, 'main.cpp')) + + for source in sources: + for file in source.files: + if file.path.endswith ('.cpp'): + add_source_path (file.path) + + has_data = False + + for resource in module.resources: + if resource.datas: + has_data = True + + if has_data: + add_source_path (os.path.join (path_fuzz, '../plugin_generated_data.cpp')) + + if module.source_language == 'max': + add_source_path (os.path.join (path_fuzz, '../%s_erbb.cpp' % module.name)) + add_source_path (os.path.join (path_fuzz, '../%s_erbui.cpp' % module.name)) + add_source_path (os.path.join (path_fuzz, '../module_max_alt.cpp')) + + def object_name (path): + if platform.system () == 'Windows': + return '$(CONFIGURATION)' + path [path.find (':') + 1:].replace ('\\', '/') + '.o' + else: + return '$(CONFIGURATION)' + path + '.o' + + def dep_name (path): + if platform.system () == 'Windows': + return '$(CONFIGURATION)' + path [path.find (':') + 1:].replace ('\\', '/') + '.d' + else: + return '$(CONFIGURATION)' + path + '.d' + + source_extra_paths = [] + source_extra_paths.append (os.path.abspath (os.path.join (PATH_LIBDAISY, 'core', 'startup_stm32h750xx.c'))) + cmsis_dsp_src_path = os.path.abspath (os.path.join (PATH_LIBDAISY, 'Drivers', 'CMSIS', 'DSP', 'Source')) + source_extra_paths.append (os.path.join (cmsis_dsp_src_path, 'CommonTables', 'arm_common_tables.c')) + source_extra_paths.append (os.path.join (cmsis_dsp_src_path, 'FastMathFunctions', 'arm_cos_f32.c')) + source_extra_paths.append (os.path.join (cmsis_dsp_src_path, 'FastMathFunctions', 'arm_sin_f32.c')) + + objects = ' '.join (map (lambda x: object_name (x), source_paths)) + objects += ' ' + ' '.join (map (lambda x: object_name (x), source_extra_paths)) + lines += '$(CONFIGURATION)/$(TARGET).elf: %s\n' % objects + lines += '\t@echo "LINK $(CONFIGURATION)/$(TARGET).elf"\n' + lines += '\t@$(CXX) -o $@ $^ $(LDFLAGS)\n\n' + + for source_extra_path in source_extra_paths: + rel_path = os.path.relpath (source_extra_path, path_fuzz) + lines += '%s: %s Makefile | $(CONFIGURATION) $(ACTIONS)\n' % (object_name (source_extra_path), rel_path.replace ('\\', '/')) + lines += '\t@echo "CC %s"\n' % rel_path.replace ('\\', '/').replace ('../', '') + lines += '\t@mkdir -p $(@D)\n' + lines += '\t@$(CC) -MMD -MP $(CFLAGS) -Wno-pedantic -Wno-missing-attributes -c -o $@ %s\n\n' % rel_path.replace ('\\', '/') + lines += '-include %s\n\n' % dep_name (source_extra_path) + + for source_path in source_paths: + rel_path = os.path.relpath (source_path, path_fuzz) + lines += '%s: %s Makefile | $(CONFIGURATION) $(ACTIONS)\n' % (object_name (source_path), rel_path.replace ('\\', '/')) + lines += '\t@echo "CXX %s"\n' % rel_path.replace ('\\', '/').replace ('../', '') + lines += '\t@mkdir -p $(@D)\n' + lines += '\t@$(CXX) -MMD -MP $(CXXFLAGS) -c -o $@ %s\n\n' % rel_path.replace ('\\', '/') + lines += '-include %s\n\n' % dep_name (source_path) + + return template.replace ('%sources%', lines) + + + #-------------------------------------------------------------------------- + + def include_sources_erb (self, module): + sources = [] + + sources.extend (self.include_gyp_sources ( + os.path.join (PATH_ROOT, 'include', 'erb', 'erb-src.gypi') + )) + + sources.extend (self.include_gyp_sources ( + os.path.join (PATH_ROOT, 'include', 'erb', 'erb-daisy.gypi') + )) + + if module.source_language == 'max': + sources.extend (self.include_gyp_sources ( + os.path.join (PATH_ROOT, 'include', 'gen_dsp', 'gen_dsp.gypi') + )) + + return sources + + + #-------------------------------------------------------------------------- + + def include_gyp_sources (self, path_gyp_file): + sources = [] + + with open (path_gyp_file, 'r', encoding='utf-8') as f: + gyp_dict = eval (f.read ()) + + gyp_sources = gyp_dict ['sources'] + path_gyp_dir = os.path.dirname (path_gyp_file) + + for source in gyp_sources: + if source.endswith ('.cpp'): + sources.append (os.path.abspath (os.path.join (path_gyp_dir, source))) + + return sources + + + #-------------------------------------------------------------------------- + + def replace_actions (self, template, module, path_fuzz): + lines = '' + lines += self.replace_actions_max (module, path_fuzz) + lines += self.replace_actions_faust (module, path_fuzz) + lines += self.replace_actions_ui (module, path_fuzz) + lines += self.replace_actions_fuzz (module, path_fuzz) + lines += self.replace_actions_data (module, path_fuzz) + + return template.replace ('%target_actions%', lines) + + + #-------------------------------------------------------------------------- + + def replace_actions_max (self, module, path_fuzz): + if module.source_language != 'max': + return '' + + lines = '' + + path_erbb_gens = os.path.relpath (PATH_ERBB_GENS, path_fuzz) + path_erbui_gens = os.path.relpath (PATH_ERBUI_GENS, path_fuzz) + + inputs = os.path.join (path_erbb_gens, 'max', 'code.py').replace ('\\', '/') + ' ' + inputs += os.path.join (path_erbui_gens, 'max', 'code.py').replace ('\\', '/') + ' ' + inputs += '../module_max.cpp' + ' ' + inputs += '../module_max.h' + + outputs = '../module_max_alt.cpp' + ' ' + outputs += '../module_max_alt.h' + ' ' + outputs += '../%s_erbb.cpp' % module.name + ' ' + outputs += '../%s_erbui.cpp' % module.name + ' ' + outputs += '../%s.h' % module.name + + lines += '%s: ACTION_MAX\n' % outputs + lines += '\t@:\n' + lines += 'ACTION_MAX: %s Makefile | $(CONFIGURATION)\n' % inputs + lines += '\t@echo "ACTION Max"\n' + lines += '\t@%s ../actions/action_max.py\n\n' % sys.executable.replace ('\\', '/') + lines += 'ACTIONS += %s\n\n' % outputs + + return lines + + + #-------------------------------------------------------------------------- + + def replace_actions_faust (self, module, path_fuzz): + if module.source_language != 'faust': + return '' + + lines = '' + + path_erbb_gens = os.path.relpath (PATH_ERBB_GENS, path_fuzz) + path_erbui_gens = os.path.relpath (PATH_ERBUI_GENS, path_fuzz) + + inputs = os.path.join (path_erbb_gens, 'faust', 'code.py').replace ('\\', '/') + ' ' + inputs += os.path.join (path_erbui_gens, 'faust', 'code.py').replace ('\\', '/') + ' ' + inputs += '../../%s.dsp' % module.name + + outputs = '../module_faust.h' + ' ' + outputs += '../%s_erbb.hpp' % module.name + ' ' + outputs += '../%s_erbui.hpp' % module.name + ' ' + outputs += '../%s.h' % module.name + + lines += '%s: ACTION_FAUST\n' % outputs + lines += '\t@:\n' + lines += 'ACTION_FAUST: %s Makefile | $(CONFIGURATION)\n' % inputs + lines += '\t@echo "ACTION Faust"\n' + lines += '\t@%s ../actions/action_faust.py\n\n' % sys.executable.replace ('\\', '/') + lines += 'ACTIONS += %s\n\n' % outputs + + return lines + + + #-------------------------------------------------------------------------- + + def replace_actions_ui (self, module, path_fuzz): + lines = '' + + path_erbui_gens = os.path.relpath (PATH_ERBUI_GENS, path_fuzz) + + inputs = os.path.join (path_erbui_gens, 'ui', 'code.py').replace ('\\', '/') + ' ' + inputs += '../../%s.erbui' % module.name + + outputs = '../%sUi.h' % module.name + + lines += '%s: ACTION_UI\n' % outputs + lines += '\t@:\n' + lines += 'ACTION_UI: %s Makefile | $(CONFIGURATION)\n' % inputs + lines += '\t@echo "ACTION UI"\n' + lines += '\t@%s ../actions/action_ui.py\n\n' % sys.executable.replace ('\\', '/') + lines += 'ACTIONS += %s\n\n' % outputs + + return lines + + + #-------------------------------------------------------------------------- + + def replace_actions_fuzz (self, module, path_fuzz): + lines = '' + + path_erbui_gens = os.path.relpath (PATH_ERBUI_GENS, path_fuzz) + + inputs = os.path.join (path_erbui_gens, 'fuzz', 'code.py').replace ('\\', '/') + ' ' + inputs += '../../%s.erbui' % module.name + + outputs = 'main.cpp' + + lines += '%s: ACTION_DAISY\n' % outputs + lines += '\t@:\n' + lines += 'ACTION_DAISY: %s Makefile | $(CONFIGURATION)\n' % inputs + lines += '\t@echo "ACTION Daisy"\n' + lines += '\t@%s ../actions/action_fuzz.py\n\n' % sys.executable.replace ('\\', '/') + lines += 'ACTIONS += %s\n\n' % outputs + + return lines + + + #-------------------------------------------------------------------------- + + def replace_actions_data (self, module, path_fuzz): + data_paths = [] + + for resource in module.resources: + for data in resource.datas: + data_paths.append (data.file.path) + + lines = '' + + if data_paths: + path_erbb_gens = os.path.relpath (PATH_ERBB_GENS, path_fuzz) + + inputs = os.path.join (path_erbb_gens, 'data', 'code.py').replace ('\\', '/') + ' ' + inputs += '../../%s.erbb' % module.name + ' ' + + for data_path in data_paths: + inputs += '%s' % os.path.relpath (data_path, path_fuzz).replace ('\\', '/') + ' ' + + outputs = '../%sData.h' % module.name + ' ' + outputs += '../plugin_generated_data.cpp' + + lines += '%s: ACTION_DATA\n' % outputs + lines += '\t@:\n' + lines += 'ACTION_DATA: %s Makefile | $(CONFIGURATION)\n' % inputs + lines += '\t@echo "ACTION Data"\n' + lines += '\t@%s ../actions/action_data.py\n\n' % sys.executable.replace ('\\', '/') + lines += 'ACTIONS += %s\n\n' % outputs + + return lines diff --git a/build-system/erbui/__init__.py b/build-system/erbui/__init__.py index 7ad84ada4..5b76d9da3 100755 --- a/build-system/erbui/__init__.py +++ b/build-system/erbui/__init__.py @@ -17,6 +17,7 @@ from .generators.vcvrack.code import Code as vcvrackCode from .generators.daisy.code import Code as daisyCode from .generators.perf.code import Code as perfCode +from .generators.fuzz.code import Code as fuzzCode from .generators.front_panel.dxf import Dxf as front_panelDxf from .generators.front_panel.pdf import Pdf as front_panelPdf from .generators.front_panel.pcb import Pcb as front_panelPcb @@ -178,6 +179,18 @@ def generate_perf (path, ast): +""" +============================================================================== +Name: generate_fuzz +============================================================================== +""" + +def generate_fuzz (path, ast): + generator = fuzzCode () + generator.generate (path, ast) + + + """ ============================================================================== Name: generate_hardware diff --git a/build-system/erbui/generators/fuzz/code.py b/build-system/erbui/generators/fuzz/code.py new file mode 100755 index 000000000..df422fda2 --- /dev/null +++ b/build-system/erbui/generators/fuzz/code.py @@ -0,0 +1,190 @@ +############################################################################## +# +# code.py +# Copyright (c) 2020 Raphael DINGE +# +#Tab=3######################################################################## + + + +import os +import random + +PATH_THIS = os.path.abspath (os.path.dirname (__file__)) + + + +class Code: + def __init__ (self): + pass + + + #-------------------------------------------------------------------------- + + def generate (self, path, root): + for module in root.modules: + self.generate_module (path, module) + + + #-------------------------------------------------------------------------- + + def generate_module (self, path, module): + path_template = os.path.join (PATH_THIS, 'code_template.cpp') + path_cpp = os.path.join (path, 'fuzz', 'main.cpp') + + with open (path_template, 'r', encoding='utf-8') as file: + template = file.read () + + template = template.replace ('%module.name%', module.name) + template = self.replace_controls_context_decl (template, module.entities); + template = self.replace_controls_context_process (template, module.entities); + template = self.replace_board_preprocess (template, module.entities); + template = self.replace_board_postprocess (template, module.entities); + template = self.replace_controls_instrument (template, module.entities); + template = self.replace_controls_preprocess (template, module.entities); + template = self.replace_controls_postprocess (template, module.entities); + template = self.replace_controls_check_audio (template, module.entities); + + with open (path_cpp, 'w', encoding='utf-8') as file: + file.write (template) + + + #-------------------------------------------------------------------------- + + def replace_controls_context_decl (self, template, entities): + lines = '' + + for entity in entities: + if entity.is_control and entity.is_input: + if entity.kind in ['AudioIn']: + lines += ' Vco %s = { %f, %f };\n' % (entity.name, random.uniform (500.0, 600.0), random.uniform (0.01, 0.1)) + elif entity.kind in ['CvIn']: + lines += ' Lfo %s = { %f, %f };\n' % (entity.name, random.uniform (10.0, 100.0), random.uniform (0.01, 0.1)) + elif entity.kind in ['Pot', 'Trim']: + lines += ' Lfo %s = { %f, %f };\n' % (entity.name, random.uniform (0.1, 1.0), random.uniform (0.01, 0.1)) + elif entity.kind in ['Button']: + lines += ' Lfo %s = { %f, %f };\n' % (entity.name, random.uniform (0.1, 1.0), random.uniform (0.01, 0.1)) + elif entity.kind in ['GateIn']: + lines += ' Lfo %s = { %f, %f };\n' % (entity.name, random.uniform (10.0, 100.0), random.uniform (0.01, 0.1)) + elif entity.kind in ['Switch']: + lines += ' Lfo %s_0 = { %f, %f };\n' % (entity.name, random.uniform (0.1, 1.0), random.uniform (0.01, 0.1)) + lines += ' Lfo %s_1 = { %f, %f };\n' % (entity.name, random.uniform (0.1, 1.0), random.uniform (0.01, 0.1)) + + return template.replace ('% controls_context_decl%', lines) + + + #-------------------------------------------------------------------------- + + def replace_controls_context_process (self, template, entities): + lines = '' + + for entity in entities: + if entity.is_control and entity.is_input: + if entity.kind in ['AudioIn']: + lines += ' context.%s.process ();\n' % entity.name + elif entity.kind in ['CvIn', 'Pot', 'Trim']: + lines += ' context.%s.process ();\n' % entity.name + elif entity.kind in ['Button', 'GateIn']: + lines += ' context.%s.process ();\n' % entity.name + elif entity.kind in ['Switch']: + lines += ' context.%s_0.process ();\n' % entity.name + lines += ' context.%s_1.process ();\n' % entity.name + + return template.replace ('% controls_context_process%', lines) + + + #-------------------------------------------------------------------------- + + def replace_board_preprocess (self, template, entities): + lines = '' + + for entity in entities: + if entity.is_control and entity.is_input: + for sub_entity in entity.entities: + if sub_entity.is_pin: + lines += ' module.ui.board.impl_preprocess (BoardType::%s);\n' % sub_entity.name + elif sub_entity.is_pin_array: + for name in sub_entity.names: + lines += ' module.ui.board.impl_preprocess (BoardType::%s);\n' % name + + + return template.replace ('% board_preprocess%', lines) + + + #-------------------------------------------------------------------------- + + def replace_board_postprocess (self, template, entities): + lines = '' + + board_type = 'decltype (module.ui.board)' + + for entity in entities: + if entity.is_control and entity.is_output: + for sub_entity in entity.entities: + if sub_entity.is_pin: + lines += ' module.ui.board.impl_postprocess (BoardType::%s);\n' % sub_entity.name + elif sub_entity.is_pin_array: + for name in sub_entity.names: + lines += ' module.ui.board.impl_postprocess (BoardType::%s);\n' % name + + + return template.replace ('% board_postprocess%', lines) + + + #-------------------------------------------------------------------------- + + def replace_controls_instrument (self, template, entities): + lines = '' + + for entity in entities: + if entity.is_control and entity.is_input: + if entity.kind in ['AudioIn']: + lines += ' const_cast (module.ui.%s.impl_data) = context.%s;\n' % (entity.name, entity.name) + elif entity.kind in ['CvIn', 'Pot', 'Trim']: + lines += ' const_cast (module.ui.%s.impl_data) = context.%s;\n' % (entity.name, entity.name) + elif entity.kind in ['Button', 'GateIn']: + lines += ' const_cast (module.ui.%s.impl_data) = context.%s < 0.5f;\n' % (entity.name, entity.name) + elif entity.kind in ['Switch']: + lines += ' const_cast (module.ui.%s._0.impl_data) = context.%s_0 < 0.5f;\n' % (entity.name, entity.name) + lines += ' const_cast (module.ui.%s._1.impl_data) = context.%s_1 < 0.5f;\n' % (entity.name, entity.name) + + return template.replace ('% controls_instrument%', lines) + + + #-------------------------------------------------------------------------- + + def replace_controls_preprocess (self, template, entities): + lines = '' + + for entity in entities: + if entity.is_control: + lines += ' module.ui.%s.impl_preprocess ();\n' % entity.name + + return template.replace ('% controls_preprocess%', lines) + + + #-------------------------------------------------------------------------- + + def replace_controls_postprocess (self, template, entities): + lines = '' + + for entity in entities: + if entity.is_control: + lines += ' module.ui.%s.impl_postprocess ();\n' % entity.name + + return template.replace ('% controls_postprocess%', lines) + + + #-------------------------------------------------------------------------- + + def replace_controls_check_audio (self, template, entities): + lines = '' + + for entity in entities: + if entity.is_control and entity.is_output: + if entity.kind in ['AudioOut']: + lines += ' check_audio_buffer (module.ui.%s.impl_data);\n' % entity.name + + return template.replace ('% controls_check_audio%', lines) + + diff --git a/build-system/erbui/generators/fuzz/code_template.cpp b/build-system/erbui/generators/fuzz/code_template.cpp new file mode 100644 index 000000000..9b609254d --- /dev/null +++ b/build-system/erbui/generators/fuzz/code_template.cpp @@ -0,0 +1,291 @@ +/***************************************************************************** + + main.cpp + Copyright (c) 2020 Raphael DINGE + +*Tab=3***********************************************************************/ + + + +/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +#include "%module.name%.h" + +#include "erb/module_fnc.h" + +#include "erb/def.h" + +erb_DISABLE_WARNINGS_DAISY +#include "daisy.h" +#include +erb_RESTORE_WARNINGS + +#include +#include + +#if defined (erb_SEMIHOSTING) +extern "C" void initialise_monitor_handles (); +#endif + + + +/*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +#if defined (erb_SEMIHOSTING) +using Logger = daisy::Logger ; +#else +using Logger = daisy::Logger ; // USB on DaisyPatch SM +#endif + + + +/* +============================================================================== +Name : check_cpu_usage +============================================================================== +*/ + +void check_cpu_usage (std::uint32_t ts_diff) +{ +#if defined (__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wnarrowing" +#endif + + const auto usage + = float {ts_diff} + / (float (daisy::System::GetTickFreq ()) * float (erb_BUFFER_SIZE) / float (erb_SAMPLE_RATE)); + +#if defined (__GNUC__) + #pragma GCC diagnostic pop +#endif + + if (usage > 0.98f) + { + Logger::PrintLine ("Realtime constraint failed"); + asm ("bkpt 255"); + } +} + + + +/* +============================================================================== +Name : check_audio_buffer +============================================================================== +*/ + +void check_audio_buffer (const erb::Buffer & buf) +{ + for (std::size_t i = 0 ; i < buf.size () ; ++i) + { + if (std::isnan (buf [i])) + { + Logger::PrintLine ("Audio buffer contains NaN"); + asm ("bkpt 255"); + } + } +} + + + +/* +============================================================================== +Name : main +============================================================================== +*/ + +int main () +{ +#if defined (erb_SEMIHOSTING) + if (CoreDebug->DHCSR & 0x01) + { + initialise_monitor_handles (); + } +#endif + + // Enable FZ (flush-to-zero) denormal behavior + uint32_t fpscr = __get_FPSCR (); + fpscr |= 0x01000000; // FZ bit + __set_FPSCR (fpscr); + + // Init system + daisy::System system; + daisy::System::Config config; + config.Boost (); + + auto program_memory_section = daisy::System::GetProgramMemoryRegion (); + auto boot_version = daisy::System::GetBootloaderVersion (); + + // When using the bootloader prior to v6, clocks have been already configured + + if ( + (boot_version == daisy::System::BootInfo::Version::LT_v6_0) + && (program_memory_section != daisy::System::MemoryRegion::INTERNAL_FLASH) + ) + { + config.skip_clocks = true; + } + + system.Init (config); + +#if (erb_SDRAM_USE_FLAG) + // Init SDRAM + // When using the bootloader priori to v6, SDRAM has been already configured + + if ( + (boot_version != daisy::System::BootInfo::Version::LT_v6_0) + || ( + (boot_version == daisy::System::BootInfo::Version::LT_v6_0) + && (program_memory_section == daisy::System::MemoryRegion::INTERNAL_FLASH) + ) + ) + { + SdramHandle sdram; + sdram.Init (); + } +#endif + + Logger::StartLog (true /* wait for PC */); + + //------------------------------------------------------------------------- + + %module.name% module; + + // The Daisy Seed stack, sitting on SRAM is 512K only. + // When more memory is needed, move the big buffers like + // delay lines or samples to the SDRAM, which is 64M. + // Since we have some heap allocations, we keep a bit of margin + // and complain when we reach 128K of stack space. + + static_assert (sizeof (module) < 128 * 1024 /* 128 */, ""); + + // The SDRAM is compararively slow compared to the SRAM, + // So try to keep all memory that is accessed often in SRAM. + + // Check 'erb::make_sdram_object' to put your buffers in + // SDRAM. + + erb::module_init (module); + + struct Osc { + void set (float freq_norm) { + const double phase_step = double (freq_norm) * 2.0 * std::numbers::pi; + _step_cos = float (std::cos (phase_step)); + _step_sin = float (std::sin (phase_step)); + } + void process () { + const float old_cos = _pos_cos; + const float old_sin = _pos_sin; + _pos_cos = old_cos * _step_cos - old_sin * _step_sin; + _pos_sin = old_cos * _step_sin + old_sin * _step_cos; + + const float norm_sq = _pos_cos * _pos_cos + _pos_sin * _pos_sin; + const float mult = 1.f / std::sqrt (norm_sq); + + _pos_cos *= mult; + _pos_sin *= mult; + } + + operator float () { return _pos_cos; } + + float _pos_cos = 1.f; + float _pos_sin = 0.f; + float _step_cos = 1.f; + float _step_sin = 0.f; + }; + + struct Vco { + Vco (float freq, float freq_mod) { + const float freq_norm = freq / float (erb_SAMPLE_RATE); + const float freq_mod_norm = freq_mod / float (erb_SAMPLE_RATE); + _freq_norm = freq_norm; + _osc2.set (freq_mod_norm); + } + void process () { + for (std::size_t i = 0 ; i < _buf.size () ; ++i) + { + _osc1.set (_freq_norm + _osc2 * _freq_norm); + _osc1.process (); + _osc2.process (); + _buf [i] = _osc1; + } + } + + operator erb::Buffer () { return _buf; }; + + Osc _osc1; + Osc _osc2; + float _freq_norm; + erb::Buffer _buf; + }; + + struct Lfo { + Lfo (float freq, float freq_mod) { + const float freq_norm = freq / float (erb_SAMPLE_RATE / erb_BUFFER_SIZE); + const float freq_mod_norm = freq_mod / float (erb_SAMPLE_RATE / erb_BUFFER_SIZE); + _freq_norm = freq_norm; + _osc2.set (freq_mod_norm); + } + void process () { + _osc1.set (_freq_norm + _osc2 * _freq_norm); + _osc1.process (); + _osc2.process (); + } + + operator float () { return (1.f + _osc1) * 0.5f; } + + Osc _osc1; + Osc _osc2; + float _freq_norm; + }; + + struct Context { +% controls_context_decl% + std::atomic execution_duration = 0; + }; + + static Context context; + + module.ui.board.run ([&module](){ + using BoardType = decltype (module.ui.board); + +% controls_context_process% + + const auto tsl_beg = daisy::System::GetTick (); + + module.ui.board.impl_preprocess (); + +% board_preprocess% +% controls_instrument% +% controls_preprocess% + + module.process (); + +% controls_postprocess% +% board_postprocess% + module.ui.board.impl_postprocess (); + + const auto tsl_end = daisy::System::GetTick (); + +% controls_check_audio% + + context.execution_duration + = std::max (context.execution_duration.load (), tsl_end - tsl_beg); + }); + + const auto tick_freq = daisy::System::GetTickFreq (); + + for (;;) + { + auto ts_beg = daisy::System::GetTick (); + + erb::module_idle (module); + module.ui.board.impl_idle (); + + // busy wait so that the idle loop is at least 6ms + while (daisy::System::GetTick () - ts_beg < tick_freq * 6 / 1000) {} + + check_cpu_usage (context.execution_duration); + } +} diff --git a/build-system/scripts/erbb b/build-system/scripts/erbb index 39377ad43..628f614cd 100755 --- a/build-system/scripts/erbb +++ b/build-system/scripts/erbb @@ -267,6 +267,9 @@ def build (args): elif target == 'performance': build_performance (args) + elif target == 'fuzz': + build_fuzz (args) + elif target == 'hardware': build_hardware (args) @@ -358,6 +361,29 @@ def build_performance (args): +#-- build_fuzz --------------------------------------------------------------- + +def build_fuzz (args): + import erbb + + cwd = os.getcwd () + + if not hasattr (args, 'logger'): + logger = 'auto' + else: + logger = args.logger + + ast_erbb = read_erbb_ast (find_erbb ()) + + # force erbui parsing to avoid cache concurrency on Windows + ast_erbui = read_erbui_ast (find_erbui ()) + + module = ast_erbb.modules [0].name + + erbb.build_fuzz_target (module, cwd, logger) + + + #-- build_hardware ----------------------------------------------------------- def build_hardware (args): @@ -390,6 +416,8 @@ def install (args): install_firmware (args) elif target == 'performance': install_performance (args) + elif target == 'fuzz': + install_fuzz (args) elif target == 'bootloader': install_bootloader (args) elif target == 'simulator': @@ -442,6 +470,25 @@ def install_performance (args): +#-- install_fuzz ------------------------------------------------------------- + +def install_fuzz (args): + import erbb + + cwd = os.getcwd () + + if not hasattr (args, 'programmer'): + programmer = 'auto' + else: + programmer = args.programmer + + ast_erbb = read_erbb_ast (find_erbb ()) + module = ast_erbb.modules [0].name + section = ast_erbb.modules [0].section.name + erbb.deploy_fuzz (module, section, cwd, programmer) + + + #-- install_bootloader ------------------------------------------------------- def install_bootloader (args): @@ -485,6 +532,8 @@ def run (args): if target == 'performance': run_performance (args) + elif target == 'fuzz': + run_fuzz (args) @@ -497,6 +546,15 @@ def run_performance (args): +#-- run_fuzz ----------------------------------------------------------------- + +def run_fuzz (args): + import erbb + + erbb.run_fuzz () + + + #-- parse_args_setup --------------------------------------------------------- def parse_args_setup (parent): @@ -630,6 +688,18 @@ def parse_args_build (parent): help = 'the logger to use, defaults to automatic selection' ) + parser_fuzz = subparsers.add_parser ( + 'fuzz', + help='build the fuzz testing firmware' + ) + + parser_fuzz.add_argument( + '--logger', + default = 'auto', + choices = ['auto', 'usb', 'semihosting'], + help = 'the logger to use, defaults to automatic selection' + ) + parser_hardware = subparsers.add_parser ( 'hardware', help='build the hardware' @@ -691,6 +761,11 @@ def parse_args_install (parent): help='install the performance analysis firmware' ) + parser_fuzz = subparsers.add_parser ( + 'fuzz', + help='install the fuzz testing firmware' + ) + parser_simulator = subparsers.add_parser ( 'simulator', help='install the simulator' @@ -723,6 +798,11 @@ def parse_args_run (parent): help='run the performance analysis firmware' ) + parser_fuzz = subparsers.add_parser ( + 'fuzz', + help='run the fuzz testing firmware' + ) + #-- parse_args ---------------------------------------------------------------