diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..f29b8674f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps and configuration necessary to reproduce the behavior. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Desktop (please complete the following information):** + - OS: [e.g. Windows 10] + - odrivetool Version (`odrivetool --version`) + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/compile.yaml b/.github/workflows/compile.yaml index 6ca4a333e..a810ddb98 100644 --- a/.github/workflows/compile.yaml +++ b/.github/workflows/compile.yaml @@ -108,9 +108,22 @@ jobs: mv tup_build.sh tup_build.bat # in reality this is a .bat script on windows .\tup_build.bat - #code-checks: - # runs-on: ubuntu-latest - # steps: + code-checks: + strategy: + fail-fast: false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Check that we're not using std::isnan + run: | + cd ${{ github.workspace }}/Firmware + + if grep 'std::isnan' -R .; then + echo "Don't use std::isnan because it's not compatible with '-ffast-math'. Use is_nan() instead." + return 1; + fi + # TODO: # - check if enums.py is consistent with yaml # - clang-format check diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 4b3e32942..e36251572 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -15,9 +15,16 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Install odrivetool + shell: bash run: | - pip install monotonic # TODO: this is dishonest. Must be removed as soon as v0.5.0 is published! - pip install odrive + pip3 list | grep setuptools || echo "setuptools not installed" # show version + pip3 install setuptools # TODO: this should be a dependency of odrive + pip3 install odrive + env: + # TODO: this is a workaround for https://github.com/pypa/setuptools/issues/2353 and we + # can remove it as soon as python3-setuptools on GitHub's ubuntu-latest + # moves to version 50.1+. + SETUPTOOLS_USE_DISTUTILS: stdlib # This one currently fails because Github Actions runs pip as non-root #- name: Check if udev rules were set up properly @@ -27,7 +34,7 @@ jobs: # This step is mentioned in the user guide - name: Add ~/.local/bin to path if: matrix.os == 'ubuntu-latest' - run: echo "::add-path::~/.local/bin" + run: echo "~/.local/bin" >> $GITHUB_PATH - name: Launch odrivetool # This returns a non-zero exit code if the odrivetool throws an exception diff --git a/.gitignore b/.gitignore index c94fc787c..eb5307efc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,6 @@ __pycache__/ *.py[cod] *$py.class -# C extensions -*.so - # Unit test / coverage reports htmlcov/ .tox/ @@ -65,4 +62,4 @@ Firmware/Tests/bin/ # GUI GUI/dist_electron GUI/node_modules -GUI/build \ No newline at end of file +GUI/build diff --git a/.gitmodules b/.gitmodules index e69de29bb..ce3f8c382 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "Firmware/Private"] + path = Firmware/Private + url = git@github.com:madcowswe/ODrivePrivate.git + branch = submodule diff --git a/CHANGELOG.md b/CHANGELOG.md index 25943d175..48f49a3ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,95 @@ # Unreleased Features Please add a note of your changes below this heading if you make a Pull Request. +# Releases +## [0.5.2] - 2021-05-21 + +### Fixed +* spinout error is no longer sticky and doesn't trigger on static torque loads due to I^2*R electrical power +* Step and direction mode resets position when entering closed loop just like `input_pos` does +* CAN baud rate setting is now correctly handled +* `odrivetool dfu` works properly when an ODrive is flashed with the `dfu` switch set to "dfu". +* `odrivetool dfu` now erases the entire flash memory before flashing firmware. This ensures that old configuration parameters are erased. +* ASCII and the Native Protocol do not run at the same time on a UART interface. See `odrv0.config.uart0_protocol` and the `STREAM_PROTOCOL_TYPE` enums for details. + +### Added +* `sc` command to ascii protocol to run `odrv.clear_errors()` +* Added phase balance check to motor calibration and MOTOR_ERROR_UNBALANCED_PHASES to error enums +* Added polarity and phase offset calibration for hall effect encoders +* [Mechanical brake support](docs/mechanical-brakes.md) +* Added periodic sending of encoder position on CAN +* Support for UART1 on GPIO3 and GPIO4. UART0 (on GPIO1/2) and UART1 can currently not be enabled at the same time. +* Thermistors now have a 2nd order lowpass filter applied to reduce noise +* 2-norm current clamping is used for AC induction motors +* Added spinout detection to detect incorrect encoder offset and CONTROLLER_ERROR_SPINOUT_DETECTED to error enums. +* Added AARCH64 support to libfibre +* Tuning input mode added to provide sinusoidal position, velocity, or torque stimulus. See INPUT_MODE_TUNING and the controller class for details. +* Added torque mirroring to INPUT_MODE_MIRROR +* `mechanical_power_bandwidth`, `electrical_power_bandwidth`, `spinout_electrical_power_threshold`, `spinout_mechanical_power_threshold` added to `controller.config` for spinout detection. +* `mechanical_power` and `electrical_power` added to `controller`. + +### Changed +* Step/dir performance improved! Dual axis step rates up to 250kHz have been tested +* Apply_config is called for encoders after a successful direction find +* Full calibration sequence now includes hall polarity calibration if a hall effect encoder is used +* Modified encoder offset calibration to work correctly when calib_scan_distance is not a multiple of 4pi +* Moved thermistors from being a top level object to belonging to Motor objects. Also changed errors: thermistor errors rolled into motor errors +* Use DMA for DRV8301 setup +* Make NVM configuration code more dynamic so that the layout doesn't have to be known at compile time. +* GPIO initialization logic was changed. GPIOs now need to be explicitly set to the mode corresponding to the feature that they are used by. See `.config.gpioX_mode`. +* Previously, if two components used the same interrupt pin (e.g. step input for axis0 and axis1) then the one that was configured later would override the other one. Now this is no longer the case (the old component remains the owner of the pin). +* New control loop architecture: + 1. TIM8 update interrupt handler (CNT = 0) runs at a high priority and invokes the system level function `sample_cb()` to sample all timing critical inputs (currently only encoder state). + 2. TIM8 update interrupt handler (CNT = 0) raises an NVIC flag to kick off a lower priority interrupt. + 3. The control loop interrupt handler checks if all ADC measurements are ready and informs both motor objects about the current measurements. + 4. The control loop interrupt handler invokes the system level function `control_loop_cb()` which updates all components (encoders, estimators, torque controllers, etc). The data paths between the components are configured by the Axis threads based on the requested state. This replaces the previous architecture where the components were updated inside the Axis threads in `Axis::run_control_loop()`. + 5. Meanwhile the TIM1 and TIM8 updates for CNT = 3500 will have fired. The control loop interrupt handler thus reads the new ADC measurements and informs both motor objects that a DC calibration event has happened. + 6. Finally, the control loop interrupt invokes `pwm_update_cb` on both motors to make them update their PWM timing registers. +* Components that need low level control over PWM timings are implemented by inheriting from the `PhaseControlLaw` interface. Three components currently inherit this interface: `FieldOrientedController`, `ResistanceMeasurementControlLaw` and `InductanceMeasurementControlLaw`. +* The FOC algorithm is now found in foc.cpp and and is presumably capable of running at a different frequency than the main control tasks (not relevant for ODrive v3). +* ACIM estimator was consolidated into a separate component `.acim_estimator`. +* The Automatic Output Enable (AOE) flag of TIM1/TIM8 is used to achieve glitch-free motor arming. +* Sensorless mode was merged into closed loop control mode. Use `.enable_sensorless_mode` to disable the use of an encoder. +* More informative profiling instrumentation was added. +* A system-level error property was introduced. +* Added `torque_mirror_ratio` and use it to feed-forward `controller_.torque_output` in `INPUT_MODE_MIRROR` +* Accumulate integer steps in step/dir to avoid float precision errors +* Circular setpoint mode must be enabled when the step/dir interface is used. + +### API Migration Notes +* `axis.config.turns_per_step` changed to `axis.controller.config.steps_per_circular_range` +* `odrive.axis.fet_thermistor`, `odrive.axis.motor_thermistor` moved to `odrive.axis.motor` object +* `enable_uart` and `uart_baudrate` were renamed to `enable_uart0` and `uart0_baudrate`. +* `enable_i2c_instead_of_can` was replaced by the separate settings `enable_i2c0` and `enable_can0`. +* `.motor.gate_driver` was moved to `.gate_driver`. +* `.min_endstop.pullup` and `.max_endstop.pullup` were removed. Use `.config.gpioX_mode = GPIO_MODE_DIGITAL / GPIO_MODE_DIGITAL_PULL_UP / GPIO_MODE_DIGITAL_PULL_DOWN` instead. +* `.config.can_node_id` was moved to `.config.can.node_id` +* `.config.can_node_id_extended` was moved to `.config.can.is_extended` +* `.config.can_heartbeat_rate_ms` was moved to `.config.can.heartbeat_rate_ms` +* `.get_oscilloscope_val()` was moved to `.oscilloscope.get_val()`. +* Several error flags from `..error` were removed. Some were moved to `.error` and some are no longer relevant because implementation details changed. +* Several error flags from `..motor.error` were removed. Some were moved to `.error` and some are no longer relevant because implementation details changed. +* `.lockin_state` was removed as the lockin implementation was replaced by a more general open loop control block (currently not exposed on the API). +* `AXIS_STATE_SENSORLESS_CONTROL` was removed. Use `AXIS_STATE_CLOSED_LOOP_CONTROL` instead with `.enable_sensorless_mode = True`. +* `.config.startup_sensorless_control` was removed. Use `.config.startup_closed_loop_control` instead with `.enable_sensorless_mode = True`. +* `.clear_errors()` was replaced by the system-wide function `.clear_errors()`. +* `.armed_state` was replaced by `.is_armed`. +* Several properties in `.motor.current_control` were changed to read-only. +* `.motor.current_control.Ibus` was moved to `.motor.I_bus`. +* `.motor.current_control.max_allowed_current` was moved to `.motor.max_allowed_current`. +* `.motor.current_control.overcurrent_trip_level` was removed. +* `.motor.current_control.acim_rotor_flux` was moved to `.acim_estimator.rotor_flux`. +* `.motor.current_control.async_phase_vel` was moved to `.acim_estimator.stator_phase_vel`. +* `.motor.current_control.async_phase_offset` was moved to `.acim_estimator.phase`. +* `.motor.timing_log` was removed in favor of `.task_times` and `..task_times`. +* `.motor.config.direction` was moved to `.encoder.config.direction`. +* `.motor.config.acim_slip_velocity` was moved to `.acim_estimator.config.slip_velocity`. +* Several properties were changed to readonly. +* `.encoder.config.offset` was renamed to ``.encoder.config.phase_offset` +* `.encoder.config.offset_float` was renamed to ``.encoder.config.phase_offset_float` +* `.config.brake_resistance == 0.0` is no longer a valid way to disable the brake resistor. Use `.config.enable_brake_resistor` instead. A reboot is necessary for this to take effect. +* `.can.set_baud_rate()` was removed. The baudrate is now automatically updated when writing to `.can.config.baud_rate`. + # Releases ## [0.5.1] - 2020-09-27 ### Added @@ -18,6 +107,8 @@ Please add a note of your changes below this heading if you make a Pull Request. * `axis.motor.get_inverter_temp()`, `axis.motor.inverter_temp_limit_lower` and `axis.motor.inverter_temp_limit_upper` have been moved to seperate fet thermistor object under `axis.fet_thermistor`. `get_inverter_temp()` function has been renamed to `temp` and is now a read-only property. * `axis.config.counts_per_step` is now `axis.config.turns_per_step` * Outputs of `axis.sensorless_estimator` are now in turns/s instead of electrical rad/s +* Fixed bug of high current during lockin-ramp caused by `motor::update()` expecting a torque command instead of current +* Fixed bug where commanded velocity was extremely high just after sensorless ramp when using `input_mode` INPUT_MODE_VEL_RAMP caused by `vel_setpoint` and `axis.config.sensorless_ramp.vel` being in different units ### Fixed * Fixed bug of high current during lockin-ramp caused by `motor::update()` expecting a torque command instead of current diff --git a/Dockerfile b/Dockerfile index 9739928c7..8810dc238 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,16 +7,22 @@ RUN add-apt-repository ppa:team-gcc-arm-embedded/ppa RUN add-apt-repository ppa:jonathonf/tup RUN apt-get update RUN apt-get -y upgrade -RUN apt-get -y install gcc-arm-embedded openocd tup python3.7 build-essential git +RUN apt-get -y install gcc-arm-embedded openocd tup python3.7 python3-yaml python3-jinja2 python3-jsonschema build-essential git # Build step below does not know about debian's python naming schemme RUN ln -s /usr/bin/python3.7 /usr/bin/python -# Copy the firmware tree into the container -RUN mkdir ODrive -COPY . ODrive +RUN mkdir -p ODrive WORKDIR ODrive/Firmware -# Hack around Tup's dependency on FUSE -RUN tup generate build.sh -RUN ./build.sh +# Must attach the firmware tree into the container +CMD \ + # Regenerate python interface + python interface_generator_stub.py \ + --definitions odrive-interface.yaml \ + --template ../tools/enums_template.j2 \ + --output ../tools/odrive/enums.py && \ + # Hack around Tup's dependency on FUSE + tup init && \ + tup generate build.sh && \ + ./build.sh diff --git a/Firmware/.gitignore b/Firmware/.gitignore index a4c86dc20..abcbd1239 100644 --- a/Firmware/.gitignore +++ b/Firmware/.gitignore @@ -26,3 +26,6 @@ STM32CubeMX #gdb log openocd.log + +# OS-specific files +.DS_Store \ No newline at end of file diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index 1d5ab7cf9..5f4ddd828 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -3,21 +3,35 @@ { "name": "Win32", "includePath": [ + "${workspaceFolder}/Private/**", "${workspaceFolder}/**" ], "defines": [ - "STM32F405xx", + "__arm__", + "STM32F722xx", + "FPU_FPV5", "USE_HAL_DRIVER", - "HW_VERSION_MAJOR=3", - "HW_VERSION_MINOR=6", - "HW_VERSION_VOLTAGE=56", - "USB_PROTOCOL_NATIVE", + "HW_VERSION_MAJOR=4", + "HW_VERSION_MINOR=1", + "HW_VERSION_VOLTAGE=58", + "FIBRE_ENABLE_SERVER", + "FIBRE_ENABLE_CLIENT", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", "__GNUC__" ], - "intelliSenseMode": "gcc-x64", - "compilerPath": "\"${ARM_GCC_ROOT}/bin/arm-none-eabi-g++.exe\" -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float", + "intelliSenseMode": "gcc-arm", + "compilerPath": "arm-none-eabi-g++.exe", + "compilerArgs": [ + "-mthumb", + "-mcpu=cortex-m7", + "-mfpu=fpv5-sp-d16", + "-mfloat-abi=hard", + "-specs=nosys.specs", + "-specs=nano.specs", + "-u _printf_float", + "-u _scanf_float" + ], "cStandard": "c11", "cppStandard": "c++17" }, @@ -27,16 +41,20 @@ "${workspaceFolder}/**" ], "defines": [ + "__arm__", "STM32F405xx", + "FPU_FPV4", "USE_HAL_DRIVER", - "HW_VERSION_MAJOR=3", - "HW_VERSION_MINOR=6", - "HW_VERSION_VOLTAGE=56", + "HW_VERSION_MAJOR=4", + "HW_VERSION_MINOR=1", + "HW_VERSION_VOLTAGE=58", + "FIBRE_ENABLE_SERVER", + "FIBRE_ENABLE_CLIENT", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", "__GNUC__" ], - "intelliSenseMode": "gcc-x64", + "intelliSenseMode": "gcc-arm", "compilerPath": "arm-none-eabi-g++ -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float", "cStandard": "c11", "cppStandard": "c++17" @@ -47,16 +65,20 @@ "${workspaceFolder}/**" ], "defines": [ + "__arm__", "STM32F405xx", + "FPU_FPV4", "USE_HAL_DRIVER", - "HW_VERSION_MAJOR=3", - "HW_VERSION_MINOR=4", - "HW_VERSION_VOLTAGE=24", + "HW_VERSION_MAJOR=4", + "HW_VERSION_MINOR=1", + "HW_VERSION_VOLTAGE=58", + "FIBRE_ENABLE_SERVER", + "FIBRE_ENABLE_CLIENT", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", "__GNUC__" ], - "intelliSenseMode": "gcc-x64", + "intelliSenseMode": "gcc-arm", "cStandard": "c11", "cppStandard": "c++17" } diff --git a/Firmware/.vscode/launch.json b/Firmware/.vscode/launch.json index cc8662d30..48ee3ff1a 100644 --- a/Firmware/.vscode/launch.json +++ b/Firmware/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "cortex-debug", "servertype": "openocd", "request": "launch", - "name": "Debug ODrive - ST-Link", + "name": "Debug ODrive v3.x - ST-Link", "executable": "${workspaceRoot}/build/ODriveFirmware.elf", "configFiles": [ "interface/stlink-v2.cfg", @@ -23,7 +23,24 @@ "type": "cortex-debug", "servertype": "openocd", "request": "launch", - "name": "Debug ODrive - ST-Link - FreeRTOS", + "name": "Debug ODrive v4.x - ST-Link", + "executable": "${workspaceRoot}/build/ODriveFirmware.elf", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f7x.cfg", + ], + "openOCDLaunchCommands": [ + "reset_config none separate" + ], + "svdFile": "${workspaceRoot}/Private/v4/STM32F722.svd", + "cwd": "${workspaceRoot}" + }, + { + // For the Cortex-Debug extension + "type": "cortex-debug", + "servertype": "openocd", + "request": "launch", + "name": "Debug ODrive v3.x - ST-Link - FreeRTOS", "executable": "${workspaceRoot}/build/ODriveFirmware.elf", "rtos": "FreeRTOS", "configFiles": [ @@ -35,7 +52,7 @@ }, { // For the Cortex-Debug extension - // ssh -t odrv -L3333:localhost:3333 bash -c "\"openocd '-f' 'interface/stlink-v2.cfg' '-f' 'target/stm32f4x_stlink.cfg'\"" + // ssh -t odrv3 -L3333:localhost:3333 bash -c "\"openocd '-f' 'interface/stlink-v2.cfg' '-f' 'target/stm32f4x_stlink.cfg'\"" "type": "cortex-debug", "servertype": "external", "gdbTarget": "localhost:3333", @@ -43,7 +60,7 @@ "load" ], "request": "launch", - "name": "Debug ODrive via external server", + "name": "Debug ODrive v3.x - Remote", "executable": "${workspaceRoot}/build/ODriveFirmware.elf", "configFiles": [ "interface/stlink-v2.cfg", @@ -52,12 +69,31 @@ "svdFile": "${workspaceRoot}/Board/v3/STM32F40x.svd", "cwd": "${workspaceRoot}" }, + { + // For the Cortex-Debug extension + // ssh -t odrv4 -L3333:localhost:3333 bash -c "\"openocd '-f' 'interface/stlink.cfg' '-f' 'target/stm32f7x.cfg' -c 'reset_config none separate'\"" + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "preLaunchCommands": [ + "load" + ], + "request": "launch", + "name": "Debug ODrive v4.x - Remote", + "executable": "${workspaceRoot}/build/ODriveFirmware.elf", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f7x.cfg", + ], + "svdFile": "${workspaceRoot}/Private/v4/STM32F722.svd", + "cwd": "${workspaceRoot}" + }, { // For the Cortex-Debug extensions "type": "cortex-debug", "servertype": "bmp", "request": "launch", - "name": "Debug ODrive - Black Magic Probe", + "name": "Debug ODrive v3.x - Black Magic Probe", "executable": "${workspaceRoot}/build/ODriveFirmware.elf", "device": "STM32F4xx", "BMPGDBSerialPort": "${env:BMP_PORT}", diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_const_structs.h b/Firmware/Board/v3/Drivers/CMSIS/Include/arm_const_structs.h deleted file mode 100644 index 726d06eb6..000000000 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_const_structs.h +++ /dev/null @@ -1,79 +0,0 @@ -/* ---------------------------------------------------------------------- -* Copyright (C) 2010-2014 ARM Limited. All rights reserved. -* -* $Date: 19. March 2015 -* $Revision: V.1.4.5 -* -* Project: CMSIS DSP Library -* Title: arm_const_structs.h -* -* Description: This file has constant structs that are initialized for -* user convenience. For example, some can be given as -* arguments to the arm_cfft_f32() function. -* -* Target Processor: Cortex-M4/Cortex-M3 -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* - Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* - Neither the name of ARM LIMITED nor the names of its contributors -* may be used to endorse or promote products derived from this -* software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -* -------------------------------------------------------------------- */ - -#ifndef _ARM_CONST_STRUCTS_H -#define _ARM_CONST_STRUCTS_H - -#include "arm_math.h" -#include "arm_common_tables.h" - - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len16; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len32; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len64; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len128; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len256; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len512; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len1024; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len2048; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len4096; - - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len16; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len32; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len64; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len128; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len256; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len512; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len1024; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len2048; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096; - - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len16; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len32; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len64; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len128; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len256; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len512; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len1024; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len2048; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len4096; - -#endif diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h b/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h deleted file mode 100644 index 3e522777c..000000000 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h +++ /dev/null @@ -1,1373 +0,0 @@ -/**************************************************************************//** - * @file cmsis_gcc.h - * @brief CMSIS Cortex-M Core Function/Instruction Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#ifndef __CMSIS_GCC_H -#define __CMSIS_GCC_H - -/* ignore some GCC warnings */ -#if defined ( __GNUC__ ) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-conversion" -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif - - -/* ########################### Core Function Access ########################### */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions - @{ - */ - -/** - \brief Enable IRQ Interrupts - \details Enables IRQ interrupts by clearing the I-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_irq(void) -{ - __ASM volatile ("cpsie i" : : : "memory"); -} - - -/** - \brief Disable IRQ Interrupts - \details Disables IRQ interrupts by setting the I-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void) -{ - __ASM volatile ("cpsid i" : : : "memory"); -} - - -/** - \brief Get Control Register - \details Returns the content of the Control Register. - \return Control Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_CONTROL(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, control" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Control Register - \details Writes the given value to the Control Register. - \param [in] control Control Register value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_CONTROL(uint32_t control) -{ - __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); -} - - -/** - \brief Get IPSR Register - \details Returns the content of the IPSR Register. - \return IPSR Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_IPSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); - return(result); -} - - -/** - \brief Get APSR Register - \details Returns the content of the APSR Register. - \return APSR Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_APSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, apsr" : "=r" (result) ); - return(result); -} - - -/** - \brief Get xPSR Register - \details Returns the content of the xPSR Register. - - \return xPSR Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_xPSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); - return(result); -} - - -/** - \brief Get Process Stack Pointer - \details Returns the current value of the Process Stack Pointer (PSP). - \return PSP Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PSP(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, psp\n" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Process Stack Pointer - \details Assigns the given value to the Process Stack Pointer (PSP). - \param [in] topOfProcStack Process Stack Pointer value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) -{ - __ASM volatile ("MSR psp, %0\n" : : "r" (topOfProcStack) : ); -} - - -/** - \brief Get Main Stack Pointer - \details Returns the current value of the Main Stack Pointer (MSP). - \return MSP Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, msp\n" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Main Stack Pointer - \details Assigns the given value to the Main Stack Pointer (MSP). - - \param [in] topOfMainStack Main Stack Pointer value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) -{ - __ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : ); -} - - -/** - \brief Get Priority Mask - \details Returns the current state of the priority mask bit from the Priority Mask Register. - \return Priority Mask value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PRIMASK(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, primask" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Priority Mask - \details Assigns the given value to the Priority Mask Register. - \param [in] priMask Priority Mask - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) -{ - __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); -} - - -#if (__CORTEX_M >= 0x03U) - -/** - \brief Enable FIQ - \details Enables FIQ interrupts by clearing the F-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_fault_irq(void) -{ - __ASM volatile ("cpsie f" : : : "memory"); -} - - -/** - \brief Disable FIQ - \details Disables FIQ interrupts by setting the F-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_fault_irq(void) -{ - __ASM volatile ("cpsid f" : : : "memory"); -} - - -/** - \brief Get Base Priority - \details Returns the current value of the Base Priority register. - \return Base Priority register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_BASEPRI(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, basepri" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Base Priority - \details Assigns the given value to the Base Priority register. - \param [in] basePri Base Priority value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI(uint32_t value) -{ - __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); -} - - -/** - \brief Set Base Priority with condition - \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, - or the new value increases the BASEPRI priority level. - \param [in] basePri Base Priority value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI_MAX(uint32_t value) -{ - __ASM volatile ("MSR basepri_max, %0" : : "r" (value) : "memory"); -} - - -/** - \brief Get Fault Mask - \details Returns the current value of the Fault Mask register. - \return Fault Mask register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FAULTMASK(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Fault Mask - \details Assigns the given value to the Fault Mask register. - \param [in] faultMask Fault Mask value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) -{ - __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); -} - -#endif /* (__CORTEX_M >= 0x03U) */ - - -#if (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) - -/** - \brief Get FPSCR - \details Returns the current value of the Floating Point Status/Control register. - \return Floating Point Status/Control register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FPSCR(void) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - uint32_t result; - - /* Empty asm statement works as a scheduling barrier */ - __ASM volatile (""); - __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); - __ASM volatile (""); - return(result); -#else - return(0); -#endif -} - - -/** - \brief Set FPSCR - \details Assigns the given value to the Floating Point Status/Control register. - \param [in] fpscr Floating Point Status/Control value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - /* Empty asm statement works as a scheduling barrier */ - __ASM volatile (""); - __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); - __ASM volatile (""); -#endif -} - -#endif /* (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) */ - - - -/*@} end of CMSIS_Core_RegAccFunctions */ - - -/* ########################## Core Instruction Access ######################### */ -/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface - Access to dedicated instructions - @{ -*/ - -/* Define macros for porting to both thumb1 and thumb2. - * For thumb1, use low register (r0-r7), specified by constraint "l" - * Otherwise, use general registers, specified by constraint "r" */ -#if defined (__thumb__) && !defined (__thumb2__) -#define __CMSIS_GCC_OUT_REG(r) "=l" (r) -#define __CMSIS_GCC_USE_REG(r) "l" (r) -#else -#define __CMSIS_GCC_OUT_REG(r) "=r" (r) -#define __CMSIS_GCC_USE_REG(r) "r" (r) -#endif - -/** - \brief No Operation - \details No Operation does nothing. This instruction can be used for code alignment purposes. - */ -__attribute__((always_inline)) __STATIC_INLINE void __NOP(void) -{ - __ASM volatile ("nop"); -} - - -/** - \brief Wait For Interrupt - \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. - */ -__attribute__((always_inline)) __STATIC_INLINE void __WFI(void) -{ - __ASM volatile ("wfi"); -} - - -/** - \brief Wait For Event - \details Wait For Event is a hint instruction that permits the processor to enter - a low-power state until one of a number of events occurs. - */ -__attribute__((always_inline)) __STATIC_INLINE void __WFE(void) -{ - __ASM volatile ("wfe"); -} - - -/** - \brief Send Event - \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. - */ -__attribute__((always_inline)) __STATIC_INLINE void __SEV(void) -{ - __ASM volatile ("sev"); -} - - -/** - \brief Instruction Synchronization Barrier - \details Instruction Synchronization Barrier flushes the pipeline in the processor, - so that all instructions following the ISB are fetched from cache or memory, - after the instruction has been completed. - */ -__attribute__((always_inline)) __STATIC_INLINE void __ISB(void) -{ - __ASM volatile ("isb 0xF":::"memory"); -} - - -/** - \brief Data Synchronization Barrier - \details Acts as a special kind of Data Memory Barrier. - It completes when all explicit memory accesses before this instruction complete. - */ -__attribute__((always_inline)) __STATIC_INLINE void __DSB(void) -{ - __ASM volatile ("dsb 0xF":::"memory"); -} - - -/** - \brief Data Memory Barrier - \details Ensures the apparent order of the explicit memory operations before - and after the instruction, without ensuring their completion. - */ -__attribute__((always_inline)) __STATIC_INLINE void __DMB(void) -{ - __ASM volatile ("dmb 0xF":::"memory"); -} - - -/** - \brief Reverse byte order (32 bit) - \details Reverses the byte order in integer value. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV(uint32_t value) -{ -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) - return __builtin_bswap32(value); -#else - uint32_t result; - - __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -#endif -} - - -/** - \brief Reverse byte order (16 bit) - \details Reverses the byte order in two unsigned short values. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV16(uint32_t value) -{ - uint32_t result; - - __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} - - -/** - \brief Reverse byte order in signed short value - \details Reverses the byte order in a signed short value with sign extension to integer. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE int32_t __REVSH(int32_t value) -{ -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - return (short)__builtin_bswap16(value); -#else - int32_t result; - - __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -#endif -} - - -/** - \brief Rotate Right in unsigned value (32 bit) - \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. - \param [in] value Value to rotate - \param [in] value Number of Bits to rotate - \return Rotated value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint32_t op2) -{ - return (op1 >> op2) | (op1 << (32U - op2)); -} - - -/** - \brief Breakpoint - \details Causes the processor to enter Debug state. - Debug tools can use this to investigate system state when the instruction at a particular address is reached. - \param [in] value is ignored by the processor. - If required, a debugger can use it to store additional information about the breakpoint. - */ -#define __BKPT(value) __ASM volatile ("bkpt "#value) - - -/** - \brief Reverse bit order of value - \details Reverses the bit order of the given value. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) -{ - uint32_t result; - -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) - __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); -#else - int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */ - - result = value; /* r will be reversed bits of v; first get LSB of v */ - for (value >>= 1U; value; value >>= 1U) - { - result <<= 1U; - result |= value & 1U; - s--; - } - result <<= s; /* shift when v's highest bits are zero */ -#endif - return(result); -} - - -/** - \brief Count leading zeros - \details Counts the number of leading zeros of a data value. - \param [in] value Value to count the leading zeros - \return number of leading zeros in value - */ -#define __CLZ __builtin_clz - - -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) - -/** - \brief LDR Exclusive (8 bit) - \details Executes a exclusive LDR instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDREXB(volatile uint8_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint8_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDR Exclusive (16 bit) - \details Executes a exclusive LDR instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDREXH(volatile uint16_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint16_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDR Exclusive (32 bit) - \details Executes a exclusive LDR instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDREXW(volatile uint32_t *addr) -{ - uint32_t result; - - __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); - return(result); -} - - -/** - \brief STR Exclusive (8 bit) - \details Executes a exclusive STR instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) -{ - uint32_t result; - - __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); - return(result); -} - - -/** - \brief STR Exclusive (16 bit) - \details Executes a exclusive STR instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) -{ - uint32_t result; - - __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); - return(result); -} - - -/** - \brief STR Exclusive (32 bit) - \details Executes a exclusive STR instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) -{ - uint32_t result; - - __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); - return(result); -} - - -/** - \brief Remove the exclusive lock - \details Removes the exclusive lock which is created by LDREX. - */ -__attribute__((always_inline)) __STATIC_INLINE void __CLREX(void) -{ - __ASM volatile ("clrex" ::: "memory"); -} - - -/** - \brief Signed Saturate - \details Saturates a signed value. - \param [in] value Value to be saturated - \param [in] sat Bit position to saturate to (1..32) - \return Saturated value - */ -#define __SSAT(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - - -/** - \brief Unsigned Saturate - \details Saturates an unsigned value. - \param [in] value Value to be saturated - \param [in] sat Bit position to saturate to (0..31) - \return Saturated value - */ -#define __USAT(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - - -/** - \brief Rotate Right with Extend (32 bit) - \details Moves each bit of a bitstring right by one bit. - The carry input is shifted in at the left end of the bitstring. - \param [in] value Value to rotate - \return Rotated value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RRX(uint32_t value) -{ - uint32_t result; - - __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} - - -/** - \brief LDRT Unprivileged (8 bit) - \details Executes a Unprivileged LDRT instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDRBT(volatile uint8_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint8_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDRT Unprivileged (16 bit) - \details Executes a Unprivileged LDRT instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDRHT(volatile uint16_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint16_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDRT Unprivileged (32 bit) - \details Executes a Unprivileged LDRT instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDRT(volatile uint32_t *addr) -{ - uint32_t result; - - __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*addr) ); - return(result); -} - - -/** - \brief STRT Unprivileged (8 bit) - \details Executes a Unprivileged STRT instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRBT(uint8_t value, volatile uint8_t *addr) -{ - __ASM volatile ("strbt %1, %0" : "=Q" (*addr) : "r" ((uint32_t)value) ); -} - - -/** - \brief STRT Unprivileged (16 bit) - \details Executes a Unprivileged STRT instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRHT(uint16_t value, volatile uint16_t *addr) -{ - __ASM volatile ("strht %1, %0" : "=Q" (*addr) : "r" ((uint32_t)value) ); -} - - -/** - \brief STRT Unprivileged (32 bit) - \details Executes a Unprivileged STRT instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRT(uint32_t value, volatile uint32_t *addr) -{ - __ASM volatile ("strt %1, %0" : "=Q" (*addr) : "r" (value) ); -} - -#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */ - -/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ - - -/* ################### Compiler specific Intrinsics ########################### */ -/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics - Access to dedicated SIMD instructions - @{ -*/ - -#if (__CORTEX_M >= 0x04U) /* only for Cortex-M4 and above */ - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -#define __SSAT16(ARG1,ARG2) \ -({ \ - int32_t __RES, __ARG1 = (ARG1); \ - __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - -#define __USAT16(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UXTB16(uint32_t op1) -{ - uint32_t result; - - __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SXTB16(uint32_t op1) -{ - uint32_t result; - - __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SEL (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __QADD( int32_t op1, int32_t op2) -{ - int32_t result; - - __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __QSUB( int32_t op1, int32_t op2) -{ - int32_t result; - - __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -#define __PKHBT(ARG1,ARG2,ARG3) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ - __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ - __RES; \ - }) - -#define __PKHTB(ARG1,ARG2,ARG3) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ - if (ARG3 == 0) \ - __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ - else \ - __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ - __RES; \ - }) - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) -{ - int32_t result; - - __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -#endif /* (__CORTEX_M >= 0x04) */ -/*@} end of group CMSIS_SIMD_intrinsics */ - - -#if defined ( __GNUC__ ) -#pragma GCC diagnostic pop -#endif - -#endif /* __CMSIS_GCC_H */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmFunc.h b/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmFunc.h deleted file mode 100644 index 652a48af0..000000000 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmFunc.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************//** - * @file core_cmFunc.h - * @brief CMSIS Cortex-M Core Function Access Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CMFUNC_H -#define __CORE_CMFUNC_H - - -/* ########################### Core Function Access ########################### */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions - @{ -*/ - -/*------------------ RealView Compiler -----------------*/ -#if defined ( __CC_ARM ) - #include "cmsis_armcc.h" - -/*------------------ ARM Compiler V6 -------------------*/ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #include "cmsis_armcc_V6.h" - -/*------------------ GNU Compiler ----------------------*/ -#elif defined ( __GNUC__ ) - #include "cmsis_gcc.h" - -/*------------------ ICC Compiler ----------------------*/ -#elif defined ( __ICCARM__ ) - #include - -/*------------------ TI CCS Compiler -------------------*/ -#elif defined ( __TMS470__ ) - #include - -/*------------------ TASKING Compiler ------------------*/ -#elif defined ( __TASKING__ ) - /* - * The CMSIS functions have been implemented as intrinsics in the compiler. - * Please use "carm -?i" to get an up to date list of all intrinsics, - * Including the CMSIS ones. - */ - -/*------------------ COSMIC Compiler -------------------*/ -#elif defined ( __CSMC__ ) - #include - -#endif - -/*@} end of CMSIS_Core_RegAccFunctions */ - -#endif /* __CORE_CMFUNC_H */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmInstr.h b/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmInstr.h deleted file mode 100644 index f474b0e6f..000000000 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmInstr.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************//** - * @file core_cmInstr.h - * @brief CMSIS Cortex-M Core Instruction Access Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CMINSTR_H -#define __CORE_CMINSTR_H - - -/* ########################## Core Instruction Access ######################### */ -/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface - Access to dedicated instructions - @{ -*/ - -/*------------------ RealView Compiler -----------------*/ -#if defined ( __CC_ARM ) - #include "cmsis_armcc.h" - -/*------------------ ARM Compiler V6 -------------------*/ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #include "cmsis_armcc_V6.h" - -/*------------------ GNU Compiler ----------------------*/ -#elif defined ( __GNUC__ ) - #include "cmsis_gcc.h" - -/*------------------ ICC Compiler ----------------------*/ -#elif defined ( __ICCARM__ ) - #include - -/*------------------ TI CCS Compiler -------------------*/ -#elif defined ( __TMS470__ ) - #include - -/*------------------ TASKING Compiler ------------------*/ -#elif defined ( __TASKING__ ) - /* - * The CMSIS functions have been implemented as intrinsics in the compiler. - * Please use "carm -?i" to get an up to date list of all intrinsics, - * Including the CMSIS ones. - */ - -/*------------------ COSMIC Compiler -------------------*/ -#elif defined ( __CSMC__ ) - #include - -#endif - -/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ - -#endif /* __CORE_CMINSTR_H */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmSimd.h b/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmSimd.h deleted file mode 100644 index 66bf5c2a7..000000000 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmSimd.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************//** - * @file core_cmSimd.h - * @brief CMSIS Cortex-M SIMD Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CMSIMD_H -#define __CORE_CMSIMD_H - -#ifdef __cplusplus - extern "C" { -#endif - - -/* ################### Compiler specific Intrinsics ########################### */ -/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics - Access to dedicated SIMD instructions - @{ -*/ - -/*------------------ RealView Compiler -----------------*/ -#if defined ( __CC_ARM ) - #include "cmsis_armcc.h" - -/*------------------ ARM Compiler V6 -------------------*/ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #include "cmsis_armcc_V6.h" - -/*------------------ GNU Compiler ----------------------*/ -#elif defined ( __GNUC__ ) - #include "cmsis_gcc.h" - -/*------------------ ICC Compiler ----------------------*/ -#elif defined ( __ICCARM__ ) - #include - -/*------------------ TI CCS Compiler -------------------*/ -#elif defined ( __TMS470__ ) - #include - -/*------------------ TASKING Compiler ------------------*/ -#elif defined ( __TASKING__ ) - /* - * The CMSIS functions have been implemented as intrinsics in the compiler. - * Please use "carm -?i" to get an up to date list of all intrinsics, - * Including the CMSIS ones. - */ - -/*------------------ COSMIC Compiler -------------------*/ -#elif defined ( __CSMC__ ) - #include - -#endif - -/*@} end of group CMSIS_SIMD_intrinsics */ - - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CMSIMD_H */ diff --git a/Firmware/Board/v3/Inc/adc.h b/Firmware/Board/v3/Inc/adc.h index 0f3d0688e..87b2610c2 100644 --- a/Firmware/Board/v3/Inc/adc.h +++ b/Firmware/Board/v3/Inc/adc.h @@ -77,8 +77,6 @@ void MX_ADC3_Init(void); /* USER CODE BEGIN Prototypes */ -float read_ADC_volts(ADC_HandleTypeDef* hadc, uint8_t injected_rank); - /* USER CODE END Prototypes */ #ifdef __cplusplus diff --git a/Firmware/Board/v3/Inc/board.h b/Firmware/Board/v3/Inc/board.h new file mode 100644 index 000000000..db6adcf72 --- /dev/null +++ b/Firmware/Board/v3/Inc/board.h @@ -0,0 +1,143 @@ +/* +* @brief Contains board specific configuration for ODrive v3.x +*/ + +#ifndef __BOARD_CONFIG_H +#define __BOARD_CONFIG_H + +#include + +// STM specific includes +#include +#include +#include +#include +#include +#include +#include +#include +#include "cmsis_os.h" + +#include + +#include + +#if HW_VERSION_MINOR <= 3 +#define SHUNT_RESISTANCE (675e-6f) +#else +#define SHUNT_RESISTANCE (500e-6f) +#endif + +#define AXIS_COUNT (2) + +// Total count of GPIOs, including encoder pins, CAN pins and a dummy GPIO0. +// ODrive v3.4 and earlier don't have GPIOs 6, 7 and 8 but to keep the numbering +// consistent we just leave a gap in the counting scheme. +#define GPIO_COUNT (17) + +#define CAN_FREQ (2000000UL) + +#if HW_VERSION_MINOR >= 5 && HW_VERSION_VOLTAGE >= 48 +#define DEFAULT_BRAKE_RESISTANCE (2.0f) // [ohm] +#else +#define DEFAULT_BRAKE_RESISTANCE (0.47f) // [ohm] +#endif + +#define DEFAULT_ERROR_PIN 0 +#define DEFAULT_MIN_DC_VOLTAGE 8.0f + +#define DEFAULT_GPIO_MODES \ + ODriveIntf::GPIO_MODE_DIGITAL, \ + ODriveIntf::GPIO_MODE_UART_A, \ + ODriveIntf::GPIO_MODE_UART_A, \ + ODriveIntf::GPIO_MODE_ANALOG_IN, \ + ODriveIntf::GPIO_MODE_ANALOG_IN, \ + ODriveIntf::GPIO_MODE_ANALOG_IN, \ + ODriveIntf::GPIO_MODE_DIGITAL, \ + ODriveIntf::GPIO_MODE_DIGITAL, \ + ODriveIntf::GPIO_MODE_DIGITAL, \ + ODriveIntf::GPIO_MODE_ENC0, \ + ODriveIntf::GPIO_MODE_ENC0, \ + ODriveIntf::GPIO_MODE_DIGITAL_PULL_DOWN, \ + ODriveIntf::GPIO_MODE_ENC1, \ + ODriveIntf::GPIO_MODE_ENC1, \ + ODriveIntf::GPIO_MODE_DIGITAL_PULL_DOWN, \ + ODriveIntf::GPIO_MODE_CAN_A, \ + ODriveIntf::GPIO_MODE_CAN_A, + +#define TIM_TIME_BASE TIM14 + +// Run control loop at the same frequency as the current measurements. +#define CONTROL_TIMER_PERIOD_TICKS (2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) + +#define TIM1_INIT_COUNT (TIM_1_8_PERIOD_CLOCKS / 2 - 1 * 128) // TODO: explain why this offset + +// The delta from the control loop timestamp to the current sense timestamp is +// exactly 0 for M0 and TIM1_INIT_COUNT for M1. +#define MAX_CONTROL_LOOP_UPDATE_TO_CURRENT_UPDATE_DELTA (TIM_1_8_PERIOD_CLOCKS / 2 + 1 * 128) + +#ifdef __cplusplus +#include +#include +#include +#include +#include + +using TGateDriver = Drv8301; +using TOpAmp = Drv8301; + +#include +#include + +extern std::array axes; +extern Motor motors[AXIS_COUNT]; +extern OnboardThermistorCurrentLimiter fet_thermistors[AXIS_COUNT]; +extern Encoder encoders[AXIS_COUNT]; +extern Stm32Gpio gpios[GPIO_COUNT]; + +struct GpioFunction { int mode = 0; uint8_t alternate_function = 0xff; }; +extern std::array alternate_functions[GPIO_COUNT]; + +extern USBD_HandleTypeDef& usb_dev_handle; + +extern Stm32SpiArbiter& ext_spi_arbiter; + +extern UART_HandleTypeDef* uart_a; +extern UART_HandleTypeDef* uart_b; +extern UART_HandleTypeDef* uart_c; + +extern PwmInput pwm0_input; +#endif + +// Period in [s] +#define CURRENT_MEAS_PERIOD ( (float)2*TIM_1_8_PERIOD_CLOCKS*(TIM_1_8_RCR+1) / (float)TIM_1_8_CLOCK_HZ ) +static const float current_meas_period = CURRENT_MEAS_PERIOD; + +// Frequency in [Hz] +#define CURRENT_MEAS_HZ ( (float)(TIM_1_8_CLOCK_HZ) / (float)(2*TIM_1_8_PERIOD_CLOCKS*(TIM_1_8_RCR+1)) ) +static const int current_meas_hz = CURRENT_MEAS_HZ; + +#if HW_VERSION_VOLTAGE >= 48 +#define VBUS_S_DIVIDER_RATIO 19.0f +#elif HW_VERSION_VOLTAGE == 24 +#define VBUS_S_DIVIDER_RATIO 11.0f +#else +#error "unknown board voltage" +#endif + +// Linear range of the DRV8301 opamp output: 0.3V...5.7V. We set the upper limit +// to 3.0V so that it's symmetric around the center point of 1.65V. +#define CURRENT_SENSE_MIN_VOLT 0.3f +#define CURRENT_SENSE_MAX_VOLT 3.0f + +// This board has no board-specific user configurations +static inline bool board_read_config() { return true; } +static inline bool board_write_config() { return true; } +static inline void board_clear_config() { } +static inline bool board_apply_config() { return true; } + +void system_init(); +bool board_init(); +void start_timers(); + +#endif // __BOARD_CONFIG_H diff --git a/Firmware/Board/v3/Inc/gpio.h b/Firmware/Board/v3/Inc/gpio.h index 81f90be91..dfd53ac72 100644 --- a/Firmware/Board/v3/Inc/gpio.h +++ b/Firmware/Board/v3/Inc/gpio.h @@ -59,7 +59,6 @@ #include "main.h" /* USER CODE BEGIN Includes */ -#include /* USER CODE END Includes */ /* USER CODE BEGIN Private defines */ @@ -70,19 +69,6 @@ void MX_GPIO_Init(void); /* USER CODE BEGIN Prototypes */ -void SetGPIO12toUART(); -bool GPIO_subscribe(GPIO_TypeDef* GPIO_port, uint16_t GPIO_pin, - uint32_t pull_up_down, void (*callback)(void*), void* ctx); -void GPIO_unsubscribe(GPIO_TypeDef* GPIO_port, uint16_t GPIO_pin); -void GPIO_set_to_analog(GPIO_TypeDef* GPIO_port, uint16_t GPIO_pin); - - -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR <= 4 -#define GPIO_COUNT 5 -#else -#define GPIO_COUNT 8 -#endif - /* USER CODE END Prototypes */ #ifdef __cplusplus diff --git a/Firmware/Board/v3/Inc/main.h b/Firmware/Board/v3/Inc/main.h index 6f663846f..26d75a060 100644 --- a/Firmware/Board/v3/Inc/main.h +++ b/Firmware/Board/v3/Inc/main.h @@ -164,21 +164,6 @@ /* USER CODE BEGIN Private defines */ #endif - -//TODO: make this come automatically out of CubeMX somehow -#define TIM_TIME_BASE TIM14 - -#define CURRENT_MEAS_PERIOD ( (float)2*TIM_1_8_PERIOD_CLOCKS*(TIM_1_8_RCR+1) / (float)TIM_1_8_CLOCK_HZ ) -#define CURRENT_MEAS_HZ ( (float)(TIM_1_8_CLOCK_HZ) / (float)(2*TIM_1_8_PERIOD_CLOCKS*(TIM_1_8_RCR+1)) ) - -#if HW_VERSION_VOLTAGE >= 48 -#define VBUS_S_DIVIDER_RATIO 19.0f -#elif HW_VERSION_VOLTAGE == 24 -#define VBUS_S_DIVIDER_RATIO 11.0f -#else -#error "unknown board voltage" -#endif - /* USER CODE END Private defines */ #ifdef __cplusplus diff --git a/Firmware/Board/v3/Inc/stm32f4xx_hal_conf.h b/Firmware/Board/v3/Inc/stm32f4xx_hal_conf.h index ed26bf394..75e7b9daa 100644 --- a/Firmware/Board/v3/Inc/stm32f4xx_hal_conf.h +++ b/Firmware/Board/v3/Inc/stm32f4xx_hal_conf.h @@ -158,7 +158,7 @@ * @brief This is the HAL system configuration section */ #define VDD_VALUE ((uint32_t)3300U) /*!< Value of VDD in mv */ -#define TICK_INT_PRIORITY ((uint32_t)0U) /*!< tick interrupt priority */ +#define TICK_INT_PRIORITY ((uint32_t)6U) /*!< tick interrupt priority */ #define USE_RTOS 0U #define PREFETCH_ENABLE 1U #define INSTRUCTION_CACHE_ENABLE 1U diff --git a/Firmware/Board/v3/Inc/stm32f4xx_it.h b/Firmware/Board/v3/Inc/stm32f4xx_it.h index 784a33339..880a132ad 100644 --- a/Firmware/Board/v3/Inc/stm32f4xx_it.h +++ b/Firmware/Board/v3/Inc/stm32f4xx_it.h @@ -58,11 +58,14 @@ void DMA1_Stream0_IRQHandler(void); void DMA1_Stream2_IRQHandler(void); void DMA1_Stream4_IRQHandler(void); void DMA1_Stream5_IRQHandler(void); -void ADC_IRQHandler(void); +void DMA1_Stream6_IRQHandler(void); +void DMA1_Stream7_IRQHandler(void); +//void ADC_IRQHandler(void); void CAN1_TX_IRQHandler(void); void CAN1_RX0_IRQHandler(void); void CAN1_RX1_IRQHandler(void); void CAN1_SCE_IRQHandler(void); +void USART2_IRQHandler(void); void TIM8_TRG_COM_TIM14_IRQHandler(void); void TIM5_IRQHandler(void); void SPI3_IRQHandler(void); diff --git a/Firmware/Board/v3/Inc/tim.h b/Firmware/Board/v3/Inc/tim.h index a4585c53a..4d2977b01 100644 --- a/Firmware/Board/v3/Inc/tim.h +++ b/Firmware/Board/v3/Inc/tim.h @@ -87,8 +87,6 @@ void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim); /* USER CODE BEGIN Prototypes */ -void OC4_PWM_Override(TIM_HandleTypeDef* htim); - /* USER CODE END Prototypes */ #ifdef __cplusplus diff --git a/Firmware/Board/v3/Inc/usart.h b/Firmware/Board/v3/Inc/usart.h index c987f7f21..ff3d48b8d 100644 --- a/Firmware/Board/v3/Inc/usart.h +++ b/Firmware/Board/v3/Inc/usart.h @@ -62,6 +62,7 @@ /* USER CODE END Includes */ extern UART_HandleTypeDef huart4; +extern UART_HandleTypeDef huart2; /* USER CODE BEGIN Private defines */ @@ -70,6 +71,7 @@ extern UART_HandleTypeDef huart4; extern void _Error_Handler(char *, int); void MX_UART4_Init(void); +void MX_USART2_UART_Init(void); /* USER CODE BEGIN Prototypes */ diff --git a/Firmware/Board/v3/Inc/usbd_conf.h b/Firmware/Board/v3/Inc/usbd_conf.h index dbf800b3d..aecb8d1ac 100644 --- a/Firmware/Board/v3/Inc/usbd_conf.h +++ b/Firmware/Board/v3/Inc/usbd_conf.h @@ -98,7 +98,7 @@ /*---------- -----------*/ #define USBD_MAX_STR_DESC_SIZ 512 /*---------- -----------*/ -#define USBD_SUPPORT_USER_STRING 1 +#define USBD_SUPPORT_USER_STRING_DESC 1 /*---------- -----------*/ #define USBD_DEBUG_LEVEL 0 /*---------- -----------*/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h deleted file mode 100644 index c029e22a7..000000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h +++ /dev/null @@ -1,188 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_cdc.h - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief header file for the usbd_cdc.c file. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (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.st.com/software_license_agreement_liberty_v2 - * - * 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. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USB_CDC_H -#define __USB_CDC_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_ioreq.h" - -/** @addtogroup STM32_USB_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup usbd_cdc - * @brief This file is the Header file for usbd_cdc.c - * @{ - */ - - -/** @defgroup usbd_cdc_Exported_Defines - * @{ - */ -#define CDC_IN_EP 0x81 /* EP1 for data IN */ -#define CDC_OUT_EP 0x01 /* EP1 for data OUT */ -#define CDC_CMD_EP 0x82 /* EP2 for CDC commands */ -#define ODRIVE_IN_EP 0x83 /* EP3 IN: ODrive device TX endpoint */ -#define ODRIVE_OUT_EP 0x03 /* EP3 OUT: ODrive device RX endpoint */ - -/* CDC Endpoints parameters: you can fine tune these values depending on the needed baudrates and performance. */ -#define CDC_DATA_HS_MAX_PACKET_SIZE 64 /* Endpoint IN & OUT Packet size */ -#define CDC_DATA_FS_MAX_PACKET_SIZE 64 /* Endpoint IN & OUT Packet size */ -#define CDC_CMD_PACKET_SIZE 8 /* Control Endpoint Packet size */ - -#define USB_CDC_CONFIG_DESC_SIZ (67 + 39) -#define CDC_DATA_HS_IN_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE -#define CDC_DATA_HS_OUT_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE - -#define CDC_DATA_FS_IN_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE -#define CDC_DATA_FS_OUT_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE - -/*---------------------------------------------------------------------*/ -/* CDC definitions */ -/*---------------------------------------------------------------------*/ -#define CDC_SEND_ENCAPSULATED_COMMAND 0x00 -#define CDC_GET_ENCAPSULATED_RESPONSE 0x01 -#define CDC_SET_COMM_FEATURE 0x02 -#define CDC_GET_COMM_FEATURE 0x03 -#define CDC_CLEAR_COMM_FEATURE 0x04 -#define CDC_SET_LINE_CODING 0x20 -#define CDC_GET_LINE_CODING 0x21 -#define CDC_SET_CONTROL_LINE_STATE 0x22 -#define CDC_SEND_BREAK 0x23 - -/** - * @} - */ - - -/** @defgroup USBD_CORE_Exported_TypesDefinitions - * @{ - */ - -/** - * @} - */ -typedef struct -{ - uint32_t bitrate; - uint8_t format; - uint8_t paritytype; - uint8_t datatype; -}USBD_CDC_LineCodingTypeDef; - -typedef struct _USBD_CDC_Itf -{ - int8_t (* Init) (void); - int8_t (* DeInit) (void); - int8_t (* Control) (uint8_t, uint8_t * , uint16_t); - int8_t (* Receive) (uint8_t *, uint32_t *, uint8_t); - -}USBD_CDC_ItfTypeDef; - -typedef struct -{ - uint8_t* Buffer; - uint32_t Length; - volatile uint8_t State; -} -USBD_CDC_EP_HandleTypeDef; - -typedef struct -{ - uint32_t data[CDC_DATA_HS_MAX_PACKET_SIZE/4]; /* Force 32bits alignment */ - uint8_t CmdOpCode; - uint8_t CmdLength; - - USBD_CDC_EP_HandleTypeDef CDC_Tx; - USBD_CDC_EP_HandleTypeDef CDC_Rx; - - USBD_CDC_EP_HandleTypeDef ODRIVE_Tx; - USBD_CDC_EP_HandleTypeDef ODRIVE_Rx; -} -USBD_CDC_HandleTypeDef; - - - -/** @defgroup USBD_CORE_Exported_Macros - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_CORE_Exported_Variables - * @{ - */ - -extern USBD_ClassTypeDef USBD_CDC; -#define USBD_CDC_CLASS &USBD_CDC -/** - * @} - */ - -/** @defgroup USB_CORE_Exported_Functions - * @{ - */ -uint8_t USBD_CDC_RegisterInterface (USBD_HandleTypeDef *pdev, - USBD_CDC_ItfTypeDef *fops); - -uint8_t USBD_CDC_SetTxBuffer (USBD_HandleTypeDef *pdev, - uint8_t *pbuff, - uint16_t length, - uint8_t endpoint_pair); - -uint8_t USBD_CDC_SetRxBuffer (USBD_HandleTypeDef *pdev, - uint8_t *pbuff, uint8_t endpoint_pair); - -uint8_t USBD_CDC_ReceivePacket (USBD_HandleTypeDef *pdev, uint8_t endpoint_pair); - -uint8_t USBD_CDC_TransmitPacket (USBD_HandleTypeDef *pdev, uint8_t endpoint_pair); -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USB_CDC_H */ -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_core.h b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_core.h deleted file mode 100644 index 013a5c14a..000000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_core.h +++ /dev/null @@ -1,167 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_core.h - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief Header file for usbd_core.c file - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (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.st.com/software_license_agreement_liberty_v2 - * - * 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. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USBD_CORE_H -#define __USBD_CORE_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_conf.h" -#include "usbd_def.h" -#include "usbd_ioreq.h" -#include "usbd_ctlreq.h" - -/** @addtogroup STM32_USB_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup USBD_CORE - * @brief This file is the Header file for usbd_core.c file - * @{ - */ - - -/** @defgroup USBD_CORE_Exported_Defines - * @{ - */ - -/** - * @} - */ - - -/** @defgroup USBD_CORE_Exported_TypesDefinitions - * @{ - */ - - -/** - * @} - */ - - - -/** @defgroup USBD_CORE_Exported_Macros - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_CORE_Exported_Variables - * @{ - */ -#define USBD_SOF USBD_LL_SOF -/** - * @} - */ - -/** @defgroup USBD_CORE_Exported_FunctionsPrototype - * @{ - */ -USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id); -USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_Start (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_Stop (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass); - -USBD_StatusTypeDef USBD_RunTestMode (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx); -USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx); - -USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup); -USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev , uint8_t epnum, uint8_t *pdata); -USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev , uint8_t epnum, uint8_t *pdata); - -USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, USBD_SpeedTypeDef speed); -USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev); - -USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); -USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); - -USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev); - -/* USBD Low Level Driver */ -USBD_StatusTypeDef USBD_LL_Init (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_DeInit (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_Stop (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_OpenEP (USBD_HandleTypeDef *pdev, - uint8_t ep_addr, - uint8_t ep_type, - uint16_t ep_mps); - -USBD_StatusTypeDef USBD_LL_CloseEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -USBD_StatusTypeDef USBD_LL_FlushEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -USBD_StatusTypeDef USBD_LL_StallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -USBD_StatusTypeDef USBD_LL_ClearStallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -uint8_t USBD_LL_IsStallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -USBD_StatusTypeDef USBD_LL_SetUSBAddress (USBD_HandleTypeDef *pdev, uint8_t dev_addr); -USBD_StatusTypeDef USBD_LL_Transmit (USBD_HandleTypeDef *pdev, - uint8_t ep_addr, - uint8_t *pbuf, - uint16_t size); - -USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, - uint8_t ep_addr, - uint8_t *pbuf, - uint16_t size); - -uint32_t USBD_LL_GetRxDataSize (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -void USBD_LL_Delay (uint32_t Delay); - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USBD_CORE_H */ - -/** - * @} - */ - -/** -* @} -*/ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ - - - diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h deleted file mode 100644 index bf8825227..000000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h +++ /dev/null @@ -1,113 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_req.h - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief Header file for the usbd_req.c file - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (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.st.com/software_license_agreement_liberty_v2 - * - * 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. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USB_REQUEST_H -#define __USB_REQUEST_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_def.h" - - -/** @addtogroup STM32_USB_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup USBD_REQ - * @brief header file for the usbd_req.c file - * @{ - */ - -/** @defgroup USBD_REQ_Exported_Defines - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_REQ_Exported_Types - * @{ - */ -/** - * @} - */ - - - -/** @defgroup USBD_REQ_Exported_Macros - * @{ - */ -/** - * @} - */ - -/** @defgroup USBD_REQ_Exported_Variables - * @{ - */ -/** - * @} - */ - -/** @defgroup USBD_REQ_Exported_FunctionsPrototype - * @{ - */ - -USBD_StatusTypeDef USBD_StdDevReq (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); -USBD_StatusTypeDef USBD_StdItfReq (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); -USBD_StatusTypeDef USBD_StdEPReq (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); - - -void USBD_CtlError (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); - -void USBD_ParseSetupRequest (USBD_SetupReqTypedef *req, uint8_t *pdata); - -void USBD_GetString (uint8_t *desc, uint8_t *unicode, uint16_t *len); -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USB_REQUEST_H */ - -/** - * @} - */ - -/** -* @} -*/ - - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_def.h b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_def.h deleted file mode 100644 index f259b51d2..000000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_def.h +++ /dev/null @@ -1,332 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_def.h - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief General defines for the usb device library - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (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.st.com/software_license_agreement_liberty_v2 - * - * 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. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USBD_DEF_H -#define __USBD_DEF_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_conf.h" - -/** @addtogroup STM32_USBD_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup USB_DEF - * @brief general defines for the usb device library file - * @{ - */ - -/** @defgroup USB_DEF_Exported_Defines - * @{ - */ - -#ifndef NULL -#define NULL 0 -#endif - - -#define USB_LEN_DEV_QUALIFIER_DESC 0x0A -#define USB_LEN_DEV_DESC 0x12 -#define USB_LEN_CFG_DESC 0x09 -#define USB_LEN_IF_DESC 0x09 -#define USB_LEN_EP_DESC 0x07 -#define USB_LEN_OTG_DESC 0x03 -#define USB_LEN_LANGID_STR_DESC 0x04 -#define USB_LEN_OTHER_SPEED_DESC_SIZ 0x09 - -#define USBD_IDX_LANGID_STR 0x00 -#define USBD_IDX_MFC_STR 0x01 -#define USBD_IDX_PRODUCT_STR 0x02 -#define USBD_IDX_SERIAL_STR 0x03 -#define USBD_IDX_CONFIG_STR 0x04 -#define USBD_IDX_INTERFACE_STR 0x05 -#define USBD_IDX_ODRIVE_INTF_STR 0x06 -#define USBD_IDX_MICROSOFT_DESC_STR 0xEE - -#define USB_REQ_TYPE_STANDARD 0x00 -#define USB_REQ_TYPE_CLASS 0x20 -#define USB_REQ_TYPE_VENDOR 0x40 -#define USB_REQ_TYPE_MASK 0x60 - -#define USB_REQ_RECIPIENT_DEVICE 0x00 -#define USB_REQ_RECIPIENT_INTERFACE 0x01 -#define USB_REQ_RECIPIENT_ENDPOINT 0x02 -#define USB_REQ_RECIPIENT_MASK 0x03 - -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -#define USB_REQ_SET_FEATURE 0x03 -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C - -#define USB_DESC_TYPE_DEVICE 1 -#define USB_DESC_TYPE_CONFIGURATION 2 -#define USB_DESC_TYPE_STRING 3 -#define USB_DESC_TYPE_INTERFACE 4 -#define USB_DESC_TYPE_ENDPOINT 5 -#define USB_DESC_TYPE_DEVICE_QUALIFIER 6 -#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 7 -#define USB_DESC_TYPE_BOS 0x0F - -#define USB_CONFIG_REMOTE_WAKEUP 2 -#define USB_CONFIG_SELF_POWERED 1 - -#define USB_FEATURE_EP_HALT 0 -#define USB_FEATURE_REMOTE_WAKEUP 1 -#define USB_FEATURE_TEST_MODE 2 - -#define USB_DEVICE_CAPABITY_TYPE 0x10 - -#define USB_HS_MAX_PACKET_SIZE 512 -#define USB_FS_MAX_PACKET_SIZE 64 -#define USB_MAX_EP0_SIZE 64 - -/* Device Status */ -#define USBD_STATE_DEFAULT 1 -#define USBD_STATE_ADDRESSED 2 -#define USBD_STATE_CONFIGURED 3 -#define USBD_STATE_SUSPENDED 4 - - -/* EP0 State */ -#define USBD_EP0_IDLE 0 -#define USBD_EP0_SETUP 1 -#define USBD_EP0_DATA_IN 2 -#define USBD_EP0_DATA_OUT 3 -#define USBD_EP0_STATUS_IN 4 -#define USBD_EP0_STATUS_OUT 5 -#define USBD_EP0_STALL 6 - -#define USBD_EP_TYPE_CTRL 0 -#define USBD_EP_TYPE_ISOC 1 -#define USBD_EP_TYPE_BULK 2 -#define USBD_EP_TYPE_INTR 3 - - -/** - * @} - */ - - -/** @defgroup USBD_DEF_Exported_TypesDefinitions - * @{ - */ - -typedef struct usb_setup_req -{ - - uint8_t bmRequest; - uint8_t bRequest; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; -}USBD_SetupReqTypedef; - -struct _USBD_HandleTypeDef; - -typedef struct _Device_cb -{ - uint8_t (*Init) (struct _USBD_HandleTypeDef *pdev , uint8_t cfgidx); - uint8_t (*DeInit) (struct _USBD_HandleTypeDef *pdev , uint8_t cfgidx); - /* Control Endpoints*/ - uint8_t (*Setup) (struct _USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req); - uint8_t (*EP0_TxSent) (struct _USBD_HandleTypeDef *pdev ); - uint8_t (*EP0_RxReady) (struct _USBD_HandleTypeDef *pdev ); - /* Class Specific Endpoints*/ - uint8_t (*DataIn) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum); - uint8_t (*DataOut) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum); - uint8_t (*SOF) (struct _USBD_HandleTypeDef *pdev); - uint8_t (*IsoINIncomplete) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum); - uint8_t (*IsoOUTIncomplete) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum); - - uint8_t *(*GetHSConfigDescriptor)(uint16_t *length); - uint8_t *(*GetFSConfigDescriptor)(uint16_t *length); - uint8_t *(*GetOtherSpeedConfigDescriptor)(uint16_t *length); - uint8_t *(*GetDeviceQualifierDescriptor)(uint16_t *length); -#if (USBD_SUPPORT_USER_STRING == 1) - uint8_t *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev ,uint8_t index, uint16_t *length); -#endif - -} USBD_ClassTypeDef; - -/* Following USB Device Speed */ -typedef enum -{ - USBD_SPEED_HIGH = 0, - USBD_SPEED_FULL = 1, - USBD_SPEED_LOW = 2, -}USBD_SpeedTypeDef; - -/* Following USB Device status */ -typedef enum { - USBD_OK = 0, - USBD_BUSY, - USBD_FAIL, -}USBD_StatusTypeDef; - -/* USB Device descriptors structure */ -typedef struct -{ - uint8_t *(*GetDeviceDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetLangIDStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetManufacturerStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetProductStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetSerialStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetConfigurationStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetInterfaceStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); -#if (USBD_LPM_ENABLED == 1) - uint8_t *(*GetBOSDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); -#endif -} USBD_DescriptorsTypeDef; - -/* USB Device handle structure */ -typedef struct -{ - uint32_t status; - uint32_t total_length; - uint32_t rem_length; - uint32_t maxpacket; -} USBD_EndpointTypeDef; - -/* USB Device handle structure */ -typedef struct _USBD_HandleTypeDef -{ - uint8_t id; - uint32_t dev_config; - uint32_t dev_default_config; - uint32_t dev_config_status; - USBD_SpeedTypeDef dev_speed; - USBD_EndpointTypeDef ep_in[15]; - USBD_EndpointTypeDef ep_out[15]; - uint32_t ep0_state; - uint32_t ep0_data_len; - uint8_t dev_state; - uint8_t dev_old_state; - uint8_t dev_address; - uint8_t dev_connection_status; - uint8_t dev_test_mode; - uint32_t dev_remote_wakeup; - - USBD_SetupReqTypedef request; - USBD_DescriptorsTypeDef *pDesc; - USBD_ClassTypeDef *pClass; - void *pClassData; - void *pUserData; - void *pData; -} USBD_HandleTypeDef; - -/** - * @} - */ - - - -/** @defgroup USBD_DEF_Exported_Macros - * @{ - */ -#define SWAPBYTE(addr) (((uint16_t)(*((uint8_t *)(addr)))) + \ - (((uint16_t)(*(((uint8_t *)(addr)) + 1))) << 8)) - -#define LOBYTE(x) ((uint8_t)(x & 0x00FF)) -#define HIBYTE(x) ((uint8_t)((x & 0xFF00) >>8)) -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - - -#if defined ( __GNUC__ ) - #ifndef __weak - #define __weak __attribute__((weak)) - #endif /* __weak */ - #ifndef __packed - #define __packed __attribute__((__packed__)) - #endif /* __packed */ -#endif /* __GNUC__ */ - - -/* In HS mode and when the DMA is used, all variables and data structures dealing - with the DMA during the transaction process should be 4-bytes aligned */ - -#if defined (__GNUC__) /* GNU Compiler */ - #define __ALIGN_END __attribute__ ((aligned (4))) - #define __ALIGN_BEGIN -#else - #define __ALIGN_END - #if defined (__CC_ARM) /* ARM Compiler */ - #define __ALIGN_BEGIN __align(4) - #elif defined (__ICCARM__) /* IAR Compiler */ - #define __ALIGN_BEGIN - #elif defined (__TASKING__) /* TASKING Compiler */ - #define __ALIGN_BEGIN __align(4) - #endif /* __CC_ARM */ -#endif /* __GNUC__ */ - - -/** - * @} - */ - -/** @defgroup USBD_DEF_Exported_Variables - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_DEF_Exported_FunctionsPrototype - * @{ - */ - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USBD_DEF_H */ - -/** - * @} - */ - -/** -* @} -*/ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h deleted file mode 100644 index b476307c5..000000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h +++ /dev/null @@ -1,128 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_ioreq.h - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief Header file for the usbd_ioreq.c file - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (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.st.com/software_license_agreement_liberty_v2 - * - * 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. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USBD_IOREQ_H -#define __USBD_IOREQ_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_def.h" -#include "usbd_core.h" - -/** @addtogroup STM32_USB_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup USBD_IOREQ - * @brief header file for the usbd_ioreq.c file - * @{ - */ - -/** @defgroup USBD_IOREQ_Exported_Defines - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_IOREQ_Exported_Types - * @{ - */ - - -/** - * @} - */ - - - -/** @defgroup USBD_IOREQ_Exported_Macros - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_IOREQ_Exported_Variables - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_IOREQ_Exported_FunctionsPrototype - * @{ - */ - -USBD_StatusTypeDef USBD_CtlSendData (USBD_HandleTypeDef *pdev, - uint8_t *buf, - uint16_t len); - -USBD_StatusTypeDef USBD_CtlContinueSendData (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len); - -USBD_StatusTypeDef USBD_CtlPrepareRx (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len); - -USBD_StatusTypeDef USBD_CtlContinueRx (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len); - -USBD_StatusTypeDef USBD_CtlSendStatus (USBD_HandleTypeDef *pdev); - -USBD_StatusTypeDef USBD_CtlReceiveStatus (USBD_HandleTypeDef *pdev); - -uint16_t USBD_GetRxCount (USBD_HandleTypeDef *pdev , - uint8_t epnum); - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USBD_IOREQ_H */ - -/** - * @} - */ - -/** -* @} -*/ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c deleted file mode 100644 index 0158829c5..000000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c +++ /dev/null @@ -1,565 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_core.c - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief This file provides all the USBD core functions. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (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.st.com/software_license_agreement_liberty_v2 - * - * 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. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_core.h" - -/** @addtogroup STM32_USBD_DEVICE_LIBRARY -* @{ -*/ - - -/** @defgroup USBD_CORE -* @brief usbd core module -* @{ -*/ - -/** @defgroup USBD_CORE_Private_TypesDefinitions -* @{ -*/ -/** -* @} -*/ - - -/** @defgroup USBD_CORE_Private_Defines -* @{ -*/ - -/** -* @} -*/ - - -/** @defgroup USBD_CORE_Private_Macros -* @{ -*/ -/** -* @} -*/ - - - - -/** @defgroup USBD_CORE_Private_FunctionPrototypes -* @{ -*/ - -/** -* @} -*/ - -/** @defgroup USBD_CORE_Private_Variables -* @{ -*/ - -/** -* @} -*/ - -/** @defgroup USBD_CORE_Private_Functions -* @{ -*/ - -/** -* @brief USBD_Init -* Initializes the device stack and load the class driver -* @param pdev: device instance -* @param pdesc: Descriptor structure address -* @param id: Low level core index -* @retval None -*/ -USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id) -{ - /* Check whether the USB Host handle is valid */ - if(pdev == NULL) - { - USBD_ErrLog("Invalid Device handle"); - return USBD_FAIL; - } - - /* Unlink previous class*/ - if(pdev->pClass != NULL) - { - pdev->pClass = NULL; - } - - /* Assign USBD Descriptors */ - if(pdesc != NULL) - { - pdev->pDesc = pdesc; - } - - /* Set Device initial State */ - pdev->dev_state = USBD_STATE_DEFAULT; - pdev->id = id; - /* Initialize low level driver */ - USBD_LL_Init(pdev); - - return USBD_OK; -} - -/** -* @brief USBD_DeInit -* Re-Initialize th device library -* @param pdev: device instance -* @retval status: status -*/ -USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev) -{ - /* Set Default State */ - pdev->dev_state = USBD_STATE_DEFAULT; - - /* Free Class Resources */ - pdev->pClass->DeInit(pdev, pdev->dev_config); - - /* Stop the low level driver */ - USBD_LL_Stop(pdev); - - /* Initialize low level driver */ - USBD_LL_DeInit(pdev); - - return USBD_OK; -} - - -/** - * @brief USBD_RegisterClass - * Link class driver to Device Core. - * @param pDevice : Device Handle - * @param pclass: Class handle - * @retval USBD Status - */ -USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass) -{ - USBD_StatusTypeDef status = USBD_OK; - if(pclass != 0) - { - /* link the class to the USB Device handle */ - pdev->pClass = pclass; - status = USBD_OK; - } - else - { - USBD_ErrLog("Invalid Class handle"); - status = USBD_FAIL; - } - - return status; -} - -/** - * @brief USBD_Start - * Start the USB Device Core. - * @param pdev: Device Handle - * @retval USBD Status - */ -USBD_StatusTypeDef USBD_Start (USBD_HandleTypeDef *pdev) -{ - - /* Start the low level driver */ - USBD_LL_Start(pdev); - - return USBD_OK; -} - -/** - * @brief USBD_Stop - * Stop the USB Device Core. - * @param pdev: Device Handle - * @retval USBD Status - */ -USBD_StatusTypeDef USBD_Stop (USBD_HandleTypeDef *pdev) -{ - /* Free Class Resources */ - pdev->pClass->DeInit(pdev, pdev->dev_config); - - /* Stop the low level driver */ - USBD_LL_Stop(pdev); - - return USBD_OK; -} - -/** -* @brief USBD_RunTestMode -* Launch test mode process -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_RunTestMode (USBD_HandleTypeDef *pdev) -{ - return USBD_OK; -} - - -/** -* @brief USBD_SetClassConfig -* Configure device and start the interface -* @param pdev: device instance -* @param cfgidx: configuration index -* @retval status -*/ - -USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx) -{ - USBD_StatusTypeDef ret = USBD_FAIL; - - if(pdev->pClass != NULL) - { - /* Set configuration and Start the Class*/ - if(pdev->pClass->Init(pdev, cfgidx) == 0) - { - ret = USBD_OK; - } - } - return ret; -} - -/** -* @brief USBD_ClrClassConfig -* Clear current configuration -* @param pdev: device instance -* @param cfgidx: configuration index -* @retval status: USBD_StatusTypeDef -*/ -USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx) -{ - /* Clear configuration and De-initialize the Class process*/ - pdev->pClass->DeInit(pdev, cfgidx); - return USBD_OK; -} - - -/** -* @brief USBD_SetupStage -* Handle the setup stage -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup) -{ - - USBD_ParseSetupRequest(&pdev->request, psetup); - - pdev->ep0_state = USBD_EP0_SETUP; - pdev->ep0_data_len = pdev->request.wLength; - - switch (pdev->request.bmRequest & 0x1F) - { - case USB_REQ_RECIPIENT_DEVICE: - USBD_StdDevReq (pdev, &pdev->request); - break; - - case USB_REQ_RECIPIENT_INTERFACE: - USBD_StdItfReq(pdev, &pdev->request); - break; - - case USB_REQ_RECIPIENT_ENDPOINT: - USBD_StdEPReq(pdev, &pdev->request); - break; - - default: - USBD_LL_StallEP(pdev , pdev->request.bmRequest & 0x80); - break; - } - return USBD_OK; -} - -/** -* @brief USBD_DataOutStage -* Handle data OUT stage -* @param pdev: device instance -* @param epnum: endpoint index -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev , uint8_t epnum, uint8_t *pdata) -{ - USBD_EndpointTypeDef *pep; - - if(epnum == 0) - { - pep = &pdev->ep_out[0]; - - if ( pdev->ep0_state == USBD_EP0_DATA_OUT) - { - if(pep->rem_length > pep->maxpacket) - { - pep->rem_length -= pep->maxpacket; - - USBD_CtlContinueRx (pdev, - pdata, - MIN(pep->rem_length ,pep->maxpacket)); - } - else - { - if((pdev->pClass->EP0_RxReady != NULL)&& - (pdev->dev_state == USBD_STATE_CONFIGURED)) - { - pdev->pClass->EP0_RxReady(pdev); - } - USBD_CtlSendStatus(pdev); - } - } - } - else if((pdev->pClass->DataOut != NULL)&& - (pdev->dev_state == USBD_STATE_CONFIGURED)) - { - pdev->pClass->DataOut(pdev, epnum); - } - return USBD_OK; -} - -/** -* @brief USBD_DataInStage -* Handle data in stage -* @param pdev: device instance -* @param epnum: endpoint index -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev ,uint8_t epnum, uint8_t *pdata) -{ - USBD_EndpointTypeDef *pep; - - if(epnum == 0) - { - pep = &pdev->ep_in[0]; - - if ( pdev->ep0_state == USBD_EP0_DATA_IN) - { - if(pep->rem_length > pep->maxpacket) - { - pep->rem_length -= pep->maxpacket; - - USBD_CtlContinueSendData (pdev, - pdata, - pep->rem_length); - - /* Prepare endpoint for premature end of transfer */ - USBD_LL_PrepareReceive (pdev, - 0, - NULL, - 0); - } - else - { /* last packet is MPS multiple, so send ZLP packet */ - if((pep->total_length % pep->maxpacket == 0) && - (pep->total_length >= pep->maxpacket) && - (pep->total_length < pdev->ep0_data_len )) - { - - USBD_CtlContinueSendData(pdev , NULL, 0); - pdev->ep0_data_len = 0; - - /* Prepare endpoint for premature end of transfer */ - USBD_LL_PrepareReceive (pdev, - 0, - NULL, - 0); - } - else - { - if((pdev->pClass->EP0_TxSent != NULL)&& - (pdev->dev_state == USBD_STATE_CONFIGURED)) - { - pdev->pClass->EP0_TxSent(pdev); - } - USBD_CtlReceiveStatus(pdev); - } - } - } - if (pdev->dev_test_mode == 1) - { - USBD_RunTestMode(pdev); - pdev->dev_test_mode = 0; - } - } - else if((pdev->pClass->DataIn != NULL)&& - (pdev->dev_state == USBD_STATE_CONFIGURED)) - { - pdev->pClass->DataIn(pdev, epnum); - } - return USBD_OK; -} - -/** -* @brief USBD_LL_Reset -* Handle Reset event -* @param pdev: device instance -* @retval status -*/ - -USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev) -{ - /* Open EP0 OUT */ - USBD_LL_OpenEP(pdev, - 0x00, - USBD_EP_TYPE_CTRL, - USB_MAX_EP0_SIZE); - - pdev->ep_out[0].maxpacket = USB_MAX_EP0_SIZE; - - /* Open EP0 IN */ - USBD_LL_OpenEP(pdev, - 0x80, - USBD_EP_TYPE_CTRL, - USB_MAX_EP0_SIZE); - - pdev->ep_in[0].maxpacket = USB_MAX_EP0_SIZE; - /* Upon Reset call user call back */ - pdev->dev_state = USBD_STATE_DEFAULT; - - if (pdev->pClassData) - pdev->pClass->DeInit(pdev, pdev->dev_config); - - - return USBD_OK; -} - - - - -/** -* @brief USBD_LL_Reset -* Handle Reset event -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, USBD_SpeedTypeDef speed) -{ - pdev->dev_speed = speed; - return USBD_OK; -} - -/** -* @brief USBD_Suspend -* Handle Suspend event -* @param pdev: device instance -* @retval status -*/ - -USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev) -{ - pdev->dev_old_state = pdev->dev_state; - pdev->dev_state = USBD_STATE_SUSPENDED; - return USBD_OK; -} - -/** -* @brief USBD_Resume -* Handle Resume event -* @param pdev: device instance -* @retval status -*/ - -USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev) -{ - pdev->dev_state = pdev->dev_old_state; - return USBD_OK; -} - -/** -* @brief USBD_SOF -* Handle SOF event -* @param pdev: device instance -* @retval status -*/ - -USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev) -{ - if(pdev->dev_state == USBD_STATE_CONFIGURED) - { - if(pdev->pClass->SOF != NULL) - { - pdev->pClass->SOF(pdev); - } - } - return USBD_OK; -} - -/** -* @brief USBD_IsoINIncomplete -* Handle iso in incomplete event -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum) -{ - return USBD_OK; -} - -/** -* @brief USBD_IsoOUTIncomplete -* Handle iso out incomplete event -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum) -{ - return USBD_OK; -} - -/** -* @brief USBD_DevConnected -* Handle device connection event -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev) -{ - return USBD_OK; -} - -/** -* @brief USBD_DevDisconnected -* Handle device disconnection event -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev) -{ - /* Free Class Resources */ - pdev->dev_state = USBD_STATE_DEFAULT; - pdev->pClass->DeInit(pdev, pdev->dev_config); - - return USBD_OK; -} -/** -* @} -*/ - - -/** -* @} -*/ - - -/** -* @} -*/ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ - diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c deleted file mode 100644 index 49330c667..000000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c +++ /dev/null @@ -1,782 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_req.c - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief This file provides the standard USB requests following chapter 9. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (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.st.com/software_license_agreement_liberty_v2 - * - * 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. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_ctlreq.h" -#include "usbd_ioreq.h" - - -/** @addtogroup STM32_USBD_STATE_DEVICE_LIBRARY - * @{ - */ - - -/** @defgroup USBD_REQ - * @brief USB standard requests module - * @{ - */ - -/** @defgroup USBD_REQ_Private_TypesDefinitions - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_REQ_Private_Defines - * @{ - */ - -/** - * @} - */ - - -/** @defgroup USBD_REQ_Private_Macros - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_REQ_Private_Variables - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_REQ_Private_FunctionPrototypes - * @{ - */ -static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_SetAddress(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_SetConfig(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_GetConfig(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_GetStatus(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_SetFeature(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_ClrFeature(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static uint8_t USBD_GetLen(uint8_t *buf); - -/** - * @} - */ - - -/** @defgroup USBD_REQ_Private_Functions - * @{ - */ - - -/** -* @brief USBD_StdDevReq -* Handle standard usb device requests -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -USBD_StatusTypeDef USBD_StdDevReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req) -{ - USBD_StatusTypeDef ret = USBD_OK; - - switch (req->bRequest) - { - case USB_REQ_GET_DESCRIPTOR: - - USBD_GetDescriptor (pdev, req) ; - break; - - case USB_REQ_SET_ADDRESS: - USBD_SetAddress(pdev, req); - break; - - case USB_REQ_SET_CONFIGURATION: - USBD_SetConfig (pdev , req); - break; - - case USB_REQ_GET_CONFIGURATION: - USBD_GetConfig (pdev , req); - break; - - case USB_REQ_GET_STATUS: - USBD_GetStatus (pdev , req); - break; - - - case USB_REQ_SET_FEATURE: - USBD_SetFeature (pdev , req); - break; - - case USB_REQ_CLEAR_FEATURE: - USBD_ClrFeature (pdev , req); - break; - - default: - USBD_CtlError(pdev , req); - break; - } - - return ret; -} - -/** -* @brief USBD_StdItfReq -* Handle standard usb interface requests -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -USBD_StatusTypeDef USBD_StdItfReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req) -{ - USBD_StatusTypeDef ret = USBD_OK; - - switch (pdev->dev_state) - { - case USBD_STATE_CONFIGURED: - - if (LOBYTE(req->wIndex) <= USBD_MAX_NUM_INTERFACES) - { - pdev->pClass->Setup (pdev, req); - - if((req->wLength == 0)&& (ret == USBD_OK)) - { - USBD_CtlSendStatus(pdev); - } - } - else - { - USBD_CtlError(pdev , req); - } - break; - - default: - USBD_CtlError(pdev , req); - break; - } - return USBD_OK; -} - -/** -* @brief USBD_StdEPReq -* Handle standard usb endpoint requests -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -USBD_StatusTypeDef USBD_StdEPReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req) -{ - - uint8_t ep_addr; - USBD_StatusTypeDef ret = USBD_OK; - USBD_EndpointTypeDef *pep; - ep_addr = LOBYTE(req->wIndex); - - /* Check if it is a class request */ - if ((req->bmRequest & 0x60) == 0x20) - { - pdev->pClass->Setup (pdev, req); - - return USBD_OK; - } - - switch (req->bRequest) - { - - case USB_REQ_SET_FEATURE : - - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - if ((ep_addr != 0x00) && (ep_addr != 0x80)) - { - USBD_LL_StallEP(pdev , ep_addr); - } - break; - - case USBD_STATE_CONFIGURED: - if (req->wValue == USB_FEATURE_EP_HALT) - { - if ((ep_addr != 0x00) && (ep_addr != 0x80)) - { - USBD_LL_StallEP(pdev , ep_addr); - - } - } - pdev->pClass->Setup (pdev, req); - USBD_CtlSendStatus(pdev); - - break; - - default: - USBD_CtlError(pdev , req); - break; - } - break; - - case USB_REQ_CLEAR_FEATURE : - - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - if ((ep_addr != 0x00) && (ep_addr != 0x80)) - { - USBD_LL_StallEP(pdev , ep_addr); - } - break; - - case USBD_STATE_CONFIGURED: - if (req->wValue == USB_FEATURE_EP_HALT) - { - if ((ep_addr & 0x7F) != 0x00) - { - USBD_LL_ClearStallEP(pdev , ep_addr); - pdev->pClass->Setup (pdev, req); - } - USBD_CtlSendStatus(pdev); - } - break; - - default: - USBD_CtlError(pdev , req); - break; - } - break; - - case USB_REQ_GET_STATUS: - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - if ((ep_addr & 0x7F) != 0x00) - { - USBD_LL_StallEP(pdev , ep_addr); - } - break; - - case USBD_STATE_CONFIGURED: - pep = ((ep_addr & 0x80) == 0x80) ? &pdev->ep_in[ep_addr & 0x7F]:\ - &pdev->ep_out[ep_addr & 0x7F]; - if(USBD_LL_IsStallEP(pdev, ep_addr)) - { - pep->status = 0x0001; - } - else - { - pep->status = 0x0000; - } - - USBD_CtlSendData (pdev, - (uint8_t *)&pep->status, - 2); - break; - - default: - USBD_CtlError(pdev , req); - break; - } - break; - - default: - break; - } - return ret; -} -/** -* @brief USBD_GetDescriptor -* Handle Get Descriptor requests -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - uint16_t len; - uint8_t *pbuf; - - - switch (req->wValue >> 8) - { -#if (USBD_LPM_ENABLED == 1) - case USB_DESC_TYPE_BOS: - pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len); - break; -#endif - case USB_DESC_TYPE_DEVICE: - pbuf = pdev->pDesc->GetDeviceDescriptor(pdev->dev_speed, &len); - break; - - case USB_DESC_TYPE_CONFIGURATION: - if(pdev->dev_speed == USBD_SPEED_HIGH ) - { - pbuf = (uint8_t *)pdev->pClass->GetHSConfigDescriptor(&len); - pbuf[1] = USB_DESC_TYPE_CONFIGURATION; - } - else - { - pbuf = (uint8_t *)pdev->pClass->GetFSConfigDescriptor(&len); - pbuf[1] = USB_DESC_TYPE_CONFIGURATION; - } - break; - - case USB_DESC_TYPE_STRING: - switch ((uint8_t)(req->wValue)) - { - case USBD_IDX_LANGID_STR: - pbuf = pdev->pDesc->GetLangIDStrDescriptor(pdev->dev_speed, &len); - break; - - case USBD_IDX_MFC_STR: - pbuf = pdev->pDesc->GetManufacturerStrDescriptor(pdev->dev_speed, &len); - break; - - case USBD_IDX_PRODUCT_STR: - pbuf = pdev->pDesc->GetProductStrDescriptor(pdev->dev_speed, &len); - break; - - case USBD_IDX_SERIAL_STR: - pbuf = pdev->pDesc->GetSerialStrDescriptor(pdev->dev_speed, &len); - break; - - case USBD_IDX_CONFIG_STR: - pbuf = pdev->pDesc->GetConfigurationStrDescriptor(pdev->dev_speed, &len); - break; - - case USBD_IDX_INTERFACE_STR: - pbuf = pdev->pDesc->GetInterfaceStrDescriptor(pdev->dev_speed, &len); - break; - - default: -#if (USBD_SUPPORT_USER_STRING == 1) - pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue) , &len); - break; -#else - USBD_CtlError(pdev , req); - return; -#endif - } - break; - case USB_DESC_TYPE_DEVICE_QUALIFIER: - - if(pdev->dev_speed == USBD_SPEED_HIGH ) - { - pbuf = (uint8_t *)pdev->pClass->GetDeviceQualifierDescriptor(&len); - break; - } - else - { - USBD_CtlError(pdev , req); - return; - } - - case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION: - if(pdev->dev_speed == USBD_SPEED_HIGH ) - { - pbuf = (uint8_t *)pdev->pClass->GetOtherSpeedConfigDescriptor(&len); - pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION; - break; - } - else - { - USBD_CtlError(pdev , req); - return; - } - - default: - USBD_CtlError(pdev , req); - return; - } - - if((len != 0)&& (req->wLength != 0)) - { - - len = MIN(len , req->wLength); - - USBD_CtlSendData (pdev, - pbuf, - len); - } - -} - -/** -* @brief USBD_SetAddress -* Set device address -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_SetAddress(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - uint8_t dev_addr; - - if ((req->wIndex == 0) && (req->wLength == 0)) - { - dev_addr = (uint8_t)(req->wValue) & 0x7F; - - if (pdev->dev_state == USBD_STATE_CONFIGURED) - { - USBD_CtlError(pdev , req); - } - else - { - pdev->dev_address = dev_addr; - USBD_LL_SetUSBAddress(pdev, dev_addr); - USBD_CtlSendStatus(pdev); - - if (dev_addr != 0) - { - pdev->dev_state = USBD_STATE_ADDRESSED; - } - else - { - pdev->dev_state = USBD_STATE_DEFAULT; - } - } - } - else - { - USBD_CtlError(pdev , req); - } -} - -/** -* @brief USBD_SetConfig -* Handle Set device configuration request -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_SetConfig(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - - static uint8_t cfgidx; - - cfgidx = (uint8_t)(req->wValue); - - if (cfgidx > USBD_MAX_NUM_CONFIGURATION ) - { - USBD_CtlError(pdev , req); - } - else - { - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - if (cfgidx) - { - pdev->dev_config = cfgidx; - pdev->dev_state = USBD_STATE_CONFIGURED; - if(USBD_SetClassConfig(pdev , cfgidx) == USBD_FAIL) - { - USBD_CtlError(pdev , req); - return; - } - USBD_CtlSendStatus(pdev); - } - else - { - USBD_CtlSendStatus(pdev); - } - break; - - case USBD_STATE_CONFIGURED: - if (cfgidx == 0) - { - pdev->dev_state = USBD_STATE_ADDRESSED; - pdev->dev_config = cfgidx; - USBD_ClrClassConfig(pdev , cfgidx); - USBD_CtlSendStatus(pdev); - - } - else if (cfgidx != pdev->dev_config) - { - /* Clear old configuration */ - USBD_ClrClassConfig(pdev , pdev->dev_config); - - /* set new configuration */ - pdev->dev_config = cfgidx; - if(USBD_SetClassConfig(pdev , cfgidx) == USBD_FAIL) - { - USBD_CtlError(pdev , req); - return; - } - USBD_CtlSendStatus(pdev); - } - else - { - USBD_CtlSendStatus(pdev); - } - break; - - default: - USBD_CtlError(pdev , req); - break; - } - } -} - -/** -* @brief USBD_GetConfig -* Handle Get device configuration request -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_GetConfig(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - - if (req->wLength != 1) - { - USBD_CtlError(pdev , req); - } - else - { - switch (pdev->dev_state ) - { - case USBD_STATE_ADDRESSED: - pdev->dev_default_config = 0; - USBD_CtlSendData (pdev, - (uint8_t *)&pdev->dev_default_config, - 1); - break; - - case USBD_STATE_CONFIGURED: - - USBD_CtlSendData (pdev, - (uint8_t *)&pdev->dev_config, - 1); - break; - - default: - USBD_CtlError(pdev , req); - break; - } - } -} - -/** -* @brief USBD_GetStatus -* Handle Get Status request -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_GetStatus(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - - - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - case USBD_STATE_CONFIGURED: - -#if ( USBD_SELF_POWERED == 1) - pdev->dev_config_status = USB_CONFIG_SELF_POWERED; -#else - pdev->dev_config_status = 0; -#endif - - if (pdev->dev_remote_wakeup) - { - pdev->dev_config_status |= USB_CONFIG_REMOTE_WAKEUP; - } - - USBD_CtlSendData (pdev, - (uint8_t *)& pdev->dev_config_status, - 2); - break; - - default : - USBD_CtlError(pdev , req); - break; - } -} - - -/** -* @brief USBD_SetFeature -* Handle Set device feature request -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_SetFeature(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - - if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) - { - pdev->dev_remote_wakeup = 1; - pdev->pClass->Setup (pdev, req); - USBD_CtlSendStatus(pdev); - } - -} - - -/** -* @brief USBD_ClrFeature -* Handle clear device feature request -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_ClrFeature(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - case USBD_STATE_CONFIGURED: - if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) - { - pdev->dev_remote_wakeup = 0; - pdev->pClass->Setup (pdev, req); - USBD_CtlSendStatus(pdev); - } - break; - - default : - USBD_CtlError(pdev , req); - break; - } -} - -/** -* @brief USBD_ParseSetupRequest -* Copy buffer into setup structure -* @param pdev: device instance -* @param req: usb request -* @retval None -*/ - -void USBD_ParseSetupRequest(USBD_SetupReqTypedef *req, uint8_t *pdata) -{ - req->bmRequest = *(uint8_t *) (pdata); - req->bRequest = *(uint8_t *) (pdata + 1); - req->wValue = SWAPBYTE (pdata + 2); - req->wIndex = SWAPBYTE (pdata + 4); - req->wLength = SWAPBYTE (pdata + 6); - -} - -/** -* @brief USBD_CtlError -* Handle USB low level Error -* @param pdev: device instance -* @param req: usb request -* @retval None -*/ - -void USBD_CtlError( USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - USBD_LL_StallEP(pdev , 0x80); - USBD_LL_StallEP(pdev , 0); -} - - -/** - * @brief USBD_GetString - * Convert Ascii string into unicode one - * @param desc : descriptor buffer - * @param unicode : Formatted string buffer (unicode) - * @param len : descriptor length - * @retval None - */ -void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len) -{ - uint8_t idx = 0; - - if (desc != NULL) - { - *len = USBD_GetLen(desc) * 2 + 2; - unicode[idx++] = *len; - unicode[idx++] = USB_DESC_TYPE_STRING; - - while (*desc != '\0') - { - unicode[idx++] = *desc++; - unicode[idx++] = 0x00; - } - } -} - -/** - * @brief USBD_GetLen - * return the string length - * @param buf : pointer to the ascii string buffer - * @retval string length - */ -static uint8_t USBD_GetLen(uint8_t *buf) -{ - uint8_t len = 0; - - while (*buf != '\0') - { - len++; - buf++; - } - - return len; -} -/** - * @} - */ - - -/** - * @} - */ - - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOSConfig_template.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOSConfig_template.h deleted file mode 100644 index 2662d2276..000000000 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOSConfig_template.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ - - -#ifndef FREERTOS_CONFIG_H -#define FREERTOS_CONFIG_H - -/*----------------------------------------------------------- - * Application specific definitions. - * - * These definitions should be adjusted for your particular hardware and - * application requirements. - * - * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE - * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. - * - * See http://www.freertos.org/a00110.html. - *----------------------------------------------------------*/ - -/* Ensure stdint is only used by the compiler, and not the assembler. */ -#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) - #include - extern uint32_t SystemCoreClock; -#endif - -#define configUSE_PREEMPTION 1 -#define configUSE_IDLE_HOOK 0 -#define configUSE_TICK_HOOK 0 -#define configCPU_CLOCK_HZ (SystemCoreClock) -#define configTICK_RATE_HZ ((TickType_t)1000) -#define configMAX_PRIORITIES (7) -#define configMINIMAL_STACK_SIZE ((uint16_t)128) -#define configTOTAL_HEAP_SIZE ((size_t)(15 * 1024)) -#define configMAX_TASK_NAME_LEN (16) -#define configUSE_TRACE_FACILITY 1 -#define configUSE_16_BIT_TICKS 0 -#define configIDLE_SHOULD_YIELD 1 -#define configUSE_MUTEXES 1 -#define configQUEUE_REGISTRY_SIZE 8 -#define configCHECK_FOR_STACK_OVERFLOW 0 -#define configUSE_RECURSIVE_MUTEXES 1 -#define configUSE_MALLOC_FAILED_HOOK 0 -#define configUSE_APPLICATION_TASK_TAG 0 -#define configUSE_COUNTING_SEMAPHORES 1 -#define configGENERATE_RUN_TIME_STATS 0 - -/* Co-routine definitions. */ -#define configUSE_CO_ROUTINES 0 -#define configMAX_CO_ROUTINE_PRIORITIES (2) - -/* Software timer definitions. */ -#define configUSE_TIMERS 0 -#define configTIMER_TASK_PRIORITY (2) -#define configTIMER_QUEUE_LENGTH 10 -#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) - -/* Set the following definitions to 1 to include the API function, or zero -to exclude the API function. */ -#define INCLUDE_vTaskPrioritySet 1 -#define INCLUDE_uxTaskPriorityGet 1 -#define INCLUDE_vTaskDelete 1 -#define INCLUDE_vTaskCleanUpResources 0 -#define INCLUDE_vTaskSuspend 1 -#define INCLUDE_vTaskDelayUntil 0 -#define INCLUDE_vTaskDelay 1 -#define INCLUDE_xTaskGetSchedulerState 1 - -/* Cortex-M specific definitions. */ -#ifdef __NVIC_PRIO_BITS - /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */ - #define configPRIO_BITS __NVIC_PRIO_BITS -#else - #define configPRIO_BITS 4 /* 15 priority levels */ -#endif - -/* The lowest interrupt priority that can be used in a call to a "set priority" -function. */ -#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf - -/* The highest interrupt priority that can be used by any interrupt service -routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL -INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER -PRIORITY THAN THIS! (higher priorities are lower numeric values. */ -#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 - -/* Interrupt priorities used by the kernel port layer itself. These are generic -to all Cortex-M ports, and do not rely on any particular library functions. */ -#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) -/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! -See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ -#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) - -/* Normal assert() semantics without relying on the provision of an assert.h -header file. */ -#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } - -/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS - standard names. */ -#define vPortSVCHandler SVC_Handler -#define xPortPendSVHandler PendSV_Handler - -/* IMPORTANT: This define MUST be commented when used with STM32Cube firmware, - to prevent overwriting SysTick_Handler defined within STM32Cube HAL */ -/* #define xPortSysTickHandler SysTick_Handler */ - -#endif /* FREERTOS_CONFIG_H */ - diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h deleted file mode 100644 index 8f7500b02..000000000 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ - -/* - * When the MPU is used the standard (non MPU) API functions are mapped to - * equivalents that start "MPU_", the prototypes for which are defined in this - * header files. This will cause the application code to call the MPU_ version - * which wraps the non-MPU version with privilege promoting then demoting code, - * so the kernel code always runs will full privileges. - */ - - -#ifndef MPU_PROTOTYPES_H -#define MPU_PROTOTYPES_H - -/* MPU versions of tasks.h API function. */ -BaseType_t MPU_xTaskCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask ); -TaskHandle_t MPU_xTaskCreateStatic( TaskFunction_t pxTaskCode, const char * const pcName, const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, StackType_t * const puxStackBuffer, StaticTask_t * const pxTaskBuffer ); -BaseType_t MPU_xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ); -void MPU_vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ); -void MPU_vTaskDelete( TaskHandle_t xTaskToDelete ); -void MPU_vTaskDelay( const TickType_t xTicksToDelay ); -void MPU_vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ); -BaseType_t MPU_xTaskAbortDelay( TaskHandle_t xTask ); -UBaseType_t MPU_uxTaskPriorityGet( TaskHandle_t xTask ); -eTaskState MPU_eTaskGetState( TaskHandle_t xTask ); -void MPU_vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ); -void MPU_vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ); -void MPU_vTaskSuspend( TaskHandle_t xTaskToSuspend ); -void MPU_vTaskResume( TaskHandle_t xTaskToResume ); -void MPU_vTaskStartScheduler( void ); -void MPU_vTaskSuspendAll( void ); -BaseType_t MPU_xTaskResumeAll( void ); -TickType_t MPU_xTaskGetTickCount( void ); -UBaseType_t MPU_uxTaskGetNumberOfTasks( void ); -char * MPU_pcTaskGetName( TaskHandle_t xTaskToQuery ); -TaskHandle_t MPU_xTaskGetHandle( const char *pcNameToQuery ); -UBaseType_t MPU_uxTaskGetStackHighWaterMark( TaskHandle_t xTask ); -void MPU_vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ); -TaskHookFunction_t MPU_xTaskGetApplicationTaskTag( TaskHandle_t xTask ); -void MPU_vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ); -void * MPU_pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ); -BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ); -TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ); -UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ); -void MPU_vTaskList( char * pcWriteBuffer ); -void MPU_vTaskGetRunTimeStats( char *pcWriteBuffer ); -BaseType_t MPU_xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ); -BaseType_t MPU_xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ); -uint32_t MPU_ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); -BaseType_t MPU_xTaskNotifyStateClear( TaskHandle_t xTask ); -BaseType_t MPU_xTaskIncrementTick( void ); -TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ); -void MPU_vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ); -BaseType_t MPU_xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ); -void MPU_vTaskMissedYield( void ); -BaseType_t MPU_xTaskGetSchedulerState( void ); - -/* MPU versions of queue.h API function. */ -BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ); -BaseType_t MPU_xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeek ); -UBaseType_t MPU_uxQueueMessagesWaiting( const QueueHandle_t xQueue ); -UBaseType_t MPU_uxQueueSpacesAvailable( const QueueHandle_t xQueue ); -void MPU_vQueueDelete( QueueHandle_t xQueue ); -QueueHandle_t MPU_xQueueCreateMutex( const uint8_t ucQueueType ); -QueueHandle_t MPU_xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ); -QueueHandle_t MPU_xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ); -QueueHandle_t MPU_xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ); -void* MPU_xQueueGetMutexHolder( QueueHandle_t xSemaphore ); -BaseType_t MPU_xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ); -BaseType_t MPU_xQueueGiveMutexRecursive( QueueHandle_t pxMutex ); -void MPU_vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcName ); -void MPU_vQueueUnregisterQueue( QueueHandle_t xQueue ); -const char * MPU_pcQueueGetName( QueueHandle_t xQueue ); -QueueHandle_t MPU_xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ); -QueueHandle_t MPU_xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ); -QueueSetHandle_t MPU_xQueueCreateSet( const UBaseType_t uxEventQueueLength ); -BaseType_t MPU_xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); -BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); -QueueSetMemberHandle_t MPU_xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ); -BaseType_t MPU_xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ); -void MPU_vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ); -UBaseType_t MPU_uxQueueGetQueueNumber( QueueHandle_t xQueue ); -uint8_t MPU_ucQueueGetQueueType( QueueHandle_t xQueue ); - -/* MPU versions of timers.h API function. */ -TimerHandle_t MPU_xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ); -TimerHandle_t MPU_xTimerCreateStatic( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, StaticTimer_t *pxTimerBuffer ); -void * MPU_pvTimerGetTimerID( const TimerHandle_t xTimer ); -void MPU_vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); -BaseType_t MPU_xTimerIsTimerActive( TimerHandle_t xTimer ); -TaskHandle_t MPU_xTimerGetTimerDaemonTaskHandle( void ); -BaseType_t MPU_xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ); -const char * MPU_pcTimerGetName( TimerHandle_t xTimer ); -TickType_t MPU_xTimerGetPeriod( TimerHandle_t xTimer ); -TickType_t MPU_xTimerGetExpiryTime( TimerHandle_t xTimer ); -BaseType_t MPU_xTimerCreateTimerTask( void ); -BaseType_t MPU_xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ); - -/* MPU versions of event_group.h API function. */ -EventGroupHandle_t MPU_xEventGroupCreate( void ); -EventGroupHandle_t MPU_xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ); -EventBits_t MPU_xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ); -EventBits_t MPU_xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ); -EventBits_t MPU_xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ); -EventBits_t MPU_xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ); -void MPU_vEventGroupDelete( EventGroupHandle_t xEventGroup ); -UBaseType_t MPU_uxEventGroupGetNumber( void* xEventGroup ); - -#endif /* MPU_PROTOTYPES_H */ - diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h deleted file mode 100644 index 00a220866..000000000 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ - -/*----------------------------------------------------------- - * Portable layer API. Each function must be defined for each port. - *----------------------------------------------------------*/ - -#ifndef PORTABLE_H -#define PORTABLE_H - -/* Each FreeRTOS port has a unique portmacro.h header file. Originally a -pre-processor definition was used to ensure the pre-processor found the correct -portmacro.h file for the port being used. That scheme was deprecated in favour -of setting the compiler's include path such that it found the correct -portmacro.h file - removing the need for the constant and allowing the -portmacro.h file to be located anywhere in relation to the port being used. -Purely for reasons of backward compatibility the old method is still valid, but -to make it clear that new projects should not use it, support for the port -specific constants has been moved into the deprecated_definitions.h header -file. */ -#include "deprecated_definitions.h" - -/* If portENTER_CRITICAL is not defined then including deprecated_definitions.h -did not result in a portmacro.h header file being included - and it should be -included here. In this case the path to the correct portmacro.h header file -must be set in the compiler's include path. */ -#ifndef portENTER_CRITICAL - #include "portmacro.h" -#endif - -#if portBYTE_ALIGNMENT == 32 - #define portBYTE_ALIGNMENT_MASK ( 0x001f ) -#endif - -#if portBYTE_ALIGNMENT == 16 - #define portBYTE_ALIGNMENT_MASK ( 0x000f ) -#endif - -#if portBYTE_ALIGNMENT == 8 - #define portBYTE_ALIGNMENT_MASK ( 0x0007 ) -#endif - -#if portBYTE_ALIGNMENT == 4 - #define portBYTE_ALIGNMENT_MASK ( 0x0003 ) -#endif - -#if portBYTE_ALIGNMENT == 2 - #define portBYTE_ALIGNMENT_MASK ( 0x0001 ) -#endif - -#if portBYTE_ALIGNMENT == 1 - #define portBYTE_ALIGNMENT_MASK ( 0x0000 ) -#endif - -#ifndef portBYTE_ALIGNMENT_MASK - #error "Invalid portBYTE_ALIGNMENT definition" -#endif - -#ifndef portNUM_CONFIGURABLE_REGIONS - #define portNUM_CONFIGURABLE_REGIONS 1 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include "mpu_wrappers.h" - -/* - * Setup the stack of a new task so it is ready to be placed under the - * scheduler control. The registers have to be placed on the stack in - * the order that the port expects to find them. - * - */ -#if( portUSING_MPU_WRAPPERS == 1 ) - PRIVILEGED_FUNCTION StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) ; -#else - PRIVILEGED_FUNCTION StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) ; -#endif - -/* Used by heap_5.c. */ -typedef struct HeapRegion -{ - uint8_t *pucStartAddress; - size_t xSizeInBytes; -} HeapRegion_t; - -/* - * Used to define multiple heap regions for use by heap_5.c. This function - * must be called before any calls to pvPortMalloc() - not creating a task, - * queue, semaphore, mutex, software timer, event group, etc. will result in - * pvPortMalloc being called. - * - * pxHeapRegions passes in an array of HeapRegion_t structures - each of which - * defines a region of memory that can be used as the heap. The array is - * terminated by a HeapRegions_t structure that has a size of 0. The region - * with the lowest start address must appear first in the array. - */ -PRIVILEGED_FUNCTION void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ); - - -/* - * Map to the memory management routines required for the port. - */ -PRIVILEGED_FUNCTION void *pvPortMalloc( size_t xSize ); -PRIVILEGED_FUNCTION void vPortFree( void *pv ); -PRIVILEGED_FUNCTION void vPortInitialiseBlocks( void ); -PRIVILEGED_FUNCTION size_t xPortGetFreeHeapSize( void ); -PRIVILEGED_FUNCTION size_t xPortGetMinimumEverFreeHeapSize( void ); - -/* - * Setup the hardware ready for the scheduler to take control. This generally - * sets up a tick interrupt and sets timers for the correct tick frequency. - */ -PRIVILEGED_FUNCTION BaseType_t xPortStartScheduler( void ); - -/* - * Undo any hardware/ISR setup that was performed by xPortStartScheduler() so - * the hardware is left in its original condition after the scheduler stops - * executing. - */ -PRIVILEGED_FUNCTION void vPortEndScheduler( void ); - -/* - * The structures and methods of manipulating the MPU are contained within the - * port layer. - * - * Fills the xMPUSettings structure with the memory region information - * contained in xRegions. - */ -#if( portUSING_MPU_WRAPPERS == 1 ) - struct xMEMORY_REGION; - PRIVILEGED_FUNCTION void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t ulStackDepth ); -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* PORTABLE_H */ - diff --git a/Firmware/Board/v3/STM32F405RGTx_FLASH.ld b/Firmware/Board/v3/STM32F405RGTx_FLASH.ld index f1259a710..79e374413 100644 --- a/Firmware/Board/v3/STM32F405RGTx_FLASH.ld +++ b/Firmware/Board/v3/STM32F405RGTx_FLASH.ld @@ -121,6 +121,7 @@ SECTIONS { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ + *(.testdata) *(.data) /* .data sections */ *(.data*) /* .data* sections */ diff --git a/Firmware/Board/v3/Src/adc.c b/Firmware/Board/v3/Src/adc.c index ed9c1bdc9..ede9c05f7 100644 --- a/Firmware/Board/v3/Src/adc.c +++ b/Firmware/Board/v3/Src/adc.c @@ -278,9 +278,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1); - /* ADC1 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ @@ -314,9 +311,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - /* ADC2 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC2_MspInit 1 */ /* USER CODE END ADC2_MspInit 1 */ @@ -340,9 +334,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - /* ADC3 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC3_MspInit 1 */ /* USER CODE END ADC3_MspInit 1 */ @@ -461,16 +452,6 @@ void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle) /* USER CODE BEGIN 1 */ #endif // END ADC Include -float read_ADC_volts(ADC_HandleTypeDef* hadc, uint8_t injected_rank) { - uint32_t ADCValue; - if(injected_rank) { - ADCValue = HAL_ADCEx_InjectedGetValue(hadc, injected_rank); - } else { - ADCValue = HAL_ADC_GetValue(hadc); - } - return (3.3f/((float)(1<<12))) * ADCValue; -} - /* USER CODE END 1 */ /** diff --git a/Firmware/Board/v3/Src/can.c b/Firmware/Board/v3/Src/can.c index 011150ff0..132ff2eb4 100644 --- a/Firmware/Board/v3/Src/can.c +++ b/Firmware/Board/v3/Src/can.c @@ -56,31 +56,46 @@ /* USER CODE END 0 */ -CAN_HandleTypeDef hcan1; - -/* CAN1 init function */ -void MX_CAN1_Init(void) -{ - - hcan1.Instance = CAN1; - hcan1.Init.Prescaler = 8; - hcan1.Init.Mode = CAN_MODE_NORMAL; - hcan1.Init.SyncJumpWidth = CAN_SJW_4TQ; - hcan1.Init.TimeSeg1 = CAN_BS1_16TQ; - hcan1.Init.TimeSeg2 = CAN_BS2_4TQ; - hcan1.Init.TimeTriggeredMode = DISABLE; - hcan1.Init.AutoBusOff = ENABLE; - hcan1.Init.AutoWakeUp = ENABLE; - hcan1.Init.AutoRetransmission = ENABLE; - hcan1.Init.ReceiveFifoLocked = DISABLE; - hcan1.Init.TransmitFifoPriority = DISABLE; - if (HAL_CAN_Init(&hcan1) != HAL_OK) - { - _Error_Handler(__FILE__, __LINE__); +CAN_HandleTypeDef hcan1 = { + .Instance = CAN1, + .Init = { + .Prescaler = 8, + .Mode = CAN_MODE_NORMAL, + .SyncJumpWidth = CAN_SJW_4TQ, + .TimeSeg1 = CAN_BS1_16TQ, + .TimeSeg2 = CAN_BS2_4TQ, + .TimeTriggeredMode = DISABLE, + .AutoBusOff = ENABLE, + .AutoWakeUp = ENABLE, + .AutoRetransmission = ENABLE, + .ReceiveFifoLocked = DISABLE, + .TransmitFifoPriority = DISABLE, } +}; -} - +/* CAN1 init function */ +//void MX_CAN1_Init(void) +//{ +// +// hcan1.Instance = CAN1; +// hcan1.Init.Prescaler = 8; +// hcan1.Init.Mode = CAN_MODE_NORMAL; +// hcan1.Init.SyncJumpWidth = CAN_SJW_4TQ; +// hcan1.Init.TimeSeg1 = CAN_BS1_16TQ; +// hcan1.Init.TimeSeg2 = CAN_BS2_4TQ; +// hcan1.Init.TimeTriggeredMode = DISABLE; +// hcan1.Init.AutoBusOff = ENABLE; +// hcan1.Init.AutoWakeUp = ENABLE; +// hcan1.Init.AutoRetransmission = ENABLE; +// hcan1.Init.ReceiveFifoLocked = DISABLE; +// hcan1.Init.TransmitFifoPriority = DISABLE; +// if (HAL_CAN_Init(&hcan1) != HAL_OK) +// { +// _Error_Handler(__FILE__, __LINE__); +// } +// +//} +// void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle) { @@ -105,13 +120,13 @@ void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle) HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* CAN1 interrupt Init */ - HAL_NVIC_SetPriority(CAN1_TX_IRQn, 6, 0); + HAL_NVIC_SetPriority(CAN1_TX_IRQn, 9, 0); HAL_NVIC_EnableIRQ(CAN1_TX_IRQn); - HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 6, 0); + HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 9, 0); HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn); - HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 6, 0); + HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 9, 0); HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn); - HAL_NVIC_SetPriority(CAN1_SCE_IRQn, 6, 0); + HAL_NVIC_SetPriority(CAN1_SCE_IRQn, 9, 0); HAL_NVIC_EnableIRQ(CAN1_SCE_IRQn); /* USER CODE BEGIN CAN1_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/dma.c b/Firmware/Board/v3/Src/dma.c index 813c735ad..56d09af90 100644 --- a/Firmware/Board/v3/Src/dma.c +++ b/Firmware/Board/v3/Src/dma.c @@ -72,17 +72,25 @@ void MX_DMA_Init(void) /* DMA interrupt init */ /* DMA1_Stream0_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 5, 0); + HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 4, 0); // SPI RX - must have lower priority than SPI TX + // and higher priority than the control loop handler HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn); /* DMA1_Stream2_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 5, 0); + HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 10, 0); HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn); /* DMA1_Stream4_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 5, 0); + HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 10, 0); HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn); /* DMA1_Stream5_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 5, 0); + HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 10, 0); HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); + /* DMA1_Stream6_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 10, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn); + /* DMA1_Stream7_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 3, 0); // SPI TX - must have higher priority than SPI RX + // and higher priority than the control loop handler + HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn); /* DMA2_Stream0_IRQn interrupt configuration */ // Dear STM, no we _don't_ want to fire an interrupt for this DMA // (it's not possible to deselect this in CubeMX) diff --git a/Firmware/Board/v3/Src/freertos.c b/Firmware/Board/v3/Src/freertos.c index b2d49e55b..d66b8ce33 100644 --- a/Firmware/Board/v3/Src/freertos.c +++ b/Firmware/Board/v3/Src/freertos.c @@ -56,12 +56,6 @@ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ -#include "freertos_vars.h" -#include "usb_device.h" -extern PCD_HandleTypeDef hpcd_USB_OTG_FS; -int odrive_main(void); -int load_configuration(void); -int construct_objects(void); /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -81,19 +75,6 @@ int construct_objects(void); /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN Variables */ -// List of semaphores -osSemaphoreId sem_usb_irq; -osSemaphoreId sem_uart_dma; -osSemaphoreId sem_usb_rx; -osSemaphoreId sem_usb_tx; -osSemaphoreId sem_can; - -osThreadId usb_irq_thread; -const uint32_t stack_size_usb_irq_thread = 2048; // Bytes - -// Place FreeRTOS heap in core coupled memory for better performance -__attribute__((section(".ccmram"))) -uint8_t ucHeap[configTOTAL_HEAP_SIZE]; /* USER CODE END Variables */ osThreadId defaultTaskHandle; const uint32_t stack_size_default_task = 2048; // Bytes @@ -134,28 +115,6 @@ __weak void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTask configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is called if a stack overflow is detected. */ } - -void usb_deferred_interrupt_thread(void * ctx) { - (void) ctx; // unused parameter - - for (;;) { - // Wait for signalling from USB interrupt (OTG_FS_IRQHandler) - osStatus semaphore_status = osSemaphoreWait(sem_usb_irq, osWaitForever); - if (semaphore_status == osOK) { - // We have a new incoming USB transmission: handle it - HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); - // Let the irq (OTG_FS_IRQHandler) fire again. - HAL_NVIC_EnableIRQ(OTG_FS_IRQn); - } - } -} - -void init_deferred_interrupts(void) { - // Start USB interrupt handler thread - osThreadDef(task_usb_pump, usb_deferred_interrupt_thread, osPriorityAboveNormal, 0, stack_size_usb_irq_thread / sizeof(StackType_t)); - usb_irq_thread = osThreadCreate(osThread(task_usb_pump), NULL); -} - /* USER CODE END 4 */ /** @@ -173,33 +132,6 @@ void MX_FREERTOS_Init(void) { /* USER CODE END RTOS_MUTEX */ /* USER CODE BEGIN RTOS_SEMAPHORES */ - // Init usb irq binary semaphore, and start with no tokens by removing the starting one. - osSemaphoreDef(sem_usb_irq); - sem_usb_irq = osSemaphoreCreate(osSemaphore(sem_usb_irq), 1); - osSemaphoreWait(sem_usb_irq, 0); - - // Create a semaphore for UART DMA and remove a token - osSemaphoreDef(sem_uart_dma); - sem_uart_dma = osSemaphoreCreate(osSemaphore(sem_uart_dma), 1); - - // Create a semaphore for USB RX - osSemaphoreDef(sem_usb_rx); - sem_usb_rx = osSemaphoreCreate(osSemaphore(sem_usb_rx), 1); - osSemaphoreWait(sem_usb_rx, 0); // Remove a token. - - // Create a semaphore for USB TX - osSemaphoreDef(sem_usb_tx); - sem_usb_tx = osSemaphoreCreate(osSemaphore(sem_usb_tx), 1); - - osSemaphoreDef(sem_can); - sem_can = osSemaphoreCreate(osSemaphore(sem_can), 1); - osSemaphoreWait(sem_can, 0); - - init_deferred_interrupts(); - - // Load persistent configuration (or defaults) - load_configuration(); - construct_objects(); /* USER CODE END RTOS_SEMAPHORES */ /* USER CODE BEGIN RTOS_TIMERS */ @@ -234,11 +166,6 @@ void StartDefaultTask(void * argument) /* USER CODE BEGIN StartDefaultTask */ - odrive_main(); - - //If we get to here, then the default task is done. - vTaskDelete(defaultTaskHandle); - /* USER CODE END StartDefaultTask */ } diff --git a/Firmware/Board/v3/Src/gpio.c b/Firmware/Board/v3/Src/gpio.c index 635091eea..15a8de7a1 100644 --- a/Firmware/Board/v3/Src/gpio.c +++ b/Firmware/Board/v3/Src/gpio.c @@ -50,8 +50,6 @@ /* Includes ------------------------------------------------------------------*/ #include "gpio.h" /* USER CODE BEGIN 0 */ -#include - #if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 1 \ || HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 2 #include "prev_board_ver/gpio_V3_2.c" @@ -106,12 +104,6 @@ void MX_GPIO_Init(void) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - /*Configure GPIO pins : PBPin PBPin */ - GPIO_InitStruct.Pin = GPIO_6_Pin|GPIO_8_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - /*Configure GPIO pin : PtPin */ GPIO_InitStruct.Pin = EN_GATE_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; @@ -119,12 +111,6 @@ void MX_GPIO_Init(void) GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(EN_GATE_GPIO_Port, &GPIO_InitStruct); - /*Configure GPIO pin : PtPin */ - GPIO_InitStruct.Pin = GPIO_7_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIO_7_GPIO_Port, &GPIO_InitStruct); - /*Configure GPIO pin : PtPin */ GPIO_InitStruct.Pin = nFAULT_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; @@ -136,149 +122,6 @@ void MX_GPIO_Init(void) /* USER CODE BEGIN 2 */ #endif // End GPIO Include -// @brief Returns the IRQ number associated with a certain pin. -// Note that all GPIOs with the same pin number map to the same IRQn, -// no matter which port they belong to. -IRQn_Type get_irq_number(uint16_t pin) { - uint16_t pin_number = 0; - pin >>= 1; - while (pin) { - pin >>= 1; - pin_number++; - } - switch (pin_number) { - case 0: return EXTI0_IRQn; - case 1: return EXTI1_IRQn; - case 2: return EXTI2_IRQn; - case 3: return EXTI3_IRQn; - case 4: return EXTI4_IRQn; - case 5: - case 6: - case 7: - case 8: - case 9: return EXTI9_5_IRQn; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: return EXTI15_10_IRQn; - default: return 0; // impossible - } -} - -// @brief Puts the GPIO's 1 and 2 into UART mode. -// This will disable any interrupt subscribers of these GPIOs. -void SetGPIO12toUART() { - GPIO_InitTypeDef GPIO_InitStruct; - - // make sure nothing is hogging the GPIO's - GPIO_unsubscribe(GPIO_1_GPIO_Port, GPIO_1_Pin); - GPIO_unsubscribe(GPIO_2_GPIO_Port, GPIO_2_Pin); - - GPIO_InitStruct.Pin = GPIO_1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_PULLDOWN; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF8_UART4; - HAL_GPIO_Init(GPIO_1_GPIO_Port, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = GPIO_2_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF8_UART4; - HAL_GPIO_Init(GPIO_2_GPIO_Port, &GPIO_InitStruct); -} - -// Expected subscriptions: 2x step signal + 2x encoder index signal -#define MAX_SUBSCRIPTIONS 10 -struct subscription_t { - GPIO_TypeDef* GPIO_port; - uint16_t GPIO_pin; - void (*callback)(void*); - void* ctx; -} subscriptions[MAX_SUBSCRIPTIONS] = { 0 }; -size_t n_subscriptions = 0; - -// Sets up the specified GPIO to trigger the specified callback -// on a rising edge of the GPIO. -// @param pull_up_down: one of GPIO_NOPULL, GPIO_PULLUP or GPIO_PULLDOWN -bool GPIO_subscribe(GPIO_TypeDef* GPIO_port, uint16_t GPIO_pin, - uint32_t pull_up_down, void (*callback)(void*), void* ctx) { - - // Register handler (or reuse existing registration) - // TODO: make thread safe - struct subscription_t* subscription = NULL; - for (size_t i = 0; i < n_subscriptions; ++i) { - if (subscriptions[i].GPIO_port == GPIO_port && - subscriptions[i].GPIO_pin == GPIO_pin) - subscription = &subscriptions[i]; - } - if (!subscription) { - if (n_subscriptions >= MAX_SUBSCRIPTIONS) - return false; - subscription = &subscriptions[n_subscriptions++]; - } - - *subscription = (struct subscription_t){ - .GPIO_port = GPIO_port, - .GPIO_pin = GPIO_pin, - .callback = callback, - .ctx = ctx - }; - - // Set up GPIO - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.Pin = GPIO_pin; - GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; - GPIO_InitStruct.Pull = pull_up_down; - HAL_GPIO_Init(GPIO_port, &GPIO_InitStruct); - - // Clear any previous triggers - __HAL_GPIO_EXTI_CLEAR_IT(GPIO_pin); - // Enable interrupt - HAL_NVIC_SetPriority(get_irq_number(GPIO_pin), 0, 0); - HAL_NVIC_EnableIRQ(get_irq_number(GPIO_pin)); - return true; -} - -void GPIO_unsubscribe(GPIO_TypeDef* GPIO_port, uint16_t GPIO_pin) { - bool is_pin_in_use = false; - for (size_t i = 0; i < n_subscriptions; ++i) { - if (subscriptions[i].GPIO_port == GPIO_port && - subscriptions[i].GPIO_pin == GPIO_pin) { - subscriptions[i].callback = NULL; - subscriptions[i].ctx = NULL; - } else if (subscriptions[i].GPIO_pin == GPIO_pin) { - is_pin_in_use = true; - } - } - if (!is_pin_in_use) - HAL_NVIC_DisableIRQ(get_irq_number(GPIO_pin)); -} - -// @brief Configures the specified GPIO as an analog input. -// This disables any subscriptions that were active for this pin. -void GPIO_set_to_analog(GPIO_TypeDef* GPIO_port, uint16_t GPIO_pin) { - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_unsubscribe(GPIO_port, GPIO_pin); - GPIO_InitStruct.Pin = GPIO_pin; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIO_port, &GPIO_InitStruct); -} - -//Dispatch processing of external interrupts based on source -void HAL_GPIO_EXTI_Callback(uint16_t GPIO_pin) { - for (size_t i = 0; i < n_subscriptions; ++i) { - if (subscriptions[i].GPIO_pin == GPIO_pin) // TODO: check for port - if (subscriptions[i].callback) - subscriptions[i].callback(subscriptions[i].ctx); - } -} - - /* USER CODE END 2 */ diff --git a/Firmware/Board/v3/Src/i2c.c b/Firmware/Board/v3/Src/i2c.c index bae77f126..c4fca9423 100644 --- a/Firmware/Board/v3/Src/i2c.c +++ b/Firmware/Board/v3/Src/i2c.c @@ -84,23 +84,11 @@ void MX_I2C1_Init(uint8_t addr) void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) { - GPIO_InitTypeDef GPIO_InitStruct; if(i2cHandle->Instance==I2C1) { /* USER CODE BEGIN I2C1_MspInit 0 */ /* USER CODE END I2C1_MspInit 0 */ - - /**I2C1 GPIO Configuration - PB8 ------> I2C1_SCL - PB9 ------> I2C1_SDA - */ - GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9; - GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; - GPIO_InitStruct.Pull = GPIO_PULLUP; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* I2C1 clock enable */ __HAL_RCC_I2C1_CLK_ENABLE(); @@ -143,9 +131,9 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) __HAL_LINKDMA(i2cHandle,hdmatx,hdma_i2c1_tx); /* I2C1 interrupt Init */ - HAL_NVIC_SetPriority(I2C1_EV_IRQn, 5, 0); + HAL_NVIC_SetPriority(I2C1_EV_IRQn, 9, 0); HAL_NVIC_EnableIRQ(I2C1_EV_IRQn); - HAL_NVIC_SetPriority(I2C1_ER_IRQn, 5, 0); + HAL_NVIC_SetPriority(I2C1_ER_IRQn, 9, 0); HAL_NVIC_EnableIRQ(I2C1_ER_IRQn); /* USER CODE BEGIN I2C1_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/main.c b/Firmware/Board/v3/Src/main.c index 154c98afc..c7a8f025f 100644 --- a/Firmware/Board/v3/Src/main.c +++ b/Firmware/Board/v3/Src/main.c @@ -60,9 +60,6 @@ #include "gpio.h" /* USER CODE BEGIN Includes */ -#include -#include "freertos_vars.h" -#include "i2c.h" /* USER CODE END Includes */ /* Private variables ---------------------------------------------------------*/ @@ -83,80 +80,19 @@ void MX_FREERTOS_Init(void); /* USER CODE BEGIN 0 */ -uint32_t _reboot_cookie __attribute__ ((section (".noinit"))); -extern char _estack; // provided by the linker script - -// Gets called from the startup assembly code -void early_start_checks(void) { - if(_reboot_cookie == 0xDEADFE75) { - /* The STM DFU bootloader enables internal pull-up resistors on PB10 (AUX_H) - * and PB11 (AUX_L), thereby causing shoot-through on the brake resistor - * FETs and obliterating them unless external 3.3k pull-down resistors are - * present. Pull-downs are only present on ODrive 3.5 or newer. - * On older boards we disable DFU by default but if the user insists - * there's only one thing left that might save it: time. - * The brake resistor gate driver needs a certain 10V supply (GVDD) to - * make it work. This voltage is supplied by the motor gate drivers which get - * disabled at system reset. So over time GVDD voltage _should_ below - * dangerous levels. This is completely handwavy and should not be relied on - * so you are on your own on if you ignore this warning. - * - * This loop takes 5 cycles per iteration and at this point the system runs - * on the internal 16MHz RC oscillator so the delay is about 2 seconds. - */ - for (size_t i = 0; i < (16000000UL / 5UL * 2UL); ++i) { - __NOP(); - } - _reboot_cookie = 0xDEADBEEF; - } - - /* We could jump to the bootloader directly on demand without rebooting - but that requires us to reset several peripherals and interrupts for it - to function correctly. Therefore it's easier to just reset the entire chip. */ - if(_reboot_cookie == 0xDEADBEEF) { - _reboot_cookie = 0xCAFEFEED; //Reset bootloader trigger - __set_MSP((uintptr_t)&_estack); - // http://www.st.com/content/ccc/resource/technical/document/application_note/6a/17/92/02/58/98/45/0c/CD00264379.pdf/files/CD00264379.pdf - void (*builtin_bootloader)(void) = (void (*)(void))(*((uint32_t *)0x1FFF0004)); - builtin_bootloader(); - } - - /* The bootloader might fail to properly clean up after itself, - so if we're not sure that the system is in a clean state we - just reset it again */ - if(_reboot_cookie != 42) { - _reboot_cookie = 42; - NVIC_SystemReset(); - } -} - /* USER CODE END 0 */ /** * @brief The application entry point. + * => Nope. We provide our own. * * @retval None */ -int main(void) -{ +//int main(void) +//{ +#if 0 /* USER CODE BEGIN 1 */ - // This procedure of building a USB serial number should be identical - // to the way the STM's built-in USB bootloader does it. This means - // that the device will have the same serial number in normal and DFU mode. - uint32_t uuid0 = *(uint32_t *)(UID_BASE + 0); - uint32_t uuid1 = *(uint32_t *)(UID_BASE + 4); - uint32_t uuid2 = *(uint32_t *)(UID_BASE + 8); - uint32_t uuid_mixed_part = uuid0 + uuid2; - serial_number = ((uint64_t)uuid_mixed_part << 16) | (uint64_t)(uuid1 >> 16); - - uint64_t val = serial_number; - for (size_t i = 0; i < 12; ++i) { - serial_number_str[i] = "0123456789ABCDEF"[(val >> (48-4)) & 0xf]; - val <<= 4; - } - serial_number_str[12] = 0; - /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ @@ -193,10 +129,6 @@ int main(void) MX_TIM13_Init(); /* USER CODE BEGIN 2 */ - //Required to use OC4 for ADC triggering. - OC4_PWM_Override(&htim1); - OC4_PWM_Override(&htim8); - /* USER CODE END 2 */ /* Call init function for freertos objects (in freertos.c) */ @@ -219,6 +151,7 @@ int main(void) /* USER CODE END 3 */ } +#endif /** * @brief System Clock Configuration @@ -313,7 +246,7 @@ void _Error_Handler(char *file, int line) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ - while(1) + while(1) // TODO: do something more useful { } /* USER CODE END Error_Handler_Debug */ diff --git a/Firmware/Board/v3/Src/prev_board_ver/adc_V3_2.c b/Firmware/Board/v3/Src/prev_board_ver/adc_V3_2.c index bc2ddddf9..8444ff784 100644 --- a/Firmware/Board/v3/Src/prev_board_ver/adc_V3_2.c +++ b/Firmware/Board/v3/Src/prev_board_ver/adc_V3_2.c @@ -215,9 +215,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1); - /* ADC1 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ @@ -253,9 +250,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - /* ADC2 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC2_MspInit 1 */ /* USER CODE END ADC2_MspInit 1 */ @@ -279,9 +273,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - /* ADC3 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC3_MspInit 1 */ /* USER CODE END ADC3_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/prev_board_ver/adc_V3_4.c b/Firmware/Board/v3/Src/prev_board_ver/adc_V3_4.c index 31ce77d04..5f1115f58 100644 --- a/Firmware/Board/v3/Src/prev_board_ver/adc_V3_4.c +++ b/Firmware/Board/v3/Src/prev_board_ver/adc_V3_4.c @@ -214,9 +214,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1); - /* ADC1 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ @@ -251,9 +248,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - /* ADC2 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC2_MspInit 1 */ /* USER CODE END ADC2_MspInit 1 */ @@ -277,9 +271,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - /* ADC3 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC3_MspInit 1 */ /* USER CODE END ADC3_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/spi.c b/Firmware/Board/v3/Src/spi.c index a3a3311e5..cde8d47ff 100644 --- a/Firmware/Board/v3/Src/spi.c +++ b/Firmware/Board/v3/Src/spi.c @@ -101,16 +101,21 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle) PC11 ------> SPI3_MISO PC12 ------> SPI3_MOSI */ - GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12; + GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_PULLUP; // required for disconnect detection on SPI encoders + GPIO_InitStruct.Pull = GPIO_PULLDOWN; // Idle clock and MOSI low. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF6_SPI3; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + // MISO pull-up required for disconnect detection on SPI encoders with even parity + GPIO_InitStruct.Pin = GPIO_PIN_11; + GPIO_InitStruct.Pull = GPIO_PULLUP; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + /* SPI3 DMA Init */ /* SPI3_TX Init */ - hdma_spi3_tx.Instance = DMA1_Stream5; + hdma_spi3_tx.Instance = DMA1_Stream7; hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0; hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE; @@ -125,7 +130,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle) } hdma_spi3_tx.Init.Mode = DMA_NORMAL; - hdma_spi3_tx.Init.Priority = DMA_PRIORITY_MEDIUM; + hdma_spi3_tx.Init.Priority = DMA_PRIORITY_HIGH; // SPI TX must have higher priority than SPI RX hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK) { @@ -158,8 +163,8 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle) __HAL_LINKDMA(spiHandle,hdmarx,hdma_spi3_rx); /* SPI3 interrupt Init */ - HAL_NVIC_SetPriority(SPI3_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(SPI3_IRQn); + //HAL_NVIC_SetPriority(SPI3_IRQn, 3, 0); + //HAL_NVIC_EnableIRQ(SPI3_IRQn); /* USER CODE BEGIN SPI3_MspInit 1 */ /* USER CODE END SPI3_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/stm32f4xx_hal_msp.c b/Firmware/Board/v3/Src/stm32f4xx_hal_msp.c index e49480c0c..d8c864e0d 100644 --- a/Firmware/Board/v3/Src/stm32f4xx_hal_msp.c +++ b/Firmware/Board/v3/Src/stm32f4xx_hal_msp.c @@ -74,7 +74,7 @@ void HAL_MspInit(void) /* UsageFault_IRQn interrupt configuration */ HAL_NVIC_SetPriority(UsageFault_IRQn, 0, 0); /* SVCall_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(SVCall_IRQn, 0, 0); + HAL_NVIC_SetPriority(SVCall_IRQn, 3, 0); /* DebugMonitor_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DebugMonitor_IRQn, 0, 0); /* PendSV_IRQn interrupt configuration */ diff --git a/Firmware/Board/v3/Src/stm32f4xx_it.c b/Firmware/Board/v3/Src/stm32f4xx_it.c index 7746ae87d..59ab950bd 100644 --- a/Firmware/Board/v3/Src/stm32f4xx_it.c +++ b/Firmware/Board/v3/Src/stm32f4xx_it.c @@ -35,26 +35,10 @@ #include "stm32f4xx.h" #include "stm32f4xx_it.h" #include "cmsis_os.h" - -/* USER CODE BEGIN 0 */ -#include "freertos_vars.h" #include -typedef void (*ADC_handler_t)(ADC_HandleTypeDef* hadc, bool injected); -void ADC_IRQ_Dispatch(ADC_HandleTypeDef* hadc, ADC_handler_t callback); - -typedef void (*TIM_capture_callback_t)(int channel, uint32_t timestamp); -void decode_tim_capture(TIM_HandleTypeDef *htim, TIM_capture_callback_t callback); - -// TODO: move somewhere else -void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected); -void vbus_sense_adc_cb(ADC_HandleTypeDef* hadc, bool injected); -void tim_update_cb(TIM_HandleTypeDef* htim); -void pwm_in_cb(int channel, uint32_t timestamp); - -extern TIM_HandleTypeDef htim1; -extern I2C_HandleTypeDef hi2c1; - +/* USER CODE BEGIN 0 */ +#include /* USER CODE END 0 */ /* External variables --------------------------------------------------------*/ @@ -70,7 +54,10 @@ extern TIM_HandleTypeDef htim5; extern TIM_HandleTypeDef htim8; extern DMA_HandleTypeDef hdma_uart4_rx; extern DMA_HandleTypeDef hdma_uart4_tx; +extern DMA_HandleTypeDef hdma_usart2_rx; +extern DMA_HandleTypeDef hdma_usart2_tx; extern UART_HandleTypeDef huart4; +extern UART_HandleTypeDef huart2; extern TIM_HandleTypeDef htim14; @@ -84,14 +71,18 @@ extern TIM_HandleTypeDef htim14; void NMI_Handler(void) { /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ - + COUNT_IRQ(NonMaskableInt_IRQn); /* USER CODE END NonMaskableInt_IRQn 0 */ /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ /* USER CODE END NonMaskableInt_IRQn 1 */ } +__attribute__((used)) void get_regs(void** stack_ptr) { + TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM + TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM + void* volatile r0 __attribute__((unused)) = stack_ptr[0]; void* volatile r1 __attribute__((unused)) = stack_ptr[1]; void* volatile r2 __attribute__((unused)) = stack_ptr[2]; @@ -102,7 +93,14 @@ void get_regs(void** stack_ptr) { void* volatile pc __attribute__((unused)) = stack_ptr[6]; // Program counter void* volatile psr __attribute__((unused)) = stack_ptr[7]; // Program status register - volatile bool stay_looping = true; + void* volatile cfsr __attribute__((unused)) = (void*)SCB->CFSR; // Configurable fault status register + void* volatile cpacr __attribute__((unused)) = (void*)SCB->CPACR; + void* volatile fpccr __attribute__((unused)) = (void*)FPU->FPCCR; + + volatile bool preciserr __attribute__((unused)) = (uint32_t)cfsr & 0x200; + volatile bool ibuserr __attribute__((unused)) = (uint32_t)cfsr & 0x100; + + volatile int stay_looping = 1; while(stay_looping); } @@ -127,11 +125,13 @@ void HardFault_Handler(void) void MemManage_Handler(void) { /* USER CODE BEGIN MemoryManagement_IRQn 0 */ - + COUNT_IRQ(MemoryManagement_IRQn); /* USER CODE END MemoryManagement_IRQn 0 */ while (1) { /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */ + TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM + TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM /* USER CODE END W1_MemoryManagement_IRQn 0 */ } /* USER CODE BEGIN MemoryManagement_IRQn 1 */ @@ -145,11 +145,13 @@ void MemManage_Handler(void) void BusFault_Handler(void) { /* USER CODE BEGIN BusFault_IRQn 0 */ - + COUNT_IRQ(BusFault_IRQn); /* USER CODE END BusFault_IRQn 0 */ while (1) { /* USER CODE BEGIN W1_BusFault_IRQn 0 */ + TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM + TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM /* USER CODE END W1_BusFault_IRQn 0 */ } /* USER CODE BEGIN BusFault_IRQn 1 */ @@ -163,11 +165,13 @@ void BusFault_Handler(void) void UsageFault_Handler(void) { /* USER CODE BEGIN UsageFault_IRQn 0 */ - + COUNT_IRQ(UsageFault_IRQn); /* USER CODE END UsageFault_IRQn 0 */ while (1) { /* USER CODE BEGIN W1_UsageFault_IRQn 0 */ + TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM + TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM /* USER CODE END W1_UsageFault_IRQn 0 */ } /* USER CODE BEGIN UsageFault_IRQn 1 */ @@ -181,7 +185,7 @@ void UsageFault_Handler(void) void DebugMon_Handler(void) { /* USER CODE BEGIN DebugMonitor_IRQn 0 */ - + COUNT_IRQ(DebugMonitor_IRQn); /* USER CODE END DebugMonitor_IRQn 0 */ /* USER CODE BEGIN DebugMonitor_IRQn 1 */ @@ -194,7 +198,7 @@ void DebugMon_Handler(void) void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ - + COUNT_IRQ(SysTick_IRQn); /* USER CODE END SysTick_IRQn 0 */ osSystickHandler(); /* USER CODE BEGIN SysTick_IRQn 1 */ @@ -215,7 +219,7 @@ void SysTick_Handler(void) void DMA1_Stream0_IRQHandler(void) { /* USER CODE BEGIN DMA1_Stream0_IRQn 0 */ - + COUNT_IRQ(DMA1_Stream0_IRQn); /* USER CODE END DMA1_Stream0_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_spi3_rx); /* USER CODE BEGIN DMA1_Stream0_IRQn 1 */ @@ -229,7 +233,7 @@ void DMA1_Stream0_IRQHandler(void) void DMA1_Stream2_IRQHandler(void) { /* USER CODE BEGIN DMA1_Stream2_IRQn 0 */ - + COUNT_IRQ(DMA1_Stream2_IRQn); /* USER CODE END DMA1_Stream2_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_uart4_rx); /* USER CODE BEGIN DMA1_Stream2_IRQn 1 */ @@ -243,7 +247,7 @@ void DMA1_Stream2_IRQHandler(void) void DMA1_Stream4_IRQHandler(void) { /* USER CODE BEGIN DMA1_Stream4_IRQn 0 */ - + COUNT_IRQ(DMA1_Stream4_IRQn); /* USER CODE END DMA1_Stream4_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_uart4_tx); /* USER CODE BEGIN DMA1_Stream4_IRQn 1 */ @@ -257,38 +261,40 @@ void DMA1_Stream4_IRQHandler(void) void DMA1_Stream5_IRQHandler(void) { /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */ - + COUNT_IRQ(DMA1_Stream5_IRQn); /* USER CODE END DMA1_Stream5_IRQn 0 */ - HAL_DMA_IRQHandler(&hdma_spi3_tx); + HAL_DMA_IRQHandler(&hdma_usart2_rx); /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */ /* USER CODE END DMA1_Stream5_IRQn 1 */ } /** -* @brief This function handles ADC1, ADC2 and ADC3 global interrupts. +* @brief This function handles DMA1 stream6 global interrupt. */ -void ADC_IRQHandler(void) +void DMA1_Stream6_IRQHandler(void) { - /* USER CODE BEGIN ADC_IRQn 0 */ + /* USER CODE BEGIN DMA1_Stream6_IRQn 0 */ + COUNT_IRQ(DMA1_Stream6_IRQn); + /* USER CODE END DMA1_Stream6_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_usart2_tx); + /* USER CODE BEGIN DMA1_Stream6_IRQn 1 */ - // The HAL's ADC handling mechanism adds many clock cycles of overhead - // So we bypass it and handle the logic ourselves. - //@TODO add vbus measurement on adc1 here - ADC_IRQ_Dispatch(&hadc1, &vbus_sense_adc_cb); - ADC_IRQ_Dispatch(&hadc2, &pwm_trig_adc_cb); - ADC_IRQ_Dispatch(&hadc3, &pwm_trig_adc_cb); - - // Bypass HAL - return; + /* USER CODE END DMA1_Stream6_IRQn 1 */ +} - /* USER CODE END ADC_IRQn 0 */ - HAL_ADC_IRQHandler(&hadc1); - HAL_ADC_IRQHandler(&hadc2); - HAL_ADC_IRQHandler(&hadc3); - /* USER CODE BEGIN ADC_IRQn 1 */ +/** +* @brief This function handles DMA1 stream7 global interrupt. +*/ +void DMA1_Stream7_IRQHandler(void) +{ + /* USER CODE BEGIN DMA1_Stream7_IRQn 0 */ + COUNT_IRQ(DMA1_Stream7_IRQn); + /* USER CODE END DMA1_Stream7_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_spi3_tx); + /* USER CODE BEGIN DMA1_Stream7_IRQn 1 */ - /* USER CODE END ADC_IRQn 1 */ + /* USER CODE END DMA1_Stream7_IRQn 1 */ } /** @@ -297,7 +303,7 @@ void ADC_IRQHandler(void) void CAN1_TX_IRQHandler(void) { /* USER CODE BEGIN CAN1_TX_IRQn 0 */ - + COUNT_IRQ(CAN1_TX_IRQn); /* USER CODE END CAN1_TX_IRQn 0 */ HAL_CAN_IRQHandler(&hcan1); /* USER CODE BEGIN CAN1_TX_IRQn 1 */ @@ -311,7 +317,7 @@ void CAN1_TX_IRQHandler(void) void CAN1_RX0_IRQHandler(void) { /* USER CODE BEGIN CAN1_RX0_IRQn 0 */ - + COUNT_IRQ(CAN1_RX0_IRQn); /* USER CODE END CAN1_RX0_IRQn 0 */ HAL_CAN_IRQHandler(&hcan1); /* USER CODE BEGIN CAN1_RX0_IRQn 1 */ @@ -325,7 +331,7 @@ void CAN1_RX0_IRQHandler(void) void CAN1_RX1_IRQHandler(void) { /* USER CODE BEGIN CAN1_RX1_IRQn 0 */ - + COUNT_IRQ(CAN1_RX1_IRQn); /* USER CODE END CAN1_RX1_IRQn 0 */ HAL_CAN_IRQHandler(&hcan1); /* USER CODE BEGIN CAN1_RX1_IRQn 1 */ @@ -339,7 +345,7 @@ void CAN1_RX1_IRQHandler(void) void CAN1_SCE_IRQHandler(void) { /* USER CODE BEGIN CAN1_SCE_IRQn 0 */ - + COUNT_IRQ(CAN1_SCE_IRQn); /* USER CODE END CAN1_SCE_IRQn 0 */ HAL_CAN_IRQHandler(&hcan1); /* USER CODE BEGIN CAN1_SCE_IRQn 1 */ @@ -347,13 +353,27 @@ void CAN1_SCE_IRQHandler(void) /* USER CODE END CAN1_SCE_IRQn 1 */ } +/** + * @brief This function handles USART2 global interrupt. + */ +void USART2_IRQHandler(void) +{ + /* USER CODE BEGIN USART2_IRQn 0 */ + + /* USER CODE END USART2_IRQn 0 */ + HAL_UART_IRQHandler(&huart2); + /* USER CODE BEGIN USART2_IRQn 1 */ + + /* USER CODE END USART2_IRQn 1 */ +} + /** * @brief This function handles TIM8 trigger and commutation interrupts and TIM14 global interrupt. */ void TIM8_TRG_COM_TIM14_IRQHandler(void) { /* USER CODE BEGIN TIM8_TRG_COM_TIM14_IRQn 0 */ - + COUNT_IRQ(TIM8_TRG_COM_TIM14_IRQn); /* USER CODE END TIM8_TRG_COM_TIM14_IRQn 0 */ HAL_TIM_IRQHandler(&htim8); HAL_TIM_IRQHandler(&htim14); @@ -362,30 +382,13 @@ void TIM8_TRG_COM_TIM14_IRQHandler(void) /* USER CODE END TIM8_TRG_COM_TIM14_IRQn 1 */ } -/** -* @brief This function handles TIM5 global interrupt. -*/ -void TIM5_IRQHandler(void) -{ - /* USER CODE BEGIN TIM5_IRQn 0 */ - - // We know we only use capture mode here, so bypass HAL - decode_tim_capture(&htim5, &pwm_in_cb); - - /* USER CODE END TIM5_IRQn 0 */ - HAL_TIM_IRQHandler(&htim5); - /* USER CODE BEGIN TIM5_IRQn 1 */ - - /* USER CODE END TIM5_IRQn 1 */ -} - /** * @brief This function handles SPI3 global interrupt. */ void SPI3_IRQHandler(void) { /* USER CODE BEGIN SPI3_IRQn 0 */ - + COUNT_IRQ(SPI3_IRQn); /* USER CODE END SPI3_IRQn 0 */ HAL_SPI_IRQHandler(&hspi3); /* USER CODE BEGIN SPI3_IRQn 1 */ @@ -399,7 +402,7 @@ void SPI3_IRQHandler(void) void UART4_IRQHandler(void) { /* USER CODE BEGIN UART4_IRQn 0 */ - + COUNT_IRQ(UART4_IRQn); /* USER CODE END UART4_IRQn 0 */ HAL_UART_IRQHandler(&huart4); /* USER CODE BEGIN UART4_IRQn 1 */ @@ -407,159 +410,7 @@ void UART4_IRQHandler(void) /* USER CODE END UART4_IRQn 1 */ } -/** -* @brief This function handles USB On The Go FS global interrupt. -*/ -void OTG_FS_IRQHandler(void) -{ - /* USER CODE BEGIN OTG_FS_IRQn 0 */ - - // Mask interrupt, and signal processing of interrupt by usb_cmd_thread - // The thread will re-enable the interrupt when all pending irqs are clear. - HAL_NVIC_DisableIRQ(OTG_FS_IRQn); - osSemaphoreRelease(sem_usb_irq); - // Bypass interrupt processing here - return; - - /* USER CODE END OTG_FS_IRQn 0 */ - HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); - /* USER CODE BEGIN OTG_FS_IRQn 1 */ - - /* USER CODE END OTG_FS_IRQn 1 */ -} - /* USER CODE BEGIN 1 */ -void ADC_IRQ_Dispatch(ADC_HandleTypeDef* hadc, ADC_handler_t callback) { - - // Injected measurements - uint32_t JEOC = __HAL_ADC_GET_FLAG(hadc, ADC_FLAG_JEOC); - uint32_t JEOC_IT_EN = __HAL_ADC_GET_IT_SOURCE(hadc, ADC_IT_JEOC); - if (JEOC && JEOC_IT_EN) { - callback(hadc, true); - __HAL_ADC_CLEAR_FLAG(hadc, (ADC_FLAG_JSTRT | ADC_FLAG_JEOC)); - } - // Regular measurements - uint32_t EOC = __HAL_ADC_GET_FLAG(hadc, ADC_FLAG_EOC); - uint32_t EOC_IT_EN = __HAL_ADC_GET_IT_SOURCE(hadc, ADC_IT_EOC); - if (EOC && EOC_IT_EN) { - callback(hadc, false); - __HAL_ADC_CLEAR_FLAG(hadc, (ADC_FLAG_STRT | ADC_FLAG_EOC)); - } -} - -void decode_tim_capture(TIM_HandleTypeDef *htim, TIM_capture_callback_t callback) { - if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1)) { - __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1); - callback(1, htim->Instance->CCR1); - } - if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2)) { - __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2); - callback(2, htim->Instance->CCR2); - } - if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3)) { - __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC3); - callback(3, htim->Instance->CCR3); - } - if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4)) { - __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC4); - callback(4, htim->Instance->CCR4); - } -} - -/** -* @brief This function handles TIM1 update interrupt and TIM10 global interrupt. -*/ -void TIM1_UP_TIM10_IRQHandler(void) -{ - __HAL_TIM_CLEAR_IT(&htim1, TIM_IT_UPDATE); - tim_update_cb(&htim1); -} - -/** -* @brief This function handles TIM8 update interrupt and TIM13 global interrupt. -*/ -void TIM8_UP_TIM13_IRQHandler(void) -{ - __HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE); - tim_update_cb(&htim8); -} - - -/** -* @brief This function handles I2C1 event interrupt. -*/ -void I2C1_EV_IRQHandler(void) -{ - HAL_I2C_EV_IRQHandler(&hi2c1); -} - -/** -* @brief This function handles I2C1 error interrupt. -*/ -void I2C1_ER_IRQHandler(void) -{ - HAL_I2C_ER_IRQHandler(&hi2c1); -} - -/** -* @brief This function handles EXTI line0 interrupt. -*/ -void EXTI0_IRQHandler(void) -{ - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); -} - -/** -* @brief This function handles EXTI line2 interrupt. -*/ -void EXTI2_IRQHandler(void) -{ - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2); -} - -/** -* @brief This function handles EXTI line3 interrupt. -*/ -void EXTI3_IRQHandler(void) -{ - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); -} - -/** -* @brief This function handles EXTI line4 interrupt. -*/ -void EXTI4_IRQHandler(void) -{ - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); -} - -/** -* @brief This function handles EXTI lines 5-9 interrupt. -*/ -void EXTI9_5_IRQHandler(void) -{ - // The true source of the interrupt is checked inside HAL_GPIO_EXTI_IRQHandler() - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5); - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6); - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7); - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8); - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9); -} - -/** -* @brief This function handles EXTI lines 10-15 interrupt. -*/ -void EXTI15_10_IRQHandler(void) -{ - // The true source of the interrupt is checked inside HAL_GPIO_EXTI_IRQHandler() - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10); - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11); - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12); - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13); - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_14); - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15); -} - /* USER CODE END 1 */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Src/tim.c b/Firmware/Board/v3/Src/tim.c index 97205c15b..40c5c8297 100644 --- a/Firmware/Board/v3/Src/tim.c +++ b/Firmware/Board/v3/Src/tim.c @@ -54,24 +54,6 @@ /* USER CODE BEGIN 0 */ -// To trigger the ADC, we must use an output channel that is in PWM mode -// However, CubeMX does not allow you to set up a channel as PWM without an output pin. -// This will set OC4 to PWM mode. Also, triggering doesn't work if the compare register -// (called pulse here) is 0, so we initialise it to 1. -void OC4_PWM_Override(TIM_HandleTypeDef* htim) { - - TIM_OC_InitTypeDef sConfigOC; - sConfigOC.OCMode = TIM_OCMODE_PWM2; - sConfigOC.Pulse = 1; - sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; - sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; - sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; - sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; - sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; - - HAL_TIM_OC_ConfigChannel(htim, &sConfigOC, TIM_CHANNEL_4); -} - /* USER CODE END 0 */ TIM_HandleTypeDef htim1; @@ -401,10 +383,6 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle) /* USER CODE END TIM1_MspInit 0 */ /* TIM1 clock enable */ __HAL_RCC_TIM1_CLK_ENABLE(); - - /* TIM1 interrupt Init */ - HAL_NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn); /* USER CODE BEGIN TIM1_MspInit 1 */ /* USER CODE END TIM1_MspInit 1 */ @@ -416,10 +394,6 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle) /* USER CODE END TIM13_MspInit 0 */ /* TIM13 clock enable */ __HAL_RCC_TIM13_CLK_ENABLE(); - - /* TIM13 interrupt Init */ - HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn); /* USER CODE BEGIN TIM13_MspInit 1 */ /* USER CODE END TIM13_MspInit 1 */ @@ -447,12 +421,6 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle) /* USER CODE END TIM8_MspInit 0 */ /* TIM8 clock enable */ __HAL_RCC_TIM8_CLK_ENABLE(); - - /* TIM8 interrupt Init */ - HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn); - HAL_NVIC_SetPriority(TIM8_TRG_COM_TIM14_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(TIM8_TRG_COM_TIM14_IRQn); /* USER CODE BEGIN TIM8_MspInit 1 */ /* USER CODE END TIM8_MspInit 1 */ @@ -462,7 +430,6 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle) void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle) { - GPIO_InitTypeDef GPIO_InitStruct; if(tim_encoderHandle->Instance==TIM3) { /* USER CODE BEGIN TIM3_MspInit 0 */ @@ -470,17 +437,6 @@ void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle) /* USER CODE END TIM3_MspInit 0 */ /* TIM3 clock enable */ __HAL_RCC_TIM3_CLK_ENABLE(); - - /**TIM3 GPIO Configuration - PB4 ------> TIM3_CH1 - PB5 ------> TIM3_CH2 - */ - GPIO_InitStruct.Pin = M0_ENC_A_Pin|M0_ENC_B_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF2_TIM3; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* USER CODE BEGIN TIM3_MspInit 1 */ @@ -493,17 +449,6 @@ void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle) /* USER CODE END TIM4_MspInit 0 */ /* TIM4 clock enable */ __HAL_RCC_TIM4_CLK_ENABLE(); - - /**TIM4 GPIO Configuration - PB6 ------> TIM4_CH1 - PB7 ------> TIM4_CH2 - */ - GPIO_InitStruct.Pin = M1_ENC_A_Pin|M1_ENC_B_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF2_TIM4; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* USER CODE BEGIN TIM4_MspInit 1 */ @@ -514,7 +459,6 @@ void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle) void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* tim_icHandle) { - GPIO_InitTypeDef GPIO_InitStruct; if(tim_icHandle->Instance==TIM5) { /* USER CODE BEGIN TIM5_MspInit 0 */ @@ -522,20 +466,9 @@ void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* tim_icHandle) /* USER CODE END TIM5_MspInit 0 */ /* TIM5 clock enable */ __HAL_RCC_TIM5_CLK_ENABLE(); - - /**TIM5 GPIO Configuration - PA2 ------> TIM5_CH3 - PA3 ------> TIM5_CH4 - */ - GPIO_InitStruct.Pin = GPIO_3_Pin|GPIO_4_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF2_TIM5; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* TIM5 interrupt Init */ - HAL_NVIC_SetPriority(TIM5_IRQn, 5, 0); + HAL_NVIC_SetPriority(TIM5_IRQn, 1, 0); HAL_NVIC_EnableIRQ(TIM5_IRQn); /* USER CODE BEGIN TIM5_MspInit 1 */ @@ -652,7 +585,6 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle) __HAL_RCC_TIM1_CLK_DISABLE(); /* TIM1 interrupt Deinit */ - HAL_NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn); /* USER CODE BEGIN TIM1_MspDeInit 1 */ /* USER CODE END TIM1_MspDeInit 1 */ @@ -710,8 +642,6 @@ void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef* tim_pwmHandle) */ /* HAL_NVIC_DisableIRQ(TIM8_UP_TIM13_IRQn); */ /* USER CODE END TIM8:TIM8_UP_TIM13_IRQn disable */ - - HAL_NVIC_DisableIRQ(TIM8_TRG_COM_TIM14_IRQn); /* USER CODE BEGIN TIM8_MspDeInit 1 */ /* USER CODE END TIM8_MspDeInit 1 */ diff --git a/Firmware/Board/v3/Src/usart.c b/Firmware/Board/v3/Src/usart.c index 0c4bed6f5..cddfd4e05 100644 --- a/Firmware/Board/v3/Src/usart.c +++ b/Firmware/Board/v3/Src/usart.c @@ -58,15 +58,18 @@ /* USER CODE END 0 */ UART_HandleTypeDef huart4; +UART_HandleTypeDef huart2; DMA_HandleTypeDef hdma_uart4_rx; DMA_HandleTypeDef hdma_uart4_tx; +DMA_HandleTypeDef hdma_usart2_rx; +DMA_HandleTypeDef hdma_usart2_tx; /* UART4 init function */ void MX_UART4_Init(void) { huart4.Instance = UART4; - huart4.Init.BaudRate = 115200; // Provisionally this can be changed to 921600 for faster transfers, the low power Arduinos will not keep up. + //huart4.Init.BaudRate = 115200; // Provisionally this can be changed to 921600 for faster transfers, the low power Arduinos will not keep up. huart4.Init.WordLength = UART_WORDLENGTH_8B; huart4.Init.StopBits = UART_STOPBITS_1; huart4.Init.Parity = UART_PARITY_NONE; @@ -78,12 +81,30 @@ void MX_UART4_Init(void) _Error_Handler(__FILE__, __LINE__); } +} +/* USART2 init function */ + +void MX_USART2_UART_Init(void) +{ + + huart2.Instance = USART2; + //huart2.Init.BaudRate = 115200; + huart2.Init.WordLength = UART_WORDLENGTH_8B; + huart2.Init.StopBits = UART_STOPBITS_1; + huart2.Init.Parity = UART_PARITY_NONE; + huart2.Init.Mode = UART_MODE_TX_RX; + huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; + huart2.Init.OverSampling = UART_OVERSAMPLING_16; + if (HAL_UART_Init(&huart2) != HAL_OK) + { + Error_Handler(); + } + } void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) { - GPIO_InitTypeDef GPIO_InitStruct; if(uartHandle->Instance==UART4) { /* USER CODE BEGIN UART4_MspInit 0 */ @@ -91,24 +112,6 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) /* USER CODE END UART4_MspInit 0 */ /* UART4 clock enable */ __HAL_RCC_UART4_CLK_ENABLE(); - - /**UART4 GPIO Configuration - PA0-WKUP ------> UART4_TX - PA1 ------> UART4_RX - */ - GPIO_InitStruct.Pin = GPIO_1_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_PULLDOWN; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF8_UART4; - HAL_GPIO_Init(GPIO_1_GPIO_Port, &GPIO_InitStruct); - - GPIO_InitStruct.Pin = GPIO_2_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF8_UART4; - HAL_GPIO_Init(GPIO_2_GPIO_Port, &GPIO_InitStruct); /* UART4 DMA Init */ /* UART4_RX Init */ @@ -148,12 +151,64 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) __HAL_LINKDMA(uartHandle,hdmatx,hdma_uart4_tx); /* UART4 interrupt Init */ - HAL_NVIC_SetPriority(UART4_IRQn, 5, 0); + HAL_NVIC_SetPriority(UART4_IRQn, 10, 0); HAL_NVIC_EnableIRQ(UART4_IRQn); /* USER CODE BEGIN UART4_MspInit 1 */ /* USER CODE END UART4_MspInit 1 */ } + else if(uartHandle->Instance==USART2) + { + /* USER CODE BEGIN USART2_MspInit 0 */ + + /* USER CODE END USART2_MspInit 0 */ + /* USART2 clock enable */ + __HAL_RCC_USART2_CLK_ENABLE(); + + /* USART2 DMA Init */ + /* USART2_RX Init */ + hdma_usart2_rx.Instance = DMA1_Stream5; + hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4; + hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; + hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE; + hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_usart2_rx.Init.Mode = DMA_CIRCULAR; + hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW; + hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK) + { + _Error_Handler(__FILE__, __LINE__); + } + + __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx); + + /* USART2_TX Init */ + hdma_usart2_tx.Instance = DMA1_Stream6; + hdma_usart2_tx.Init.Channel = DMA_CHANNEL_4; + hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE; + hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_usart2_tx.Init.Mode = DMA_NORMAL; + hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW; + hdma_usart2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK) + { + _Error_Handler(__FILE__, __LINE__); + } + + __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart2_tx); + + /* USART2 interrupt Init */ + HAL_NVIC_SetPriority(USART2_IRQn, 10, 0); + HAL_NVIC_EnableIRQ(USART2_IRQn); + /* USER CODE BEGIN USART2_MspInit 1 */ + + /* USER CODE END USART2_MspInit 1 */ + } } void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) @@ -166,12 +221,6 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) /* USER CODE END UART4_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_UART4_CLK_DISABLE(); - - /**UART4 GPIO Configuration - PA0-WKUP ------> UART4_TX - PA1 ------> UART4_RX - */ - HAL_GPIO_DeInit(GPIOA, GPIO_1_Pin|GPIO_2_Pin); /* UART4 DMA DeInit */ HAL_DMA_DeInit(uartHandle->hdmarx); @@ -183,6 +232,24 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) /* USER CODE END UART4_MspDeInit 1 */ } + else if(uartHandle->Instance==USART2) + { + /* USER CODE BEGIN USART2_MspDeInit 0 */ + + /* USER CODE END USART2_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_USART2_CLK_DISABLE(); + + /* USART2 DMA DeInit */ + HAL_DMA_DeInit(uartHandle->hdmarx); + HAL_DMA_DeInit(uartHandle->hdmatx); + + /* USART2 interrupt Deinit */ + HAL_NVIC_DisableIRQ(USART2_IRQn); + /* USER CODE BEGIN USART2_MspDeInit 1 */ + + /* USER CODE END USART2_MspDeInit 1 */ + } } /* USER CODE BEGIN 1 */ diff --git a/Firmware/Board/v3/Src/usbd_cdc_if.c b/Firmware/Board/v3/Src/usbd_cdc_if.c index b8cb6e6c9..adc19b022 100644 --- a/Firmware/Board/v3/Src/usbd_cdc_if.c +++ b/Firmware/Board/v3/Src/usbd_cdc_if.c @@ -114,15 +114,6 @@ * @brief Private variables. * @{ */ -/* Create buffer for reception and transmission */ -/* It's up to user to redefine and/or remove those define */ -/** Received data over USB are stored in this buffer */ -uint8_t CDCRxBufferFS[APP_RX_DATA_SIZE]; -uint8_t ODRIVERxBufferFS[APP_RX_DATA_SIZE]; - -/** Data to send over USB CDC are stored in this buffer */ -uint8_t CDCTxBufferFS[APP_TX_DATA_SIZE]; -uint8_t ODRIVETxBufferFS[APP_TX_DATA_SIZE]; /* USER CODE BEGIN PRIVATE_VARIABLES */ /* USER CODE END PRIVATE_VARIABLES */ @@ -179,10 +170,7 @@ static int8_t CDC_Init_FS(void) { /* USER CODE BEGIN 3 */ /* Set Application Buffers */ - USBD_CDC_SetTxBuffer(&hUsbDeviceFS, CDCTxBufferFS, 0, CDC_OUT_EP); - USBD_CDC_SetRxBuffer(&hUsbDeviceFS, CDCRxBufferFS, CDC_OUT_EP); - USBD_CDC_SetTxBuffer(&hUsbDeviceFS, ODRIVETxBufferFS, 0, ODRIVE_OUT_EP); - USBD_CDC_SetRxBuffer(&hUsbDeviceFS, ODRIVERxBufferFS, ODRIVE_OUT_EP); + osMessagePut(usb_event_queue, 1, 0); return (USBD_OK); /* USER CODE END 3 */ } @@ -194,6 +182,7 @@ static int8_t CDC_Init_FS(void) static int8_t CDC_DeInit_FS(void) { /* USER CODE BEGIN 4 */ + osMessagePut(usb_event_queue, 2, 0); return (USBD_OK); /* USER CODE END 4 */ } @@ -315,7 +304,6 @@ uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len, uint8_t endpoint_pair) { uint8_t result = USBD_OK; /* USER CODE BEGIN 7 */ - //Check length if (Len > USB_TX_DATA_SIZE) return USBD_FAIL; @@ -324,13 +312,10 @@ uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len, uint8_t endpoint_pair) // Select EP USBD_CDC_EP_HandleTypeDef* hEP_Tx; - uint8_t* TxBuff; - if (endpoint_pair == CDC_OUT_EP) { + if (endpoint_pair == CDC_IN_EP) { hEP_Tx = &hcdc->CDC_Tx; - TxBuff = CDCTxBufferFS; - } else if (endpoint_pair == ODRIVE_OUT_EP) { + } else if (endpoint_pair == ODRIVE_IN_EP) { hEP_Tx = &hcdc->ODRIVE_Tx; - TxBuff = ODRIVETxBufferFS; } else { return USBD_FAIL; } @@ -338,11 +323,8 @@ uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len, uint8_t endpoint_pair) // Check for ongoing transmission if (hEP_Tx->State != 0) return USBD_BUSY; - // memcpy Buf into UserTxBufferFS - memcpy(TxBuff, Buf, Len); - // Update Len - USBD_CDC_SetTxBuffer(&hUsbDeviceFS, TxBuff, Len, endpoint_pair); - result = USBD_CDC_TransmitPacket(&hUsbDeviceFS, endpoint_pair); + + result = USBD_CDC_TransmitPacket(&hUsbDeviceFS, Buf, Len, endpoint_pair); /* USER CODE END 7 */ return result; } diff --git a/Firmware/Board/v3/Src/usbd_conf.c b/Firmware/Board/v3/Src/usbd_conf.c index 2d66d4a12..3d8f6b9af 100644 --- a/Firmware/Board/v3/Src/usbd_conf.c +++ b/Firmware/Board/v3/Src/usbd_conf.c @@ -114,7 +114,7 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle) __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); /* Peripheral interrupt init */ - HAL_NVIC_SetPriority(OTG_FS_IRQn, 5, 0); + HAL_NVIC_SetPriority(OTG_FS_IRQn, 6, 0); HAL_NVIC_EnableIRQ(OTG_FS_IRQn); /* USER CODE BEGIN USB_OTG_FS_MspInit 1 */ @@ -248,6 +248,7 @@ void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd) __HAL_PCD_GATE_PHYCLOCK(hpcd); /* Enter in STOP mode. */ /* USER CODE BEGIN 2 */ + // TODO: do we really want this? if (hpcd->Init.low_power_enable) { /* Set SLEEPDEEP bit and SleepOnExit of Cortex System Control Register */ @@ -676,7 +677,7 @@ USBD_StatusTypeDef USBD_LL_SetUSBAddress(USBD_HandleTypeDef *pdev, uint8_t dev_a * @param size: Data size * @retval USBD status */ -USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint16_t size) +USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size) { HAL_StatusTypeDef hal_status = HAL_OK; USBD_StatusTypeDef usb_status = USBD_OK; @@ -711,7 +712,7 @@ USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, u * @param size: Data size * @retval USBD status */ -USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint16_t size) +USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size) { HAL_StatusTypeDef hal_status = HAL_OK; USBD_StatusTypeDef usb_status = USBD_OK; diff --git a/Firmware/Board/v3/Src/usbd_desc.c b/Firmware/Board/v3/Src/usbd_desc.c index d213f64ac..299ed329e 100644 --- a/Firmware/Board/v3/Src/usbd_desc.c +++ b/Firmware/Board/v3/Src/usbd_desc.c @@ -99,7 +99,6 @@ #define USBD_PRODUCT_STR(s) #s #define USBD_PRODUCT_STRING_FS ODrive HW_VERSION_MAJOR.HW_VERSION_MINOR CDC Interface #define NATIVE_STRING ODrive HW_VERSION_MAJOR.HW_VERSION_MINOR Native Interface -#define USBD_SERIALNUMBER_STRING_FS "000000000001" #define USBD_CONFIGURATION_STRING_FS "CDC Config" #define USBD_INTERFACE_STRING_FS "CDC Interface" diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp new file mode 100644 index 000000000..5f7a36999 --- /dev/null +++ b/Firmware/Board/v3/board.cpp @@ -0,0 +1,593 @@ +/* +* @brief Contains board specific variables and initialization functions +*/ + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +// this should technically be in task_timer.cpp but let's not make a one-line file +bool TaskTimer::enabled = false; + +extern "C" void SystemClock_Config(void); // defined in main.c generated by CubeMX + +#define ControlLoop_IRQHandler OTG_HS_IRQHandler +#define ControlLoop_IRQn OTG_HS_IRQn + +// This array is placed at the very start of the ram (0x20000000) and will be +// used during manufacturing to test the struct that will go to the OTP before +// _actually_ putting anything into OTP. This avoids bulk-destroying STM32's if +// we introduce unintended breakage in our manufacturing scripts. +uint8_t __attribute__((section(".testdata"))) fake_otp[FLASH_OTP_END + 1 - FLASH_OTP_BASE]; + +Stm32SpiArbiter spi3_arbiter{&hspi3}; +Stm32SpiArbiter& ext_spi_arbiter = spi3_arbiter; + +UART_HandleTypeDef* uart_a = &huart4; +UART_HandleTypeDef* uart_b = &huart2; // TODO: this could be supported in ODrive v3.6 (or similar) using STM32's USART2 +UART_HandleTypeDef* uart_c = nullptr; + +Drv8301 m0_gate_driver{ + &spi3_arbiter, + {M0_nCS_GPIO_Port, M0_nCS_Pin}, // nCS + {}, // EN pin (shared between both motors, therefore we actuate it outside of the drv8301 driver) + {nFAULT_GPIO_Port, nFAULT_Pin} // nFAULT pin (shared between both motors) +}; + +Drv8301 m1_gate_driver{ + &spi3_arbiter, + {M1_nCS_GPIO_Port, M1_nCS_Pin}, // nCS + {}, // EN pin (shared between both motors, therefore we actuate it outside of the drv8301 driver) + {nFAULT_GPIO_Port, nFAULT_Pin} // nFAULT pin (shared between both motors) +}; + +const float fet_thermistor_poly_coeffs[] = + {363.93910201f, -462.15369634f, 307.55129571f, -27.72569531f}; +const size_t fet_thermistor_num_coeffs = sizeof(fet_thermistor_poly_coeffs)/sizeof(fet_thermistor_poly_coeffs[1]); + +OnboardThermistorCurrentLimiter fet_thermistors[AXIS_COUNT] = { + { + 15, // adc_channel + &fet_thermistor_poly_coeffs[0], // coefficients + fet_thermistor_num_coeffs // num_coeffs + }, { +#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 + 4, // adc_channel +#else + 1, // adc_channel +#endif + &fet_thermistor_poly_coeffs[0], // coefficients + fet_thermistor_num_coeffs // num_coeffs + } +}; + +OffboardThermistorCurrentLimiter motor_thermistors[AXIS_COUNT]; + +Motor motors[AXIS_COUNT] = { + { + &htim1, // timer + 0b110, // current_sensor_mask + 1.0f / SHUNT_RESISTANCE, // shunt_conductance [S] + m0_gate_driver, // gate_driver + m0_gate_driver, // opamp + fet_thermistors[0], + motor_thermistors[0] + }, + { + &htim8, // timer + 0b110, // current_sensor_mask + 1.0f / SHUNT_RESISTANCE, // shunt_conductance [S] + m1_gate_driver, // gate_driver + m1_gate_driver, // opamp + fet_thermistors[1], + motor_thermistors[1] + } +}; + +Encoder encoders[AXIS_COUNT] = { + { + &htim3, // timer + {M0_ENC_Z_GPIO_Port, M0_ENC_Z_Pin}, // index_gpio + {M0_ENC_A_GPIO_Port, M0_ENC_A_Pin}, // hallA_gpio + {M0_ENC_B_GPIO_Port, M0_ENC_B_Pin}, // hallB_gpio + {M0_ENC_Z_GPIO_Port, M0_ENC_Z_Pin}, // hallC_gpio + &spi3_arbiter // spi_arbiter + }, + { + &htim4, // timer + {M1_ENC_Z_GPIO_Port, M1_ENC_Z_Pin}, // index_gpio + {M1_ENC_A_GPIO_Port, M1_ENC_A_Pin}, // hallA_gpio + {M1_ENC_B_GPIO_Port, M1_ENC_B_Pin}, // hallB_gpio + {M1_ENC_Z_GPIO_Port, M1_ENC_Z_Pin}, // hallC_gpio + &spi3_arbiter // spi_arbiter + } +}; + +// TODO: this has no hardware dependency and should be allocated depending on config +Endstop endstops[2 * AXIS_COUNT]; +MechanicalBrake mechanical_brakes[AXIS_COUNT]; + +SensorlessEstimator sensorless_estimators[AXIS_COUNT]; +Controller controllers[AXIS_COUNT]; +TrapezoidalTrajectory trap[AXIS_COUNT]; + +std::array axes{{ + { + 0, // axis_num + 1, // step_gpio_pin + 2, // dir_gpio_pin + (osPriority)(osPriorityHigh + (osPriority)1), // thread_priority + encoders[0], // encoder + sensorless_estimators[0], // sensorless_estimator + controllers[0], // controller + motors[0], // motor + trap[0], // trap + endstops[0], endstops[1], // min_endstop, max_endstop + mechanical_brakes[0], // mechanical brake + }, + { + 1, // axis_num +#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 5 + 7, // step_gpio_pin + 8, // dir_gpio_pin +#else + 3, // step_gpio_pin + 4, // dir_gpio_pin +#endif + osPriorityHigh, // thread_priority + encoders[1], // encoder + sensorless_estimators[1], // sensorless_estimator + controllers[1], // controller + motors[1], // motor + trap[1], // trap + endstops[2], endstops[3], // min_endstop, max_endstop + mechanical_brakes[1], // mechanical brake + }, +}}; + + + +#if (HW_VERSION_MINOR == 1) || (HW_VERSION_MINOR == 2) +Stm32Gpio gpios[] = { + {nullptr, 0}, // dummy GPIO0 so that PCB labels and software numbers match + + {GPIOB, GPIO_PIN_2}, // GPIO1 + {GPIOA, GPIO_PIN_5}, // GPIO2 + {GPIOA, GPIO_PIN_4}, // GPIO3 + {GPIOA, GPIO_PIN_3}, // GPIO4 + {nullptr, 0}, // GPIO5 (doesn't exist on this board) + {nullptr, 0}, // GPIO6 (doesn't exist on this board) + {nullptr, 0}, // GPIO7 (doesn't exist on this board) + {nullptr, 0}, // GPIO8 (doesn't exist on this board) + + {GPIOB, GPIO_PIN_4}, // ENC0_A + {GPIOB, GPIO_PIN_5}, // ENC0_B + {GPIOA, GPIO_PIN_15}, // ENC0_Z + {GPIOB, GPIO_PIN_6}, // ENC1_A + {GPIOB, GPIO_PIN_7}, // ENC1_B + {GPIOB, GPIO_PIN_3}, // ENC1_Z + {GPIOB, GPIO_PIN_8}, // CAN_R + {GPIOB, GPIO_PIN_9}, // CAN_D +}; +#elif (HW_VERSION_MINOR == 3) || (HW_VERSION_MINOR == 4) +Stm32Gpio gpios[] = { + {nullptr, 0}, // dummy GPIO0 so that PCB labels and software numbers match + + {GPIOA, GPIO_PIN_0}, // GPIO1 + {GPIOA, GPIO_PIN_1}, // GPIO2 + {GPIOA, GPIO_PIN_2}, // GPIO3 + {GPIOA, GPIO_PIN_3}, // GPIO4 + {GPIOB, GPIO_PIN_2}, // GPIO5 + {nullptr, 0}, // GPIO6 (doesn't exist on this board) + {nullptr, 0}, // GPIO7 (doesn't exist on this board) + {nullptr, 0}, // GPIO8 (doesn't exist on this board) + + {GPIOB, GPIO_PIN_4}, // ENC0_A + {GPIOB, GPIO_PIN_5}, // ENC0_B + {GPIOA, GPIO_PIN_15}, // ENC0_Z + {GPIOB, GPIO_PIN_6}, // ENC1_A + {GPIOB, GPIO_PIN_7}, // ENC1_B + {GPIOB, GPIO_PIN_3}, // ENC1_Z + {GPIOB, GPIO_PIN_8}, // CAN_R + {GPIOB, GPIO_PIN_9}, // CAN_D +}; +#elif (HW_VERSION_MINOR == 5) || (HW_VERSION_MINOR == 6) +Stm32Gpio gpios[GPIO_COUNT] = { + {nullptr, 0}, // dummy GPIO0 so that PCB labels and software numbers match + + {GPIOA, GPIO_PIN_0}, // GPIO1 + {GPIOA, GPIO_PIN_1}, // GPIO2 + {GPIOA, GPIO_PIN_2}, // GPIO3 + {GPIOA, GPIO_PIN_3}, // GPIO4 + {GPIOC, GPIO_PIN_4}, // GPIO5 + {GPIOB, GPIO_PIN_2}, // GPIO6 + {GPIOA, GPIO_PIN_15}, // GPIO7 + {GPIOB, GPIO_PIN_3}, // GPIO8 + + {GPIOB, GPIO_PIN_4}, // ENC0_A + {GPIOB, GPIO_PIN_5}, // ENC0_B + {GPIOC, GPIO_PIN_9}, // ENC0_Z + {GPIOB, GPIO_PIN_6}, // ENC1_A + {GPIOB, GPIO_PIN_7}, // ENC1_B + {GPIOC, GPIO_PIN_15}, // ENC1_Z + {GPIOB, GPIO_PIN_8}, // CAN_R + {GPIOB, GPIO_PIN_9}, // CAN_D +}; +#else +#error "unknown GPIOs" +#endif + +std::array alternate_functions[GPIO_COUNT] = { + /* GPIO0 (inexistent): */ {{}}, + +#if HW_VERSION_MINOR >= 3 + /* GPIO1: */ {{{ODrive::GPIO_MODE_UART_A, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}}, + /* GPIO2: */ {{{ODrive::GPIO_MODE_UART_A, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}}, + /* GPIO3: */ {{{ODrive::GPIO_MODE_UART_B, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}}, +#else + /* GPIO1: */ {{}}, + /* GPIO2: */ {{}}, + /* GPIO3: */ {{}}, +#endif + + /* GPIO4: */ {{{ODrive::GPIO_MODE_UART_B, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}}, + /* GPIO5: */ {{}}, + /* GPIO6: */ {{}}, + /* GPIO7: */ {{}}, + /* GPIO8: */ {{}}, + /* ENC0_A: */ {{{ODrive::GPIO_MODE_ENC0, GPIO_AF2_TIM3}}}, + /* ENC0_B: */ {{{ODrive::GPIO_MODE_ENC0, GPIO_AF2_TIM3}}}, + /* ENC0_Z: */ {{}}, + /* ENC1_A: */ {{{ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}, {ODrive::GPIO_MODE_ENC1, GPIO_AF2_TIM4}}}, + /* ENC1_B: */ {{{ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}, {ODrive::GPIO_MODE_ENC1, GPIO_AF2_TIM4}}}, + /* ENC1_Z: */ {{}}, + /* CAN_R: */ {{{ODrive::GPIO_MODE_CAN_A, GPIO_AF9_CAN1}, {ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}}}, + /* CAN_D: */ {{{ODrive::GPIO_MODE_CAN_A, GPIO_AF9_CAN1}, {ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}}}, +}; + +#if HW_VERSION_MINOR <= 2 +PwmInput pwm0_input{&htim5, {0, 0, 0, 4}}; // 0 means not in use +#else +PwmInput pwm0_input{&htim5, {1, 2, 3, 4}}; +#endif + +extern USBD_HandleTypeDef hUsbDeviceFS; +USBD_HandleTypeDef& usb_dev_handle = hUsbDeviceFS; + +bool check_board_version(const uint8_t* otp_ptr) { + return (otp_ptr[3] == HW_VERSION_MAJOR) && + (otp_ptr[4] == HW_VERSION_MINOR) && + (otp_ptr[5] == HW_VERSION_VOLTAGE); +} + +void system_init() { + // Reset of all peripherals, Initializes the Flash interface and the Systick. + HAL_Init(); + + // Configure the system clock + SystemClock_Config(); + + // If the OTP is pristine, use the fake-otp in RAM instead + const uint8_t* otp_ptr = (const uint8_t*)FLASH_OTP_BASE; + if (*otp_ptr == 0xff) { + otp_ptr = fake_otp; + } + + // Ensure that the board version for which this firmware is compiled matches + // the board we're running on. + if (!check_board_version(otp_ptr)) { + for (;;); + } +} + +bool board_init() { + // Initialize all configured peripherals + MX_GPIO_Init(); + MX_DMA_Init(); + MX_ADC1_Init(); + MX_ADC2_Init(); + MX_TIM1_Init(); + MX_TIM8_Init(); + MX_TIM3_Init(); + MX_TIM4_Init(); + MX_SPI3_Init(); + MX_ADC3_Init(); + MX_TIM2_Init(); + MX_TIM5_Init(); + MX_TIM13_Init(); + + // External interrupt lines are individually enabled in stm32_gpio.cpp + HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI0_IRQn); + HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI1_IRQn); + HAL_NVIC_SetPriority(EXTI2_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI2_IRQn); + HAL_NVIC_SetPriority(EXTI3_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI3_IRQn); + HAL_NVIC_SetPriority(EXTI4_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI4_IRQn); + HAL_NVIC_SetPriority(EXTI9_5_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); + HAL_NVIC_SetPriority(EXTI15_10_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); + + HAL_NVIC_SetPriority(ControlLoop_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(ControlLoop_IRQn); + + HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn); + + if (odrv.config_.enable_uart_a) { + uart_a->Init.BaudRate = odrv.config_.uart_a_baudrate; + MX_UART4_Init(); + } + + if (odrv.config_.enable_uart_b) { + uart_b->Init.BaudRate = odrv.config_.uart_b_baudrate; + MX_USART2_UART_Init(); + } + + if (odrv.config_.enable_i2c_a) { + // Set up the direction GPIO as input + get_gpio(3).config(GPIO_MODE_INPUT, GPIO_PULLUP); + get_gpio(4).config(GPIO_MODE_INPUT, GPIO_PULLUP); + get_gpio(5).config(GPIO_MODE_INPUT, GPIO_PULLUP); + + osDelay(1); // This has no effect but was here before. + i2c_stats_.addr = (0xD << 3); + i2c_stats_.addr |= get_gpio(3).read() ? 0x1 : 0; + i2c_stats_.addr |= get_gpio(4).read() ? 0x2 : 0; + i2c_stats_.addr |= get_gpio(5).read() ? 0x4 : 0; + MX_I2C1_Init(i2c_stats_.addr); + } + + if (odrv.config_.enable_can_a) { + // The CAN initialization will (and must) init its own GPIOs before the + // GPIO modes are initialized. Therefore we ensure that the later GPIO + // mode initialization won't override the CAN mode. + if (odrv.config_.gpio_modes[15] != ODriveIntf::GPIO_MODE_CAN_A || odrv.config_.gpio_modes[16] != ODriveIntf::GPIO_MODE_CAN_A) { + odrv.misconfigured_ = true; + } + } + + // Ensure that debug halting of the core doesn't leave the motor PWM running + __HAL_DBGMCU_FREEZE_TIM1(); + __HAL_DBGMCU_FREEZE_TIM8(); + __HAL_DBGMCU_FREEZE_TIM13(); + + Stm32Gpio drv_enable_gpio = {EN_GATE_GPIO_Port, EN_GATE_Pin}; + + // Reset both DRV chips. The enable pin also controls the SPI interface, not + // only the driver stages. + drv_enable_gpio.write(false); + delay_us(40); // mimumum pull-down time for full reset: 20us + drv_enable_gpio.write(true); + delay_us(20000); // mimumum pull-down time for full reset: 20us + + return true; +} + +void start_timers() { + CRITICAL_SECTION() { + // Temporarily disable ADC triggers so they don't trigger as a side + // effect of starting the timers. + hadc1.Instance->CR2 &= ~(ADC_CR2_JEXTEN); + hadc2.Instance->CR2 &= ~(ADC_CR2_EXTEN | ADC_CR2_JEXTEN); + hadc3.Instance->CR2 &= ~(ADC_CR2_EXTEN | ADC_CR2_JEXTEN); + + /* + * Synchronize TIM1, TIM8 and TIM13 such that: + * 1. The triangle waveform of TIM1 leads the triangle waveform of TIM8 by a + * 90° phase shift. + * 2. Each TIM13 reload coincides with a TIM1 lower update event. + */ + Stm32Timer::start_synchronously<3>( + {&htim1, &htim8, &htim13}, + {TIM1_INIT_COUNT, 0, TIM1_INIT_COUNT / 2 /* TIM13 is on a clock that's only have as fast as TIM1 */} + ); + + hadc1.Instance->CR2 |= (ADC_EXTERNALTRIGINJECCONVEDGE_RISING); + hadc2.Instance->CR2 |= (ADC_EXTERNALTRIGCONVEDGE_RISING | ADC_EXTERNALTRIGINJECCONVEDGE_RISING); + hadc3.Instance->CR2 |= (ADC_EXTERNALTRIGCONVEDGE_RISING | ADC_EXTERNALTRIGINJECCONVEDGE_RISING); + + __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_JEOC); + __HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_JEOC); + __HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_JEOC); + __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_EOC); + __HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_EOC); + __HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_EOC); + __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_OVR); + __HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_OVR); + __HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_OVR); + + __HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE); + __HAL_TIM_ENABLE_IT(&htim8, TIM_IT_UPDATE); + } +} + +static bool fetch_and_reset_adcs( + std::optional* current0, + std::optional* current1) { + bool all_adcs_done = (ADC1->SR & ADC_SR_JEOC) == ADC_SR_JEOC + && (ADC2->SR & (ADC_SR_EOC | ADC_SR_JEOC)) == (ADC_SR_EOC | ADC_SR_JEOC) + && (ADC3->SR & (ADC_SR_EOC | ADC_SR_JEOC)) == (ADC_SR_EOC | ADC_SR_JEOC); + if (!all_adcs_done) { + return false; + } + + vbus_sense_adc_cb(ADC1->JDR1); + + if (m0_gate_driver.is_ready()) { + std::optional phB = motors[0].phase_current_from_adcval(ADC2->JDR1); + std::optional phC = motors[0].phase_current_from_adcval(ADC3->JDR1); + if (phB.has_value() && phC.has_value()) { + *current0 = {-*phB - *phC, *phB, *phC}; + } + } + + if (m1_gate_driver.is_ready()) { + std::optional phB = motors[1].phase_current_from_adcval(ADC2->DR); + std::optional phC = motors[1].phase_current_from_adcval(ADC3->DR); + if (phB.has_value() && phC.has_value()) { + *current1 = {-*phB - *phC, *phB, *phC}; + } + } + + ADC1->SR = ~(ADC_SR_JEOC); + ADC2->SR = ~(ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_OVR); + ADC3->SR = ~(ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_OVR); + + return true; +} + +extern "C" { + +void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { + HAL_SPI_TxRxCpltCallback(hspi); +} + +void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { + HAL_SPI_TxRxCpltCallback(hspi); +} + +void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { + if (hspi == &hspi3) { + spi3_arbiter.on_complete(); + } +} + +void TIM5_IRQHandler(void) { + COUNT_IRQ(TIM5_IRQn); + pwm0_input.on_capture(); +} + +volatile uint32_t timestamp_ = 0; +volatile bool counting_down_ = false; + +void TIM8_UP_TIM13_IRQHandler(void) { + COUNT_IRQ(TIM8_UP_TIM13_IRQn); + + // Entry into this function happens at 21-23 clock cycles after the timer + // update event. + __HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE); + + // If the corresponding timer is counting up, we just sampled in SVM vector 0, i.e. real current + // If we are counting down, we just sampled in SVM vector 7, with zero current + bool counting_down = TIM8->CR1 & TIM_CR1_DIR; + + bool timer_update_missed = (counting_down_ == counting_down); + if (timer_update_missed) { + motors[0].disarm_with_error(Motor::ERROR_TIMER_UPDATE_MISSED); + motors[1].disarm_with_error(Motor::ERROR_TIMER_UPDATE_MISSED); + return; + } + counting_down_ = counting_down; + + timestamp_ += TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1); + + if (!counting_down) { + TaskTimer::enabled = odrv.task_timers_armed_; + // Run sampling handlers and kick off control tasks when TIM8 is + // counting up. + odrv.sampling_cb(); + NVIC->STIR = ControlLoop_IRQn; + } else { + // Tentatively reset all PWM outputs to 50% duty cycles. If the control + // loop handler finishes in time then these values will be overridden + // before they go into effect. + TIM1->CCR1 = + TIM1->CCR2 = + TIM1->CCR3 = + TIM8->CCR1 = + TIM8->CCR2 = + TIM8->CCR3 = + TIM_1_8_PERIOD_CLOCKS / 2; + } +} + +void ControlLoop_IRQHandler(void) { + COUNT_IRQ(ControlLoop_IRQn); + uint32_t timestamp = timestamp_; + + // Ensure that all the ADCs are done + std::optional current0; + std::optional current1; + + if (!fetch_and_reset_adcs(¤t0, ¤t1)) { + motors[0].disarm_with_error(Motor::ERROR_BAD_TIMING); + motors[1].disarm_with_error(Motor::ERROR_BAD_TIMING); + } + + // If the motor FETs are not switching then we can't measure the current + // because for this we need the low side FET to conduct. + // So for now we guess the current to be 0 (this is not correct shortly after + // disarming and when the motor spins fast in idle). Passing an invalid + // current reading would create problems with starting FOC. + if (!(TIM1->BDTR & TIM_BDTR_MOE_Msk)) { + current0 = {0.0f, 0.0f}; + } + if (!(TIM8->BDTR & TIM_BDTR_MOE_Msk)) { + current1 = {0.0f, 0.0f}; + } + + motors[0].current_meas_cb(timestamp - TIM1_INIT_COUNT, current0); + motors[1].current_meas_cb(timestamp, current1); + + odrv.control_loop_cb(timestamp); + + // By this time the ADCs for both M0 and M1 should have fired again. But + // let's wait for them just to be sure. + MEASURE_TIME(odrv.task_times_.dc_calib_wait) { + while (!(ADC2->SR & ADC_SR_EOC)); + } + + if (!fetch_and_reset_adcs(¤t0, ¤t1)) { + motors[0].disarm_with_error(Motor::ERROR_BAD_TIMING); + motors[1].disarm_with_error(Motor::ERROR_BAD_TIMING); + } + + motors[0].dc_calib_cb(timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1) - TIM1_INIT_COUNT, current0); + motors[1].dc_calib_cb(timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1), current1); + + motors[0].pwm_update_cb(timestamp + 3 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1) - TIM1_INIT_COUNT); + motors[1].pwm_update_cb(timestamp + 3 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)); + + // If we did everything right, the TIM8 update handler should have been + // called exactly once between the start of this function and now. + + if (timestamp_ != timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) { + motors[0].disarm_with_error(Motor::ERROR_CONTROL_DEADLINE_MISSED); + motors[1].disarm_with_error(Motor::ERROR_CONTROL_DEADLINE_MISSED); + } + + odrv.task_timers_armed_ = odrv.task_timers_armed_ && !TaskTimer::enabled; + TaskTimer::enabled = false; +} + +void I2C1_EV_IRQHandler(void) { + COUNT_IRQ(I2C1_EV_IRQn); + HAL_I2C_EV_IRQHandler(&hi2c1); +} + +void I2C1_ER_IRQHandler(void) { + COUNT_IRQ(I2C1_ER_IRQn); + HAL_I2C_ER_IRQHandler(&hi2c1); +} + +extern PCD_HandleTypeDef hpcd_USB_OTG_FS; // defined in usbd_conf.c +void OTG_FS_IRQHandler(void) { + COUNT_IRQ(OTG_FS_IRQn); + HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); +} + +} diff --git a/Firmware/Drivers/DRV8301/drv8301.c b/Firmware/Drivers/DRV8301/drv8301.c deleted file mode 100644 index c65cc4d3d..000000000 --- a/Firmware/Drivers/DRV8301/drv8301.c +++ /dev/null @@ -1,778 +0,0 @@ -/* --COPYRIGHT--,BSD - * Copyright (c) 2015, Texas Instruments Incorporated - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Texas Instruments Incorporated nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * --/COPYRIGHT--*/ -//! \file drivers/drvic/drv8301/src/32b/f28x/f2806x/drv8301.c -//! \brief Contains the various functions related to the DRV8301 object -//! -//! (C) Copyright 2015, Texas Instruments, Inc. - - -// ************************************************************************** -// the includes - -#include "assert.h" -#include -#include "cmsis_os.h" - -// drivers -#include "drv8301.h" - -#include "utils.hpp" - - -// ************************************************************************** -// the defines - - -// ************************************************************************** -// the globals - - -// ************************************************************************** -// the function prototypes - -void DRV8301_enable(DRV8301_Handle handle) -{ - - //Enable driver - HAL_GPIO_WritePin(handle->EngpioHandle, handle->EngpioNumber, GPIO_PIN_SET); - - //Wait for driver to come online - osDelay(10); - - // Make sure the Fault bit is not set during startup - while((DRV8301_readSpi(handle,DRV8301_RegName_Status_1) & DRV8301_STATUS1_FAULT_BITS) != 0); - - // Wait for the DRV8301 registers to update - osDelay(1); - - return; -} - -DRV8301_DcCalMode_e DRV8301_getDcCalMode(DRV8301_Handle handle,const DRV8301_ShuntAmpNumber_e ampNumber) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_2); - - // clear the bits - if(ampNumber == DRV8301_ShuntAmpNumber_1) - { - data &= (~DRV8301_CTRL2_DC_CAL_1_BITS); - - } - else if(ampNumber == DRV8301_ShuntAmpNumber_2) - { - data &= (~DRV8301_CTRL2_DC_CAL_2_BITS); - } - - return((DRV8301_DcCalMode_e)data); -} // end of DRV8301_getDcCalMode() function - - -DRV8301_FaultType_e DRV8301_getFaultType(DRV8301_Handle handle) -{ - DRV8301_Word_t readWord; - DRV8301_FaultType_e faultType = DRV8301_FaultType_NoFault; - - - // read the data - readWord = DRV8301_readSpi(handle,DRV8301_RegName_Status_1); - - if(readWord & DRV8301_STATUS1_FAULT_BITS) - { - faultType = (DRV8301_FaultType_e)(readWord & DRV8301_FAULT_TYPE_MASK); - - if(faultType == DRV8301_FaultType_NoFault) - { - // read the data - readWord = DRV8301_readSpi(handle,DRV8301_RegName_Status_2); - - if(readWord & DRV8301_STATUS2_GVDD_OV_BITS) - { - faultType = DRV8301_FaultType_GVDD_OV; - } - } - } - - return(faultType); -} // end of DRV8301_getFaultType() function - - -uint16_t DRV8301_getId(DRV8301_Handle handle) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Status_2); - - // mask bits - data &= DRV8301_STATUS2_ID_BITS; - - return(data); -} // end of DRV8301_getId() function - - -DRV8301_VdsLevel_e DRV8301_getOcLevel(DRV8301_Handle handle) -{ - uint16_t data; - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_1); - - // clear the bits - data &= (~DRV8301_CTRL1_OC_ADJ_SET_BITS); - - return((DRV8301_VdsLevel_e)data); -} // end of DRV8301_getOcLevel() function - - -DRV8301_OcMode_e DRV8301_getOcMode(DRV8301_Handle handle) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_1); - - // clear the bits - data &= (~DRV8301_CTRL1_OC_MODE_BITS); - - return((DRV8301_OcMode_e)data); -} // end of DRV8301_getOcMode() function - - -DRV8301_OcOffTimeMode_e DRV8301_getOcOffTimeMode(DRV8301_Handle handle) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_2); - - // clear the bits - data &= (~DRV8301_CTRL2_OC_TOFF_BITS); - - return((DRV8301_OcOffTimeMode_e)data); -} // end of DRV8301_getOcOffTimeMode() function - - -DRV8301_OcTwMode_e DRV8301_getOcTwMode(DRV8301_Handle handle) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_2); - - // clear the bits - data &= (~DRV8301_CTRL2_OCTW_SET_BITS); - - return((DRV8301_OcTwMode_e)data); -} // end of DRV8301_getOcTwMode() function - - -DRV8301_PeakCurrent_e DRV8301_getPeakCurrent(DRV8301_Handle handle) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_1); - - // clear the bits - data &= (~DRV8301_CTRL1_GATE_CURRENT_BITS); - - return((DRV8301_PeakCurrent_e)data); -} // end of DRV8301_getPeakCurrent() function - - -DRV8301_PwmMode_e DRV8301_getPwmMode(DRV8301_Handle handle) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_1); - - // clear the bits - data &= (~DRV8301_CTRL1_PWM_MODE_BITS); - - return((DRV8301_PwmMode_e)data); -} // end of DRV8301_getPwmMode() function - - -DRV8301_ShuntAmpGain_e DRV8301_getShuntAmpGain(DRV8301_Handle handle) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_2); - - // clear the bits - data &= (~DRV8301_CTRL2_GAIN_BITS); - - return((DRV8301_ShuntAmpGain_e)data); -} // end of DRV8301_getShuntAmpGain() function - - -DRV8301_Handle DRV8301_init(void *pMemory,const size_t numBytes) -{ - DRV8301_Handle handle; - - - if(numBytes < sizeof(DRV8301_Obj)) - return((DRV8301_Handle)NULL); - - - // assign the handle - handle = (DRV8301_Handle)pMemory; - - DRV8301_resetRxTimeout(handle); - DRV8301_resetEnableTimeout(handle); - - - return(handle); -} // end of DRV8301_init() function - - -void DRV8301_setEnGpioHandle(DRV8301_Handle handle,GPIO_Handle gpioHandle) -{ - DRV8301_Obj *obj = (DRV8301_Obj *)handle; - - // initialize the gpio interface object - obj->EngpioHandle = gpioHandle; - - return; -} // end of DRV8301_setGpioHandle() function - - -void DRV8301_setEnGpioNumber(DRV8301_Handle handle,GPIO_Number_e gpioNumber) -{ - DRV8301_Obj *obj = (DRV8301_Obj *)handle; - - // initialize the gpio interface object - obj->EngpioNumber = gpioNumber; - - return; -} // end of DRV8301_setGpioNumber() function - - -void DRV8301_setnCSGpioHandle(DRV8301_Handle handle,GPIO_Handle gpioHandle) -{ - DRV8301_Obj *obj = (DRV8301_Obj *)handle; - - // initialize the gpio interface object - obj->nCSgpioHandle = gpioHandle; - - return; -} // end of DRV8301_setGpioHandle() function - - -void DRV8301_setnCSGpioNumber(DRV8301_Handle handle,GPIO_Number_e gpioNumber) -{ - DRV8301_Obj *obj = (DRV8301_Obj *)handle; - - // initialize the gpio interface object - obj->nCSgpioNumber = gpioNumber; - - return; -} // end of DRV8301_setGpioNumber() function - - -void DRV8301_setSpiHandle(DRV8301_Handle handle,SPI_Handle spiHandle) -{ - DRV8301_Obj *obj = (DRV8301_Obj *)handle; - - // initialize the serial peripheral interface object - obj->spiHandle = spiHandle; - - return; -} // end of DRV8301_setSpiHandle() function - - -bool DRV8301_isFault(DRV8301_Handle handle) -{ - DRV8301_Word_t readWord; - bool status=false; - - - // read the data - readWord = DRV8301_readSpi(handle,DRV8301_RegName_Status_1); - - if(readWord & DRV8301_STATUS1_FAULT_BITS) - { - status = true; - } - - return(status); -} // end of DRV8301_isFault() function - - -bool DRV8301_isReset(DRV8301_Handle handle) -{ - DRV8301_Word_t readWord; - bool status=false; - - - // read the data - readWord = DRV8301_readSpi(handle,DRV8301_RegName_Control_1); - - if(readWord & DRV8301_CTRL1_GATE_RESET_BITS) - { - status = true; - } - - return(status); -} // end of DRV8301_isReset() function - - -uint16_t DRV8301_readSpi(DRV8301_Handle handle, const DRV8301_RegName_e regName) -{ - - // Actuate chipselect - HAL_GPIO_WritePin(handle->nCSgpioHandle, handle->nCSgpioNumber, GPIO_PIN_RESET); - delay_us(1); - - // Do blocking read - uint16_t zerobuff = 0; - uint16_t controlword = (uint16_t)DRV8301_buildCtrlWord(DRV8301_CtrlMode_Read, regName, 0); - uint16_t recbuff = 0xbeef; - HAL_SPI_Transmit(handle->spiHandle, (uint8_t*)(&controlword), 1, 1000); - - // Datasheet says you don't have to pulse the nCS between transfers, (16 clocks should commit the transfer) - // but for some reason you actually need to pulse it. - // Actuate chipselect - HAL_GPIO_WritePin(handle->nCSgpioHandle, handle->nCSgpioNumber, GPIO_PIN_SET); - delay_us(1); - // Actuate chipselect - HAL_GPIO_WritePin(handle->nCSgpioHandle, handle->nCSgpioNumber, GPIO_PIN_RESET); - delay_us(1); - - HAL_SPI_TransmitReceive(handle->spiHandle, (uint8_t*)(&zerobuff), (uint8_t*)(&recbuff), 1, 1000); - delay_us(1); - - // Actuate chipselect - HAL_GPIO_WritePin(handle->nCSgpioHandle, handle->nCSgpioNumber, GPIO_PIN_SET); - delay_us(1); - - assert(recbuff != 0xbeef); - - return(recbuff & DRV8301_DATA_MASK); -} // end of DRV8301_readSpi() function - - -void DRV8301_reset(DRV8301_Handle handle) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_1); - - // set the bits - data |= DRV8301_CTRL1_GATE_RESET_BITS; - - // write the data - DRV8301_writeSpi(handle,DRV8301_RegName_Control_1,data); - - return; -} // end of DRV8301_reset() function - - -void DRV8301_setDcCalMode(DRV8301_Handle handle,const DRV8301_ShuntAmpNumber_e ampNumber,const DRV8301_DcCalMode_e mode) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_2); - - // clear the bits - if(ampNumber == DRV8301_ShuntAmpNumber_1) - { - data &= (~DRV8301_CTRL2_DC_CAL_1_BITS); - - } - else if(ampNumber == DRV8301_ShuntAmpNumber_2) - { - data &= (~DRV8301_CTRL2_DC_CAL_2_BITS); - } - - // set the bits - data |= mode; - - // write the data - DRV8301_writeSpi(handle,DRV8301_RegName_Control_2,data); - - return; -} // end of DRV8301_setDcCalMode() function - - -void DRV8301_setOcLevel(DRV8301_Handle handle,const DRV8301_VdsLevel_e VdsLevel) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_1); - - // clear the bits - data &= (~DRV8301_CTRL1_OC_ADJ_SET_BITS); - - // set the bits - data |= VdsLevel; - - // write the data - DRV8301_writeSpi(handle,DRV8301_RegName_Control_1,data); - - return; -} // end of DRV8301_setOcLevel() function - - -void DRV8301_setOcMode(DRV8301_Handle handle,const DRV8301_OcMode_e mode) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_1); - - // clear the bits - data &= (~DRV8301_CTRL1_OC_MODE_BITS); - - // set the bits - data |= mode; - - // write the data - DRV8301_writeSpi(handle,DRV8301_RegName_Control_1,data); - - return; -} // end of DRV8301_setOcMode() function - - -void DRV8301_setOcOffTimeMode(DRV8301_Handle handle,const DRV8301_OcOffTimeMode_e mode) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_2); - - // clear the bits - data &= (~DRV8301_CTRL2_OC_TOFF_BITS); - - // set the bits - data |= mode; - - // write the data - DRV8301_writeSpi(handle,DRV8301_RegName_Control_2,data); - - return; -} // end of DRV8301_setOcOffTimeMode() function - - -void DRV8301_setOcTwMode(DRV8301_Handle handle,const DRV8301_OcTwMode_e mode) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_2); - - // clear the bits - data &= (~DRV8301_CTRL2_OCTW_SET_BITS); - - // set the bits - data |= mode; - - // write the data - DRV8301_writeSpi(handle,DRV8301_RegName_Control_2,data); - - return; -} // end of DRV8301_setOcTwMode() function - - -void DRV8301_setPeakCurrent(DRV8301_Handle handle,const DRV8301_PeakCurrent_e peakCurrent) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_1); - - // clear the bits - data &= (~DRV8301_CTRL1_GATE_CURRENT_BITS); - - // set the bits - data |= peakCurrent; - - // write the data - DRV8301_writeSpi(handle,DRV8301_RegName_Control_1,data); - - return; -} // end of DRV8301_setPeakCurrent() function - - -void DRV8301_setPwmMode(DRV8301_Handle handle,const DRV8301_PwmMode_e mode) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_1); - - // clear the bits - data &= (~DRV8301_CTRL1_PWM_MODE_BITS); - - // set the bits - data |= mode; - - // write the data - DRV8301_writeSpi(handle,DRV8301_RegName_Control_1,data); - - return; -} // end of DRV8301_setPwmMode() function - - -void DRV8301_setShuntAmpGain(DRV8301_Handle handle,const DRV8301_ShuntAmpGain_e gain) -{ - uint16_t data; - - - // read data - data = DRV8301_readSpi(handle,DRV8301_RegName_Control_2); - - // clear the bits - data &= (~DRV8301_CTRL2_GAIN_BITS); - - // set the bits - data |= gain; - - // write the data - DRV8301_writeSpi(handle,DRV8301_RegName_Control_2,data); - - return; -} // end of DRV8301_setShuntAmpGain() function - - -void DRV8301_writeSpi(DRV8301_Handle handle, const DRV8301_RegName_e regName,const uint16_t data) -{ - // Actuate chipselect - HAL_GPIO_WritePin(handle->nCSgpioHandle, handle->nCSgpioNumber, GPIO_PIN_RESET); - delay_us(1); - - // Do blocking write - uint16_t controlword = (uint16_t)DRV8301_buildCtrlWord(DRV8301_CtrlMode_Write, regName, data); - HAL_SPI_Transmit(handle->spiHandle, (uint8_t*)(&controlword), 1, 1000); - delay_us(1); - - // Actuate chipselect - HAL_GPIO_WritePin(handle->nCSgpioHandle, handle->nCSgpioNumber, GPIO_PIN_SET); - delay_us(1); - - return; -} // end of DRV8301_writeSpi() function - - -void DRV8301_writeData(DRV8301_Handle handle, DRV_SPI_8301_Vars_t *Spi_8301_Vars) -{ - DRV8301_RegName_e drvRegName; - uint16_t drvDataNew; - - - if(Spi_8301_Vars->SndCmd) - { - // Update Control Register 1 - drvRegName = DRV8301_RegName_Control_1; - drvDataNew = Spi_8301_Vars->Ctrl_Reg_1.DRV8301_CURRENT | \ - Spi_8301_Vars->Ctrl_Reg_1.DRV8301_RESET | \ - Spi_8301_Vars->Ctrl_Reg_1.PWM_MODE | \ - Spi_8301_Vars->Ctrl_Reg_1.OC_MODE | \ - Spi_8301_Vars->Ctrl_Reg_1.OC_ADJ_SET; - DRV8301_writeSpi(handle,drvRegName,drvDataNew); - - // Update Control Register 2 - drvRegName = DRV8301_RegName_Control_2; - drvDataNew = Spi_8301_Vars->Ctrl_Reg_2.OCTW_SET | \ - Spi_8301_Vars->Ctrl_Reg_2.GAIN | \ - Spi_8301_Vars->Ctrl_Reg_2.DC_CAL_CH1p2 | \ - Spi_8301_Vars->Ctrl_Reg_2.OC_TOFF; - DRV8301_writeSpi(handle,drvRegName,drvDataNew); - - Spi_8301_Vars->SndCmd = false; - } - - return; -} // end of DRV8301_writeData() function - - -void DRV8301_readData(DRV8301_Handle handle, DRV_SPI_8301_Vars_t *Spi_8301_Vars) -{ - DRV8301_RegName_e drvRegName; - uint16_t drvDataNew; - - - if(Spi_8301_Vars->RcvCmd) - { - // Update Status Register 1 - drvRegName = DRV8301_RegName_Status_1; - drvDataNew = DRV8301_readSpi(handle,drvRegName); - Spi_8301_Vars->Stat_Reg_1.FAULT = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FAULT_BITS); - Spi_8301_Vars->Stat_Reg_1.GVDD_UV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_GVDD_UV_BITS); - Spi_8301_Vars->Stat_Reg_1.PVDD_UV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_PVDD_UV_BITS); - Spi_8301_Vars->Stat_Reg_1.OTSD = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_OTSD_BITS); - Spi_8301_Vars->Stat_Reg_1.OTW = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_OTW_BITS); - Spi_8301_Vars->Stat_Reg_1.FETHA_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHA_OC_BITS); - Spi_8301_Vars->Stat_Reg_1.FETLA_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLA_OC_BITS); - Spi_8301_Vars->Stat_Reg_1.FETHB_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHB_OC_BITS); - Spi_8301_Vars->Stat_Reg_1.FETLB_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLB_OC_BITS); - Spi_8301_Vars->Stat_Reg_1.FETHC_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHC_OC_BITS); - Spi_8301_Vars->Stat_Reg_1.FETLC_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLC_OC_BITS); - Spi_8301_Vars->Stat_Reg_1_Value = drvDataNew; - - // Update Status Register 2 - drvRegName = DRV8301_RegName_Status_2; - drvDataNew = DRV8301_readSpi(handle,drvRegName); - Spi_8301_Vars->Stat_Reg_2.GVDD_OV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS2_GVDD_OV_BITS); - Spi_8301_Vars->Stat_Reg_2.DeviceID = (uint16_t)(drvDataNew & (uint16_t)DRV8301_STATUS2_ID_BITS); - Spi_8301_Vars->Stat_Reg_2_Value = drvDataNew; - - // Update Control Register 1 - drvRegName = DRV8301_RegName_Control_1; - drvDataNew = DRV8301_readSpi(handle,drvRegName); - Spi_8301_Vars->Ctrl_Reg_1.DRV8301_CURRENT = (DRV8301_PeakCurrent_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_GATE_CURRENT_BITS); - Spi_8301_Vars->Ctrl_Reg_1.DRV8301_RESET = (DRV8301_Reset_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_GATE_RESET_BITS); - Spi_8301_Vars->Ctrl_Reg_1.PWM_MODE = (DRV8301_PwmMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_PWM_MODE_BITS); - Spi_8301_Vars->Ctrl_Reg_1.OC_MODE = (DRV8301_OcMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_OC_MODE_BITS); - Spi_8301_Vars->Ctrl_Reg_1.OC_ADJ_SET = (DRV8301_VdsLevel_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_OC_ADJ_SET_BITS); - Spi_8301_Vars->Ctrl_Reg_1_Value = drvDataNew; - - // Update Control Register 2 - drvRegName = DRV8301_RegName_Control_2; - drvDataNew = DRV8301_readSpi(handle,drvRegName); - Spi_8301_Vars->Ctrl_Reg_2.OCTW_SET = (DRV8301_OcTwMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_OCTW_SET_BITS); - Spi_8301_Vars->Ctrl_Reg_2.GAIN = (DRV8301_ShuntAmpGain_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_GAIN_BITS); - Spi_8301_Vars->Ctrl_Reg_2.DC_CAL_CH1p2 = (DRV8301_DcCalMode_e)(drvDataNew & (uint16_t)(DRV8301_CTRL2_DC_CAL_1_BITS | DRV8301_CTRL2_DC_CAL_2_BITS)); - Spi_8301_Vars->Ctrl_Reg_2.OC_TOFF = (DRV8301_OcOffTimeMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_OC_TOFF_BITS); - Spi_8301_Vars->Ctrl_Reg_2_Value = drvDataNew; - Spi_8301_Vars->RcvCmd = false; - } - - return; -} // end of DRV8301_readData() function - - -void DRV8301_setupSpi(DRV8301_Handle handle, DRV_SPI_8301_Vars_t *Spi_8301_Vars) -{ - DRV8301_RegName_e drvRegName; - uint16_t drvDataNew; - -// Why impose hardcoded values? -// Defaults should be device defaults or application level specified. -// Setting other hardcoded here is just confusing! - -#if 0 - // Update Control Register 1 - drvRegName = DRV8301_RegName_Control_1; - drvDataNew = (DRV8301_PeakCurrent_0p25_A | \ - DRV8301_Reset_Normal | \ - DRV8301_PwmMode_Six_Inputs | \ - DRV8301_OcMode_CurrentLimit | \ - DRV8301_VdsLevel_0p730_V); - DRV8301_writeSpi(handle,drvRegName,drvDataNew); - - // Update Control Register 2 - drvRegName = DRV8301_RegName_Control_2; - drvDataNew = (DRV8301_OcTwMode_Both | \ - DRV8301_ShuntAmpGain_10VpV | \ - DRV8301_DcCalMode_Ch1_Load | \ - DRV8301_DcCalMode_Ch2_Load | \ - DRV8301_OcOffTimeMode_Normal); - DRV8301_writeSpi(handle,drvRegName,drvDataNew); -#endif - - - Spi_8301_Vars->SndCmd = false; - Spi_8301_Vars->RcvCmd = false; - - - // Wait for the DRV8301 registers to update - osDelay(1); - - - // Update Status Register 1 - drvRegName = DRV8301_RegName_Status_1; - drvDataNew = DRV8301_readSpi(handle,drvRegName); - Spi_8301_Vars->Stat_Reg_1.FAULT = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FAULT_BITS); - Spi_8301_Vars->Stat_Reg_1.GVDD_UV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_GVDD_UV_BITS); - Spi_8301_Vars->Stat_Reg_1.PVDD_UV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_PVDD_UV_BITS); - Spi_8301_Vars->Stat_Reg_1.OTSD = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_OTSD_BITS); - Spi_8301_Vars->Stat_Reg_1.OTW = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_OTW_BITS); - Spi_8301_Vars->Stat_Reg_1.FETHA_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHA_OC_BITS); - Spi_8301_Vars->Stat_Reg_1.FETLA_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLA_OC_BITS); - Spi_8301_Vars->Stat_Reg_1.FETHB_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHB_OC_BITS); - Spi_8301_Vars->Stat_Reg_1.FETLB_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLB_OC_BITS); - Spi_8301_Vars->Stat_Reg_1.FETHC_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHC_OC_BITS); - Spi_8301_Vars->Stat_Reg_1.FETLC_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLC_OC_BITS); - - // Update Status Register 2 - drvRegName = DRV8301_RegName_Status_2; - drvDataNew = DRV8301_readSpi(handle,drvRegName); - Spi_8301_Vars->Stat_Reg_2.GVDD_OV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS2_GVDD_OV_BITS); - Spi_8301_Vars->Stat_Reg_2.DeviceID = (uint16_t)(drvDataNew & (uint16_t)DRV8301_STATUS2_ID_BITS); - - // Update Control Register 1 - drvRegName = DRV8301_RegName_Control_1; - drvDataNew = DRV8301_readSpi(handle,drvRegName); - Spi_8301_Vars->Ctrl_Reg_1.DRV8301_CURRENT = (DRV8301_PeakCurrent_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_GATE_CURRENT_BITS); - Spi_8301_Vars->Ctrl_Reg_1.DRV8301_RESET = (DRV8301_Reset_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_GATE_RESET_BITS); - Spi_8301_Vars->Ctrl_Reg_1.PWM_MODE = (DRV8301_PwmMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_PWM_MODE_BITS); - Spi_8301_Vars->Ctrl_Reg_1.OC_MODE = (DRV8301_OcMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_OC_MODE_BITS); - Spi_8301_Vars->Ctrl_Reg_1.OC_ADJ_SET = (DRV8301_VdsLevel_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_OC_ADJ_SET_BITS); - - // Update Control Register 2 - drvRegName = DRV8301_RegName_Control_2; - drvDataNew = DRV8301_readSpi(handle,drvRegName); - Spi_8301_Vars->Ctrl_Reg_2.OCTW_SET = (DRV8301_OcTwMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_OCTW_SET_BITS); - Spi_8301_Vars->Ctrl_Reg_2.GAIN = (DRV8301_ShuntAmpGain_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_GAIN_BITS); - Spi_8301_Vars->Ctrl_Reg_2.DC_CAL_CH1p2 = (DRV8301_DcCalMode_e)(drvDataNew & (uint16_t)(DRV8301_CTRL2_DC_CAL_1_BITS | DRV8301_CTRL2_DC_CAL_2_BITS)); - Spi_8301_Vars->Ctrl_Reg_2.OC_TOFF = (DRV8301_OcOffTimeMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_OC_TOFF_BITS); - - return; -} - - -// end of file diff --git a/Firmware/Drivers/DRV8301/drv8301.cpp b/Firmware/Drivers/DRV8301/drv8301.cpp new file mode 100644 index 000000000..61175d735 --- /dev/null +++ b/Firmware/Drivers/DRV8301/drv8301.cpp @@ -0,0 +1,176 @@ + +#include "drv8301.hpp" +#include "utils.hpp" +#include "cmsis_os.h" +#include "board.h" + +const SPI_InitTypeDef Drv8301::spi_config_ = { + .Mode = SPI_MODE_MASTER, + .Direction = SPI_DIRECTION_2LINES, + .DataSize = SPI_DATASIZE_16BIT, + .CLKPolarity = SPI_POLARITY_LOW, + .CLKPhase = SPI_PHASE_2EDGE, + .NSS = SPI_NSS_SOFT, + .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16, + .FirstBit = SPI_FIRSTBIT_MSB, + .TIMode = SPI_TIMODE_DISABLE, + .CRCCalculation = SPI_CRCCALCULATION_DISABLE, + .CRCPolynomial = 10, +}; + +bool Drv8301::config(float requested_gain, float* actual_gain) { + // Calculate gain setting: Snap down to have equal or larger range as + // requested or largest possible range otherwise + + // for reference: + // 20V/V on 500uOhm gives a range of +/- 150A + // 40V/V on 500uOhm gives a range of +/- 75A + // 20V/V on 666uOhm gives a range of +/- 110A + // 40V/V on 666uOhm gives a range of +/- 55A + + uint16_t gain_setting = 3; + float gain_choices[] = {10.0f, 20.0f, 40.0f, 80.0f}; + while (gain_setting && (gain_choices[gain_setting] > requested_gain)) { + gain_setting--; + } + + if (actual_gain) { + *actual_gain = gain_choices[gain_setting]; + } + + RegisterFile new_config; + + new_config.control_register_1 = + (21 << 6) // Overcurrent set to approximately 150A at 100degC. This may need tweaking. + | (0b01 << 4) // OCP_MODE: latch shut down + | (0b0 << 3) // 6x PWM mode + | (0b0 << 2) // don't reset latched faults + | (0b00 << 0); // gate-drive peak current: 1.7A + + new_config.control_register_2 = + (0b0 << 6) // OC_TOFF: cycle by cycle + | (0b00 << 4) // calibration off (normal operation) + | (gain_setting << 2) // select gain + | (0b00 << 0); // report both over temperature and over current on nOCTW pin + + bool regs_equal = (regs_.control_register_1 == new_config.control_register_1) + && (regs_.control_register_2 == new_config.control_register_2); + + if (!regs_equal) { + regs_ = new_config; + state_ = kStateUninitialized; + enable_gpio_.write(false); + } + + return true; +} + +bool Drv8301::init() { + uint16_t val; + + if (state_ == kStateReady) { + return true; + } + + // Reset DRV chip. The enable pin also controls the SPI interface, not only + // the driver stages. + enable_gpio_.write(false); + delay_us(40); // mimumum pull-down time for full reset: 20us + state_ = kStateUninitialized; // make is_ready() ignore transient errors before registers are set up + enable_gpio_.write(true); + osDelay(20); // t_spi_ready, max = 10ms + + // Write current configuration + bool wrote_regs = write_reg(kRegNameControl1, regs_.control_register_1) + && write_reg(kRegNameControl1, regs_.control_register_1) + && write_reg(kRegNameControl1, regs_.control_register_1) + && write_reg(kRegNameControl1, regs_.control_register_1) + && write_reg(kRegNameControl1, regs_.control_register_1) // the write operation tends to be ignored if only done once (not sure why) + && write_reg(kRegNameControl2, regs_.control_register_2); + if (!wrote_regs) { + return false; + } + + // Wait for configuration to be applied + delay_us(100); + state_ = kStateStartupChecks; + + bool is_read_regs = read_reg(kRegNameControl1, &val) && (val == regs_.control_register_1) + && read_reg(kRegNameControl2, &val) && (val == regs_.control_register_2); + if (!is_read_regs) { + return false; + } + + if (get_error() != FaultType_NoFault) { + return false; + } + + // There could have been an nFAULT edge meanwhile. In this case we shouldn't + // consider the driver ready. + CRITICAL_SECTION() { + if (state_ == kStateStartupChecks) { + state_ = kStateReady; + } + } + + return state_ == kStateReady; +} + +void Drv8301::do_checks() { + if (state_ != kStateUninitialized && !nfault_gpio_.read()) { + state_ = kStateUninitialized; + } +} + +bool Drv8301::is_ready() { + return state_ == kStateReady; +} + +Drv8301::FaultType_e Drv8301::get_error() { + uint16_t fault1, fault2; + + if (!read_reg(kRegNameStatus1, &fault1) || + !read_reg(kRegNameStatus2, &fault2)) { + return (FaultType_e)0xffffffff; + } + + return (FaultType_e)((uint32_t)fault1 | ((uint32_t)(fault2 & 0x0080) << 16)); +} + +bool Drv8301::read_reg(const RegName_e regName, uint16_t* data) { + tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Read, regName, 0); + if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) { + return false; + } + + delay_us(1); + + tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Read, regName, 0); + rx_buf_ = 0xffff; + if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), (uint8_t *)(&rx_buf_), 1, 1000)) { + return false; + } + + delay_us(1); + + if (rx_buf_ == 0xbeef) { + return false; + } + + if (data) { + *data = rx_buf_ & 0x07FF; + } + + return true; +} + +bool Drv8301::write_reg(const RegName_e regName, const uint16_t data) { + // Do blocking write + tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Write, regName, data); + if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) { + return false; + } + delay_us(1); + + return true; +} diff --git a/Firmware/Drivers/DRV8301/drv8301.h b/Firmware/Drivers/DRV8301/drv8301.h deleted file mode 100644 index e3c3e3f34..000000000 --- a/Firmware/Drivers/DRV8301/drv8301.h +++ /dev/null @@ -1,727 +0,0 @@ -/* --COPYRIGHT--,BSD - * Copyright (c) 2015, Texas Instruments Incorporated - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Texas Instruments Incorporated nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * --/COPYRIGHT--*/ -#ifndef _DRV8301_H_ -#define _DRV8301_H_ - -//! \file drivers/drvic/drv8301/src/32b/f28x/f2806x/drv8301.h -//! \brief Contains public interface to various functions related -//! to the DRV8301 object -//! -//! (C) Copyright 2015, Texas Instruments, Inc. - - -// ************************************************************************** -// the includes - -#include "stdbool.h" -#include "stdint.h" - -// drivers - -#include "stm32f4xx_hal.h" - -// Port -typedef SPI_HandleTypeDef* SPI_Handle; -typedef GPIO_TypeDef* GPIO_Handle; -typedef uint16_t GPIO_Number_e; - - -//! -//! \defgroup DRV8301 - -//! -//! \ingroup DRV8301 -//@{ - - -#ifdef __cplusplus -extern "C" { -#endif - - -// ************************************************************************** -// the defines - -//! \brief Defines the address mask -//! -#define DRV8301_ADDR_MASK (0x7800) - - -//! \brief Defines the data mask -//! -#define DRV8301_DATA_MASK (0x07FF) - - -//! \brief Defines the R/W mask -//! -#define DRV8301_RW_MASK (0x8000) - - -//! \brief Defines the R/W mask -//! -#define DRV8301_FAULT_TYPE_MASK (0x07FF) - - -//! \brief Defines the location of the FETLC_OC (FET Low side, Phase C Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETLC_OC_BITS (1 << 0) - -//! \brief Defines the location of the FETLC_OC (FET High side, Phase C Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETHC_OC_BITS (1 << 1) - -//! \brief Defines the location of the FETLC_OC (FET Low side, Phase B Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETLB_OC_BITS (1 << 2) - -//! \brief Defines the location of the FETLC_OC (FET High side, Phase B Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETHB_OC_BITS (1 << 3) - -//! \brief Defines the location of the FETLC_OC (FET Low side, Phase A Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETLA_OC_BITS (1 << 4) - -//! \brief Defines the location of the FETLC_OC (FET High side, Phase A Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETHA_OC_BITS (1 << 5) - -//! \brief Defines the location of the OTW (Over Temperature Warning) bits in the Status 1 register -//! -#define DRV8301_STATUS1_OTW_BITS (1 << 6) - -//! \brief Defines the location of the OTSD (Over Temperature Shut Down) bits in the Status 1 register -//! -#define DRV8301_STATUS1_OTSD_BITS (1 << 7) - -//! \brief Defines the location of the PVDD_UV (Power supply Vdd, Under Voltage) bits in the Status 1 register -//! -#define DRV8301_STATUS1_PVDD_UV_BITS (1 << 8) - -//! \brief Defines the location of the GVDD_UV (DRV8301 Vdd, Under Voltage) bits in the Status 1 register -//! -#define DRV8301_STATUS1_GVDD_UV_BITS (1 << 9) - -//! \brief Defines the location of the FAULT bits in the Status 1 register -//! -#define DRV8301_STATUS1_FAULT_BITS (1 << 10) - - -//! \brief Defines the location of the Device ID bits in the Status 2 register -//! -#define DRV8301_STATUS2_ID_BITS (15 << 0) - -//! \brief Defines the location of the GVDD_OV (DRV8301 Vdd, Over Voltage) bits in the Status 2 register -//! -#define DRV8301_STATUS2_GVDD_OV_BITS (1 << 7) - - -//! \brief Defines the location of the GATE_CURRENT bits in the Control 1 register -//! -#define DRV8301_CTRL1_GATE_CURRENT_BITS (3 << 0) - -//! \brief Defines the location of the GATE_RESET bits in the Control 1 register -//! -#define DRV8301_CTRL1_GATE_RESET_BITS (1 << 2) - -//! \brief Defines the location of the PWM_MODE bits in the Control 1 register -//! -#define DRV8301_CTRL1_PWM_MODE_BITS (1 << 3) - -//! \brief Defines the location of the OC_MODE bits in the Control 1 register -//! -#define DRV8301_CTRL1_OC_MODE_BITS (3 << 4) - -//! \brief Defines the location of the OC_ADJ bits in the Control 1 register -//! -#define DRV8301_CTRL1_OC_ADJ_SET_BITS (31 << 6) - - -//! \brief Defines the location of the OCTW_SET bits in the Control 2 register -//! -#define DRV8301_CTRL2_OCTW_SET_BITS (3 << 0) - -//! \brief Defines the location of the GAIN bits in the Control 2 register -//! -#define DRV8301_CTRL2_GAIN_BITS (3 << 2) - -//! \brief Defines the location of the DC_CAL_1 bits in the Control 2 register -//! -#define DRV8301_CTRL2_DC_CAL_1_BITS (1 << 4) - -//! \brief Defines the location of the DC_CAL_2 bits in the Control 2 register -//! -#define DRV8301_CTRL2_DC_CAL_2_BITS (1 << 5) - -//! \brief Defines the location of the OC_TOFF bits in the Control 2 register -//! -#define DRV8301_CTRL2_OC_TOFF_BITS (1 << 6) - - -// ************************************************************************** -// the typedefs - -//! \brief Enumeration for the R/W modes -//! -typedef enum -{ - DRV8301_CtrlMode_Read = 1 << 15, //!< Read Mode - DRV8301_CtrlMode_Write = 0 << 15 //!< Write Mode -} DRV8301_CtrlMode_e; - - -//! \brief Enumeration for the DC calibration modes -//! -typedef enum -{ - DRV8301_DcCalMode_Ch1_Load = (0 << 4), //!< Shunt amplifier 1 connected to load via input pins - DRV8301_DcCalMode_Ch1_NoLoad = (1 << 4), //!< Shunt amplifier 1 disconnected from load and input pins are shorted - DRV8301_DcCalMode_Ch2_Load = (0 << 5), //!< Shunt amplifier 2 connected to load via input pins - DRV8301_DcCalMode_Ch2_NoLoad = (1 << 5) //!< Shunt amplifier 2 disconnected from load and input pins are shorted -} DRV8301_DcCalMode_e; - - -//! \brief Enumeration for the fault types -//! -typedef enum -{ - DRV8301_FaultType_NoFault = (0 << 0), //!< No fault - DRV8301_FaultType_FETLC_OC = (1 << 0), //!< FET Low side, Phase C Over Current fault - DRV8301_FaultType_FETHC_OC = (1 << 1), //!< FET High side, Phase C Over Current fault - DRV8301_FaultType_FETLB_OC = (1 << 2), //!< FET Low side, Phase B Over Current fault - DRV8301_FaultType_FETHB_OC = (1 << 3), //!< FET High side, Phase B Over Current fault - DRV8301_FaultType_FETLA_OC = (1 << 4), //!< FET Low side, Phase A Over Current fault - DRV8301_FaultType_FETHA_OC = (1 << 5), //!< FET High side, Phase A Over Current fault - DRV8301_FaultType_OTW = (1 << 6), //!< Over Temperature Warning fault - DRV8301_FaultType_OTSD = (1 << 7), //!< Over Temperature Shut Down fault - DRV8301_FaultType_PVDD_UV = (1 << 8), //!< Power supply Vdd Under Voltage fault - DRV8301_FaultType_GVDD_UV = (1 << 9), //!< DRV8301 Vdd Under Voltage fault - DRV8301_FaultType_GVDD_OV = (1 << 10) //!< DRV8301 Vdd Over Voltage fault -} DRV8301_FaultType_e; - - -//! \brief Enumeration for the Over Current modes -//! -typedef enum -{ - DRV8301_OcMode_CurrentLimit = 0 << 4, //!< current limit when OC detected - DRV8301_OcMode_LatchShutDown = 1 << 4, //!< latch shut down when OC detected - DRV8301_OcMode_ReportOnly = 2 << 4, //!< report only when OC detected - DRV8301_OcMode_Disabled = 3 << 4 //!< OC protection disabled -} DRV8301_OcMode_e; - - -//! \brief Enumeration for the Over Current Off Time modes -//! -typedef enum -{ - DRV8301_OcOffTimeMode_Normal = 0 << 6, //!< normal CBC operation - DRV8301_OcOffTimeMode_Ctrl = 1 << 6 //!< off time control during OC -} DRV8301_OcOffTimeMode_e; - - -//! \brief Enumeration for the Over Current, Temperature Warning modes -//! -typedef enum -{ - DRV8301_OcTwMode_Both = 0 << 0, //!< report both OT and OC at /OCTW pin - DRV8301_OcTwMode_OT_Only = 1 << 0, //!< report only OT at /OCTW pin - DRV8301_OcTwMode_OC_Only = 2 << 0 //!< report only OC at /OCTW pin -} DRV8301_OcTwMode_e; - - -//! \brief Enumeration for the drv8301 peak current levels -//! -typedef enum -{ - DRV8301_PeakCurrent_1p70_A = 0 << 0, //!< drv8301 driver peak current 1.70A - DRV8301_PeakCurrent_0p70_A = 1 << 0, //!< drv8301 driver peak current 0.70A - DRV8301_PeakCurrent_0p25_A = 2 << 0 //!< drv8301 driver peak current 0.25A -} DRV8301_PeakCurrent_e; - - -//! \brief Enumeration for the PWM modes -//! -typedef enum -{ - DRV8301_PwmMode_Six_Inputs = 0 << 3, //!< six independent inputs - DRV8301_PwmMode_Three_Inputs = 1 << 3 //!< three independent nputs -} DRV8301_PwmMode_e; - - -//! \brief Enumeration for the register names -//! -typedef enum -{ - DRV8301_RegName_Status_1 = 0 << 11, //!< Status Register 1 - DRV8301_RegName_Status_2 = 1 << 11, //!< Status Register 2 - DRV8301_RegName_Control_1 = 2 << 11, //!< Control Register 1 - DRV8301_RegName_Control_2 = 3 << 11 //!< Control Register 2 -} DRV8301_RegName_e; - - - -//! \brief Enumeration for the shunt amplifier gains -//! -typedef enum -{ - DRV8301_Reset_Normal = 0 << 2, //!< normal - DRV8301_Reset_All = 1 << 2 //!< reset all -} DRV8301_Reset_e; - - -//! \brief Enumeration for the shunt amplifier gains -//! -typedef enum -{ - DRV8301_ShuntAmpGain_10VpV = 0 << 2, //!< 10 V per V - DRV8301_ShuntAmpGain_20VpV = 1 << 2, //!< 20 V per V - DRV8301_ShuntAmpGain_40VpV = 2 << 2, //!< 40 V per V - DRV8301_ShuntAmpGain_80VpV = 3 << 2 //!< 80 V per V -} DRV8301_ShuntAmpGain_e; - - -//! \brief Enumeration for the shunt amplifier number -//! -typedef enum -{ - DRV8301_ShuntAmpNumber_1 = 1, //!< Shunt amplifier number 1 - DRV8301_ShuntAmpNumber_2 = 2 //!< Shunt amplifier number 2 -} DRV8301_ShuntAmpNumber_e; - - -//! \brief Enumeration for the Vds level for th over current adjustment -//! -typedef enum -{ - DRV8301_VdsLevel_0p060_V = 0 << 6, //!< Vds = 0.060 V - DRV8301_VdsLevel_0p068_V = 1 << 6, //!< Vds = 0.068 V - DRV8301_VdsLevel_0p076_V = 2 << 6, //!< Vds = 0.076 V - DRV8301_VdsLevel_0p086_V = 3 << 6, //!< Vds = 0.086 V - DRV8301_VdsLevel_0p097_V = 4 << 6, //!< Vds = 0.097 V - DRV8301_VdsLevel_0p109_V = 5 << 6, //!< Vds = 0.109 V - DRV8301_VdsLevel_0p123_V = 6 << 6, //!< Vds = 0.123 V - DRV8301_VdsLevel_0p138_V = 7 << 6, //!< Vds = 0.138 V - DRV8301_VdsLevel_0p155_V = 8 << 6, //!< Vds = 0.155 V - DRV8301_VdsLevel_0p175_V = 9 << 6, //!< Vds = 0.175 V - DRV8301_VdsLevel_0p197_V = 10 << 6, //!< Vds = 0.197 V - DRV8301_VdsLevel_0p222_V = 11 << 6, //!< Vds = 0.222 V - DRV8301_VdsLevel_0p250_V = 12 << 6, //!< Vds = 0.250 V - DRV8301_VdsLevel_0p282_V = 13 << 6, //!< Vds = 0.282 V - DRV8301_VdsLevel_0p317_V = 14 << 6, //!< Vds = 0.317 V - DRV8301_VdsLevel_0p358_V = 15 << 6, //!< Vds = 0.358 V - DRV8301_VdsLevel_0p403_V = 16 << 6, //!< Vds = 0.403 V - DRV8301_VdsLevel_0p454_V = 17 << 6, //!< Vds = 0.454 V - DRV8301_VdsLevel_0p511_V = 18 << 6, //!< Vds = 0.511 V - DRV8301_VdsLevel_0p576_V = 19 << 6, //!< Vds = 0.576 V - DRV8301_VdsLevel_0p648_V = 20 << 6, //!< Vds = 0.648 V - DRV8301_VdsLevel_0p730_V = 21 << 6, //!< Vds = 0.730 V - DRV8301_VdsLevel_0p822_V = 22 << 6, //!< Vds = 0.822 V - DRV8301_VdsLevel_0p926_V = 23 << 6, //!< Vds = 0.926 V - DRV8301_VdsLevel_1p043_V = 24 << 6, //!< Vds = 1.403 V - DRV8301_VdsLevel_1p175_V = 25 << 6, //!< Vds = 1.175 V - DRV8301_VdsLevel_1p324_V = 26 << 6, //!< Vds = 1.324 V - DRV8301_VdsLevel_1p491_V = 27 << 6, //!< Vds = 1.491 V - DRV8301_VdsLevel_1p679_V = 28 << 6, //!< Vds = 1.679 V - DRV8301_VdsLevel_1p892_V = 29 << 6, //!< Vds = 1.892 V - DRV8301_VdsLevel_2p131_V = 30 << 6, //!< Vds = 2.131 V - DRV8301_VdsLevel_2p400_V = 31 << 6 //!< Vds = 2.400 V -} DRV8301_VdsLevel_e; - - -typedef enum -{ - DRV8301_GETID=0 -} Drv8301SpiOutputDataSelect_e; - - -typedef struct _DRV_SPI_8301_Stat1_t_ -{ - bool FAULT; - bool GVDD_UV; - bool PVDD_UV; - bool OTSD; - bool OTW; - bool FETHA_OC; - bool FETLA_OC; - bool FETHB_OC; - bool FETLB_OC; - bool FETHC_OC; - bool FETLC_OC; -}DRV_SPI_8301_Stat1_t_; - - -typedef struct _DRV_SPI_8301_Stat2_t_ -{ - bool GVDD_OV; - uint16_t DeviceID; -}DRV_SPI_8301_Stat2_t_; - - -typedef struct _DRV_SPI_8301_CTRL1_t_ -{ - DRV8301_PeakCurrent_e DRV8301_CURRENT; - DRV8301_Reset_e DRV8301_RESET; - DRV8301_PwmMode_e PWM_MODE; - DRV8301_OcMode_e OC_MODE; - DRV8301_VdsLevel_e OC_ADJ_SET; -}DRV_SPI_8301_CTRL1_t_; - - -typedef struct _DRV_SPI_8301_CTRL2_t_ -{ - DRV8301_OcTwMode_e OCTW_SET; - DRV8301_ShuntAmpGain_e GAIN; - DRV8301_DcCalMode_e DC_CAL_CH1p2; - DRV8301_OcOffTimeMode_e OC_TOFF; -}DRV_SPI_8301_CTRL2_t_; - - -typedef struct _DRV_SPI_8301_Vars_t_ -{ - DRV_SPI_8301_Stat1_t_ Stat_Reg_1; - DRV_SPI_8301_Stat2_t_ Stat_Reg_2; - DRV_SPI_8301_CTRL1_t_ Ctrl_Reg_1; - DRV_SPI_8301_CTRL2_t_ Ctrl_Reg_2; - uint16_t Stat_Reg_1_Value; - uint16_t Stat_Reg_2_Value; - uint16_t Ctrl_Reg_1_Value; - uint16_t Ctrl_Reg_2_Value; - bool SndCmd; - bool RcvCmd; - -}DRV_SPI_8301_Vars_t; - - -//! \brief Defines the DRV8301 object -//! -typedef struct _DRV8301_Obj_ -{ - SPI_Handle spiHandle; //!< the handle for the serial peripheral interface - GPIO_Handle EngpioHandle; //!< the gpio handle that is connected to the drv8301 enable pin - GPIO_Number_e EngpioNumber; //!< the gpio number that is connected to the drv8301 enable pin - GPIO_Handle nCSgpioHandle; //!< the gpio handle that is connected to the drv8301 nCS pin - GPIO_Number_e nCSgpioNumber; //!< the gpio number that is connected to the drv8301 nCS pin - bool RxTimeOut; //!< the timeout flag for the RX fifo - bool enableTimeOut; //!< the timeout flag for drv8301 enable -} DRV8301_Obj; - - -//! \brief Defines the DRV8301 handle -//! -typedef struct _DRV8301_Obj_ *DRV8301_Handle; - - -//! \brief Defines the DRV8301 Word type -//! -typedef uint16_t DRV8301_Word_t; - - -// ************************************************************************** -// the globals - - - -// ************************************************************************** -// the function prototypes - - -//! \brief Builds the control word -//! \param[in] ctrlMode The control mode -//! \param[in] regName The register name -//! \param[in] data The data -//! \return The control word -static inline DRV8301_Word_t DRV8301_buildCtrlWord(const DRV8301_CtrlMode_e ctrlMode, - const DRV8301_RegName_e regName, - const uint16_t data) -{ - DRV8301_Word_t ctrlWord = ctrlMode | regName | (data & DRV8301_DATA_MASK); - - return(ctrlWord); -} // end of DRV8301_buildCtrlWord() function - - -//! \brief Gets the DC calibration mode -//! \param[in] handle The DRV8301 handle -//! \param[in] ampNumber The shunt amplifier number -//! \return The DC calibration mode -extern DRV8301_DcCalMode_e DRV8301_getDcCalMode(DRV8301_Handle handle, - const DRV8301_ShuntAmpNumber_e ampNumber); - -//! \brief Enables the DRV8301 -//! \param[in] handle The DRV8301 handle -extern void DRV8301_enable(DRV8301_Handle handle); - - -//! \brief Gets the fault type -//! \param[in] handle The DRV8301 handle -//! \return The fault type -extern DRV8301_FaultType_e DRV8301_getFaultType(DRV8301_Handle handle); - - -//! \brief Gets the device ID -//! \param[in] handle The DRV8301 handle -//! \return The device ID -extern uint16_t DRV8301_getId(DRV8301_Handle handle); - - -//! \brief Gets the over current level -//! \param[in] handle The DRV8301 handle -//! \return The over current level, V -extern DRV8301_VdsLevel_e DRV8301_getOcLevel(DRV8301_Handle handle); - - -//! \brief Gets the over current mode -//! \param[in] handle The DRV8301 handle -//! \return The over current mode -extern DRV8301_OcMode_e DRV8301_getOcMode(DRV8301_Handle handle); - - -//! \brief Gets the over current off time mode -//! \param[in] handle The DRV8301 handle -//! \return The over current off time mode -extern DRV8301_OcOffTimeMode_e DRV8301_getOcOffTimeMode(DRV8301_Handle handle); - - -//! \brief Gets the over current, temperature warning mode -//! \param[in] handle The DRV8301 handle -//! \return The over current, temperature warning mode -extern DRV8301_OcTwMode_e DRV8301_getOcTwMode(DRV8301_Handle handle); - - -//! \brief Gets the peak current value -//! \param[in] handle The DRV8301 handle -//! \return The peak current value -extern DRV8301_PeakCurrent_e DRV8301_getPeakCurrent(DRV8301_Handle handle); - - -//! \brief Gets the PWM mode -//! \param[in] handle The DRV8301 handle -//! \return The PWM mode -extern DRV8301_PwmMode_e DRV8301_getPwmMode(DRV8301_Handle handle); - - -//! \brief Gets the shunt amplifier gain value -//! \param[in] handle The DRV8301 handle -//! \return The shunt amplifier gain value -extern DRV8301_ShuntAmpGain_e DRV8301_getShuntAmpGain(DRV8301_Handle handle); - - -//! \brief Gets the status register 1 value -//! \param[in] handle The DRV8301 handle -//! \return The status register1 value -extern uint16_t DRV8301_getStatusRegister1(DRV8301_Handle handle); - - -//! \brief Gets the status register 2 value -//! \param[in] handle The DRV8301 handle -//! \return The status register2 value -extern uint16_t DRV8301_getStatusRegister2(DRV8301_Handle handle); - - -//! \brief Initializes the DRV8301 object -//! \param[in] pMemory A pointer to the memory for the DRV8301 object -//! \param[in] numBytes The number of bytes allocated for the DRV8301 object, bytes -//! \return The DRV8301 object handle -extern DRV8301_Handle DRV8301_init(void *pMemory,const size_t numBytes); - - -//! \brief Determines if DRV8301 fault has occurred -//! \param[in] handle The DRV8301 handle -//! \return A boolean value denoting if a fault has occurred (true) or not (false) -extern bool DRV8301_isFault(DRV8301_Handle handle); - - -//! \brief Determines if DRV8301 is in reset -//! \param[in] handle The DRV8301 handle -//! \return A boolean value denoting if the DRV8301 is in reset (true) or not (false) -extern bool DRV8301_isReset(DRV8301_Handle handle); - - -//! \brief Reads data from the DRV8301 register -//! \param[in] handle The DRV8301 handle -//! \param[in] regName The register name -//! \return The data value -extern uint16_t DRV8301_readSpi(DRV8301_Handle handle,const DRV8301_RegName_e regName); - - -//! \brief Resets the DRV8301 -//! \param[in] handle The DRV8301 handle -extern void DRV8301_reset(DRV8301_Handle handle); - - -//! \brief Resets the enable timeout flag -//! \param[in] handle The DRV8301 handle -static inline void DRV8301_resetEnableTimeout(DRV8301_Handle handle) -{ - DRV8301_Obj *obj = (DRV8301_Obj *)handle; - - obj->enableTimeOut = false; - - return; -} - - -//! \brief Resets the RX fifo timeout flag -//! \param[in] handle The DRV8301 handle -static inline void DRV8301_resetRxTimeout(DRV8301_Handle handle) -{ - DRV8301_Obj *obj = (DRV8301_Obj *)handle; - - obj->RxTimeOut = false; - - return; -} - - -//! \brief Sets the DC calibration mode -//! \param[in] handle The DRV8301 handle -//! \param[in] ampNumber The shunt amplifier number -//! \param[in] mode The DC calibration mode -extern void DRV8301_setDcCalMode(DRV8301_Handle handle, - const DRV8301_ShuntAmpNumber_e ampNumber, - const DRV8301_DcCalMode_e mode); - - -//! \brief Sets the GPIO handle in the DRV8301 -//! \param[in] handle The DRV8301 handle -//! \param[in] gpioHandle The GPIO handle to use -void DRV8301_setGpioHandle(DRV8301_Handle handle,GPIO_Handle gpioHandle); - - -//! \brief Sets the GPIO number in the DRV8301 -//! \param[in] handle The DRV8301 handle -//! \param[in] gpioHandle The GPIO number to use -void DRV8301_setGpioNumber(DRV8301_Handle handle,GPIO_Number_e gpioNumber); - - -//! \brief Sets the over current level in terms of Vds -//! \param[in] handle The DRV8301 handle -//! \param[in] VdsLevel The over current level, V -extern void DRV8301_setOcLevel(DRV8301_Handle handle,const DRV8301_VdsLevel_e VdsLevel); - - -//! \brief Sets the over current mode -//! \param[in] handle The DRV8301 handle -//! \param[in] mode The over current mode -extern void DRV8301_setOcMode(DRV8301_Handle handle,const DRV8301_OcMode_e mode); - - -//! \brief Sets the over current off time mode -//! \param[in] handle The DRV8301 handle -//! \param[in] mode The over current off time mode -extern void DRV8301_setOcOffTimeMode(DRV8301_Handle handle,const DRV8301_OcOffTimeMode_e mode); - - -//! \brief Sets the over current, temperature warning mode -//! \param[in] handle The DRV8301 handle -//! \param[in] mode The over current, temperature warning mode -extern void DRV8301_setOcTwMode(DRV8301_Handle handle,const DRV8301_OcTwMode_e mode); - - -//! \brief Sets the peak current value -//! \param[in] handle The DRV8301 handle -//! \param[in] peakCurrent The peak current value -extern void DRV8301_setPeakCurrent(DRV8301_Handle handle,const DRV8301_PeakCurrent_e peakCurrent); - - -//! \brief Sets the PWM mode -//! \param[in] handle The DRV8301 handle -//! \param[in] mode The PWM mode -extern void DRV8301_setPwmMode(DRV8301_Handle handle,const DRV8301_PwmMode_e mode); - - -//! \brief Sets the shunt amplifier gain value -//! \param[in] handle The DRV8301 handle -//! \param[in] gain The shunt amplifier gain value -extern void DRV8301_setShuntAmpGain(DRV8301_Handle handle,const DRV8301_ShuntAmpGain_e gain); - - -//! \brief Sets the SPI handle in the DRV8301 -//! \param[in] handle The DRV8301 handle -//! \param[in] spiHandle The SPI handle to use -void DRV8301_setSpiHandle(DRV8301_Handle handle,SPI_Handle spiHandle); - - -//! \brief Writes data to the DRV8301 register -//! \param[in] handle The DRV8301 handle -//! \param[in] regName The register name -//! \param[in] data The data value -extern void DRV8301_writeSpi(DRV8301_Handle handle,const DRV8301_RegName_e regName,const uint16_t data); - - -//! \brief Interface to all 8301 SPI variables -//! -//! \details Call this function periodically to be able to read the DRV8301 Status1, Status2, -//! Control1, and Control2 registers and write the Control1 and Control2 registers. -//! This function updates the members of the structure DRV_SPI_8301_Vars_t. -//! How to use in Setup -//! Code -//! Add the structure declaration DRV_SPI_8301_Vars_t to your code -//! Make sure the SPI and 8301 EN_Gate GPIO are setup for the 8301 by using HAL_init and HAL_setParams -//! During code setup, call HAL_enableDrv and HAL_setupDrvSpi -//! In background loop, call DRV8301_writeData and DRV8301_readData -//! How to use in Runtime -//! Watch window -//! Add the structure, declared by DRV_SPI_8301_Vars_t above, to the watch window -//! Runtime -//! Pull down the menus from the DRV_SPI_8301_Vars_t strcuture to the desired setting -//! Set SndCmd to send the settings to the DRV8301 -//! If a read of the DRV8301 registers is required, se RcvCmd -//! -//! \param[in] handle The DRV8301 handle -//! \param[in] Spi_8301_Vars The (DRV_SPI_8301_Vars_t) structure that contains all DRV8301 Status/Control register options -extern void DRV8301_writeData(DRV8301_Handle handle, DRV_SPI_8301_Vars_t *Spi_8301_Vars); - - -//! \param[in] handle The DRV8301 handle -//! \param[in] Spi_8301_Vars The (DRV_SPI_8301_Vars_t) structure that contains all DRV8301 Status/Control register options -extern void DRV8301_readData(DRV8301_Handle handle, DRV_SPI_8301_Vars_t *Spi_8301_Vars); - - -//! \brief Initialize the interface to all 8301 SPI variables -//! \param[in] handle The DRV8301 handle -extern void DRV8301_setupSpi(DRV8301_Handle handle, DRV_SPI_8301_Vars_t *Spi_8301_Vars); - - -#ifdef __cplusplus -} -#endif // extern "C" - -//@} // ingroup - -#endif // end of _DRV8301_H_ definition - - - - - diff --git a/Firmware/Drivers/DRV8301/drv8301.hpp b/Firmware/Drivers/DRV8301/drv8301.hpp new file mode 100644 index 000000000..29a044657 --- /dev/null +++ b/Firmware/Drivers/DRV8301/drv8301.hpp @@ -0,0 +1,145 @@ +#ifndef __DRV8301_HPP +#define __DRV8301_HPP + +#include "stdbool.h" +#include "stdint.h" + +#include +#include +#include + + +class Drv8301 : public GateDriverBase, public OpAmpBase { +public: + typedef enum { + FaultType_NoFault = (0 << 0), //!< No fault + + // Status Register 1 + FaultType_FETLC_OC = (1 << 0), //!< FET Low side, Phase C Over Current fault + FaultType_FETHC_OC = (1 << 1), //!< FET High side, Phase C Over Current fault + FaultType_FETLB_OC = (1 << 2), //!< FET Low side, Phase B Over Current fault + FaultType_FETHB_OC = (1 << 3), //!< FET High side, Phase B Over Current fault + FaultType_FETLA_OC = (1 << 4), //!< FET Low side, Phase A Over Current fault + FaultType_FETHA_OC = (1 << 5), //!< FET High side, Phase A Over Current fault + FaultType_OTW = (1 << 6), //!< Over Temperature Warning fault + FaultType_OTSD = (1 << 7), //!< Over Temperature Shut Down fault + FaultType_PVDD_UV = (1 << 8), //!< Power supply Vdd Under Voltage fault + FaultType_GVDD_UV = (1 << 9), //!< DRV8301 Vdd Under Voltage fault + FaultType_FAULT = (1 << 10), + + // Status Register 2 + FaultType_GVDD_OV = (1 << 23) //!< DRV8301 Vdd Over Voltage fault + } FaultType_e; + + Drv8301(Stm32SpiArbiter* spi_arbiter, Stm32Gpio ncs_gpio, + Stm32Gpio enable_gpio, Stm32Gpio nfault_gpio) + : spi_arbiter_(spi_arbiter), ncs_gpio_(ncs_gpio), + enable_gpio_(enable_gpio), nfault_gpio_(nfault_gpio) {} + + /** + * @brief Prepares the gate driver's configuration. + * + * If the gate driver was in ready state and the new configuration is + * different from the old one then the gate driver will exit ready state. + * + * In any case changes to the configuration only take effect with a call to + * init(). + */ + bool config(float requested_gain, float* actual_gain); + + /** + * @brief Initializes the gate driver to the configuration prepared with + * config(). + * + * Returns true on success or false otherwise (e.g. if the gate driver is + * not connected or not powered or if config() was not yet called). + */ + bool init(); + + /** + * @brief Monitors the nFAULT pin. + * + * This must be run at an interval of <8ms from the moment the init() + * functions starts to run, otherwise it's possible that a temporary power + * loss is missed, leading to unwanted register values. + * In case of power loss the nFAULT pin can be low for as little as 8ms. + */ + void do_checks(); + + /** + * @brief Returns true if and only if the DRV8301 chip is in an initialized + * state and ready to do switching and current sensor opamp operation. + */ + bool is_ready() final; + + /** + * @brief This has no effect on this driver chip because the drive stages are + * always enabled while the chip is initialized + */ + bool set_enabled(bool enabled) final { return true; } + + FaultType_e get_error(); + + float get_midpoint() final { + return 0.5f; // [V] + } + + float get_max_output_swing() final { + return 1.35f / 1.65f; // +-1.35V, normalized from a scale of +-1.65V to +-0.5 + } + +private: + enum CtrlMode_e { + DRV8301_CtrlMode_Read = 1 << 15, //!< Read Mode + DRV8301_CtrlMode_Write = 0 << 15 //!< Write Mode + }; + + enum RegName_e { + kRegNameStatus1 = 0 << 11, //!< Status Register 1 + kRegNameStatus2 = 1 << 11, //!< Status Register 2 + kRegNameControl1 = 2 << 11, //!< Control Register 1 + kRegNameControl2 = 3 << 11 //!< Control Register 2 + }; + + struct RegisterFile { + uint16_t control_register_1; + uint16_t control_register_2; + }; + + static inline uint16_t build_ctrl_word(const CtrlMode_e ctrlMode, + const RegName_e regName, + const uint16_t data) { + return ctrlMode | regName | (data & 0x07FF); + } + + /** @brief Reads data from a DRV8301 register */ + bool read_reg(const RegName_e regName, uint16_t* data); + + /** @brief Writes data to a DRV8301 register. There is no check if the write succeeded. */ + bool write_reg(const RegName_e regName, const uint16_t data); + + static const SPI_InitTypeDef spi_config_; + + // Configuration + Stm32SpiArbiter* spi_arbiter_; + Stm32Gpio ncs_gpio_; + Stm32Gpio enable_gpio_; + Stm32Gpio nfault_gpio_; + + RegisterFile regs_; //!< Current configuration. If is_ready_ is + //!< true then this can be considered consistent + //!< with the actual file on the DRV8301 chip. + + // We don't put these buffers on the stack because we place the stack in + // a RAM section which cannot be used by DMA. + uint16_t tx_buf_, rx_buf_; + + enum { + kStateUninitialized, + kStateStartupChecks, + kStateReady, + } state_ = kStateUninitialized; +}; + + +#endif // __DRV8301_HPP diff --git a/Firmware/Drivers/STM32/stm32_gpio.cpp b/Firmware/Drivers/STM32/stm32_gpio.cpp new file mode 100644 index 000000000..eca0e653f --- /dev/null +++ b/Firmware/Drivers/STM32/stm32_gpio.cpp @@ -0,0 +1,234 @@ + +#include "stm32_gpio.hpp" + +#define N_EXTI 16 + +struct subscription_t { + GPIO_TypeDef* port = nullptr; + void (*callback)(void*) = nullptr; + void* ctx = nullptr; +} subscriptions[N_EXTI]; + +const Stm32Gpio Stm32Gpio::none{nullptr, 0}; + +/** + * @brief Returns the IRQ number associated with a certain pin. + * Note that all GPIOs with the same pin number map to the same IRQn, + * no matter which port they belong to. + */ +IRQn_Type get_irq_number(uint16_t pin_number) { + switch (pin_number) { + case 0: return EXTI0_IRQn; + case 1: return EXTI1_IRQn; + case 2: return EXTI2_IRQn; + case 3: return EXTI3_IRQn; + case 4: return EXTI4_IRQn; + case 5: + case 6: + case 7: + case 8: + case 9: return EXTI9_5_IRQn; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: return EXTI15_10_IRQn; + default: return (IRQn_Type)0; // impossible + } +} + +#define GPIO_MODE 0x00000003U +#define GPIO_OUTPUT_TYPE 0x00000010U + + +bool Stm32Gpio::config(uint32_t mode, uint32_t pull, uint32_t speed) { + if (port_ == GPIOA) { + __HAL_RCC_GPIOA_CLK_ENABLE(); + } else if (port_ == GPIOB) { + __HAL_RCC_GPIOB_CLK_ENABLE(); + } else if (port_ == GPIOC) { + __HAL_RCC_GPIOC_CLK_ENABLE(); + } else if (port_ == GPIOD) { + __HAL_RCC_GPIOD_CLK_ENABLE(); + } else if (port_ == GPIOE) { + __HAL_RCC_GPIOE_CLK_ENABLE(); + } else if (port_ == GPIOF) { + __HAL_RCC_GPIOF_CLK_ENABLE(); + } else if (port_ == GPIOG) { + __HAL_RCC_GPIOG_CLK_ENABLE(); + } else if (port_ == GPIOH) { + __HAL_RCC_GPIOH_CLK_ENABLE(); + } else { + return false; + } + + size_t position = get_pin_number(); + + // The following code is mostly taken from HAL_GPIO_Init + + /* Configure IO Direction mode (Input, Output, Alternate or Analog) */ + uint32_t temp = port_->MODER; + temp &= ~(GPIO_MODER_MODER0 << (position * 2U)); + temp |= ((mode & GPIO_MODE) << (position * 2U)); + port_->MODER = temp; + + /* In case of Output or Alternate function mode selection */ + if((mode == GPIO_MODE_OUTPUT_PP) || (mode == GPIO_MODE_AF_PP) || + (mode == GPIO_MODE_OUTPUT_OD) || (mode == GPIO_MODE_AF_OD)) + { + /* Check the Speed parameter */ + assert_param(IS_GPIO_SPEED(speed)); + /* Configure the IO Speed */ + temp = port_->OSPEEDR; + temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U)); + temp |= (speed << (position * 2U)); + port_->OSPEEDR = temp; + + /* Configure the IO Output Type */ + temp = port_->OTYPER; + temp &= ~(GPIO_OTYPER_OT_0 << position) ; + temp |= (((mode & GPIO_OUTPUT_TYPE) >> 4U) << position); + port_->OTYPER = temp; + } + + /* Activate the Pull-up or Pull down resistor for the current IO */ + temp = port_->PUPDR; + temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U)); + temp |= ((pull) << (position * 2U)); + port_->PUPDR = temp; + + return true; +} + +bool Stm32Gpio::subscribe(bool rising_edge, bool falling_edge, void (*callback)(void*), void* ctx) { + uint32_t pin_number = get_pin_number(); + if (pin_number >= N_EXTI) { + return false; // invalid pin number + } + + struct subscription_t& subscription = subscriptions[pin_number]; + + GPIO_TypeDef* no_port = nullptr; + if (!__atomic_compare_exchange_n(&subscription.port, &no_port, port_, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { + return false; // already in use + } + + // The following code is mostly taken from HAL_GPIO_Init + __HAL_RCC_SYSCFG_CLK_ENABLE(); + + uint32_t temp = SYSCFG->EXTICR[pin_number >> 2U]; + temp &= ~(0x0FU << (4U * (pin_number & 0x03U))); + temp |= ((uint32_t)(GPIO_GET_INDEX(port_)) << (4U * (pin_number & 0x03U))); + SYSCFG->EXTICR[pin_number >> 2U] = temp; + + + if (rising_edge) { + EXTI->RTSR |= (uint32_t)pin_mask_; + } else { + EXTI->RTSR &= ~((uint32_t)pin_mask_); + } + + if (falling_edge) { + EXTI->FTSR |= (uint32_t)pin_mask_; + } else { + EXTI->FTSR &= ~((uint32_t)pin_mask_); + } + + EXTI->EMR &= ~((uint32_t)pin_mask_); + EXTI->IMR |= (uint32_t)pin_mask_; + + // Clear any previous triggers + __HAL_GPIO_EXTI_CLEAR_IT(pin_mask_); + + subscription.ctx = ctx; + subscription.callback = callback; + return true; +} + +void Stm32Gpio::unsubscribe() { + uint32_t pin_number = get_pin_number(); + if (pin_number >= N_EXTI) { + return; // invalid pin number + } + + struct subscription_t& subscription = subscriptions[pin_number]; + + if (subscription.port != port_) { + return; // the subscription was not for this GPIO + } + + EXTI->IMR |= (uint32_t)pin_mask_; + __HAL_GPIO_EXTI_CLEAR_IT(pin_mask_); + + // At this point no more interrupts will be triggered for this GPIO + + subscription.callback = nullptr; + subscription.ctx = nullptr; + subscription.port = nullptr; // after this line, the subscription can be reused (possibly by another thread) +} + +void maybe_handle(uint16_t exti_number) { + if(__HAL_GPIO_EXTI_GET_IT(1 << exti_number) == RESET) { + return; // This interrupt source did not trigger the interrupt line + } + + __HAL_GPIO_EXTI_CLEAR_IT(1 << exti_number); + + if (exti_number >= N_EXTI) { + return; + } + + subscription_t& subscription = subscriptions[exti_number]; + if (subscription.callback) { + (*subscription.callback)(subscription.ctx); + } +} + +extern "C" { + +/** @brief Entrypoint for the EXTI line 0 interrupt. */ +void EXTI0_IRQHandler(void) { + maybe_handle(0); +} + +/** @brief Entrypoint for the EXTI line 1 interrupt. */ +void EXTI1_IRQHandler(void) { + maybe_handle(1); +} + +/** @brief Entrypoint for the EXTI line 2 interrupt. */ +void EXTI2_IRQHandler(void) { + maybe_handle(2); +} + +/** @brief Entrypoint for the EXTI line 3 interrupt. */ +void EXTI3_IRQHandler(void) { + maybe_handle(3); +} + +/** @brief Entrypoint for the EXTI line 4 interrupt. */ +void EXTI4_IRQHandler(void) { + maybe_handle(4); +} + +/** @brief Entrypoint for the EXTI lines 5-9 interrupt. */ +void EXTI9_5_IRQHandler(void) { + maybe_handle(5); + maybe_handle(6); + maybe_handle(7); + maybe_handle(8); + maybe_handle(9); +} + +/** @brief This function handles EXTI lines 10-15 interrupt. */ +void EXTI15_10_IRQHandler(void) { + maybe_handle(10); + maybe_handle(11); + maybe_handle(12); + maybe_handle(13); + maybe_handle(14); + maybe_handle(15); +} + +} diff --git a/Firmware/Drivers/STM32/stm32_gpio.hpp b/Firmware/Drivers/STM32/stm32_gpio.hpp new file mode 100644 index 000000000..1e1199f9a --- /dev/null +++ b/Firmware/Drivers/STM32/stm32_gpio.hpp @@ -0,0 +1,82 @@ +#ifndef __STM32_GPIO_HPP +#define __STM32_GPIO_HPP + +#include + +class Stm32Gpio { +public: + static const Stm32Gpio none; + + Stm32Gpio() : port_(nullptr), pin_mask_(0) {} + Stm32Gpio(GPIO_TypeDef* port, uint16_t pin) : port_(port), pin_mask_(pin) {} + + operator bool() const { return port_ && pin_mask_; } + + /** + * @brief Configures the GPIO with the specified parameters. + * + * This can be done regardless of the current state of the GPIO. + * + * If any subscription is in place, it is not disabled by this function. + */ + bool config(uint32_t mode, uint32_t pull, uint32_t speed = GPIO_SPEED_FREQ_LOW); + + void write(bool state) { + if (port_) { + HAL_GPIO_WritePin(port_, pin_mask_, state ? GPIO_PIN_SET : GPIO_PIN_RESET); + } + } + + bool read() { + return port_ && (port_->IDR & pin_mask_); + } + + /** + * @brief Subscribes to external interrupts on the specified GPIO. + * + * Before calling this function the gpio should most likely be configured as + * input (however this is not mandatory, the interrupt works in output mode + * too). + * Also you need to enable the EXTIx_IRQn interrupt vectors in the NVIC, + * otherwise the subscription won't have any effect. + * + * Only one subscription is allowed per pin number. I.e. it is not possible + * to set up a subscription for both PA0 and PB0 at the same time. + * + * This function is thread-safe with respect to all other public functions + * of this class. + * + * Returns true if the subscription was set up successfully or false otherwise. + */ + bool subscribe(bool rising_edge, bool falling_edge, void (*callback)(void*), void* ctx); + + /** + * @brief Unsubscribes from external interrupt on the specified GPIO. + * + * If no subscription was active for this GPIO, calling this function has no + * effect. + * + * This function is thread-safe with respect to all other public functions + * of this class, however it must not be called from an interrupt routine + * running at a higher priority than the interrupt that is being unsubscribed. + * + * After this function returns the callback given to subscribe() will no + * longer be invoked. + */ + void unsubscribe(); + + uint16_t get_pin_number() { + uint16_t pin_number = 0; + uint16_t pin_mask = pin_mask_ >> 1; + while (pin_mask) { + pin_mask >>= 1; + pin_number++; + } + return pin_number; + } + + GPIO_TypeDef* port_; + uint16_t pin_mask_; // TODO: store pin_number_ instead of pin_mask_ +}; + +#endif // __STM32_GPIO_HPP diff --git a/Firmware/MotorControl/nvm.c b/Firmware/Drivers/STM32/stm32_nvm.c similarity index 89% rename from Firmware/MotorControl/nvm.c rename to Firmware/Drivers/STM32/stm32_nvm.c index 5a1b4a71e..3d31ce18e 100644 --- a/Firmware/MotorControl/nvm.c +++ b/Firmware/Drivers/STM32/stm32_nvm.c @@ -30,22 +30,38 @@ * fields as "valid" (in the direction of increasing address). */ -#include "nvm.h" +#include "stm32_nvm.h" -#include -#include #include #if defined(STM32F405xx) +#include +#include + // refer to page 75 of datasheet: // http://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf -#define FLASH_SECTOR_10_BASE (const volatile uint8_t*)0x80C0000UL -#define FLASH_SECTOR_10_SIZE 0x20000UL -#define FLASH_SECTOR_11_BASE (const volatile uint8_t*)0x80E0000UL -#define FLASH_SECTOR_11_SIZE 0x20000UL +#define FLASH_SECTOR_A FLASH_SECTOR_10 +#define FLASH_SECTOR_A_BASE (const volatile uint8_t*)0x80C0000UL +#define FLASH_SECTOR_A_SIZE 0x20000UL +#define FLASH_SECTOR_B FLASH_SECTOR_11 +#define FLASH_SECTOR_B_BASE (const volatile uint8_t*)0x80E0000UL +#define FLASH_SECTOR_B_SIZE 0x20000UL + +#elif defined(STM32F722xx) + +#include +#include + +// refer to page 68 of datasheet: +// https://www.st.com/resource/en/reference_manual/dm00305990-stm32f72xxx-and-stm32f73xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf +#define FLASH_SECTOR_A FLASH_SECTOR_1 +#define FLASH_SECTOR_A_BASE (const volatile uint8_t*)0x8004000UL +#define FLASH_SECTOR_A_SIZE 0x4000UL +#define FLASH_SECTOR_B FLASH_SECTOR_2 +#define FLASH_SECTOR_B_BASE (const volatile uint8_t*)0x8008000UL +#define FLASH_SECTOR_B_SIZE 0x4000UL -#define HAL_FLASH_ClearError() __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR | FLASH_FLAG_PGPERR) #else #error "unknown flash sector size" #endif @@ -66,30 +82,58 @@ typedef struct { } sector_t; sector_t sectors[] = { { - .sector_id = FLASH_SECTOR_10, - .n_data = FLASH_SECTOR_10_SIZE >> 3, - .n_reserved = (FLASH_SECTOR_10_SIZE >> 3) >> 5, - .alloc_table = FLASH_SECTOR_10_BASE, - .data = (uint64_t *)FLASH_SECTOR_10_BASE + .sector_id = FLASH_SECTOR_A, + .n_data = FLASH_SECTOR_A_SIZE >> 3, + .n_reserved = (FLASH_SECTOR_A_SIZE >> 3) >> 5, + .alloc_table = FLASH_SECTOR_A_BASE, + .data = (uint64_t *)FLASH_SECTOR_A_BASE }, { - .sector_id = FLASH_SECTOR_11, - .n_data = FLASH_SECTOR_11_SIZE >> 3, - .n_reserved = (FLASH_SECTOR_11_SIZE >> 3) >> 5, - .alloc_table = FLASH_SECTOR_11_BASE, - .data = (uint64_t *)FLASH_SECTOR_11_BASE + .sector_id = FLASH_SECTOR_B, + .n_data = FLASH_SECTOR_B_SIZE >> 3, + .n_reserved = (FLASH_SECTOR_B_SIZE >> 3) >> 5, + .alloc_table = FLASH_SECTOR_B_BASE, + .data = (uint64_t *)FLASH_SECTOR_B_BASE }}; uint8_t read_sector_; // 0 or 1 to indicate which sector to read from and which to write to size_t n_staging_area_; // number of 64-bit values that were reserved using NVM_start_write size_t n_valid_; // number of 64-bit fields that can be read +static const uint32_t FLASH_ERR_FLAGS = +#if defined(FLASH_FLAG_EOP) + FLASH_FLAG_EOP | +#endif +#if defined(FLASH_FLAG_OPERR) + FLASH_FLAG_OPERR | +#endif +#if defined(FLASH_FLAG_WRPERR) + FLASH_FLAG_WRPERR | +#endif +#if defined(FLASH_FLAG_PGAERR) + FLASH_FLAG_PGAERR | +#endif +#if defined(FLASH_FLAG_PGSERR) + FLASH_FLAG_PGSERR | +#endif +#if defined(FLASH_FLAG_PGPERR) + FLASH_FLAG_PGPERR | +#endif + 0; + +static void HAL_FLASH_ClearError() { + __HAL_FLASH_CLEAR_FLAG(FLASH_ERR_FLAGS); +} + + // @brief Erases a flash sector. This sets all bits in the sector to 1. // The sector's current index is reset to the minimum value (n_reserved). // @returns 0 on success or a non-zero error code otherwise int erase(sector_t *sector) { FLASH_EraseInitTypeDef erase_struct = { .TypeErase = FLASH_TYPEERASE_SECTORS, +#if defined(FLASH_OPTCR_nDBANK) .Banks = 0, // only used for mass erase +#endif .Sector = sector->sector_id, .NbSectors = 1, .VoltageRange = FLASH_VOLTAGE_RANGE_3 @@ -262,6 +306,7 @@ size_t NVM_get_max_write_length(void) { } // @brief Reads from the latest committed block in the non-volatile memory. +// The function either succeeds or leaves the provided buffer unmodified. // @param offset: offset in bytes (0 meaning the beginning of the valid area) // @param data: buffer to write to // @param length: length in bytes (if (offset + length) is out of range, the function fails) @@ -374,6 +419,7 @@ int NVM_commit(void) { #include +#include /** @brief Call this at startup to test/demo the NVM driver Expected output when starting with a fully erased NVM diff --git a/Firmware/MotorControl/nvm.h b/Firmware/Drivers/STM32/stm32_nvm.h similarity index 98% rename from Firmware/MotorControl/nvm.h rename to Firmware/Drivers/STM32/stm32_nvm.h index efffa1743..8e0e8c2f0 100644 --- a/Firmware/MotorControl/nvm.h +++ b/Firmware/Drivers/STM32/stm32_nvm.h @@ -1,5 +1,5 @@ /* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __NVML_H +#ifndef __NVM_H #define __NVM_H #ifdef __cplusplus diff --git a/Firmware/Drivers/STM32/stm32_spi_arbiter.cpp b/Firmware/Drivers/STM32/stm32_spi_arbiter.cpp new file mode 100644 index 000000000..a1565ef65 --- /dev/null +++ b/Firmware/Drivers/STM32/stm32_spi_arbiter.cpp @@ -0,0 +1,129 @@ + +#include "stm32_spi_arbiter.hpp" +#include "stm32_system.h" +#include "utils.hpp" +#include + +bool equals(const SPI_InitTypeDef& lhs, const SPI_InitTypeDef& rhs) { + return (lhs.Mode == rhs.Mode) + && (lhs.Direction == rhs.Direction) + && (lhs.DataSize == rhs.DataSize) + && (lhs.CLKPolarity == rhs.CLKPolarity) + && (lhs.CLKPhase == rhs.CLKPhase) + && (lhs.NSS == rhs.NSS) + && (lhs.BaudRatePrescaler == rhs.BaudRatePrescaler) + && (lhs.FirstBit == rhs.FirstBit) + && (lhs.TIMode == rhs.TIMode) + && (lhs.CRCCalculation == rhs.CRCCalculation) + && (lhs.CRCPolynomial == rhs.CRCPolynomial); +} + +bool Stm32SpiArbiter::acquire_task(SpiTask* task) { + return !__atomic_exchange_n(&task->is_in_use, true, __ATOMIC_SEQ_CST); +} + +void Stm32SpiArbiter::release_task(SpiTask* task) { + task->is_in_use = false; +} + +bool Stm32SpiArbiter::start() { + if (!task_list_) { + return false; + } + + SpiTask& task = *task_list_; + if (!equals(task.config, hspi_->Init)) { + HAL_SPI_DeInit(hspi_); + hspi_->Init = task.config; + HAL_SPI_Init(hspi_); + __HAL_SPI_ENABLE(hspi_); + } + task.ncs_gpio.write(false); + + HAL_StatusTypeDef status = HAL_ERROR; + + if (hspi_->hdmatx->State != HAL_DMA_STATE_READY || hspi_->hdmarx->State != HAL_DMA_STATE_READY) { + // This can happen if the DMA or interrupt priorities are not configured properly. + status = HAL_BUSY; + } else if (task.tx_buf && task.rx_buf) { + status = HAL_SPI_TransmitReceive_DMA(hspi_, (uint8_t*)task.tx_buf, task.rx_buf, task.length); + } else if (task.tx_buf) { + status = HAL_SPI_Transmit_DMA(hspi_, (uint8_t*)task.tx_buf, task.length); + } else if (task.rx_buf) { + status = HAL_SPI_Receive_DMA(hspi_, task.rx_buf, task.length); + } + + if (status != HAL_OK) { + task.ncs_gpio.write(true); + } + + return status == HAL_OK; +} + +void Stm32SpiArbiter::transfer_async(SpiTask* task) { + task->next = nullptr; + + // Append new task to task list. + // We could try to do this lock free but we could also use our time for useful things. + SpiTask** ptr = &task_list_; + CRITICAL_SECTION() { + while (*ptr) + ptr = &(*ptr)->next; + *ptr = task; + } + + // If the list was empty before, kick off the SPI arbiter now + if (ptr == &task_list_) { + if (!start()) { + if (task->on_complete) { + (*task->on_complete)(task->on_complete_ctx, false); + } + } + } +} + +// TODO: this currently only works when called in a CMSIS thread. +bool Stm32SpiArbiter::transfer(SPI_InitTypeDef config, Stm32Gpio ncs_gpio, const uint8_t* tx_buf, uint8_t* rx_buf, size_t length, uint32_t timeout_ms) { + volatile uint8_t result = 0xff; + + SpiTask task = { + .config = config, + .ncs_gpio = ncs_gpio, + .tx_buf = tx_buf, + .rx_buf = rx_buf, + .length = length, + .on_complete = [](void* ctx, bool success) { *(volatile uint8_t*)ctx = success ? 1 : 0; }, + .on_complete_ctx = (void*)&result, + .is_in_use = false, + .next = nullptr + }; + + transfer_async(&task); + + while (result == 0xff) { + osDelay(1); // TODO: honor timeout + } + + return result; +} + +void Stm32SpiArbiter::on_complete() { + if (!task_list_) { + return; // this should not happen + } + + // Wrap up transfer + task_list_->ncs_gpio.write(true); + if (task_list_->on_complete) { + (*task_list_->on_complete)(task_list_->on_complete_ctx, true); + } + + // Start next task if any + SpiTask* next = nullptr; + CRITICAL_SECTION() { + next = task_list_ = task_list_->next; + } + if (next) { + start(); + } +} diff --git a/Firmware/Drivers/STM32/stm32_spi_arbiter.hpp b/Firmware/Drivers/STM32/stm32_spi_arbiter.hpp new file mode 100644 index 000000000..d6883fc20 --- /dev/null +++ b/Firmware/Drivers/STM32/stm32_spi_arbiter.hpp @@ -0,0 +1,94 @@ +#ifndef __STM32_SPI_ARBITER_HPP +#define __STM32_SPI_ARBITER_HPP + +#include "stm32_gpio.hpp" + +#include + +class Stm32SpiArbiter { +public: + struct SpiTask { + SPI_InitTypeDef config; + Stm32Gpio ncs_gpio; + const uint8_t* tx_buf; + uint8_t* rx_buf; + size_t length; + void (*on_complete)(void*, bool); + void* on_complete_ctx; + bool is_in_use = false; + struct SpiTask* next; + }; + + Stm32SpiArbiter(SPI_HandleTypeDef* hspi): hspi_(hspi) {} + + /** + * Reserves the task for the caller if it's not in use currently. + * + * This can be used by the caller to ensure that the task structure is not + * overwritten while it's in use in a preceding transfer. + * + * Example: + * + * if (acquire_task(&task)) { + * transfer_async(&task) + * } + * + * A call to release_task() makes the task available for use again. + */ + static bool acquire_task(SpiTask* task); + + /** + * Releases the task so that the next call to `acquire_task()` returns true. + * This should usually be called inside the on_complete() callback after + * the rx buffer has been processed. + */ + static void release_task(SpiTask* task); + + /** + * @brief Enqueues a non-blocking transfer. + * + * Once the transfer completes, fails or is aborted, the callback is invoked. + * + * This function is thread-safe with respect to all other public functions + * of this class. + * + * @param task: Contains all configuration data for this transfer. + * The struct pointed to by this argument must remain valid and + * unmodified until the completion callback is invoked. + */ + void transfer_async(SpiTask* task); + + /** + * @brief Executes a blocking transfer. + * + * If the SPI is busy this function waits until it becomes available or + * the specified timeout passes, whichever comes first. + * + * Returns true on successful transfer or false otherwise. + * + * This function is thread-safe with respect to all other public functions + * of this class. + * + * @param config: The SPI configuration to apply for this transfer. + * @param ncs_gpio: The active low GPIO to actuate during this transfer. + * @param tx_buf: Buffer for the outgoing data to be sent. Can be null unless + * rx_buf is null too. + * @param rx_buf: Buffer for the incoming data to be sent. Can be null unless + * tx_buf is null too. + */ + bool transfer(SPI_InitTypeDef config, Stm32Gpio ncs_gpio, const uint8_t* tx_buf, uint8_t* rx_buf, size_t length, uint32_t timeout_ms); + + /** + * @brief Completion method to be called from HAL_SPI_TxCpltCallback, + * HAL_SPI_RxCpltCallback and HAL_SPI_TxRxCpltCallback. + */ + void on_complete(); + +private: + bool start(); + + SPI_HandleTypeDef* hspi_; + SpiTask* task_list_ = nullptr; +}; + +#endif // __STM32_SPI_ARBITER_HPP \ No newline at end of file diff --git a/Firmware/Drivers/STM32/stm32_system.cpp b/Firmware/Drivers/STM32/stm32_system.cpp new file mode 100644 index 000000000..078cc0584 --- /dev/null +++ b/Firmware/Drivers/STM32/stm32_system.cpp @@ -0,0 +1,4 @@ + +#include "stm32_system.h" + +uint32_t irq_counters[254]; // 14 core interrupts, 240 NVIC interrupts diff --git a/Firmware/Drivers/STM32/stm32_system.h b/Firmware/Drivers/STM32/stm32_system.h new file mode 100644 index 000000000..42727d532 --- /dev/null +++ b/Firmware/Drivers/STM32/stm32_system.h @@ -0,0 +1,70 @@ +#ifndef __STM32_SYSTEM_H +#define __STM32_SYSTEM_H + +#if defined(STM32F405xx) +#include +#elif defined(STM32F722xx) +#include +#else +#error "unknown STM32 microcontroller" +#endif + +// C/C++ definitions + +#ifdef __cplusplus +extern "C" { +#endif + +// Uncomment the following line to sacrifice 1kB of RAM for the ability to +// monitor the number of times each interrupt fires. +//#define ENABLE_IRQ_COUNTER + +#ifdef ENABLE_IRQ_COUNTER +extern uint32_t irq_counters[]; +#define COUNT_IRQ(irqn) (++irq_counters[irqn + 14]) +#define GET_IRQ_COUNTER(irqn) irq_counters[irqn + 14] +#else +#define COUNT_IRQ(irqn) ((void)0) +#define GET_IRQ_COUNTER(irqn) 0 +#endif + +static inline uint32_t cpu_enter_critical() { + uint32_t primask = __get_PRIMASK(); + __disable_irq(); + return primask; +} + +static inline void cpu_exit_critical(uint32_t priority_mask) { + __set_PRIMASK(priority_mask); +} + +#ifdef __cplusplus +} +#endif + + +// C++ only definitions + +#ifdef __cplusplus + +struct CriticalSectionContext { + CriticalSectionContext(const CriticalSectionContext&) = delete; + CriticalSectionContext(const CriticalSectionContext&&) = delete; + void operator=(const CriticalSectionContext&) = delete; + void operator=(const CriticalSectionContext&&) = delete; + operator bool() { return true; }; + CriticalSectionContext() : mask_(cpu_enter_critical()) {} + ~CriticalSectionContext() { cpu_exit_critical(mask_); } + uint32_t mask_; + bool exit_ = false; +}; + +#ifdef __clang__ +#define CRITICAL_SECTION() for (CriticalSectionContext __critical_section_context; !__critical_section_context.exit_; __critical_section_context.exit_ = true) +#else +#define CRITICAL_SECTION() if (CriticalSectionContext __critical_section_context{}) +#endif + +#endif + +#endif // __STM32_SYSTEM_H \ No newline at end of file diff --git a/Firmware/Drivers/STM32/stm32_timer.hpp b/Firmware/Drivers/STM32/stm32_timer.hpp new file mode 100644 index 000000000..f6432f00d --- /dev/null +++ b/Firmware/Drivers/STM32/stm32_timer.hpp @@ -0,0 +1,83 @@ +#ifndef __STM32_TIMER_HPP +#define __STM32_TIMER_HPP + +#include "stm32_system.h" +#include +#include + +class Stm32Timer { +public: + /** + * @brief Starts multiple timers deterministically and synchronously from the + * specified offset. + * + * All timers are atomically (*) put into the following state (regardless of + * their previous state/configuration): + * - TIMx_CNT will be initialized according to the corresponding counter[i] parameter. + * - If the timer is in center-aligned mode, it will be set to up-counting direction. + * - The update repetition counter is reset to TIMx_RCR (if applicable). + * - The prescaler counter is reset. + * - Update interrupts are disabled. + * - The counter put into running state. + * + * This function is implemented by generating an update event on all selected timers. + * That means as a side effect all things that are connected to the update event + * except the interrupt routine itself (i.e. ADCs, DMAs, slave timers, etc) will + * be triggered. + * + * Also you probably want to disable any connected PWM outputs to prevent glitches. + * + * (*) Best-effort atomically. There will be skew of a handful of clock cycles + * but it's always the same given the compiler version and configuration. + */ + template + static void start_synchronously(std::array timers, std::array counters) { + start_synchronously_impl(timers, counters, std::make_index_sequence()); + } + +private: + +#pragma GCC push_options +#pragma GCC optimize (3) + + template + static void start_synchronously_impl(std::array timers, std::array counters, std::index_sequence) { + for (size_t i = 0; i < I; ++i) { + TIM_HandleTypeDef* htim = timers[i]; + + // Stop the timer so we can start all of them later more atomically. + htim->Instance->CR1 &= ~TIM_CR1_CEN; + + // Generate update event to force all of the timer's registers into + // a known state. + __HAL_TIM_DISABLE_IT(htim, TIM_IT_UPDATE); + htim->Instance->EGR |= TIM_EGR_UG; + __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE); + + // Load counter with the desired value. + htim->Instance->CNT = counters[i]; + } + + register volatile uint32_t* cr_addr[I]; + register uint32_t cr_val[I]; + for (size_t i = 0; i < I; ++i) { + cr_addr[i] = &timers[i]->Instance->CR1; + cr_val[i] = timers[i]->Instance->CR1 | TIM_CR1_CEN; + } + + // Restart all timers as atomically as possible. + // By inspection we find that this is compiled to the following code: + // f7ff faa0 bl 800bdd0 + // f8c9 6000 str.w r6, [r9] + // f8c8 5000 str.w r5, [r8] + // 603c str r4, [r7, #0] + // f7ff fa9d bl 800bdd8 + uint32_t mask = cpu_enter_critical(); + int dummy[I] = {(*cr_addr[Is] = cr_val[Is], 0)...}; + (void)dummy; + cpu_exit_critical(mask); + } +#pragma GCC pop_options +}; + +#endif // __STM32_TIMER_HPP \ No newline at end of file diff --git a/Firmware/Drivers/gate_driver.hpp b/Firmware/Drivers/gate_driver.hpp new file mode 100644 index 000000000..99f7fc1ff --- /dev/null +++ b/Firmware/Drivers/gate_driver.hpp @@ -0,0 +1,42 @@ +#ifndef __GATE_DRIVER_HPP +#define __GATE_DRIVER_HPP + +struct GateDriverBase { + /** + * @brief Unlocks or locks the gate signals of the gate driver. + * + * While locked the PWM inputs are ignored and the switches are always in + * OFF state. + * Not all gate drivers implement this function and may return true even if + * the gate driver was not locked. + */ + virtual bool set_enabled(bool enabled) = 0; + + /** + * @brief Returns false if the gate driver is in a state where the output + * drive stages are disarmed or not properly configured (e.g. because they + * are not initialized or there was a fault condition). + */ + virtual bool is_ready() = 0; +}; + +struct OpAmpBase { + /** + * @brief Returns false if the opamp is in a state where it's not operating + * with the latest configured gain (e.g. because it was not initialized or + * there was a fault condition). + */ + virtual bool is_ready() = 0; + + /** + * @brief Returns the neutral voltage of the OpAmp in Volts + */ + virtual float get_midpoint() = 0; + + /** + * @brief Returns the maximum voltage swing away from the midpoint voltage (in Volts) + */ + virtual float get_max_output_swing() = 0; +}; + +#endif // __GATE_DRIVER_HPP \ No newline at end of file diff --git a/Firmware/Makefile b/Firmware/Makefile index a40ae89f1..18ed7f779 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -5,22 +5,79 @@ BUILD_DIR = build FIRMWARE = $(BUILD_DIR)/ODriveFirmware.elf FIRMWARE_HEX = $(BUILD_DIR)/ODriveFirmware.hex -OPENOCD := openocd -f interface/stlink-v2.cfg \ - $(if $(value PROGRAMMER),-c 'hla_serial $(PROGRAMMER)',) \ - -f target/stm32f4x.cfg +PROGRAMMER_CMD=$(if $(value PROGRAMMER),-c 'hla_serial $(PROGRAMMER)',) +include tup.config # source build configuration to get CONFIG_BOARD_VERSION + +ifeq ($(shell python -c "import sys; print(sys.version_info.major)"), 3) + PY_CMD := python -B +else + PY_CMD := python3 -B +endif + +ifneq (,$(findstring v3.,$(CONFIG_BOARD_VERSION))) + OPENOCD := openocd -f interface/stlink-v2.cfg $(PROGRAMMER_CMD) -f target/stm32f4x.cfg -c init + GDB := arm-none-eabi-gdb --ex 'target extended-remote | openocd -f "interface/stlink-v2.cfg" -f "target/stm32f4x.cfg" -c "gdb_port pipe; log_output openocd.log"' --ex 'monitor reset halt' +else ifneq (,$(findstring v4.,$(CONFIG_BOARD_VERSION))) + OPENOCD := openocd -f interface/stlink.cfg $(PROGRAMMER_CMD) -f target/stm32f7x.cfg -c 'reset_config none separate' -c init + GDB := arm-none-eabi-gdb --ex 'target extended-remote | openocd -f "interface/stlink-v2.cfg" -f "target/stm32f7x.cfg" -c "reset_config none separate" -c "gdb_port pipe; log_output openocd.log"' --ex 'monitor reset halt' +else + $(error unknown board version) +endif + +$(info board version: $(CONFIG_BOARD_VERSION)) all: @tup --quiet --no-environ-check + @$(PY_CMD) interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/enums_template.j2 --output ../tools/odrive/enums.py + +# Copy libfibre files to odrivetool if they were built + @ ! test -f "fibre-cpp/build/libfibre-linux-amd64.so" || cp fibre-cpp/build/libfibre-linux-amd64.so ../tools/odrive/pyfibre/fibre/ + @ ! test -f "fibre-cpp/build/libfibre-linux-armhf.so" || cp fibre-cpp/build/libfibre-linux-armhf.so ../tools/odrive/pyfibre/fibre/ + @ ! test -f "fibre-cpp/build/libfibre-linux-aarch64.so" || cp fibre-cpp/build/libfibre-linux-aarch64.so ../tools/odrive/pyfibre/fibre/ + @ ! test -f "fibre-cpp/build/libfibre-macos-x86.dylib" || cp fibre-cpp/build/libfibre-macos-x86.dylib ../tools/odrive/pyfibre/fibre/ + @ ! test -f "fibre-cpp/build/libfibre-windows-amd64.dll" || cp fibre-cpp/build/libfibre-windows-amd64.dll ../tools/odrive/pyfibre/fibre/ + +libfibre-linux-armhf: + docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/fibre-linux-armhf-build:/build/build -w /build fibre-compiler configs/linux-armhf.config + cp /tmp/fibre-linux-armhf-build/libfibre-*.so ../tools/odrive/pyfibre/fibre/ + +libfibre-all: + docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/linux-amd64.config + cp /tmp/libfibre-build/libfibre-linux-amd64.so ../tools/odrive/pyfibre/fibre/ + docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/linux-armhf.config + cp /tmp/libfibre-build/libfibre-linux-armhf.so ../tools/odrive/pyfibre/fibre/ + docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/linux-aarch64.config + cp /tmp/libfibre-build/libfibre-linux-aarch64.so ../tools/odrive/pyfibre/fibre/ + docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/macos-x86.config + cp /tmp/libfibre-build/libfibre-macos-x86.dylib ../tools/odrive/pyfibre/fibre/ + docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/windows-amd64.config + cp /tmp/libfibre-build/libfibre-windows-amd64.dll ../tools/odrive/pyfibre/fibre/ + docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/wasm.config + cp /tmp/libfibre-build/libfibre-wasm.* ../GUI/fibre-js/ + +clean: + -rm -fR .dep $(BUILD_DIR) -flash: all - $(OPENOCD) -c init \ +flash-stlink2: all + $(OPENOCD) \ -c 'reset halt' \ -c 'flash write_image erase $(FIRMWARE)' \ -c 'reset run' \ -c exit -flashbmp: all +gdb-stlink2: + $(GDB) $(FIRMWARE) + +# Erase entire STM32 +erase-stlink2: + $(OPENOCD) -c 'reset halt' -c 'flash erase_sector 0 0 last' -c exit + +# Sometimes the STM32 will get it's protection bits set for unknown reasons. Unlock it with this command +unlock-stlink2: + $(OPENOCD) -c 'reset halt' -c 'stm32f2x unlock 0' + +flash-bmp: all arm-none-eabi-gdb --ex 'target extended-remote $(BMP_PORT)' \ --ex 'monitor swdp_scan' \ --ex 'attach 1' \ @@ -29,80 +86,20 @@ flashbmp: all --ex 'quit' \ $(FIRMWARE) -gdb: all - arm-none-eabi-gdb $(FIRMWARE) -x openocd.gdbinit - -dfu: all - python ../tools/odrivetool $(if $(value SERIAL_NUMBER),--serial-number $(SERIAL_NUMBER),) dfu $(FIRMWARE_HEX) - -bmp: all +gdb-bmp: all arm-none-eabi-gdb --ex 'target extended-remote /dev/stlink' \ --ex 'monitor swdp_scan' \ --ex 'attach 1' \ --ex 'load' $(FIRMWARE) -# Erase entire STM32 -erase: - $(OPENOCD) -c init -c reset\ halt -c flash\ erase_address\ 0x8000000\ 0x100000 -c reset\ run -c exit - -# Erase all configuration from the ODrive -erase_config: - $(OPENOCD) -c init -c reset\ halt -c flash\ erase_address\ 0x80C0000\ 0x40000 -c reset\ init -c reset\ run -c exit - -# Sometimes the STM32 will get it's protection bits set for unknown reasons. Unlock it with this command -unlock: - $(OPENOCD) -c init -c reset\ halt -c stm32f2x\ unlock\ 0 - -# The one-time programmable memory stores the board version -# has the following format: -# - OTP format version (0xFE: version 1) -# - vendor ID (01: ODrive Robotics - do not use this on custom incompatible hardware!) -# - product ID (01: ODrive) -# - hardware major version -# - hardware minor version -# - hardware variant (equal to the board nominal voltage) -# Bits in the OTP can only ever be set to 0 but never back to 1. -# Therefore do not try to run this command on the same board -# twice with different data. -# -# This OpenOCD command is intended for a STM32F405 and does the following: -# FLASH_KEYR = 0x45670123; // unlock FLASH_CR -# FLASH_KEYR = 0xCDEF89AB; // unlock FLASH_CR -# FLASH_CR = (1 << FLASH_CR_PG); // unlock flash memory -# [write OTP] -write_otp: -ifeq ($(OTP_CONFIRM),TRUE) - # Data: - $(OPENOCD) \ - -c init \ - -c 'reset halt' \ - -c 'mww 0x40023C04 0x45670123' \ - -c 'mww 0x40023C04 0xCDEF89AB' \ - -c 'mww 0x40023C10 0x00000001' -c 'sleep 10' \ - -c 'mwb 0x1fff7800 0xFE' -c 'sleep 10' \ - -c 'mwb 0x1fff7801 0x01' -c 'sleep 10' \ - -c 'mwb 0x1fff7802 0x01' -c 'sleep 10' \ - -c 'mwb 0x1fff7803 3' -c 'sleep 10' \ - -c 'mwb 0x1fff7804 6' -c 'sleep 10' \ - -c 'mwb 0x1fff7805 56' -c 'sleep 10' \ - -c 'reset run' \ - -c exit - @echo "OK" -else - @echo "The one-time programmable memory can only be" - @echo "written ONCE on every board (what a surprise)." - @echo "If you're on an ODrive v3.5 or later we already did this for you." - @echo "Otherwise, if you're mentally ready for this irreversible action," - @echo "take the following steps:" - @echo " 1. open the Makefile and look at the write_otp target" - @echo " 2. understand the structure of the OTP" - @echo " 3. edit the bytes that are written to match your board version" - @echo "Run this command again, this time with OTP_CONFIRM=TRUE appended" - @echo "to the command in the terminal" -endif - -clean: - -rm -fR .dep $(BUILD_DIR) +dfu: all + python ../tools/odrivetool $(if $(value SERIAL_NUMBER),--serial-number $(SERIAL_NUMBER),) dfu $(FIRMWARE_HEX) -.PHONY: all flash gdb dfu bmp clean erase_config +flash: flash-stlink2 +gdb: gdb-stlink2 +erase: erase-stlink2 +unlock: unlock-stlink2 +.PHONY: stlink2-config flash-stlink2 gdb-stlink2 erase-stlink2 unlock-stlink2 +.PHONY: flash-bmp gdb-bmp +.PHONY: all clean flash gdb erase unlock dfu fibre diff --git a/Firmware/MotorControl/acim_estimator.cpp b/Firmware/MotorControl/acim_estimator.cpp new file mode 100644 index 000000000..55cc1be81 --- /dev/null +++ b/Firmware/MotorControl/acim_estimator.cpp @@ -0,0 +1,45 @@ + +#include "acim_estimator.hpp" +#include + +void AcimEstimator::update(uint32_t timestamp) { + std::optional rotor_phase = rotor_phase_src_.present(); + std::optional rotor_phase_vel = rotor_phase_vel_src_.present(); + std::optional idq = idq_src_.present(); + + if (!rotor_phase.has_value() || !rotor_phase_vel.has_value() || !idq.has_value()) { + active_ = false; + return; + } + + auto [id, iq] = *idq; + + float dt = (float)(timestamp - last_timestamp_) / (float)TIM_1_8_CLOCK_HZ; + last_timestamp_ = timestamp; + + if (!active_) { + // Skip first iteration and use it to reset state + rotor_flux_ = 0.0f; + phase_offset_ = 0.0f; + active_ = true; + return; + } + + // Note that the effect of the current commands on the real currents is actually 1.5 PWM cycles later + // However the rotor time constant is (usually) so slow that it doesn't matter + // So we elect to write it as if the effect is immediate, to have cleaner code + + // acim_rotor_flux is normalized to units of [A] tracking Id; rotor inductance is unspecified + float dflux_by_dt = config_.slip_velocity * (id - rotor_flux_); + rotor_flux_ += dflux_by_dt * dt; + float slip_velocity = config_.slip_velocity * (iq / rotor_flux_); + // Check for issues with small denominator. + if (is_nan(slip_velocity) || (std::abs(slip_velocity) > 0.1f / dt)) { + slip_velocity = 0.0f; + } + slip_vel_ = slip_velocity; // reporting only + + stator_phase_vel_ = *rotor_phase_vel + slip_velocity; + phase_offset_ = wrap_pm_pi(phase_offset_ + slip_velocity * dt); + stator_phase_ = wrap_pm_pi(*rotor_phase + phase_offset_); +} diff --git a/Firmware/MotorControl/acim_estimator.hpp b/Firmware/MotorControl/acim_estimator.hpp new file mode 100644 index 000000000..242b5d865 --- /dev/null +++ b/Firmware/MotorControl/acim_estimator.hpp @@ -0,0 +1,36 @@ +#ifndef __ACIM_ESTIMATOR_HPP +#define __ACIM_ESTIMATOR_HPP + +#include +#include +#include + +class AcimEstimator : public ComponentBase { +public: + struct Config_t { + float slip_velocity = 14.706f; // [rad/s electrical] = 1/rotor_tau + }; + + void update(uint32_t timestamp) final; + + // Config + Config_t config_; + + // Inputs + InputPort rotor_phase_src_; + InputPort rotor_phase_vel_src_; + InputPort idq_src_; + + // State variables + float active_ = false; + uint32_t last_timestamp_ = 0; + float rotor_flux_ = 0.0f; // [A] + float phase_offset_ = 0.0f; // [A] + + // Outputs + OutputPort slip_vel_ = 0.0f; // [rad/s electrical] + OutputPort stator_phase_vel_ = 0.0f; // [rad/s] rotor flux angular velocity estimate + OutputPort stator_phase_ = 0.0f; // [rad] rotor flux phase angle estimate +}; + +#endif // __ACIM_ESTIMATOR_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/arm_cos_f32.c b/Firmware/MotorControl/arm_cos_f32.c index a63d14cbd..80f7e1adc 100644 --- a/Firmware/MotorControl/arm_cos_f32.c +++ b/Firmware/MotorControl/arm_cos_f32.c @@ -25,10 +25,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include // Sets up the correct chip specifc defines required by arm_math -#define ARM_MATH_CM4 // TODO: might change in future board versions + +#include #include "arm_math.h" #include "arm_common_tables.h" + /** * @ingroup groupFastMath */ diff --git a/Firmware/MotorControl/arm_sin_f32.c b/Firmware/MotorControl/arm_sin_f32.c index f037248f0..baec23183 100644 --- a/Firmware/MotorControl/arm_sin_f32.c +++ b/Firmware/MotorControl/arm_sin_f32.c @@ -26,8 +26,7 @@ * limitations under the License. */ -#include // Sets up the correct chip specifc defines required by arm_math -#define ARM_MATH_CM4 // TODO: might change in future board versions +#include #include "arm_math.h" #include "arm_common_tables.h" diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 3385d4208..e8421d0b2 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -5,51 +5,41 @@ #include "odrive_main.h" #include "utils.hpp" -#include "gpio_utils.hpp" #include "communication/interface_can.hpp" Axis::Axis(int axis_num, - const AxisHardwareConfig_t& hw_config, - Config_t& config, + uint16_t default_step_gpio_pin, + uint16_t default_dir_gpio_pin, + osPriority thread_priority, Encoder& encoder, SensorlessEstimator& sensorless_estimator, Controller& controller, - OnboardThermistorCurrentLimiter& fet_thermistor, - OffboardThermistorCurrentLimiter& motor_thermistor, Motor& motor, TrapezoidalTrajectory& trap, Endstop& min_endstop, - Endstop& max_endstop) + Endstop& max_endstop, + MechanicalBrake& mechanical_brake) : axis_num_(axis_num), - hw_config_(hw_config), - config_(config), + default_step_gpio_pin_(default_step_gpio_pin), + default_dir_gpio_pin_(default_dir_gpio_pin), + thread_priority_(thread_priority), encoder_(encoder), sensorless_estimator_(sensorless_estimator), controller_(controller), - fet_thermistor_(fet_thermistor), - motor_thermistor_(motor_thermistor), motor_(motor), trap_traj_(trap), min_endstop_(min_endstop), max_endstop_(max_endstop), - current_limiters_(make_array( - static_cast(&fet_thermistor), - static_cast(&motor_thermistor))), - thermistors_(make_array( - static_cast(&fet_thermistor), - static_cast(&motor_thermistor))) + mechanical_brake_(mechanical_brake) { encoder_.axis_ = this; sensorless_estimator_.axis_ = this; controller_.axis_ = this; - fet_thermistor_.axis_ = this; - motor_thermistor.axis_ = this; motor_.axis_ = this; trap_traj_.axis_ = this; min_endstop_.axis_ = this; max_endstop_.axis_ = this; - decode_step_dir_pins(); - watchdog_feed(); + mechanical_brake_.axis_ = this; } Axis::LockinConfig_t Axis::default_calibration() { @@ -84,10 +74,18 @@ static void step_cb_wrapper(void* ctx) { reinterpret_cast(ctx)->step_cb(); } +bool Axis::apply_config() { + config_.parent = this; + decode_step_dir_pins(); + watchdog_feed(); + return true; +} -// @brief Does Nothing -void Axis::setup() { - // Does nothing - Motor and encoder setup called separately. +void Axis::clear_config() { + config_ = {}; + config_.step_gpio_pin = default_step_gpio_pin_; + config_.dir_gpio_pin = default_dir_gpio_pin_; + config_.can.node_id = axis_num_; } static void run_state_machine_loop_wrapper(void* ctx) { @@ -97,118 +95,73 @@ static void run_state_machine_loop_wrapper(void* ctx) { // @brief Starts run_state_machine_loop in a new thread void Axis::start_thread() { - osThreadDef(thread_def, run_state_machine_loop_wrapper, hw_config_.thread_priority, 0, stack_size_ / sizeof(StackType_t)); + osThreadDef(thread_def, run_state_machine_loop_wrapper, thread_priority_, 0, stack_size_ / sizeof(StackType_t)); thread_id_ = osThreadCreate(osThread(thread_def), this); thread_id_valid_ = true; } -// @brief Unblocks the control loop thread. -// This is called from the current sense interrupt handler. -void Axis::signal_current_meas() { - if (thread_id_valid_) - osSignalSet(thread_id_, M_SIGNAL_PH_CURRENT_MEAS); -} - -// @brief Blocks until a current measurement is completed -// @returns True on success, false otherwise -bool Axis::wait_for_current_meas() { - return osSignalWait(M_SIGNAL_PH_CURRENT_MEAS, PH_CURRENT_MEAS_TIMEOUT).status == osEventSignal; +/** + * @brief Blocks until at least one complete control loop has been executed. + */ +bool Axis::wait_for_control_iteration() { + osSignalWait(0x0001, osWaitForever); // this might return instantly + osSignalWait(0x0001, osWaitForever); // this might be triggered at the + // end of a control loop iteration + // which was started before we entered + // this function + osSignalWait(0x0001, osWaitForever); + return true; } // step/direction interface void Axis::step_cb() { - const bool dir_pin = dir_port_->IDR & dir_pin_; - const int32_t dir = (-1 + 2 * dir_pin) * step_dir_active_; - controller_.input_pos_ += dir * config_.turns_per_step; - controller_.input_pos_updated(); -}; - -void Axis::load_default_step_dir_pin_config( - const AxisHardwareConfig_t& hw_config, Config_t* config) { - config->step_gpio_pin = hw_config.step_gpio_pin; - config->dir_gpio_pin = hw_config.dir_gpio_pin; -} - -void Axis::load_default_can_id(const int& id, Config_t& config){ - config.can_node_id = id; + if (step_dir_active_) { + dir_gpio_.read() ? ++steps_ : --steps_; + controller_.input_pos_updated(); + } } void Axis::decode_step_dir_pins() { - step_port_ = get_gpio_port_by_pin(config_.step_gpio_pin); - step_pin_ = get_gpio_pin_by_pin(config_.step_gpio_pin); - dir_port_ = get_gpio_port_by_pin(config_.dir_gpio_pin); - dir_pin_ = get_gpio_pin_by_pin(config_.dir_gpio_pin); + step_gpio_ = get_gpio(config_.step_gpio_pin); + dir_gpio_ = get_gpio(config_.dir_gpio_pin); } // @brief (de)activates step/dir input void Axis::set_step_dir_active(bool active) { if (active) { - // Set up the direction GPIO as input - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.Pin = dir_pin_; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(dir_port_, &GPIO_InitStruct); - // Subscribe to rising edges of the step GPIO - GPIO_subscribe(step_port_, step_pin_, GPIO_PULLDOWN, step_cb_wrapper, this); + if (!step_gpio_.subscribe(true, false, step_cb_wrapper, this)) { + odrv.misconfigured_ = true; + } step_dir_active_ = true; } else { step_dir_active_ = false; // Unsubscribe from step GPIO - GPIO_unsubscribe(step_port_, step_pin_); + // TODO: if we change the GPIO while the subscription is active and then + // unsubscribe then the unsubscribe is for the wrong pin. + step_gpio_.unsubscribe(); } } // @brief Do axis level checks and call subcomponent do_checks // Returns true if everything is ok. -bool Axis::do_checks() { - if (!brake_resistor_armed) - error_ |= ERROR_BRAKE_RESISTOR_DISARMED; - if ((current_state_ != AXIS_STATE_IDLE) && (motor_.armed_state_ == Motor::ARMED_STATE_DISARMED)) - // motor got disarmed in something other than the idle loop - error_ |= ERROR_MOTOR_DISARMED; - if (!(vbus_voltage >= odrv.config_.dc_bus_undervoltage_trip_level)) - error_ |= ERROR_DC_BUS_UNDER_VOLTAGE; - if (!(vbus_voltage <= odrv.config_.dc_bus_overvoltage_trip_level)) - error_ |= ERROR_DC_BUS_OVER_VOLTAGE; - +bool Axis::do_checks(uint32_t timestamp) { // Sub-components should use set_error which will propegate to this error_ - for (ThermistorCurrentLimiter* thermistor : thermistors_) { - thermistor->do_checks(); - } - motor_.do_checks(); - // encoder_.do_checks(); - // sensorless_estimator_.do_checks(); - // controller_.do_checks(); + motor_.effective_current_lim(); + motor_.do_checks(timestamp); // Check for endstop presses - if (min_endstop_.config_.enabled && min_endstop_.get_state() && !(current_state_ == AXIS_STATE_HOMING)) { + if (min_endstop_.config_.enabled && min_endstop_.rose() && !(current_state_ == AXIS_STATE_HOMING)) { error_ |= ERROR_MIN_ENDSTOP_PRESSED; - } else if (max_endstop_.config_.enabled && max_endstop_.get_state() && !(current_state_ == AXIS_STATE_HOMING)) { + } else if (max_endstop_.config_.enabled && max_endstop_.rose() && !(current_state_ == AXIS_STATE_HOMING)) { error_ |= ERROR_MAX_ENDSTOP_PRESSED; } return check_for_errors(); } -// @brief Update all esitmators -bool Axis::do_updates() { - // Sub-components should use set_error which will propegate to this error_ - for (ThermistorCurrentLimiter* thermistor : thermistors_) { - thermistor->update(); - } - encoder_.update(); - sensorless_estimator_.update(); - min_endstop_.update(); - max_endstop_.update(); - bool ret = check_for_errors(); - odCAN->send_heartbeat(this); - return ret; -} - // @brief Feed the watchdog to prevent watchdog timeouts. void Axis::watchdog_feed() { watchdog_current_value_ = get_watchdog_reset(); @@ -228,132 +181,185 @@ bool Axis::watchdog_check() { } } -bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config) { - // Spiral up current for softer rotor lock-in - lockin_state_ = LOCKIN_STATE_RAMP; - float x = 0.0f; - run_control_loop([&]() { - float phase = wrap_pm_pi(lockin_config.ramp_distance * x); - float torque = lockin_config.current * motor_.config_.torque_constant * x; - x += current_meas_period / lockin_config.ramp_time; - if (!motor_.update(torque, phase, 0.0f)) - return false; - return x < 1.0f; - }); - - // Spin states - float distance = lockin_config.ramp_distance; - float phase = wrap_pm_pi(distance); - float vel = distance / lockin_config.ramp_time; - - // Function of states to check if we are done - auto spin_done = [&](bool vel_override = false) -> bool { - bool done = false; - if (lockin_config.finish_on_vel || vel_override) - done = done || std::abs(vel) >= std::abs(lockin_config.vel); - if (lockin_config.finish_on_distance) - done = done || std::abs(distance) >= std::abs(lockin_config.finish_distance); - if (lockin_config.finish_on_enc_idx) - done = done || encoder_.index_found_; - return done; - }; - - // Accelerate - lockin_state_ = LOCKIN_STATE_ACCELERATE; - run_control_loop([&]() { - vel += lockin_config.accel * current_meas_period; - distance += vel * current_meas_period; - phase = wrap_pm_pi(phase + vel * current_meas_period); - - if (!motor_.update(lockin_config.current * motor_.config_.torque_constant, phase, vel)) - return false; - return !spin_done(true); //vel_override to go to next phase - }); +bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed, + std::function loop_cb) { + CRITICAL_SECTION() { + // Reset state variables + open_loop_controller_.Idq_setpoint_ = {0.0f, 0.0f}; + open_loop_controller_.Vdq_setpoint_ = {0.0f, 0.0f}; + open_loop_controller_.phase_ = 0.0f; + open_loop_controller_.phase_vel_ = 0.0f; + + open_loop_controller_.max_current_ramp_ = lockin_config.current / lockin_config.ramp_time; + open_loop_controller_.max_voltage_ramp_ = lockin_config.current / lockin_config.ramp_time; + open_loop_controller_.max_phase_vel_ramp_ = lockin_config.accel; + open_loop_controller_.target_current_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? lockin_config.current : 0.0f; + open_loop_controller_.target_voltage_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? 0.0f : lockin_config.current; + open_loop_controller_.target_vel_ = lockin_config.vel; + open_loop_controller_.total_distance_ = 0.0f; + + motor_.current_control_.enable_current_control_src_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL; + motor_.current_control_.Idq_setpoint_src_.connect_to(&open_loop_controller_.Idq_setpoint_); + motor_.current_control_.Vdq_setpoint_src_.connect_to(&open_loop_controller_.Vdq_setpoint_); + + motor_.current_control_.phase_src_.connect_to(&open_loop_controller_.phase_); + acim_estimator_.rotor_phase_src_.connect_to(&open_loop_controller_.phase_); + + motor_.phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_); + motor_.current_control_.phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_); + acim_estimator_.rotor_phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_); + } + wait_for_control_iteration(); - if (!encoder_.index_found_) - encoder_.set_idx_subscribe(true); + motor_.arm(&motor_.current_control_); - // Constant speed - if (!spin_done()) { - lockin_state_ = LOCKIN_STATE_CONST_VEL; - vel = lockin_config.vel; // reset to actual specified vel to avoid small integration error - run_control_loop([&]() { - distance += vel * current_meas_period; - phase = wrap_pm_pi(phase + vel * current_meas_period); + bool subscribed_to_idx_once = false; + bool success = false; + float dir = lockin_config.vel >= 0.0f ? 1.0f : -1.0f; - if (!motor_.update(lockin_config.current * motor_.config_.torque_constant, phase, vel)) - return false; - return !spin_done(); - }); + while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_) { + bool reached_target_vel = std::abs(open_loop_controller_.phase_vel_.any().value_or(0.0f) - lockin_config.vel) <= std::numeric_limits::epsilon(); + bool reached_target_dist = open_loop_controller_.total_distance_.any().value_or(0.0f) * dir >= lockin_config.finish_distance * dir; + + // Check if terminal condition is reached + bool terminal_condition = (reached_target_vel && lockin_config.finish_on_vel) + || (reached_target_dist && lockin_config.finish_on_distance) + || (encoder_.index_found_ && lockin_config.finish_on_enc_idx); + if (terminal_condition) { + success = true; + break; + } + + // Activate index pin as soon as target velocity was reached. This is + // to avoid hitting the index from the wrong direction. + if (reached_target_vel && !encoder_.index_found_ && !subscribed_to_idx_once) { + encoder_.set_idx_subscribe(true); + subscribed_to_idx_once = true; + } + + if (loop_cb) + if (!loop_cb(reached_target_vel)) + break; + + // TODO: use new sync function instead + asm volatile ("" ::: "memory"); + osDelay(1); } - lockin_state_ = LOCKIN_STATE_INACTIVE; - return check_for_errors(); -} + if (!success || !remain_armed) { + motor_.disarm(); + } -// Note run_sensorless_control_loop and run_closed_loop_control_loop are very similar and differ only in where we get the estimate from. -bool Axis::run_sensorless_control_loop() { - controller_.pos_estimate_linear_src_ = nullptr; - controller_.pos_estimate_circular_src_ = nullptr; - controller_.pos_estimate_valid_src_ = nullptr; - controller_.vel_estimate_src_ = &sensorless_estimator_.vel_estimate_; - controller_.vel_estimate_valid_src_ = &sensorless_estimator_.vel_estimate_valid_; - - run_control_loop([this](){ - // Note that all estimators are updated in the loop prefix in run_control_loop - float torque_setpoint; - if (!controller_.update(&torque_setpoint)) - return error_ |= ERROR_CONTROLLER_FAILED, false; - if (!motor_.update(torque_setpoint, sensorless_estimator_.phase_, sensorless_estimator_.vel_estimate_)) - return false; // set_error should update axis.error_ - return true; - }); - return check_for_errors(); + return success; } -bool Axis::run_closed_loop_control_loop() { - if (!controller_.select_encoder(controller_.config_.load_encoder_axis)) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - // To avoid any transient on startup, we intialize the setpoint to be the current position - if (controller_.config_.circular_setpoints) { - if (!controller_.pos_estimate_circular_src_) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - else { - controller_.pos_setpoint_ = *controller_.pos_estimate_circular_src_; - controller_.input_pos_ = *controller_.pos_estimate_circular_src_; +bool Axis::start_closed_loop_control() { + bool sensorless_mode = config_.enable_sensorless_mode; + + if (sensorless_mode) { + // TODO: restart if desired + if (!run_lockin_spin(config_.sensorless_ramp, true)) { + return false; } } - else { - if (!controller_.pos_estimate_linear_src_) { - return error_ |= ERROR_CONTROLLER_FAILED, false; + + // Hook up the data paths between the components + CRITICAL_SECTION() { + if (sensorless_mode) { + controller_.pos_estimate_linear_src_.disconnect(); + controller_.pos_estimate_circular_src_.disconnect(); + controller_.pos_wrap_src_.disconnect(); + controller_.vel_estimate_src_.connect_to(&sensorless_estimator_.vel_estimate_); + } else if (controller_.config_.load_encoder_axis < AXIS_COUNT) { + Axis* ax = &axes[controller_.config_.load_encoder_axis]; + controller_.pos_estimate_circular_src_.connect_to(&ax->encoder_.pos_circular_); + controller_.pos_wrap_src_.connect_to(&controller_.config_.circular_setpoint_range); + controller_.pos_estimate_linear_src_.connect_to(&ax->encoder_.pos_estimate_); + controller_.vel_estimate_src_.connect_to(&ax->encoder_.vel_estimate_); + } else { + controller_.pos_estimate_circular_src_.disconnect(); + controller_.pos_estimate_linear_src_.disconnect(); + controller_.pos_wrap_src_.disconnect(); + controller_.vel_estimate_src_.disconnect(); + controller_.set_error(Controller::ERROR_INVALID_LOAD_ENCODER); + return false; } - else { - controller_.pos_setpoint_ = *controller_.pos_estimate_linear_src_; - controller_.input_pos_ = *controller_.pos_estimate_linear_src_; + + // To avoid any transient on startup, we intialize the setpoint to be the current position + // note - input_pos_ is not set here. It is set to 0 earlier in this method and velocity control is used. + if (controller_.config_.control_mode >= Controller::CONTROL_MODE_POSITION_CONTROL) { + std::optional pos_init = (controller_.config_.circular_setpoints ? + controller_.pos_estimate_circular_src_ : + controller_.pos_estimate_linear_src_).any(); + if (!pos_init.has_value()) { + return false; + } else { + controller_.pos_setpoint_ = *pos_init; + controller_.input_pos_ = *pos_init; + float range = controller_.config_.circular_setpoint_range; + steps_ = (int64_t)(fmodf_pos(*pos_init, range) / range * controller_.config_.steps_per_circular_range); + } + } + controller_.input_pos_updated(); + + // Avoid integrator windup issues + controller_.vel_integrator_torque_ = 0.0f; + + motor_.torque_setpoint_src_.connect_to(&controller_.torque_output_); + motor_.direction_ = sensorless_mode ? 1.0f : encoder_.config_.direction; + + motor_.current_control_.enable_current_control_src_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL; + motor_.current_control_.Idq_setpoint_src_.connect_to(&motor_.Idq_setpoint_); + motor_.current_control_.Vdq_setpoint_src_.connect_to(&motor_.Vdq_setpoint_); + + bool is_acim = motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM; + // phase + OutputPort* phase_src = sensorless_mode ? &sensorless_estimator_.phase_ : &encoder_.phase_; + acim_estimator_.rotor_phase_src_.connect_to(phase_src); + OutputPort* stator_phase_src = is_acim ? &acim_estimator_.stator_phase_ : phase_src; + motor_.current_control_.phase_src_.connect_to(stator_phase_src); + // phase vel + OutputPort* phase_vel_src = sensorless_mode ? &sensorless_estimator_.phase_vel_ : &encoder_.phase_vel_; + acim_estimator_.rotor_phase_vel_src_.connect_to(phase_vel_src); + OutputPort* stator_phase_vel_src = is_acim ? &acim_estimator_.stator_phase_vel_ : phase_vel_src; + motor_.phase_vel_src_.connect_to(stator_phase_vel_src); + motor_.current_control_.phase_vel_src_.connect_to(stator_phase_vel_src); + + if (sensorless_mode) { + // Make the final velocity of the loĉk-in spin the setpoint of the + // closed loop controller to allow for smooth transition. + float vel = config_.sensorless_ramp.vel / (2.0f * M_PI * motor_.config_.pole_pairs); + controller_.input_vel_ = vel; + controller_.vel_setpoint_ = vel; } } - controller_.input_pos_updated(); - // Avoid integrator windup issues - controller_.vel_integrator_torque_ = 0.0f; + // In sensorless mode the motor is already armed. + if (!motor_.is_armed_) { + wait_for_control_iteration(); + motor_.arm(&motor_.current_control_); + } + + return true; +} + +bool Axis::stop_closed_loop_control() { + motor_.disarm(); + return check_for_errors(); +} +bool Axis::run_closed_loop_control_loop() { + start_closed_loop_control(); set_step_dir_active(config_.enable_step_dir); - run_control_loop([this](){ - // Note that all estimators are updated in the loop prefix in run_control_loop - float torque_setpoint; - if (!controller_.update(&torque_setpoint)) - return error_ |= ERROR_CONTROLLER_FAILED, false; - float phase_vel = (2*M_PI) * encoder_.vel_estimate_ * motor_.config_.pole_pairs; - if (!motor_.update(torque_setpoint, encoder_.phase_, phase_vel)) - return false; // set_error should update axis.error_ + while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_) { + osDelay(1); + } - return true; - }); set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on); + stop_closed_loop_control(); + return check_for_errors(); } @@ -380,73 +386,45 @@ bool Axis::run_homing() { homing_.is_homed = false; - if (!controller_.select_encoder(controller_.config_.load_encoder_axis)) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - - // To avoid any transient on startup, we intialize the setpoint to be the current position - // note - input_pos_ is not set here. It is set to 0 earlier in this method and velocity control is used. - if (controller_.config_.circular_setpoints) { - if (!controller_.pos_estimate_circular_src_) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - else { - controller_.pos_setpoint_ = *controller_.pos_estimate_circular_src_; - } - } - else { - if (!controller_.pos_estimate_linear_src_) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - else { - controller_.pos_setpoint_ = *controller_.pos_estimate_linear_src_; - } - } - - // Avoid integrator windup issues - controller_.vel_integrator_torque_ = 0.0f; + start_closed_loop_control(); - run_control_loop([this](){ - // Note that all estimators are updated in the loop prefix in run_control_loop - float torque_setpoint; - if (!controller_.update(&torque_setpoint)) - return error_ |= ERROR_CONTROLLER_FAILED, false; + // Driving toward the endstop + while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_ && !min_endstop_.get_state()) { + osDelay(1); + } - float phase_vel = (2*M_PI) * encoder_.vel_estimate_ * motor_.config_.pole_pairs; - if (!motor_.update(torque_setpoint, encoder_.phase_, phase_vel)) - return false; // set_error should update axis.error_ + stop_closed_loop_control(); + + controller_.input_vel_ = 0.0f; - return !min_endstop_.get_state(); - }); error_ &= ~ERROR_MIN_ENDSTOP_PRESSED; // clear this error since we deliberately drove into the endstop - // pos_setpoint is the starting position for the trap_traj so we need to set it. - controller_.pos_setpoint_ = min_endstop_.config_.offset; - controller_.vel_setpoint_ = 0.0f; // Change directions without decelerating - - // Set our current position in encoder counts to make control more logical - encoder_.set_linear_count((int32_t)(controller_.pos_setpoint_ * encoder_.config_.cpr)); - + std::optional pos_estimate_local = encoder_.pos_estimate_.any(); + if (pos_estimate_local == std::nullopt || !pos_estimate_local.has_value()){ + return error_ |= ERROR_UNKNOWN_POSITION, false; + } + controller_.config_.control_mode = Controller::CONTROL_MODE_POSITION_CONTROL; controller_.config_.input_mode = Controller::INPUT_MODE_TRAP_TRAJ; - controller_.input_pos_ = 0.0f; + // Initialize closed loop control, and then set the desired location. + start_closed_loop_control(); + + controller_.input_pos_ = pos_estimate_local.value() + min_endstop_.config_.offset; + controller_.pos_setpoint_ = pos_estimate_local.value(); + controller_.vel_setpoint_ = 0.0f; controller_.input_pos_updated(); - controller_.input_vel_ = 0.0f; - controller_.input_torque_ = 0.0f; - - run_control_loop([this](){ - // Note that all estimators are updated in the loop prefix in run_control_loop - float torque_setpoint; - if (!controller_.update(&torque_setpoint)) - return error_ |= ERROR_CONTROLLER_FAILED, false; + + while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_ && !controller_.trajectory_done_) { + osDelay(1); + } - float phase_vel = (2*M_PI) * encoder_.vel_estimate_ * motor_.config_.pole_pairs; - if (!motor_.update(torque_setpoint, encoder_.phase_, phase_vel)) - return false; // set_error should update axis.error_ + stop_closed_loop_control(); - return !controller_.trajectory_done_; - }); + // Set the current position to 0. + encoder_.set_linear_count(0); + controller_.input_pos_ = 0; + controller_.input_pos_updated(); controller_.config_.control_mode = stored_control_mode; controller_.config_.input_mode = stored_input_mode; @@ -456,22 +434,18 @@ bool Axis::run_homing() { } bool Axis::run_idle_loop() { - // run_control_loop ignores missed modulation timing updates - // if and only if we're in AXIS_STATE_IDLE - safety_critical_disarm_motor_pwm(motor_); + last_drv_fault_ = motor_.gate_driver_.get_error(); + mechanical_brake_.engage(); set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on); - run_control_loop([this]() { - return true; - }); + while (requested_state_ == AXIS_STATE_UNDEFINED) { + motor_.setup(); + osDelay(1); + } return check_for_errors(); } // Infinite loop that does calibration and enters main control loop as appropriate void Axis::run_state_machine_loop() { - - // arm! - motor_.arm(); - for (;;) { // Load the task chain if a specific request is pending if (requested_state_ != AXIS_STATE_UNDEFINED) { @@ -487,11 +461,11 @@ void Axis::run_state_machine_loop() { task_chain_[pos++] = AXIS_STATE_HOMING; if (config_.startup_closed_loop_control) task_chain_[pos++] = AXIS_STATE_CLOSED_LOOP_CONTROL; - else if (config_.startup_sensorless_control) - task_chain_[pos++] = AXIS_STATE_SENSORLESS_CONTROL; task_chain_[pos++] = AXIS_STATE_IDLE; } else if (requested_state_ == AXIS_STATE_FULL_CALIBRATION_SEQUENCE) { task_chain_[pos++] = AXIS_STATE_MOTOR_CALIBRATION; + if (encoder_.config_.mode == ODriveIntf::EncoderIntf::MODE_HALL) + task_chain_[pos++] = AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION; if (encoder_.config_.use_index) task_chain_[pos++] = AXIS_STATE_ENCODER_INDEX_SEARCH; task_chain_[pos++] = AXIS_STATE_ENCODER_OFFSET_CALIBRATION; @@ -513,57 +487,81 @@ void Axis::run_state_machine_loop() { bool status; switch (current_state_) { case AXIS_STATE_MOTOR_CALIBRATION: { + // These error checks are a hacky way to force legacy behavior + // when an error is raised. TODO: remove this when we overhaul + // the error architecture + // (https://github.com/madcowswe/ODrive/issues/526). + //if (odrv.any_error()) + // goto invalid_state_label; status = motor_.run_calibration(); } break; case AXIS_STATE_ENCODER_INDEX_SEARCH: { + //if (odrv.any_error()) + // goto invalid_state_label; if (!motor_.is_calibrated_) goto invalid_state_label; - if (encoder_.config_.idx_search_unidirectional && motor_.config_.direction==0) - goto invalid_state_label; status = encoder_.run_index_search(); } break; case AXIS_STATE_ENCODER_DIR_FIND: { + //if (odrv.any_error()) + // goto invalid_state_label; if (!motor_.is_calibrated_) goto invalid_state_label; status = encoder_.run_direction_find(); + // Help facilitate encoder.is_ready without reboot + if (status) + encoder_.apply_config(motor_.config_.motor_type); + } break; + + case AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION: { + if (!motor_.is_calibrated_) + goto invalid_state_label; + + status = encoder_.run_hall_polarity_calibration(); + } break; + + case AXIS_STATE_ENCODER_HALL_PHASE_CALIBRATION: { + if (!motor_.is_calibrated_) + goto invalid_state_label; + + if (!encoder_.config_.hall_polarity_calibrated) { + encoder_.set_error(ODriveIntf::EncoderIntf::ERROR_HALL_NOT_CALIBRATED_YET); + goto invalid_state_label; + } + + status = encoder_.run_hall_phase_calibration(); } break; case AXIS_STATE_HOMING: { + //if (odrv.any_error()) + // goto invalid_state_label; status = run_homing(); } break; case AXIS_STATE_ENCODER_OFFSET_CALIBRATION: { + //if (odrv.any_error()) + // goto invalid_state_label; if (!motor_.is_calibrated_) goto invalid_state_label; status = encoder_.run_offset_calibration(); } break; case AXIS_STATE_LOCKIN_SPIN: { - if (!motor_.is_calibrated_ || motor_.config_.direction==0) + //if (odrv.any_error()) + // goto invalid_state_label; + if (!motor_.is_calibrated_ || encoder_.config_.direction==0) goto invalid_state_label; - status = run_lockin_spin(config_.general_lockin); - } break; - - case AXIS_STATE_SENSORLESS_CONTROL: { - if (!motor_.is_calibrated_ || motor_.config_.direction==0) - goto invalid_state_label; - status = run_lockin_spin(config_.sensorless_ramp); // TODO: restart if desired - if (status) { - // call to controller.reset() that happend when arming means that vel_setpoint - // is zeroed. So we make the setpoint the spinup target for smooth transition. - controller_.vel_setpoint_ = config_.sensorless_ramp.vel / (2.0f * M_PI * motor_.config_.pole_pairs); - status = run_sensorless_control_loop(); - } + status = run_lockin_spin(config_.general_lockin, false); } break; case AXIS_STATE_CLOSED_LOOP_CONTROL: { - if (!motor_.is_calibrated_ || motor_.config_.direction==0) - goto invalid_state_label; - if (!encoder_.is_ready_) + //if (odrv.any_error()) + // goto invalid_state_label; + if (!motor_.is_calibrated_ || (encoder_.config_.direction==0 && !config_.enable_sensorless_mode)) goto invalid_state_label; watchdog_feed(); status = run_closed_loop_control_loop(); @@ -571,7 +569,7 @@ void Axis::run_state_machine_loop() { case AXIS_STATE_IDLE: { run_idle_loop(); - status = motor_.arm(); // done with idling - try to arm the motor + status = true; } break; default: diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 446958d63..14f05a63c 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -1,9 +1,19 @@ #ifndef __AXIS_HPP #define __AXIS_HPP -#ifndef __ODRIVE_MAIN_H -#error "This file should not be included directly. Include odrive_main.h instead." -#endif +class Axis; + +#include "encoder.hpp" +#include "acim_estimator.hpp" +#include "sensorless_estimator.hpp" +#include "controller.hpp" +#include "open_loop_controller.hpp" +#include "trapTraj.hpp" +#include "endstop.hpp" +#include "mechanical_brake.hpp" +#include "low_level.h" +#include "utils.hpp" +#include "task_timer.hpp" #include @@ -21,17 +31,39 @@ class Axis : public ODriveIntf::AxisIntf { bool finish_on_enc_idx = false; }; + struct TaskTimes { + TaskTimer thermistor_update; + TaskTimer encoder_update; + TaskTimer sensorless_estimator_update; + TaskTimer endstop_update; + TaskTimer can_heartbeat; + TaskTimer controller_update; + TaskTimer open_loop_controller_update; + TaskTimer acim_estimator_update; + TaskTimer motor_update; + TaskTimer current_controller_update; + TaskTimer dc_calib; + TaskTimer current_sense; + TaskTimer pwm_update; + }; + static LockinConfig_t default_calibration(); static LockinConfig_t default_sensorless(); static LockinConfig_t default_lockin(); + struct CANConfig_t { + uint32_t node_id = 0; + bool is_extended = false; + uint32_t heartbeat_rate_ms = 100; + uint32_t encoder_rate_ms = 10; + }; + struct Config_t { bool startup_motor_calibration = false; // - void run_control_loop(const T& update_handler) { - while (requested_state_ == AXIS_STATE_UNDEFINED) { - // look for errors at axis level and also all subcomponents - bool checks_ok = do_checks(); - // Update all estimators - // Note: updates run even if checks fail - bool updates_ok = do_updates(); - - // make sure the watchdog is being fed. - bool watchdog_ok = watchdog_check(); - - if (!checks_ok || !updates_ok || !watchdog_ok) { - // It's not useful to quit idle since that is the safe action - // Also leaving idle would rearm the motors - if (current_state_ != AXIS_STATE_IDLE) - break; - } - - // Run main loop function, defer quitting for after wait - // TODO: change arming logic to arm after waiting - bool main_continue = update_handler(); - - // Check we meet deadlines after queueing - ++loop_counter_; - - // Wait until the current measurement interrupt fires - if (!wait_for_current_meas()) { - // maybe the interrupt handler is dead, let's be - // safe and float the phases - safety_critical_disarm_motor_pwm(motor_); - update_brake_current(); - error_ |= ERROR_CURRENT_MEASUREMENT_TIMEOUT; - break; - } - - if (!main_continue) - break; - } - } - - bool run_lockin_spin(const LockinConfig_t &lockin_config); - bool run_sensorless_control_loop(); + bool start_closed_loop_control(); + bool stop_closed_loop_control(); + bool run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed, + std::function loop_cb = {} ); bool run_closed_loop_control_loop(); bool run_homing(); bool run_idle_loop(); @@ -193,46 +150,45 @@ class Axis : public ODriveIntf::AxisIntf { void run_state_machine_loop(); + // hardware config int axis_num_; - const AxisHardwareConfig_t& hw_config_; - Config_t& config_; + uint16_t default_step_gpio_pin_; + uint16_t default_dir_gpio_pin_; + osPriority thread_priority_; + Config_t config_; Encoder& encoder_; + AcimEstimator acim_estimator_; SensorlessEstimator& sensorless_estimator_; Controller& controller_; - OnboardThermistorCurrentLimiter& fet_thermistor_; - OffboardThermistorCurrentLimiter& motor_thermistor_; + OpenLoopController open_loop_controller_; Motor& motor_; TrapezoidalTrajectory& trap_traj_; Endstop& min_endstop_; Endstop& max_endstop_; + MechanicalBrake& mechanical_brake_; + TaskTimes task_times_; - // List of current_limiters and thermistors to - // provide easy iteration. - std::array current_limiters_; - std::array thermistors_; - - osThreadId thread_id_; + osThreadId thread_id_ = 0; const uint32_t stack_size_ = 2048; // Bytes volatile bool thread_id_valid_ = false; // variables exposed on protocol Error error_ = ERROR_NONE; bool step_dir_active_ = false; // auto enabled after calibration, based on config.enable_step_dir + int64_t steps_ = 0; // Steps counted at interface + uint32_t last_drv_fault_ = 0; // updated from config in constructor, and on protocol hook - GPIO_TypeDef* step_port_; - uint16_t step_pin_; - GPIO_TypeDef* dir_port_; - uint16_t dir_pin_; + Stm32Gpio step_gpio_; + Stm32Gpio dir_gpio_; AxisState requested_state_ = AXIS_STATE_STARTUP_SEQUENCE; std::array task_chain_ = { AXIS_STATE_UNDEFINED }; AxisState& current_state_ = task_chain_.front(); - uint32_t loop_counter_ = 0; - LockinState lockin_state_ = LOCKIN_STATE_INACTIVE; Homing_t homing_; - uint32_t last_heartbeat_ = 0; + CAN_t can_; + // watchdog uint32_t watchdog_current_value_= 0; diff --git a/Firmware/MotorControl/board_config_v3.h b/Firmware/MotorControl/board_config_v3.h deleted file mode 100644 index 64cbdc78a..000000000 --- a/Firmware/MotorControl/board_config_v3.h +++ /dev/null @@ -1,175 +0,0 @@ -/* -* @brief Contains board specific configuration for ODrive v3.x -*/ - -#ifndef __BOARD_CONFIG_H -#define __BOARD_CONFIG_H - -// STM specific includes -#include -#include -#include -#include - -#if HW_VERSION_MAJOR == 3 -#if HW_VERSION_MINOR <= 3 -#define SHUNT_RESISTANCE (675e-6f) -#else -#define SHUNT_RESISTANCE (500e-6f) -#endif -#endif - - -typedef struct { - uint16_t step_gpio_pin; - uint16_t dir_gpio_pin; - osPriority thread_priority; -} AxisHardwareConfig_t; - -typedef struct { - TIM_HandleTypeDef* timer; - GPIO_TypeDef* index_port; - uint16_t index_pin; - GPIO_TypeDef* hallA_port; - uint16_t hallA_pin; - GPIO_TypeDef* hallB_port; - uint16_t hallB_pin; - GPIO_TypeDef* hallC_port; - uint16_t hallC_pin; - SPI_HandleTypeDef* spi; -} EncoderHardwareConfig_t; -typedef struct { - TIM_HandleTypeDef* timer; - uint16_t control_deadline; - float shunt_conductance; -} MotorHardwareConfig_t; -typedef struct { - const float* const coeffs; - size_t num_coeffs; - size_t adc_ch; -} ThermistorHardwareConfig_t; -typedef struct { - SPI_HandleTypeDef* spi; - GPIO_TypeDef* enable_port; - uint16_t enable_pin; - GPIO_TypeDef* nCS_port; - uint16_t nCS_pin; - GPIO_TypeDef* nFAULT_port; - uint16_t nFAULT_pin; -} GateDriverHardwareConfig_t; -typedef struct { - AxisHardwareConfig_t axis_config; - EncoderHardwareConfig_t encoder_config; - MotorHardwareConfig_t motor_config; - ThermistorHardwareConfig_t thermistor_config; - GateDriverHardwareConfig_t gate_driver_config; -} BoardHardwareConfig_t; - -extern const BoardHardwareConfig_t hw_configs[2]; - -//TODO stick this in a C file -#ifdef __MAIN_CPP__ -const float fet_thermistor_poly_coeffs[] = - {363.93910201f, -462.15369634f, 307.55129571f, -27.72569531f}; -const size_t fet_thermistor_num_coeffs = sizeof(fet_thermistor_poly_coeffs)/sizeof(fet_thermistor_poly_coeffs[1]); - -const BoardHardwareConfig_t hw_configs[2] = { { - //M0 - .axis_config = { - .step_gpio_pin = 1, - .dir_gpio_pin = 2, - .thread_priority = (osPriority)(osPriorityHigh + (osPriority)1), - }, - .encoder_config = { - .timer = &htim3, - .index_port = M0_ENC_Z_GPIO_Port, - .index_pin = M0_ENC_Z_Pin, - .hallA_port = M0_ENC_A_GPIO_Port, - .hallA_pin = M0_ENC_A_Pin, - .hallB_port = M0_ENC_B_GPIO_Port, - .hallB_pin = M0_ENC_B_Pin, - .hallC_port = M0_ENC_Z_GPIO_Port, - .hallC_pin = M0_ENC_Z_Pin, - .spi = &hspi3, - }, - .motor_config = { - .timer = &htim1, - .control_deadline = TIM_1_8_PERIOD_CLOCKS, - .shunt_conductance = 1.0f / SHUNT_RESISTANCE, //[S] - }, - .thermistor_config = { - .coeffs = &fet_thermistor_poly_coeffs[0], - .num_coeffs = fet_thermistor_num_coeffs, - .adc_ch = 15, - }, - .gate_driver_config = { - .spi = &hspi3, - // Note: this board has the EN_Gate pin shared! - .enable_port = EN_GATE_GPIO_Port, - .enable_pin = EN_GATE_Pin, - .nCS_port = M0_nCS_GPIO_Port, - .nCS_pin = M0_nCS_Pin, - .nFAULT_port = nFAULT_GPIO_Port, // the nFAULT pin is shared between both motors - .nFAULT_pin = nFAULT_Pin, - } -},{ - //M1 - .axis_config = { -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 5 - .step_gpio_pin = 7, - .dir_gpio_pin = 8, -#else - .step_gpio_pin = 3, - .dir_gpio_pin = 4, -#endif - .thread_priority = osPriorityHigh, - }, - .encoder_config = { - .timer = &htim4, - .index_port = M1_ENC_Z_GPIO_Port, - .index_pin = M1_ENC_Z_Pin, - .hallA_port = M1_ENC_A_GPIO_Port, - .hallA_pin = M1_ENC_A_Pin, - .hallB_port = M1_ENC_B_GPIO_Port, - .hallB_pin = M1_ENC_B_Pin, - .hallC_port = M1_ENC_Z_GPIO_Port, - .hallC_pin = M1_ENC_Z_Pin, - .spi = &hspi3, - }, - .motor_config = { - .timer = &htim8, - .control_deadline = (3 * TIM_1_8_PERIOD_CLOCKS) / 2, - .shunt_conductance = 1.0f / SHUNT_RESISTANCE, //[S] - }, - .thermistor_config = { - .coeffs = &fet_thermistor_poly_coeffs[0], - .num_coeffs = fet_thermistor_num_coeffs, -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 - .adc_ch = 4, -#else - .adc_ch = 1, -#endif - }, - .gate_driver_config = { - .spi = &hspi3, - // Note: this board has the EN_Gate pin shared! - .enable_port = EN_GATE_GPIO_Port, - .enable_pin = EN_GATE_Pin, - .nCS_port = M1_nCS_GPIO_Port, - .nCS_pin = M1_nCS_Pin, - .nFAULT_port = nFAULT_GPIO_Port, // the nFAULT pin is shared between both motors - .nFAULT_pin = nFAULT_Pin, - } -} }; -#endif - - - -#define I2C_A0_PORT GPIO_3_GPIO_Port -#define I2C_A0_PIN GPIO_3_Pin -#define I2C_A1_PORT GPIO_4_GPIO_Port -#define I2C_A1_PIN GPIO_4_Pin -#define I2C_A2_PORT GPIO_5_GPIO_Port -#define I2C_A2_PIN GPIO_5_Pin - -#endif // __BOARD_CONFIG_H diff --git a/Firmware/MotorControl/component.hpp b/Firmware/MotorControl/component.hpp new file mode 100644 index 000000000..4569de99b --- /dev/null +++ b/Firmware/MotorControl/component.hpp @@ -0,0 +1,182 @@ +#ifndef __COMPONENT_HPP +#define __COMPONENT_HPP + +#include +#include +#include + +class ComponentBase { +public: + /** + * @brief Shall run the update action of this component. + * + * This function gets called in a low priority interrupt context and is + * allowed to call CMSIS functions. + * + * @param timestamp: The timestamp (in HCLK ticks) for which this update + * is run. + */ + virtual void update(uint32_t timestamp) = 0; +}; + + +template +class InputPort; + +/** + * @brief An output port stores a value for consumption by a connecting input + * port. + * + * Output ports are supposed to be reset at the beginning of a control loop + * iteration. This ensures that connecting input ports don't use an outdated + * value and, more importantly, ensures proper handling if the producer of the + * value is incapable of producing the value for any reason. + * + * Member functions of this class are not thread-safe unless noted otherwise. + */ +template +class OutputPort { +public: + /** + * @brief Initializes the output port with the specified value. + * + * An initialization value is required for any() to work properly. + * present() and previous() cannot be used to fetch the + * initialization value. + */ + OutputPort(T val) : content_(val) {} + + /** + * @brief Updates the underlying value of this output port. + */ + void operator=(T value) { + content_ = value; + age_ = 0; + } + + /** + * @brief Marks the contained value as outdated. The value is not actually + * deleted and can still be accessed through some of the member functions + * of this class. + */ + void reset() { + // This will eventually overflow to 0 so present() could + // theoretically return a very old value however it is very likely that + // the motor will be long disarmed by then. + age_++; + } + + /** + * @brief Returns the value from this control loop iteration or std::nullopt + * if the value was not yet set during this control loop iteration. + */ + std::optional present() { + if (age_ == 0) { + return content_; + } else { + return std::nullopt; + } + } + + /** + * @brief Returns the value from exactly the previous control loop iteration. + * + * If during the last iteration no value was set or the value was already + * overwritten during this control loop iteration then this function returns + * std::nullopt. + */ + std::optional previous() { + if (age_ == 1) { + return content_; + } else { + return std::nullopt; + } + } + + /** + * @brief Returns the value contained in this output port with disregard of + * when the value was set. + * + * This function is thread-safe if load/store operations of T are atomic. + */ + std::optional any() { + return content_; + } + +private: + uint32_t age_ = 2; // Age in number of control loop iterations + T content_; +}; + +/** + * @brief An input port provides a value from the source to which it's configured. + * + * The source can be one of: + * - an internally stored value + * - an externally stored value (referenced by a pointer) + * - an external OutputPort (referenced by a pointer) + * - none (all queries will return std::nullopt) + * + * Member functions of this class are not thread-safe unless otherwise noted. + */ +template +class InputPort { +public: + void connect_to(OutputPort* input_port) { + content_ = input_port; + } + + void connect_to(T* input_ptr) { + content_ = input_ptr; + } + + void disconnect() { + content_ = (OutputPort*)nullptr; + } + + std::optional present() { + if (content_.index() == 2) { + OutputPort* ptr = std::get<2>(content_); + return ptr ? ptr->present() : std::nullopt; + } else if (content_.index() == 1) { + T* ptr = std::get<1>(content_); + return ptr ? std::make_optional(*ptr) : std::nullopt; + } else { + return std::get<0>(content_); + } + } + + // TODO: probably it makes sense to let the application define that it's + // ok for this input port to fetch the value from the last iteration. + // This would provide a general way to resolve same-iteration data path cycles. + + //std::optional previous() { + // if (content_.index() == 2) { + // OutputPort* ptr = std::get<2>(content_); + // return ptr ? ptr->previous() : std::nullopt; + // } else if (content_.index() == 1) { + // T* ptr = std::get<1>(content_); + // return ptr ? std::make_optional(*ptr) : std::nullopt; + // } else { + // return std::get<0>(content_); + // } + //} + + std::optional any() { + if (content_.index() == 2) { + OutputPort* ptr = std::get<2>(content_); + return ptr ? ptr->any() : std::nullopt; + } else if (content_.index() == 1) { + T* ptr = std::get<1>(content_); + return ptr ? std::make_optional(*ptr) : std::nullopt; + } else { + return std::get<0>(content_); + } + } + +private: + std::variant*> content_; +}; + + +#endif // __COMPONENT_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index ef87376a8..c94fdfa07 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -2,12 +2,10 @@ #include "odrive_main.h" #include -#include - -Controller::Controller(Config_t& config) : - config_(config) -{ +bool Controller::apply_config() { + config_.parent = this; update_filter_gains(); + return true; } void Controller::reset() { @@ -15,11 +13,13 @@ void Controller::reset() { vel_setpoint_ = 0.0f; vel_integrator_torque_ = 0.0f; torque_setpoint_ = 0.0f; + mechanical_power_ = 0.0f; + electrical_power_ = 0.0f; } void Controller::set_error(Error error) { error_ |= error; - axis_->error_ |= Axis::ERROR_CONTROLLER_FAILED; + last_error_time_ = odrv.n_evt_control_loop_ * current_meas_period; } //-------------------------------- @@ -27,21 +27,6 @@ void Controller::set_error(Error error) { //-------------------------------- -bool Controller::select_encoder(size_t encoder_num) { - if (encoder_num < AXIS_COUNT) { - Axis* ax = axes[encoder_num]; - pos_estimate_circular_src_ = &ax->encoder_.pos_circular_; - pos_wrap_src_ = &config_.circular_setpoint_range; - pos_estimate_linear_src_ = &ax->encoder_.pos_estimate_; - pos_estimate_valid_src_ = &ax->encoder_.pos_estimate_valid_; - vel_estimate_src_ = &ax->encoder_.vel_estimate_; - vel_estimate_valid_src_ = &ax->encoder_.vel_estimate_valid_; - return true; - } else { - return set_error(Controller::ERROR_INVALID_LOAD_ENCODER), false; - } -} - void Controller::move_to_pos(float goal_point) { axis_->trap_traj_.planTrapezoidal(goal_point, pos_setpoint_, vel_setpoint_, axis_->trap_traj_.config_.vel_limit, @@ -114,29 +99,39 @@ static float limitVel(const float vel_limit, const float vel_estimate, const flo return std::clamp(torque, Tmin, Tmax); } -bool Controller::update(float* torque_setpoint_output) { - float* pos_estimate_linear = (pos_estimate_valid_src_ && *pos_estimate_valid_src_) - ? pos_estimate_linear_src_ : nullptr; - float* pos_estimate_circular = (pos_estimate_valid_src_ && *pos_estimate_valid_src_) - ? pos_estimate_circular_src_ : nullptr; - float* vel_estimate_src = (vel_estimate_valid_src_ && *vel_estimate_valid_src_) - ? vel_estimate_src_ : nullptr; +bool Controller::update() { + std::optional pos_estimate_linear = pos_estimate_linear_src_.present(); + std::optional pos_estimate_circular = pos_estimate_circular_src_.present(); + std::optional pos_wrap = pos_wrap_src_.present(); + std::optional vel_estimate = vel_estimate_src_.present(); + + std::optional anticogging_pos_estimate = axis_->encoder_.pos_estimate_.present(); + std::optional anticogging_vel_estimate = axis_->encoder_.vel_estimate_.present(); + + if (axis_->step_dir_active_) { + if (!pos_wrap.has_value()) { + set_error(ERROR_INVALID_CIRCULAR_RANGE); + return false; + } + input_pos_ = (float)(axis_->steps_ % config_.steps_per_circular_range) * (*pos_wrap / (float)(config_.steps_per_circular_range)); + } - // Calib_anticogging is only true when calibration is occurring, so we can't block anticogging_pos - float anticogging_pos = axis_->encoder_.pos_estimate_ / axis_->encoder_.getCoggingRatio(); if (config_.anticogging.calib_anticogging) { - if (!axis_->encoder_.pos_estimate_valid_ || !axis_->encoder_.vel_estimate_valid_) { + if (!anticogging_pos_estimate.has_value() || !anticogging_vel_estimate.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } // non-blocking - anticogging_calibration(axis_->encoder_.pos_estimate_, axis_->encoder_.vel_estimate_); + anticogging_calibration(*anticogging_pos_estimate, *anticogging_vel_estimate); } // TODO also enable circular deltas for 2nd order filter, etc. if (config_.circular_setpoints) { - // Keep pos setpoint from drifting - input_pos_ = fmodf_pos(input_pos_, config_.circular_setpoint_range); + if (!pos_wrap.has_value()) { + set_error(ERROR_INVALID_CIRCULAR_RANGE); + return false; + } + input_pos_ = fmodf_pos(input_pos_, *pos_wrap); } // Update inputs @@ -167,6 +162,13 @@ bool Controller::update(float* torque_setpoint_output) { case INPUT_MODE_POS_FILTER: { // 2nd order pos tracking filter float delta_pos = input_pos_ - pos_setpoint_; // Pos error + if (config_.circular_setpoints) { + if (!pos_wrap.has_value()) { + set_error(ERROR_INVALID_CIRCULAR_RANGE); + return false; + } + delta_pos = wrap_pm(delta_pos, *pos_wrap); + } float delta_vel = input_vel_ - vel_setpoint_; // Vel error float accel = input_filter_kp_*delta_pos + input_filter_ki_*delta_vel; // Feedback torque_setpoint_ = accel * config_.inertia; // Accel @@ -175,8 +177,18 @@ bool Controller::update(float* torque_setpoint_output) { } break; case INPUT_MODE_MIRROR: { if (config_.axis_to_mirror < AXIS_COUNT) { - pos_setpoint_ = axes[config_.axis_to_mirror]->encoder_.pos_estimate_ * config_.mirror_ratio; - vel_setpoint_ = axes[config_.axis_to_mirror]->encoder_.vel_estimate_ * config_.mirror_ratio; + std::optional other_pos = axes[config_.axis_to_mirror].encoder_.pos_estimate_.present(); + std::optional other_vel = axes[config_.axis_to_mirror].encoder_.vel_estimate_.present(); + std::optional other_torque = axes[config_.axis_to_mirror].controller_.torque_output_.present(); + + if (!other_pos.has_value() || !other_vel.has_value() || !other_torque.has_value()) { + set_error(ERROR_INVALID_ESTIMATE); + return false; + } + + pos_setpoint_ = *other_pos * config_.mirror_ratio; + vel_setpoint_ = *other_vel * config_.mirror_ratio; + torque_setpoint_ = *other_torque * config_.torque_mirror_ratio; } else { set_error(ERROR_INVALID_MIRROR_AXIS); return false; @@ -208,7 +220,13 @@ bool Controller::update(float* torque_setpoint_output) { torque_setpoint_ = traj_step.Ydd * config_.inertia; axis_->trap_traj_.t_ += current_meas_period; } - anticogging_pos = pos_setpoint_; // FF the position setpoint instead of the pos_estimate + anticogging_pos_estimate = pos_setpoint_; // FF the position setpoint instead of the pos_estimate + } break; + case INPUT_MODE_TUNING: { + autotuning_phase_ = wrap_pm_pi(autotuning_phase_ + (2.0f * M_PI * autotuning_.frequency * current_meas_period)); + pos_setpoint_ = autotuning_.pos_amplitude * our_arm_sin_f32(autotuning_phase_ + autotuning_.pos_phase); + vel_setpoint_ = autotuning_.vel_amplitude * our_arm_sin_f32(autotuning_phase_ + autotuning_.vel_phase); + torque_setpoint_ = autotuning_.torque_amplitude * our_arm_sin_f32(autotuning_phase_ + autotuning_.torque_phase); } break; default: { set_error(ERROR_INVALID_INPUT_MODE); @@ -225,17 +243,17 @@ bool Controller::update(float* torque_setpoint_output) { float pos_err; if (config_.circular_setpoints) { - if(!pos_estimate_circular) { + if (!pos_estimate_circular.has_value() || !pos_wrap.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } // Keep pos setpoint from drifting - pos_setpoint_ = fmodf_pos(pos_setpoint_, *pos_wrap_src_); + pos_setpoint_ = fmodf_pos(pos_setpoint_, *pos_wrap); // Circular delta pos_err = pos_setpoint_ - *pos_estimate_circular; - pos_err = wrap_pm(pos_err, 0.5f * *pos_wrap_src_); + pos_err = wrap_pm(pos_err, *pos_wrap); } else { - if(!pos_estimate_linear) { + if (!pos_estimate_linear.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } @@ -258,11 +276,11 @@ bool Controller::update(float* torque_setpoint_output) { // Check for overspeed fault (done in this module (controller) for cohesion with vel_lim) if (config_.enable_overspeed_error) { // 0.0f to disable - if (!vel_estimate_src) { + if (!vel_estimate.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } - if (std::abs(*vel_estimate_src) > config_.vel_limit_tolerance * vel_lim) { + if (std::abs(*vel_estimate) > config_.vel_limit_tolerance * vel_lim) { set_error(ERROR_OVERSPEED); return false; } @@ -273,9 +291,9 @@ bool Controller::update(float* torque_setpoint_output) { float vel_gain = config_.vel_gain; float vel_integrator_gain = config_.vel_integrator_gain; if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) { - float effective_flux = axis_->motor_.current_control_.acim_rotor_flux; + float effective_flux = axis_->acim_estimator_.rotor_flux_; float minflux = axis_->motor_.config_.acim_gain_min_flux; - if (fabsf(effective_flux) < minflux) + if (std::abs(effective_flux) < minflux) effective_flux = std::copysignf(minflux, effective_flux); vel_gain /= effective_flux; vel_integrator_gain /= effective_flux; @@ -290,17 +308,22 @@ bool Controller::update(float* torque_setpoint_output) { // We get the current position and apply a current feed-forward // ensuring that we handle negative encoder positions properly (-1 == motor->encoder.encoder_cpr - 1) if (anticogging_valid_ && config_.anticogging.anticogging_enabled) { + if (!anticogging_pos_estimate.has_value()) { + set_error(ERROR_INVALID_ESTIMATE); + return false; + } + float anticogging_pos = *anticogging_pos_estimate / axis_->encoder_.getCoggingRatio(); torque += config_.anticogging.cogging_map[std::clamp(mod((int)anticogging_pos, 3600), 0, 3600)]; } float v_err = 0.0f; if (config_.control_mode >= CONTROL_MODE_VELOCITY_CONTROL) { - if (!vel_estimate_src) { + if (!vel_estimate.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } - v_err = vel_des - *vel_estimate_src; + v_err = vel_des - *vel_estimate; torque += (vel_gain * gain_scheduling_multiplier) * v_err; // Velocity integral action before limiting @@ -309,11 +332,11 @@ bool Controller::update(float* torque_setpoint_output) { // Velocity limiting in current mode if (config_.control_mode < CONTROL_MODE_VELOCITY_CONTROL && config_.enable_current_mode_vel_limit) { - if (!vel_estimate_src) { + if (!vel_estimate.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } - torque = limitVel(config_.vel_limit, *vel_estimate_src, vel_gain, torque); + torque = limitVel(config_.vel_limit, *vel_estimate, vel_gain, torque); } // Torque limiting @@ -341,6 +364,33 @@ bool Controller::update(float* torque_setpoint_output) { } } - if (torque_setpoint_output) *torque_setpoint_output = torque; + float ideal_electrical_power = 0.0f; + if (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL) { + ideal_electrical_power = axis_->motor_.current_control_.power_ - \ + SQ(axis_->motor_.current_control_.Iq_measured_) * 1.5f * axis_->motor_.config_.phase_resistance - \ + SQ(axis_->motor_.current_control_.Id_measured_) * 1.5f * axis_->motor_.config_.phase_resistance; + } + else { + ideal_electrical_power = axis_->motor_.current_control_.power_; + } + mechanical_power_ += config_.mechanical_power_bandwidth * current_meas_period * (torque * *vel_estimate * M_PI * 2.0f - mechanical_power_); + electrical_power_ += config_.electrical_power_bandwidth * current_meas_period * (ideal_electrical_power - electrical_power_); + + // Spinout check + // If mechanical power is negative (braking) and measured power is positive, something is wrong + // This indicates that the controller is trying to stop, but torque is being produced. + // Usually caused by an incorrect encoder offset + if (mechanical_power_ < config_.spinout_mechanical_power_threshold && electrical_power_ > config_.spinout_electrical_power_threshold) { + set_error(ERROR_SPINOUT_DETECTED); + return false; + } + + torque_output_ = torque; + + // TODO: this is inconsistent with the other errors which are sticky. + // However if we make ERROR_INVALID_ESTIMATE sticky then it will be + // confusing that a normal sequence of motor calibration + encoder + // calibration would leave the controller in an error state. + error_ &= ~ERROR_INVALID_ESTIMATE; return true; } diff --git a/Firmware/MotorControl/controller.hpp b/Firmware/MotorControl/controller.hpp index 8022e946b..39f4cd0cb 100644 --- a/Firmware/MotorControl/controller.hpp +++ b/Firmware/MotorControl/controller.hpp @@ -1,13 +1,9 @@ #ifndef __CONTROLLER_HPP #define __CONTROLLER_HPP -#ifndef __ODRIVE_MAIN_H -#error "This file should not be included directly. Include odrive_main.h instead." -#endif - class Controller : public ODriveIntf::ControllerIntf { public: - typedef struct { + struct Anticogging_t { uint32_t index = 0; float cogging_map[3600]; bool pre_calibrated = false; @@ -16,7 +12,17 @@ class Controller : public ODriveIntf::ControllerIntf { float calib_vel_threshold = 1.0f; float cogging_ratio = 1.0f; bool anticogging_enabled = true; - } Anticogging_t; + }; + + struct Autotuning_t { + float frequency = 0.0f; + float pos_amplitude = 0.0f; + float pos_phase = 0.0f; + float vel_amplitude = 0.0f; + float vel_phase = 0.0f; + float torque_amplitude = 0.0f; + float torque_phase = 0.0f; + }; struct Config_t { ControlMode control_mode = CONTROL_MODE_POSITION_CONTROL; //see: ControlMode_t @@ -30,10 +36,11 @@ class Controller : public ODriveIntf::ControllerIntf { float vel_ramp_rate = 1.0f; // [(turn/s) / s] float torque_ramp_rate = 0.01f; // Nm / sec bool circular_setpoints = false; - float circular_setpoint_range = 1.0f; // Circular range when circular_setpoints is true. [turn] - float inertia = 0.0f; // [Nm/(turn/s^2)] - float input_filter_bandwidth = 2.0f; // [1/s] - float homing_speed = 0.25f; // [turn/s] + float circular_setpoint_range = 1.0f; // Circular range when circular_setpoints is true. [turn] + uint32_t steps_per_circular_range = 1024; + float inertia = 0.0f; // [Nm/(turn/s^2)] + float input_filter_bandwidth = 2.0f; // [1/s] + float homing_speed = 0.25f; // [turn/s] Anticogging_t anticogging; float gain_scheduling_width = 10.0f; bool enable_gain_scheduling = false; @@ -42,14 +49,23 @@ class Controller : public ODriveIntf::ControllerIntf { bool enable_current_mode_vel_limit = true; // enable velocity limit in current control mode (requires a valid velocity estimator) uint8_t axis_to_mirror = -1; float mirror_ratio = 1.0f; - uint8_t load_encoder_axis = -1; // default depends on Axis number and is set in load_configuration() + float torque_mirror_ratio = 0.0f; + uint8_t load_encoder_axis = -1; // default depends on Axis number and is set in load_configuration(). Set to -1 to select sensorless estimator. + float mechanical_power_bandwidth = 20.0f; // [rad/s] filter cutoff for mechanical power for spinout detction + float electrical_power_bandwidth = 20.0f; // [rad/s] filter cutoff for electrical power for spinout detection + float spinout_electrical_power_threshold = 10.0f; // [W] electrical power threshold for spinout detection + float spinout_mechanical_power_threshold = -10.0f; // [W] mechanical power threshold for spinout detection // custom setters Controller* parent; void set_input_filter_bandwidth(float value) { input_filter_bandwidth = value; parent->update_filter_gains(); } + void set_steps_per_circular_range(uint32_t value) { steps_per_circular_range = value > 0 ? value : steps_per_circular_range; } }; - explicit Controller(Config_t& config); + Controller() {} + + bool apply_config(); + void reset(); void set_error(Error error); @@ -68,20 +84,19 @@ class Controller : public ODriveIntf::ControllerIntf { bool anticogging_calibration(float pos_estimate, float vel_estimate); void update_filter_gains(); - bool update(float* torque_setpoint); + bool update(); - Config_t& config_; + Config_t config_; Axis* axis_ = nullptr; // set by Axis constructor Error error_ = ERROR_NONE; + float last_error_time_ = 0.0f; - float* pos_estimate_linear_src_ = nullptr; - float* pos_estimate_circular_src_ = nullptr; - bool* pos_estimate_valid_src_ = nullptr; - float* vel_estimate_src_ = nullptr; - bool* vel_estimate_valid_src_ = nullptr; - float* pos_wrap_src_ = nullptr; - + // Inputs + InputPort pos_estimate_linear_src_; + InputPort pos_estimate_circular_src_; + InputPort vel_estimate_src_; + InputPort pos_wrap_src_; float pos_setpoint_ = 0.0f; // [turns] float vel_setpoint_ = 0.0f; // [turn/s] @@ -95,15 +110,22 @@ class Controller : public ODriveIntf::ControllerIntf { float input_filter_kp_ = 0.0f; float input_filter_ki_ = 0.0f; + Autotuning_t autotuning_; + float autotuning_phase_ = 0.0f; + bool input_pos_updated_ = false; bool trajectory_done_ = true; bool anticogging_valid_ = false; + float mechanical_power_ = 0.0f; // [W] + float electrical_power_ = 0.0f; // [W] + + // Outputs + OutputPort torque_output_ = 0.0f; // custom setters void set_input_pos(float value) { input_pos_ = value; input_pos_updated(); } - }; #endif // __CONTROLLER_HPP diff --git a/Firmware/MotorControl/current_limiter.hpp b/Firmware/MotorControl/current_limiter.hpp index 4334f5c04..acb96f0bb 100644 --- a/Firmware/MotorControl/current_limiter.hpp +++ b/Firmware/MotorControl/current_limiter.hpp @@ -1,10 +1,6 @@ #ifndef __CURRENT_LIMITER_HPP #define __CURRENT_LIMITER_HPP -#ifndef __ODRIVE_MAIN_H -#error "This file should not be included directly. Include odrive_main.h instead." -#endif - class CurrentLimiter { public: virtual ~CurrentLimiter() = default; diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 2c4659880..71072923c 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -1,34 +1,65 @@ #include "odrive_main.h" +#include +#include + +Encoder::Encoder(TIM_HandleTypeDef* timer, Stm32Gpio index_gpio, + Stm32Gpio hallA_gpio, Stm32Gpio hallB_gpio, Stm32Gpio hallC_gpio, + Stm32SpiArbiter* spi_arbiter) : + timer_(timer), index_gpio_(index_gpio), + hallA_gpio_(hallA_gpio), hallB_gpio_(hallB_gpio), hallC_gpio_(hallC_gpio), + spi_arbiter_(spi_arbiter) +{ +} + +static void enc_index_cb_wrapper(void* ctx) { + reinterpret_cast(ctx)->enc_index_cb(); +} +bool Encoder::apply_config(ODriveIntf::MotorIntf::MotorType motor_type) { + config_.parent = this; -Encoder::Encoder(const EncoderHardwareConfig_t& hw_config, - Config_t& config, const Motor::Config_t& motor_config) : - hw_config_(hw_config), - config_(config) -{ update_pll_gains(); - if (config.pre_calibrated) { - if (config.mode == Encoder::MODE_HALL || config.mode == Encoder::MODE_SINCOS) + if (config_.pre_calibrated) { + if (config_.mode == Encoder::MODE_HALL && config_.hall_polarity_calibrated) is_ready_ = true; - if (motor_config.motor_type == Motor::MOTOR_TYPE_ACIM) + if (config_.mode == Encoder::MODE_SINCOS) + is_ready_ = true; + if (motor_type == Motor::MOTOR_TYPE_ACIM) is_ready_ = true; } -} -static void enc_index_cb_wrapper(void* ctx) { - reinterpret_cast(ctx)->enc_index_cb(); + return true; } void Encoder::setup() { - HAL_TIM_Encoder_Start(hw_config_.timer, TIM_CHANNEL_ALL); + HAL_TIM_Encoder_Start(timer_, TIM_CHANNEL_ALL); set_idx_subscribe(); mode_ = config_.mode; + + spi_task_.config = { + .Mode = SPI_MODE_MASTER, + .Direction = SPI_DIRECTION_2LINES, + .DataSize = SPI_DATASIZE_16BIT, + .CLKPolarity = (mode_ == MODE_SPI_ABS_AEAT || mode_ == MODE_SPI_ABS_MA732) ? SPI_POLARITY_HIGH : SPI_POLARITY_LOW, + .CLKPhase = SPI_PHASE_2EDGE, + .NSS = SPI_NSS_SOFT, + .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32, + .FirstBit = SPI_FIRSTBIT_MSB, + .TIMode = SPI_TIMODE_DISABLE, + .CRCCalculation = SPI_CRCCALCULATION_DISABLE, + .CRCPolynomial = 10, + }; + + if (mode_ == MODE_SPI_ABS_MA732) { + abs_spi_dma_tx_[0] = 0x0000; + } + if(mode_ & MODE_FLAG_ABS){ abs_spi_cs_pin_init(); - abs_spi_init(); + if (axis_->controller_.config_.anticogging.pre_calibrated) { axis_->controller_.anticogging_valid_ = true; } @@ -39,7 +70,6 @@ void Encoder::set_error(Error error) { vel_estimate_valid_ = false; pos_estimate_valid_ = false; error_ |= error; - axis_->error_ |= Axis::ERROR_ENCODER_FAILED; } bool Encoder::do_checks(){ @@ -56,8 +86,8 @@ bool Encoder::do_checks(){ void Encoder::enc_index_cb() { if (config_.use_index) { set_circular_count(0, false); - if (config_.zero_count_on_find_idx) - set_linear_count(0); // Avoid position control transient after search + if (config_.use_index_offset) + set_linear_count((int32_t)(config_.index_offset * config_.cpr)); if (config_.pre_calibrated) { is_ready_ = true; if(axis_->controller_.config_.anticogging.pre_calibrated){ @@ -73,15 +103,16 @@ void Encoder::enc_index_cb() { } // Disable interrupt - GPIO_unsubscribe(hw_config_.index_port, hw_config_.index_pin); + index_gpio_.unsubscribe(); } void Encoder::set_idx_subscribe(bool override_enable) { if (config_.use_index && (override_enable || !config_.find_idx_on_lockin_only)) { - GPIO_subscribe(hw_config_.index_port, hw_config_.index_pin, GPIO_PULLDOWN, - enc_index_cb_wrapper, this); + if (!index_gpio_.subscribe(true, false, enc_index_cb_wrapper, this)) { + odrv.misconfigured_ = true; + } } else if (!config_.use_index || config_.find_idx_on_lockin_only) { - GPIO_unsubscribe(hw_config_.index_port, hw_config_.index_pin); + index_gpio_.unsubscribe(); } } @@ -97,10 +128,12 @@ void Encoder::update_pll_gains() { void Encoder::check_pre_calibrated() { // TODO: restoring config from python backup is fragile here (ACIM motor type must be set first) - if (!is_ready_ && axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_ACIM) - config_.pre_calibrated = false; - if (mode_ == MODE_INCREMENTAL && !index_found_) - config_.pre_calibrated = false; + if (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_ACIM) { + if (!is_ready_) + config_.pre_calibrated = false; + if (mode_ == MODE_INCREMENTAL && !index_found_) + config_.pre_calibrated = false; + } } // Function that sets the current encoder count to a desired 32-bit value. @@ -114,7 +147,7 @@ void Encoder::set_linear_count(int32_t count) { tim_cnt_sample_ = count; //Write hardware last - hw_config_.timer->Instance->CNT = count; + timer_->Instance->CNT = count; cpu_exit_critical(prim); } @@ -126,8 +159,8 @@ void Encoder::set_circular_count(int32_t count, bool update_offset) { uint32_t prim = cpu_enter_critical(); if (update_offset) { - config_.offset += count - count_in_cpr_; - config_.offset = mod(config_.offset, config_.cpr); + config_.phase_offset += count - count_in_cpr_; + config_.phase_offset = mod(config_.phase_offset, config_.cpr); } // Update states @@ -140,49 +173,159 @@ void Encoder::set_circular_count(int32_t count, bool update_offset) { bool Encoder::run_index_search() { config_.use_index = true; index_found_ = false; - if (!config_.idx_search_unidirectional && axis_->motor_.config_.direction == 0) { - axis_->motor_.config_.direction = 1; - } set_idx_subscribe(); - bool status = axis_->run_lockin_spin(axis_->config_.calibration_lockin); - return status; + bool success = axis_->run_lockin_spin(axis_->config_.calibration_lockin, false); + return success; } bool Encoder::run_direction_find() { int32_t init_enc_val = shadow_count_; - axis_->motor_.config_.direction = 1; // Must test spin forwards for direction detect logic Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin; lockin_config.finish_distance = lockin_config.vel * 3.0f; // run for 3 seconds lockin_config.finish_on_distance = true; lockin_config.finish_on_enc_idx = false; lockin_config.finish_on_vel = false; - bool status = axis_->run_lockin_spin(lockin_config); + bool success = axis_->run_lockin_spin(lockin_config, false); - if (status) { + if (success) { // Check response and direction if (shadow_count_ > init_enc_val + 8) { // motor same dir as encoder - axis_->motor_.config_.direction = 1; + config_.direction = 1; } else if (shadow_count_ < init_enc_val - 8) { // motor opposite dir as encoder - axis_->motor_.config_.direction = -1; + config_.direction = -1; } else { - axis_->motor_.config_.direction = 0; + config_.direction = 0; } } - return status; + return success; +} + + +bool Encoder::run_hall_polarity_calibration() { + Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin; + lockin_config.finish_distance = lockin_config.vel * 3.0f; // run for 3 seconds + lockin_config.finish_on_distance = true; + lockin_config.finish_on_enc_idx = false; + lockin_config.finish_on_vel = false; + + auto loop_cb = [this](bool const_vel) { + if (const_vel) + sample_hall_states_ = true; + // No need to cancel early + return true; + }; + + config_.hall_polarity_calibrated = false; + states_seen_count_.fill(0); + bool success = axis_->run_lockin_spin(lockin_config, false, loop_cb); + sample_hall_states_ = false; + + if (success) { + std::bitset<8> state_seen; + std::bitset<8> state_confirmed; + for (int i = 0; i < 8; i++) { + if (states_seen_count_[i] > 0) + state_seen[i] = true; + if (states_seen_count_[i] > 50) + state_confirmed[i] = true; + } + if (!(state_seen == state_confirmed)) { + set_error(ERROR_ILLEGAL_HALL_STATE); + return false; + } + + // Hall effect sensors can be arranged at 60 or 120 electrical degrees. + // Out of 8 possible states, 120 and 60 deg arrangements each miss 2 states. + // ODrive assumes 120 deg separation - if a 60 deg setup is used, it can + // be converted to 120 deg states by flipping the polarity of one sensor. + uint8_t states = state_seen.to_ulong(); + uint8_t hall_polarity = 0; + auto flip_detect = [](uint8_t states, unsigned int idx)->bool { + return (~states & 0xFF) == (1<<(0+idx) | 1<<(7-idx)); + }; + if (flip_detect(states, 0)) { + hall_polarity = 0b000; + } else if (flip_detect(states, 1)) { + hall_polarity = 0b001; + } else if (flip_detect(states, 2)) { + hall_polarity = 0b010; + } else if (flip_detect(states, 3)) { + hall_polarity = 0b100; + } else { + set_error(ERROR_ILLEGAL_HALL_STATE); + return false; + } + config_.hall_polarity = hall_polarity; + config_.hall_polarity_calibrated = true; + } + + return success; +} + +bool Encoder::run_hall_phase_calibration() { + Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin; + lockin_config.finish_distance = lockin_config.vel * 30.0f; // run for 30 seconds + lockin_config.finish_on_distance = true; + lockin_config.finish_on_enc_idx = false; + lockin_config.finish_on_vel = false; + + auto loop_cb = [this](bool const_vel) { + if (const_vel) + sample_hall_phase_ = true; + // No need to cancel early + return true; + }; + + // TODO: There is a race condition here with the execution in Encoder::update. + // We should evaluate making thread execution synchronous with the control loops + // at least optionally. + // Perhaps the new loop_sync feature will give a loose timing guarantee that may be sufficient + calibrate_hall_phase_ = true; + config_.hall_edge_phcnt.fill(0.0f); + hall_phase_calib_seen_count_.fill(0); + bool success = axis_->run_lockin_spin(lockin_config, false, loop_cb); + if (error_ & ERROR_ILLEGAL_HALL_STATE) + success = false; + + if (success) { + // Check deltas to dicern rotation direction + float delta_phase = 0.0f; + for (int i = 0; i < 6; i++) { + int next_i = (i == 5) ? 0 : i+1; + delta_phase += wrap_pm_pi(config_.hall_edge_phcnt[next_i] - config_.hall_edge_phcnt[i]); + } + // Correct reverse rotation + if (delta_phase < 0.0f) { + config_.direction = -1; + for (int i = 0; i < 6; i++) + config_.hall_edge_phcnt[i] = wrap_pm_pi(-config_.hall_edge_phcnt[i]); + } else { + config_.direction = 1; + } + // Normalize edge timing to 1st edge in sequence, and change units to counts + float offset = config_.hall_edge_phcnt[0]; + for (int i = 0; i < 6; i++) { + float& phcnt = config_.hall_edge_phcnt[i]; + phcnt = fmodf_pos((6.0f / (2.0f * M_PI)) * (phcnt - offset), 6.0f); + } + } else { + config_.hall_edge_phcnt = hall_edge_defaults; + } + + calibrate_hall_phase_ = false; + return success; } // @brief Turns the motor in one direction for a bit and then in the other // direction in order to find the offset between the electrical phase 0 // and the encoder state 0. -// TODO: Do the scan with current, not voltage! bool Encoder::run_offset_calibration() { const float start_lock_duration = 1.0f; - const int num_steps = (int)(config_.calib_scan_distance / config_.calib_scan_omega * (float)current_meas_hz); // Require index found if enabled if (config_.use_index && !index_found_) { @@ -190,92 +333,129 @@ bool Encoder::run_offset_calibration() { return false; } + if (config_.mode == MODE_HALL && !config_.hall_polarity_calibrated) { + set_error(ERROR_HALL_NOT_CALIBRATED_YET); + return false; + } + // We use shadow_count_ to do the calibration, but the offset is used by count_in_cpr_ // Therefore we have to sync them for calibration shadow_count_ = count_in_cpr_; - float voltage_magnitude; - if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_HIGH_CURRENT) - voltage_magnitude = axis_->motor_.config_.calibration_current * axis_->motor_.config_.phase_resistance; - else if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) - voltage_magnitude = axis_->motor_.config_.calibration_current; - else - return false; + CRITICAL_SECTION() { + // Reset state variables + axis_->open_loop_controller_.Idq_setpoint_ = {0.0f, 0.0f}; + axis_->open_loop_controller_.Vdq_setpoint_ = {0.0f, 0.0f}; + axis_->open_loop_controller_.phase_ = 0.0f; + axis_->open_loop_controller_.phase_vel_ = 0.0f; + + float max_current_ramp = axis_->motor_.config_.calibration_current / start_lock_duration * 2.0f; + axis_->open_loop_controller_.max_current_ramp_ = max_current_ramp; + axis_->open_loop_controller_.max_voltage_ramp_ = max_current_ramp; + axis_->open_loop_controller_.max_phase_vel_ramp_ = INFINITY; + axis_->open_loop_controller_.target_current_ = axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? axis_->motor_.config_.calibration_current : 0.0f; + axis_->open_loop_controller_.target_voltage_ = axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? 0.0f : axis_->motor_.config_.calibration_current; + axis_->open_loop_controller_.target_vel_ = 0.0f; + axis_->open_loop_controller_.total_distance_ = 0.0f; + axis_->open_loop_controller_.phase_ = axis_->open_loop_controller_.initial_phase_ = wrap_pm_pi(0 - config_.calib_scan_distance / 2.0f); + + axis_->motor_.current_control_.enable_current_control_src_ = (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL); + axis_->motor_.current_control_.Idq_setpoint_src_.connect_to(&axis_->open_loop_controller_.Idq_setpoint_); + axis_->motor_.current_control_.Vdq_setpoint_src_.connect_to(&axis_->open_loop_controller_.Vdq_setpoint_); + + axis_->motor_.current_control_.phase_src_.connect_to(&axis_->open_loop_controller_.phase_); + axis_->acim_estimator_.rotor_phase_src_.connect_to(&axis_->open_loop_controller_.phase_); + + axis_->motor_.phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_); + axis_->motor_.current_control_.phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_); + axis_->acim_estimator_.rotor_phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_); + } + axis_->wait_for_control_iteration(); + + axis_->motor_.arm(&axis_->motor_.current_control_); + + // go to start position of forward scan for start_lock_duration to get ready to scan + for (size_t i = 0; i < (size_t)(start_lock_duration * 1000.0f); ++i) { + if (!axis_->motor_.is_armed_) { + return false; // TODO: return "disarmed" error code + } + if (axis_->requested_state_ != Axis::AXIS_STATE_UNDEFINED) { + axis_->motor_.disarm(); + return false; // TODO: return "aborted" error code + } + osDelay(1); + } - // go to motor zero phase for start_lock_duration to get ready to scan - int i = 0; - axis_->run_control_loop([&](){ - if (!axis_->motor_.enqueue_voltage_timings(voltage_magnitude, 0.0f)) - return false; // error set inside enqueue_voltage_timings - axis_->motor_.log_timing(TIMING_LOG_ENC_CALIB); - return ++i < start_lock_duration * current_meas_hz; - }); - if (axis_->error_ != Axis::ERROR_NONE) - return false; int32_t init_enc_val = shadow_count_; + uint32_t num_steps = 0; int64_t encvaluesum = 0; - // scan forward - i = 0; - axis_->run_control_loop([&]() { - float phase = wrap_pm_pi(config_.calib_scan_distance * (float)i / (float)num_steps - config_.calib_scan_distance / 2.0f); - float v_alpha = voltage_magnitude * our_arm_cos_f32(phase); - float v_beta = voltage_magnitude * our_arm_sin_f32(phase); - if (!axis_->motor_.enqueue_voltage_timings(v_alpha, v_beta)) - return false; // error set inside enqueue_voltage_timings - axis_->motor_.log_timing(TIMING_LOG_ENC_CALIB); + CRITICAL_SECTION() { + axis_->open_loop_controller_.target_vel_ = config_.calib_scan_omega; + axis_->open_loop_controller_.total_distance_ = 0.0f; + } + // scan forward + while ((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_) { + bool reached_target_dist = axis_->open_loop_controller_.total_distance_.any().value_or(-INFINITY) >= config_.calib_scan_distance; + if (reached_target_dist) { + break; + } encvaluesum += shadow_count_; - - return ++i < num_steps; - }); - if (axis_->error_ != Axis::ERROR_NONE) - return false; + num_steps++; + osDelay(1); + } // Check response and direction if (shadow_count_ > init_enc_val + 8) { // motor same dir as encoder - axis_->motor_.config_.direction = 1; + config_.direction = 1; } else if (shadow_count_ < init_enc_val - 8) { // motor opposite dir as encoder - axis_->motor_.config_.direction = -1; + config_.direction = -1; } else { // Encoder response error set_error(ERROR_NO_RESPONSE); + axis_->motor_.disarm(); return false; } - //TODO avoid recomputing elec_rad_per_enc every time // Check CPR float elec_rad_per_enc = axis_->motor_.config_.pole_pairs * 2 * M_PI * (1.0f / (float)(config_.cpr)); float expected_encoder_delta = config_.calib_scan_distance / elec_rad_per_enc; calib_scan_response_ = std::abs(shadow_count_ - init_enc_val); if (std::abs(calib_scan_response_ - expected_encoder_delta) / expected_encoder_delta > config_.calib_range) { set_error(ERROR_CPR_POLEPAIRS_MISMATCH); + axis_->motor_.disarm(); return false; } - // scan backwards - i = 0; - axis_->run_control_loop([&]() { - float phase = wrap_pm_pi(-config_.calib_scan_distance * (float)i / (float)num_steps + config_.calib_scan_distance / 2.0f); - float v_alpha = voltage_magnitude * our_arm_cos_f32(phase); - float v_beta = voltage_magnitude * our_arm_sin_f32(phase); - if (!axis_->motor_.enqueue_voltage_timings(v_alpha, v_beta)) - return false; // error set inside enqueue_voltage_timings - axis_->motor_.log_timing(TIMING_LOG_ENC_CALIB); + CRITICAL_SECTION() { + axis_->open_loop_controller_.target_vel_ = -config_.calib_scan_omega; + } + // scan backwards + while ((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_) { + bool reached_target_dist = axis_->open_loop_controller_.total_distance_.any().value_or(INFINITY) <= 0.0f; + if (reached_target_dist) { + break; + } encvaluesum += shadow_count_; - - return ++i < num_steps; - }); - if (axis_->error_ != Axis::ERROR_NONE) + num_steps++; + osDelay(1); + } + + // Motor disarmed because of an error + if (!axis_->motor_.is_armed_) { return false; + } - config_.offset = encvaluesum / (num_steps * 2); - int32_t residual = encvaluesum - ((int64_t)config_.offset * (int64_t)(num_steps * 2)); - config_.offset_float = (float)residual / (float)(num_steps * 2) + 0.5f; // add 0.5 to center-align state to phase + axis_->motor_.disarm(); + + config_.phase_offset = encvaluesum / num_steps; + int32_t residual = encvaluesum - ((int64_t)config_.phase_offset * (int64_t)num_steps); + config_.phase_offset_float = (float)residual / (float)num_steps + 0.5f; // add 0.5 to center-align state to phase is_ready_ = true; return true; @@ -296,7 +476,7 @@ static bool decode_hall(uint8_t hall_state, int32_t* hall_cnt) { void Encoder::sample_now() { switch (mode_) { case MODE_INCREMENTAL: { - tim_cnt_sample_ = (int16_t)hw_config_.timer->Instance->CNT; + tim_cnt_sample_ = (int16_t)timer_->Instance->CNT; } break; case MODE_HALL: { @@ -304,16 +484,17 @@ void Encoder::sample_now() { } break; case MODE_SINCOS: { - sincos_sample_s_ = (get_adc_voltage(get_gpio_port_by_pin(config_.sincos_gpio_pin_sin), get_gpio_pin_by_pin(config_.sincos_gpio_pin_sin)) / 3.3f) - 0.5f; - sincos_sample_c_ = (get_adc_voltage(get_gpio_port_by_pin(config_.sincos_gpio_pin_cos), get_gpio_pin_by_pin(config_.sincos_gpio_pin_cos)) / 3.3f) - 0.5f; + sincos_sample_s_ = get_adc_relative_voltage(get_gpio(config_.sincos_gpio_pin_sin)) - 0.5f; + sincos_sample_c_ = get_adc_relative_voltage(get_gpio(config_.sincos_gpio_pin_cos)) - 0.5f; } break; case MODE_SPI_ABS_AMS: case MODE_SPI_ABS_CUI: case MODE_SPI_ABS_AEAT: case MODE_SPI_ABS_RLS: + case MODE_SPI_ABS_MA732: { - axis_->motor_.log_timing(TIMING_LOG_SAMPLE_NOW); + abs_spi_start_transaction(); // Do nothing } break; @@ -321,41 +502,43 @@ void Encoder::sample_now() { set_error(ERROR_UNSUPPORTED_ENCODER_MODE); } break; } + + // Sample all GPIO digital input data registers, used for HALL sensors for example. + for (size_t i = 0; i < sizeof(ports_to_sample) / sizeof(ports_to_sample[0]); ++i) { + port_samples_[i] = ports_to_sample[i]->IDR; + } } -bool Encoder::abs_spi_init(){ - if ((mode_ & MODE_FLAG_ABS) == 0x0) - return false; +bool Encoder::read_sampled_gpio(Stm32Gpio gpio) { + for (size_t i = 0; i < sizeof(ports_to_sample) / sizeof(ports_to_sample[0]); ++i) { + if (ports_to_sample[i] == gpio.port_) { + return port_samples_[i] & gpio.pin_mask_; + } + } + return false; +} - SPI_HandleTypeDef * spi = hw_config_.spi; - spi->Init.Mode = SPI_MODE_MASTER; - spi->Init.Direction = SPI_DIRECTION_2LINES; - spi->Init.DataSize = SPI_DATASIZE_16BIT; - spi->Init.CLKPolarity = SPI_POLARITY_LOW; - spi->Init.CLKPhase = SPI_PHASE_2EDGE; - spi->Init.NSS = SPI_NSS_SOFT; - spi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; - spi->Init.FirstBit = SPI_FIRSTBIT_MSB; - spi->Init.TIMode = SPI_TIMODE_DISABLE; - spi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; - spi->Init.CRCPolynomial = 10; - if (mode_ == MODE_SPI_ABS_AEAT) { - spi->Init.CLKPolarity = SPI_POLARITY_HIGH; - } - HAL_SPI_DeInit(spi); - HAL_SPI_Init(spi); - return true; +void Encoder::decode_hall_samples() { + hall_state_ = (read_sampled_gpio(hallA_gpio_) ? 1 : 0) + | (read_sampled_gpio(hallB_gpio_) ? 2 : 0) + | (read_sampled_gpio(hallC_gpio_) ? 4 : 0); } -bool Encoder::abs_spi_start_transaction(){ +bool Encoder::abs_spi_start_transaction() { if (mode_ & MODE_FLAG_ABS){ - axis_->motor_.log_timing(TIMING_LOG_SPI_START); - if(hw_config_.spi->State != HAL_SPI_STATE_READY){ - set_error(ERROR_ABS_SPI_NOT_READY); + if (Stm32SpiArbiter::acquire_task(&spi_task_)) { + spi_task_.ncs_gpio = abs_spi_cs_gpio_; + spi_task_.tx_buf = (uint8_t*)abs_spi_dma_tx_; + spi_task_.rx_buf = (uint8_t*)abs_spi_dma_rx_; + spi_task_.length = 1; + spi_task_.on_complete = [](void* ctx, bool success) { ((Encoder*)ctx)->abs_spi_cb(success); }; + spi_task_.on_complete_ctx = this; + spi_task_.next = nullptr; + + spi_arbiter_->transfer_async(&spi_task_); + } else { return false; } - HAL_GPIO_WritePin(abs_spi_cs_port_, abs_spi_cs_pin_, GPIO_PIN_RESET); - HAL_SPI_TransmitReceive_DMA(hw_config_.spi, (uint8_t*)abs_spi_dma_tx_, (uint8_t*)abs_spi_dma_rx_, 1); } return true; } @@ -375,19 +558,19 @@ uint8_t cui_parity(uint16_t v) { return ~v & 3; } -void Encoder::abs_spi_cb(){ - HAL_GPIO_WritePin(abs_spi_cs_port_, abs_spi_cs_pin_, GPIO_PIN_SET); - - axis_->motor_.log_timing(TIMING_LOG_SPI_END); - +void Encoder::abs_spi_cb(bool success) { uint16_t pos; + if (!success) { + goto done; + } + switch (mode_) { case MODE_SPI_ABS_AMS: { uint16_t rawVal = abs_spi_dma_rx_[0]; // check if parity is correct (even) and error flag clear if (ams_parity(rawVal) || ((rawVal >> 14) & 1)) { - return; + goto done; } pos = rawVal & 0x3fff; } break; @@ -396,7 +579,7 @@ void Encoder::abs_spi_cb(){ uint16_t rawVal = abs_spi_dma_rx_[0]; // check if parity is correct if (cui_parity(rawVal)) { - return; + goto done; } pos = rawVal & 0x3fff; } break; @@ -406,9 +589,14 @@ void Encoder::abs_spi_cb(){ pos = (rawVal >> 2) & 0x3fff; } break; + case MODE_SPI_ABS_MA732: { + uint16_t rawVal = abs_spi_dma_rx_[0]; + pos = (rawVal >> 2) & 0x3fff; + } break; + default: { set_error(ERROR_UNSUPPORTED_ENCODER_MODE); - return; + goto done; } break; } @@ -417,24 +605,46 @@ void Encoder::abs_spi_cb(){ if (config_.pre_calibrated) { is_ready_ = true; } + +done: + Stm32SpiArbiter::release_task(&spi_task_); } void Encoder::abs_spi_cs_pin_init(){ - // Decode cs pin - abs_spi_cs_port_ = get_gpio_port_by_pin(config_.abs_spi_cs_gpio_pin); - abs_spi_cs_pin_ = get_gpio_pin_by_pin(config_.abs_spi_cs_gpio_pin); - - // Init cs pin - HAL_GPIO_DeInit(abs_spi_cs_port_, abs_spi_cs_pin_); - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.Pin = abs_spi_cs_pin_; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_PULLUP; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - HAL_GPIO_Init(abs_spi_cs_port_, &GPIO_InitStruct); + // Decode and init cs pin +#if HW_VERSION_MAJOR == 4 + if (mode_ == MODE_SPI_ABS_MA732) + abs_spi_cs_gpio_ = {GPIOA, GPIO_PIN_15}; + else +#else + abs_spi_cs_gpio_ = get_gpio(config_.abs_spi_cs_gpio_pin); +#endif + abs_spi_cs_gpio_.config(GPIO_MODE_OUTPUT_PP, GPIO_PULLUP); // Write pin high - HAL_GPIO_WritePin(abs_spi_cs_port_, abs_spi_cs_pin_, GPIO_PIN_SET); + abs_spi_cs_gpio_.write(true); +} + +// Note that this may return counts +1 or -1 without any wrapping +int32_t Encoder::hall_model(float internal_pos) { + int32_t base_cnt = (int32_t)std::floor(internal_pos); + + float pos_in_range = fmodf_pos(internal_pos, 6.0f); + int pos_idx = (int)pos_in_range; + if (pos_idx == 6) pos_idx = 5; // in case of rounding error + int next_i = (pos_idx == 5) ? 0 : pos_idx+1; + + float below_edge = config_.hall_edge_phcnt[pos_idx]; + float above_edge = config_.hall_edge_phcnt[next_i]; + + // if we are blow the "below" edge, we are the count under + if (wrap_pm(pos_in_range - below_edge, 6.0f) < 0.0f) + return base_cnt - 1; + // if we are above the "above" edge, we are the count over + else if (wrap_pm(pos_in_range - above_edge, 6.0f) > 0.0f) + return base_cnt + 1; + // otherwise we are in the nominal count (or completely lost) + return base_cnt; } bool Encoder::update() { @@ -451,16 +661,57 @@ bool Encoder::update() { } break; case MODE_HALL: { - int32_t hall_cnt; - if (decode_hall(hall_state_, &hall_cnt)) { - delta_enc = hall_cnt - count_in_cpr_; - delta_enc = mod(delta_enc, 6); - if (delta_enc > 3) - delta_enc -= 6; - } else { - if (!config_.ignore_illegal_hall_state) { - set_error(ERROR_ILLEGAL_HALL_STATE); - return false; + decode_hall_samples(); + if (sample_hall_states_) { + states_seen_count_[hall_state_]++; + } + if (config_.hall_polarity_calibrated) { + int32_t hall_cnt; + if (decode_hall((hall_state_ ^ config_.hall_polarity), &hall_cnt)) { + if (calibrate_hall_phase_) { + if (sample_hall_phase_ && last_hall_cnt_.has_value()) { + int mod_hall_cnt = mod(hall_cnt - last_hall_cnt_.value(), 6); + size_t edge_idx; + if (mod_hall_cnt == 0) { goto skip; } // no count - do nothing + else if (mod_hall_cnt == 1) { // counted up + edge_idx = hall_cnt; + } else if (mod_hall_cnt == 5) { // counted down + edge_idx = last_hall_cnt_.value(); + } else { + set_error(ERROR_ILLEGAL_HALL_STATE); + return false; + } + + auto maybe_phase = axis_->open_loop_controller_.phase_.any(); + if (maybe_phase) { + float phase = maybe_phase.value(); + // Early increment to get the right divisor in recursive average + hall_phase_calib_seen_count_[edge_idx]++; + float& edge_phase = config_.hall_edge_phcnt[edge_idx]; + if (hall_phase_calib_seen_count_[edge_idx] == 1) + edge_phase = phase; + else { + // circularly wrapped recursive average + edge_phase += (phase - edge_phase) / hall_phase_calib_seen_count_[edge_idx]; + edge_phase = wrap_pm_pi(edge_phase); + } + } + } + skip: + last_hall_cnt_ = hall_cnt; + + return true; // Skip all velocity and phase estimation + } + + delta_enc = hall_cnt - count_in_cpr_; + delta_enc = mod(delta_enc, 6); + if (delta_enc > 3) + delta_enc -= 6; + } else { + if (!config_.ignore_illegal_hall_state) { + set_error(ERROR_ILLEGAL_HALL_STATE); + return false; + } } } } break; @@ -479,12 +730,15 @@ bool Encoder::update() { case MODE_SPI_ABS_RLS: case MODE_SPI_ABS_AMS: case MODE_SPI_ABS_CUI: - case MODE_SPI_ABS_AEAT: { + case MODE_SPI_ABS_AEAT: + case MODE_SPI_ABS_MA732: { if (abs_spi_pos_updated_ == false) { // Low pass filter the error spi_error_rate_ += current_meas_period * (1.0f - spi_error_rate_); - if (spi_error_rate_ > 0.005f) + if (spi_error_rate_ > 0.005f) { set_error(ERROR_ABS_SPI_COM_FAIL); + return false; + } } else { // Low pass filter the error spi_error_rate_ += current_meas_period * (0.0f - spi_error_rate_); @@ -499,8 +753,8 @@ bool Encoder::update() { }break; default: { - set_error(ERROR_UNSUPPORTED_ENCODER_MODE); - return false; + set_error(ERROR_UNSUPPORTED_ENCODER_MODE); + return false; } break; } @@ -511,14 +765,25 @@ bool Encoder::update() { if(mode_ & MODE_FLAG_ABS) count_in_cpr_ = pos_abs_latched; + // Memory for pos_circular + float pos_cpr_counts_last = pos_cpr_counts_; + //// run pll (for now pll is in units of encoder counts) // Predict current pos pos_estimate_counts_ += current_meas_period * vel_estimate_counts_; pos_cpr_counts_ += current_meas_period * vel_estimate_counts_; + // Encoder model + auto encoder_model = [this](float internal_pos)->int32_t { + if (config_.mode == MODE_HALL) + return hall_model(internal_pos); + else + return (int32_t)std::floor(internal_pos); + }; // discrete phase detector - float delta_pos_counts = (float)(shadow_count_ - (int32_t)std::floor(pos_estimate_counts_)); - float delta_pos_cpr_counts = (float)(count_in_cpr_ - (int32_t)std::floor(pos_cpr_counts_)); - delta_pos_cpr_counts = wrap_pm(delta_pos_cpr_counts, 0.5f * (float)(config_.cpr)); + float delta_pos_counts = (float)(shadow_count_ - encoder_model(pos_estimate_counts_)); + float delta_pos_cpr_counts = (float)(count_in_cpr_ - encoder_model(pos_cpr_counts_)); + delta_pos_cpr_counts = wrap_pm(delta_pos_cpr_counts, (float)(config_.cpr)); + delta_pos_cpr_counts_ += 0.1f * (delta_pos_cpr_counts - delta_pos_cpr_counts_); // for debug // pll feedback pos_estimate_counts_ += current_meas_period * pll_kp_ * delta_pos_counts; pos_cpr_counts_ += current_meas_period * pll_kp_ * delta_pos_cpr_counts; @@ -531,16 +796,19 @@ bool Encoder::update() { } // Outputs from Encoder for Controller - float pos_cpr_last = pos_cpr_; pos_estimate_ = pos_estimate_counts_ / (float)config_.cpr; vel_estimate_ = vel_estimate_counts_ / (float)config_.cpr; - pos_cpr_= pos_cpr_counts_ / (float)config_.cpr; - float delta_pos_cpr = wrap_pm(pos_cpr_ - pos_cpr_last, 0.5f); - pos_circular_ += delta_pos_cpr; - pos_circular_ = fmodf_pos(pos_circular_, axis_->controller_.config_.circular_setpoint_range); + + // TODO: we should strictly require that this value is from the previous iteration + // to avoid spinout scenarios. However that requires a proper way to reset + // the encoder from error states. + float pos_circular = pos_circular_.any().value_or(0.0f); + pos_circular += wrap_pm((pos_cpr_counts_ - pos_cpr_counts_last) / (float)config_.cpr, 1.0f); + pos_circular = fmodf_pos(pos_circular, axis_->controller_.config_.circular_setpoint_range); + pos_circular_ = pos_circular; //// run encoder count interpolation - int32_t corrected_enc = count_in_cpr_ - config_.offset; + int32_t corrected_enc = count_in_cpr_ - config_.phase_offset; // if we are stopped, make sure we don't randomly drift if (snap_to_zero_vel || !config_.enable_phase_interpolation) { interpolation_ = 0.5f; @@ -562,11 +830,12 @@ bool Encoder::update() { //// compute electrical phase //TODO avoid recomputing elec_rad_per_enc every time float elec_rad_per_enc = axis_->motor_.config_.pole_pairs * 2 * M_PI * (1.0f / (float)(config_.cpr)); - float ph = elec_rad_per_enc * (interpolated_enc - config_.offset_float); - // ph = fmodf(ph, 2*M_PI); - phase_ = wrap_pm_pi(ph); + float ph = elec_rad_per_enc * (interpolated_enc - config_.phase_offset_float); + + if (is_ready_) { + phase_ = wrap_pm_pi(ph) * config_.direction; + phase_vel_ = (2*M_PI) * *vel_estimate_.present() * axis_->motor_.config_.pole_pairs * config_.direction; + } - vel_estimate_valid_ = true; - pos_estimate_valid_ = true; return true; } diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index d100d1681..d764582f2 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -1,39 +1,50 @@ #ifndef __ENCODER_HPP #define __ENCODER_HPP -#ifndef __ODRIVE_MAIN_H -#error "This file should not be included directly. Include odrive_main.h instead." -#endif +class Encoder; + +#include // needed for arm_math.h +#include +#include "utils.hpp" +#include +#include "component.hpp" class Encoder : public ODriveIntf::EncoderIntf { public: static constexpr uint32_t MODE_FLAG_ABS = 0x100; + static constexpr std::array hall_edge_defaults = + {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f}; struct Config_t { Mode mode = MODE_INCREMENTAL; + float calib_range = 0.02f; // Accuracy required to pass encoder cpr check + float calib_scan_distance = 16.0f * M_PI; // rad electrical + float calib_scan_omega = 4.0f * M_PI; // rad/s electrical + float bandwidth = 1000.0f; + int32_t phase_offset = 0; // Offset between encoder count and rotor electrical phase + float phase_offset_float = 0.0f; // Sub-count phase alignment offset + int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, + float index_offset = 0.0f; bool use_index = false; bool pre_calibrated = false; // If true, this means the offset stored in // configuration is valid and does not need // be determined by run_offset_calibration. // In this case the encoder will enter ready // state as soon as the index is found. - bool zero_count_on_find_idx = true; - int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, - int32_t offset = 0; // Offset between encoder count and rotor electrical phase - float offset_float = 0.0f; // Sub-count phase alignment offset + int32_t direction = 0; // direction with respect to motor + bool use_index_offset = true; bool enable_phase_interpolation = true; // Use velocity to interpolate inside the count state - float calib_range = 0.02f; // Accuracy required to pass encoder cpr check - float calib_scan_distance = 16.0f * M_PI; // rad electrical - float calib_scan_omega = 4.0f * M_PI; // rad/s electrical - float bandwidth = 1000.0f; bool find_idx_on_lockin_only = false; // Only be sensitive during lockin scan constant vel state - bool idx_search_unidirectional = false; // Only allow index search in known direction bool ignore_illegal_hall_state = false; // dont error on bad states like 000 or 111 + uint8_t hall_polarity = 0; + bool hall_polarity_calibrated = false; + std::array hall_edge_phcnt = hall_edge_defaults; uint16_t abs_spi_cs_gpio_pin = 1; uint16_t sincos_gpio_pin_sin = 3; uint16_t sincos_gpio_pin_cos = 4; + // custom setters Encoder* parent = nullptr; void set_use_index(bool value) { use_index = value; parent->set_idx_subscribe(); } @@ -43,9 +54,11 @@ class Encoder : public ODriveIntf::EncoderIntf { void set_bandwidth(float value) { bandwidth = value; parent->update_pll_gains(); } }; - Encoder(const EncoderHardwareConfig_t& hw_config, - Config_t& config, const Motor::Config_t& motor_config); + Encoder(TIM_HandleTypeDef* timer, Stm32Gpio index_gpio, + Stm32Gpio hallA_gpio, Stm32Gpio hallB_gpio, Stm32Gpio hallC_gpio, + Stm32SpiArbiter* spi_arbiter); + bool apply_config(ODriveIntf::MotorIntf::MotorType motor_type); void setup(); void set_error(Error error); bool do_checks(); @@ -61,23 +74,36 @@ class Encoder : public ODriveIntf::EncoderIntf { bool run_index_search(); bool run_direction_find(); + bool run_hall_polarity_calibration(); + bool run_hall_phase_calibration(); bool run_offset_calibration(); void sample_now(); + bool read_sampled_gpio(Stm32Gpio gpio); + void decode_hall_samples(); + int32_t hall_model(float internal_pos); bool update(); - const EncoderHardwareConfig_t& hw_config_; - Config_t& config_; + TIM_HandleTypeDef* timer_; + Stm32Gpio index_gpio_; + Stm32Gpio hallA_gpio_; + Stm32Gpio hallB_gpio_; + Stm32Gpio hallC_gpio_; + Stm32SpiArbiter* spi_arbiter_; Axis* axis_ = nullptr; // set by Axis constructor + Config_t config_; + Error error_ = ERROR_NONE; bool index_found_ = false; bool is_ready_ = false; int32_t shadow_count_ = 0; int32_t count_in_cpr_ = 0; float interpolation_ = 0.0f; - float phase_ = 0.0f; // [count] + OutputPort phase_ = 0.0f; // [rad] + OutputPort phase_vel_ = 0.0f; // [rad/s] float pos_estimate_counts_ = 0.0f; // [count] float pos_cpr_counts_ = 0.0f; // [count] + float delta_pos_cpr_counts_ = 0.0f; // [count] phase detector result for debug float vel_estimate_counts_ = 0.0f; // [count/s] float pll_kp_ = 0.0f; // [count/s / count] float pll_ki_ = 0.0f; // [(count/s^2) / count] @@ -85,36 +111,44 @@ class Encoder : public ODriveIntf::EncoderIntf { int32_t pos_abs_ = 0; float spi_error_rate_ = 0.0f; - float pos_estimate_ = 0.0f; // [turn] - float vel_estimate_ = 0.0f; // [turn/s] - float pos_cpr_ = 0.0f; // [turn] - float pos_circular_ = 0.0f; // [turn] + OutputPort pos_estimate_ = 0.0f; // [turn] + OutputPort vel_estimate_ = 0.0f; // [turn/s] + OutputPort pos_circular_ = 0.0f; // [turn] bool pos_estimate_valid_ = false; bool vel_estimate_valid_ = false; int16_t tim_cnt_sample_ = 0; // + static const constexpr GPIO_TypeDef* ports_to_sample[] = { GPIOA, GPIOB, GPIOC }; + uint16_t port_samples_[sizeof(ports_to_sample) / sizeof(ports_to_sample[0])]; // Updated by low_level pwm_adc_cb uint8_t hall_state_ = 0x0; // bit[0] = HallA, .., bit[2] = HallC + std::optional last_hall_cnt_ = std::nullopt; // Used to find hall edges for calibration + bool calibrate_hall_phase_ = false; + bool sample_hall_states_ = false; + bool sample_hall_phase_ = false; + std::array states_seen_count_; // for hall polarity calibration + std::array hall_phase_calib_seen_count_; + float sincos_sample_s_ = 0.0f; float sincos_sample_c_ = 0.0f; - bool abs_spi_init(); bool abs_spi_start_transaction(); - void abs_spi_cb(); + void abs_spi_cb(bool success); void abs_spi_cs_pin_init(); - uint16_t abs_spi_dma_tx_[1] = {0xFFFF}; - uint16_t abs_spi_dma_rx_[1]; bool abs_spi_pos_updated_ = false; Mode mode_ = MODE_INCREMENTAL; - GPIO_TypeDef* abs_spi_cs_port_; - uint16_t abs_spi_cs_pin_; + Stm32Gpio abs_spi_cs_gpio_; uint32_t abs_spi_cr1; uint32_t abs_spi_cr2; + uint16_t abs_spi_dma_tx_[1] = {0xFFFF}; + uint16_t abs_spi_dma_rx_[1]; + Stm32SpiArbiter::SpiTask spi_task_; constexpr float getCoggingRatio(){ return 1.0f / 3600.0f; } + }; #endif // __ENCODER_HPP diff --git a/Firmware/MotorControl/endstop.cpp b/Firmware/MotorControl/endstop.cpp index 7febba169..2e5b93329 100644 --- a/Firmware/MotorControl/endstop.cpp +++ b/Firmware/MotorControl/endstop.cpp @@ -1,20 +1,13 @@ #include -Endstop::Endstop(Endstop::Config_t& config) - : config_(config) { - update_config(); - debounceTimer_.setIncrement(current_meas_period); -} - void Endstop::update() { debounceTimer_.update(); + last_state_ = endstop_state_; if (config_.enabled) { bool last_pin_state = pin_state_; - uint16_t gpio_pin = get_gpio_pin_by_pin(config_.gpio_num); - GPIO_TypeDef* gpio_port = get_gpio_port_by_pin(config_.gpio_num); - pin_state_ = HAL_GPIO_ReadPin(gpio_port, gpio_pin); + pin_state_ = get_gpio(config_.gpio_num).read(); // If the pin state has changed, reset the timer if (pin_state_ != last_pin_state) @@ -27,29 +20,13 @@ void Endstop::update() { } } -bool Endstop::get_state() { - return endstop_state_; -} - -void Endstop::update_config() { - set_enabled(config_.enabled); - debounceTimer_.setIncrement(config_.debounce_ms * 0.001f); -} - -void Endstop::set_enabled(bool enable) { +bool Endstop::apply_config() { debounceTimer_.reset(); - if (config_.gpio_num != 0) { - uint16_t gpio_pin = get_gpio_pin_by_pin(config_.gpio_num); - GPIO_TypeDef* gpio_port = get_gpio_port_by_pin(config_.gpio_num); - if (enable) { - HAL_GPIO_DeInit(gpio_port, gpio_pin); - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.Pin = gpio_pin; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = config_.pullup ? GPIO_PULLUP : GPIO_PULLDOWN; - HAL_GPIO_Init(gpio_port, &GPIO_InitStruct); - debounceTimer_.start(); - } else - debounceTimer_.stop(); + if (config_.enabled) { + debounceTimer_.start(); + } else { + debounceTimer_.stop(); } -} \ No newline at end of file + debounceTimer_.setIncrement(config_.debounce_ms * 0.001f); + return true; +} diff --git a/Firmware/MotorControl/endstop.hpp b/Firmware/MotorControl/endstop.hpp index f108dffe8..5126f55cc 100644 --- a/Firmware/MotorControl/endstop.hpp +++ b/Firmware/MotorControl/endstop.hpp @@ -10,29 +10,38 @@ class Endstop { uint16_t gpio_num = 0; bool enabled = false; bool is_active_high = false; - bool pullup = true; // custom setters Endstop* parent = nullptr; - void set_gpio_num(uint16_t value) { gpio_num = value; parent->update_config(); } - void set_enabled(uint32_t value) { enabled = value; parent->update_config(); } - void set_debounce_ms(uint32_t value) { debounce_ms = value; parent->update_config(); } + void set_gpio_num(uint16_t value) { gpio_num = value; parent->apply_config(); } + void set_enabled(uint32_t value) { enabled = value; parent->apply_config(); } + void set_debounce_ms(uint32_t value) { debounce_ms = value; parent->apply_config(); } }; - explicit Endstop(Endstop::Config_t& config); + Endstop() {} - Endstop::Config_t& config_; + Endstop::Config_t config_; Axis* axis_ = nullptr; - void update_config(); - void set_enabled(bool enabled); + bool apply_config(); void update(); - bool get_state(); + constexpr bool get_state(){ + return endstop_state_; + } + + constexpr bool rose(){ + return (endstop_state_ != last_state_) && endstop_state_; + } + + constexpr bool fell(){ + return (endstop_state_ != last_state_) && !endstop_state_; + } bool endstop_state_ = false; private: + bool last_state_ = false; bool pin_state_ = false; float pos_when_pressed_ = 0.0f; Timer debounceTimer_; diff --git a/Firmware/MotorControl/foc.cpp b/Firmware/MotorControl/foc.cpp new file mode 100644 index 000000000..46fc8e000 --- /dev/null +++ b/Firmware/MotorControl/foc.cpp @@ -0,0 +1,193 @@ + +#include "foc.hpp" +#include + +Motor::Error AlphaBetaFrameController::on_measurement( + std::optional vbus_voltage, + std::optional> currents, + uint32_t input_timestamp) { + + std::optional Ialpha_beta; + + if (currents.has_value()) { + // Clarke transform + Ialpha_beta = { + (*currents)[0], + one_by_sqrt3 * ((*currents)[1] - (*currents)[2]) + }; + } + + return on_measurement(vbus_voltage, Ialpha_beta, input_timestamp); +} + +Motor::Error AlphaBetaFrameController::get_output( + uint32_t output_timestamp, float (&pwm_timings)[3], + std::optional* ibus) { + std::optional mod_alpha_beta; + Motor::Error status = get_alpha_beta_output(output_timestamp, &mod_alpha_beta, ibus); + + if (status != Motor::ERROR_NONE) { + return status; + } else if (!mod_alpha_beta.has_value() || is_nan(mod_alpha_beta->first) || is_nan(mod_alpha_beta->second)) { + return Motor::ERROR_MODULATION_IS_NAN; + } + + auto [tA, tB, tC, success] = SVM(mod_alpha_beta->first, mod_alpha_beta->second); + if (!success) { + return Motor::ERROR_MODULATION_MAGNITUDE; + } + + pwm_timings[0] = tA; + pwm_timings[1] = tB; + pwm_timings[2] = tC; + + return Motor::ERROR_NONE; +} + +void FieldOrientedController::reset() { + v_current_control_integral_d_ = 0.0f; + v_current_control_integral_q_ = 0.0f; + vbus_voltage_measured_ = std::nullopt; + Ialpha_beta_measured_ = std::nullopt; + power_ = 0.0f; +} + +Motor::Error FieldOrientedController::on_measurement( + std::optional vbus_voltage, std::optional Ialpha_beta, + uint32_t input_timestamp) { + // Store the measurements for later processing. + i_timestamp_ = input_timestamp; + vbus_voltage_measured_ = vbus_voltage; + Ialpha_beta_measured_ = Ialpha_beta; + + return Motor::ERROR_NONE; +} + +ODriveIntf::MotorIntf::Error FieldOrientedController::get_alpha_beta_output( + uint32_t output_timestamp, std::optional* mod_alpha_beta, + std::optional* ibus) { + + if (!vbus_voltage_measured_.has_value() || !Ialpha_beta_measured_.has_value()) { + // FOC didn't receive a current measurement yet. + return Motor::ERROR_CONTROLLER_INITIALIZING; + } else if (abs((int32_t)(i_timestamp_ - ctrl_timestamp_)) > MAX_CONTROL_LOOP_UPDATE_TO_CURRENT_UPDATE_DELTA) { + // Data from control loop and current measurement are too far apart. + return Motor::ERROR_BAD_TIMING; + } + + // TODO: improve efficiency in case PWM updates are requested at a higher + // rate than current sensor updates. In this case we can reuse mod_d and + // mod_q from a previous iteration. + + if (!Vdq_setpoint_.has_value()) { + return Motor::ERROR_UNKNOWN_VOLTAGE_COMMAND; + } else if (!phase_.has_value() || !phase_vel_.has_value()) { + return Motor::ERROR_UNKNOWN_PHASE_ESTIMATE; + } else if (!vbus_voltage_measured_.has_value()) { + return Motor::ERROR_UNKNOWN_VBUS_VOLTAGE; + } + + auto [Vd, Vq] = *Vdq_setpoint_; + float phase = *phase_; + float phase_vel = *phase_vel_; + float vbus_voltage = *vbus_voltage_measured_; + + std::optional Idq; + + // Park transform + if (Ialpha_beta_measured_.has_value()) { + auto [Ialpha, Ibeta] = *Ialpha_beta_measured_; + float I_phase = phase + phase_vel * ((float)(int32_t)(i_timestamp_ - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ); + float c_I = our_arm_cos_f32(I_phase); + float s_I = our_arm_sin_f32(I_phase); + Idq = { + c_I * Ialpha + s_I * Ibeta, + c_I * Ibeta - s_I * Ialpha + }; + Id_measured_ += I_measured_report_filter_k_ * (Idq->first - Id_measured_); + Iq_measured_ += I_measured_report_filter_k_ * (Idq->second - Iq_measured_); + } else { + Id_measured_ = 0.0f; + Iq_measured_ = 0.0f; + } + + + float mod_to_V = (2.0f / 3.0f) * vbus_voltage; + float V_to_mod = 1.0f / mod_to_V; + float mod_d; + float mod_q; + + if (enable_current_control_) { + // Current control mode + + if (!pi_gains_.has_value()) { + return Motor::ERROR_UNKNOWN_GAINS; + } else if (!Idq.has_value()) { + return Motor::ERROR_UNKNOWN_CURRENT_MEASUREMENT; + } else if (!Idq_setpoint_.has_value()) { + return Motor::ERROR_UNKNOWN_CURRENT_COMMAND; + } + + auto [p_gain, i_gain] = *pi_gains_; + auto [Id, Iq] = *Idq; + auto [Id_setpoint, Iq_setpoint] = *Idq_setpoint_; + + float Ierr_d = Id_setpoint - Id; + float Ierr_q = Iq_setpoint - Iq; + + // Apply PI control (V{d,q}_setpoint act as feed-forward terms in this mode) + mod_d = V_to_mod * (Vd + v_current_control_integral_d_ + Ierr_d * p_gain); + mod_q = V_to_mod * (Vq + v_current_control_integral_q_ + Ierr_q * p_gain); + + // Vector modulation saturation, lock integrator if saturated + // TODO make maximum modulation configurable + float mod_scalefactor = 0.80f * sqrt3_by_2 * 1.0f / std::sqrt(mod_d * mod_d + mod_q * mod_q); + if (mod_scalefactor < 1.0f) { + mod_d *= mod_scalefactor; + mod_q *= mod_scalefactor; + // TODO make decayfactor configurable + v_current_control_integral_d_ *= 0.99f; + v_current_control_integral_q_ *= 0.99f; + } else { + v_current_control_integral_d_ += Ierr_d * (i_gain * current_meas_period); + v_current_control_integral_q_ += Ierr_q * (i_gain * current_meas_period); + } + + } else { + // Voltage control mode + mod_d = V_to_mod * Vd; + mod_q = V_to_mod * Vq; + } + + // Inverse park transform + float pwm_phase = phase + phase_vel * ((float)(int32_t)(output_timestamp - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ); + float c_p = our_arm_cos_f32(pwm_phase); + float s_p = our_arm_sin_f32(pwm_phase); + float mod_alpha = c_p * mod_d - s_p * mod_q; + float mod_beta = c_p * mod_q + s_p * mod_d; + + // Report final applied voltage in stationary frame (for sensorless estimator) + final_v_alpha_ = mod_to_V * mod_alpha; + final_v_beta_ = mod_to_V * mod_beta; + + *mod_alpha_beta = {mod_alpha, mod_beta}; + + if (Idq.has_value()) { + auto [Id, Iq] = *Idq; + *ibus = mod_d * Id + mod_q * Iq; + power_ = vbus_voltage * (*ibus).value(); + } + + return Motor::ERROR_NONE; +} + +void FieldOrientedController::update(uint32_t timestamp) { + CRITICAL_SECTION() { + ctrl_timestamp_ = timestamp; + enable_current_control_ = enable_current_control_src_; + Idq_setpoint_ = Idq_setpoint_src_.present(); + Vdq_setpoint_ = Vdq_setpoint_src_.present(); + phase_ = phase_src_.present(); + phase_vel_ = phase_vel_src_.present(); + } +} diff --git a/Firmware/MotorControl/foc.hpp b/Firmware/MotorControl/foc.hpp new file mode 100644 index 000000000..12936f70d --- /dev/null +++ b/Firmware/MotorControl/foc.hpp @@ -0,0 +1,66 @@ +#ifndef __FOC_HPP +#define __FOC_HPP + +#include "phase_control_law.hpp" +#include "component.hpp" + +/** + * @brief Field oriented controller. + * + * This controller can run in either current control mode or voltage control + * mode. + */ +class FieldOrientedController : public AlphaBetaFrameController, public ComponentBase { +public: + void update(uint32_t timestamp) final; + + void reset() final; + + ODriveIntf::MotorIntf::Error on_measurement( + std::optional vbus_voltage, + std::optional Ialpha_beta, + uint32_t input_timestamp) final; + + ODriveIntf::MotorIntf::Error get_alpha_beta_output( + uint32_t output_timestamp, + std::optional* mod_alpha_beta, + std::optional* ibus) final; + + // Config - these values are set while this controller is inactive + std::optional pi_gains_; // [V/A, V/As] should be auto set after resistance and inductance measurement + float I_measured_report_filter_k_ = 1.0f; + + // Inputs + bool enable_current_control_src_ = false; + InputPort Idq_setpoint_src_; + InputPort Vdq_setpoint_src_; + InputPort phase_src_; + InputPort phase_vel_src_; + + // These values are set atomically by the update() function and read by the + // calculate() function in an interrupt context. + uint32_t ctrl_timestamp_; // [HCLK ticks] + bool enable_current_control_ = false; // true: FOC runs in current control mode using I{dq}_setpoint, false: FOC runs in voltage control mode using V{dq}_setpoint + std::optional Idq_setpoint_; // [A] only used if enable_current_control_ == true + std::optional Vdq_setpoint_; // [V] feed-forward voltage term (or standalone setpoint if enable_current_control_ == false) + std::optional phase_; // [rad] + std::optional phase_vel_; // [rad/s] + + // These values (or some of them) are updated inside on_measurement() and get_alpha_beta_output() + uint32_t i_timestamp_; + std::optional vbus_voltage_measured_; // [V] + std::optional Ialpha_beta_measured_; // [A, A] + float Id_measured_; // [A] + float Iq_measured_; // [A] + float v_current_control_integral_d_ = 0.0f; // [V] + float v_current_control_integral_q_ = 0.0f; // [V] + //float mod_to_V_ = 0.0f; + //float mod_d_ = 0.0f; + //float mod_q_ = 0.0f; + //float ibus_ = 0.0f; + float final_v_alpha_ = 0.0f; // [V] + float final_v_beta_ = 0.0f; // [V] + float power_ = 0.0f; // [W] dot product of Vdq and Idq +}; + +#endif // __FOC_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/gpio_utils.hpp b/Firmware/MotorControl/gpio_utils.hpp deleted file mode 100644 index fb70f46ed..000000000 --- a/Firmware/MotorControl/gpio_utils.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "gpio.h" -constexpr GPIO_TypeDef* get_gpio_port_by_pin(uint16_t GPIO_pin){ - switch(GPIO_pin){ - case 1: return GPIO_1_GPIO_Port; break; - case 2: return GPIO_2_GPIO_Port; break; - case 3: return GPIO_3_GPIO_Port; break; - case 4: return GPIO_4_GPIO_Port; break; -#ifdef GPIO_5_GPIO_Port - case 5: return GPIO_5_GPIO_Port; break; -#endif -#ifdef GPIO_6_GPIO_Port - case 6: return GPIO_6_GPIO_Port; break; -#endif -#ifdef GPIO_7_GPIO_Port - case 7: return GPIO_7_GPIO_Port; break; -#endif -#ifdef GPIO_8_GPIO_Port - case 8: return GPIO_8_GPIO_Port; break; -#endif - default: return GPIO_1_GPIO_Port; - } -} - -constexpr uint16_t get_gpio_pin_by_pin(uint16_t GPIO_pin){ - switch(GPIO_pin){ - case 1: return GPIO_1_Pin; break; - case 2: return GPIO_2_Pin; break; - case 3: return GPIO_3_Pin; break; - case 4: return GPIO_4_Pin; break; -#ifdef GPIO_5_Pin - case 5: return GPIO_5_Pin; break; -#endif -#ifdef GPIO_6_Pin - case 6: return GPIO_6_Pin; break; -#endif -#ifdef GPIO_7_Pin - case 7: return GPIO_7_Pin; break; -#endif -#ifdef GPIO_8_Pin - case 8: return GPIO_8_Pin; break; -#endif - default: return GPIO_1_Pin; - } -} diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index e8b996e5d..aee91b8f5 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -1,14 +1,9 @@ /* Includes ------------------------------------------------------------------*/ -// Because of broken cmsis_os.h, we need to include arm_math first, -// otherwise chip specific defines are ommited -#include -#include // Sets up the correct chip specifc defines required by arm_math -#define ARM_MATH_CM4 -#include +#include #include -#include +#include #include #include @@ -39,12 +34,6 @@ float ibus_ = 0.0f; // exposed for monitoring only bool brake_resistor_armed = false; bool brake_resistor_saturated = false; /* Private constant data -----------------------------------------------------*/ -static const GPIO_TypeDef* GPIOs_to_samp[] = { GPIOA, GPIOB, GPIOC }; -static const int num_GPIO = sizeof(GPIOs_to_samp) / sizeof(GPIOs_to_samp[0]); -/* Private variables ---------------------------------------------------------*/ - -// Two motors, sampling port A,B,C (coherent with current meas timing) -static uint16_t GPIO_port_samples [2][num_GPIO]; /* CPU critical section helpers ----------------------------------------------*/ /* Safety critical functions -------------------------------------------------*/ @@ -83,84 +72,19 @@ static uint16_t GPIO_port_samples [2][num_GPIO]; * at a high rate. */ -// @brief Floats ALL phases immediately and disarms both motors and the brake resistor. -void low_level_fault(Motor::Error error) { - // Disable all motors NOW! - for (size_t i = 0; i < AXIS_COUNT; ++i) { - safety_critical_disarm_motor_pwm(axes[i]->motor_); - axes[i]->motor_.error_ |= error; - } - - safety_critical_disarm_brake_resistor(); -} - -// @brief Kicks off the arming process of the motor. -// All calls to this function must clearly originate -// from user input. -void safety_critical_arm_motor_pwm(Motor& motor) { - uint32_t mask = cpu_enter_critical(); - if (brake_resistor_armed) { - motor.armed_state_ = Motor::ARMED_STATE_WAITING_FOR_TIMINGS; - } - cpu_exit_critical(mask); -} - -// @brief Disarms the motor PWM. -// After calling this function, it is guaranteed that all three -// motor phases are floating and will not be enabled again until -// safety_critical_arm_motor_phases is called. -// @returns true if the motor was in a state other than disarmed before -bool safety_critical_disarm_motor_pwm(Motor& motor) { - uint32_t mask = cpu_enter_critical(); - bool was_armed = motor.armed_state_ != Motor::ARMED_STATE_DISARMED; - motor.armed_state_ = Motor::ARMED_STATE_DISARMED; - __HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(motor.hw_config_.timer); - cpu_exit_critical(mask); - return was_armed; -} - -// @brief Updates the phase timings unless the motor is disarmed. -// -// If this is called at a rate higher than the motor's timer period, -// the actual PMW timings on the pins can be undefined for up to one -// timer period. -void safety_critical_apply_motor_pwm_timings(Motor& motor, uint16_t timings[3]) { - uint32_t mask = cpu_enter_critical(); - if (!brake_resistor_armed) { - motor.armed_state_ = Motor::ARMED_STATE_DISARMED; - } - - motor.hw_config_.timer->Instance->CCR1 = timings[0]; - motor.hw_config_.timer->Instance->CCR2 = timings[1]; - motor.hw_config_.timer->Instance->CCR3 = timings[2]; - - if (motor.armed_state_ == Motor::ARMED_STATE_WAITING_FOR_TIMINGS) { - // timings were just loaded into the timer registers - // the timer register are buffered, so they won't have an effect - // on the output just yet so we need to wait until the next - // interrupt before we actually enable the output - motor.armed_state_ = Motor::ARMED_STATE_WAITING_FOR_UPDATE; - } else if (motor.armed_state_ == Motor::ARMED_STATE_WAITING_FOR_UPDATE) { - // now we waited long enough. Enter armed state and - // enable the actual PWM outputs. - motor.armed_state_ = Motor::ARMED_STATE_ARMED; - __HAL_TIM_MOE_ENABLE(motor.hw_config_.timer); // enable pwm outputs - } else if (motor.armed_state_ == Motor::ARMED_STATE_ARMED) { - // nothing to do, PWM is running, all good - } else { - // unknown state oh no - safety_critical_disarm_motor_pwm(motor); - } - cpu_exit_critical(mask); -} // @brief Arms the brake resistor void safety_critical_arm_brake_resistor() { - uint32_t mask = cpu_enter_critical(); - brake_resistor_armed = true; - htim2.Instance->CCR3 = 0; - htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; - cpu_exit_critical(mask); + CRITICAL_SECTION() { + for (size_t i = 0; i < AXIS_COUNT; ++i) { + axes[i].motor_.I_bus_ = 0.0f; + } + brake_resistor_armed = true; +#if HW_VERSION_MAJOR == 3 + htim2.Instance->CCR3 = 0; + htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; +#endif + } } // @brief Disarms the brake resistor and by extension @@ -168,164 +92,92 @@ void safety_critical_arm_brake_resistor() { // After calling this, the brake resistor can only be armed again // by calling safety_critical_arm_brake_resistor(). void safety_critical_disarm_brake_resistor() { - uint32_t mask = cpu_enter_critical(); - brake_resistor_armed = false; - htim2.Instance->CCR3 = 0; - htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; - for (size_t i = 0; i < AXIS_COUNT; ++i) { - safety_critical_disarm_motor_pwm(axes[i]->motor_); + bool brake_resistor_was_armed = brake_resistor_armed; + + CRITICAL_SECTION() { + brake_resistor_armed = false; +#if HW_VERSION_MAJOR == 3 + htim2.Instance->CCR3 = 0; + htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; +#endif + } + + // Check necessary to prevent infinite recursion + if (brake_resistor_was_armed) { + for (auto& axis: axes) { + axis.motor_.disarm(); + } } - cpu_exit_critical(mask); } // @brief Updates the brake resistor PWM timings unless // the brake resistor is disarmed. void safety_critical_apply_brake_resistor_timings(uint32_t low_off, uint32_t high_on) { - if (high_on - low_off < TIM_APB1_DEADTIME_CLOCKS) - low_level_fault(Motor::ERROR_BRAKE_DEADTIME_VIOLATION); - uint32_t mask = cpu_enter_critical(); - if (brake_resistor_armed) { - // Safe update of low and high side timings - // To avoid race condition, first reset timings to safe state - // ch3 is low side, ch4 is high side - htim2.Instance->CCR3 = 0; - htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; - htim2.Instance->CCR3 = low_off; - htim2.Instance->CCR4 = high_on; + if (high_on - low_off < TIM_APB1_DEADTIME_CLOCKS) { + odrv.disarm_with_error(ODrive::ERROR_BRAKE_DEADTIME_VIOLATION); + } + + CRITICAL_SECTION() { + if (brake_resistor_armed) { +#if HW_VERSION_MAJOR == 3 + // Safe update of low and high side timings + // To avoid race condition, first reset timings to safe state + // ch3 is low side, ch4 is high side + htim2.Instance->CCR3 = 0; + htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; + htim2.Instance->CCR3 = low_off; + htim2.Instance->CCR4 = high_on; +#endif + } } - cpu_exit_critical(mask); } /* Function implementations --------------------------------------------------*/ void start_adc_pwm() { + // Disarm motors + for (auto& axis: axes) { + axis.motor_.disarm(); + } + + for (Motor& motor: motors) { + // Init PWM + int half_load = TIM_1_8_PERIOD_CLOCKS / 2; + motor.timer_->Instance->CCR1 = half_load; + motor.timer_->Instance->CCR2 = half_load; + motor.timer_->Instance->CCR3 = half_load; + + // Enable PWM outputs (they are still masked by MOE though) + motor.timer_->Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_1); + motor.timer_->Instance->CCER |= (TIM_CCxN_ENABLE << TIM_CHANNEL_1); + motor.timer_->Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_2); + motor.timer_->Instance->CCER |= (TIM_CCxN_ENABLE << TIM_CHANNEL_2); + motor.timer_->Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_3); + motor.timer_->Instance->CCER |= (TIM_CCxN_ENABLE << TIM_CHANNEL_3); + } + // Enable ADC and interrupts __HAL_ADC_ENABLE(&hadc1); __HAL_ADC_ENABLE(&hadc2); __HAL_ADC_ENABLE(&hadc3); // Warp field stabilize. osDelay(2); - __HAL_ADC_ENABLE_IT(&hadc1, ADC_IT_JEOC); - __HAL_ADC_ENABLE_IT(&hadc2, ADC_IT_JEOC); - __HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_JEOC); - __HAL_ADC_ENABLE_IT(&hadc2, ADC_IT_EOC); - __HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_EOC); - - // Ensure that debug halting of the core doesn't leave the motor PWM running - __HAL_DBGMCU_FREEZE_TIM1(); - __HAL_DBGMCU_FREEZE_TIM8(); - __HAL_DBGMCU_FREEZE_TIM13(); - - start_pwm(&htim1); - start_pwm(&htim8); - // TODO: explain why this offset - sync_timers(&htim1, &htim8, TIM_CLOCKSOURCE_ITR0, TIM_1_8_PERIOD_CLOCKS / 2 - 1 * 128, - &htim13); - - // Motor output starts in the disabled state - __HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(&htim1); - __HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(&htim8); - - // Enable the update interrupt (used to coherently sample GPIO) - __HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE); - __HAL_TIM_ENABLE_IT(&htim8, TIM_IT_UPDATE); + + + start_timers(); + // Start brake resistor PWM in floating output configuration +#if HW_VERSION_MAJOR == 3 htim2.Instance->CCR3 = 0; htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4); +#endif - // Disarm motors and arm brake resistor - for (size_t i = 0; i < AXIS_COUNT; ++i) { - safety_critical_disarm_motor_pwm(axes[i]->motor_); - } - safety_critical_arm_brake_resistor(); -} - -void start_pwm(TIM_HandleTypeDef* htim) { - // Init PWM - int half_load = TIM_1_8_PERIOD_CLOCKS / 2; - htim->Instance->CCR1 = half_load; - htim->Instance->CCR2 = half_load; - htim->Instance->CCR3 = half_load; - - // This hardware obfustication layer really is getting on my nerves - HAL_TIM_PWM_Start(htim, TIM_CHANNEL_1); - HAL_TIMEx_PWMN_Start(htim, TIM_CHANNEL_1); - HAL_TIM_PWM_Start(htim, TIM_CHANNEL_2); - HAL_TIMEx_PWMN_Start(htim, TIM_CHANNEL_2); - HAL_TIM_PWM_Start(htim, TIM_CHANNEL_3); - HAL_TIMEx_PWMN_Start(htim, TIM_CHANNEL_3); - - htim->Instance->CCR4 = 1; - HAL_TIM_PWM_Start_IT(htim, TIM_CHANNEL_4); -} - -/* - * Initial intention of this function: - * Synchronize TIM1, TIM8 and TIM13 such that: - * 1. The triangle waveform of TIM1 leads the triangle waveform of TIM8 by a - * 90° phase shift. - * 2. The timer update events of TIM1 and TIM8 are symmetrically interleaved. - * 3. Each TIM13 reload coincides with a TIM1 lower update event. - * - * However right now this function only ensures point (1) and (3) but because - * TIM1 and TIM3 only trigger an update on every third reload, this does not - * imply (or even allow for) (2). - * - * TODO: revisit the timing topic in general. - */ -void sync_timers(TIM_HandleTypeDef* htim_a, TIM_HandleTypeDef* htim_b, - uint16_t TIM_CLOCKSOURCE_ITRx, uint16_t count_offset, - TIM_HandleTypeDef* htim_refbase) { - // Store intial timer configs - uint16_t MOE_store_a = htim_a->Instance->BDTR & (TIM_BDTR_MOE); - uint16_t MOE_store_b = htim_b->Instance->BDTR & (TIM_BDTR_MOE); - uint16_t CR2_store = htim_a->Instance->CR2; - uint16_t SMCR_store = htim_b->Instance->SMCR; - // Turn off output - htim_a->Instance->BDTR &= ~(TIM_BDTR_MOE); - htim_b->Instance->BDTR &= ~(TIM_BDTR_MOE); - // Disable both timer counters - htim_a->Instance->CR1 &= ~TIM_CR1_CEN; - htim_b->Instance->CR1 &= ~TIM_CR1_CEN; - // Set first timer to send TRGO on counter enable - htim_a->Instance->CR2 &= ~TIM_CR2_MMS; - htim_a->Instance->CR2 |= TIM_TRGO_ENABLE; - // Set Trigger Source of second timer to the TRGO of the first timer - htim_b->Instance->SMCR &= ~TIM_SMCR_TS; - htim_b->Instance->SMCR |= TIM_CLOCKSOURCE_ITRx; - // Set 2nd timer to start on trigger - htim_b->Instance->SMCR &= ~TIM_SMCR_SMS; - htim_b->Instance->SMCR |= TIM_SLAVEMODE_TRIGGER; - // Dir bit is read only in center aligned mode, so we clear the mode for now - uint16_t CMS_store_a = htim_a->Instance->CR1 & TIM_CR1_CMS; - uint16_t CMS_store_b = htim_b->Instance->CR1 & TIM_CR1_CMS; - htim_a->Instance->CR1 &= ~TIM_CR1_CMS; - htim_b->Instance->CR1 &= ~TIM_CR1_CMS; - // Set both timers to up-counting state - htim_a->Instance->CR1 &= ~TIM_CR1_DIR; - htim_b->Instance->CR1 &= ~TIM_CR1_DIR; - // Restore center aligned mode - htim_a->Instance->CR1 |= CMS_store_a; - htim_b->Instance->CR1 |= CMS_store_b; - // set counter offset - htim_a->Instance->CNT = count_offset; - htim_b->Instance->CNT = 0; - // Set and start reference timebase timer (if used) - if (htim_refbase) { - htim_refbase->Instance->CNT = count_offset; - htim_refbase->Instance->CR1 |= (TIM_CR1_CEN); // start + if (odrv.config_.enable_brake_resistor) { + safety_critical_arm_brake_resistor(); } - // Start Timer a - htim_a->Instance->CR1 |= (TIM_CR1_CEN); - // Restore timer configs - htim_a->Instance->CR2 = CR2_store; - htim_b->Instance->SMCR = SMCR_store; - // restore output - htim_a->Instance->BDTR |= MOE_store_a; - htim_b->Instance->BDTR |= MOE_store_b; } // @brief ADC1 measurements are written to this buffer by DMA @@ -356,9 +208,9 @@ void start_general_purpose_adc() { hadc1.Init.NbrOfConversion = ADC_CHANNEL_COUNT; hadc1.Init.DMAContinuousRequests = ENABLE; hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; - if (HAL_ADC_Init(&hadc1) != HAL_OK) - { - _Error_Handler((char*)__FILE__, __LINE__); + if (HAL_ADC_Init(&hadc1) != HAL_OK) { + odrv.misconfigured_ = true; // TODO: this is a bit of an abuse of this flag + return; } // Set up sampling sequence (channel 0 ... channel 15) @@ -366,17 +218,19 @@ void start_general_purpose_adc() { for (uint32_t channel = 0; channel < ADC_CHANNEL_COUNT; ++channel) { sConfig.Channel = channel << ADC_CR1_AWDCH_Pos; sConfig.Rank = channel + 1; // rank numbering starts at 1 - if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) - _Error_Handler((char*)__FILE__, __LINE__); + if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { + odrv.misconfigured_ = true; // TODO: this is a bit of an abuse of this flag + return; + } } HAL_ADC_Start_DMA(&hadc1, reinterpret_cast(adc_measurements_), ADC_CHANNEL_COUNT); } // @brief Returns the ADC voltage associated with the specified pin. -// GPIO_set_to_analog() must be called first to put the Pin into -// analog mode. -// Returns NaN if the pin has no associated ADC1 channel. +// This only works if the GPIO was not used for anything else since bootup, otherwise +// it must be put to analog mode first. +// Returns -1.0f if the pin has no associated ADC1 channel. // // On ODrive 3.3 and 3.4 the following pins can be used with this function: // GPIO_1, GPIO_2, GPIO_3, GPIO_4 and some pins that are connected to @@ -390,275 +244,130 @@ void start_general_purpose_adc() { // 21000kHz / (15+26) / 16 = 32kHz // The true frequency is slightly lower because of the injected vbus // measurements -float get_adc_voltage(const GPIO_TypeDef* const GPIO_port, uint16_t GPIO_pin) { - const uint16_t channel = channel_from_gpio(GPIO_port, GPIO_pin); - return get_adc_voltage_channel(channel); +float get_adc_voltage(Stm32Gpio gpio) { + return get_adc_relative_voltage(gpio) * adc_ref_voltage; +} + +float get_adc_relative_voltage(Stm32Gpio gpio) { + const uint16_t channel = channel_from_gpio(gpio); + return get_adc_relative_voltage_ch(channel); } // @brief Given a GPIO_port and pin return the associated adc_channel. // returns UINT16_MAX if there is no adc_channel; -uint16_t channel_from_gpio(const GPIO_TypeDef* const GPIO_port, uint16_t GPIO_pin) -{ - uint16_t channel = UINT16_MAX; - if (GPIO_port == GPIOA) { - if (GPIO_pin == GPIO_PIN_0) +uint16_t channel_from_gpio(Stm32Gpio gpio) { + uint32_t channel = UINT32_MAX; + if (gpio.port_ == GPIOA) { + if (gpio.pin_mask_ == GPIO_PIN_0) channel = 0; - else if (GPIO_pin == GPIO_PIN_1) + else if (gpio.pin_mask_ == GPIO_PIN_1) channel = 1; - else if (GPIO_pin == GPIO_PIN_2) + else if (gpio.pin_mask_ == GPIO_PIN_2) channel = 2; - else if (GPIO_pin == GPIO_PIN_3) + else if (gpio.pin_mask_ == GPIO_PIN_3) channel = 3; - else if (GPIO_pin == GPIO_PIN_4) + else if (gpio.pin_mask_ == GPIO_PIN_4) channel = 4; - else if (GPIO_pin == GPIO_PIN_5) + else if (gpio.pin_mask_ == GPIO_PIN_5) channel = 5; - else if (GPIO_pin == GPIO_PIN_6) + else if (gpio.pin_mask_ == GPIO_PIN_6) channel = 6; - else if (GPIO_pin == GPIO_PIN_7) + else if (gpio.pin_mask_ == GPIO_PIN_7) channel = 7; - } else if (GPIO_port == GPIOB) { - if (GPIO_pin == GPIO_PIN_0) + } else if (gpio.port_ == GPIOB) { + if (gpio.pin_mask_ == GPIO_PIN_0) channel = 8; - else if (GPIO_pin == GPIO_PIN_1) + else if (gpio.pin_mask_ == GPIO_PIN_1) channel = 9; - } else if (GPIO_port == GPIOC) { - if (GPIO_pin == GPIO_PIN_0) + } else if (gpio.port_ == GPIOC) { + if (gpio.pin_mask_ == GPIO_PIN_0) channel = 10; - else if (GPIO_pin == GPIO_PIN_1) + else if (gpio.pin_mask_ == GPIO_PIN_1) channel = 11; - else if (GPIO_pin == GPIO_PIN_2) + else if (gpio.pin_mask_ == GPIO_PIN_2) channel = 12; - else if (GPIO_pin == GPIO_PIN_3) + else if (gpio.pin_mask_ == GPIO_PIN_3) channel = 13; - else if (GPIO_pin == GPIO_PIN_4) + else if (gpio.pin_mask_ == GPIO_PIN_4) channel = 14; - else if (GPIO_pin == GPIO_PIN_5) + else if (gpio.pin_mask_ == GPIO_PIN_5) channel = 15; } return channel; } -// @brief Given an adc channel return the measured voltage. -// returns NaN if the channel is not valid. -float get_adc_voltage_channel(uint16_t channel) -{ +// @brief Given an adc channel return the voltage as a ratio of adc_ref_voltage +// returns -1.0f if the channel is not valid. +float get_adc_relative_voltage_ch(uint16_t channel) { if (channel < ADC_CHANNEL_COUNT) - return ((float)adc_measurements_[channel]) * (adc_ref_voltage / adc_full_scale); + return (float)adc_measurements_[channel] / adc_full_scale; else - return 0.0f / 0.0f; // NaN + return -1.0f; } //-------------------------------- // IRQ Callbacks //-------------------------------- -void vbus_sense_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { +void vbus_sense_adc_cb(uint32_t adc_value) { constexpr float voltage_scale = adc_ref_voltage * VBUS_S_DIVIDER_RATIO / adc_full_scale; - // Only one conversion in sequence, so only rank1 - uint32_t ADCValue = HAL_ADCEx_InjectedGetValue(hadc, ADC_INJECTED_RANK_1); - vbus_voltage = ADCValue * voltage_scale; + vbus_voltage = adc_value * voltage_scale; } -static void decode_hall_samples(Encoder& enc, uint16_t GPIO_samples[num_GPIO]) { - GPIO_TypeDef* hall_ports[] = { - enc.hw_config_.hallC_port, - enc.hw_config_.hallB_port, - enc.hw_config_.hallA_port, - }; - uint16_t hall_pins[] = { - enc.hw_config_.hallC_pin, - enc.hw_config_.hallB_pin, - enc.hw_config_.hallA_pin, - }; - - uint8_t hall_state = 0x0; - for (int i = 0; i < 3; ++i) { - int port_idx = 0; - for (;;) { - auto port = GPIOs_to_samp[port_idx]; - if (port == hall_ports[i]) - break; - ++port_idx; +// @brief Sums up the Ibus contribution of each motor and updates the +// brake resistor PWM accordingly. +void update_brake_current() { + float Ibus_sum = 0.0f; + for (size_t i = 0; i < AXIS_COUNT; ++i) { + if (axes[i].motor_.is_armed_) { + Ibus_sum += axes[i].motor_.I_bus_; } - - hall_state <<= 1; - hall_state |= (GPIO_samples[port_idx] & hall_pins[i]) ? 1 : 0; } - enc.hall_state_ = hall_state; -} - -// This is the callback from the ADC that we expect after the PWM has triggered an ADC conversion. -// Timing diagram: Firmware/timing_diagram_v3.png -void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { -#define calib_tau 0.2f //@TOTO make more easily configurable - constexpr float calib_filter_k = CURRENT_MEAS_PERIOD / calib_tau; + float brake_duty; - // Ensure ADCs are expected ones to simplify the logic below - if (!(hadc == &hadc2 || hadc == &hadc3)) { - low_level_fault(Motor::ERROR_ADC_FAILED); - return; - }; - - // Motor 0 is on Timer 1, which triggers ADC 2 and 3 on an injected conversion - // Motor 1 is on Timer 8, which triggers ADC 2 and 3 on a regular conversion - // If the corresponding timer is counting up, we just sampled in SVM vector 0, i.e. real current - // If we are counting down, we just sampled in SVM vector 7, with zero current - Axis& axis = injected ? *axes[0] : *axes[1]; - int axis_num = injected ? 0 : 1; - Axis& other_axis = injected ? *axes[1] : *axes[0]; - bool counting_down = axis.motor_.hw_config_.timer->Instance->CR1 & TIM_CR1_DIR; - bool current_meas_not_DC_CAL = !counting_down; - - // Check the timing of the sequencing - if (current_meas_not_DC_CAL) - axis.motor_.log_timing(TIMING_LOG_ADC_CB_I); - else - axis.motor_.log_timing(TIMING_LOG_ADC_CB_DC); - - bool update_timings = false; - if (hadc == &hadc2) { - if (&axis == axes[1] && counting_down) - update_timings = true; // update timings of M0 - else if (&axis == axes[0] && !counting_down) - update_timings = true; // update timings of M1 - - // TODO: this is out of place here. However when moving it somewhere - // else we have to consider the timing requirements to prevent the SPI - // transfers of axis0 and axis1 from conflicting. - // Also see comment on sync_timers. - if((current_meas_not_DC_CAL && !axis_num) || - (axis_num && !current_meas_not_DC_CAL)){ - axis.encoder_.abs_spi_start_transaction(); + if (odrv.config_.enable_brake_resistor) { + if (!(odrv.config_.brake_resistance > 0.0f)) { + odrv.disarm_with_error(ODrive::ERROR_INVALID_BRAKE_RESISTANCE); + return; } - } - - // Load next timings for the motor that we're not currently sampling - if (update_timings) { - if (!other_axis.motor_.next_timings_valid_) { - // the motor control loop failed to update the timings in time - // we must assume that it died and therefore float all phases - bool was_armed = safety_critical_disarm_motor_pwm(other_axis.motor_); - if (was_armed) { - other_axis.motor_.error_ |= Motor::ERROR_CONTROL_DEADLINE_MISSED; - } - } else { - other_axis.motor_.next_timings_valid_ = false; - safety_critical_apply_motor_pwm_timings( - other_axis.motor_, other_axis.motor_.next_timings_ - ); + + // Don't start braking until -Ibus > regen_current_allowed + float brake_current = -Ibus_sum - odrv.config_.max_regen_current; + brake_duty = brake_current * odrv.config_.brake_resistance / vbus_voltage; + + if (odrv.config_.enable_dc_bus_overvoltage_ramp && (odrv.config_.brake_resistance > 0.0f) && (odrv.config_.dc_bus_overvoltage_ramp_start < odrv.config_.dc_bus_overvoltage_ramp_end)) { + brake_duty += std::max((vbus_voltage - odrv.config_.dc_bus_overvoltage_ramp_start) / (odrv.config_.dc_bus_overvoltage_ramp_end - odrv.config_.dc_bus_overvoltage_ramp_start), 0.0f); } - update_brake_current(); - } - uint32_t ADCValue; - if (injected) { - ADCValue = HAL_ADCEx_InjectedGetValue(hadc, ADC_INJECTED_RANK_1); - } else { - ADCValue = HAL_ADC_GetValue(hadc); - } - float current = axis.motor_.phase_current_from_adcval(ADCValue); - - if (current_meas_not_DC_CAL) { - // ADC2 and ADC3 record the phB and phC currents concurrently, - // and their interrupts should arrive on the same clock cycle. - // We dispatch the callbacks in order, so ADC2 will always be processed before ADC3. - // Therefore we store the value from ADC2 and signal the thread that the - // measurement is ready when we receive the ADC3 measurement - - // return or continue - if (hadc == &hadc2) { - axis.motor_.current_meas_.phB = current - axis.motor_.DC_calib_.phB; + if (is_nan(brake_duty)) { + // Shuts off all motors AND brake resistor, sets error code on all motors. + odrv.disarm_with_error(ODrive::ERROR_BRAKE_DUTY_CYCLE_NAN); return; - } else { - axis.motor_.current_meas_.phC = current - axis.motor_.DC_calib_.phC; - } - // Prepare hall readings - // TODO move this to inside encoder update function - decode_hall_samples(axis.encoder_, GPIO_port_samples[axis_num]); - // Trigger axis thread - axis.signal_current_meas(); - } else { - // DC_CAL measurement - if (hadc == &hadc2) { - axis.motor_.DC_calib_.phB += (current - axis.motor_.DC_calib_.phB) * calib_filter_k; - } else { - axis.motor_.DC_calib_.phC += (current - axis.motor_.DC_calib_.phC) * calib_filter_k; } - } -} - -void tim_update_cb(TIM_HandleTypeDef* htim) { - - // If the corresponding timer is counting up, we just sampled in SVM vector 0, i.e. real current - // If we are counting down, we just sampled in SVM vector 7, with zero current - bool counting_down = htim->Instance->CR1 & TIM_CR1_DIR; - if (counting_down) - return; - - int sample_ch; - Axis* axis; - if (htim == &htim1) { - sample_ch = 0; - axis = axes[0]; - } else if (htim == &htim8) { - sample_ch = 1; - axis = axes[1]; - } else { - low_level_fault(Motor::ERROR_UNEXPECTED_TIMER_CALLBACK); - return; - } - axis->encoder_.sample_now(); - - for (int i = 0; i < num_GPIO; ++i) { - GPIO_port_samples[sample_ch][i] = GPIOs_to_samp[i]->IDR; - } -} - -// @brief Sums up the Ibus contribution of each motor and updates the -// brake resistor PWM accordingly. -void update_brake_current() { - float Ibus_sum = 0.0f; - for (size_t i = 0; i < AXIS_COUNT; ++i) { - if (axes[i]->motor_.armed_state_ == Motor::ARMED_STATE_ARMED) { - Ibus_sum += axes[i]->motor_.current_control_.Ibus; + if (brake_duty >= 0.95f) { + brake_resistor_saturated = true; } - } - - // Don't start braking until -Ibus > regen_current_allowed - float brake_current = -Ibus_sum - odrv.config_.max_regen_current; - float brake_duty = brake_current * odrv.config_.brake_resistance / vbus_voltage; - - if (odrv.config_.enable_dc_bus_overvoltage_ramp && (odrv.config_.brake_resistance > 0.0f) && (odrv.config_.dc_bus_overvoltage_ramp_start < odrv.config_.dc_bus_overvoltage_ramp_end)) { - brake_duty += std::fmax((vbus_voltage - odrv.config_.dc_bus_overvoltage_ramp_start) / (odrv.config_.dc_bus_overvoltage_ramp_end - odrv.config_.dc_bus_overvoltage_ramp_start), 0.0f); - } - if (std::isnan(brake_duty)) { - // Shuts off all motors AND brake resistor, sets error code on all motors. - low_level_fault(Motor::ERROR_BRAKE_DUTY_CYCLE_NAN); - return; - } + // Duty limit at 95% to allow bootstrap caps to charge + brake_duty = std::clamp(brake_duty, 0.0f, 0.95f); - if (brake_duty >= 0.95f) { - brake_resistor_saturated = true; + // This cannot result in NaN (safe for race conditions) because we check + // brake_resistance != 0 further up. + Ibus_sum += brake_duty * vbus_voltage / odrv.config_.brake_resistance; + } else { + brake_duty = 0; } - // Duty limit at 95% to allow bootstrap caps to charge - brake_duty = std::clamp(brake_duty, 0.0f, 0.95f); - - // Special handling to avoid the case 0.0/0.0 == NaN. - Ibus_sum += brake_duty ? (brake_duty * vbus_voltage / odrv.config_.brake_resistance) : 0.0f; - ibus_ += odrv.ibus_report_filter_k_ * (Ibus_sum - ibus_); if (Ibus_sum > odrv.config_.dc_max_positive_current) { - low_level_fault(Motor::ERROR_DC_BUS_OVER_CURRENT); + odrv.disarm_with_error(ODrive::ERROR_DC_BUS_OVER_CURRENT); return; } if (Ibus_sum < odrv.config_.dc_max_negative_current) { - low_level_fault(Motor::ERROR_DC_BUS_OVER_REGEN_CURRENT); + odrv.disarm_with_error(ODrive::ERROR_DC_BUS_OVER_REGEN_CURRENT); return; } @@ -669,127 +378,11 @@ void update_brake_current() { } -/* RC PWM input --------------------------------------------------------------*/ - -// @brief Returns the ODrive GPIO number for a given -// TIM2 or TIM5 input capture channel number. -int tim_2_5_channel_num_to_gpio_num(int channel) { -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 - if (channel >= 1 && channel <= 4) { - // the channel numbers just happen to coincide with - // the GPIO numbers - return channel; - } else { - return -1; - } -#else - // Only ch4 is available on v3.2 - if (channel == 4) { - return 4; - } else { - return -1; - } -#endif -} -// @brief Returns the TIM2 or TIM5 channel number -// for a given GPIO number. -uint32_t gpio_num_to_tim_2_5_channel(int gpio_num) { -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 - switch (gpio_num) { - case 1: return TIM_CHANNEL_1; - case 2: return TIM_CHANNEL_2; - case 3: return TIM_CHANNEL_3; - case 4: return TIM_CHANNEL_4; - default: return 0; - } -#else - // Only ch4 is available on v3.2 - if (gpio_num == 4) { - return TIM_CHANNEL_4; - } else { - return 0; - } -#endif -} - -void pwm_in_init() { - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_PULLDOWN; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = GPIO_AF2_TIM5; - - TIM_IC_InitTypeDef sConfigIC; - sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE; - sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; - sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; - sConfigIC.ICFilter = 15; - -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 - for (int gpio_num = 1; gpio_num <= 4; ++gpio_num) { -#else - int gpio_num = 4; { -#endif - if (fibre::is_endpoint_ref_valid(odrv.config_.pwm_mappings[gpio_num - 1].endpoint)) { - GPIO_InitStruct.Pin = get_gpio_pin_by_pin(gpio_num); - HAL_GPIO_DeInit(get_gpio_port_by_pin(gpio_num), get_gpio_pin_by_pin(gpio_num)); - HAL_GPIO_Init(get_gpio_port_by_pin(gpio_num), &GPIO_InitStruct); - HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, gpio_num_to_tim_2_5_channel(gpio_num)); - HAL_TIM_IC_Start_IT(&htim5, gpio_num_to_tim_2_5_channel(gpio_num)); - } - } -} - -//TODO: These expressions have integer division by 1MHz, so it will be incorrect for clock speeds of not-integer MHz -#define TIM_2_5_CLOCK_HZ TIM_APB1_CLOCK_HZ -#define PWM_MIN_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 1000UL) // 1ms high is considered full reverse -#define PWM_MAX_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2000UL) // 2ms high is considered full forward -#define PWM_MIN_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 500UL) // ignore high periods shorter than 0.5ms -#define PWM_MAX_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2500UL) // ignore high periods longer than 2.5ms -#define PWM_INVERT_INPUT false - -void handle_pulse(int gpio_num, uint32_t high_time) { - if (high_time < PWM_MIN_LEGAL_HIGH_TIME || high_time > PWM_MAX_LEGAL_HIGH_TIME) - return; - - if (high_time < PWM_MIN_HIGH_TIME) - high_time = PWM_MIN_HIGH_TIME; - if (high_time > PWM_MAX_HIGH_TIME) - high_time = PWM_MAX_HIGH_TIME; - float fraction = (float)(high_time - PWM_MIN_HIGH_TIME) / (float)(PWM_MAX_HIGH_TIME - PWM_MIN_HIGH_TIME); - float value = odrv.config_.pwm_mappings[gpio_num - 1].min + - (fraction * (odrv.config_.pwm_mappings[gpio_num - 1].max - odrv.config_.pwm_mappings[gpio_num - 1].min)); - - fibre::set_endpoint_from_float(odrv.config_.pwm_mappings[gpio_num - 1].endpoint, value); -} - -void pwm_in_cb(int channel, uint32_t timestamp) { - static uint32_t last_timestamp[GPIO_COUNT] = { 0 }; - static bool last_pin_state[GPIO_COUNT] = { false }; - static bool last_sample_valid[GPIO_COUNT] = { false }; - - int gpio_num = tim_2_5_channel_num_to_gpio_num(channel); - if (gpio_num < 1 || gpio_num > GPIO_COUNT) - return; - bool current_pin_state = HAL_GPIO_ReadPin(get_gpio_port_by_pin(gpio_num), get_gpio_pin_by_pin(gpio_num)) != GPIO_PIN_RESET; - - if (last_sample_valid[gpio_num - 1] - && (last_pin_state[gpio_num - 1] != PWM_INVERT_INPUT) - && (current_pin_state == PWM_INVERT_INPUT)) { - handle_pulse(gpio_num, timestamp - last_timestamp[gpio_num - 1]); - } - - last_timestamp[gpio_num - 1] = timestamp; - last_pin_state[gpio_num - 1] = current_pin_state; - last_sample_valid[gpio_num - 1] = true; -} - - /* Analog speed control input */ static void update_analog_endpoint(const struct PWMMapping_t *map, int gpio) { - float fraction = get_adc_voltage(get_gpio_port_by_pin(gpio), get_gpio_pin_by_pin(gpio)) / 3.3f; + float fraction = get_adc_voltage(get_gpio(gpio)) / 3.3f; float value = map->min + (fraction * (map->max - map->min)); fibre::set_endpoint_from_float(map->endpoint, value); } @@ -801,7 +394,7 @@ static void analog_polling_thread(void *) struct PWMMapping_t *map = &odrv.config_.analog_mappings[i]; if (fibre::is_endpoint_ref_valid(map->endpoint)) - update_analog_endpoint(map, i + 1); + update_analog_endpoint(map, i); } osDelay(10); } @@ -811,12 +404,3 @@ void start_analog_thread() { osThreadDef(thread_def, analog_polling_thread, osPriorityLow, 0, 512 / sizeof(StackType_t)); osThreadCreate(osThread(thread_def), NULL); } - - -void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) -{ - if(hspi->pRxBuffPtr == (uint8_t*)axes[0]->encoder_.abs_spi_dma_rx_) - axes[0]->encoder_.abs_spi_cb(); - else if (hspi->pRxBuffPtr == (uint8_t*)axes[1]->encoder_.abs_spi_dma_rx_) - axes[1]->encoder_.abs_spi_cb(); -} diff --git a/Firmware/MotorControl/low_level.h b/Firmware/MotorControl/low_level.h index 11494cbf5..e648ce37f 100644 --- a/Firmware/MotorControl/low_level.h +++ b/Firmware/MotorControl/low_level.h @@ -2,10 +2,6 @@ #ifndef __LOW_LEVEL_H #define __LOW_LEVEL_H -#ifndef __ODRIVE_MAIN_H -#error "This file should not be included directly. Include odrive_main.h instead." -#endif - #ifdef __cplusplus extern "C" { #endif @@ -29,19 +25,14 @@ extern uint16_t adc_measurements_[ADC_CHANNEL_COUNT]; /* Exported macro ------------------------------------------------------------*/ /* Exported functions --------------------------------------------------------*/ -void safety_critical_arm_motor_pwm(Motor& motor); -bool safety_critical_disarm_motor_pwm(Motor& motor); -void safety_critical_apply_motor_pwm_timings(Motor& motor, uint16_t timings[3]); void safety_critical_arm_brake_resistor(); void safety_critical_disarm_brake_resistor(); void safety_critical_apply_brake_resistor_timings(uint32_t low_off, uint32_t high_on); // called from STM platform code extern "C" { -void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected); -void vbus_sense_adc_cb(ADC_HandleTypeDef* hadc, bool injected); -void tim_update_cb(TIM_HandleTypeDef* htim); -void pwm_in_cb(int channel, uint32_t timestamp); +void vbus_sense_adc_cb(uint32_t adc_value); +void pwm_in_cb(TIM_HandleTypeDef *htim); } // Initalisation @@ -51,23 +42,16 @@ void sync_timers(TIM_HandleTypeDef* htim_a, TIM_HandleTypeDef* htim_b, uint16_t TIM_CLOCKSOURCE_ITRx, uint16_t count_offset, TIM_HandleTypeDef* htim_refbase = nullptr); void start_general_purpose_adc(); -float get_adc_voltage(const GPIO_TypeDef* const GPIO_port, uint16_t GPIO_pin); -uint16_t channel_from_gpio(const GPIO_TypeDef* const GPIO_port, uint16_t GPIO_pin); -float get_adc_voltage_channel(uint16_t channel); void pwm_in_init(); void start_analog_thread(); -void update_brake_current(); +// ADC getters +uint16_t channel_from_gpio(Stm32Gpio gpio); +float get_adc_voltage(Stm32Gpio gpio); +float get_adc_relative_voltage(Stm32Gpio gpio); +float get_adc_relative_voltage_ch(uint16_t channel); -inline uint32_t cpu_enter_critical() { - uint32_t primask = __get_PRIMASK(); - __disable_irq(); - return primask; -} - -inline void cpu_exit_critical(uint32_t priority_mask) { - __set_PRIMASK(priority_mask); -} +void update_brake_current(); #ifdef __cplusplus } diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 78bc9b370..1c7753119 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -5,100 +5,182 @@ #include "usart.h" #include "freertos_vars.h" +#include "usb_device.h" #include #include #include #include -ODriveCAN::Config_t can_config; -Encoder::Config_t encoder_configs[AXIS_COUNT]; -SensorlessEstimator::Config_t sensorless_configs[AXIS_COUNT]; -Controller::Config_t controller_configs[AXIS_COUNT]; -Motor::Config_t motor_configs[AXIS_COUNT]; -OnboardThermistorCurrentLimiter::Config_t fet_thermistor_configs[AXIS_COUNT]; -OffboardThermistorCurrentLimiter::Config_t motor_thermistor_configs[AXIS_COUNT]; -Axis::Config_t axis_configs[AXIS_COUNT]; -TrapezoidalTrajectory::Config_t trap_configs[AXIS_COUNT]; -Endstop::Config_t min_endstop_configs[AXIS_COUNT]; -Endstop::Config_t max_endstop_configs[AXIS_COUNT]; - -std::array axes; -ODriveCAN *odCAN = nullptr; +osSemaphoreId sem_usb_irq; +osMessageQId uart_event_queue; +osMessageQId usb_event_queue; +osSemaphoreId sem_can; + +#if defined(STM32F405xx) +// Place FreeRTOS heap in core coupled memory for better performance +__attribute__((section(".ccmram"))) +#endif +uint8_t ucHeap[configTOTAL_HEAP_SIZE]; + +uint32_t _reboot_cookie __attribute__ ((section (".noinit"))); +extern char _estack; // provided by the linker script + + ODrive odrv{}; -typedef Config< - BoardConfig_t, - ODriveCAN::Config_t, - Encoder::Config_t[AXIS_COUNT], - SensorlessEstimator::Config_t[AXIS_COUNT], - Controller::Config_t[AXIS_COUNT], - Motor::Config_t[AXIS_COUNT], - OnboardThermistorCurrentLimiter::Config_t[AXIS_COUNT], - OffboardThermistorCurrentLimiter::Config_t[AXIS_COUNT], - TrapezoidalTrajectory::Config_t[AXIS_COUNT], - Endstop::Config_t[AXIS_COUNT], - Endstop::Config_t[AXIS_COUNT], - Axis::Config_t[AXIS_COUNT]> ConfigFormat; - -void ODrive::save_configuration(void) { - if (ConfigFormat::safe_store_config( - &odrv.config_, - &can_config, - &encoder_configs, - &sensorless_configs, - &controller_configs, - &motor_configs, - &fet_thermistor_configs, - &motor_thermistor_configs, - &trap_configs, - &min_endstop_configs, - &max_endstop_configs, - &axis_configs)) { - printf("saving configuration failed\r\n"); osDelay(5); + +ConfigManager config_manager; + +class StatusLedController { +public: + void update(); +}; + +StatusLedController status_led_controller; + +void StatusLedController::update() { +#if HW_VERSION_MAJOR == 4 + uint32_t t = HAL_GetTick(); + + bool is_booting = std::any_of(axes.begin(), axes.end(), [](Axis& axis){ + return axis.current_state_ == Axis::AXIS_STATE_UNDEFINED; + }); + + if (is_booting) { + return; + } + + bool is_armed = std::any_of(axes.begin(), axes.end(), [](Axis& axis){ + return axis.motor_.is_armed_; + }); + bool any_error = odrv.any_error(); + + if (is_armed) { + // Fast green pulsating + const uint32_t period_ms = 256; + const uint8_t min_brightness = 0; + const uint8_t max_brightness = 255; + uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; + brightness = (brightness * brightness) >> 8; // eye response very roughly sqrt + status_led.set_color(rgb_t{(uint8_t)(any_error ? brightness / 2 : 0), (uint8_t)brightness, 0}); + } else if (any_error) { + // Red pulsating + const uint32_t period_ms = 1024; + const uint8_t min_brightness = 0; + const uint8_t max_brightness = 255; + uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; + brightness = (brightness * brightness) >> 8; // eye response very roughly sqrt + status_led.set_color(rgb_t{(uint8_t)brightness, 0, 0}); } else { - odrv.user_config_loaded_ = true; + // Slow blue pulsating + const uint32_t period_ms = 4096; + const uint8_t min_brightness = 50; + const uint8_t max_brightness = 160; + uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; + brightness = (brightness * brightness) >> 8; // eye response very roughly sqrt + status_led.set_color(rgb_t{0, 0, (uint8_t)brightness}); } +#endif } -extern "C" int load_configuration(void) { - // Try to load configs - if (NVM_init() || - ConfigFormat::safe_load_config( - &odrv.config_, - &can_config, - &encoder_configs, - &sensorless_configs, - &controller_configs, - &motor_configs, - &fet_thermistor_configs, - &motor_thermistor_configs, - &trap_configs, - &min_endstop_configs, - &max_endstop_configs, - &axis_configs)) { - //If loading failed, restore defaults - odrv.config_ = BoardConfig_t(); - can_config = ODriveCAN::Config_t(); - for (size_t i = 0; i < AXIS_COUNT; ++i) { - encoder_configs[i] = Encoder::Config_t(); - sensorless_configs[i] = SensorlessEstimator::Config_t(); - controller_configs[i] = Controller::Config_t(); - motor_configs[i] = Motor::Config_t(); - fet_thermistor_configs[i] = OnboardThermistorCurrentLimiter::Config_t(); - motor_thermistor_configs[i] = OffboardThermistorCurrentLimiter::Config_t(); - trap_configs[i] = TrapezoidalTrajectory::Config_t(); - axis_configs[i] = Axis::Config_t(); - // Default step/dir pins are different, so we need to explicitly load them - Axis::load_default_step_dir_pin_config(hw_configs[i].axis_config, &axis_configs[i]); - Axis::load_default_can_id(i, axis_configs[i]); - min_endstop_configs[i] = Endstop::Config_t(); - max_endstop_configs[i] = Endstop::Config_t(); - controller_configs[i].load_encoder_axis = i; +static bool config_read_all() { + bool success = board_read_config() && + config_manager.read(&odrv.config_) && + config_manager.read(&odrv.can_.config_); + for (size_t i = 0; (i < AXIS_COUNT) && success; ++i) { + success = config_manager.read(&encoders[i].config_) && + config_manager.read(&axes[i].sensorless_estimator_.config_) && + config_manager.read(&axes[i].controller_.config_) && + config_manager.read(&axes[i].trap_traj_.config_) && + config_manager.read(&axes[i].min_endstop_.config_) && + config_manager.read(&axes[i].max_endstop_.config_) && + config_manager.read(&axes[i].mechanical_brake_.config_) && + config_manager.read(&motors[i].config_) && + config_manager.read(&motors[i].fet_thermistor_.config_) && + config_manager.read(&motors[i].motor_thermistor_.config_) && + config_manager.read(&axes[i].config_); + } + return success; +} + +static bool config_write_all() { + bool success = board_write_config() && + config_manager.write(&odrv.config_) && + config_manager.write(&odrv.can_.config_); + for (size_t i = 0; (i < AXIS_COUNT) && success; ++i) { + success = config_manager.write(&encoders[i].config_) && + config_manager.write(&axes[i].sensorless_estimator_.config_) && + config_manager.write(&axes[i].controller_.config_) && + config_manager.write(&axes[i].trap_traj_.config_) && + config_manager.write(&axes[i].min_endstop_.config_) && + config_manager.write(&axes[i].max_endstop_.config_) && + config_manager.write(&axes[i].mechanical_brake_.config_) && + config_manager.write(&motors[i].config_) && + config_manager.write(&motors[i].fet_thermistor_.config_) && + config_manager.write(&motors[i].motor_thermistor_.config_) && + config_manager.write(&axes[i].config_); + } + return success; +} + +static void config_clear_all() { + odrv.config_ = {}; + odrv.can_.config_ = {}; + for (size_t i = 0; i < AXIS_COUNT; ++i) { + encoders[i].config_ = {}; + axes[i].sensorless_estimator_.config_ = {}; + axes[i].controller_.config_ = {}; + axes[i].controller_.config_.load_encoder_axis = i; + axes[i].trap_traj_.config_ = {}; + axes[i].min_endstop_.config_ = {}; + axes[i].max_endstop_.config_ = {}; + axes[i].mechanical_brake_.config_ = {}; + motors[i].config_ = {}; + motors[i].fet_thermistor_.config_ = {}; + motors[i].motor_thermistor_.config_ = {}; + axes[i].clear_config(); + } +} + +static bool config_apply_all() { + bool success = odrv.can_.apply_config(); + for (size_t i = 0; (i < AXIS_COUNT) && success; ++i) { + success = encoders[i].apply_config(motors[i].config_.motor_type) + && axes[i].controller_.apply_config() + && axes[i].min_endstop_.apply_config() + && axes[i].max_endstop_.apply_config() + && motors[i].apply_config() + && motors[i].motor_thermistor_.apply_config() + && axes[i].apply_config(); + } + return success; +} + +bool ODrive::save_configuration(void) { + bool success; + + CRITICAL_SECTION() { + bool any_armed = std::any_of(axes.begin(), axes.end(), + [](auto& axis){ return axis.motor_.is_armed_; }); + if (any_armed) { + return false; } - } else { - odrv.user_config_loaded_ = true; + + size_t config_size = 0; + success = config_manager.prepare_store() + && config_write_all() + && config_manager.start_store(&config_size) + && config_write_all() + && config_manager.finish_store(); + + // FIXME: during save_configuration we might miss some interrupts + // because the CPU gets halted during a flash erase. Missing events + // (encoder updates, step/dir steps) is not good so to be sure we just + // reboot. + NVIC_SystemReset(); } - return odrv.user_config_loaded_; + + return success; } void ODrive::erase_configuration(void) { @@ -130,180 +212,627 @@ void ODrive::enter_dfu_mode() { } } -extern "C" int construct_objects(){ -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 - if (odrv.config_.enable_i2c_instead_of_can) { - // Set up the direction GPIO as input - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = GPIO_PULLUP; - - GPIO_InitStruct.Pin = I2C_A0_PIN; - HAL_GPIO_Init(I2C_A0_PORT, &GPIO_InitStruct); - GPIO_InitStruct.Pin = I2C_A1_PIN; - HAL_GPIO_Init(I2C_A1_PORT, &GPIO_InitStruct); - GPIO_InitStruct.Pin = I2C_A2_PIN; - HAL_GPIO_Init(I2C_A2_PORT, &GPIO_InitStruct); +bool ODrive::any_error() { + return error_ != ODrive::ERROR_NONE + || std::any_of(axes.begin(), axes.end(), [](Axis& axis){ + return axis.error_ != Axis::ERROR_NONE + || axis.motor_.error_ != Motor::ERROR_NONE + || axis.sensorless_estimator_.error_ != SensorlessEstimator::ERROR_NONE + || axis.encoder_.error_ != Encoder::ERROR_NONE + || axis.controller_.error_ != Controller::ERROR_NONE; + }); +} - osDelay(1); - i2c_stats_.addr = (0xD << 3); - i2c_stats_.addr |= HAL_GPIO_ReadPin(I2C_A0_PORT, I2C_A0_PIN) != GPIO_PIN_RESET ? 0x1 : 0; - i2c_stats_.addr |= HAL_GPIO_ReadPin(I2C_A1_PORT, I2C_A1_PIN) != GPIO_PIN_RESET ? 0x2 : 0; - i2c_stats_.addr |= HAL_GPIO_ReadPin(I2C_A2_PORT, I2C_A2_PIN) != GPIO_PIN_RESET ? 0x4 : 0; - MX_I2C1_Init(i2c_stats_.addr); - } else -#endif - MX_CAN1_Init(); - - HAL_UART_DeInit(&huart4); - huart4.Init.BaudRate = odrv.config_.uart_baudrate; - HAL_UART_Init(&huart4); - - // Init general user ADC on some GPIOs. - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Pin = GPIO_1_Pin; - HAL_GPIO_Init(GPIO_1_GPIO_Port, &GPIO_InitStruct); - GPIO_InitStruct.Pin = GPIO_2_Pin; - HAL_GPIO_Init(GPIO_2_GPIO_Port, &GPIO_InitStruct); - GPIO_InitStruct.Pin = GPIO_3_Pin; - HAL_GPIO_Init(GPIO_3_GPIO_Port, &GPIO_InitStruct); - GPIO_InitStruct.Pin = GPIO_4_Pin; - HAL_GPIO_Init(GPIO_4_GPIO_Port, &GPIO_InitStruct); -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 5 - GPIO_InitStruct.Pin = GPIO_5_Pin; - HAL_GPIO_Init(GPIO_5_GPIO_Port, &GPIO_InitStruct); +uint64_t ODrive::get_drv_fault() { +#if AXIS_COUNT == 1 + return motors[0].gate_driver_.get_error(); +#elif AXIS_COUNT == 2 + return (uint64_t)motors[0].gate_driver_.get_error() | ((uint64_t)motors[1].gate_driver_.get_error() << 32ULL); +#else + #error "not supported" #endif +} - // Construct all objects. - odCAN = new ODriveCAN(can_config, &hcan1); - for (size_t i = 0; i < AXIS_COUNT; ++i) { - Encoder *encoder = new Encoder(hw_configs[i].encoder_config, - encoder_configs[i], motor_configs[i]); - SensorlessEstimator *sensorless_estimator = new SensorlessEstimator(sensorless_configs[i]); - Controller *controller = new Controller(controller_configs[i]); - - OnboardThermistorCurrentLimiter *fet_thermistor = new OnboardThermistorCurrentLimiter(hw_configs[i].thermistor_config, - fet_thermistor_configs[i]); - OffboardThermistorCurrentLimiter *motor_thermistor = new OffboardThermistorCurrentLimiter(motor_thermistor_configs[i]); - - Motor *motor = new Motor(hw_configs[i].motor_config, - hw_configs[i].gate_driver_config, - motor_configs[i]); - TrapezoidalTrajectory *trap = new TrapezoidalTrajectory(trap_configs[i]); - Endstop *min_endstop = new Endstop(min_endstop_configs[i]); - Endstop *max_endstop = new Endstop(max_endstop_configs[i]); - axes[i] = new Axis(i, hw_configs[i].axis_config, axis_configs[i], - *encoder, *sensorless_estimator, *controller, *fet_thermistor, - *motor_thermistor, *motor, *trap, *min_endstop, *max_endstop); - - controller_configs[i].parent = controller; - encoder_configs[i].parent = encoder; - motor_thermistor_configs[i].parent = motor_thermistor; - motor_configs[i].parent = motor; - min_endstop_configs[i].parent = min_endstop; - max_endstop_configs[i].parent = max_endstop; - axis_configs[i].parent = axes[i]; - } - return 0; +void ODrive::clear_errors() { + for (auto& axis: axes) { + axis.motor_.error_ = Motor::ERROR_NONE; + axis.controller_.error_ = Controller::ERROR_NONE; + axis.sensorless_estimator_.error_ = SensorlessEstimator::ERROR_NONE; + axis.encoder_.error_ = Encoder::ERROR_NONE; + axis.encoder_.spi_error_rate_ = 0.0f; + axis.error_ = Axis::ERROR_NONE; + } + error_ = ERROR_NONE; + if (odrv.config_.enable_brake_resistor) { + safety_critical_arm_brake_resistor(); + } } extern "C" { -int odrive_main(void); + void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed portCHAR *pcTaskName) { - for(auto& axis : axes){ - safety_critical_disarm_motor_pwm(axis->motor_); + for(auto& axis: axes){ + axis.motor_.disarm(); } - safety_critical_disarm_brake_resistor(); + safety_critical_disarm_brake_resistor(); for (;;); // TODO: safe action } + void vApplicationIdleHook(void) { if (odrv.system_stats_.fully_booted) { odrv.system_stats_.uptime = xTaskGetTickCount(); odrv.system_stats_.min_heap_space = xPortGetMinimumEverFreeHeapSize(); - odrv.system_stats_.min_stack_space_comms = uxTaskGetStackHighWaterMark(comm_thread) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_axis0 = uxTaskGetStackHighWaterMark(axes[0]->thread_id_) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_axis1 = uxTaskGetStackHighWaterMark(axes[1]->thread_id_) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_usb = uxTaskGetStackHighWaterMark(usb_thread) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_uart = uxTaskGetStackHighWaterMark(uart_thread) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_usb_irq = uxTaskGetStackHighWaterMark(usb_irq_thread) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_startup = uxTaskGetStackHighWaterMark(defaultTaskHandle) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_can = uxTaskGetStackHighWaterMark(odCAN->thread_id_) * sizeof(StackType_t); - - // Actual usage, in bytes, so we don't have to math - odrv.system_stats_.stack_usage_axis0 = axes[0]->stack_size_ - odrv.system_stats_.min_stack_space_axis0; - odrv.system_stats_.stack_usage_axis1 = axes[1]->stack_size_ - odrv.system_stats_.min_stack_space_axis1; - odrv.system_stats_.stack_usage_comms = stack_size_comm_thread - odrv.system_stats_.min_stack_space_comms; - odrv.system_stats_.stack_usage_usb = stack_size_usb_thread - odrv.system_stats_.min_stack_space_usb; - odrv.system_stats_.stack_usage_uart = stack_size_uart_thread - odrv.system_stats_.min_stack_space_uart; - odrv.system_stats_.stack_usage_usb_irq = stack_size_usb_irq_thread - odrv.system_stats_.min_stack_space_usb_irq; - odrv.system_stats_.stack_usage_startup = stack_size_default_task - odrv.system_stats_.min_stack_space_startup; - odrv.system_stats_.stack_usage_can = odCAN->stack_size_ - odrv.system_stats_.min_stack_space_can; + + uint32_t min_stack_space[AXIS_COUNT]; + std::transform(axes.begin(), axes.end(), std::begin(min_stack_space), [](auto& axis) { return uxTaskGetStackHighWaterMark(axis.thread_id_) * sizeof(StackType_t); }); + odrv.system_stats_.max_stack_usage_axis = axes[0].stack_size_ - *std::min_element(std::begin(min_stack_space), std::end(min_stack_space)); + odrv.system_stats_.max_stack_usage_usb = stack_size_usb_thread - uxTaskGetStackHighWaterMark(usb_thread) * sizeof(StackType_t); + odrv.system_stats_.max_stack_usage_uart = stack_size_uart_thread - uxTaskGetStackHighWaterMark(uart_thread) * sizeof(StackType_t); + odrv.system_stats_.max_stack_usage_startup = stack_size_default_task - uxTaskGetStackHighWaterMark(defaultTaskHandle) * sizeof(StackType_t); + odrv.system_stats_.max_stack_usage_can = odrv.can_.stack_size_ - uxTaskGetStackHighWaterMark(odrv.can_.thread_id_) * sizeof(StackType_t); + + odrv.system_stats_.stack_size_axis = axes[0].stack_size_; + odrv.system_stats_.stack_size_usb = stack_size_usb_thread; + odrv.system_stats_.stack_size_uart = stack_size_uart_thread; + odrv.system_stats_.stack_size_startup = stack_size_default_task; + odrv.system_stats_.stack_size_can = odrv.can_.stack_size_; + + odrv.system_stats_.prio_axis = osThreadGetPriority(axes[0].thread_id_); + odrv.system_stats_.prio_usb = osThreadGetPriority(usb_thread); + odrv.system_stats_.prio_uart = osThreadGetPriority(uart_thread); + odrv.system_stats_.prio_startup = osThreadGetPriority(defaultTaskHandle); + odrv.system_stats_.prio_can = osThreadGetPriority(odrv.can_.thread_id_); + + status_led_controller.update(); + } +} +} + +/** + * @brief Runs system-level checks that need to be as real-time as possible. + * + * This function is called after every current measurement of every motor. + * It should finish as quickly as possible. + */ +void ODrive::do_fast_checks() { + if (!(vbus_voltage >= config_.dc_bus_undervoltage_trip_level)) + disarm_with_error(ERROR_DC_BUS_UNDER_VOLTAGE); + if (!(vbus_voltage <= config_.dc_bus_overvoltage_trip_level)) + disarm_with_error(ERROR_DC_BUS_OVER_VOLTAGE); +} + +/** + * @brief Floats all power phases on the system (all motors and brake resistors). + * + * This should be called if a system level exception ocurred that makes it + * unsafe to run power through the system in general. + */ +void ODrive::disarm_with_error(Error error) { + CRITICAL_SECTION() { + for (auto& axis: axes) { + axis.motor_.disarm_with_error(Motor::ERROR_SYSTEM_LEVEL); + } + safety_critical_disarm_brake_resistor(); + error_ |= error; + } +} + +/** + * @brief Runs the periodic sampling tasks + * + * All components that need to sample real-world data should do it in this + * function as it runs on a high interrupt priority and provides lowest possible + * timing jitter. + * + * All function called from this function should adhere to the following rules: + * - Try to use the same number of CPU cycles in every iteration. + * (reason: Tasks that run later in the function still want lowest possible timing jitter) + * - Use as few cycles as possible. + * (reason: The interrupt blocks other important interrupts (TODO: which ones?)) + * - Not call any FreeRTOS functions. + * (reason: The interrupt priority is higher than the max allowed priority for syscalls) + * + * Time consuming and undeterministic logic/arithmetic should live on + * control_loop_cb() instead. + */ +void ODrive::sampling_cb() { + n_evt_sampling_++; + + MEASURE_TIME(task_times_.sampling) { + for (auto& axis: axes) { + axis.encoder_.sample_now(); + } + } +} + +/** + * @brief Runs the periodic control loop. + * + * This function is executed in a low priority interrupt context and is allowed + * to call CMSIS functions. + * + * Yet it runs at a higher priority than communication workloads. + * + * @param update_cnt: The true count of update events (wrapping around at 16 + * bits). This is used for timestamp calculation in the face of + * potentially missed timer update interrupts. Therefore this counter + * must not rely on any interrupts. + */ +void ODrive::control_loop_cb(uint32_t timestamp) { + last_update_timestamp_ = timestamp; + n_evt_control_loop_++; + + // TODO: use a configurable component list for most of the following things + + MEASURE_TIME(task_times_.control_loop_misc) { + // Reset all output ports so that we are certain about the freshness of + // all values that we use. + // If we forget to reset a value here the worst that can happen is that + // this safety check doesn't work. + // TODO: maybe we should add a check to output ports that prevents + // double-setting the value. + for (auto& axis: axes) { + axis.acim_estimator_.slip_vel_.reset(); + axis.acim_estimator_.stator_phase_vel_.reset(); + axis.acim_estimator_.stator_phase_.reset(); + axis.controller_.torque_output_.reset(); + axis.encoder_.phase_.reset(); + axis.encoder_.phase_vel_.reset(); + axis.encoder_.pos_estimate_.reset(); + axis.encoder_.vel_estimate_.reset(); + axis.encoder_.pos_circular_.reset(); + axis.motor_.Vdq_setpoint_.reset(); + axis.motor_.Idq_setpoint_.reset(); + axis.open_loop_controller_.Idq_setpoint_.reset(); + axis.open_loop_controller_.Vdq_setpoint_.reset(); + axis.open_loop_controller_.phase_.reset(); + axis.open_loop_controller_.phase_vel_.reset(); + axis.open_loop_controller_.total_distance_.reset(); + axis.sensorless_estimator_.phase_.reset(); + axis.sensorless_estimator_.phase_vel_.reset(); + axis.sensorless_estimator_.vel_estimate_.reset(); + } + + uart_poll(); + odrv.oscilloscope_.update(); + } + + MEASURE_TIME(task_times_.control_loop_checks) { + for (auto& axis: axes) { + // look for errors at axis level and also all subcomponents + bool checks_ok = axis.do_checks(timestamp); + + // make sure the watchdog is being fed. + bool watchdog_ok = axis.watchdog_check(); + + if (!checks_ok || !watchdog_ok) { + axis.motor_.disarm(); + } + } + } + + for (auto& axis: axes) { + // Sub-components should use set_error which will propegate to this error_ + MEASURE_TIME(axis.task_times_.thermistor_update) { + axis.motor_.fet_thermistor_.update(); + axis.motor_.motor_thermistor_.update(); + } + + MEASURE_TIME(axis.task_times_.encoder_update) + axis.encoder_.update(); + } + + // Controller of either axis might use the encoder estimate of the other + // axis so we process both encoders before we continue. + + for (auto& axis: axes) { + MEASURE_TIME(axis.task_times_.sensorless_estimator_update) + axis.sensorless_estimator_.update(); + + MEASURE_TIME(axis.task_times_.endstop_update) { + axis.min_endstop_.update(); + axis.max_endstop_.update(); + } + + MEASURE_TIME(axis.task_times_.controller_update) + axis.controller_.update(); // uses position and velocity from encoder + + MEASURE_TIME(axis.task_times_.open_loop_controller_update) + axis.open_loop_controller_.update(timestamp); + + MEASURE_TIME(axis.task_times_.motor_update) + axis.motor_.update(timestamp); // uses torque from controller and phase_vel from encoder + + MEASURE_TIME(axis.task_times_.current_controller_update) + axis.motor_.current_control_.update(timestamp); // uses the output of controller_ or open_loop_contoller_ and encoder_ or sensorless_estimator_ or acim_estimator_ } + + // Tell the axis threads that the control loop has finished + for (auto& axis: axes) { + if (axis.thread_id_) { + osSignalSet(axis.thread_id_, 0x0001); + } + } + + get_gpio(odrv.config_.error_gpio_pin).write(odrv.any_error()); } + + +/** @brief For diagnostics only */ +uint32_t ODrive::get_interrupt_status(int32_t irqn) { + if ((irqn < -14) || (irqn >= 240)) { + return 0xffffffff; + } + + uint8_t priority = (irqn < -12) + ? 0 // hard fault and NMI always have maximum priority + : NVIC_GetPriority((IRQn_Type)irqn); + uint32_t counter = GET_IRQ_COUNTER((IRQn_Type)irqn); + bool is_enabled = (irqn < 0) + ? true // processor interrupt vectors are always enabled + : NVIC->ISER[(((uint32_t)(int32_t)irqn) >> 5UL)] & (uint32_t)(1UL << (((uint32_t)(int32_t)irqn) & 0x1FUL)); + + return priority | ((counter & 0x7ffffff) << 8) | (is_enabled ? 0x80000000 : 0); +} + +/** @brief For diagnostics only */ +uint32_t ODrive::get_dma_status(uint8_t stream_num) { + DMA_Stream_TypeDef* streams[] = { + DMA1_Stream0, DMA1_Stream1, DMA1_Stream2, DMA1_Stream3, DMA1_Stream4, DMA1_Stream5, DMA1_Stream6, DMA1_Stream7, + DMA2_Stream0, DMA2_Stream1, DMA2_Stream2, DMA2_Stream3, DMA2_Stream4, DMA2_Stream5, DMA2_Stream6, DMA2_Stream7 + }; + if (stream_num >= 16) { + return 0xffffffff; + } + DMA_Stream_TypeDef* stream = streams[stream_num]; + bool is_reset = (stream->CR == 0x00000000) + && (stream->NDTR == 0x00000000) + && (stream->PAR == 0x00000000) + && (stream->M0AR == 0x00000000) + && (stream->M1AR == 0x00000000) + && (stream->FCR == 0x00000021); + uint8_t channel = ((stream->CR & DMA_SxCR_CHSEL_Msk) >> DMA_SxCR_CHSEL_Pos); + uint8_t priority = ((stream->CR & DMA_SxCR_PL_Msk) >> DMA_SxCR_PL_Pos); + return (is_reset ? 0 : 0x80000000) | ((channel & 0x7) << 2) | (priority & 0x3); } -int odrive_main(void) { +uint32_t ODrive::get_gpio_states() { + // TODO: get values that were sampled synchronously with the control loop + uint32_t val = 0; + for (size_t i = 0; i < GPIO_COUNT; ++i) { + val |= ((gpios[i].read() ? 1UL : 0UL) << i); + } + return val; +} + +/** + * @brief Main thread started from main(). + */ +static void rtos_main(void*) { + // Init USB device + MX_USB_DEVICE_Init(); + + // Start ADC for temperature measurements and user measurements start_general_purpose_adc(); - // TODO: make dynamically reconfigurable -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 - if (odrv.config_.enable_uart) { - SetGPIO12toUART(); - } -#endif //osDelay(100); // Init communications (this requires the axis objects to be constructed) init_communication(); // Start pwm-in compare modules // must happen after communication is initialized - pwm_in_init(); + pwm0_input.init(); - // Set up the CS pins for absolute encoders + // Set up the CS pins for absolute encoders (TODO: move to GPIO init switch statement) for(auto& axis : axes){ - if(axis->encoder_.config_.mode & Encoder::MODE_FLAG_ABS){ - axis->encoder_.abs_spi_cs_pin_init(); + if(axis.encoder_.config_.mode & Encoder::MODE_FLAG_ABS){ + axis.encoder_.abs_spi_cs_pin_init(); } } - // Setup motors (DRV8301 SPI transactions here) - for(auto& axis : axes){ - axis->motor_.setup(); + // Try to initialized gate drivers for fault-free startup. + // If this does not succeed, a fault will be raised and the idle loop will + // periodically attempt to reinit the gate driver. + for(auto& axis: axes){ + axis.motor_.setup(); } - // Setup encoders (Starts encoder SPI transactions) - for(auto& axis : axes){ - axis->encoder_.setup(); + for(auto& axis: axes){ + axis.encoder_.setup(); } - // Setup anything remaining in each axis - for(auto& axis : axes){ - axis->setup(); + for(auto& axis: axes){ + axis.acim_estimator_.idq_src_.connect_to(&axis.motor_.Idq_setpoint_); } // Start PWM and enable adc interrupts/callbacks start_adc_pwm(); + start_analog_thread(); - // This delay serves two purposes: - // - Let the current sense calibration converge (the current - // sense interrupts are firing in background by now) - // - Allow a user to interrupt the code, e.g. by flashing a new code, - // before it does anything crazy - // TODO make timing a function of calibration filter tau - osDelay(1500); + // Wait for up to 2s for motor to become ready to allow for error-free + // startup. This delay gives the current sensor calibration time to + // converge. If the DRV chip is unpowered, the motor will not become ready + // but we still enter idle state. + for (size_t i = 0; i < 2000; ++i) { + bool motors_ready = std::all_of(axes.begin(), axes.end(), [](auto& axis) { + return axis.motor_.current_meas_.has_value(); + }); + if (motors_ready) { + break; + } + osDelay(1); + } + + for (auto& axis: axes) { + axis.sensorless_estimator_.error_ &= ~SensorlessEstimator::ERROR_UNKNOWN_CURRENT_MEASUREMENT; + } // Start state machine threads. Each thread will go through various calibration // procedures and then run the actual controller loops. // TODO: generalize for AXIS_COUNT != 2 for (size_t i = 0; i < AXIS_COUNT; ++i) { - axes[i]->start_thread(); + axes[i].start_thread(); } - start_analog_thread(); - odrv.system_stats_.fully_booted = true; - return 0; + + // Main thread finished starting everything and can delete itself now (yes this is legal). + vTaskDelete(defaultTaskHandle); +} + +/** + * @brief Carries out early startup tasks that need to run before any static + * initializers. + * This function gets called from the startup assembly code. + */ +extern "C" void early_start_checks(void) { + if(_reboot_cookie == 0xDEADFE75) { + /* The STM DFU bootloader enables internal pull-up resistors on PB10 (AUX_H) + * and PB11 (AUX_L), thereby causing shoot-through on the brake resistor + * FETs and obliterating them unless external 3.3k pull-down resistors are + * present. Pull-downs are only present on ODrive 3.5 or newer. + * On older boards we disable DFU by default but if the user insists + * there's only one thing left that might save it: time. + * The brake resistor gate driver needs a certain 10V supply (GVDD) to + * make it work. This voltage is supplied by the motor gate drivers which get + * disabled at system reset. So over time GVDD voltage _should_ below + * dangerous levels. This is completely handwavy and should not be relied on + * so you are on your own on if you ignore this warning. + * + * This loop takes 5 cycles per iteration and at this point the system runs + * on the internal 16MHz RC oscillator so the delay is about 2 seconds. + */ + for (size_t i = 0; i < (16000000UL / 5UL * 2UL); ++i) { + __NOP(); + } + _reboot_cookie = 0xDEADBEEF; + } + + /* We could jump to the bootloader directly on demand without rebooting + but that requires us to reset several peripherals and interrupts for it + to function correctly. Therefore it's easier to just reset the entire chip. */ + if(_reboot_cookie == 0xDEADBEEF) { + _reboot_cookie = 0xCAFEFEED; //Reset bootloader trigger + __set_MSP((uintptr_t)&_estack); + // http://www.st.com/content/ccc/resource/technical/document/application_note/6a/17/92/02/58/98/45/0c/CD00264379.pdf/files/CD00264379.pdf + void (*builtin_bootloader)(void) = (void (*)(void))(*((uint32_t *)0x1FFF0004)); + builtin_bootloader(); + } + + /* The bootloader might fail to properly clean up after itself, + so if we're not sure that the system is in a clean state we + just reset it again */ + if(_reboot_cookie != 42) { + _reboot_cookie = 42; + NVIC_SystemReset(); + } +} + +/** + * @brief Main entry point called from assembly startup code. + */ +extern "C" int main(void) { + // This procedure of building a USB serial number should be identical + // to the way the STM's built-in USB bootloader does it. This means + // that the device will have the same serial number in normal and DFU mode. + uint32_t uuid0 = *(uint32_t *)(UID_BASE + 0); + uint32_t uuid1 = *(uint32_t *)(UID_BASE + 4); + uint32_t uuid2 = *(uint32_t *)(UID_BASE + 8); + uint32_t uuid_mixed_part = uuid0 + uuid2; + serial_number = ((uint64_t)uuid_mixed_part << 16) | (uint64_t)(uuid1 >> 16); + + uint64_t val = serial_number; + for (size_t i = 0; i < 12; ++i) { + serial_number_str[i] = "0123456789ABCDEF"[(val >> (48-4)) & 0xf]; + val <<= 4; + } + serial_number_str[12] = 0; + + // Init low level system functions (clocks, flash interface) + system_init(); + + // Load configuration from NVM. This needs to happen after system_init() + // since the flash interface must be initialized and before board_init() + // since board initialization can depend on the config. + size_t config_size = 0; + bool success = config_manager.start_load() + && config_read_all() + && config_manager.finish_load(&config_size) + && config_apply_all(); + if (success) { + odrv.user_config_loaded_ = config_size; + } else { + config_clear_all(); + config_apply_all(); + } + + odrv.misconfigured_ = odrv.misconfigured_ + || (odrv.config_.enable_uart_a && !uart_a) + || (odrv.config_.enable_uart_b && !uart_b) + || (odrv.config_.enable_uart_c && !uart_c); + + // Init board-specific peripherals + if (!board_init()) { + for (;;); // TODO: handle properly + } + + // Init GPIOs according to their configured mode + for (size_t i = 0; i < GPIO_COUNT; ++i) { + // Skip unavailable GPIOs + if (!get_gpio(i)) { + continue; + } + + ODriveIntf::GpioMode mode = odrv.config_.gpio_modes[i]; + + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.Pin = get_gpio(i).pin_mask_; + + // Set Alternate Function setting for this GPIO mode + if (mode == ODriveIntf::GPIO_MODE_DIGITAL || + mode == ODriveIntf::GPIO_MODE_DIGITAL_PULL_UP || + mode == ODriveIntf::GPIO_MODE_DIGITAL_PULL_DOWN || + mode == ODriveIntf::GPIO_MODE_MECH_BRAKE || + mode == ODriveIntf::GPIO_MODE_STATUS || + mode == ODriveIntf::GPIO_MODE_ANALOG_IN) { + GPIO_InitStruct.Alternate = 0; + } else { + auto it = std::find_if( + alternate_functions[i].begin(), alternate_functions[i].end(), + [mode](auto a) { return a.mode == mode; }); + + if (it == alternate_functions[i].end()) { + odrv.misconfigured_ = true; // this GPIO doesn't support the selected mode + continue; + } + GPIO_InitStruct.Alternate = it->alternate_function; + } + + switch (mode) { + case ODriveIntf::GPIO_MODE_DIGITAL: { + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + } break; + case ODriveIntf::GPIO_MODE_DIGITAL_PULL_UP: { + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + } break; + case ODriveIntf::GPIO_MODE_DIGITAL_PULL_DOWN: { + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + } break; + case ODriveIntf::GPIO_MODE_ANALOG_IN: { + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + } break; + case ODriveIntf::GPIO_MODE_UART_A: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = (i == 0) ? GPIO_PULLDOWN : GPIO_PULLUP; // this is probably swapped but imitates old behavior + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + if (!odrv.config_.enable_uart_a) { + odrv.misconfigured_ = true; + } + } break; + case ODriveIntf::GPIO_MODE_UART_B: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = (i == 0) ? GPIO_PULLDOWN : GPIO_PULLUP; // this is probably swapped but imitates old behavior + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + if (!odrv.config_.enable_uart_b) { + odrv.misconfigured_ = true; + } + } break; + case ODriveIntf::GPIO_MODE_UART_C: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = (i == 0) ? GPIO_PULLDOWN : GPIO_PULLUP; // this is probably swapped but imitates old behavior + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + if (!odrv.config_.enable_uart_c) { + odrv.misconfigured_ = true; + } + } break; + case ODriveIntf::GPIO_MODE_CAN_A: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + if (!odrv.config_.enable_can_a) { + odrv.misconfigured_ = true; + } + } break; + case ODriveIntf::GPIO_MODE_I2C_A: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + if (!odrv.config_.enable_i2c_a) { + odrv.misconfigured_ = true; + } + } break; + //case ODriveIntf::GPIO_MODE_SPI_A: { // TODO + //} break; + case ODriveIntf::GPIO_MODE_PWM: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + } break; + case ODriveIntf::GPIO_MODE_ENC0: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + } break; + case ODriveIntf::GPIO_MODE_ENC1: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + } break; + case ODriveIntf::GPIO_MODE_ENC2: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + } break; + case ODriveIntf::GPIO_MODE_MECH_BRAKE: { + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + } break; + case ODriveIntf::GPIO_MODE_STATUS: { + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + } break; + default: { + odrv.misconfigured_ = true; + continue; + } + } + + HAL_GPIO_Init(get_gpio(i).port_, &GPIO_InitStruct); + } + + // Init usb irq binary semaphore, and start with no tokens by removing the starting one. + osSemaphoreDef(sem_usb_irq); + sem_usb_irq = osSemaphoreCreate(osSemaphore(sem_usb_irq), 1); + osSemaphoreWait(sem_usb_irq, 0); + + // Create an event queue for UART + osMessageQDef(uart_event_queue, 4, uint32_t); + uart_event_queue = osMessageCreate(osMessageQ(uart_event_queue), NULL); + + // Create an event queue for USB + osMessageQDef(usb_event_queue, 7, uint32_t); + usb_event_queue = osMessageCreate(osMessageQ(usb_event_queue), NULL); + + osSemaphoreDef(sem_can); + sem_can = osSemaphoreCreate(osSemaphore(sem_can), 1); + osSemaphoreWait(sem_can, 0); + + // Create main thread + osThreadDef(defaultTask, rtos_main, osPriorityNormal, 0, stack_size_default_task / sizeof(StackType_t)); + defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL); + + // Start scheduler + osKernelStart(); + + for (;;); } diff --git a/Firmware/MotorControl/mechanical_brake.cpp b/Firmware/MotorControl/mechanical_brake.cpp new file mode 100644 index 000000000..c9d3602f8 --- /dev/null +++ b/Firmware/MotorControl/mechanical_brake.cpp @@ -0,0 +1,13 @@ +#include + +void MechanicalBrake::engage() { + if (odrv.config_.gpio_modes[config_.gpio_num] == ODriveIntf::GPIO_MODE_MECH_BRAKE){ + get_gpio(config_.gpio_num).write(config_.is_active_low ? 0 : 1); + } +} + +void MechanicalBrake::release() { + if (odrv.config_.gpio_modes[config_.gpio_num] == ODriveIntf::GPIO_MODE_MECH_BRAKE){ + get_gpio(config_.gpio_num).write(config_.is_active_low ? 1 : 0); + } +} diff --git a/Firmware/MotorControl/mechanical_brake.hpp b/Firmware/MotorControl/mechanical_brake.hpp new file mode 100644 index 000000000..26362ce5e --- /dev/null +++ b/Firmware/MotorControl/mechanical_brake.hpp @@ -0,0 +1,25 @@ +#ifndef __MECHANICAL_BRAKE_HPP +#define __MECHANICAL_BRAKE_HPP + +#include + +class MechanicalBrake : public ODriveIntf::MechanicalBrakeIntf { + public: + struct Config_t { + uint16_t gpio_num = 0; + bool is_active_low = true; + + // custom setters + MechanicalBrake* parent = nullptr; + void set_gpio_num(uint16_t value) { gpio_num = value; } + }; + + MechanicalBrake() {} + + MechanicalBrake::Config_t config_; + Axis* axis_ = nullptr; + + void release(); + void engage(); +}; +#endif // __MECHANICAL_BRAKE_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index f22c46c2f..6d05d347e 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -1,57 +1,294 @@ +#include "motor.hpp" +#include "axis.hpp" +#include "low_level.h" +#include "odrive_main.h" + #include -#include "drv8301.h" -#include "odrive_main.h" +static constexpr auto CURRENT_ADC_LOWER_BOUND = (uint32_t)((float)(1 << 12) * CURRENT_SENSE_MIN_VOLT / 3.3f); +static constexpr auto CURRENT_ADC_UPPER_BOUND = (uint32_t)((float)(1 << 12) * CURRENT_SENSE_MAX_VOLT / 3.3f); + +/** + * @brief This control law adjusts the output voltage such that a predefined + * current is tracked. A hardcoded integrator gain is used for this. + * + * TODO: this might as well be implemented using the FieldOrientedController. + */ +struct ResistanceMeasurementControlLaw : AlphaBetaFrameController { + void reset() final { + test_voltage_ = 0.0f; + test_mod_ = std::nullopt; + } + ODriveIntf::MotorIntf::Error on_measurement( + std::optional vbus_voltage, + std::optional Ialpha_beta, + uint32_t input_timestamp) final { + + if (Ialpha_beta.has_value()) { + actual_current_ = Ialpha_beta->first; + test_voltage_ += (kI * current_meas_period) * (target_current_ - actual_current_); + I_beta_ += (kIBetaFilt * current_meas_period) * (Ialpha_beta->second - I_beta_); + } else { + actual_current_ = 0.0f; + test_voltage_ = 0.0f; + } + + if (std::abs(test_voltage_) > max_voltage_) { + test_voltage_ = NAN; + return Motor::ERROR_PHASE_RESISTANCE_OUT_OF_RANGE; + } else if (!vbus_voltage.has_value()) { + return Motor::ERROR_UNKNOWN_VBUS_VOLTAGE; + } else { + float vfactor = 1.0f / ((2.0f / 3.0f) * *vbus_voltage); + test_mod_ = test_voltage_ * vfactor; + return Motor::ERROR_NONE; + } + } -Motor::Motor(const MotorHardwareConfig_t& hw_config, - const GateDriverHardwareConfig_t& gate_driver_config, - Config_t& config) : - hw_config_(hw_config), - gate_driver_config_(gate_driver_config), - config_(config), - gate_driver_({ - .spiHandle = gate_driver_config_.spi, - .EngpioHandle = gate_driver_config_.enable_port, - .EngpioNumber = gate_driver_config_.enable_pin, - .nCSgpioHandle = gate_driver_config_.nCS_port, - .nCSgpioNumber = gate_driver_config_.nCS_pin, - }) { - update_current_controller_gains(); + ODriveIntf::MotorIntf::Error get_alpha_beta_output( + uint32_t output_timestamp, + std::optional* mod_alpha_beta, + std::optional* ibus) final { + if (!test_mod_.has_value()) { + return Motor::ERROR_CONTROLLER_INITIALIZING; + } else { + *mod_alpha_beta = {*test_mod_, 0.0f}; + *ibus = *test_mod_ * actual_current_; + return Motor::ERROR_NONE; + } + } + + float get_resistance() { + return test_voltage_ / target_current_; + } + + float get_Ibeta() { + return I_beta_; + } + + const float kI = 1.0f; // [(V/s)/A] + const float kIBetaFilt = 80.0f; + float max_voltage_ = 0.0f; + float actual_current_ = 0.0f; + float target_current_ = 0.0f; + float test_voltage_ = 0.0f; + float I_beta_ = 0.0f; // [A] low pass filtered Ibeta response + std::optional test_mod_ = NAN; +}; + +/** + * @brief This control law toggles rapidly between positive and negative output + * voltage. By measuring how large the current ripples are, the phase inductance + * can be determined. + * + * TODO: this method assumes a certain synchronization between current measurement and output application + */ +struct InductanceMeasurementControlLaw : AlphaBetaFrameController { + void reset() final { + attached_ = false; + } + + ODriveIntf::MotorIntf::Error on_measurement( + std::optional vbus_voltage, + std::optional Ialpha_beta, + uint32_t input_timestamp) final + { + if (!Ialpha_beta.has_value()) { + return {Motor::ERROR_UNKNOWN_CURRENT_MEASUREMENT}; + } + + float Ialpha = Ialpha_beta->first; + + if (attached_) { + float sign = test_voltage_ >= 0.0f ? 1.0f : -1.0f; + deltaI_ += -sign * (Ialpha - last_Ialpha_); + } else { + start_timestamp_ = input_timestamp; + attached_ = true; + } + + last_Ialpha_ = Ialpha; + last_input_timestamp_ = input_timestamp; + + return Motor::ERROR_NONE; + } + + ODriveIntf::MotorIntf::Error get_alpha_beta_output( + uint32_t output_timestamp, std::optional* mod_alpha_beta, + std::optional* ibus) final + { + test_voltage_ *= -1.0f; + float vfactor = 1.0f / ((2.0f / 3.0f) * vbus_voltage); + *mod_alpha_beta = {test_voltage_ * vfactor, 0.0f}; + *ibus = 0.0f; + return Motor::ERROR_NONE; + } + + float get_inductance() { + // Note: A more correct formula would also take into account that there is a finite timestep. + // However, the discretisation in the current control loop inverts the same discrepancy + float dt = (float)(last_input_timestamp_ - start_timestamp_) / (float)TIM_1_8_CLOCK_HZ; // at 216MHz this overflows after 19 seconds + return std::abs(test_voltage_) / (deltaI_ / dt); + } + + // Config + float test_voltage_ = 0.0f; + + // State + bool attached_ = false; + float sign_ = 0; + + // Outputs + uint32_t start_timestamp_ = 0; + float last_Ialpha_ = NAN; + uint32_t last_input_timestamp_ = 0; + float deltaI_ = 0.0f; +}; + + +Motor::Motor(TIM_HandleTypeDef* timer, + uint8_t current_sensor_mask, + float shunt_conductance, + TGateDriver& gate_driver, + TOpAmp& opamp, + OnboardThermistorCurrentLimiter& fet_thermistor, + OffboardThermistorCurrentLimiter& motor_thermistor) : + timer_(timer), + current_sensor_mask_(current_sensor_mask), + shunt_conductance_(shunt_conductance), + gate_driver_(gate_driver), + opamp_(opamp), + fet_thermistor_(fet_thermistor), + motor_thermistor_(motor_thermistor) { + apply_config(); + fet_thermistor_.motor_ = this; + motor_thermistor_.motor_ = this; } -// @brief Arms the PWM outputs that belong to this motor. -// -// Note that this does not yet activate the PWM outputs, it just unlocks them. -// -// While the motor is armed, the control loop must set new modulation timings -// between any two interrupts (that is, enqueue_modulation_timings must be executed). -// If the control loop fails to do so, the next interrupt handler floats the -// phases. Once this happens, missed_control_deadline is set to true and -// the motor can be considered disarmed. -// -// @returns: True on success, false otherwise -bool Motor::arm() { - - // Reset controller states, integrators, setpoints, etc. - axis_->controller_.reset(); - reset_current_control(); - - // Wait until the interrupt handler triggers twice. This gives - // the control loop the correct time quota to set up modulation timings. - if (!axis_->wait_for_current_meas()) - return axis_->error_ |= Axis::ERROR_CURRENT_MEASUREMENT_TIMEOUT, false; - next_timings_valid_ = false; - safety_critical_arm_motor_pwm(*this); +/** + * @brief Arms the PWM outputs that belong to this motor. + * + * Note that this does not activate the PWM outputs immediately, it just sets + * a flag so they will be enabled later. + * + * The sequence goes like this: + * - Motor::arm() sets the is_armed_ flag. + * - On the next timer update event Motor::timer_update_cb() gets called in an + * interrupt context + * - Motor::timer_update_cb() runs specified control law to determine PWM values + * - Motor::timer_update_cb() calls Motor::apply_pwm_timings() + * - Motor::apply_pwm_timings() sets the output compare registers and the AOE + * (automatic output enable) bit. + * - On the next update event the timer latches the configured values into the + * active shadow register and enables the outputs at the same time. + * + * The sequence can be aborted at any time by calling Motor::disarm(). + * + * @param control_law: An control law that is called at the frequency of current + * measurements. The function must return as quickly as possible + * such that the resulting PWM timings are available before the next + * timer update event. + * @returns: True on success, false otherwise + */ +bool Motor::arm(PhaseControlLaw<3>* control_law) { + axis_->mechanical_brake_.release(); + + CRITICAL_SECTION() { + control_law_ = control_law; + + // Reset controller states, integrators, setpoints, etc. + axis_->controller_.reset(); + axis_->acim_estimator_.rotor_flux_ = 0.0f; + if (control_law_) { + control_law_->reset(); + } + + if (!odrv.config_.enable_brake_resistor || brake_resistor_armed) { + armed_state_ = 1; + is_armed_ = true; + } else { + error_ |= Motor::ERROR_BRAKE_RESISTOR_DISARMED; + } + } + return true; } -void Motor::reset_current_control() { - current_control_.v_current_control_integral_d = 0.0f; - current_control_.v_current_control_integral_q = 0.0f; - current_control_.acim_rotor_flux = 0.0f; - current_control_.Ibus = 0.0f; +/** + * @brief Updates the phase PWM timings unless the motor is disarmed. + * + * If the motor is armed, the PWM timings come into effect at the next update + * event (and are enabled if they weren't already), unless the motor is disarmed + * prior to that. + * + * @param tentative: If true, the update is not counted as "refresh". + */ +void Motor::apply_pwm_timings(uint16_t timings[3], bool tentative) { + CRITICAL_SECTION() { + if (odrv.config_.enable_brake_resistor && !brake_resistor_armed) { + disarm_with_error(ERROR_BRAKE_RESISTOR_DISARMED); + } + + TIM_HandleTypeDef* htim = timer_; + TIM_TypeDef* tim = htim->Instance; + tim->CCR1 = timings[0]; + tim->CCR2 = timings[1]; + tim->CCR3 = timings[2]; + + if (!tentative) { + if (is_armed_) { + // Set the Automatic Output Enable so that the Master Output Enable + // bit will be automatically enabled on the next update event. + tim->BDTR |= TIM_BDTR_AOE; + } + } + + // If a timer update event occurred just now while we were updating the + // timings, we can't be sure what values the shadow registers now contain, + // so we must disarm the motor. + // (this also protects against the case where the update interrupt has too + // low priority, but that should not happen) + //if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE)) { + // disarm_with_error(ERROR_CONTROL_DEADLINE_MISSED); + //} + } +} + +/** + * @brief Disarms the motor PWM. + * + * After this function returns, it is guaranteed that all three + * motor phases are floating and will not be enabled again until + * arm() is called. + */ +bool Motor::disarm(bool* p_was_armed) { + bool was_armed; + + CRITICAL_SECTION() { + was_armed = is_armed_; + if (is_armed_) { + gate_driver_.set_enabled(false); + } + is_armed_ = false; + armed_state_ = 0; + TIM_HandleTypeDef* timer = timer_; + timer->Instance->BDTR &= ~TIM_BDTR_AOE; // prevent the PWMs from automatically enabling at the next update + __HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(timer); + control_law_ = nullptr; + } + + // Check necessary to prevent infinite recursion + if (was_armed) { + update_brake_current(); + } + + if (p_was_armed) { + *p_was_armed = was_armed; + } + + return true; } // @brief Tune the current controller based on phase resistance and inductance @@ -59,98 +296,68 @@ void Motor::reset_current_control() { // TODO: allow update on user-request or update automatically via hooks void Motor::update_current_controller_gains() { // Calculate current control gains - current_control_.p_gain = config_.current_control_bandwidth * config_.phase_inductance; + float p_gain = config_.current_control_bandwidth * config_.phase_inductance; float plant_pole = config_.phase_resistance / config_.phase_inductance; - current_control_.i_gain = plant_pole * current_control_.p_gain; + current_control_.pi_gains_ = {p_gain, plant_pole * p_gain}; +} + +bool Motor::apply_config() { + config_.parent = this; + is_calibrated_ = config_.pre_calibrated; + update_current_controller_gains(); + return true; } // @brief Set up the gate drivers -void Motor::DRV8301_setup() { - // for reference: - // 20V/V on 500uOhm gives a range of +/- 150A - // 40V/V on 500uOhm gives a range of +/- 75A - // 20V/V on 666uOhm gives a range of +/- 110A - // 40V/V on 666uOhm gives a range of +/- 55A +bool Motor::setup() { + fet_thermistor_.update(); + motor_thermistor_.update(); // Solve for exact gain, then snap down to have equal or larger range as requested // or largest possible range otherwise constexpr float kMargin = 0.90f; - constexpr float kTripMargin = 1.0f; // Trip level is at edge of linear range of amplifer constexpr float max_output_swing = 1.35f; // [V] out of amplifier - float max_unity_gain_current = kMargin * max_output_swing * hw_config_.shunt_conductance; // [A] + float max_unity_gain_current = kMargin * max_output_swing * shunt_conductance_; // [A] float requested_gain = max_unity_gain_current / config_.requested_current_range; // [V/V] - - // Decoding array for snapping gain - std::array, 4> gain_choices = { - std::make_pair(10.0f, DRV8301_ShuntAmpGain_10VpV), - std::make_pair(20.0f, DRV8301_ShuntAmpGain_20VpV), - std::make_pair(40.0f, DRV8301_ShuntAmpGain_40VpV), - std::make_pair(80.0f, DRV8301_ShuntAmpGain_80VpV) - }; - - // We use lower_bound in reverse because it snaps up by default, we want to snap down. - auto gain_snap_down = std::lower_bound(gain_choices.crbegin(), gain_choices.crend(), requested_gain, - [](std::pair pair, float val){ - return pair.first > val; - }); - - // If we snap to outside the array, clip to smallest val - if(gain_snap_down == gain_choices.crend()) - --gain_snap_down; + + float actual_gain; + if (!gate_driver_.config(requested_gain, &actual_gain)) + return false; // Values for current controller - phase_current_rev_gain_ = 1.0f / gain_snap_down->first; + phase_current_rev_gain_ = 1.0f / actual_gain; // Clip all current control to actual usable range - current_control_.max_allowed_current = max_unity_gain_current * phase_current_rev_gain_; - // Set trip level - current_control_.overcurrent_trip_level = (kTripMargin / kMargin) * current_control_.max_allowed_current; - - // We now have the gain settings we want to use, lets set up DRV chip - DRV_SPI_8301_Vars_t* local_regs = &gate_driver_regs_; - DRV8301_enable(&gate_driver_); - DRV8301_setupSpi(&gate_driver_, local_regs); - - local_regs->Ctrl_Reg_1.OC_MODE = DRV8301_OcMode_LatchShutDown; - // Overcurrent set to approximately 150A at 100degC. This may need tweaking. - local_regs->Ctrl_Reg_1.OC_ADJ_SET = DRV8301_VdsLevel_0p730_V; - local_regs->Ctrl_Reg_2.GAIN = gain_snap_down->second; - - local_regs->SndCmd = true; - DRV8301_writeData(&gate_driver_, local_regs); - local_regs->RcvCmd = true; - DRV8301_readData(&gate_driver_, local_regs); -} + max_allowed_current_ = max_unity_gain_current * phase_current_rev_gain_; -// @brief Checks if the gate driver is in operational state. -// @returns: true if the gate driver is OK (no fault), false otherwise -bool Motor::check_DRV_fault() { - //TODO: make this pin configurable per motor ch - GPIO_PinState nFAULT_state = HAL_GPIO_ReadPin(gate_driver_config_.nFAULT_port, gate_driver_config_.nFAULT_pin); - if (nFAULT_state == GPIO_PIN_RESET) { - // Update DRV Fault Code - gate_driver_exported_.drv_fault = (GateDriverIntf::DrvFault)DRV8301_getFaultType(&gate_driver_); - // Update/Cache all SPI device registers - // DRV_SPI_8301_Vars_t* local_regs = &gate_driver_regs_; - // local_regs->RcvCmd = true; - // DRV8301_readData(&gate_driver_, local_regs); + max_dc_calib_ = 0.1f * max_allowed_current_; + + if (!gate_driver_.init()) return false; - }; + return true; } -void Motor::set_error(Motor::Error error){ +void Motor::disarm_with_error(Motor::Error error){ error_ |= error; - axis_->error_ |= Axis::ERROR_MOTOR_FAILED; - safety_critical_disarm_motor_pwm(*this); - update_brake_current(); + last_error_time_ = odrv.n_evt_control_loop_ * current_meas_period; + disarm(); } -bool Motor::do_checks() { - if (!check_DRV_fault()) { - set_error(ERROR_DRV_FAULT); +bool Motor::do_checks(uint32_t timestamp) { + gate_driver_.do_checks(); + + if (!gate_driver_.is_ready()) { + disarm_with_error(ERROR_DRV_FAULT); + return false; + } + if (!motor_thermistor_.do_checks()) { + disarm_with_error(ERROR_MOTOR_THERMISTOR_OVER_TEMP); + return false; + } + if (!fet_thermistor_.do_checks()) { + disarm_with_error(ERROR_FET_THERMISTOR_OVER_TEMP); return false; } - return true; } @@ -161,14 +368,12 @@ float Motor::effective_current_lim() { if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) { current_lim = std::min(current_lim, 0.98f*one_by_sqrt3*vbus_voltage); //gimbal motor is voltage control } else { - current_lim = std::min(current_lim, axis_->motor_.current_control_.max_allowed_current); - } - - // Apply axis current limiters - for (const CurrentLimiter* const limiter : axis_->current_limiters_) { - current_lim = std::min(current_lim, limiter->get_current_limit(config_.current_lim)); + current_lim = std::min(current_lim, axis_->motor_.max_allowed_current_); } + // Apply thermistor current limiters + current_lim = std::min(current_lim, motor_thermistor_.get_current_limit(config_.current_lim)); + current_lim = std::min(current_lim, fet_thermistor_.get_current_limit(config_.current_lim)); effective_current_lim_ = current_lim; return effective_current_lim_; @@ -178,31 +383,27 @@ float Motor::effective_current_lim() { //Note - for ACIM motors, available torque is allowed to be 0. float Motor::max_available_torque() { if (config_.motor_type == Motor::MOTOR_TYPE_ACIM) { - float max_torque = effective_current_lim() * config_.torque_constant * current_control_.acim_rotor_flux; + float max_torque = effective_current_lim_ * config_.torque_constant * axis_->acim_estimator_.rotor_flux_; max_torque = std::clamp(max_torque, 0.0f, config_.torque_lim); return max_torque; - } - else { - float max_torque = effective_current_lim() * config_.torque_constant; + } else { + float max_torque = effective_current_lim_ * config_.torque_constant; max_torque = std::clamp(max_torque, 0.0f, config_.torque_lim); return max_torque; } } -void Motor::log_timing(TimingLog_t log_idx) { - static const uint16_t clocks_per_cnt = (uint16_t)((float)TIM_1_8_CLOCK_HZ / (float)TIM_APB1_CLOCK_HZ); - uint16_t timing = clocks_per_cnt * htim13.Instance->CNT; // TODO: Use a hw_config - - if (log_idx < TIMING_LOG_NUM_SLOTS) { - timing_log_[log_idx] = timing; +std::optional Motor::phase_current_from_adcval(uint32_t ADCValue) { + // Make sure the measurements don't come too close to the current sensor's hardware limitations + if (ADCValue < CURRENT_ADC_LOWER_BOUND || ADCValue > CURRENT_ADC_UPPER_BOUND) { + error_ |= ERROR_CURRENT_SENSE_SATURATION; + return std::nullopt; } -} -float Motor::phase_current_from_adcval(uint32_t ADCValue) { int adcval_bal = (int)ADCValue - (1 << 11); float amp_out_volt = (3.3f / (float)(1 << 12)) * (float)adcval_bal; float shunt_volt = amp_out_volt * phase_current_rev_gain_; - float current = shunt_volt * hw_config_.shunt_conductance; + float current = shunt_volt * shunt_conductance_; return current; } @@ -212,81 +413,89 @@ float Motor::phase_current_from_adcval(uint32_t ADCValue) { // TODO check Ibeta balance to verify good motor connection bool Motor::measure_phase_resistance(float test_current, float max_voltage) { - static const float kI = 10.0f; // [(V/s)/A] - static const int num_test_cycles = (int)(3.0f / CURRENT_MEAS_PERIOD); // Test runs for 3s - float test_voltage = 0.0f; - - size_t i = 0; - axis_->run_control_loop([&](){ - float Ialpha = -(current_meas_.phB + current_meas_.phC); - test_voltage += (kI * current_meas_period) * (test_current - Ialpha); - if (test_voltage > max_voltage || test_voltage < -max_voltage) - return set_error(ERROR_PHASE_RESISTANCE_OUT_OF_RANGE), false; - - // Test voltage along phase A - if (!enqueue_voltage_timings(test_voltage, 0.0f)) - return false; // error set inside enqueue_voltage_timings - log_timing(TIMING_LOG_MEAS_R); - - return ++i < num_test_cycles; - }); - if (axis_->error_ != Axis::ERROR_NONE) - return false; + ResistanceMeasurementControlLaw control_law; + control_law.target_current_ = test_current; + control_law.max_voltage_ = max_voltage; + + arm(&control_law); + + for (size_t i = 0; i < 3000; ++i) { + if (!((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_)) { + break; + } + osDelay(1); + } + + bool success = is_armed_; //// De-energize motor //if (!enqueue_voltage_timings(motor, 0.0f, 0.0f)) // return false; // error set inside enqueue_voltage_timings - float R = test_voltage / test_current; - config_.phase_resistance = R; - return true; // if we ran to completion that means success + disarm(); + + config_.phase_resistance = control_law.get_resistance(); + if (is_nan(config_.phase_resistance)) { + // TODO: the motor is already disarmed at this stage. This is an error + // that only pretains to the measurement and its result so it should + // just be a return value of this function. + disarm_with_error(ERROR_PHASE_RESISTANCE_OUT_OF_RANGE); + success = false; + } + + float I_beta = control_law.get_Ibeta(); + if (is_nan(I_beta) || (abs(I_beta) / test_current) > 0.1f) { + disarm_with_error(ERROR_UNBALANCED_PHASES); + success = false; + } + + return success; } -bool Motor::measure_phase_inductance(float voltage_low, float voltage_high) { - float test_voltages[2] = {voltage_low, voltage_high}; - float Ialphas[2] = {0.0f}; - static const int num_cycles = 5000; - size_t t = 0; - axis_->run_control_loop([&](){ - int i = t & 1; - Ialphas[i] += -current_meas_.phB - current_meas_.phC; +bool Motor::measure_phase_inductance(float test_voltage) { + InductanceMeasurementControlLaw control_law; + control_law.test_voltage_ = test_voltage; - // Test voltage along phase A - if (!enqueue_voltage_timings(test_voltages[i], 0.0f)) - return false; // error set inside enqueue_voltage_timings - log_timing(TIMING_LOG_MEAS_L); + arm(&control_law); - return ++t < (num_cycles << 1); - }); - if (axis_->error_ != Axis::ERROR_NONE) - return false; + for (size_t i = 0; i < 1250; ++i) { + if (!((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_)) { + break; + } + osDelay(1); + } + + bool success = is_armed_; //// De-energize motor //if (!enqueue_voltage_timings(motor, 0.0f, 0.0f)) // return false; // error set inside enqueue_voltage_timings - float v_L = 0.5f * (voltage_high - voltage_low); - // Note: A more correct formula would also take into account that there is a finite timestep. - // However, the discretisation in the current control loop inverts the same discrepancy - float dI_by_dt = (Ialphas[1] - Ialphas[0]) / (current_meas_period * (float)num_cycles); - float L = v_L / dI_by_dt; + disarm(); - config_.phase_inductance = L; + config_.phase_inductance = control_law.get_inductance(); + // TODO arbitrary values set for now - if (L < 2e-6f || L > 4000e-6f) - return set_error(ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE), false; - return true; + if (!(config_.phase_inductance >= 2e-6f && config_.phase_inductance <= 4000e-6f)) { + error_ |= ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE; + success = false; + } + + return success; } +// TODO: motor calibration should only be a utility function that's called from +// the UI on explicit user request. It should take its parameters as input +// arguments and return the measured results without modifying any config values. bool Motor::run_calibration() { float R_calib_max_voltage = config_.resistance_calib_max_voltage; if (config_.motor_type == MOTOR_TYPE_HIGH_CURRENT || config_.motor_type == MOTOR_TYPE_ACIM) { if (!measure_phase_resistance(config_.calibration_current, R_calib_max_voltage)) return false; - if (!measure_phase_inductance(-R_calib_max_voltage, R_calib_max_voltage)) + if (!measure_phase_inductance(R_calib_max_voltage)) return false; } else if (config_.motor_type == MOTOR_TYPE_GIMBAL) { // no calibration needed @@ -300,202 +509,224 @@ bool Motor::run_calibration() { return true; } -bool Motor::enqueue_modulation_timings(float mod_alpha, float mod_beta) { - float tA, tB, tC; - if (SVM(mod_alpha, mod_beta, &tA, &tB, &tC) != 0) - return set_error(ERROR_MODULATION_MAGNITUDE), false; - next_timings_[0] = (uint16_t)(tA * (float)TIM_1_8_PERIOD_CLOCKS); - next_timings_[1] = (uint16_t)(tB * (float)TIM_1_8_PERIOD_CLOCKS); - next_timings_[2] = (uint16_t)(tC * (float)TIM_1_8_PERIOD_CLOCKS); - next_timings_valid_ = true; - return true; -} +void Motor::update(uint32_t timestamp) { + // Load torque setpoint, convert to motor direction + std::optional maybe_torque = torque_setpoint_src_.present(); + if (!maybe_torque.has_value()) { + error_ |= ERROR_UNKNOWN_TORQUE; + return; + } + float torque = direction_ * *maybe_torque; + + // Load setpoints from previous iteration. + auto [id, iq] = Idq_setpoint_.previous() + .value_or(float2D{0.0f, 0.0f}); + // Load effective current limit + float ilim = axis_->motor_.effective_current_lim_; + + // Autoflux tracks old Iq (that may be 2-norm clamped last cycle) to make sure we are chasing a feasable current. + if ((axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) && config_.acim_autoflux_enable) { + float abs_iq = std::abs(iq); + float gain = abs_iq > id ? config_.acim_autoflux_attack_gain : config_.acim_autoflux_decay_gain; + id += gain * (abs_iq - id) * current_meas_period; + id = std::clamp(id, config_.acim_autoflux_min_Id, 0.9f * ilim); // 10% space reserved for Iq + } else { + id = std::clamp(id, -ilim*0.99f, ilim*0.99f); // 1% space reserved for Iq to avoid numerical issues + } -bool Motor::enqueue_voltage_timings(float v_alpha, float v_beta) { - float vfactor = 1.0f / ((2.0f / 3.0f) * vbus_voltage); - float mod_alpha = vfactor * v_alpha; - float mod_beta = vfactor * v_beta; - if (!enqueue_modulation_timings(mod_alpha, mod_beta)) - return false; - log_timing(TIMING_LOG_FOC_VOLTAGE); - return true; -} + // Convert requested torque to current + if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) { + iq = torque / (axis_->motor_.config_.torque_constant * std::max(axis_->acim_estimator_.rotor_flux_, config_.acim_gain_min_flux)); + } else { + iq = torque / axis_->motor_.config_.torque_constant; + } -// We should probably make FOC Current call FOC Voltage to avoid duplication. -bool Motor::FOC_voltage(float v_d, float v_q, float pwm_phase) { - float c = our_arm_cos_f32(pwm_phase); - float s = our_arm_sin_f32(pwm_phase); - float v_alpha = c*v_d - s*v_q; - float v_beta = c*v_q + s*v_d; - return enqueue_voltage_timings(v_alpha, v_beta); -} + // 2-norm clamping where Id takes priority + float iq_lim_sqr = SQ(ilim) - SQ(id); + float Iq_lim = (iq_lim_sqr <= 0.0f) ? 0.0f : sqrt(iq_lim_sqr); + iq = std::clamp(iq, -Iq_lim, Iq_lim); -bool Motor::FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_phase) { - // Syntactic sugar - CurrentControl_t& ictrl = current_control_; + if (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL) { + Idq_setpoint_ = {id, iq}; + } - // For Reporting - ictrl.Iq_setpoint = Iq_des; + // This update call is in bit a weird position because it depends on the + // Id,q setpoint but outputs the phase velocity that we depend on later + // in this function. + // A cleaner fix would be to take the feedforward calculation out of here + // and turn it into a separate component. + MEASURE_TIME(axis_->task_times_.acim_estimator_update) + axis_->acim_estimator_.update(timestamp); - // Check for current sense saturation - if (std::abs(current_meas_.phB) > ictrl.overcurrent_trip_level || std::abs(current_meas_.phC) > ictrl.overcurrent_trip_level) { - set_error(ERROR_CURRENT_SENSE_SATURATION); - return false; - } + float vd = 0.0f; + float vq = 0.0f; - // Clarke transform - float Ialpha = -current_meas_.phB - current_meas_.phC; - float Ibeta = one_by_sqrt3 * (current_meas_.phB - current_meas_.phC); + std::optional phase_vel = phase_vel_src_.present(); - // Park transform - float c_I = our_arm_cos_f32(I_phase); - float s_I = our_arm_sin_f32(I_phase); - float Id = c_I * Ialpha + s_I * Ibeta; - float Iq = c_I * Ibeta - s_I * Ialpha; - ictrl.Iq_measured += ictrl.I_measured_report_filter_k * (Iq - ictrl.Iq_measured); - ictrl.Id_measured += ictrl.I_measured_report_filter_k * (Id - ictrl.Id_measured); + if (config_.R_wL_FF_enable) { + if (!phase_vel.has_value()) { + error_ |= ERROR_UNKNOWN_PHASE_VEL; + return; + } - // Check for violation of current limit - float I_trip = effective_current_lim() + config_.current_lim_margin; - if (SQ(Id) + SQ(Iq) > SQ(I_trip)) { - set_error(ERROR_CURRENT_LIMIT_VIOLATION); - return false; + vd -= *phase_vel * config_.phase_inductance * iq; + vq += *phase_vel * config_.phase_inductance * id; + vd += config_.phase_resistance * id; + vq += config_.phase_resistance * iq; } - // Current error - float Ierr_d = Id_des - Id; - float Ierr_q = Iq_des - Iq; - - // TODO look into feed forward terms (esp omega, since PI pole maps to RL tau) - // Apply PI control - float Vd = ictrl.v_current_control_integral_d + Ierr_d * ictrl.p_gain; - float Vq = ictrl.v_current_control_integral_q + Ierr_q * ictrl.p_gain; - - float mod_to_V = (2.0f / 3.0f) * vbus_voltage; - float V_to_mod = 1.0f / mod_to_V; - float mod_d = V_to_mod * Vd; - float mod_q = V_to_mod * Vq; - - // Vector modulation saturation, lock integrator if saturated - // TODO make maximum modulation configurable - float mod_scalefactor = 0.80f * sqrt3_by_2 * 1.0f / sqrtf(mod_d * mod_d + mod_q * mod_q); - if (mod_scalefactor < 1.0f) { - mod_d *= mod_scalefactor; - mod_q *= mod_scalefactor; - // TODO make decayfactor configurable - ictrl.v_current_control_integral_d *= 0.99f; - ictrl.v_current_control_integral_q *= 0.99f; + if (config_.bEMF_FF_enable) { + if (!phase_vel.has_value()) { + error_ |= ERROR_UNKNOWN_PHASE_VEL; + return; + } + + vq += *phase_vel * (2.0f/3.0f) * (config_.torque_constant / config_.pole_pairs); + } + + if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) { + // reinterpret current as voltage + Vdq_setpoint_ = {vd + id, vq + iq}; } else { - ictrl.v_current_control_integral_d += Ierr_d * (ictrl.i_gain * current_meas_period); - ictrl.v_current_control_integral_q += Ierr_q * (ictrl.i_gain * current_meas_period); + Vdq_setpoint_ = {vd, vq}; } +} - // Compute estimated bus current - ictrl.Ibus = mod_d * Id + mod_q * Iq; - // Inverse park transform - float c_p = our_arm_cos_f32(pwm_phase); - float s_p = our_arm_sin_f32(pwm_phase); - float mod_alpha = c_p * mod_d - s_p * mod_q; - float mod_beta = c_p * mod_q + s_p * mod_d; +/** + * @brief Called when the underlying hardware timer triggers an update event. + */ +void Motor::current_meas_cb(uint32_t timestamp, std::optional current) { + // TODO: this is platform specific + //const float current_meas_period = static_cast(2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) / TIM_1_8_CLOCK_HZ; + TaskTimerContext tmr{axis_->task_times_.current_sense}; + + n_evt_current_measurement_++; + + bool dc_calib_valid = (dc_calib_running_since_ >= config_.dc_calib_tau * 7.5f) + && (abs(DC_calib_.phA) < max_dc_calib_) + && (abs(DC_calib_.phB) < max_dc_calib_) + && (abs(DC_calib_.phC) < max_dc_calib_); + + if (armed_state_ == 1 || armed_state_ == 2) { + current_meas_ = {0.0f, 0.0f, 0.0f}; + armed_state_ += 1; + } else if (current.has_value() && dc_calib_valid) { + current_meas_ = { + current->phA - DC_calib_.phA, + current->phB - DC_calib_.phB, + current->phC - DC_calib_.phC + }; + } else { + current_meas_ = std::nullopt; + } - // Report final applied voltage in stationary frame (for sensorles estimator) - ictrl.final_v_alpha = mod_to_V * mod_alpha; - ictrl.final_v_beta = mod_to_V * mod_beta; + // Run system-level checks (e.g. overvoltage/undervoltage condition) + // The motor might be disarmed in this function. In this case the + // handler will continue to run until the end but it won't have an + // effect on the PWM. + odrv.do_fast_checks(); + + if (current_meas_.has_value()) { + // Check for violation of current limit + // If Ia + Ib + Ic == 0 holds then we have: + // Inorm^2 = Id^2 + Iq^2 = Ialpha^2 + Ibeta^2 = 2/3 * (Ia^2 + Ib^2 + Ic^2) + float Itrip = effective_current_lim_ + config_.current_lim_margin; + float Inorm_sq = 2.0f / 3.0f * (SQ(current_meas_->phA) + + SQ(current_meas_->phB) + + SQ(current_meas_->phC)); + + // Hack: we disable the current check during motor calibration because + // it tends to briefly overshoot when the motor moves to align flux with I_alpha + if (Inorm_sq > SQ(Itrip)) { + disarm_with_error(ERROR_CURRENT_LIMIT_VIOLATION); + } + } else if (is_armed_) { + // Since we can't check current limits, be safe for now and disarm. + // Theoretically we could continue to operate if there is no active + // current limit. + disarm_with_error(ERROR_UNKNOWN_CURRENT_MEASUREMENT); + } - // Apply SVM - if (!enqueue_modulation_timings(mod_alpha, mod_beta)) - return false; // error set inside enqueue_modulation_timings - log_timing(TIMING_LOG_FOC_CURRENT); + if (control_law_) { + Error err = control_law_->on_measurement(vbus_voltage, + current_meas_.has_value() ? + std::make_optional(std::array{current_meas_->phA, current_meas_->phB, current_meas_->phC}) + : std::nullopt, + timestamp); + if (err != ERROR_NONE) { + disarm_with_error(err); + } + } +} - if (axis_->axis_num_ == 0) { +/** + * @brief Called when the underlying hardware timer triggers an update event. + */ +void Motor::dc_calib_cb(uint32_t timestamp, std::optional current) { + const float dc_calib_period = static_cast(2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) / TIM_1_8_CLOCK_HZ; + TaskTimerContext tmr{axis_->task_times_.dc_calib}; + + if (current.has_value()) { + const float calib_filter_k = std::min(dc_calib_period / config_.dc_calib_tau, 1.0f); + DC_calib_.phA += (current->phA - DC_calib_.phA) * calib_filter_k; + DC_calib_.phB += (current->phB - DC_calib_.phB) * calib_filter_k; + DC_calib_.phC += (current->phC - DC_calib_.phC) * calib_filter_k; + dc_calib_running_since_ += dc_calib_period; + } else { + DC_calib_.phA = 0.0f; + DC_calib_.phB = 0.0f; + DC_calib_.phC = 0.0f; + dc_calib_running_since_ = 0.0f; + } +} - // Edit these to suit your capture needs - float trigger_data = ictrl.v_current_control_integral_d; - float trigger_threshold = 0.5f; - float sample_data = Ialpha; - static bool ready = false; - static bool capturing = false; - if (trigger_data < trigger_threshold) { - ready = true; - } - if (ready && trigger_data >= trigger_threshold) { - capturing = true; - ready = false; - } - if (capturing) { - oscilloscope[oscilloscope_pos] = sample_data; - if (++oscilloscope_pos >= OSCILLOSCOPE_SIZE) { - oscilloscope_pos = 0; - capturing = false; - } +void Motor::pwm_update_cb(uint32_t output_timestamp) { + TaskTimerContext tmr{axis_->task_times_.pwm_update}; + n_evt_pwm_update_++; + + Error control_law_status = ERROR_CONTROLLER_FAILED; + float pwm_timings[3] = {NAN, NAN, NAN}; + std::optional i_bus; + + if (control_law_) { + control_law_status = control_law_->get_output( + output_timestamp, pwm_timings, &i_bus); + } + + // Apply control law to calculate PWM duty cycles + if (is_armed_ && control_law_status == ERROR_NONE) { + uint16_t next_timings[] = { + (uint16_t)(pwm_timings[0] * (float)TIM_1_8_PERIOD_CLOCKS), + (uint16_t)(pwm_timings[1] * (float)TIM_1_8_PERIOD_CLOCKS), + (uint16_t)(pwm_timings[2] * (float)TIM_1_8_PERIOD_CLOCKS) + }; + apply_pwm_timings(next_timings, false); + } else if (is_armed_) { + if (!(timer_->Instance->BDTR & TIM_BDTR_MOE) && (control_law_status == ERROR_CONTROLLER_INITIALIZING)) { + // If the PWM output is armed in software but not yet in + // hardware we tolerate the "initializing" error. + i_bus = 0.0f; + } else { + disarm_with_error(control_law_status); } } - return true; -} + if (!is_armed_) { + // If something above failed, reset I_bus to 0A. + i_bus = 0.0f; + } else if (is_armed_ && !i_bus.has_value()) { + // If the motor is armed then i_bus must be known + disarm_with_error(ERROR_UNKNOWN_CURRENT_MEASUREMENT); + i_bus = 0.0f; + } -// torque_setpoint [Nm] -// phase [rad electrical] -// phase_vel [rad/s electrical] -bool Motor::update(float torque_setpoint, float phase, float phase_vel) { - float current_setpoint = 0.0f; - phase *= config_.direction; - phase_vel *= config_.direction; - - if (config_.motor_type == MOTOR_TYPE_ACIM) { - current_setpoint = torque_setpoint / (config_.torque_constant * fmax(current_control_.acim_rotor_flux, config_.acim_gain_min_flux)); - } - else { - current_setpoint = torque_setpoint / config_.torque_constant; - } - current_setpoint *= config_.direction; - - // TODO: 2-norm vs independent clamping (current could be sqrt(2) bigger) - float ilim = effective_current_lim(); - float id = std::clamp(current_control_.Id_setpoint, -ilim, ilim); - float iq = std::clamp(current_setpoint, -ilim, ilim); - - if (config_.motor_type == MOTOR_TYPE_ACIM) { - // Note that the effect of the current commands on the real currents is actually 1.5 PWM cycles later - // However the rotor time constant is (usually) so slow that it doesn't matter - // So we elect to write it as if the effect is immediate, to have cleaner code - - if (config_.acim_autoflux_enable) { - float abs_iq = fabsf(iq); - float gain = abs_iq > id ? config_.acim_autoflux_attack_gain : config_.acim_autoflux_decay_gain; - id += gain * (abs_iq - id) * current_meas_period; - id = std::clamp(id, config_.acim_autoflux_min_Id, ilim); - current_control_.Id_setpoint = id; - } + I_bus_ = *i_bus; - // acim_rotor_flux is normalized to units of [A] tracking Id; rotor inductance is unspecified - float dflux_by_dt = config_.acim_slip_velocity * (id - current_control_.acim_rotor_flux); - current_control_.acim_rotor_flux += dflux_by_dt * current_meas_period; - float slip_velocity = config_.acim_slip_velocity * (iq / current_control_.acim_rotor_flux); - // Check for issues with small denominator. Polarity of check to catch NaN too - bool acceptable_vel = fabsf(slip_velocity) <= 0.1f * (float)current_meas_hz; - if (!acceptable_vel) - slip_velocity = 0.0f; - phase_vel += slip_velocity; - // reporting only: - current_control_.async_phase_vel = slip_velocity; - - current_control_.async_phase_offset += slip_velocity * current_meas_period; - current_control_.async_phase_offset = wrap_pm_pi(current_control_.async_phase_offset); - phase += current_control_.async_phase_offset; - phase = wrap_pm_pi(phase); - } - - float pwm_phase = phase + 1.5f * current_meas_period * phase_vel; - - // Execute current command - switch(config_.motor_type){ - case MOTOR_TYPE_HIGH_CURRENT: return FOC_current(id, iq, phase, pwm_phase); break; - case MOTOR_TYPE_ACIM: return FOC_current(id, iq, phase, pwm_phase); break; - case MOTOR_TYPE_GIMBAL: return FOC_voltage(id, iq, pwm_phase); break; - default: set_error(ERROR_NOT_IMPLEMENTED_MOTOR_TYPE); return false; break; + if (*i_bus < config_.I_bus_hard_min || *i_bus > config_.I_bus_hard_max) { + disarm_with_error(ERROR_I_BUS_OUT_OF_RANGE); } - return true; + + update_brake_current(); } diff --git a/Firmware/MotorControl/motor.hpp b/Firmware/MotorControl/motor.hpp index b8fdcc331..7091e2538 100644 --- a/Firmware/MotorControl/motor.hpp +++ b/Firmware/MotorControl/motor.hpp @@ -1,39 +1,15 @@ #ifndef __MOTOR_HPP #define __MOTOR_HPP -#ifndef __ODRIVE_MAIN_H -#error "This file should not be included directly. Include odrive_main.h instead." -#endif +class Axis; // declared in axis.hpp +class Motor; -#include "drv8301.h" +#include +#include +#include "foc.hpp" class Motor : public ODriveIntf::MotorIntf { public: - struct Iph_BC_t { - float phB; - float phC; - }; - - struct CurrentControl_t{ - float p_gain; // [V/A] - float i_gain; // [V/As] - float v_current_control_integral_d; // [V] - float v_current_control_integral_q; // [V] - float Ibus; // DC bus current [A] - // Voltage applied at end of cycle: - float final_v_alpha; // [V] - float final_v_beta; // [V] - float Id_setpoint; // [A] - float Iq_setpoint; // [A] - float Iq_measured; // [A] - float Id_measured; // [A] - float I_measured_report_filter_k; - float max_allowed_current; // [A] - float overcurrent_trip_level; // [A] - float acim_rotor_flux; // [A] - float async_phase_vel; // [rad/s electrical] - float async_phase_offset; // [rad electrical] - }; // NOTE: for gimbal motors, all units of Nm are instead V. // example: vel_gain is [V/(turn/s)] instead of [Nm/(turn/s)] @@ -46,7 +22,6 @@ class Motor : public ODriveIntf::MotorIntf { float phase_inductance = 0.0f; // to be set by measure_phase_inductance float phase_resistance = 0.0f; // to be set by measure_phase_resistance float torque_constant = 0.04f; // [Nm/A] for PM motors, [Nm/A^2] for induction motors. Equal to 8.27/Kv of the motor - int32_t direction = 0; // 1 or -1 (0 = unspecified) MotorType motor_type = MOTOR_TYPE_HIGH_CURRENT; // Read out max_allowed_current to see max supported value for current_lim. // float current_lim = 70.0f; //[A] @@ -58,12 +33,21 @@ class Motor : public ODriveIntf::MotorIntf { float current_control_bandwidth = 1000.0f; // [rad/s] float inverter_temp_limit_lower = 100; float inverter_temp_limit_upper = 120; - float acim_slip_velocity = 14.706f; // [rad/s electrical] = 1/rotor_tau + float acim_gain_min_flux = 10; // [A] float acim_autoflux_min_Id = 10; // [A] bool acim_autoflux_enable = false; float acim_autoflux_attack_gain = 10.0f; float acim_autoflux_decay_gain = 1.0f; + + bool R_wL_FF_enable = false; // Enable feedforwards for R*I and w*L*I terms + bool bEMF_FF_enable = false; // Enable feedforward for bEMF + + float I_bus_hard_min = -INFINITY; + float I_bus_hard_max = INFINITY; + float I_leak_max = 0.1f; + + float dc_calib_tau = 0.2f; // custom property setters Motor* parent = nullptr; @@ -76,90 +60,80 @@ class Motor : public ODriveIntf::MotorIntf { void set_current_control_bandwidth(float value) { current_control_bandwidth = value; parent->update_current_controller_gains(); } }; - Motor(const MotorHardwareConfig_t& hw_config, - const GateDriverHardwareConfig_t& gate_driver_config, - Config_t& config); + Motor(TIM_HandleTypeDef* timer, + uint8_t current_sensor_mask, + float shunt_conductance, + TGateDriver& gate_driver, + TOpAmp& opamp, + OnboardThermistorCurrentLimiter& fet_thermistor, + OffboardThermistorCurrentLimiter& motor_thermistor); - bool arm(); - void disarm(); - void setup() { - DRV8301_setup(); - } - void reset_current_control(); + bool arm(PhaseControlLaw<3>* control_law); + void apply_pwm_timings(uint16_t timings[3], bool tentative); + bool disarm(bool* was_armed = nullptr); + bool apply_config(); + bool setup(); void update_current_controller_gains(); - void DRV8301_setup(); - bool check_DRV_fault(); - void set_error(Error error); - bool do_checks(); + void disarm_with_error(Error error); + bool do_checks(uint32_t timestamp); float effective_current_lim(); float max_available_torque(); - void log_timing(TimingLog_t log_idx); - float phase_current_from_adcval(uint32_t ADCValue); + std::optional phase_current_from_adcval(uint32_t ADCValue); bool measure_phase_resistance(float test_current, float max_voltage); - bool measure_phase_inductance(float voltage_low, float voltage_high); + bool measure_phase_inductance(float test_voltage); bool run_calibration(); - bool enqueue_modulation_timings(float mod_alpha, float mod_beta); - bool enqueue_voltage_timings(float v_alpha, float v_beta); - bool FOC_voltage(float v_d, float v_q, float pwm_phase); - bool FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_phase); - bool update(float current_setpoint, float phase, float phase_vel); - - const MotorHardwareConfig_t& hw_config_; - const GateDriverHardwareConfig_t gate_driver_config_; - Config_t& config_; + void update(uint32_t timestamp); + + // These functions are called as appropriate from the board.cpp file. + void current_meas_cb(uint32_t timestamp, std::optional current); + void dc_calib_cb(uint32_t timestamp, std::optional current); + void pwm_update_cb(uint32_t output_timestamp); + + // hardware config + TIM_HandleTypeDef* const timer_; + const uint8_t current_sensor_mask_; + const float shunt_conductance_; + TGateDriver& gate_driver_; + TOpAmp& opamp_; + OnboardThermistorCurrentLimiter& fet_thermistor_; + OffboardThermistorCurrentLimiter& motor_thermistor_; + + Config_t config_; Axis* axis_ = nullptr; // set by Axis constructor //private: - DRV8301_Obj gate_driver_; // initialized in constructor - uint16_t next_timings_[3] = { - TIM_1_8_PERIOD_CLOCKS / 2, - TIM_1_8_PERIOD_CLOCKS / 2, - TIM_1_8_PERIOD_CLOCKS / 2 - }; - bool next_timings_valid_ = false; - uint16_t last_cpu_time_ = 0; - int timing_log_index_ = 0; - struct { - uint16_t& operator[](size_t idx) { return content[idx]; } - uint16_t& get(size_t idx) { return content[idx]; } - uint16_t content[TIMING_LOG_NUM_SLOTS]; - } timing_log_; + uint32_t n_evt_current_measurement_ = 0; + uint32_t n_evt_pwm_update_ = 0; // variables exposed on protocol Error error_ = ERROR_NONE; + float last_error_time_ = 0.0f; // Do not write to this variable directly! // It is for exclusive use by the safety_critical_... functions. - ArmedState armed_state_ = ARMED_STATE_DISARMED; - bool is_calibrated_ = config_.pre_calibrated; - Iph_BC_t current_meas_ = {0.0f, 0.0f}; - Iph_BC_t DC_calib_ = {0.0f, 0.0f}; + bool is_armed_ = false; + uint8_t armed_state_ = 0; + bool is_calibrated_ = false; // Set in apply_config() + std::optional current_meas_; + Iph_ABC_t DC_calib_ = {0.0f, 0.0f, 0.0f}; + float dc_calib_running_since_ = 0.0f; // current sensor calibration needs some time to settle + float I_bus_ = 0.0f; // this motors contribution to the bus current float phase_current_rev_gain_ = 0.0f; // Reverse gain for ADC to Amps (to be set by DRV8301_setup) - CurrentControl_t current_control_ = { - .p_gain = 0.0f, // [V/A] should be auto set after resistance and inductance measurement - .i_gain = 0.0f, // [V/As] should be auto set after resistance and inductance measurement - .v_current_control_integral_d = 0.0f, - .v_current_control_integral_q = 0.0f, - .Ibus = 0.0f, - .final_v_alpha = 0.0f, - .final_v_beta = 0.0f, - .Id_setpoint = 0.0f, - .Iq_setpoint = 0.0f, - .Iq_measured = 0.0f, - .Id_measured = 0.0f, - .I_measured_report_filter_k = 1.0f, - .max_allowed_current = 0.0f, - .overcurrent_trip_level = 0.0f, - .acim_rotor_flux = 0.0f, - .async_phase_vel = 0.0f, - .async_phase_offset = 0.0f, - }; - struct : GateDriverIntf { - DrvFault drv_fault = DRV_FAULT_NO_FAULT; - } gate_driver_exported_; - DRV_SPI_8301_Vars_t gate_driver_regs_; //Local view of DRV registers (initialized by DRV8301_setup) - float effective_current_lim_ = 10.0f; + FieldOrientedController current_control_; + float effective_current_lim_ = 10.0f; // [A] + float max_allowed_current_ = 0.0f; // [A] set in setup() + float max_dc_calib_ = 0.0f; // [A] set in setup() + + InputPort torque_setpoint_src_; // Usually points to the Controller object's output + InputPort phase_vel_src_; // Usually points to the Encoder object's output + + float direction_ = 0.0f; // if -1 then positive torque is converted to negative Iq + OutputPort Vdq_setpoint_ = {{0.0f, 0.0f}}; // fed to the FOC + OutputPort Idq_setpoint_ = {{0.0f, 0.0f}}; // fed to the FOC + + PhaseControlLaw<3>* control_law_; }; + #endif // __MOTOR_HPP diff --git a/Firmware/MotorControl/nvm_config.hpp b/Firmware/MotorControl/nvm_config.hpp index 2a96f595e..706a8d43f 100644 --- a/Firmware/MotorControl/nvm_config.hpp +++ b/Firmware/MotorControl/nvm_config.hpp @@ -9,10 +9,9 @@ #include #include -#include -#include "nvm.h" -#include +#include +#include /* Private defines -----------------------------------------------------------*/ @@ -26,116 +25,168 @@ /* Private constant data -----------------------------------------------------*/ // IMPORTANT: if you change, reorder or otherwise modify any of the fields in -// the config structs, make sure to increment this number: +// the config structs without changing its total length, make sure to increment this number: static constexpr uint16_t config_version = 0x0001; /* Private variables ---------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ /* Function implementations --------------------------------------------------*/ - -// @brief Manages configuration load and store operations from and to NVM -// -// The NVM stores consecutive one-to-one copies of arbitrary objects. -// The types of these objects are passed as template arguments to Config. -// -// Config has two template specializations to implement template recursion: -// - Config handles loading/storing of the first object (type T) and leaves -// the rest of the objects to an "inner" class Config. -// - Config<> represents the leaf of the recursion. -template -struct Config; - -template<> -struct Config<> { - static size_t get_size() { - return 0; - } - static int load_config(size_t offset, uint16_t* crc16) { - return 0; +/** + * @brief Manages configuration load and store operations from and to NVM + * + * Usage: + * 1. start_load() + * 2. read() (as often needed) + * 3. finish_load() (to see if all reads were successful and the CRC in the end is valid) + * + * 1. prepare_store() + * 2. write() (as often as needed) + * 3. start_store() + * 4. write() (same sequence as before) + * 5. finish_store() + * + * The two store passes are required in order to measure the size on the first + * pass. If the size increases between the first and second pass, finish_store() + * will return an error. + */ +class ConfigManager { +public: + /** + * @brief Starts a load operation. This can be called at any time, even half + * way through a previous load operation. + */ + bool start_load() { + if (NVM_init() != 0) { + return (load_state = kLoadStateFailed), false; + } + load_offset = 0; + load_crc16 = CONFIG_CRC16_INIT ^ config_version; + load_state = kLoadStateInProgress; + return true; } - static int store_config(size_t offset, uint16_t* crc16) { - return 0; + + /** + * @brief Loads the next chunk from NVM. + * Note that this may return true even if invalid data was read. The user + * will know the final verdict by the return value of finish_load(). + */ + template + bool read(T* val) { + if (load_state != 1) { + return (load_state = kLoadStateFailed), false; + } + size_t size = sizeof(T); + if (NVM_read(load_offset, (uint8_t *)val, size) != 0) + return (load_state = kLoadStateFailed), false; + load_crc16 = calc_crc16(load_crc16, (uint8_t *)val, size); + load_offset += size; + return true; } -}; -template -struct Config { - static size_t get_size() { - return sizeof(T) + Config::get_size(); + /** + * @brief Checks the final state of the load operation. + * If this function returns false, it is possible that previous read() + * operations actually returned garbage. + */ + bool finish_load(size_t* occupied_size) { + if (occupied_size) { + *occupied_size = load_offset + 2; + } + + uint16_t crc16_calculated = load_crc16; + uint16_t crc16_loaded; + if (!read(&crc16_loaded)) { + return (load_state = kLoadStateFailed), false; + } + bool result = (load_state == 1) && (crc16_loaded == crc16_calculated); + load_state = kLoadStateIdle; + return result; } - // @brief Loads one or more consecutive objects from the NVM. - // During loading this function also calculates the CRC over the loaded data. - // @param offset: 0 means that the function should start reading at the beginning - // of the last comitted NVM block - // @param crc16: the result of the CRC calculation is written to this address - // @param val0, vals: the values to be loaded - static int load_config(size_t offset, uint16_t* crc16, T* val0, Ts* ... vals) { - size_t size = sizeof(T); - // save current CRC (in case val0 and crc16 point to the same address) - size_t previous_crc16 = *crc16; - if (NVM_read(offset, (uint8_t *)val0, size)) - return -1; - *crc16 = calc_crc16(previous_crc16, (uint8_t *)val0, size); - if (Config::load_config(offset + size, crc16, vals...)) - return -1; - return 0; + /** + * @brief Starts preparation of a new store operation. + */ + bool prepare_store() { + if (store_state != kStoreStateIdle) { + // it might be possible to restart the store process from other states but let's be safe + return (store_state = kStoreStateFailed), false; + } + store_offset = 0; + store_crc16 = CONFIG_CRC16_INIT ^ config_version; + store_state = kStoreStatePreparing; + return true; } - // @brief Stores one or more consecutive objects to the NVM. - // During storing this function also calculates the CRC over the stored data. - // @param offset: 0 means that the function should start writing at the beginning - // of the currently active NVM write block - // @param crc16: the result of the CRC calculation is written to this address - // @param val0, vals: the values to be stored - static int store_config(size_t offset, uint16_t* crc16, const T* val0, const Ts* ... vals) { - size_t size = sizeof(T); - if (NVM_write(offset, (uint8_t *)val0, size)) - return -1; - // update CRC _after_ writing (in case val0 and crc16 point to the same address) - if (crc16) - *crc16 = calc_crc16(*crc16, (uint8_t *)val0, size); - if (Config::store_config(offset + size, crc16, vals...)) - return -1; - return 0; + template + bool write(T* val) { + if (store_state == kStoreStateInProgress) { + if (NVM_write(store_offset, (uint8_t*)val, sizeof(T)) != 0) { + return (store_state = kStoreStateFailed), false; + } + } else if (store_state != kStoreStatePreparing) { + return (store_state = kStoreStateFailed), false; + } + store_crc16 = calc_crc16(store_crc16, (uint8_t *)val, sizeof(T)); + store_offset += sizeof(T); + return true; } - // @brief Loads one or more consecutive objects from the NVM. The loaded data - // is validated using a CRC value that is stored at the beginning of the data. - static int safe_load_config(T* val0, Ts* ... vals) { - //printf("have %d bytes\r\n", NVM_get_max_read_length()); osDelay(5); - if (Config::get_size() > NVM_get_max_read_length()) - return -1; - uint16_t crc16 = CONFIG_CRC16_INIT ^ config_version; - if (Config::load_config(0, &crc16, val0, vals..., &crc16)) - return -1; - if (crc16) - return -1; - return 0; + /** + * @brief Finishes the prepare pass and starts the actual store pass. + */ + bool start_store(size_t* occupied_size) { + if (occupied_size) { + *occupied_size = store_offset + 2; + } + + if (store_state != kStoreStatePreparing) { + return (store_state = kStoreStateFailed), false; + } + store_offset += 2; // account for CRC16 + if (store_offset > NVM_get_max_write_length()) { + return (store_state = kStoreStateFailed), false; + } + if (NVM_start_write(store_offset) != 0) { + return (store_state = kStoreStateFailed), false; + } + store_offset = 0; + store_crc16 = CONFIG_CRC16_INIT ^ config_version; + store_state = kStoreStateInProgress; + return true; } - // @brief Stores one or more consecutive objects to the NVM. In addition to the - // provided objects, a CRC of the data is stored. - // - // The CRC includes a version number and thus adds some protection against - // changes of the config structs during firmware update. Note that if the total - // config data length changes, the CRC validation will fail even if the developer - // forgets to update the config version number. - static int safe_store_config(const T* val0, const Ts* ... vals) { - size_t size = Config::get_size() + 2; - //printf("config is %d bytes\r\n", size); osDelay(5); - if (size > NVM_get_max_write_length()) - return -1; - if (NVM_start_write(size)) - return -1; - uint16_t crc16 = CONFIG_CRC16_INIT ^ config_version; - if (Config::store_config(0, &crc16, val0, vals...)) - return -1; - if (Config::store_config(size - 2, nullptr, (uint8_t *)&crc16 + 1, (uint8_t *)&crc16)) - return -1; - if (NVM_commit()) - return -1; - return 0; + /** + * @brief Commits the store operation. + * If this function succeeds, the new configuration was successfully saved. + * If this function fails, the old configuration was not touched. + */ + bool finish_store() { + uint16_t crc16 = store_crc16; + if (!write(&crc16)) { + return (store_state = kStoreStateFailed), false; + } + if (NVM_commit() != 0) { + return (store_state = kStoreStateFailed), false; + } + store_state = kStoreStateIdle; + return true; } + + enum { + kLoadStateIdle = 0, + kLoadStateInProgress = 1, + kLoadStateFailed = 2 + } load_state = kLoadStateIdle; + size_t load_offset; + size_t load_crc16; + + enum { + kStoreStateIdle = 0, + kStoreStatePreparing = 1, + kStoreStateInProgress = 2, + kStoreStateFailed = 3 + } store_state = kStoreStateIdle; + size_t store_offset; + size_t store_crc16; }; diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 392b9c7eb..fddc6b440 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -1,46 +1,20 @@ #ifndef __ODRIVE_MAIN_H #define __ODRIVE_MAIN_H -// Note on central include scheme by Samuel: -// there are circular dependencies between some of the header files, -// e.g. the Motor header needs a forward declaration of Axis and vice versa -// so I figured I'd make one main header that takes care of -// the forward declarations and right ordering -// btw this pattern is not so uncommon, for instance IIRC the stdlib uses it too +// Hardware configuration +#include #ifdef __cplusplus -#include #include #include +#include +#include extern "C" { #endif -// STM specific includes -#include // Sets up the correct chip specifc defines required by arm_math -#include -#include -#define ARM_MATH_CM4 // TODO: might change in future board versions -#include - // OS includes #include -// Hardware configuration -#if HW_VERSION_MAJOR == 3 -#include "board_config_v3.h" -#else -#error "unknown board version" -#endif - -//default timeout waiting for phase measurement signals -#define PH_CURRENT_MEAS_TIMEOUT 2 // [ms] - -// Period in [s] -static const float current_meas_period = CURRENT_MEAS_PERIOD; - -// Frequency in [Hz] -static const int current_meas_hz = CURRENT_MEAS_HZ; - // extern const float elec_rad_per_enc; extern uint32_t _reboot_cookie; @@ -54,46 +28,56 @@ typedef struct { bool fully_booted; uint32_t uptime; // [ms] uint32_t min_heap_space; // FreeRTOS heap [Bytes] - uint32_t min_stack_space_axis0; // minimum remaining space since startup [Bytes] - uint32_t min_stack_space_axis1; - uint32_t min_stack_space_comms; - uint32_t min_stack_space_usb; - uint32_t min_stack_space_uart; - uint32_t min_stack_space_usb_irq; - uint32_t min_stack_space_startup; - uint32_t min_stack_space_can; - - uint32_t stack_usage_axis0; - uint32_t stack_usage_axis1; - uint32_t stack_usage_comms; - uint32_t stack_usage_usb; - uint32_t stack_usage_uart; - uint32_t stack_usage_usb_irq; - uint32_t stack_usage_startup; - uint32_t stack_usage_can; + uint32_t max_stack_usage_axis; // minimum remaining space since startup [Bytes] + uint32_t max_stack_usage_usb; + uint32_t max_stack_usage_uart; + uint32_t max_stack_usage_startup; + uint32_t max_stack_usage_can; + + uint32_t stack_size_axis; + uint32_t stack_size_usb; + uint32_t stack_size_uart; + uint32_t stack_size_startup; + uint32_t stack_size_can; + + int32_t prio_axis; + int32_t prio_usb; + int32_t prio_uart; + int32_t prio_startup; + int32_t prio_can; USBStats_t& usb = usb_stats_; I2CStats_t& i2c = i2c_stats_; } SystemStats_t; struct PWMMapping_t { - endpoint_ref_t endpoint; + endpoint_ref_t endpoint = {0, 0}; float min = 0; float max = 0; }; // @brief general user configurable board configuration struct BoardConfig_t { - bool enable_uart = true; - bool enable_i2c_instead_of_can = false; - bool enable_ascii_protocol_on_usb = true; + ODriveIntf::GpioMode gpio_modes[GPIO_COUNT] = { + DEFAULT_GPIO_MODES + }; + + bool enable_uart_a = true; + bool enable_uart_b = false; + bool enable_uart_c = false; + uint32_t uart_a_baudrate = 115200; + uint32_t uart_b_baudrate = 115200; + uint32_t uart_c_baudrate = 115200; + bool enable_can_a = true; + bool enable_i2c_a = false; + ODriveIntf::StreamProtocolType uart0_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT; + ODriveIntf::StreamProtocolType uart1_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT; + ODriveIntf::StreamProtocolType uart2_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT; + ODriveIntf::StreamProtocolType usb_cdc_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT; float max_regen_current = 0.0f; -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 5 && HW_VERSION_VOLTAGE >= 48 - float brake_resistance = 2.0f; // [ohm] -#else - float brake_resistance = 0.47f; // [ohm] -#endif - float dc_bus_undervoltage_trip_level = 8.0f; // axes; -extern ODriveCAN *odCAN; - -// if you use the oscilloscope feature you can bump up this value -#define OSCILLOSCOPE_SIZE 4096 -extern float oscilloscope[OSCILLOSCOPE_SIZE]; -extern size_t oscilloscope_pos; // TODO: move // this is technically not thread-safe but practically it might be @@ -177,31 +134,11 @@ inline ENUMTYPE &operator &= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast inline ENUMTYPE &operator ^= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast(reinterpret_cast&>(a) ^= static_cast>(b)); } \ inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast(~static_cast>(a)); } - -enum TimingLog_t { - TIMING_LOG_GENERAL, - TIMING_LOG_ADC_CB_I, - TIMING_LOG_ADC_CB_DC, - TIMING_LOG_MEAS_R, - TIMING_LOG_MEAS_L, - TIMING_LOG_ENC_CALIB, - TIMING_LOG_IDX_SEARCH, - TIMING_LOG_FOC_VOLTAGE, - TIMING_LOG_FOC_CURRENT, - TIMING_LOG_SPI_START, - TIMING_LOG_SAMPLE_NOW, - TIMING_LOG_SPI_END, - TIMING_LOG_NUM_SLOTS -}; - - #include "autogen/interfaces.hpp" // ODrive specific includes #include -#include #include -#include #include #include #include @@ -209,8 +146,11 @@ enum TimingLog_t { #include #include #include +#include #include +#include #include +#include // Defined in autogen/version.c based on git-derived version numbers extern "C" { @@ -220,21 +160,22 @@ extern const unsigned char fw_version_revision_; extern const unsigned char fw_version_unreleased_; } +static Stm32Gpio get_gpio(size_t gpio_num) { + return (gpio_num < GPIO_COUNT) ? gpios[gpio_num] : GPIO_COUNT ? gpios[0] : Stm32Gpio::none; +} // general system functions defined in main.cpp class ODrive : public ODriveIntf { public: - void save_configuration() override; + bool save_configuration() override; void erase_configuration() override; void reboot() override { NVIC_SystemReset(); } void enter_dfu_mode() override; - - float get_oscilloscope_val(uint32_t index) override { - return oscilloscope[index]; - } + bool any_error(); + void clear_errors() override; float get_adc_voltage(uint32_t gpio) override { - return ::get_adc_voltage(get_gpio_port_by_pin(gpio), get_gpio_pin_by_pin(gpio)); + return ::get_adc_voltage(get_gpio(gpio)); } int32_t test_function(int32_t delta) override { @@ -242,36 +183,30 @@ class ODrive : public ODriveIntf { return cnt += delta; } - Axis& get_axis(int num) { return *axes[num]; } - ODriveCAN& get_can() { return *odCAN; } + void do_fast_checks(); + void sampling_cb(); + void control_loop_cb(uint32_t timestamp); + + Axis& get_axis(int num) { return axes[num]; } + uint32_t get_interrupt_status(int32_t irqn); + uint32_t get_dma_status(uint8_t stream_num); + uint32_t get_gpio_states(); + uint64_t get_drv_fault(); + void disarm_with_error(Error error); + + Error error_ = ERROR_NONE; float& vbus_voltage_ = ::vbus_voltage; // TODO: make this the actual variable float& ibus_ = ::ibus_; // TODO: make this the actual variable float ibus_report_filter_k_ = 1.0f; const uint64_t& serial_number_ = ::serial_number; -#if HW_VERSION_MAJOR == 3 - // Determine start address of the OTP struct: - // The OTP is organized into 16-byte blocks. - // If the first block starts with "0xfe" we use the first block. - // If the first block starts with "0x00" and the second block starts with "0xfe", - // we use the second block. This gives the user the chance to screw up once. - // If none of the above is the case, we consider the OTP invalid (otp_ptr will be NULL). - const uint8_t* otp_ptr = - (*(uint8_t*)FLASH_OTP_BASE == 0xfe) ? (uint8_t*)FLASH_OTP_BASE : - (*(uint8_t*)FLASH_OTP_BASE != 0x00) ? NULL : - (*(uint8_t*)(FLASH_OTP_BASE + 0x10) != 0xfe) ? NULL : - (uint8_t*)(FLASH_OTP_BASE + 0x10); - - // Read hardware version from OTP if available, otherwise fall back - // to software defined version. - const uint8_t hw_version_major_ = otp_ptr ? otp_ptr[3] : HW_VERSION_MAJOR; - const uint8_t hw_version_minor_ = otp_ptr ? otp_ptr[4] : HW_VERSION_MINOR; - const uint8_t hw_version_variant_ = otp_ptr ? otp_ptr[5] : HW_VERSION_VOLTAGE; -#else -#error "not implemented" -#endif + // Hardware version is compared with OTP on startup to ensure that we're + // running on the right board version. + const uint8_t hw_version_major_ = HW_VERSION_MAJOR; + const uint8_t hw_version_minor_ = HW_VERSION_MINOR; + const uint8_t hw_version_variant_ = HW_VERSION_VOLTAGE; // the corresponding macros are defined in the autogenerated version.h const uint8_t fw_version_major_ = ::fw_version_major_; @@ -284,10 +219,26 @@ class ODrive : public ODriveIntf { SystemStats_t system_stats_; + // Edit these to suit your capture needs + Oscilloscope oscilloscope_{ + nullptr, // trigger_src + 0.5f, // trigger_threshold + nullptr // data_src TODO: change data type + }; + + ODriveCAN can_; + BoardConfig_t config_; - bool user_config_loaded_; + uint32_t user_config_loaded_ = 0; + bool misconfigured_ = false; uint32_t test_property_ = 0; + + uint32_t last_update_timestamp_ = 0; + uint32_t n_evt_sampling_ = 0; + uint32_t n_evt_control_loop_ = 0; + bool task_timers_armed_ = false; + TaskTimes task_times_; }; extern ODrive odrv; // defined in main.cpp diff --git a/Firmware/MotorControl/open_loop_controller.cpp b/Firmware/MotorControl/open_loop_controller.cpp new file mode 100644 index 000000000..7d21df980 --- /dev/null +++ b/Firmware/MotorControl/open_loop_controller.cpp @@ -0,0 +1,30 @@ + +#include "open_loop_controller.hpp" +#include + +void OpenLoopController::update(uint32_t timestamp) { + auto [prev_Id, prev_Iq] = Idq_setpoint_.previous().value_or(float2D{0.0f, 0.0f}); + auto [prev_Vd, prev_Vq] = Vdq_setpoint_.previous().value_or(float2D{0.0f, 0.0f}); + float phase = phase_.previous().value_or(initial_phase_); + float phase_vel = phase_vel_.previous().value_or(0.0f); + + (void)prev_Iq; // unused + (void)prev_Vq; // unused + + float dt = (float)(timestamp - timestamp_) / (float)TIM_1_8_CLOCK_HZ; + + Idq_setpoint_ = { + std::clamp(target_current_, prev_Id - max_current_ramp_ * dt, prev_Id + max_current_ramp_ * dt), + 0.0f + }; + Vdq_setpoint_ = { + std::clamp(target_voltage_, prev_Vd - max_voltage_ramp_ * dt, prev_Vd + max_voltage_ramp_ * dt), + 0.0f + }; + + phase_vel = std::clamp(target_vel_, phase_vel - max_phase_vel_ramp_ * dt, phase_vel + max_phase_vel_ramp_ * dt); + phase_vel_ = phase_vel; + phase_ = wrap_pm_pi(phase + phase_vel * dt); + total_distance_ = total_distance_.previous().value_or(0.0f) + phase_vel * dt; + timestamp_ = timestamp; +} diff --git a/Firmware/MotorControl/open_loop_controller.hpp b/Firmware/MotorControl/open_loop_controller.hpp new file mode 100644 index 000000000..54371bd3b --- /dev/null +++ b/Firmware/MotorControl/open_loop_controller.hpp @@ -0,0 +1,32 @@ +#ifndef __OPEN_LOOP_CONTROLLER_HPP +#define __OPEN_LOOP_CONTROLLER_HPP + +#include "component.hpp" +#include +#include + +class OpenLoopController : public ComponentBase { +public: + void update(uint32_t timestamp) final; + + // Config + float max_current_ramp_ = INFINITY; // [A/s] + float max_voltage_ramp_ = INFINITY; // [V/s] + float max_phase_vel_ramp_ = INFINITY; // [rad/s^2] + + // Inputs + float target_vel_ = 0.0f; + float target_current_ = 0.0f; + float target_voltage_ = 0.0f; + float initial_phase_ = 0.0f; + + // State/Outputs + uint32_t timestamp_ = 0; + OutputPort Idq_setpoint_ = {{0.0f, 0.0f}}; + OutputPort Vdq_setpoint_ = {{0.0f, 0.0f}}; + OutputPort phase_ = 0.0f; + OutputPort phase_vel_ = 0.0f; + OutputPort total_distance_ = 0.0f; +}; + +#endif // __OPEN_LOOP_CONTROLLER_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/oscilloscope.cpp b/Firmware/MotorControl/oscilloscope.cpp new file mode 100644 index 000000000..21320fd65 --- /dev/null +++ b/Firmware/MotorControl/oscilloscope.cpp @@ -0,0 +1,27 @@ + +#include "oscilloscope.hpp" + +// if you use the oscilloscope feature you can bump up this value +#define OSCILLOSCOPE_SIZE 4096 + +void Oscilloscope::update() { + float trigger_data = trigger_src_ ? *trigger_src_ : 0.0f; + float trigger_threshold = trigger_threshold_; + float sample_data = data_src_ ? **data_src_ : 0.0f; + + if (trigger_data < trigger_threshold) { + ready_ = true; + } + if (ready_ && trigger_data >= trigger_threshold) { + capturing_ = true; + ready_ = false; + } + if (capturing_) { + if (pos_ < OSCILLOSCOPE_SIZE) { + data_[pos_++] = sample_data; + } else { + pos_ = 0; + capturing_ = false; + } + } +} diff --git a/Firmware/MotorControl/oscilloscope.hpp b/Firmware/MotorControl/oscilloscope.hpp new file mode 100644 index 000000000..df6dcd2a5 --- /dev/null +++ b/Firmware/MotorControl/oscilloscope.hpp @@ -0,0 +1,31 @@ +#ifndef __OSCILLOSCOPE_HPP +#define __OSCILLOSCOPE_HPP + +#include + +// if you use the oscilloscope feature you can bump up this value +#define OSCILLOSCOPE_SIZE 4096 + +class Oscilloscope : public ODriveIntf::OscilloscopeIntf { +public: + Oscilloscope(float* trigger_src, float trigger_threshold, float** data_src) + : trigger_src_(trigger_src), trigger_threshold_(trigger_threshold), data_src_(data_src) {} + + float get_val(uint32_t index) override { + return index < OSCILLOSCOPE_SIZE ? data_[index] : NAN; + } + + void update(); + + const uint32_t size_ = OSCILLOSCOPE_SIZE; + const float* trigger_src_; + const float trigger_threshold_; + float* const * data_src_; + + float data_[OSCILLOSCOPE_SIZE] = {0}; + size_t pos_ = 0; + bool ready_ = false; + bool capturing_ = false; +}; + +#endif // __OSCILLOSCOPE_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/phase_control_law.hpp b/Firmware/MotorControl/phase_control_law.hpp new file mode 100644 index 000000000..1a95ac29b --- /dev/null +++ b/Firmware/MotorControl/phase_control_law.hpp @@ -0,0 +1,97 @@ +#ifndef __PHASE_CONTROL_LAW_HPP +#define __PHASE_CONTROL_LAW_HPP + +#include +#include + +template +class PhaseControlLaw { +public: + /** + * @brief Called when this controller becomes the active controller. + */ + virtual void reset() = 0; + + /** + * @brief Informs the control law about a new set of measurements. + * + * This function gets called in a high priority interrupt context and should + * run fast. + * + * Beware that all inputs can be NAN. + * + * @param vbus_voltage: The most recently measured DC link voltage. Can be + * std::nullopt if the measurement is not available or valid for any + * reason. + * @param currents: The most recently measured (or inferred) phase currents + * in Amps. Can be std::nullopt if no valid measurements are available + * (e.g. because the opamp isn't started or because the sensors were + * saturated). + * @param input_timestamp: The timestamp (in HCLK ticks) corresponding to + * the vbus_voltage and current measurement. + */ + virtual ODriveIntf::MotorIntf::Error on_measurement( + std::optional vbus_voltage, + std::optional> currents, + uint32_t input_timestamp) = 0; + + /** + * @brief Shall calculate the PWM timings for the specified target time. + * + * This function gets called in a high priority interrupt context and should + * run fast. + * + * Beware that this function can be called before a call to on_measurement(). + * + * @param output_timestamp: The timestamp (in HCLK ticks) corresponding to + * the middle of the time span during which the output will be + * active. + * @param pwm_timings: This array referenced by this argument shall be + * filled with the desired PWM timings. Each item corresponds to one + * phase and must lie in [0.0f, 1.0f]. + * The function is not required to return valid PWM timings in case + * of an error. + * @param ibus: The variable pointed to by this argument is set to the + * estimated DC current around the output timestamp when the desired + * PWM timings get applied. + * The function is not required to return a valid I_bus estimate in + * case of an error. + * + * @returns: An error code or ERROR_NONE. If the function returns an error + * the motor gets disarmed with one exception: If the controller + * never returned valid PWM timings since it became active then it + * is allowed to return ERROR_CONTROLLER_INITIALIZING without + * triggering a motor disarm. In this phase the PWMs will not yet + * be truly active. + */ + virtual ODriveIntf::MotorIntf::Error get_output( + uint32_t output_timestamp, + float (&pwm_timings)[N_PHASES], + std::optional* ibus) = 0; +}; + +class AlphaBetaFrameController : public PhaseControlLaw<3> { +private: + ODriveIntf::MotorIntf::Error on_measurement( + std::optional vbus_voltage, + std::optional> currents, + uint32_t input_timestamp) final; + + ODriveIntf::MotorIntf::Error get_output( + uint32_t output_timestamp, + float (&pwm_timings)[3], + std::optional* ibus) final; + +protected: + virtual ODriveIntf::MotorIntf::Error on_measurement( + std::optional vbus_voltage, + std::optional Ialpha_beta, + uint32_t input_timestamp) = 0; + + virtual ODriveIntf::MotorIntf::Error get_alpha_beta_output( + uint32_t output_timestamp, + std::optional* mod_alpha_beta, + std::optional* ibus) = 0; +}; + +#endif // __PHASE_CONTROL_LAW_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/pwm_input.cpp b/Firmware/MotorControl/pwm_input.cpp new file mode 100644 index 000000000..176b7eb23 --- /dev/null +++ b/Firmware/MotorControl/pwm_input.cpp @@ -0,0 +1,91 @@ + +#include "pwm_input.hpp" +#include "odrive_main.h" + +void PwmInput::init() { + TIM_IC_InitTypeDef sConfigIC; + sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE; + sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; + sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; + sConfigIC.ICFilter = 15; + + uint32_t channels[] = {TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4}; + + for (size_t i = 0; i < 4; ++i) { + if (!fibre::is_endpoint_ref_valid(odrv.config_.pwm_mappings[i].endpoint)) + continue; + HAL_TIM_IC_ConfigChannel(htim_, &sConfigIC, channels[i]); + HAL_TIM_IC_Start_IT(htim_, channels[i]); + } +} + +//TODO: These expressions have integer division by 1MHz, so it will be incorrect for clock speeds of not-integer MHz +#define TIM_2_5_CLOCK_HZ TIM_APB1_CLOCK_HZ +#define PWM_MIN_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 1000UL) // 1ms high is considered full reverse +#define PWM_MAX_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2000UL) // 2ms high is considered full forward +#define PWM_MIN_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 500UL) // ignore high periods shorter than 0.5ms +#define PWM_MAX_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2500UL) // ignore high periods longer than 2.5ms +#define PWM_INVERT_INPUT false + +/** + * @param channel: A channel number in [0, 3] + */ +void handle_pulse(int channel, uint32_t high_time) { + if (high_time < PWM_MIN_LEGAL_HIGH_TIME || high_time > PWM_MAX_LEGAL_HIGH_TIME) + return; + + if (high_time < PWM_MIN_HIGH_TIME) + high_time = PWM_MIN_HIGH_TIME; + if (high_time > PWM_MAX_HIGH_TIME) + high_time = PWM_MAX_HIGH_TIME; + float fraction = (float)(high_time - PWM_MIN_HIGH_TIME) / (float)(PWM_MAX_HIGH_TIME - PWM_MIN_HIGH_TIME); + float value = odrv.config_.pwm_mappings[channel].min + + (fraction * (odrv.config_.pwm_mappings[channel].max - odrv.config_.pwm_mappings[channel].min)); + + fibre::set_endpoint_from_float(odrv.config_.pwm_mappings[channel].endpoint, value); +} + +/** + * @param channel: A channel number in [0, 3] + */ +void PwmInput::on_capture(int channel, uint32_t timestamp) { + static uint32_t last_timestamp[4] = { 0 }; + static bool last_pin_state[4] = { false }; + static bool last_sample_valid[4] = { false }; + + if (channel >= 4) + return; + Stm32Gpio gpio = get_gpio(gpios_[channel]); + if (!gpio) + return; + bool current_pin_state = gpio.read(); + + if (last_sample_valid[channel] + && (last_pin_state[channel] != PWM_INVERT_INPUT) + && (current_pin_state == PWM_INVERT_INPUT)) { + handle_pulse(channel, timestamp - last_timestamp[channel]); + } + + last_timestamp[channel] = timestamp; + last_pin_state[channel] = current_pin_state; + last_sample_valid[channel] = true; +} + +void PwmInput::on_capture() { + if(__HAL_TIM_GET_FLAG(htim_, TIM_FLAG_CC1)) { + __HAL_TIM_CLEAR_IT(htim_, TIM_IT_CC1); + on_capture(0, htim_->Instance->CCR1); + } + if(__HAL_TIM_GET_FLAG(htim_, TIM_FLAG_CC2)) { + __HAL_TIM_CLEAR_IT(htim_, TIM_IT_CC2); + on_capture(1, htim_->Instance->CCR2); + } + if(__HAL_TIM_GET_FLAG(htim_, TIM_FLAG_CC3)) { + __HAL_TIM_CLEAR_IT(htim_, TIM_IT_CC3); + on_capture(2, htim_->Instance->CCR3); + } + if(__HAL_TIM_GET_FLAG(htim_, TIM_FLAG_CC4)) { + __HAL_TIM_CLEAR_IT(htim_, TIM_IT_CC4); + on_capture(3, htim_->Instance->CCR4); + } +} diff --git a/Firmware/MotorControl/pwm_input.hpp b/Firmware/MotorControl/pwm_input.hpp new file mode 100644 index 000000000..679dee662 --- /dev/null +++ b/Firmware/MotorControl/pwm_input.hpp @@ -0,0 +1,22 @@ +#ifndef __PWM_INPUT_HPP +#define __PWM_INPUT_HPP + +#include +#include + +class PwmInput { +public: + PwmInput(TIM_HandleTypeDef* htim, std::array gpios) + : htim_(htim), gpios_(gpios) {} + + void init(); + void on_capture(); + +private: + void on_capture(int channel, uint32_t timestamp); + + TIM_HandleTypeDef* htim_; + std::array gpios_; +}; + +#endif // __PWM_INPUT_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/sensorless_estimator.cpp b/Firmware/MotorControl/sensorless_estimator.cpp index 4c521b34a..94c09c680 100644 --- a/Firmware/MotorControl/sensorless_estimator.cpp +++ b/Firmware/MotorControl/sensorless_estimator.cpp @@ -1,9 +1,14 @@ #include "odrive_main.h" -SensorlessEstimator::SensorlessEstimator(Config_t& config) : - config_(config) - {}; +void SensorlessEstimator::reset() { + pll_pos_ = 0.0f; + vel_estimate_ = 0.0f; + V_alpha_beta_memory_[0] = 0.0f; + V_alpha_beta_memory_[1] = 0.0f; + flux_state_[0] = 0.0f; + flux_state_[1] = 0.0f; +} bool SensorlessEstimator::update() { // Algorithm based on paper: Sensorless Control of Surface-Mount Permanent-Magnet Synchronous Motors Based on a Nonlinear Observer @@ -14,13 +19,38 @@ bool SensorlessEstimator::update() { // is the one computed two cycles ago. To get the correct measurement, it was stored twice: // once by final_v_alpha/final_v_beta in the current control reporting, and once by V_alpha_beta_memory. + // PLL + // TODO: the PLL part has some code duplication with the encoder PLL + // Pll gains as a function of bandwidth + float pll_kp = 2.0f * config_.pll_bandwidth; + // Critically damped + float pll_ki = 0.25f * (pll_kp * pll_kp); + + // Check that we don't get problems with discrete time approximation + if (!(current_meas_period * pll_kp < 1.0f)) { + error_ |= ERROR_UNSTABLE_GAIN; + reset(); // Reset state for when the next valid current measurement comes in. + return false; + } + + // TODO: we read values here which are modified by a higher priority interrupt. + // This is not thread-safe. + auto current_meas = axis_->motor_.current_meas_; + if (!axis_->motor_.is_armed_) { + // While the motor is disarmed the current is not measurable so we + // assume that it's zero. + current_meas = {0.0f, 0.0f}; + } + if (!current_meas.has_value()) { + error_ |= ERROR_UNKNOWN_CURRENT_MEASUREMENT; + reset(); // Reset state for when the next valid current measurement comes in. + return false; + } + // Clarke transform float I_alpha_beta[2] = { - -axis_->motor_.current_meas_.phB - axis_->motor_.current_meas_.phC, - one_by_sqrt3 * (axis_->motor_.current_meas_.phB - axis_->motor_.current_meas_.phC)}; - - // Swap sign of I_beta if motor is reversed - I_alpha_beta[1] *= axis_->motor_.config_.direction; + current_meas->phA, + one_by_sqrt3 * (current_meas->phB - current_meas->phC)}; // alpha-beta vector operations float eta[2]; @@ -53,33 +83,24 @@ bool SensorlessEstimator::update() { } // Flux state estimation done, store V_alpha_beta for next timestep - V_alpha_beta_memory_[0] = axis_->motor_.current_control_.final_v_alpha; - V_alpha_beta_memory_[1] = axis_->motor_.current_control_.final_v_beta * axis_->motor_.config_.direction; + V_alpha_beta_memory_[0] = axis_->motor_.current_control_.final_v_alpha_; + V_alpha_beta_memory_[1] = axis_->motor_.current_control_.final_v_beta_; - // PLL - // TODO: the PLL part has some code duplication with the encoder PLL - // Pll gains as a function of bandwidth - float pll_kp = 2.0f * config_.pll_bandwidth; - // Critically damped - float pll_ki = 0.25f * (pll_kp * pll_kp); - // Check that we don't get problems with discrete time approximation - if (!(current_meas_period * pll_kp < 1.0f)) { - error_ |= ERROR_UNSTABLE_GAIN; - vel_estimate_valid_ = false; - return false; - } + float phase_vel = phase_vel_.previous().value_or(0.0f); // predict PLL phase with velocity - pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * vel_estimate_erad_); + pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * phase_vel); // update PLL phase with observer permanent magnet phase - phase_ = fast_atan2(eta[1], eta[0]); - float delta_phase = wrap_pm_pi(phase_ - pll_pos_); + float phase = fast_atan2(eta[1], eta[0]); + float delta_phase = wrap_pm_pi(phase - pll_pos_); pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * pll_kp * delta_phase); // update PLL velocity - vel_estimate_erad_ += current_meas_period * pll_ki * delta_phase; - // convert to mechanical turns/s for controller usage. - vel_estimate_ = vel_estimate_erad_ / (std::max((float)axis_->motor_.config_.pole_pairs, 1.0f) * 2.0f * M_PI); + phase_vel += current_meas_period * pll_ki * delta_phase; + + // set outputs + phase_ = phase; + phase_vel_ = phase_vel; + vel_estimate_ = phase_vel / (std::max((float)axis_->motor_.config_.pole_pairs, 1.0f) * 2.0f * M_PI); - vel_estimate_valid_ = true; return true; }; diff --git a/Firmware/MotorControl/sensorless_estimator.hpp b/Firmware/MotorControl/sensorless_estimator.hpp index 27f6f1c90..3ac6f4885 100644 --- a/Firmware/MotorControl/sensorless_estimator.hpp +++ b/Firmware/MotorControl/sensorless_estimator.hpp @@ -1,6 +1,8 @@ #ifndef __SENSORLESS_ESTIMATOR_HPP #define __SENSORLESS_ESTIMATOR_HPP +#include "component.hpp" + class SensorlessEstimator : public ODriveIntf::SensorlessEstimatorIntf { public: struct Config_t { @@ -9,25 +11,21 @@ class SensorlessEstimator : public ODriveIntf::SensorlessEstimatorIntf { float pm_flux_linkage = 1.58e-3f; // [V / (rad/s)] { 5.51328895422 / ( * ) } }; - explicit SensorlessEstimator(Config_t& config); - + void reset(); bool update(); Axis* axis_ = nullptr; // set by Axis constructor - Config_t& config_; + Config_t config_; // TODO: expose on protocol Error error_ = ERROR_NONE; - float phase_ = 0.0f; // [rad] float pll_pos_ = 0.0f; // [rad] - float vel_estimate_ = 0.0f; // [turn/s] - float vel_estimate_erad_ = 0.0f; // [rad/s] - bool vel_estimate_valid_ = false; - // float pll_kp_ = 0.0f; // [rad/s / rad] - // float pll_ki_ = 0.0f; // [(rad/s^2) / rad] float flux_state_[2] = {0.0f, 0.0f}; // [Vs] float V_alpha_beta_memory_[2] = {0.0f, 0.0f}; // [V] - bool estimator_good_ = false; + + OutputPort phase_ = 0.0f; // [rad] + OutputPort phase_vel_ = 0.0f; // [rad/s] + OutputPort vel_estimate_ = 0.0f; // [turns/s] }; #endif /* __SENSORLESS_ESTIMATOR_HPP */ diff --git a/Firmware/MotorControl/task_timer.hpp b/Firmware/MotorControl/task_timer.hpp new file mode 100644 index 000000000..a6506de45 --- /dev/null +++ b/Firmware/MotorControl/task_timer.hpp @@ -0,0 +1,65 @@ +#ifndef __TASK_TIMER_HPP +#define __TASK_TIMER_HPP + +#include +#include + +#define MEASURE_START_TIME +#define MEASURE_END_TIME +#define MEASURE_LENGTH +#define MEASURE_MAX_LENGTH + +inline uint16_t sample_TIM13() { + constexpr uint16_t clocks_per_cnt = (uint16_t)((float)TIM_1_8_CLOCK_HZ / (float)TIM_APB1_CLOCK_HZ); + return clocks_per_cnt * TIM13->CNT; // TODO: Use a hw_config +} + +struct TaskTimer { + uint32_t start_time_ = 0; + uint32_t end_time_ = 0; + uint32_t length_ = 0; + uint32_t max_length_ = 0; + + static bool enabled; + + uint32_t start() { + return sample_TIM13(); + } + + void stop(uint32_t start_time) { + uint32_t end_time = sample_TIM13(); + uint32_t length = end_time - start_time; + + if (enabled) { +#ifdef MEASURE_START_TIME + start_time_ = start_time; +#endif +#ifdef MEASURE_END_TIME + end_time_ = end_time; +#endif +#ifdef MEASURE_LENGTH + length_ = length; +#endif + } +#ifdef MEASURE_MAX_LENGTH + max_length_ = std::max(max_length_, length); +#endif + } +}; + +struct TaskTimerContext { + TaskTimerContext(const TaskTimerContext&) = delete; + TaskTimerContext(const TaskTimerContext&&) = delete; + void operator=(const TaskTimerContext&) = delete; + void operator=(const TaskTimerContext&&) = delete; + TaskTimerContext(TaskTimer& timer) : timer_(timer), start_time(timer.start()) {} + ~TaskTimerContext() { timer_.stop(start_time); } + + TaskTimer& timer_; + uint32_t start_time; + bool exit_ = false; +}; + +#define MEASURE_TIME(timer) for (TaskTimerContext __task_timer_ctx{timer}; !__task_timer_ctx.exit_; __task_timer_ctx.exit_ = true) + +#endif // __TASK_TIMER_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/thermistor.cpp b/Firmware/MotorControl/thermistor.cpp index 68656315a..077302edc 100644 --- a/Firmware/MotorControl/thermistor.cpp +++ b/Firmware/MotorControl/thermistor.cpp @@ -14,21 +14,29 @@ ThermistorCurrentLimiter::ThermistorCurrentLimiter(uint16_t adc_channel, temperature_(NAN), temp_limit_lower_(temp_limit_lower), temp_limit_upper_(temp_limit_upper), - enabled_(enabled), - error_(ERROR_NONE) + enabled_(enabled) { } void ThermistorCurrentLimiter::update() { - const float voltage = get_adc_voltage_channel(adc_channel_); - const float normalized_voltage = voltage / adc_ref_voltage; - temperature_ = horner_fma(normalized_voltage, coefficients_, num_coeffs_); + const float normalized_voltage = get_adc_relative_voltage_ch(adc_channel_); + float raw_temperature_ = horner_poly_eval(normalized_voltage, coefficients_, num_coeffs_); + + constexpr float tau = 0.1f; // [sec] + float k = current_meas_period / tau; + float val = raw_temperature_; + for (float& lpf_val : lpf_vals_) { + lpf_val += k * (val - lpf_val); + val = lpf_val; + } + if (is_nan(val)) { + lpf_vals_.fill(0.0f); + } + temperature_ = lpf_vals_.back(); } bool ThermistorCurrentLimiter::do_checks() { if (enabled_ && temperature_ >= temp_limit_upper_ + 5) { - error_ = ERROR_OVER_TEMP; - axis_->error_ |= Axis::ERROR_OVER_TEMP; return false; } return true; @@ -42,39 +50,40 @@ float ThermistorCurrentLimiter::get_current_limit(float base_current_lim) const const float temp_margin = temp_limit_upper_ - temperature_; const float derating_range = temp_limit_upper_ - temp_limit_lower_; float thermal_current_lim = base_current_lim * (temp_margin / derating_range); - if (!(thermal_current_lim >= 0.0f)) { // Funny polarity to also catch NaN + if (thermal_current_lim < 0.0f || is_nan(thermal_current_lim)) { thermal_current_lim = 0.0f; } return std::min(thermal_current_lim, base_current_lim); } -OnboardThermistorCurrentLimiter::OnboardThermistorCurrentLimiter(const ThermistorHardwareConfig_t& hw_config, Config_t& config) : - ThermistorCurrentLimiter(hw_config.adc_ch, - hw_config.coeffs, - hw_config.num_coeffs, - config.temp_limit_lower, - config.temp_limit_upper, - config.enabled), - config_(config) +OnboardThermistorCurrentLimiter::OnboardThermistorCurrentLimiter(uint16_t adc_channel, const float* const coefficients, size_t num_coeffs) : + ThermistorCurrentLimiter(adc_channel, + coefficients, + num_coeffs, + config_.temp_limit_lower, + config_.temp_limit_upper, + config_.enabled) { } -OffboardThermistorCurrentLimiter::OffboardThermistorCurrentLimiter(Config_t& config) : +OffboardThermistorCurrentLimiter::OffboardThermistorCurrentLimiter() : ThermistorCurrentLimiter(UINT16_MAX, - &config.thermistor_poly_coeffs[0], + &config_.thermistor_poly_coeffs[0], num_coeffs_, - config.temp_limit_lower, - config.temp_limit_upper, - config.enabled), - config_(config) + config_.temp_limit_lower, + config_.temp_limit_upper, + config_.enabled) { decode_pin(); } -void OffboardThermistorCurrentLimiter::decode_pin() { - const GPIO_TypeDef* const port = get_gpio_port_by_pin(config_.gpio_pin); - const uint16_t pin = get_gpio_pin_by_pin(config_.gpio_pin); +bool OffboardThermistorCurrentLimiter::apply_config() { + config_.parent = this; + decode_pin(); + return true; +} - adc_channel_ = channel_from_gpio(port, pin); +void OffboardThermistorCurrentLimiter::decode_pin() { + adc_channel_ = channel_from_gpio(get_gpio(config_.gpio_pin)); } diff --git a/Firmware/MotorControl/thermistor.hpp b/Firmware/MotorControl/thermistor.hpp index c403f189f..c101be3c8 100644 --- a/Firmware/MotorControl/thermistor.hpp +++ b/Firmware/MotorControl/thermistor.hpp @@ -1,9 +1,10 @@ #ifndef __THERMISTOR_HPP #define __THERMISTOR_HPP -#ifndef __ODRIVE_MAIN_H -#error "This file should not be included directly. Include odrive_main.h instead." -#endif +class Motor; // declared in motor.hpp + +#include "current_limiter.hpp" +#include class ThermistorCurrentLimiter : public CurrentLimiter, public ODriveIntf::ThermistorCurrentLimiterIntf { public: @@ -23,12 +24,12 @@ class ThermistorCurrentLimiter : public CurrentLimiter, public ODriveIntf::Therm uint16_t adc_channel_; const float* const coefficients_; const size_t num_coeffs_; - float temperature_; + float temperature_ = NAN; // [°C] NaN while the ODrive is initializing. const float& temp_limit_lower_; const float& temp_limit_upper_; const bool& enabled_; - Error error_; - Axis* axis_ = nullptr; // set by Axis constructor + Motor* motor_ = nullptr; // set by Motor::apply_config() + std::array lpf_vals_ = { 0.0f }; }; class OnboardThermistorCurrentLimiter : public ThermistorCurrentLimiter, public ODriveIntf::OnboardThermistorCurrentLimiterIntf { @@ -40,9 +41,9 @@ class OnboardThermistorCurrentLimiter : public ThermistorCurrentLimiter, public }; virtual ~OnboardThermistorCurrentLimiter() = default; - OnboardThermistorCurrentLimiter(const ThermistorHardwareConfig_t& hw_config, Config_t& config); + OnboardThermistorCurrentLimiter(uint16_t adc_channel, const float* const coefficients, size_t num_coeffs); - Config_t& config_; + Config_t config_; }; class OffboardThermistorCurrentLimiter : public ThermistorCurrentLimiter, public ODriveIntf::OffboardThermistorCurrentLimiterIntf { @@ -52,7 +53,11 @@ class OffboardThermistorCurrentLimiter : public ThermistorCurrentLimiter, public struct Config_t { float thermistor_poly_coeffs[num_coeffs_]; +#if HW_VERSION_MAJOR == 3 uint16_t gpio_pin = 4; +#elif HW_VERSION_MAJOR == 4 + uint16_t gpio_pin = 2; +#endif float temp_limit_lower = 100; float temp_limit_upper = 120; bool enabled = false; @@ -63,9 +68,11 @@ class OffboardThermistorCurrentLimiter : public ThermistorCurrentLimiter, public }; virtual ~OffboardThermistorCurrentLimiter() = default; - OffboardThermistorCurrentLimiter(Config_t& config); + OffboardThermistorCurrentLimiter(); + + Config_t config_; - Config_t& config_; + bool apply_config(); private: void decode_pin(); diff --git a/Firmware/MotorControl/trapTraj.cpp b/Firmware/MotorControl/trapTraj.cpp index dd7f64a22..b4f94a37a 100644 --- a/Firmware/MotorControl/trapTraj.cpp +++ b/Firmware/MotorControl/trapTraj.cpp @@ -1,4 +1,4 @@ -#include +#include #include "odrive_main.h" #include "utils.hpp" @@ -15,8 +15,6 @@ float sign_hard(float val) { // Vmax, Amax, Dmax and jmax Kinematic bounds // Ar, Dr and Vr Reached values of acceleration and velocity -TrapezoidalTrajectory::TrapezoidalTrajectory(Config_t& config) : config_(config) {} - bool TrapezoidalTrajectory::planTrapezoidal(float Xf, float Xi, float Vi, float Vmax, float Amax, float Dmax) { float dX = Xf - Xi; // Distance to travel @@ -44,7 +42,7 @@ bool TrapezoidalTrajectory::planTrapezoidal(float Xf, float Xi, float Vi, // Are we displacing enough to reach cruising speed? if (s*dX < s*dXmin) { // Short move (triangle profile) - Vr_ = s * sqrtf(std::fmax((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); + Vr_ = s * std::sqrt(std::max((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); Ta_ = std::max(0.0f, (Vr_ - Vi) / Ar_); Td_ = std::max(0.0f, -Vr_ / Dr_); Tv_ = 0.0f; diff --git a/Firmware/MotorControl/trapTraj.hpp b/Firmware/MotorControl/trapTraj.hpp index 9fa5ae338..ec91c0559 100644 --- a/Firmware/MotorControl/trapTraj.hpp +++ b/Firmware/MotorControl/trapTraj.hpp @@ -15,13 +15,12 @@ class TrapezoidalTrajectory { float Ydd; }; - explicit TrapezoidalTrajectory(Config_t& config); bool planTrapezoidal(float Xf, float Xi, float Vi, float Vmax, float Amax, float Dmax); Step_t eval(float t); Axis* axis_ = nullptr; // set by Axis constructor - Config_t& config_; + Config_t config_; float Xi_; float Xf_; diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index 579a47a7b..f41d9813b 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -1,12 +1,14 @@ #include -#include -#include -#include -#include +#include -int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { +// Compute rising edge timings (0.0 - 1.0) as a function of alpha-beta +// as per the magnitude invariant clarke transform +// The magnitude of the alpha-beta vector may not be larger than sqrt(3)/2 +// Returns true on success, and false if the input was out of range +std::tuple SVM(float alpha, float beta) { + float tA, tB, tC; int Sextant; if (beta >= 0.0f) { @@ -16,7 +18,6 @@ int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { Sextant = 2; //sextant v2-v3 else Sextant = 1; //sextant v1-v2 - } else { //quadrant II if (-one_by_sqrt3 * beta > alpha) @@ -48,9 +49,9 @@ int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t2 = two_by_sqrt3 * beta; // PWM timings - *tA = (1.0f - t1 - t2) * 0.5f; - *tB = *tA + t1; - *tC = *tB + t2; + tA = (1.0f - t1 - t2) * 0.5f; + tB = tA + t1; + tC = tB + t2; } break; // sextant v2-v3 @@ -60,9 +61,9 @@ int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t3 = -alpha + one_by_sqrt3 * beta; // PWM timings - *tB = (1.0f - t2 - t3) * 0.5f; - *tA = *tB + t3; - *tC = *tA + t2; + tB = (1.0f - t2 - t3) * 0.5f; + tA = tB + t3; + tC = tA + t2; } break; // sextant v3-v4 @@ -72,9 +73,9 @@ int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t4 = -alpha - one_by_sqrt3 * beta; // PWM timings - *tB = (1.0f - t3 - t4) * 0.5f; - *tC = *tB + t3; - *tA = *tC + t4; + tB = (1.0f - t3 - t4) * 0.5f; + tC = tB + t3; + tA = tC + t4; } break; // sextant v4-v5 @@ -84,9 +85,9 @@ int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t5 = -two_by_sqrt3 * beta; // PWM timings - *tC = (1.0f - t4 - t5) * 0.5f; - *tB = *tC + t5; - *tA = *tB + t4; + tC = (1.0f - t4 - t5) * 0.5f; + tB = tC + t5; + tA = tB + t4; } break; // sextant v5-v6 @@ -96,9 +97,9 @@ int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t6 = alpha - one_by_sqrt3 * beta; // PWM timings - *tC = (1.0f - t5 - t6) * 0.5f; - *tA = *tC + t5; - *tB = *tA + t6; + tC = (1.0f - t5 - t6) * 0.5f; + tA = tC + t5; + tB = tA + t6; } break; // sextant v6-v1 @@ -108,27 +109,26 @@ int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t1 = alpha + one_by_sqrt3 * beta; // PWM timings - *tA = (1.0f - t6 - t1) * 0.5f; - *tC = *tA + t1; - *tB = *tC + t6; + tA = (1.0f - t6 - t1) * 0.5f; + tC = tA + t1; + tB = tC + t6; } break; } - // if any of the results becomes NaN, result_valid will evaluate to false - int result_valid = - *tA >= 0.0f && *tA <= 1.0f - && *tB >= 0.0f && *tB <= 1.0f - && *tC >= 0.0f && *tC <= 1.0f; - return result_valid ? 0 : -1; + bool result_valid = + tA >= 0.0f && tA <= 1.0f + && tB >= 0.0f && tB <= 1.0f + && tC >= 0.0f && tC <= 1.0f; + return {tA, tB, tC, result_valid}; } // based on https://math.stackexchange.com/a/1105038/81278 float fast_atan2(float y, float x) { // a := min (|x|, |y|) / max (|x|, |y|) - float abs_y = fabsf(y); - float abs_x = fabsf(x); + float abs_y = std::abs(y); + float abs_x = std::abs(x); // inject FLT_MIN in denominator to avoid division by zero - float a = MACRO_MIN(abs_x, abs_y) / (MACRO_MAX(abs_x, abs_y) + FLT_MIN); + float a = std::min(abs_x, abs_y) / (std::max(abs_x, abs_y) + std::numeric_limits::min()); // s := a * a float s = a * a; // r := ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a @@ -146,22 +146,6 @@ float fast_atan2(float y, float x) { return r; } -// Evaluate polynomials using Fused Multiply Add intrisic instruction. -// coeffs[0] is highest order, as per numpy.polyfit -// p(x) = coeffs[0] * x^deg + ... + coeffs[deg], for some degree "deg" -float horner_fma(float x, const float *coeffs, size_t count) { - float result = 0.0f; - for (size_t idx = 0; idx < count; ++idx) - result = fmaf(result, x, coeffs[idx]); - return result; -} - -// Modulo (as opposed to remainder), per https://stackoverflow.com/a/19288271 -int mod(int dividend, int divisor){ - int r = dividend % divisor; - return (r < 0) ? (r + divisor) : r; -} - // @brief: Returns how much time is left until the deadline is reached. // If the deadline has already passed, the return value is 0 (except if // the deadline is very far in the past) @@ -200,6 +184,7 @@ void delay_us(uint32_t us) { uint32_t start = micros(); while (micros() - start < (uint32_t) us) { - __ASM("nop"); + asm volatile ("nop"); } } + diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 49f9434dd..67c1fceac 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -1,9 +1,11 @@ - -#ifndef __UTILS_H -#define __UTILS_H +#pragma once #include -#include +#include +#include +#include +#include +#include /** * @brief Flash size register address @@ -54,16 +56,34 @@ #ifdef M_PI #undef M_PI #endif -#define M_PI (3.14159265358979323846f) -#define MACRO_MAX(x, y) (((x) > (y)) ? (x) : (y)) -#define MACRO_MIN(x, y) (((x) < (y)) ? (x) : (y)) +// Math Constants +constexpr float M_PI = 3.14159265358979323846f; +constexpr float one_by_sqrt3 = 0.57735026919f; +constexpr float two_by_sqrt3 = 1.15470053838f; +constexpr float sqrt3_by_2 = 0.86602540378f; -#define SQ(x) ((x) * (x)) +// Function prototypes for implementations in utils.cpp +std::tuple SVM(float alpha, float beta); +float fast_atan2(float y, float x); +uint32_t deadline_to_timeout(uint32_t deadline_ms); +uint32_t timeout_to_deadline(uint32_t timeout_ms); +int is_in_the_future(uint32_t time_ms); +uint32_t micros(void); +void delay_us(uint32_t us); -#ifdef __cplusplus +extern "C" { +float our_arm_sin_f32(float x); +float our_arm_cos_f32(float x); +} -#include +// ---------------- +// Inline functions + +template +constexpr T SQ(const T& x){ + return x * x; +} /** * @brief Small helper to make array with known size @@ -71,62 +91,68 @@ * has to match exactly. Whereas initializer lists allow * less arguments. */ -template -std::array make_array(T head, Tail... tail) -{ - return std::array({ head, tail ... }); +template +std::array make_array(T head, Tail... tail) { + return std::array({head, tail...}); } -extern "C" { -#endif - -static const float one_by_sqrt3 = 0.57735026919f; -static const float two_by_sqrt3 = 1.15470053838f; -static const float sqrt3_by_2 = 0.86602540378f; - -// like fmodf, but always positive -static inline float fmodf_pos(float x, float y) { - float out = fmodf(x, y); - if (out < 0.0f) - out += y; - return out; +// To allow use of -ffast-math we need to have a special check for nan +// that bypasses the "ignore nan" flag +__attribute__((optimize("-fno-finite-math-only"))) +inline bool is_nan(float x) { + return __builtin_isnan(x); } -/** - * @brief Similar to modulo operator, except that the output range is centered - * around zero. - * The returned value is always in the range [-pm_range, pm_range). - */ -static inline float wrap_pm(float x, float pm_range) { - return fmodf_pos(x + pm_range, 2.0f * pm_range) - pm_range; +// Round to integer +// Default rounding mode: round to nearest +inline int round_int(float x) { +#ifdef __arm__ + int res; + asm("vcvtr.s32.f32 %[res], %[x]" + : [res] "=X" (res) + : [x] "w" (x) ); + return res; +#else + return (int)nearbyint(x); +#endif } -static inline float wrap_pm_pi(float theta) { - return wrap_pm(theta, M_PI); +// Wrap value to range. +// With default rounding mode (round to nearest), +// the result will be in range -y/2 to y/2 +inline float wrap_pm(float x, float y) { +#ifdef FPU_FPV4 + float intval = (float)round_int(x / y); +#else + float intval = nearbyintf(x / y); +#endif + return x - intval * y; } -// Compute rising edge timings (0.0 - 1.0) as a function of alpha-beta -// as per the magnitude invariant clarke transform -// The magnitude of the alpha-beta vector may not be larger than sqrt(3)/2 -// Returns 0 on success, and -1 if the input was out of range -int SVM(float alpha, float beta, float* tA, float* tB, float* tC); - -float fast_atan2(float y, float x); -float horner_fma(float x, const float *coeffs, size_t count); -int mod(int dividend, int divisor); - -uint32_t deadline_to_timeout(uint32_t deadline_ms); -uint32_t timeout_to_deadline(uint32_t timeout_ms); -int is_in_the_future(uint32_t time_ms); - -uint32_t micros(void); -void delay_us(uint32_t us); +// Same as fmodf but result is positive and y must be positive +inline float fmodf_pos(float x, float y) { + float res = wrap_pm(x, y); + if (res < 0) res += y; + return res; +} -float our_arm_sin_f32(float x); -float our_arm_cos_f32(float x); +inline float wrap_pm_pi(float x) { + return wrap_pm(x, 2 * M_PI); +} -#ifdef __cplusplus +// Evaluate polynomials in an efficient way +// coeffs[0] is highest order, as per numpy.polyfit +// p(x) = coeffs[0] * x^deg + ... + coeffs[deg], for some degree "deg" +inline float horner_poly_eval(float x, const float *coeffs, size_t count) { + float result = 0.0f; + for (size_t idx = 0; idx < count; ++idx) + result = (result * x) + coeffs[idx]; + return result; } -#endif -#endif //__UTILS_H +// Modulo (as opposed to remainder), per https://stackoverflow.com/a/19288271 +inline int mod(const int dividend, const int divisor){ + int r = dividend % divisor; + if (r < 0) r += divisor; + return r; +} diff --git a/Firmware/Private b/Firmware/Private new file mode 160000 index 000000000..2bdcd0acc --- /dev/null +++ b/Firmware/Private @@ -0,0 +1 @@ +Subproject commit 2bdcd0acc2001ec9ef423cbe1674c2ea58185e6b diff --git a/Firmware/Tests/test_can.cpp b/Firmware/Tests/test_can.cpp index 5a0db2fee..db062fea2 100644 --- a/Firmware/Tests/test_can.cpp +++ b/Firmware/Tests/test_can.cpp @@ -3,7 +3,7 @@ #include #include -#include "communication/can_helpers.hpp" +#include "communication/can/can_helpers.hpp" enum InputMode { INPUT_MODE_INACTIVE, @@ -39,6 +39,9 @@ TEST_SUITE("CAN Functions") { val = can_getSignal(rxmsg, 0, 16, true, 1, 0); CHECK(val == 0x1234); + val = can_getSignal(rxmsg, 0, 16, true); + CHECK(val == 0x1234); + val = can_getSignal(rxmsg, 0, 16, false, 1, 0); CHECK(val == 0x3412); diff --git a/Firmware/Tests/test_runner.cpp b/Firmware/Tests/test_runner.cpp index e111405f7..e4056fb5e 100644 --- a/Firmware/Tests/test_runner.cpp +++ b/Firmware/Tests/test_runner.cpp @@ -11,8 +11,6 @@ #include -#include - using std::cout; using std::endl; @@ -118,7 +116,7 @@ TEST_SUITE("vel_ramp") { float full_step = input_vel_ - vel_setpoint_; return std::clamp(full_step, -max_step_size, max_step_size); } - + uint8_t parity(uint16_t v) { v ^= v >> 8; v ^= v >> 4; @@ -154,4 +152,45 @@ TEST_SUITE("vel_ramp") { CHECK(parity(0x8DDF & 0x7FFF) == 0); CHECK(parity(0x5BFF & 0x7FFF) == 1); } -} \ No newline at end of file +} + +TEST_SUITE("") { + float step_cb(bool step_dir_active_, bool dir_pin, float& input_pos_, float turns_per_step) { + if (step_dir_active_) { + // const bool dir_pin = dir_gpio_.read(); + const float dir = dir_pin ? 1.0f : -1.0f; + input_pos_ += dir * turns_per_step; + // controller_.input_pos_ += dir * config_.turns_per_step; + // controller_.input_pos_updated(); + } + return input_pos_; + } + + float step_cb_new(bool step_dir_active_, bool dir_pin, int64_t& steps_, float turns_per_step) { + if (step_dir_active_) { + dir_pin ? ++steps_ : --steps_; + // controller_.input_pos_ = steps_ * config_.turns_per_step; + // controller_.input_pos_updated(); + } + return steps_ * turns_per_step; + } + + TEST_CASE("step_cb") { + bool step_dir_active = true; + bool dir_pin = true; + float input_pos = 0.0f; + float turns_per_step = 1/8192.0f; + int64_t steps = 0; + + CHECK(step_cb(step_dir_active, dir_pin, input_pos, turns_per_step) == + step_cb_new(step_dir_active, dir_pin, steps, turns_per_step)); + + for (uint64_t i = 0; i < 1ULL << 33; ++i) { + step_cb(step_dir_active, dir_pin, input_pos, turns_per_step); + step_cb_new(step_dir_active, dir_pin, steps, turns_per_step); + } + + std::cout << step_cb(step_dir_active, dir_pin, input_pos, turns_per_step) << '\n'; + std::cout << step_cb_new(step_dir_active, dir_pin, steps, turns_per_step) << '\n'; + } +} diff --git a/Firmware/Tests/test_trap_traj.cpp b/Firmware/Tests/test_trap_traj.cpp index b5d300140..8d5aad2de 100644 --- a/Firmware/Tests/test_trap_traj.cpp +++ b/Firmware/Tests/test_trap_traj.cpp @@ -85,8 +85,8 @@ bool TrapezoidalTrajectory::planTrapezoidal(float Xf, float Xi, float Vi, // Are we displacing enough to reach cruising speed? if (s*dX < s*dXmin) { // Short move (triangle profile) - Vr_ = s * sqrtf(std::fmax((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); - //Vr_ = s * sqrtf((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_)); + Vr_ = s * std::sqrt(std::max((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); + //Vr_ = s * std::sqrt((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_)); Ta_ = std::max(0.0f, (Vr_ - Vi) / Ar_); Td_ = std::max(0.0f, -Vr_ / Dr_); Tv_ = 0.0f; diff --git a/Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f405xx.h b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/stm32f405xx.h similarity index 100% rename from Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f405xx.h rename to Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/stm32f405xx.h diff --git a/Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h similarity index 100% rename from Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h rename to Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h diff --git a/Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/system_stm32f4xx.h b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/system_stm32f4xx.h similarity index 100% rename from Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/system_stm32f4xx.h rename to Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/system_stm32f4xx.h diff --git a/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/stm32f722xx.h b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/stm32f722xx.h new file mode 100644 index 000000000..095ec93c6 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/stm32f722xx.h @@ -0,0 +1,15462 @@ +/** + ****************************************************************************** + * @file stm32f722xx.h + * @author MCD Application Team + * @brief CMSIS Cortex-M7 Device Peripheral Access Layer Header File. + * + * This file contains: + * - Data structures and the address mapping for all peripherals + * - Peripheral's registers declarations and bits definition + * - Macros to access peripheral’s registers hardware + * + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2016 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS_Device + * @{ + */ + +/** @addtogroup stm32f722xx + * @{ + */ + +#ifndef __STM32F722xx_H +#define __STM32F722xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Configuration_section_for_CMSIS + * @{ + */ + +/** + * @brief STM32F7xx Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ +/****** Cortex-M7 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M7 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M7 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M7 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M7 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M7 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M7 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M7 System Tick Interrupt */ +/****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */ + DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */ + DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */ + DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */ + DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */ + DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */ + DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */ + ADC_IRQn = 18, /*!< ADC1, ADC2 and ADC3 global Interrupts */ + CAN1_TX_IRQn = 19, /*!< CAN1 TX Interrupt */ + CAN1_RX0_IRQn = 20, /*!< CAN1 RX0 Interrupt */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM9_IRQn = 24, /*!< TIM1 Break interrupt and TIM9 global interrupt */ + TIM1_UP_TIM10_IRQn = 25, /*!< TIM1 Update Interrupt and TIM10 global interrupt */ + TIM1_TRG_COM_TIM11_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */ + OTG_FS_WKUP_IRQn = 42, /*!< USB OTG FS Wakeup through EXTI line interrupt */ + TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */ + TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */ + TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */ + FMC_IRQn = 48, /*!< FMC global Interrupt */ + SDMMC1_IRQn = 49, /*!< SDMMC1 global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn = 55, /*!< TIM7 global interrupt */ + DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */ + DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */ + DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */ + DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */ + DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */ + ETH_IRQn = 61, /*!< Ethernet global Interrupt */ + ETH_WKUP_IRQn = 62, /*!< Ethernet Wakeup through EXTI line Interrupt */ + OTG_FS_IRQn = 67, /*!< USB OTG FS global Interrupt */ + DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */ + DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */ + DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */ + USART6_IRQn = 71, /*!< USART6 global interrupt */ + I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */ + I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */ + OTG_HS_EP1_OUT_IRQn = 74, /*!< USB OTG HS End Point 1 Out global interrupt */ + OTG_HS_EP1_IN_IRQn = 75, /*!< USB OTG HS End Point 1 In global interrupt */ + OTG_HS_WKUP_IRQn = 76, /*!< USB OTG HS Wakeup through EXTI interrupt */ + OTG_HS_IRQn = 77, /*!< USB OTG HS global interrupt */ + RNG_IRQn = 80, /*!< RNG global interrupt */ + FPU_IRQn = 81, /*!< FPU global interrupt */ + UART7_IRQn = 82, /*!< UART7 global interrupt */ + UART8_IRQn = 83, /*!< UART8 global interrupt */ + SPI4_IRQn = 84, /*!< SPI4 global Interrupt */ + SPI5_IRQn = 85, /*!< SPI5 global Interrupt */ + SAI1_IRQn = 87, /*!< SAI1 global Interrupt */ + SAI2_IRQn = 91, /*!< SAI2 global Interrupt */ + QUADSPI_IRQn = 92, /*!< Quad SPI global interrupt */ + LPTIM1_IRQn = 93, /*!< LP TIM1 interrupt */ + SDMMC2_IRQn = 103, /*!< SDMMC2 global Interrupt */ +} IRQn_Type; + +/** + * @} + */ + +/** + * @brief Configuration of the Cortex-M7 Processor and Core Peripherals + */ +#define __CM7_REV 0x0100U /*!< Cortex-M7 revision r1p0 */ +#define __MPU_PRESENT 1 /*!< CM7 provides an MPU */ +#define __NVIC_PRIO_BITS 4 /*!< CM7 uses 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */ +#define __FPU_PRESENT 1 /*!< FPU present */ +#define __ICACHE_PRESENT 1 /*!< CM7 instruction cache present */ +#define __DCACHE_PRESENT 1 /*!< CM7 data cache present */ +#include "core_cm7.h" /*!< Cortex-M7 processor and core peripherals */ + + +#include "system_stm32f7xx.h" +#include + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< ADC status register, Address offset: 0x00 */ + __IO uint32_t CR1; /*!< ADC control register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< ADC control register 2, Address offset: 0x08 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x0C */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x10 */ + __IO uint32_t JOFR1; /*!< ADC injected channel data offset register 1, Address offset: 0x14 */ + __IO uint32_t JOFR2; /*!< ADC injected channel data offset register 2, Address offset: 0x18 */ + __IO uint32_t JOFR3; /*!< ADC injected channel data offset register 3, Address offset: 0x1C */ + __IO uint32_t JOFR4; /*!< ADC injected channel data offset register 4, Address offset: 0x20 */ + __IO uint32_t HTR; /*!< ADC watchdog higher threshold register, Address offset: 0x24 */ + __IO uint32_t LTR; /*!< ADC watchdog lower threshold register, Address offset: 0x28 */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x2C */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x30 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x34 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x38*/ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x3C */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x40 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x44 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x48 */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x4C */ +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1 base address + 0x300 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1 base address + 0x304 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1 base address + 0x308 */ +} ADC_Common_TypeDef; + + +/** + * @brief Controller Area Network TxMailBox + */ + +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** + * @brief Controller Area Network FIFOMailBox + */ + +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** + * @brief Controller Area Network FilterRegister + */ + +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** + * @brief Controller Area Network + */ + +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ + uint32_t RESERVED2; /*!< Reserved, 0x0C */ + __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */ + __IO uint32_t POL; /*!< CRC polynomial register, Address offset: 0x14 */ +} CRC_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +}DBGMCU_TypeDef; + + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA stream x configuration register */ + __IO uint32_t NDTR; /*!< DMA stream x number of data register */ + __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ + __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ + __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ + __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ +} DMA_Stream_TypeDef; + +typedef struct +{ + __IO uint32_t LISR; /*!< DMA low interrupt status register, Address offset: 0x00 */ + __IO uint32_t HISR; /*!< DMA high interrupt status register, Address offset: 0x04 */ + __IO uint32_t LIFCR; /*!< DMA low interrupt flag clear register, Address offset: 0x08 */ + __IO uint32_t HIFCR; /*!< DMA high interrupt flag clear register, Address offset: 0x0C */ +} DMA_TypeDef; + + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR; /*!< EXTI Interrupt mask register, Address offset: 0x00 */ + __IO uint32_t EMR; /*!< EXTI Event mask register, Address offset: 0x04 */ + __IO uint32_t RTSR; /*!< EXTI Rising trigger selection register, Address offset: 0x08 */ + __IO uint32_t FTSR; /*!< EXTI Falling trigger selection register, Address offset: 0x0C */ + __IO uint32_t SWIER; /*!< EXTI Software interrupt event register, Address offset: 0x10 */ + __IO uint32_t PR; /*!< EXTI Pending register, Address offset: 0x14 */ +} EXTI_TypeDef; + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t KEYR; /*!< FLASH key register, Address offset: 0x04 */ + __IO uint32_t OPTKEYR; /*!< FLASH option key register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< FLASH status register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FLASH control register, Address offset: 0x10 */ + __IO uint32_t OPTCR; /*!< FLASH option control register , Address offset: 0x14 */ + __IO uint32_t OPTCR1; /*!< FLASH option control register 1 , Address offset: 0x18 */ + __IO uint32_t OPTCR2; /*!< FLASH option control register 2 , Address offset: 0x1C */ +} FLASH_TypeDef; + + + +/** + * @brief Flexible Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ +} FMC_Bank1_TypeDef; + +/** + * @brief Flexible Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FMC_Bank1E_TypeDef; + +/** + * @brief Flexible Memory Controller Bank3 + */ + +typedef struct +{ + __IO uint32_t PCR; /*!< NAND Flash control register, Address offset: 0x80 */ + __IO uint32_t SR; /*!< NAND Flash FIFO status and interrupt register, Address offset: 0x84 */ + __IO uint32_t PMEM; /*!< NAND Flash Common memory space timing register, Address offset: 0x88 */ + __IO uint32_t PATT; /*!< NAND Flash Attribute memory space timing register, Address offset: 0x8C */ + uint32_t RESERVED0; /*!< Reserved, 0x90 */ + __IO uint32_t ECCR; /*!< NAND Flash ECC result registers, Address offset: 0x94 */ +} FMC_Bank3_TypeDef; + +/** + * @brief Flexible Memory Controller Bank5_6 + */ + +typedef struct +{ + __IO uint32_t SDCR[2]; /*!< SDRAM Control registers , Address offset: 0x140-0x144 */ + __IO uint32_t SDTR[2]; /*!< SDRAM Timing registers , Address offset: 0x148-0x14C */ + __IO uint32_t SDCMR; /*!< SDRAM Command Mode register, Address offset: 0x150 */ + __IO uint32_t SDRTR; /*!< SDRAM Refresh Timer register, Address offset: 0x154 */ + __IO uint32_t SDSR; /*!< SDRAM Status register, Address offset: 0x158 */ +} FMC_Bank5_6_TypeDef; + + +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ +} GPIO_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + __IO uint32_t MEMRMP; /*!< SYSCFG memory remap register, Address offset: 0x00 */ + __IO uint32_t PMC; /*!< SYSCFG peripheral mode configuration register, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + uint32_t RESERVED[2]; /*!< Reserved, 0x18-0x1C */ + __IO uint32_t CMPCR; /*!< SYSCFG Compensation cell control register, Address offset: 0x20 */ +} SYSCFG_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address 1 register, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address 2 register, Address offset: 0x0C */ + __IO uint32_t TIMINGR; /*!< I2C Timing register, Address offset: 0x10 */ + __IO uint32_t TIMEOUTR; /*!< I2C Timeout register, Address offset: 0x14 */ + __IO uint32_t ISR; /*!< I2C Interrupt and status register, Address offset: 0x18 */ + __IO uint32_t ICR; /*!< I2C Interrupt clear register, Address offset: 0x1C */ + __IO uint32_t PECR; /*!< I2C PEC register, Address offset: 0x20 */ + __IO uint32_t RXDR; /*!< I2C Receive data register, Address offset: 0x24 */ + __IO uint32_t TXDR; /*!< I2C Transmit data register, Address offset: 0x28 */ +} I2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ + __IO uint32_t WINR; /*!< IWDG Window register, Address offset: 0x10 */ +} IWDG_TypeDef; + + + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< PWR power control register 1, Address offset: 0x00 */ + __IO uint32_t CSR1; /*!< PWR power control/status register 2, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< PWR power control register 2, Address offset: 0x08 */ + __IO uint32_t CSR2; /*!< PWR power control/status register 2, Address offset: 0x0C */ +} PWR_TypeDef; + + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t PLLCFGR; /*!< RCC PLL configuration register, Address offset: 0x04 */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ + __IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x0C */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x10 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x14 */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved, 0x1C */ + __IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x20 */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x24 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x28-0x2C */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0x30 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0x34 */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0x38 */ + uint32_t RESERVED2; /*!< Reserved, 0x3C */ + __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x40 */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x44 */ + uint32_t RESERVED3[2]; /*!< Reserved, 0x48-0x4C */ + __IO uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */ + __IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */ + __IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */ + uint32_t RESERVED4; /*!< Reserved, 0x5C */ + __IO uint32_t APB1LPENR; /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */ + __IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */ + uint32_t RESERVED5[2]; /*!< Reserved, 0x68-0x6C */ + __IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x70 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */ + uint32_t RESERVED6[2]; /*!< Reserved, 0x78-0x7C */ + __IO uint32_t SSCGR; /*!< RCC spread spectrum clock generation register, Address offset: 0x80 */ + __IO uint32_t PLLI2SCFGR; /*!< RCC PLLI2S configuration register, Address offset: 0x84 */ + __IO uint32_t PLLSAICFGR; /*!< RCC PLLSAI configuration register, Address offset: 0x88 */ + __IO uint32_t DCKCFGR1; /*!< RCC Dedicated Clocks configuration register1, Address offset: 0x8C */ + __IO uint32_t DCKCFGR2; /*!< RCC Dedicated Clocks configuration register 2, Address offset: 0x90 */ + +} RCC_TypeDef; + +/** + * @brief Real-Time Clock + */ + +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x08 */ + __IO uint32_t ISR; /*!< RTC initialization and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + uint32_t reserved; /*!< Reserved */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x1C */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + __IO uint32_t SSR; /*!< RTC sub second register, Address offset: 0x28 */ + __IO uint32_t SHIFTR; /*!< RTC shift control register, Address offset: 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + __IO uint32_t TSSSR; /*!< RTC time-stamp sub second register, Address offset: 0x38 */ + __IO uint32_t CALR; /*!< RTC calibration register, Address offset: 0x3C */ + __IO uint32_t TAMPCR; /*!< RTC tamper configuration register, Address offset: 0x40 */ + __IO uint32_t ALRMASSR; /*!< RTC alarm A sub second register, Address offset: 0x44 */ + __IO uint32_t ALRMBSSR; /*!< RTC alarm B sub second register, Address offset: 0x48 */ + __IO uint32_t OR; /*!< RTC option register, Address offset: 0x4C */ + __IO uint32_t BKP0R; /*!< RTC backup register 0, Address offset: 0x50 */ + __IO uint32_t BKP1R; /*!< RTC backup register 1, Address offset: 0x54 */ + __IO uint32_t BKP2R; /*!< RTC backup register 2, Address offset: 0x58 */ + __IO uint32_t BKP3R; /*!< RTC backup register 3, Address offset: 0x5C */ + __IO uint32_t BKP4R; /*!< RTC backup register 4, Address offset: 0x60 */ + __IO uint32_t BKP5R; /*!< RTC backup register 5, Address offset: 0x64 */ + __IO uint32_t BKP6R; /*!< RTC backup register 6, Address offset: 0x68 */ + __IO uint32_t BKP7R; /*!< RTC backup register 7, Address offset: 0x6C */ + __IO uint32_t BKP8R; /*!< RTC backup register 8, Address offset: 0x70 */ + __IO uint32_t BKP9R; /*!< RTC backup register 9, Address offset: 0x74 */ + __IO uint32_t BKP10R; /*!< RTC backup register 10, Address offset: 0x78 */ + __IO uint32_t BKP11R; /*!< RTC backup register 11, Address offset: 0x7C */ + __IO uint32_t BKP12R; /*!< RTC backup register 12, Address offset: 0x80 */ + __IO uint32_t BKP13R; /*!< RTC backup register 13, Address offset: 0x84 */ + __IO uint32_t BKP14R; /*!< RTC backup register 14, Address offset: 0x88 */ + __IO uint32_t BKP15R; /*!< RTC backup register 15, Address offset: 0x8C */ + __IO uint32_t BKP16R; /*!< RTC backup register 16, Address offset: 0x90 */ + __IO uint32_t BKP17R; /*!< RTC backup register 17, Address offset: 0x94 */ + __IO uint32_t BKP18R; /*!< RTC backup register 18, Address offset: 0x98 */ + __IO uint32_t BKP19R; /*!< RTC backup register 19, Address offset: 0x9C */ + __IO uint32_t BKP20R; /*!< RTC backup register 20, Address offset: 0xA0 */ + __IO uint32_t BKP21R; /*!< RTC backup register 21, Address offset: 0xA4 */ + __IO uint32_t BKP22R; /*!< RTC backup register 22, Address offset: 0xA8 */ + __IO uint32_t BKP23R; /*!< RTC backup register 23, Address offset: 0xAC */ + __IO uint32_t BKP24R; /*!< RTC backup register 24, Address offset: 0xB0 */ + __IO uint32_t BKP25R; /*!< RTC backup register 25, Address offset: 0xB4 */ + __IO uint32_t BKP26R; /*!< RTC backup register 26, Address offset: 0xB8 */ + __IO uint32_t BKP27R; /*!< RTC backup register 27, Address offset: 0xBC */ + __IO uint32_t BKP28R; /*!< RTC backup register 28, Address offset: 0xC0 */ + __IO uint32_t BKP29R; /*!< RTC backup register 29, Address offset: 0xC4 */ + __IO uint32_t BKP30R; /*!< RTC backup register 30, Address offset: 0xC8 */ + __IO uint32_t BKP31R; /*!< RTC backup register 31, Address offset: 0xCC */ +} RTC_TypeDef; + + +/** + * @brief Serial Audio Interface + */ + +typedef struct +{ + __IO uint32_t GCR; /*!< SAI global configuration register, Address offset: 0x00 */ +} SAI_TypeDef; + +typedef struct +{ + __IO uint32_t CR1; /*!< SAI block x configuration register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< SAI block x configuration register 2, Address offset: 0x08 */ + __IO uint32_t FRCR; /*!< SAI block x frame configuration register, Address offset: 0x0C */ + __IO uint32_t SLOTR; /*!< SAI block x slot register, Address offset: 0x10 */ + __IO uint32_t IMR; /*!< SAI block x interrupt mask register, Address offset: 0x14 */ + __IO uint32_t SR; /*!< SAI block x status register, Address offset: 0x18 */ + __IO uint32_t CLRFR; /*!< SAI block x clear flag register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< SAI block x data register, Address offset: 0x20 */ +} SAI_Block_TypeDef; + + +/** + * @brief SD host Interface + */ + +typedef struct +{ + __IO uint32_t POWER; /*!< SDMMC power control register, Address offset: 0x00 */ + __IO uint32_t CLKCR; /*!< SDMMClock control register, Address offset: 0x04 */ + __IO uint32_t ARG; /*!< SDMMC argument register, Address offset: 0x08 */ + __IO uint32_t CMD; /*!< SDMMC command register, Address offset: 0x0C */ + __I uint32_t RESPCMD; /*!< SDMMC command response register, Address offset: 0x10 */ + __I uint32_t RESP1; /*!< SDMMC response 1 register, Address offset: 0x14 */ + __I uint32_t RESP2; /*!< SDMMC response 2 register, Address offset: 0x18 */ + __I uint32_t RESP3; /*!< SDMMC response 3 register, Address offset: 0x1C */ + __I uint32_t RESP4; /*!< SDMMC response 4 register, Address offset: 0x20 */ + __IO uint32_t DTIMER; /*!< SDMMC data timer register, Address offset: 0x24 */ + __IO uint32_t DLEN; /*!< SDMMC data length register, Address offset: 0x28 */ + __IO uint32_t DCTRL; /*!< SDMMC data control register, Address offset: 0x2C */ + __I uint32_t DCOUNT; /*!< SDMMC data counter register, Address offset: 0x30 */ + __I uint32_t STA; /*!< SDMMC status register, Address offset: 0x34 */ + __IO uint32_t ICR; /*!< SDMMC interrupt clear register, Address offset: 0x38 */ + __IO uint32_t MASK; /*!< SDMMC mask register, Address offset: 0x3C */ + uint32_t RESERVED0[2]; /*!< Reserved, 0x40-0x44 */ + __I uint32_t FIFOCNT; /*!< SDMMC FIFO counter register, Address offset: 0x48 */ + uint32_t RESERVED1[13]; /*!< Reserved, 0x4C-0x7C */ + __IO uint32_t FIFO; /*!< SDMMC data FIFO register, Address offset: 0x80 */ +} SDMMC_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI control register 1 (not used in I2S mode), Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI control register 2, Address offset: 0x04 */ + __IO uint32_t SR; /*!< SPI status register, Address offset: 0x08 */ + __IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */ + __IO uint32_t CRCPR; /*!< SPI CRC polynomial register (not used in I2S mode), Address offset: 0x10 */ + __IO uint32_t RXCRCR; /*!< SPI RX CRC register (not used in I2S mode), Address offset: 0x14 */ + __IO uint32_t TXCRCR; /*!< SPI TX CRC register (not used in I2S mode), Address offset: 0x18 */ + __IO uint32_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */ + __IO uint32_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */ +} SPI_TypeDef; + +/** + * @brief QUAD Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< QUADSPI Control register, Address offset: 0x00 */ + __IO uint32_t DCR; /*!< QUADSPI Device Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< QUADSPI Status register, Address offset: 0x08 */ + __IO uint32_t FCR; /*!< QUADSPI Flag Clear register, Address offset: 0x0C */ + __IO uint32_t DLR; /*!< QUADSPI Data Length register, Address offset: 0x10 */ + __IO uint32_t CCR; /*!< QUADSPI Communication Configuration register, Address offset: 0x14 */ + __IO uint32_t AR; /*!< QUADSPI Address register, Address offset: 0x18 */ + __IO uint32_t ABR; /*!< QUADSPI Alternate Bytes register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< QUADSPI Data register, Address offset: 0x20 */ + __IO uint32_t PSMKR; /*!< QUADSPI Polling Status Mask register, Address offset: 0x24 */ + __IO uint32_t PSMAR; /*!< QUADSPI Polling Status Match register, Address offset: 0x28 */ + __IO uint32_t PIR; /*!< QUADSPI Polling Interval register, Address offset: 0x2C */ + __IO uint32_t LPTR; /*!< QUADSPI Low Power Timeout register, Address offset: 0x30 */ +} QUADSPI_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ + __IO uint32_t OR; /*!< TIM option register, Address offset: 0x50 */ + __IO uint32_t CCMR3; /*!< TIM capture/compare mode register 3, Address offset: 0x54 */ + __IO uint32_t CCR5; /*!< TIM capture/compare mode register5, Address offset: 0x58 */ + __IO uint32_t CCR6; /*!< TIM capture/compare mode register6, Address offset: 0x5C */ + +} TIM_TypeDef; + +/** + * @brief LPTIMIMER + */ +typedef struct +{ + __IO uint32_t ISR; /*!< LPTIM Interrupt and Status register, Address offset: 0x00 */ + __IO uint32_t ICR; /*!< LPTIM Interrupt Clear register, Address offset: 0x04 */ + __IO uint32_t IER; /*!< LPTIM Interrupt Enable register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< LPTIM Configuration register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< LPTIM Control register, Address offset: 0x10 */ + __IO uint32_t CMP; /*!< LPTIM Compare register, Address offset: 0x14 */ + __IO uint32_t ARR; /*!< LPTIM Autoreload register, Address offset: 0x18 */ + __IO uint32_t CNT; /*!< LPTIM Counter register, Address offset: 0x1C */ +} LPTIM_TypeDef; + + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */ + __IO uint32_t RTOR; /*!< USART Receiver Time Out register, Address offset: 0x14 */ + __IO uint32_t RQR; /*!< USART Request register, Address offset: 0x18 */ + __IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */ + __IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */ + __IO uint32_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */ + __IO uint32_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */ +} USART_TypeDef; + + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + + +/** + * @brief RNG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ +} RNG_TypeDef; + +/** + * @} + */ + +/** + * @brief USB_OTG_Core_Registers + */ +typedef struct +{ + __IO uint32_t GOTGCTL; /*!< USB_OTG Control and Status Register 000h */ + __IO uint32_t GOTGINT; /*!< USB_OTG Interrupt Register 004h */ + __IO uint32_t GAHBCFG; /*!< Core AHB Configuration Register 008h */ + __IO uint32_t GUSBCFG; /*!< Core USB Configuration Register 00Ch */ + __IO uint32_t GRSTCTL; /*!< Core Reset Register 010h */ + __IO uint32_t GINTSTS; /*!< Core Interrupt Register 014h */ + __IO uint32_t GINTMSK; /*!< Core Interrupt Mask Register 018h */ + __IO uint32_t GRXSTSR; /*!< Receive Sts Q Read Register 01Ch */ + __IO uint32_t GRXSTSP; /*!< Receive Sts Q Read & POP Register 020h */ + __IO uint32_t GRXFSIZ; /*!< Receive FIFO Size Register 024h */ + __IO uint32_t DIEPTXF0_HNPTXFSIZ; /*!< EP0 / Non Periodic Tx FIFO Size Register 028h */ + __IO uint32_t HNPTXSTS; /*!< Non Periodic Tx FIFO/Queue Sts reg 02Ch */ + uint32_t Reserved30[2]; /*!< Reserved 030h */ + __IO uint32_t GCCFG; /*!< General Purpose IO Register 038h */ + __IO uint32_t CID; /*!< User ID Register 03Ch */ + uint32_t Reserved5[3]; /*!< Reserved 040h-048h */ + __IO uint32_t GHWCFG3; /*!< User HW config3 04Ch */ + uint32_t Reserved6; /*!< Reserved 050h */ + __IO uint32_t GLPMCFG; /*!< LPM Register 054h */ + uint32_t Reserved7; /*!< Reserved 058h */ + __IO uint32_t GDFIFOCFG; /*!< DFIFO Software Config Register 05Ch */ + uint32_t Reserved43[40]; /*!< Reserved 60h-0FFh */ + __IO uint32_t HPTXFSIZ; /*!< Host Periodic Tx FIFO Size Reg 100h */ + __IO uint32_t DIEPTXF[0x0F]; /*!< dev Periodic Transmit FIFO 104h-13Ch */ +} USB_OTG_GlobalTypeDef; + + +/** + * @brief USB_OTG_device_Registers + */ +typedef struct +{ + __IO uint32_t DCFG; /*!< dev Configuration Register 800h */ + __IO uint32_t DCTL; /*!< dev Control Register 804h */ + __IO uint32_t DSTS; /*!< dev Status Register (RO) 808h */ + uint32_t Reserved0C; /*!< Reserved 80Ch */ + __IO uint32_t DIEPMSK; /*!< dev IN Endpoint Mask 810h */ + __IO uint32_t DOEPMSK; /*!< dev OUT Endpoint Mask 814h */ + __IO uint32_t DAINT; /*!< dev All Endpoints Itr Reg 818h */ + __IO uint32_t DAINTMSK; /*!< dev All Endpoints Itr Mask 81Ch */ + uint32_t Reserved20; /*!< Reserved 820h */ + uint32_t Reserved9; /*!< Reserved 824h */ + __IO uint32_t DVBUSDIS; /*!< dev VBUS discharge Register 828h */ + __IO uint32_t DVBUSPULSE; /*!< dev VBUS Pulse Register 82Ch */ + __IO uint32_t DTHRCTL; /*!< dev threshold 830h */ + __IO uint32_t DIEPEMPMSK; /*!< dev empty msk 834h */ + __IO uint32_t DEACHINT; /*!< dedicated EP interrupt 838h */ + __IO uint32_t DEACHMSK; /*!< dedicated EP msk 83Ch */ + uint32_t Reserved40; /*!< dedicated EP mask 840h */ + __IO uint32_t DINEP1MSK; /*!< dedicated EP mask 844h */ + uint32_t Reserved44[15]; /*!< Reserved 844-87Ch */ + __IO uint32_t DOUTEP1MSK; /*!< dedicated EP msk 884h */ +} USB_OTG_DeviceTypeDef; + + +/** + * @brief USB_OTG_IN_Endpoint-Specific_Register + */ +typedef struct +{ + __IO uint32_t DIEPCTL; /*!< dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved 900h + (ep_num * 20h) + 04h */ + __IO uint32_t DIEPINT; /*!< dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved 900h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DIEPTSIZ; /*!< IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h */ + __IO uint32_t DIEPDMA; /*!< IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h */ + __IO uint32_t DTXFSTS; /*!< IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h */ + uint32_t Reserved18; /*!< Reserved 900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch */ +} USB_OTG_INEndpointTypeDef; + + +/** + * @brief USB_OTG_OUT_Endpoint-Specific_Registers + */ +typedef struct +{ + __IO uint32_t DOEPCTL; /*!< dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved B00h + (ep_num * 20h) + 04h */ + __IO uint32_t DOEPINT; /*!< dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved B00h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DOEPTSIZ; /*!< dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h */ + __IO uint32_t DOEPDMA; /*!< dev OUT Endpoint DMA Address B00h + (ep_num * 20h) + 14h */ + uint32_t Reserved18[2]; /*!< Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch */ +} USB_OTG_OUTEndpointTypeDef; + + +/** + * @brief USB_OTG_Host_Mode_Register_Structures + */ +typedef struct +{ + __IO uint32_t HCFG; /*!< Host Configuration Register 400h */ + __IO uint32_t HFIR; /*!< Host Frame Interval Register 404h */ + __IO uint32_t HFNUM; /*!< Host Frame Nbr/Frame Remaining 408h */ + uint32_t Reserved40C; /*!< Reserved 40Ch */ + __IO uint32_t HPTXSTS; /*!< Host Periodic Tx FIFO/ Queue Status 410h */ + __IO uint32_t HAINT; /*!< Host All Channels Interrupt Register 414h */ + __IO uint32_t HAINTMSK; /*!< Host All Channels Interrupt Mask 418h */ +} USB_OTG_HostTypeDef; + +/** + * @brief USB_OTG_Host_Channel_Specific_Registers + */ +typedef struct +{ + __IO uint32_t HCCHAR; /*!< Host Channel Characteristics Register 500h */ + __IO uint32_t HCSPLT; /*!< Host Channel Split Control Register 504h */ + __IO uint32_t HCINT; /*!< Host Channel Interrupt Register 508h */ + __IO uint32_t HCINTMSK; /*!< Host Channel Interrupt Mask Register 50Ch */ + __IO uint32_t HCTSIZ; /*!< Host Channel Transfer Size Register 510h */ + __IO uint32_t HCDMA; /*!< Host Channel DMA Address Register 514h */ + uint32_t Reserved[2]; /*!< Reserved */ +} USB_OTG_HostChannelTypeDef; +/** + * @} + */ + + + + +/** @addtogroup Peripheral_memory_map + * @{ + */ +#define RAMITCM_BASE 0x00000000UL /*!< Base address of : 16KB RAM reserved for CPU execution/instruction accessible over ITCM */ +#define FLASHITCM_BASE 0x00200000UL /*!< Base address of : (up to 512 KB) embedded FLASH memory accessible over ITCM */ +#define FLASHAXI_BASE 0x08000000UL /*!< Base address of : (up to 512 KB) embedded FLASH memory accessible over AXI */ +#define RAMDTCM_BASE 0x20000000UL /*!< Base address of : 64KB system data RAM accessible over DTCM */ +#define PERIPH_BASE 0x40000000UL /*!< Base address of : AHB/ABP Peripherals */ +#define BKPSRAM_BASE 0x40024000UL /*!< Base address of : Backup SRAM(4 KB) */ +#define QSPI_BASE 0x90000000UL /*!< Base address of : QSPI memories accessible over AXI */ +#define FMC_R_BASE 0xA0000000UL /*!< Base address of : FMC Control registers */ +#define QSPI_R_BASE 0xA0001000UL /*!< Base address of : QSPI Control registers */ +#define SRAM1_BASE 0x20010000UL /*!< Base address of : 176KB RAM1 accessible over AXI/AHB */ +#define SRAM2_BASE 0x2003C000UL /*!< Base address of : 16KB RAM2 accessible over AXI/AHB */ +#define FLASH_END 0x0807FFFFUL /*!< FLASH end address */ +#define FLASH_OTP_BASE 0x1FF07800UL /*!< Base address of : (up to 528 Bytes) embedded FLASH OTP Area */ +#define FLASH_OTP_END 0x1FF07A0FUL /*!< End address of : (up to 528 Bytes) embedded FLASH OTP Area */ + +/* Legacy define */ +#define FLASH_BASE FLASHAXI_BASE + +/*!< Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x10000000UL) + +/*!< APB1 peripherals */ +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000UL) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400UL) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800UL) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00UL) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000UL) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400UL) +#define TIM12_BASE (APB1PERIPH_BASE + 0x1800UL) +#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00UL) +#define TIM14_BASE (APB1PERIPH_BASE + 0x2000UL) +#define LPTIM1_BASE (APB1PERIPH_BASE + 0x2400UL) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800UL) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00UL) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000UL) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800UL) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00UL) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400UL) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800UL) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00UL) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000UL) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400UL) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800UL) +#define I2C3_BASE (APB1PERIPH_BASE + 0x5C00UL) +#define CAN1_BASE (APB1PERIPH_BASE + 0x6400UL) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000UL) +#define DAC_BASE (APB1PERIPH_BASE + 0x7400UL) +#define UART7_BASE (APB1PERIPH_BASE + 0x7800UL) +#define UART8_BASE (APB1PERIPH_BASE + 0x7C00UL) + +/*!< APB2 peripherals */ +#define TIM1_BASE (APB2PERIPH_BASE + 0x0000UL) +#define TIM8_BASE (APB2PERIPH_BASE + 0x0400UL) +#define USART1_BASE (APB2PERIPH_BASE + 0x1000UL) +#define USART6_BASE (APB2PERIPH_BASE + 0x1400UL) +#define SDMMC2_BASE (APB2PERIPH_BASE + 0x1C00UL) +#define ADC1_BASE (APB2PERIPH_BASE + 0x2000UL) +#define ADC2_BASE (APB2PERIPH_BASE + 0x2100UL) +#define ADC3_BASE (APB2PERIPH_BASE + 0x2200UL) +#define ADC_BASE (APB2PERIPH_BASE + 0x2300UL) +#define SDMMC1_BASE (APB2PERIPH_BASE + 0x2C00UL) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000UL) +#define SPI4_BASE (APB2PERIPH_BASE + 0x3400UL) +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x3800UL) +#define EXTI_BASE (APB2PERIPH_BASE + 0x3C00UL) +#define TIM9_BASE (APB2PERIPH_BASE + 0x4000UL) +#define TIM10_BASE (APB2PERIPH_BASE + 0x4400UL) +#define TIM11_BASE (APB2PERIPH_BASE + 0x4800UL) +#define SPI5_BASE (APB2PERIPH_BASE + 0x5000UL) +#define SAI1_BASE (APB2PERIPH_BASE + 0x5800UL) +#define SAI2_BASE (APB2PERIPH_BASE + 0x5C00UL) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x004UL) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x024UL) +#define SAI2_Block_A_BASE (SAI2_BASE + 0x004UL) +#define SAI2_Block_B_BASE (SAI2_BASE + 0x024UL) +/*!< AHB1 peripherals */ +#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000UL) +#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400UL) +#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800UL) +#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00UL) +#define GPIOE_BASE (AHB1PERIPH_BASE + 0x1000UL) +#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400UL) +#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800UL) +#define GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00UL) +#define GPIOI_BASE (AHB1PERIPH_BASE + 0x2000UL) +#define CRC_BASE (AHB1PERIPH_BASE + 0x3000UL) +#define RCC_BASE (AHB1PERIPH_BASE + 0x3800UL) +#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x3C00UL) +#define UID_BASE 0x1FF07A10UL /*!< Unique device ID register base address */ +#define FLASHSIZE_BASE 0x1FF07A22UL /*!< FLASH Size register base address */ +#define PACKAGE_BASE 0x1FF07BF0UL /*!< Package size register base address */ +/* Legacy define */ +#define PACKAGESIZE_BASE PACKAGE_BASE + +#define DMA1_BASE (AHB1PERIPH_BASE + 0x6000UL) +#define DMA1_Stream0_BASE (DMA1_BASE + 0x010UL) +#define DMA1_Stream1_BASE (DMA1_BASE + 0x028UL) +#define DMA1_Stream2_BASE (DMA1_BASE + 0x040UL) +#define DMA1_Stream3_BASE (DMA1_BASE + 0x058UL) +#define DMA1_Stream4_BASE (DMA1_BASE + 0x070UL) +#define DMA1_Stream5_BASE (DMA1_BASE + 0x088UL) +#define DMA1_Stream6_BASE (DMA1_BASE + 0x0A0UL) +#define DMA1_Stream7_BASE (DMA1_BASE + 0x0B8UL) +#define DMA2_BASE (AHB1PERIPH_BASE + 0x6400UL) +#define DMA2_Stream0_BASE (DMA2_BASE + 0x010UL) +#define DMA2_Stream1_BASE (DMA2_BASE + 0x028UL) +#define DMA2_Stream2_BASE (DMA2_BASE + 0x040UL) +#define DMA2_Stream3_BASE (DMA2_BASE + 0x058UL) +#define DMA2_Stream4_BASE (DMA2_BASE + 0x070UL) +#define DMA2_Stream5_BASE (DMA2_BASE + 0x088UL) +#define DMA2_Stream6_BASE (DMA2_BASE + 0x0A0UL) +#define DMA2_Stream7_BASE (DMA2_BASE + 0x0B8UL) +/*!< AHB2 peripherals */ +#define RNG_BASE (AHB2PERIPH_BASE + 0x60800UL) +/*!< FMC Bankx registers base address */ +#define FMC_Bank1_R_BASE (FMC_R_BASE + 0x0000UL) +#define FMC_Bank1E_R_BASE (FMC_R_BASE + 0x0104UL) +#define FMC_Bank3_R_BASE (FMC_R_BASE + 0x0080UL) +#define FMC_Bank5_6_R_BASE (FMC_R_BASE + 0x0140UL) + +/* Debug MCU registers base address */ +#define DBGMCU_BASE 0xE0042000UL + +/*!< USB registers base address */ +#define USB_OTG_HS_PERIPH_BASE 0x40040000UL +#define USB_OTG_FS_PERIPH_BASE 0x50000000UL + +#define USB_OTG_GLOBAL_BASE 0x0000UL +#define USB_OTG_DEVICE_BASE 0x0800UL +#define USB_OTG_IN_ENDPOINT_BASE 0x0900UL +#define USB_OTG_OUT_ENDPOINT_BASE 0x0B00UL +#define USB_OTG_EP_REG_SIZE 0x0020UL +#define USB_OTG_HOST_BASE 0x0400UL +#define USB_OTG_HOST_PORT_BASE 0x0440UL +#define USB_OTG_HOST_CHANNEL_BASE 0x0500UL +#define USB_OTG_HOST_CHANNEL_SIZE 0x0020UL +#define USB_OTG_PCGCCTL_BASE 0x0E00UL +#define USB_OTG_FIFO_BASE 0x1000UL +#define USB_OTG_FIFO_SIZE 0x1000UL + +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) +#define TIM13 ((TIM_TypeDef *) TIM13_BASE) +#define TIM14 ((TIM_TypeDef *) TIM14_BASE) +#define LPTIM1 ((LPTIM_TypeDef *) LPTIM1_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG ((WWDG_TypeDef *) WWDG_BASE) +#define IWDG ((IWDG_TypeDef *) IWDG_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define CAN1 ((CAN_TypeDef *) CAN1_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define DAC1 ((DAC_TypeDef *) DAC_BASE) +#define DAC ((DAC_TypeDef *) DAC_BASE) /* Kept for legacy purpose */ +#define UART7 ((USART_TypeDef *) UART7_BASE) +#define UART8 ((USART_TypeDef *) UART8_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define USART6 ((USART_TypeDef *) USART6_BASE) +#define ADC ((ADC_Common_TypeDef *) ADC_BASE) +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define ADC2 ((ADC_TypeDef *) ADC2_BASE) +#define ADC3 ((ADC_TypeDef *) ADC3_BASE) +#define ADC123_COMMON ((ADC_Common_TypeDef *) ADC_BASE) +#define SDMMC1 ((SDMMC_TypeDef *) SDMMC1_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define SPI4 ((SPI_TypeDef *) SPI4_BASE) +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define TIM9 ((TIM_TypeDef *) TIM9_BASE) +#define TIM10 ((TIM_TypeDef *) TIM10_BASE) +#define TIM11 ((TIM_TypeDef *) TIM11_BASE) +#define SPI5 ((SPI_TypeDef *) SPI5_BASE) +#define SAI1 ((SAI_TypeDef *) SAI1_BASE) +#define SAI2 ((SAI_TypeDef *) SAI2_BASE) +#define SAI1_Block_A ((SAI_Block_TypeDef *)SAI1_Block_A_BASE) +#define SAI1_Block_B ((SAI_Block_TypeDef *)SAI1_Block_B_BASE) +#define SAI2_Block_A ((SAI_Block_TypeDef *)SAI2_Block_A_BASE) +#define SAI2_Block_B ((SAI_Block_TypeDef *)SAI2_Block_B_BASE) +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) +#define GPIOI ((GPIO_TypeDef *) GPIOI_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA1_Stream0 ((DMA_Stream_TypeDef *) DMA1_Stream0_BASE) +#define DMA1_Stream1 ((DMA_Stream_TypeDef *) DMA1_Stream1_BASE) +#define DMA1_Stream2 ((DMA_Stream_TypeDef *) DMA1_Stream2_BASE) +#define DMA1_Stream3 ((DMA_Stream_TypeDef *) DMA1_Stream3_BASE) +#define DMA1_Stream4 ((DMA_Stream_TypeDef *) DMA1_Stream4_BASE) +#define DMA1_Stream5 ((DMA_Stream_TypeDef *) DMA1_Stream5_BASE) +#define DMA1_Stream6 ((DMA_Stream_TypeDef *) DMA1_Stream6_BASE) +#define DMA1_Stream7 ((DMA_Stream_TypeDef *) DMA1_Stream7_BASE) +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMA2_Stream0 ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE) +#define DMA2_Stream1 ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE) +#define DMA2_Stream2 ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE) +#define DMA2_Stream3 ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE) +#define DMA2_Stream4 ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE) +#define DMA2_Stream5 ((DMA_Stream_TypeDef *) DMA2_Stream5_BASE) +#define DMA2_Stream6 ((DMA_Stream_TypeDef *) DMA2_Stream6_BASE) +#define DMA2_Stream7 ((DMA_Stream_TypeDef *) DMA2_Stream7_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) +#define FMC_Bank1 ((FMC_Bank1_TypeDef *) FMC_Bank1_R_BASE) +#define FMC_Bank1E ((FMC_Bank1E_TypeDef *) FMC_Bank1E_R_BASE) +#define FMC_Bank3 ((FMC_Bank3_TypeDef *) FMC_Bank3_R_BASE) +#define FMC_Bank5_6 ((FMC_Bank5_6_TypeDef *) FMC_Bank5_6_R_BASE) +#define QUADSPI ((QUADSPI_TypeDef *) QSPI_R_BASE) +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) +#define USB_OTG_FS ((USB_OTG_GlobalTypeDef *) USB_OTG_FS_PERIPH_BASE) +#define USB_OTG_HS ((USB_OTG_GlobalTypeDef *) USB_OTG_HS_PERIPH_BASE) +#define SDMMC2 ((SDMMC_TypeDef *) SDMMC2_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + + /** @addtogroup Peripheral_Registers_Bits_Definition + * @{ + */ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ +#define VREFINT_CAL_ADDR_CMSIS ((uint16_t*) (0x1FF07A2A)) /*!
© Copyright (c) 2016 STMicroelectronics. + * All rights reserved.
+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f7xx + * @{ + */ + +#ifndef __STM32F7xx_H +#define __STM32F7xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Library_configuration_section + * @{ + */ + +/** + * @brief STM32 Family + */ +#if !defined (STM32F7) +#define STM32F7 +#endif /* STM32F7 */ + +/* Uncomment the line below according to the target STM32 device used in your + application + */ +#if !defined (STM32F756xx) && !defined (STM32F746xx) && !defined (STM32F745xx) && !defined (STM32F765xx) && \ + !defined (STM32F767xx) && !defined (STM32F769xx) && !defined (STM32F777xx) && !defined (STM32F779xx) && \ + !defined (STM32F722xx) && !defined (STM32F723xx) && !defined (STM32F732xx) && !defined (STM32F733xx) && \ + !defined (STM32F730xx) && !defined (STM32F750xx) + + /* #define STM32F756xx */ /*!< STM32F756VG, STM32F756ZG, STM32F756ZG, STM32F756IG, STM32F756BG, + STM32F756NG Devices */ + /* #define STM32F746xx */ /*!< STM32F746VE, STM32F746VG, STM32F746ZE, STM32F746ZG, STM32F746IE, STM32F746IG, + STM32F746BE, STM32F746BG, STM32F746NE, STM32F746NG Devices */ + /* #define STM32F745xx */ /*!< STM32F745VE, STM32F745VG, STM32F745ZG, STM32F745ZE, STM32F745IE, STM32F745IG Devices */ + /* #define STM32F765xx */ /*!< STM32F765BI, STM32F765BG, STM32F765NI, STM32F765NG, STM32F765II, STM32F765IG, + STM32F765ZI, STM32F765ZG, STM32F765VI, STM32F765VG Devices */ + /* #define STM32F767xx */ /*!< STM32F767BG, STM32F767BI, STM32F767IG, STM32F767II, STM32F767NG, STM32F767NI, + STM32F767VG, STM32F767VI, STM32F767ZG, STM32F767ZI Devices */ + /* #define STM32F769xx */ /*!< STM32F769AG, STM32F769AI, STM32F769BG, STM32F769BI, STM32F769IG, STM32F769II, + STM32F769NG, STM32F769NI, STM32F768AI Devices */ + /* #define STM32F777xx */ /*!< STM32F777VI, STM32F777ZI, STM32F777II, STM32F777BI, STM32F777NI Devices */ + /* #define STM32F779xx */ /*!< STM32F779II, STM32F779BI, STM32F779NI, STM32F779AI, STM32F778AI Devices */ + /* #define STM32F722xx */ /*!< STM32F722IE, STM32F722ZE, STM32F722VE, STM32F722RE, STM32F722IC, STM32F722ZC, + STM32F722VC, STM32F722RC Devices */ + /* #define STM32F723xx */ /*!< STM32F723IE, STM32F723ZE, STM32F723VE, STM32F723IC, STM32F723ZC, STM32F723VC Devices */ + /* #define STM32F732xx */ /*!< STM32F732IE, STM32F732ZE, STM32F732VE, STM32F732RE Devices */ + /* #define STM32F733xx */ /*!< STM32F733IE, STM32F733ZE, STM32F733VE Devices */ + /* #define STM32F730xx */ /*!< STM32F730R, STM32F730V, STM32F730Z, STM32F730I Devices */ + /* #define STM32F750xx */ /*!< STM32F750V, STM32F750Z, STM32F750N Devices */ +#endif + +/* Tip: To avoid modifying this file each time you need to switch between these + devices, you can define the device in your toolchain compiler preprocessor. + */ + +#if !defined (USE_HAL_DRIVER) +/** + * @brief Comment the line below if you will not use the peripherals drivers. + In this case, these drivers will not be included and the application code will + be based on direct access to peripherals registers + */ + /*#define USE_HAL_DRIVER */ +#endif /* USE_HAL_DRIVER */ + +/** + * @brief CMSIS Device version number V1.2.5 + */ +#define __STM32F7_CMSIS_VERSION_MAIN (0x01) /*!< [31:24] main version */ +#define __STM32F7_CMSIS_VERSION_SUB1 (0x02) /*!< [23:16] sub1 version */ +#define __STM32F7_CMSIS_VERSION_SUB2 (0x05) /*!< [15:8] sub2 version */ +#define __STM32F7_CMSIS_VERSION_RC (0x00) /*!< [7:0] release candidate */ +#define __STM32F7_CMSIS_VERSION ((__STM32F7_CMSIS_VERSION_MAIN << 24)\ + |(__STM32F7_CMSIS_VERSION_SUB1 << 16)\ + |(__STM32F7_CMSIS_VERSION_SUB2 << 8 )\ + |(__STM32F7_CMSIS_VERSION_RC)) +/** + * @} + */ + +/** @addtogroup Device_Included + * @{ + */ +#if defined(STM32F722xx) + #include "stm32f722xx.h" +#elif defined(STM32F723xx) + #include "stm32f723xx.h" +#elif defined(STM32F732xx) + #include "stm32f732xx.h" +#elif defined(STM32F733xx) + #include "stm32f733xx.h" +#elif defined(STM32F756xx) + #include "stm32f756xx.h" +#elif defined(STM32F746xx) + #include "stm32f746xx.h" +#elif defined(STM32F745xx) + #include "stm32f745xx.h" +#elif defined(STM32F765xx) + #include "stm32f765xx.h" +#elif defined(STM32F767xx) + #include "stm32f767xx.h" +#elif defined(STM32F769xx) + #include "stm32f769xx.h" +#elif defined(STM32F777xx) + #include "stm32f777xx.h" +#elif defined(STM32F779xx) + #include "stm32f779xx.h" +#elif defined(STM32F730xx) + #include "stm32f730xx.h" +#elif defined(STM32F750xx) + #include "stm32f750xx.h" +#else + #error "Please select first the target STM32F7xx device used in your application (in stm32f7xx.h file)" +#endif + +/** + * @} + */ + +/** @addtogroup Exported_types + * @{ + */ +typedef enum +{ + RESET = 0U, + SET = !RESET +} FlagStatus, ITStatus; + +typedef enum +{ + DISABLE = 0U, + ENABLE = !DISABLE +} FunctionalState; +#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE)) + +typedef enum +{ + SUCCESS = 0U, + ERROR = !SUCCESS +} ErrorStatus; + +/** + * @} + */ + +/** @addtogroup Exported_macro + * @{ + */ +#define SET_BIT(REG, BIT) ((REG) |= (BIT)) + +#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) + +#define READ_BIT(REG, BIT) ((REG) & (BIT)) + +#define CLEAR_REG(REG) ((REG) = (0x0)) + +#define WRITE_REG(REG, VAL) ((REG) = (VAL)) + +#define READ_REG(REG) ((REG)) + +#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) + +#define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL))) + +/** + * @} + */ + +#ifdef USE_HAL_DRIVER + #include "stm32f7xx_hal.h" +#endif /* USE_HAL_DRIVER */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __STM32F7xx_H */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/system_stm32f7xx.h b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/system_stm32f7xx.h new file mode 100644 index 000000000..140be1b60 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/system_stm32f7xx.h @@ -0,0 +1,123 @@ +/** + ****************************************************************************** + * @file system_stm32f7xx.h + * @author MCD Application Team + * @brief CMSIS Cortex-M7 Device System Source File for STM32F7xx devices. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f7xx_system + * @{ + */ + +/** + * @brief Define to prevent recursive inclusion + */ +#ifndef __SYSTEM_STM32F7XX_H +#define __SYSTEM_STM32F7XX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** @addtogroup STM32F7xx_System_Includes + * @{ + */ + +/** + * @} + */ + + +/** @addtogroup STM32F7xx_System_Exported_Variables + * @{ + */ + /* The SystemCoreClock variable is updated in three ways: + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetSysClockFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency + Note: If you use this function to configure the system clock; then there + is no need to call the 2 first functions listed above, since SystemCoreClock + variable is updated automatically. + */ +extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ + +extern const uint8_t AHBPrescTable[16]; /*!< AHB prescalers table values */ +extern const uint8_t APBPrescTable[8]; /*!< APB prescalers table values */ + + +/** + * @} + */ + +/** @addtogroup STM32F7xx_System_Exported_Constants + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F7xx_System_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F7xx_System_Exported_Functions + * @{ + */ + +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /*__SYSTEM_STM32F7XX_H */ + +/** + * @} + */ + +/** + * @} + */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_common_tables.h b/Firmware/ThirdParty/CMSIS/Include/arm_common_tables.h similarity index 53% rename from Firmware/Board/v3/Drivers/CMSIS/Include/arm_common_tables.h rename to Firmware/ThirdParty/CMSIS/Include/arm_common_tables.h index 8742a5699..dfea7460e 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_common_tables.h +++ b/Firmware/ThirdParty/CMSIS/Include/arm_common_tables.h @@ -1,42 +1,30 @@ /* ---------------------------------------------------------------------- -* Copyright (C) 2010-2014 ARM Limited. All rights reserved. -* -* $Date: 19. October 2015 -* $Revision: V.1.4.5 a -* -* Project: CMSIS DSP Library -* Title: arm_common_tables.h -* -* Description: This file has extern declaration for common tables like Bitreverse, reciprocal etc which are used across different functions -* -* Target Processor: Cortex-M4/Cortex-M3 -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* - Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* - Neither the name of ARM LIMITED nor the names of its contributors -* may be used to endorse or promote products derived from this -* software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -* -------------------------------------------------------------------- */ + * Project: CMSIS DSP Library + * Title: arm_common_tables.h + * Description: Extern declaration for common tables + * + * $Date: 27. January 2017 + * $Revision: V.1.5.1 + * + * Target Processor: Cortex-M cores + * -------------------------------------------------------------------- */ +/* + * Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ #ifndef _ARM_COMMON_TABLES_H #define _ARM_COMMON_TABLES_H @@ -46,8 +34,6 @@ extern const uint16_t armBitRevTable[1024]; extern const q15_t armRecipTableQ15[64]; extern const q31_t armRecipTableQ31[64]; -/* extern const q31_t realCoefAQ31[1024]; */ -/* extern const q31_t realCoefBQ31[1024]; */ extern const float32_t twiddleCoef_16[32]; extern const float32_t twiddleCoef_32[64]; extern const float32_t twiddleCoef_64[128]; @@ -85,45 +71,44 @@ extern const float32_t twiddleCoef_rfft_1024[1024]; extern const float32_t twiddleCoef_rfft_2048[2048]; extern const float32_t twiddleCoef_rfft_4096[4096]; - /* floating-point bit reversal tables */ -#define ARMBITREVINDEXTABLE__16_TABLE_LENGTH ((uint16_t)20 ) -#define ARMBITREVINDEXTABLE__32_TABLE_LENGTH ((uint16_t)48 ) -#define ARMBITREVINDEXTABLE__64_TABLE_LENGTH ((uint16_t)56 ) -#define ARMBITREVINDEXTABLE_128_TABLE_LENGTH ((uint16_t)208 ) -#define ARMBITREVINDEXTABLE_256_TABLE_LENGTH ((uint16_t)440 ) -#define ARMBITREVINDEXTABLE_512_TABLE_LENGTH ((uint16_t)448 ) -#define ARMBITREVINDEXTABLE1024_TABLE_LENGTH ((uint16_t)1800) -#define ARMBITREVINDEXTABLE2048_TABLE_LENGTH ((uint16_t)3808) -#define ARMBITREVINDEXTABLE4096_TABLE_LENGTH ((uint16_t)4032) +#define ARMBITREVINDEXTABLE_16_TABLE_LENGTH ((uint16_t)20) +#define ARMBITREVINDEXTABLE_32_TABLE_LENGTH ((uint16_t)48) +#define ARMBITREVINDEXTABLE_64_TABLE_LENGTH ((uint16_t)56) +#define ARMBITREVINDEXTABLE_128_TABLE_LENGTH ((uint16_t)208) +#define ARMBITREVINDEXTABLE_256_TABLE_LENGTH ((uint16_t)440) +#define ARMBITREVINDEXTABLE_512_TABLE_LENGTH ((uint16_t)448) +#define ARMBITREVINDEXTABLE_1024_TABLE_LENGTH ((uint16_t)1800) +#define ARMBITREVINDEXTABLE_2048_TABLE_LENGTH ((uint16_t)3808) +#define ARMBITREVINDEXTABLE_4096_TABLE_LENGTH ((uint16_t)4032) -extern const uint16_t armBitRevIndexTable16[ARMBITREVINDEXTABLE__16_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable32[ARMBITREVINDEXTABLE__32_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable64[ARMBITREVINDEXTABLE__64_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable16[ARMBITREVINDEXTABLE_16_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable32[ARMBITREVINDEXTABLE_32_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable64[ARMBITREVINDEXTABLE_64_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable128[ARMBITREVINDEXTABLE_128_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable256[ARMBITREVINDEXTABLE_256_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable512[ARMBITREVINDEXTABLE_512_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable1024[ARMBITREVINDEXTABLE1024_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable2048[ARMBITREVINDEXTABLE2048_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable4096[ARMBITREVINDEXTABLE4096_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable1024[ARMBITREVINDEXTABLE_1024_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable2048[ARMBITREVINDEXTABLE_2048_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable4096[ARMBITREVINDEXTABLE_4096_TABLE_LENGTH]; /* fixed-point bit reversal tables */ -#define ARMBITREVINDEXTABLE_FIXED___16_TABLE_LENGTH ((uint16_t)12 ) -#define ARMBITREVINDEXTABLE_FIXED___32_TABLE_LENGTH ((uint16_t)24 ) -#define ARMBITREVINDEXTABLE_FIXED___64_TABLE_LENGTH ((uint16_t)56 ) -#define ARMBITREVINDEXTABLE_FIXED__128_TABLE_LENGTH ((uint16_t)112 ) -#define ARMBITREVINDEXTABLE_FIXED__256_TABLE_LENGTH ((uint16_t)240 ) -#define ARMBITREVINDEXTABLE_FIXED__512_TABLE_LENGTH ((uint16_t)480 ) -#define ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH ((uint16_t)992 ) +#define ARMBITREVINDEXTABLE_FIXED_16_TABLE_LENGTH ((uint16_t)12) +#define ARMBITREVINDEXTABLE_FIXED_32_TABLE_LENGTH ((uint16_t)24) +#define ARMBITREVINDEXTABLE_FIXED_64_TABLE_LENGTH ((uint16_t)56) +#define ARMBITREVINDEXTABLE_FIXED_128_TABLE_LENGTH ((uint16_t)112) +#define ARMBITREVINDEXTABLE_FIXED_256_TABLE_LENGTH ((uint16_t)240) +#define ARMBITREVINDEXTABLE_FIXED_512_TABLE_LENGTH ((uint16_t)480) +#define ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH ((uint16_t)992) #define ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH ((uint16_t)1984) #define ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH ((uint16_t)4032) -extern const uint16_t armBitRevIndexTable_fixed_16[ARMBITREVINDEXTABLE_FIXED___16_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_32[ARMBITREVINDEXTABLE_FIXED___32_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_64[ARMBITREVINDEXTABLE_FIXED___64_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_128[ARMBITREVINDEXTABLE_FIXED__128_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_256[ARMBITREVINDEXTABLE_FIXED__256_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_512[ARMBITREVINDEXTABLE_FIXED__512_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_16[ARMBITREVINDEXTABLE_FIXED_16_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_32[ARMBITREVINDEXTABLE_FIXED_32_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_64[ARMBITREVINDEXTABLE_FIXED_64_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_128[ARMBITREVINDEXTABLE_FIXED_128_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_256[ARMBITREVINDEXTABLE_FIXED_256_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_512[ARMBITREVINDEXTABLE_FIXED_512_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable_fixed_1024[ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable_fixed_2048[ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable_fixed_4096[ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH]; diff --git a/Firmware/ThirdParty/CMSIS/Include/arm_const_structs.h b/Firmware/ThirdParty/CMSIS/Include/arm_const_structs.h new file mode 100644 index 000000000..80a3e8bbe --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/arm_const_structs.h @@ -0,0 +1,66 @@ +/* ---------------------------------------------------------------------- + * Project: CMSIS DSP Library + * Title: arm_const_structs.h + * Description: Constant structs that are initialized for user convenience. + * For example, some can be given as arguments to the arm_cfft_f32() function. + * + * $Date: 27. January 2017 + * $Revision: V.1.5.1 + * + * Target Processor: Cortex-M cores + * -------------------------------------------------------------------- */ +/* + * Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#ifndef _ARM_CONST_STRUCTS_H +#define _ARM_CONST_STRUCTS_H + +#include "arm_math.h" +#include "arm_common_tables.h" + + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len16; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len32; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len64; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len128; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len256; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len512; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len1024; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len2048; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len4096; + + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len16; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len32; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len64; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len128; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len256; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len512; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len1024; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len2048; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096; + + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len16; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len32; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len64; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len128; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len256; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len512; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len1024; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len2048; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len4096; + +#endif diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_math.h b/Firmware/ThirdParty/CMSIS/Include/arm_math.h similarity index 95% rename from Firmware/Board/v3/Drivers/CMSIS/Include/arm_math.h rename to Firmware/ThirdParty/CMSIS/Include/arm_math.h index d33f8a9b3..ea9dd26aa 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_math.h +++ b/Firmware/ThirdParty/CMSIS/Include/arm_math.h @@ -1,42 +1,26 @@ -/* ---------------------------------------------------------------------- -* Copyright (C) 2010-2015 ARM Limited. All rights reserved. -* -* $Date: 20. October 2015 -* $Revision: V1.4.5 b -* -* Project: CMSIS DSP Library -* Title: arm_math.h -* -* Description: Public header file for CMSIS DSP Library -* -* Target Processor: Cortex-M7/Cortex-M4/Cortex-M3/Cortex-M0 -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* - Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* - Neither the name of ARM LIMITED nor the names of its contributors -* may be used to endorse or promote products derived from this -* software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. - * -------------------------------------------------------------------- */ +/****************************************************************************** + * @file arm_math.h + * @brief Public header file for CMSIS DSP LibraryU + * @version V1.5.3 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (c) 2010-2018 Arm Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ /** \mainpage CMSIS DSP Software Library @@ -66,26 +50,34 @@ * ------------ * * The library installer contains prebuilt versions of the libraries in the Lib folder. - * - arm_cortexM7lfdp_math.lib (Little endian and Double Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7bfdp_math.lib (Big endian and Double Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7lfsp_math.lib (Little endian and Single Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7bfsp_math.lib (Big endian and Single Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7l_math.lib (Little endian on Cortex-M7) - * - arm_cortexM7b_math.lib (Big endian on Cortex-M7) - * - arm_cortexM4lf_math.lib (Little endian and Floating Point Unit on Cortex-M4) - * - arm_cortexM4bf_math.lib (Big endian and Floating Point Unit on Cortex-M4) - * - arm_cortexM4l_math.lib (Little endian on Cortex-M4) - * - arm_cortexM4b_math.lib (Big endian on Cortex-M4) - * - arm_cortexM3l_math.lib (Little endian on Cortex-M3) - * - arm_cortexM3b_math.lib (Big endian on Cortex-M3) - * - arm_cortexM0l_math.lib (Little endian on Cortex-M0 / CortexM0+) - * - arm_cortexM0b_math.lib (Big endian on Cortex-M0 / CortexM0+) + * - arm_cortexM7lfdp_math.lib (Cortex-M7, Little endian, Double Precision Floating Point Unit) + * - arm_cortexM7bfdp_math.lib (Cortex-M7, Big endian, Double Precision Floating Point Unit) + * - arm_cortexM7lfsp_math.lib (Cortex-M7, Little endian, Single Precision Floating Point Unit) + * - arm_cortexM7bfsp_math.lib (Cortex-M7, Big endian and Single Precision Floating Point Unit on) + * - arm_cortexM7l_math.lib (Cortex-M7, Little endian) + * - arm_cortexM7b_math.lib (Cortex-M7, Big endian) + * - arm_cortexM4lf_math.lib (Cortex-M4, Little endian, Floating Point Unit) + * - arm_cortexM4bf_math.lib (Cortex-M4, Big endian, Floating Point Unit) + * - arm_cortexM4l_math.lib (Cortex-M4, Little endian) + * - arm_cortexM4b_math.lib (Cortex-M4, Big endian) + * - arm_cortexM3l_math.lib (Cortex-M3, Little endian) + * - arm_cortexM3b_math.lib (Cortex-M3, Big endian) + * - arm_cortexM0l_math.lib (Cortex-M0 / Cortex-M0+, Little endian) + * - arm_cortexM0b_math.lib (Cortex-M0 / Cortex-M0+, Big endian) + * - arm_ARMv8MBLl_math.lib (Armv8-M Baseline, Little endian) + * - arm_ARMv8MMLl_math.lib (Armv8-M Mainline, Little endian) + * - arm_ARMv8MMLlfsp_math.lib (Armv8-M Mainline, Little endian, Single Precision Floating Point Unit) + * - arm_ARMv8MMLld_math.lib (Armv8-M Mainline, Little endian, DSP instructions) + * - arm_ARMv8MMLldfsp_math.lib (Armv8-M Mainline, Little endian, DSP instructions, Single Precision Floating Point Unit) * * The library functions are declared in the public file arm_math.h which is placed in the Include folder. * Simply include this file and link the appropriate library in the application and begin calling the library functions. The Library supports single - * public header file arm_math.h for Cortex-M7/M4/M3/M0/M0+ with little endian and big endian. Same header file will be used for floating point unit(FPU) variants. - * Define the appropriate pre processor MACRO ARM_MATH_CM7 or ARM_MATH_CM4 or ARM_MATH_CM3 or + * public header file arm_math.h for Cortex-M cores with little endian and big endian. Same header file will be used for floating point unit(FPU) variants. + * Define the appropriate preprocessor macro ARM_MATH_CM7 or ARM_MATH_CM4 or ARM_MATH_CM3 or * ARM_MATH_CM0 or ARM_MATH_CM0PLUS depending on the target processor in the application. + * For Armv8-M cores define preprocessor macro ARM_MATH_ARMV8MBL or ARM_MATH_ARMV8MML. + * Set preprocessor macro __DSP_PRESENT if Armv8-M Mainline core supports DSP instructions. + * * * Examples * -------- @@ -95,22 +87,22 @@ * Toolchain Support * ------------ * - * The library has been developed and tested with MDK-ARM version 5.14.0.0 + * The library has been developed and tested with MDK version 5.14.0.0 * The library is being tested in GCC and IAR toolchains and updates on this activity will be made available shortly. * * Building the Library * ------------ * - * The library installer contains a project file to re build libraries on MDK-ARM Tool chain in the CMSIS\\DSP_Lib\\Source\\ARM folder. + * The library installer contains a project file to rebuild libraries on MDK toolchain in the CMSIS\\DSP_Lib\\Source\\ARM folder. * - arm_cortexM_math.uvprojx * * - * The libraries can be built by opening the arm_cortexM_math.uvprojx project in MDK-ARM, selecting a specific target, and defining the optional pre processor MACROs detailed above. + * The libraries can be built by opening the arm_cortexM_math.uvprojx project in MDK-ARM, selecting a specific target, and defining the optional preprocessor macros detailed above. * - * Pre-processor Macros + * Preprocessor Macros * ------------ * - * Each library project have differant pre-processor macros. + * Each library project have different preprocessor macros. * * - UNALIGNED_SUPPORT_DISABLE: * @@ -134,9 +126,18 @@ * and ARM_MATH_CM0 for building library on Cortex-M0 target, ARM_MATH_CM0PLUS for building library on Cortex-M0+ target, and * ARM_MATH_CM7 for building the library on cortex-M7. * + * - ARM_MATH_ARMV8MxL: + * + * Define macro ARM_MATH_ARMV8MBL for building the library on Armv8-M Baseline target, ARM_MATH_ARMV8MML for building library + * on Armv8-M Mainline target. + * * - __FPU_PRESENT: * - * Initialize macro __FPU_PRESENT = 1 when building on FPU supported Targets. Enable this macro for M4bf and M4lf libraries + * Initialize macro __FPU_PRESENT = 1 when building on FPU supported Targets. Enable this macro for floating point libraries. + * + * - __DSP_PRESENT: + * + * Initialize macro __DSP_PRESENT = 1 when Armv8-M Mainline core supports DSP instructions. * *
* CMSIS-DSP in ARM::CMSIS Pack @@ -158,7 +159,7 @@ * Copyright Notice * ------------ * - * Copyright (C) 2010-2015 ARM Limited. All rights reserved. + * Copyright (C) 2010-2015 Arm Limited. All rights reserved. */ @@ -238,9 +239,9 @@ * * \par Size Checking * By default all of the matrix functions perform size checking on the input and - * output matrices. For example, the matrix addition function verifies that the + * output matrices. For example, the matrix addition function verifies that the * two input matrices and the output matrix all have the same number of rows and - * columns. If the size check fails the functions return: + * columns. If the size check fails the functions return: *
  *     ARM_MATH_SIZE_MISMATCH
  * 
@@ -254,9 +255,9 @@ * ARM_MATH_MATRIX_CHECK * * within the library project settings. By default this macro is defined - * and size checking is enabled. By changing the project settings and + * and size checking is enabled. By changing the project settings and * undefining this macro size checking is eliminated and the functions - * run a bit faster. With size checking disabled the functions always + * run a bit faster. With size checking disabled the functions always * return ARM_MATH_SUCCESS. */ @@ -288,20 +289,38 @@ #ifndef _ARM_MATH_H #define _ARM_MATH_H -/* ignore some GCC warnings */ -#if defined ( __GNUC__ ) +/* Compiler specific diagnostic adjustment */ +#if defined ( __CC_ARM ) + +#elif defined ( __ARMCC_VERSION ) && ( __ARMCC_VERSION >= 6010050 ) + +#elif defined ( __GNUC__ ) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-conversion" #pragma GCC diagnostic ignored "-Wconversion" #pragma GCC diagnostic ignored "-Wunused-parameter" + +#elif defined ( __ICCARM__ ) + +#elif defined ( __TI_ARM__ ) + +#elif defined ( __CSMC__ ) + +#elif defined ( __TASKING__ ) + +#else + #error Unknown compiler #endif + #define __CMSIS_GENERIC /* disable NVIC and Systick functions */ #if defined(ARM_MATH_CM7) #include "core_cm7.h" + #define ARM_MATH_DSP #elif defined (ARM_MATH_CM4) #include "core_cm4.h" + #define ARM_MATH_DSP #elif defined (ARM_MATH_CM3) #include "core_cm3.h" #elif defined (ARM_MATH_CM0) @@ -310,8 +329,16 @@ #elif defined (ARM_MATH_CM0PLUS) #include "core_cm0plus.h" #define ARM_MATH_CM0_FAMILY +#elif defined (ARM_MATH_ARMV8MBL) + #include "core_armv8mbl.h" + #define ARM_MATH_CM0_FAMILY +#elif defined (ARM_MATH_ARMV8MML) + #include "core_armv8mml.h" + #if (defined (__DSP_PRESENT) && (__DSP_PRESENT == 1)) + #define ARM_MATH_DSP + #endif #else - #error "Define according the used Cortex core ARM_MATH_CM7, ARM_MATH_CM4, ARM_MATH_CM3, ARM_MATH_CM0PLUS or ARM_MATH_CM0" + #error "Define according the used Cortex core ARM_MATH_CM7, ARM_MATH_CM4, ARM_MATH_CM3, ARM_MATH_CM0PLUS, ARM_MATH_CM0, ARM_MATH_ARMV8MBL, ARM_MATH_ARMV8MML" #endif #undef __CMSIS_GENERIC /* enable NVIC and Systick functions */ @@ -331,7 +358,7 @@ extern "C" #define DELTA_Q15 0x5 #define INDEX_MASK 0x0000003F #ifndef PI -#define PI 3.14159265358979f + #define PI 3.14159265358979f #endif /** @@ -342,7 +369,6 @@ extern "C" #define FAST_MATH_Q31_SHIFT (32 - 10) #define FAST_MATH_Q15_SHIFT (16 - 10) #define CONTROLLER_Q31_SHIFT (32 - 9) -#define TABLE_SIZE 256 #define TABLE_SPACING_Q31 0x400000 #define TABLE_SPACING_Q15 0x80 @@ -414,29 +440,40 @@ extern "C" /** * @brief definition to read/write two 16 bit values. */ -#if defined __CC_ARM +#if defined ( __CC_ARM ) #define __SIMD32_TYPE int32_t __packed #define CMSIS_UNUSED __attribute__((unused)) + #define CMSIS_INLINE __attribute__((always_inline)) -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined ( __ARMCC_VERSION ) && ( __ARMCC_VERSION >= 6010050 ) #define __SIMD32_TYPE int32_t #define CMSIS_UNUSED __attribute__((unused)) + #define CMSIS_INLINE __attribute__((always_inline)) -#elif defined __GNUC__ +#elif defined ( __GNUC__ ) #define __SIMD32_TYPE int32_t #define CMSIS_UNUSED __attribute__((unused)) + #define CMSIS_INLINE __attribute__((always_inline)) -#elif defined __ICCARM__ +#elif defined ( __ICCARM__ ) #define __SIMD32_TYPE int32_t __packed #define CMSIS_UNUSED + #define CMSIS_INLINE -#elif defined __CSMC__ +#elif defined ( __TI_ARM__ ) + #define __SIMD32_TYPE int32_t + #define CMSIS_UNUSED __attribute__((unused)) + #define CMSIS_INLINE + +#elif defined ( __CSMC__ ) #define __SIMD32_TYPE int32_t #define CMSIS_UNUSED + #define CMSIS_INLINE -#elif defined __TASKING__ +#elif defined ( __TASKING__ ) #define __SIMD32_TYPE __unaligned int32_t #define CMSIS_UNUSED + #define CMSIS_INLINE #else #error Unknown compiler @@ -447,17 +484,16 @@ extern "C" #define _SIMD32_OFFSET(addr) (*(__SIMD32_TYPE *) (addr)) #define __SIMD64(addr) (*(int64_t **) & (addr)) -#if defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) +#if !defined (ARM_MATH_DSP) /** * @brief definition to pack two 16 bit values. */ -#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) | \ - (((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) ) -#define __PKHTB(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0xFFFF0000) | \ - (((int32_t)(ARG2) >> ARG3) & (int32_t)0x0000FFFF) ) - -#endif +#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) | \ + (((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) ) +#define __PKHTB(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0xFFFF0000) | \ + (((int32_t)(ARG2) >> ARG3) & (int32_t)0x0000FFFF) ) +#endif /* !defined (ARM_MATH_DSP) */ /** * @brief definition to pack four 8 bit values. @@ -481,7 +517,7 @@ extern "C" /** * @brief Clips Q63 to Q31 values. */ - static __INLINE q31_t clip_q63_to_q31( + CMSIS_INLINE __STATIC_INLINE q31_t clip_q63_to_q31( q63_t x) { return ((q31_t) (x >> 32) != ((q31_t) x >> 31)) ? @@ -491,7 +527,7 @@ extern "C" /** * @brief Clips Q63 to Q15 values. */ - static __INLINE q15_t clip_q63_to_q15( + CMSIS_INLINE __STATIC_INLINE q15_t clip_q63_to_q15( q63_t x) { return ((q31_t) (x >> 32) != ((q31_t) x >> 31)) ? @@ -501,7 +537,7 @@ extern "C" /** * @brief Clips Q31 to Q7 values. */ - static __INLINE q7_t clip_q31_to_q7( + CMSIS_INLINE __STATIC_INLINE q7_t clip_q31_to_q7( q31_t x) { return ((q31_t) (x >> 24) != ((q31_t) x >> 23)) ? @@ -511,7 +547,7 @@ extern "C" /** * @brief Clips Q31 to Q15 values. */ - static __INLINE q15_t clip_q31_to_q15( + CMSIS_INLINE __STATIC_INLINE q15_t clip_q31_to_q15( q31_t x) { return ((q31_t) (x >> 16) != ((q31_t) x >> 15)) ? @@ -522,7 +558,7 @@ extern "C" * @brief Multiplies 32 X 64 and returns 32 bit result in 2.30 format. */ - static __INLINE q63_t mult32x64( + CMSIS_INLINE __STATIC_INLINE q63_t mult32x64( q63_t x, q31_t y) { @@ -530,37 +566,11 @@ extern "C" (((q63_t) (x >> 32) * y))); } -/* - #if defined (ARM_MATH_CM0_FAMILY) && defined ( __CC_ARM ) - #define __CLZ __clz - #endif - */ -/* note: function can be removed when all toolchain support __CLZ for Cortex-M0 */ -#if defined (ARM_MATH_CM0_FAMILY) && ((defined (__ICCARM__)) ) - static __INLINE uint32_t __CLZ( - q31_t data); - - static __INLINE uint32_t __CLZ( - q31_t data) - { - uint32_t count = 0; - uint32_t mask = 0x80000000; - - while((data & mask) == 0) - { - count += 1u; - mask = mask >> 1u; - } - - return (count); - } -#endif - /** * @brief Function to Calculates 1/in (reciprocal) value of Q31 Data type. */ - static __INLINE uint32_t arm_recip_q31( + CMSIS_INLINE __STATIC_INLINE uint32_t arm_recip_q31( q31_t in, q31_t * dst, q31_t * pRecipTable) @@ -570,7 +580,7 @@ extern "C" uint32_t index, i; uint32_t signBits; - if(in > 0) + if (in > 0) { signBits = ((uint32_t) (__CLZ( in) - 1)); } @@ -591,7 +601,7 @@ extern "C" /* calculation of reciprocal value */ /* running approximation for two iterations */ - for (i = 0u; i < 2u; i++) + for (i = 0U; i < 2U; i++) { tempVal = (uint32_t) (((q63_t) in * out) >> 31); tempVal = 0x7FFFFFFFu - tempVal; @@ -604,14 +614,14 @@ extern "C" *dst = out; /* return num of signbits of out = 1/in value */ - return (signBits + 1u); + return (signBits + 1U); } /** * @brief Function to Calculates 1/in (reciprocal) value of Q15 Data type. */ - static __INLINE uint32_t arm_recip_q15( + CMSIS_INLINE __STATIC_INLINE uint32_t arm_recip_q15( q15_t in, q15_t * dst, q15_t * pRecipTable) @@ -621,7 +631,7 @@ extern "C" uint32_t index = 0, i = 0; uint32_t signBits = 0; - if(in > 0) + if (in > 0) { signBits = ((uint32_t)(__CLZ( in) - 17)); } @@ -642,7 +652,7 @@ extern "C" /* calculation of reciprocal value */ /* running approximation for two iterations */ - for (i = 0u; i < 2u; i++) + for (i = 0U; i < 2U; i++) { tempVal = (uint32_t) (((q31_t) in * out) >> 15); tempVal = 0x7FFFu - tempVal; @@ -659,55 +669,15 @@ extern "C" } - /* - * @brief C custom defined intrinisic function for only M0 processors - */ -#if defined(ARM_MATH_CM0_FAMILY) - static __INLINE q31_t __SSAT( - q31_t x, - uint32_t y) - { - int32_t posMax, negMin; - uint32_t i; - - posMax = 1; - for (i = 0; i < (y - 1); i++) - { - posMax = posMax * 2; - } - - if(x > 0) - { - posMax = (posMax - 1); - - if(x > posMax) - { - x = posMax; - } - } - else - { - negMin = -posMax; - - if(x < negMin) - { - x = negMin; - } - } - return (x); - } -#endif /* end of ARM_MATH_CM0_FAMILY */ - - - /* - * @brief C custom defined intrinsic function for M3 and M0 processors - */ -#if defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) +/* + * @brief C custom defined intrinsic function for M3 and M0 processors + */ +#if !defined (ARM_MATH_DSP) /* * @brief C custom defined QADD8 for M3 and M0 processors */ - static __INLINE uint32_t __QADD8( + CMSIS_INLINE __STATIC_INLINE uint32_t __QADD8( uint32_t x, uint32_t y) { @@ -725,7 +695,7 @@ extern "C" /* * @brief C custom defined QSUB8 for M3 and M0 processors */ - static __INLINE uint32_t __QSUB8( + CMSIS_INLINE __STATIC_INLINE uint32_t __QSUB8( uint32_t x, uint32_t y) { @@ -743,7 +713,7 @@ extern "C" /* * @brief C custom defined QADD16 for M3 and M0 processors */ - static __INLINE uint32_t __QADD16( + CMSIS_INLINE __STATIC_INLINE uint32_t __QADD16( uint32_t x, uint32_t y) { @@ -760,7 +730,7 @@ extern "C" /* * @brief C custom defined SHADD16 for M3 and M0 processors */ - static __INLINE uint32_t __SHADD16( + CMSIS_INLINE __STATIC_INLINE uint32_t __SHADD16( uint32_t x, uint32_t y) { @@ -776,7 +746,7 @@ extern "C" /* * @brief C custom defined QSUB16 for M3 and M0 processors */ - static __INLINE uint32_t __QSUB16( + CMSIS_INLINE __STATIC_INLINE uint32_t __QSUB16( uint32_t x, uint32_t y) { @@ -792,7 +762,7 @@ extern "C" /* * @brief C custom defined SHSUB16 for M3 and M0 processors */ - static __INLINE uint32_t __SHSUB16( + CMSIS_INLINE __STATIC_INLINE uint32_t __SHSUB16( uint32_t x, uint32_t y) { @@ -808,7 +778,7 @@ extern "C" /* * @brief C custom defined QASX for M3 and M0 processors */ - static __INLINE uint32_t __QASX( + CMSIS_INLINE __STATIC_INLINE uint32_t __QASX( uint32_t x, uint32_t y) { @@ -824,7 +794,7 @@ extern "C" /* * @brief C custom defined SHASX for M3 and M0 processors */ - static __INLINE uint32_t __SHASX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SHASX( uint32_t x, uint32_t y) { @@ -840,7 +810,7 @@ extern "C" /* * @brief C custom defined QSAX for M3 and M0 processors */ - static __INLINE uint32_t __QSAX( + CMSIS_INLINE __STATIC_INLINE uint32_t __QSAX( uint32_t x, uint32_t y) { @@ -856,7 +826,7 @@ extern "C" /* * @brief C custom defined SHSAX for M3 and M0 processors */ - static __INLINE uint32_t __SHSAX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SHSAX( uint32_t x, uint32_t y) { @@ -872,7 +842,7 @@ extern "C" /* * @brief C custom defined SMUSDX for M3 and M0 processors */ - static __INLINE uint32_t __SMUSDX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMUSDX( uint32_t x, uint32_t y) { @@ -883,7 +853,7 @@ extern "C" /* * @brief C custom defined SMUADX for M3 and M0 processors */ - static __INLINE uint32_t __SMUADX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMUADX( uint32_t x, uint32_t y) { @@ -895,7 +865,7 @@ extern "C" /* * @brief C custom defined QADD for M3 and M0 processors */ - static __INLINE int32_t __QADD( + CMSIS_INLINE __STATIC_INLINE int32_t __QADD( int32_t x, int32_t y) { @@ -906,7 +876,7 @@ extern "C" /* * @brief C custom defined QSUB for M3 and M0 processors */ - static __INLINE int32_t __QSUB( + CMSIS_INLINE __STATIC_INLINE int32_t __QSUB( int32_t x, int32_t y) { @@ -917,7 +887,7 @@ extern "C" /* * @brief C custom defined SMLAD for M3 and M0 processors */ - static __INLINE uint32_t __SMLAD( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMLAD( uint32_t x, uint32_t y, uint32_t sum) @@ -931,7 +901,7 @@ extern "C" /* * @brief C custom defined SMLADX for M3 and M0 processors */ - static __INLINE uint32_t __SMLADX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMLADX( uint32_t x, uint32_t y, uint32_t sum) @@ -945,7 +915,7 @@ extern "C" /* * @brief C custom defined SMLSDX for M3 and M0 processors */ - static __INLINE uint32_t __SMLSDX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMLSDX( uint32_t x, uint32_t y, uint32_t sum) @@ -959,7 +929,7 @@ extern "C" /* * @brief C custom defined SMLALD for M3 and M0 processors */ - static __INLINE uint64_t __SMLALD( + CMSIS_INLINE __STATIC_INLINE uint64_t __SMLALD( uint32_t x, uint32_t y, uint64_t sum) @@ -974,7 +944,7 @@ extern "C" /* * @brief C custom defined SMLALDX for M3 and M0 processors */ - static __INLINE uint64_t __SMLALDX( + CMSIS_INLINE __STATIC_INLINE uint64_t __SMLALDX( uint32_t x, uint32_t y, uint64_t sum) @@ -989,7 +959,7 @@ extern "C" /* * @brief C custom defined SMUAD for M3 and M0 processors */ - static __INLINE uint32_t __SMUAD( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMUAD( uint32_t x, uint32_t y) { @@ -1001,7 +971,7 @@ extern "C" /* * @brief C custom defined SMUSD for M3 and M0 processors */ - static __INLINE uint32_t __SMUSD( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMUSD( uint32_t x, uint32_t y) { @@ -1013,14 +983,25 @@ extern "C" /* * @brief C custom defined SXTB16 for M3 and M0 processors */ - static __INLINE uint32_t __SXTB16( + CMSIS_INLINE __STATIC_INLINE uint32_t __SXTB16( uint32_t x) { return ((uint32_t)(((((q31_t)x << 24) >> 24) & (q31_t)0x0000FFFF) | ((((q31_t)x << 8) >> 8) & (q31_t)0xFFFF0000) )); } -#endif /* defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) */ + /* + * @brief C custom defined SMMLA for M3 and M0 processors + */ + CMSIS_INLINE __STATIC_INLINE int32_t __SMMLA( + int32_t x, + int32_t y, + int32_t sum) + { + return (sum + (int32_t) (((int64_t) x * y) >> 32)); + } + +#endif /* !defined (ARM_MATH_DSP) */ /** @@ -1737,7 +1718,7 @@ extern "C" typedef struct { q15_t A0; /**< The derived gain, A0 = Kp + Ki + Kd . */ -#ifdef ARM_MATH_CM0_FAMILY +#if !defined (ARM_MATH_DSP) q15_t A1; q15_t A2; #else @@ -4792,7 +4773,7 @@ void arm_rfft_fast_f32( * @param[in] in input sample to process * @return out processed output sample. */ - static __INLINE float32_t arm_pid_f32( + CMSIS_INLINE __STATIC_INLINE float32_t arm_pid_f32( arm_pid_instance_f32 * S, float32_t in) { @@ -4826,7 +4807,7 @@ void arm_rfft_fast_f32( * In order to avoid overflows completely the input signal must be scaled down by 2 bits as there are four additions. * After all multiply-accumulates are performed, the 2.62 accumulator is truncated to 1.32 format and then saturated to 1.31 format. */ - static __INLINE q31_t arm_pid_q31( + CMSIS_INLINE __STATIC_INLINE q31_t arm_pid_q31( arm_pid_instance_q31 * S, q31_t in) { @@ -4843,7 +4824,7 @@ void arm_rfft_fast_f32( acc += (q63_t) S->A2 * S->state[1]; /* convert output to 1.31 format to add y[n-1] */ - out = (q31_t) (acc >> 31u); + out = (q31_t) (acc >> 31U); /* out += y[n-1] */ out += S->state[2]; @@ -4873,14 +4854,14 @@ void arm_rfft_fast_f32( * After all additions have been performed, the accumulator is truncated to 34.15 format by discarding low 15 bits. * Lastly, the accumulator is saturated to yield a result in 1.15 format. */ - static __INLINE q15_t arm_pid_q15( + CMSIS_INLINE __STATIC_INLINE q15_t arm_pid_q15( arm_pid_instance_q15 * S, q15_t in) { q63_t acc; q15_t out; -#ifndef ARM_MATH_CM0_FAMILY +#if defined (ARM_MATH_DSP) __SIMD32_TYPE *vstate; /* Implementation of PID controller */ @@ -4984,7 +4965,7 @@ void arm_rfft_fast_f32( * @param[out] pIalpha points to output two-phase orthogonal vector axis alpha * @param[out] pIbeta points to output two-phase orthogonal vector axis beta */ - static __INLINE void arm_clarke_f32( + CMSIS_INLINE __STATIC_INLINE void arm_clarke_f32( float32_t Ia, float32_t Ib, float32_t * pIalpha, @@ -5011,7 +4992,7 @@ void arm_rfft_fast_f32( * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. * There is saturation on the addition, hence there is no risk of overflow. */ - static __INLINE void arm_clarke_q31( + CMSIS_INLINE __STATIC_INLINE void arm_clarke_q31( q31_t Ia, q31_t Ib, q31_t * pIalpha, @@ -5081,7 +5062,7 @@ void arm_rfft_fast_f32( * @param[out] pIa points to output three-phase coordinate a * @param[out] pIb points to output three-phase coordinate b */ - static __INLINE void arm_inv_clarke_f32( + CMSIS_INLINE __STATIC_INLINE void arm_inv_clarke_f32( float32_t Ialpha, float32_t Ibeta, float32_t * pIa, @@ -5108,7 +5089,7 @@ void arm_rfft_fast_f32( * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. * There is saturation on the subtraction, hence there is no risk of overflow. */ - static __INLINE void arm_inv_clarke_q31( + CMSIS_INLINE __STATIC_INLINE void arm_inv_clarke_q31( q31_t Ialpha, q31_t Ibeta, q31_t * pIa, @@ -5191,7 +5172,7 @@ void arm_rfft_fast_f32( * The function implements the forward Park transform. * */ - static __INLINE void arm_park_f32( + CMSIS_INLINE __STATIC_INLINE void arm_park_f32( float32_t Ialpha, float32_t Ibeta, float32_t * pId, @@ -5222,7 +5203,7 @@ void arm_rfft_fast_f32( * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. * There is saturation on the addition and subtraction, hence there is no risk of overflow. */ - static __INLINE void arm_park_q31( + CMSIS_INLINE __STATIC_INLINE void arm_park_q31( q31_t Ialpha, q31_t Ibeta, q31_t * pId, @@ -5304,7 +5285,7 @@ void arm_rfft_fast_f32( * @param[in] sinVal sine value of rotation angle theta * @param[in] cosVal cosine value of rotation angle theta */ - static __INLINE void arm_inv_park_f32( + CMSIS_INLINE __STATIC_INLINE void arm_inv_park_f32( float32_t Id, float32_t Iq, float32_t * pIalpha, @@ -5335,7 +5316,7 @@ void arm_rfft_fast_f32( * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. * There is saturation on the addition, hence there is no risk of overflow. */ - static __INLINE void arm_inv_park_q31( + CMSIS_INLINE __STATIC_INLINE void arm_inv_park_q31( q31_t Id, q31_t Iq, q31_t * pIalpha, @@ -5430,7 +5411,7 @@ void arm_rfft_fast_f32( * @return y processed output sample. * */ - static __INLINE float32_t arm_linear_interp_f32( + CMSIS_INLINE __STATIC_INLINE float32_t arm_linear_interp_f32( arm_linear_interp_instance_f32 * S, float32_t x) { @@ -5444,12 +5425,12 @@ void arm_rfft_fast_f32( /* Calculation of index */ i = (int32_t) ((x - S->x1) / xSpacing); - if(i < 0) + if (i < 0) { /* Iniatilize output for below specified range as least output value of table */ y = pYData[0]; } - else if((uint32_t)i >= S->nValues) + else if ((uint32_t)i >= S->nValues) { /* Iniatilize output for above specified range as last output value of table */ y = pYData[S->nValues - 1]; @@ -5487,7 +5468,7 @@ void arm_rfft_fast_f32( * This function can support maximum of table size 2^12. * */ - static __INLINE q31_t arm_linear_interp_q31( + CMSIS_INLINE __STATIC_INLINE q31_t arm_linear_interp_q31( q31_t * pYData, q31_t x, uint32_t nValues) @@ -5502,11 +5483,11 @@ void arm_rfft_fast_f32( /* Index value calculation */ index = ((x & (q31_t)0xFFF00000) >> 20); - if(index >= (int32_t)(nValues - 1)) + if (index >= (int32_t)(nValues - 1)) { return (pYData[nValues - 1]); } - else if(index < 0) + else if (index < 0) { return (pYData[0]); } @@ -5527,7 +5508,7 @@ void arm_rfft_fast_f32( y += ((q31_t) (((q63_t) y1 * fract) >> 32)); /* Convert y to 1.31 format */ - return (y << 1u); + return (y << 1U); } } @@ -5545,7 +5526,7 @@ void arm_rfft_fast_f32( * This function can support maximum of table size 2^12. * */ - static __INLINE q15_t arm_linear_interp_q15( + CMSIS_INLINE __STATIC_INLINE q15_t arm_linear_interp_q15( q15_t * pYData, q31_t x, uint32_t nValues) @@ -5560,11 +5541,11 @@ void arm_rfft_fast_f32( /* Index value calculation */ index = ((x & (int32_t)0xFFF00000) >> 20); - if(index >= (int32_t)(nValues - 1)) + if (index >= (int32_t)(nValues - 1)) { return (pYData[nValues - 1]); } - else if(index < 0) + else if (index < 0) { return (pYData[0]); } @@ -5602,7 +5583,7 @@ void arm_rfft_fast_f32( * Input sample x is in 12.20 format which contains 12 bits for table index and 20 bits for fractional part. * This function can support maximum of table size 2^12. */ - static __INLINE q7_t arm_linear_interp_q7( + CMSIS_INLINE __STATIC_INLINE q7_t arm_linear_interp_q7( q7_t * pYData, q31_t x, uint32_t nValues) @@ -5621,7 +5602,7 @@ void arm_rfft_fast_f32( } index = (x >> 20) & 0xfff; - if(index >= (nValues - 1)) + if (index >= (nValues - 1)) { return (pYData[nValues - 1]); } @@ -5742,11 +5723,11 @@ void arm_rfft_fast_f32( * @return The function returns ARM_MATH_SUCCESS if input value is positive value or ARM_MATH_ARGUMENT_ERROR if * in is negative value and returns zero output for negative values. */ - static __INLINE arm_status arm_sqrt_f32( + CMSIS_INLINE __STATIC_INLINE arm_status arm_sqrt_f32( float32_t in, float32_t * pOut) { - if(in >= 0.0f) + if (in >= 0.0f) { #if (__FPU_USED == 1) && defined ( __CC_ARM ) @@ -5802,7 +5783,7 @@ void arm_rfft_fast_f32( /** * @brief floating-point Circular write function. */ - static __INLINE void arm_circularWrite_f32( + CMSIS_INLINE __STATIC_INLINE void arm_circularWrite_f32( int32_t * circBuffer, int32_t L, uint16_t * writeOffset, @@ -5811,7 +5792,7 @@ void arm_rfft_fast_f32( int32_t srcInc, uint32_t blockSize) { - uint32_t i = 0u; + uint32_t i = 0U; int32_t wOffset; /* Copy the value of Index pointer that points @@ -5821,7 +5802,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the input sample to the circular buffer */ circBuffer[wOffset] = *src; @@ -5831,7 +5812,7 @@ void arm_rfft_fast_f32( /* Circularly update wOffset. Watch out for positive and negative value */ wOffset += bufferInc; - if(wOffset >= L) + if (wOffset >= L) wOffset -= L; /* Decrement the loop counter */ @@ -5847,7 +5828,7 @@ void arm_rfft_fast_f32( /** * @brief floating-point Circular Read function. */ - static __INLINE void arm_circularRead_f32( + CMSIS_INLINE __STATIC_INLINE void arm_circularRead_f32( int32_t * circBuffer, int32_t L, int32_t * readOffset, @@ -5858,7 +5839,7 @@ void arm_rfft_fast_f32( int32_t dstInc, uint32_t blockSize) { - uint32_t i = 0u; + uint32_t i = 0U; int32_t rOffset, dst_end; /* Copy the value of Index pointer that points @@ -5869,7 +5850,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the sample from the circular buffer to the destination buffer */ *dst = circBuffer[rOffset]; @@ -5877,7 +5858,7 @@ void arm_rfft_fast_f32( /* Update the input pointer */ dst += dstInc; - if(dst == (int32_t *) dst_end) + if (dst == (int32_t *) dst_end) { dst = dst_base; } @@ -5885,7 +5866,7 @@ void arm_rfft_fast_f32( /* Circularly update rOffset. Watch out for positive and negative value */ rOffset += bufferInc; - if(rOffset >= L) + if (rOffset >= L) { rOffset -= L; } @@ -5902,7 +5883,7 @@ void arm_rfft_fast_f32( /** * @brief Q15 Circular write function. */ - static __INLINE void arm_circularWrite_q15( + CMSIS_INLINE __STATIC_INLINE void arm_circularWrite_q15( q15_t * circBuffer, int32_t L, uint16_t * writeOffset, @@ -5911,7 +5892,7 @@ void arm_rfft_fast_f32( int32_t srcInc, uint32_t blockSize) { - uint32_t i = 0u; + uint32_t i = 0U; int32_t wOffset; /* Copy the value of Index pointer that points @@ -5921,7 +5902,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the input sample to the circular buffer */ circBuffer[wOffset] = *src; @@ -5931,7 +5912,7 @@ void arm_rfft_fast_f32( /* Circularly update wOffset. Watch out for positive and negative value */ wOffset += bufferInc; - if(wOffset >= L) + if (wOffset >= L) wOffset -= L; /* Decrement the loop counter */ @@ -5946,7 +5927,7 @@ void arm_rfft_fast_f32( /** * @brief Q15 Circular Read function. */ - static __INLINE void arm_circularRead_q15( + CMSIS_INLINE __STATIC_INLINE void arm_circularRead_q15( q15_t * circBuffer, int32_t L, int32_t * readOffset, @@ -5969,7 +5950,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the sample from the circular buffer to the destination buffer */ *dst = circBuffer[rOffset]; @@ -5977,7 +5958,7 @@ void arm_rfft_fast_f32( /* Update the input pointer */ dst += dstInc; - if(dst == (q15_t *) dst_end) + if (dst == (q15_t *) dst_end) { dst = dst_base; } @@ -5985,7 +5966,7 @@ void arm_rfft_fast_f32( /* Circularly update wOffset. Watch out for positive and negative value */ rOffset += bufferInc; - if(rOffset >= L) + if (rOffset >= L) { rOffset -= L; } @@ -6002,7 +5983,7 @@ void arm_rfft_fast_f32( /** * @brief Q7 Circular write function. */ - static __INLINE void arm_circularWrite_q7( + CMSIS_INLINE __STATIC_INLINE void arm_circularWrite_q7( q7_t * circBuffer, int32_t L, uint16_t * writeOffset, @@ -6011,7 +5992,7 @@ void arm_rfft_fast_f32( int32_t srcInc, uint32_t blockSize) { - uint32_t i = 0u; + uint32_t i = 0U; int32_t wOffset; /* Copy the value of Index pointer that points @@ -6021,7 +6002,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the input sample to the circular buffer */ circBuffer[wOffset] = *src; @@ -6031,7 +6012,7 @@ void arm_rfft_fast_f32( /* Circularly update wOffset. Watch out for positive and negative value */ wOffset += bufferInc; - if(wOffset >= L) + if (wOffset >= L) wOffset -= L; /* Decrement the loop counter */ @@ -6046,7 +6027,7 @@ void arm_rfft_fast_f32( /** * @brief Q7 Circular Read function. */ - static __INLINE void arm_circularRead_q7( + CMSIS_INLINE __STATIC_INLINE void arm_circularRead_q7( q7_t * circBuffer, int32_t L, int32_t * readOffset, @@ -6069,7 +6050,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the sample from the circular buffer to the destination buffer */ *dst = circBuffer[rOffset]; @@ -6077,7 +6058,7 @@ void arm_rfft_fast_f32( /* Update the input pointer */ dst += dstInc; - if(dst == (q7_t *) dst_end) + if (dst == (q7_t *) dst_end) { dst = dst_base; } @@ -6085,7 +6066,7 @@ void arm_rfft_fast_f32( /* Circularly update rOffset. Watch out for positive and negative value */ rOffset += bufferInc; - if(rOffset >= L) + if (rOffset >= L) { rOffset -= L; } @@ -6749,7 +6730,7 @@ void arm_rfft_fast_f32( * @param[in] Y interpolation coordinate. * @return out interpolated value. */ - static __INLINE float32_t arm_bilinear_interp_f32( + CMSIS_INLINE __STATIC_INLINE float32_t arm_bilinear_interp_f32( const arm_bilinear_interp_instance_f32 * S, float32_t X, float32_t Y) @@ -6766,7 +6747,7 @@ void arm_rfft_fast_f32( /* Care taken for table outside boundary */ /* Returns zero output when values are outside table boundary */ - if(xIndex < 0 || xIndex > (S->numRows - 1) || yIndex < 0 || yIndex > (S->numCols - 1)) + if (xIndex < 0 || xIndex > (S->numRows - 1) || yIndex < 0 || yIndex > (S->numCols - 1)) { return (0); } @@ -6815,7 +6796,7 @@ void arm_rfft_fast_f32( * @param[in] Y interpolation coordinate in 12.20 format. * @return out interpolated value. */ - static __INLINE q31_t arm_bilinear_interp_q31( + CMSIS_INLINE __STATIC_INLINE q31_t arm_bilinear_interp_q31( arm_bilinear_interp_instance_q31 * S, q31_t X, q31_t Y) @@ -6840,14 +6821,14 @@ void arm_rfft_fast_f32( /* Care taken for table outside boundary */ /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) + if (rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) { return (0); } /* 20 bits for the fractional part */ /* shift left xfract by 11 to keep 1.31 format */ - xfract = (X & 0x000FFFFF) << 11u; + xfract = (X & 0x000FFFFF) << 11U; /* Read two nearest output values from the index */ x1 = pYData[(rI) + (int32_t)nCols * (cI) ]; @@ -6855,7 +6836,7 @@ void arm_rfft_fast_f32( /* 20 bits for the fractional part */ /* shift left yfract by 11 to keep 1.31 format */ - yfract = (Y & 0x000FFFFF) << 11u; + yfract = (Y & 0x000FFFFF) << 11U; /* Read two nearest output values from the index */ y1 = pYData[(rI) + (int32_t)nCols * (cI + 1) ]; @@ -6889,7 +6870,7 @@ void arm_rfft_fast_f32( * @param[in] Y interpolation coordinate in 12.20 format. * @return out interpolated value. */ - static __INLINE q15_t arm_bilinear_interp_q15( + CMSIS_INLINE __STATIC_INLINE q15_t arm_bilinear_interp_q15( arm_bilinear_interp_instance_q15 * S, q31_t X, q31_t Y) @@ -6914,7 +6895,7 @@ void arm_rfft_fast_f32( /* Care taken for table outside boundary */ /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) + if (rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) { return (0); } @@ -6939,19 +6920,19 @@ void arm_rfft_fast_f32( /* x1 is in 1.15(q15), xfract in 12.20 format and out is in 13.35 format */ /* convert 13.35 to 13.31 by right shifting and out is in 1.31 */ - out = (q31_t) (((q63_t) x1 * (0xFFFFF - xfract)) >> 4u); + out = (q31_t) (((q63_t) x1 * (0xFFFFF - xfract)) >> 4U); acc = ((q63_t) out * (0xFFFFF - yfract)); /* x2 * (xfract) * (1-yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) x2 * (0xFFFFF - yfract)) >> 4u); + out = (q31_t) (((q63_t) x2 * (0xFFFFF - yfract)) >> 4U); acc += ((q63_t) out * (xfract)); /* y1 * (1 - xfract) * (yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) y1 * (0xFFFFF - xfract)) >> 4u); + out = (q31_t) (((q63_t) y1 * (0xFFFFF - xfract)) >> 4U); acc += ((q63_t) out * (yfract)); /* y2 * (xfract) * (yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) y2 * (xfract)) >> 4u); + out = (q31_t) (((q63_t) y2 * (xfract)) >> 4U); acc += ((q63_t) out * (yfract)); /* acc is in 13.51 format and down shift acc by 36 times */ @@ -6967,7 +6948,7 @@ void arm_rfft_fast_f32( * @param[in] Y interpolation coordinate in 12.20 format. * @return out interpolated value. */ - static __INLINE q7_t arm_bilinear_interp_q7( + CMSIS_INLINE __STATIC_INLINE q7_t arm_bilinear_interp_q7( arm_bilinear_interp_instance_q7 * S, q31_t X, q31_t Y) @@ -6992,7 +6973,7 @@ void arm_rfft_fast_f32( /* Care taken for table outside boundary */ /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) + if (rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) { return (0); } @@ -7063,7 +7044,7 @@ void arm_rfft_fast_f32( a = (q31_t) (((q63_t) x * y ) >> 32) -#if defined ( __CC_ARM ) +#if defined ( __CC_ARM ) /* Enter low optimization region - place directly above function definition */ #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) #define LOW_OPTIMIZATION_ENTER \ @@ -7074,7 +7055,7 @@ void arm_rfft_fast_f32( #endif /* Exit low optimization region - place directly after end of function definition */ - #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) + #if defined ( ARM_MATH_CM4 ) || defined ( ARM_MATH_CM7 ) #define LOW_OPTIMIZATION_EXIT \ _Pragma ("pop") #else @@ -7087,21 +7068,22 @@ void arm_rfft_fast_f32( /* Exit low optimization region - place directly after end of function definition */ #define IAR_ONLY_LOW_OPTIMIZATION_EXIT -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION ) && ( __ARMCC_VERSION >= 6010050 ) #define LOW_OPTIMIZATION_ENTER #define LOW_OPTIMIZATION_EXIT #define IAR_ONLY_LOW_OPTIMIZATION_ENTER #define IAR_ONLY_LOW_OPTIMIZATION_EXIT -#elif defined(__GNUC__) - #define LOW_OPTIMIZATION_ENTER __attribute__(( optimize("-O1") )) +#elif defined ( __GNUC__ ) + #define LOW_OPTIMIZATION_ENTER \ + __attribute__(( optimize("-O1") )) #define LOW_OPTIMIZATION_EXIT #define IAR_ONLY_LOW_OPTIMIZATION_ENTER #define IAR_ONLY_LOW_OPTIMIZATION_EXIT -#elif defined(__ICCARM__) +#elif defined ( __ICCARM__ ) /* Enter low optimization region - place directly above function definition */ - #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) + #if defined ( ARM_MATH_CM4 ) || defined ( ARM_MATH_CM7 ) #define LOW_OPTIMIZATION_ENTER \ _Pragma ("optimize=low") #else @@ -7112,7 +7094,7 @@ void arm_rfft_fast_f32( #define LOW_OPTIMIZATION_EXIT /* Enter low optimization region - place directly above function definition */ - #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) + #if defined ( ARM_MATH_CM4 ) || defined ( ARM_MATH_CM7 ) #define IAR_ONLY_LOW_OPTIMIZATION_ENTER \ _Pragma ("optimize=low") #else @@ -7122,13 +7104,19 @@ void arm_rfft_fast_f32( /* Exit low optimization region - place directly after end of function definition */ #define IAR_ONLY_LOW_OPTIMIZATION_EXIT -#elif defined(__CSMC__) +#elif defined ( __TI_ARM__ ) + #define LOW_OPTIMIZATION_ENTER + #define LOW_OPTIMIZATION_EXIT + #define IAR_ONLY_LOW_OPTIMIZATION_ENTER + #define IAR_ONLY_LOW_OPTIMIZATION_EXIT + +#elif defined ( __CSMC__ ) #define LOW_OPTIMIZATION_ENTER #define LOW_OPTIMIZATION_EXIT #define IAR_ONLY_LOW_OPTIMIZATION_ENTER #define IAR_ONLY_LOW_OPTIMIZATION_EXIT -#elif defined(__TASKING__) +#elif defined ( __TASKING__ ) #define LOW_OPTIMIZATION_ENTER #define LOW_OPTIMIZATION_EXIT #define IAR_ONLY_LOW_OPTIMIZATION_ENTER @@ -7141,9 +7129,24 @@ void arm_rfft_fast_f32( } #endif +/* Compiler specific diagnostic adjustment */ +#if defined ( __CC_ARM ) -#if defined ( __GNUC__ ) +#elif defined ( __ARMCC_VERSION ) && ( __ARMCC_VERSION >= 6010050 ) + +#elif defined ( __GNUC__ ) #pragma GCC diagnostic pop + +#elif defined ( __ICCARM__ ) + +#elif defined ( __TI_ARM__ ) + +#elif defined ( __CSMC__ ) + +#elif defined ( __TASKING__ ) + +#else + #error Unknown compiler #endif #endif /* _ARM_MATH_H */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_armcc.h similarity index 75% rename from Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc.h rename to Firmware/ThirdParty/CMSIS/Include/cmsis_armcc.h index 74c49c67d..4d9d0645d 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc.h +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_armcc.h @@ -1,43 +1,104 @@ /**************************************************************************//** * @file cmsis_armcc.h - * @brief CMSIS Cortex-M Core Function/Instruction Header File - * @version V4.30 - * @date 20. October 2015 + * @brief CMSIS compiler ARMCC (Arm Compiler 5) header file + * @version V5.0.4 + * @date 10. January 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ #ifndef __CMSIS_ARMCC_H #define __CMSIS_ARMCC_H #if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 400677) - #error "Please use ARM Compiler Toolchain V4.0.677 or later!" + #error "Please use Arm Compiler Toolchain V4.0.677 or later!" +#endif + +/* CMSIS compiler control architecture macros */ +#if ((defined (__TARGET_ARCH_6_M ) && (__TARGET_ARCH_6_M == 1)) || \ + (defined (__TARGET_ARCH_6S_M ) && (__TARGET_ARCH_6S_M == 1)) ) + #define __ARM_ARCH_6M__ 1 +#endif + +#if (defined (__TARGET_ARCH_7_M ) && (__TARGET_ARCH_7_M == 1)) + #define __ARM_ARCH_7M__ 1 +#endif + +#if (defined (__TARGET_ARCH_7E_M) && (__TARGET_ARCH_7E_M == 1)) + #define __ARM_ARCH_7EM__ 1 +#endif + + /* __ARM_ARCH_8M_BASE__ not applicable */ + /* __ARM_ARCH_8M_MAIN__ not applicable */ + + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE static __forceinline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __declspec(noreturn) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed)) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT __packed struct +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION __packed union +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #define __UNALIGNED_UINT32(x) (*((__packed uint32_t *)(x))) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #define __UNALIGNED_UINT16_WRITE(addr, val) ((*((__packed uint16_t *)(addr))) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #define __UNALIGNED_UINT16_READ(addr) (*((const __packed uint16_t *)(addr))) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #define __UNALIGNED_UINT32_WRITE(addr, val) ((*((__packed uint32_t *)(addr))) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #define __UNALIGNED_UINT32_READ(addr) (*((const __packed uint32_t *)(addr))) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict #endif /* ########################### Core Function Access ########################### */ @@ -46,7 +107,19 @@ @{ */ +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ /* intrinsic void __enable_irq(); */ + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ /* intrinsic void __disable_irq(); */ /** @@ -181,7 +254,8 @@ __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) } -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) /** \brief Enable FIQ @@ -256,13 +330,12 @@ __STATIC_INLINE uint32_t __get_FAULTMASK(void) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) { register uint32_t __regFaultMask __ASM("faultmask"); - __regFaultMask = (faultMask & (uint32_t)1); + __regFaultMask = (faultMask & (uint32_t)1U); } -#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */ - +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ -#if (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) /** \brief Get FPSCR @@ -271,7 +344,8 @@ __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) */ __STATIC_INLINE uint32_t __get_FPSCR(void) { -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) register uint32_t __regfpscr __ASM("fpscr"); return(__regfpscr); #else @@ -287,15 +361,15 @@ __STATIC_INLINE uint32_t __get_FPSCR(void) */ __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) { -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) register uint32_t __regfpscr __ASM("fpscr"); __regfpscr = (fpscr); +#else + (void)fpscr; #endif } -#endif /* (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) */ - - /*@} end of CMSIS_Core_RegAccFunctions */ @@ -369,9 +443,10 @@ __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) __schedule_barrier();\ } while (0U) + /** \brief Reverse byte order (32 bit) - \details Reverses the byte order in integer value. + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. \param [in] value Value to reverse \return Reversed value */ @@ -380,7 +455,7 @@ __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) /** \brief Reverse byte order (16 bit) - \details Reverses the byte order in two unsigned short values. + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. \param [in] value Value to reverse \return Reversed value */ @@ -392,14 +467,15 @@ __attribute__((section(".rev16_text"))) __STATIC_INLINE __ASM uint32_t __REV16(u } #endif + /** - \brief Reverse byte order in signed short value - \details Reverses the byte order in a signed short value with sign extension to integer. + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. \param [in] value Value to reverse \return Reversed value */ #ifndef __NO_EMBEDDED_ASM -__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(int32_t value) +__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int16_t __REVSH(int16_t value) { revsh r0, r0 bx lr @@ -410,8 +486,8 @@ __attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(in /** \brief Rotate Right in unsigned value (32 bit) \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. - \param [in] value Value to rotate - \param [in] value Number of Bits to rotate + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate \return Rotated value */ #define __ROR __ror @@ -433,23 +509,24 @@ __attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(in \param [in] value Value to reverse \return Reversed value */ -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) #define __RBIT __rbit #else __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) { uint32_t result; - int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */ + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ result = value; /* r will be reversed bits of v; first get LSB of v */ - for (value >>= 1U; value; value >>= 1U) + for (value >>= 1U; value != 0U; value >>= 1U) { result <<= 1U; result |= value & 1U; s--; } result <<= s; /* shift when v's highest bits are zero */ - return(result); + return result; } #endif @@ -463,7 +540,8 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) #define __CLZ __clz -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) /** \brief LDR Exclusive (8 bit) @@ -645,7 +723,60 @@ __attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint3 */ #define __STRT(value, ptr) __strt(value, ptr) -#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */ +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__attribute__((always_inline)) __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ /*@}*/ /* end of group CMSIS_Core_InstructionInterface */ @@ -656,7 +787,7 @@ __attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint3 @{ */ -#if (__CORTEX_M >= 0x04U) /* only for Cortex-M4 and above */ +#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) #define __SADD8 __sadd8 #define __QADD8 __qadd8 @@ -727,7 +858,7 @@ __attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint3 #define __SMMLA(ARG1,ARG2,ARG3) ( (int32_t)((((int64_t)(ARG1) * (ARG2)) + \ ((int64_t)(ARG3) << 32U) ) >> 32U)) -#endif /* (__CORTEX_M >= 0x04) */ +#endif /* ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ /*@} end of group CMSIS_SIMD_intrinsics */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc_V6.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_armclang.h similarity index 57% rename from Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc_V6.h rename to Firmware/ThirdParty/CMSIS/Include/cmsis_armclang.h index cd13240ce..162a400ea 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc_V6.h +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_armclang.h @@ -1,39 +1,115 @@ /**************************************************************************//** - * @file cmsis_armcc_V6.h - * @brief CMSIS Cortex-M Core Function/Instruction Header File - * @version V4.30 - * @date 20. October 2015 + * @file cmsis_armclang.h + * @brief CMSIS compiler armclang (Arm Compiler 6) header file + * @version V5.0.4 + * @date 10. January 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#ifndef __CMSIS_ARMCC_V6_H -#define __CMSIS_ARMCC_V6_H +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */ + +#ifndef __CMSIS_ARMCLANG_H +#define __CMSIS_ARMCLANG_H + +#pragma clang system_header /* treat file as system include file */ + +#ifndef __ARM_COMPAT_H +#include /* Compatibility header for Arm Compiler 5 intrinsics */ +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static __inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */ + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */ + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif /* ########################### Core Function Access ########################### */ @@ -47,10 +123,7 @@ \details Enables IRQ interrupts by clearing the I-bit in the CPSR. Can only be executed in Privileged modes. */ -__attribute__((always_inline)) __STATIC_INLINE void __enable_irq(void) -{ - __ASM volatile ("cpsie i" : : : "memory"); -} +/* intrinsic void __enable_irq(); see arm_compat.h */ /** @@ -58,10 +131,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __enable_irq(void) \details Disables IRQ interrupts by setting the I-bit in the CPSR. Can only be executed in Privileged modes. */ -__attribute__((always_inline)) __STATIC_INLINE void __disable_irq(void) -{ - __ASM volatile ("cpsid i" : : : "memory"); -} +/* intrinsic void __disable_irq(); see arm_compat.h */ /** @@ -69,7 +139,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __disable_irq(void) \details Returns the content of the Control Register. \return Control Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_CONTROL(void) +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) { uint32_t result; @@ -78,13 +148,13 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_CONTROL(void) } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Control Register (non-secure) \details Returns the content of the non-secure Control Register when in secure mode. \return non-secure Control Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_CONTROL_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) { uint32_t result; @@ -99,19 +169,19 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_CONTROL_NS(void \details Writes the given value to the Control Register. \param [in] control Control Register value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_CONTROL(uint32_t control) +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) { __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Control Register (non-secure) \details Writes the given value to the non-secure Control Register when in secure state. \param [in] control Control Register value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_CONTROL_NS(uint32_t control) +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) { __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); } @@ -123,7 +193,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __TZ_set_CONTROL_NS(uint32_t \details Returns the content of the IPSR Register. \return IPSR Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_IPSR(void) +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) { uint32_t result; @@ -132,28 +202,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_IPSR(void) } -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get IPSR Register (non-secure) - \details Returns the content of the non-secure IPSR Register when in secure state. - \return IPSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_IPSR_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, ipsr_ns" : "=r" (result) ); - return(result); -} -#endif - - /** \brief Get APSR Register \details Returns the content of the APSR Register. \return APSR Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_APSR(void) +__STATIC_FORCEINLINE uint32_t __get_APSR(void) { uint32_t result; @@ -162,28 +216,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_APSR(void) } -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get APSR Register (non-secure) - \details Returns the content of the non-secure APSR Register when in secure state. - \return APSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_APSR_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, apsr_ns" : "=r" (result) ); - return(result); -} -#endif - - /** \brief Get xPSR Register \details Returns the content of the xPSR Register. \return xPSR Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_xPSR(void) +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) { uint32_t result; @@ -192,45 +230,29 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_xPSR(void) } -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get xPSR Register (non-secure) - \details Returns the content of the non-secure xPSR Register when in secure state. - \return xPSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_xPSR_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, xpsr_ns" : "=r" (result) ); - return(result); -} -#endif - - /** \brief Get Process Stack Pointer \details Returns the current value of the Process Stack Pointer (PSP). \return PSP Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PSP(void) +__STATIC_FORCEINLINE uint32_t __get_PSP(void) { - register uint32_t result; + uint32_t result; __ASM volatile ("MRS %0, psp" : "=r" (result) ); return(result); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Process Stack Pointer (non-secure) \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. \return PSP Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PSP_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) { - register uint32_t result; + uint32_t result; __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); return(result); @@ -243,21 +265,21 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PSP_NS(void) \details Assigns the given value to the Process Stack Pointer (PSP). \param [in] topOfProcStack Process Stack Pointer value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) { - __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : "sp"); + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Process Stack Pointer (non-secure) \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. \param [in] topOfProcStack Process Stack Pointer value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) { - __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : "sp"); + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); } #endif @@ -267,24 +289,24 @@ __attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PSP_NS(uint32_t top \details Returns the current value of the Main Stack Pointer (MSP). \return MSP Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_MSP(void) +__STATIC_FORCEINLINE uint32_t __get_MSP(void) { - register uint32_t result; + uint32_t result; __ASM volatile ("MRS %0, msp" : "=r" (result) ); return(result); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Main Stack Pointer (non-secure) \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. \return MSP Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_MSP_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) { - register uint32_t result; + uint32_t result; __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); return(result); @@ -297,21 +319,48 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_MSP_NS(void) \details Assigns the given value to the Main Stack Pointer (MSP). \param [in] topOfMainStack Main Stack Pointer value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) { - __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : "sp"); + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Main Stack Pointer (non-secure) \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. \param [in] topOfMainStack Main Stack Pointer value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) { - __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : "sp"); + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); } #endif @@ -321,7 +370,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __TZ_set_MSP_NS(uint32_t top \details Returns the current state of the priority mask bit from the Priority Mask Register. \return Priority Mask value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PRIMASK(void) +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) { uint32_t result; @@ -330,13 +379,13 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PRIMASK(void) } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Priority Mask (non-secure) \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. \return Priority Mask value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PRIMASK_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) { uint32_t result; @@ -351,36 +400,34 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PRIMASK_NS(void \details Assigns the given value to the Priority Mask Register. \param [in] priMask Priority Mask */ -__attribute__((always_inline)) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) { __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Priority Mask (non-secure) \details Assigns the given value to the non-secure Priority Mask Register when in secure state. \param [in] priMask Priority Mask */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) { __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); } #endif -#if ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=3 */ - +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) /** \brief Enable FIQ \details Enables FIQ interrupts by clearing the F-bit in the CPSR. Can only be executed in Privileged modes. */ -__attribute__((always_inline)) __STATIC_INLINE void __enable_fault_irq(void) -{ - __ASM volatile ("cpsie f" : : : "memory"); -} +#define __enable_fault_irq __enable_fiq /* see arm_compat.h */ /** @@ -388,10 +435,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __enable_fault_irq(void) \details Disables FIQ interrupts by setting the F-bit in the CPSR. Can only be executed in Privileged modes. */ -__attribute__((always_inline)) __STATIC_INLINE void __disable_fault_irq(void) -{ - __ASM volatile ("cpsid f" : : : "memory"); -} +#define __disable_fault_irq __disable_fiq /* see arm_compat.h */ /** @@ -399,7 +443,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __disable_fault_irq(void) \details Returns the current value of the Base Priority register. \return Base Priority register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_BASEPRI(void) +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) { uint32_t result; @@ -408,13 +452,13 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_BASEPRI(void) } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Base Priority (non-secure) \details Returns the current value of the non-secure Base Priority register when in secure state. \return Base Priority register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_BASEPRI_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) { uint32_t result; @@ -429,21 +473,21 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_BASEPRI_NS(void \details Assigns the given value to the Base Priority register. \param [in] basePri Base Priority value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_BASEPRI(uint32_t value) +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) { - __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Base Priority (non-secure) \details Assigns the given value to the non-secure Base Priority register when in secure state. \param [in] basePri Base Priority value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_BASEPRI_NS(uint32_t value) +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) { - __ASM volatile ("MSR basepri_ns, %0" : : "r" (value) : "memory"); + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); } #endif @@ -454,32 +498,18 @@ __attribute__((always_inline)) __STATIC_INLINE void __TZ_set_BASEPRI_NS(uint32_t or the new value increases the BASEPRI priority level. \param [in] basePri Base Priority value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_BASEPRI_MAX(uint32_t value) +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) { - __ASM volatile ("MSR basepri_max, %0" : : "r" (value) : "memory"); + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); } -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set Base Priority with condition (non_secure) - \details Assigns the given value to the non-secure Base Priority register when in secure state only if BASEPRI masking is disabled, - or the new value increases the BASEPRI priority level. - \param [in] basePri Base Priority value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_BASEPRI_MAX_NS(uint32_t value) -{ - __ASM volatile ("MSR basepri_max_ns, %0" : : "r" (value) : "memory"); -} -#endif - - /** \brief Get Fault Mask \details Returns the current value of the Fault Mask register. \return Fault Mask register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_FAULTMASK(void) +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) { uint32_t result; @@ -488,13 +518,13 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_FAULTMASK(void) } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Fault Mask (non-secure) \details Returns the current value of the non-secure Fault Mask register when in secure state. \return Fault Mask register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_FAULTMASK_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) { uint32_t result; @@ -509,223 +539,233 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_FAULTMASK_NS(vo \details Assigns the given value to the Fault Mask register. \param [in] faultMask Fault Mask value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) { __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Fault Mask (non-secure) \details Assigns the given value to the non-secure Fault Mask register when in secure state. \param [in] faultMask Fault Mask value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) { __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); } #endif +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ -#endif /* ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_8M__ == 1U)) */ - -#if (__ARM_ARCH_8M__ == 1U) +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) /** \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). \return PSPLIM Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PSPLIM(void) +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) { - register uint32_t result; - +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; __ASM volatile ("MRS %0, psplim" : "=r" (result) ); - return(result); + return result; +#endif } - -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. \return PSPLIM Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PSPLIM_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) { - register uint32_t result; - +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); - return(result); + return result; +#endif } #endif /** \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) { +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif } -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) { +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif } #endif /** \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). \return MSPLIM Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_MSPLIM(void) +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) { - register uint32_t result; - +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; __ASM volatile ("MRS %0, msplim" : "=r" (result) ); - - return(result); + return result; +#endif } -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. \return MSPLIM Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_MSPLIM_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) { - register uint32_t result; - +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); - return(result); + return result; +#endif } #endif /** \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) { +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif } -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. \param [in] MainStackPtrLimit Main Stack Pointer value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) { +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif } #endif -#endif /* (__ARM_ARCH_8M__ == 1U) */ - - -#if ((__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=4 */ +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ /** \brief Get FPSCR - \details eturns the current value of the Floating Point Status/Control register. - \return Floating Point Status/Control register value - */ -#define __get_FPSCR __builtin_arm_get_fpscr -#if 0 -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_FPSCR(void) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - uint32_t result; - - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); - __ASM volatile (""); - return(result); -#else - return(0); -#endif -} -#endif - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get FPSCR (non-secure) - \details Returns the current value of the non-secure Floating Point Status/Control register when in secure state. + \details Returns the current value of the Floating Point Status/Control register. \return Floating Point Status/Control register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_FPSCR_NS(void) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - uint32_t result; - - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMRS %0, fpscr_ns" : "=r" (result) ); - __ASM volatile (""); - return(result); +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __get_FPSCR (uint32_t)__builtin_arm_get_fpscr #else - return(0); -#endif -} +#define __get_FPSCR() ((uint32_t)0U) #endif - /** \brief Set FPSCR \details Assigns the given value to the Floating Point Status/Control register. \param [in] fpscr Floating Point Status/Control value to set */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) #define __set_FPSCR __builtin_arm_set_fpscr -#if 0 -__attribute__((always_inline)) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); - __ASM volatile (""); -#endif -} -#endif - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set FPSCR (non-secure) - \details Assigns the given value to the non-secure Floating Point Status/Control register when in secure state. - \param [in] fpscr Floating Point Status/Control value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_FPSCR_NS(uint32_t fpscr) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMSR fpscr_ns, %0" : : "r" (fpscr) : "vfpcc"); - __ASM volatile (""); -#endif -} +#else +#define __set_FPSCR(x) ((void)(x)) #endif -#endif /* ((__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) */ - - /*@} end of CMSIS_Core_RegAccFunctions */ @@ -801,45 +841,29 @@ __attribute__((always_inline)) __STATIC_INLINE void __TZ_set_FPSCR_NS(uint32_t f /** \brief Reverse byte order (32 bit) - \details Reverses the byte order in integer value. + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. \param [in] value Value to reverse \return Reversed value */ -#define __REV __builtin_bswap32 +#define __REV(value) __builtin_bswap32(value) /** \brief Reverse byte order (16 bit) - \details Reverses the byte order in two unsigned short values. + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. \param [in] value Value to reverse \return Reversed value */ -#define __REV16 __builtin_bswap16 /* ToDo: ARMCC_V6: check if __builtin_bswap16 could be used */ -#if 0 -__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV16(uint32_t value) -{ - uint32_t result; - - __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} -#endif +#define __REV16(value) __ROR(__REV(value), 16) /** - \brief Reverse byte order in signed short value - \details Reverses the byte order in a signed short value with sign extension to integer. + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. \param [in] value Value to reverse \return Reversed value */ - /* ToDo: ARMCC_V6: check if __builtin_bswap16 could be used */ -__attribute__((always_inline)) __STATIC_INLINE int32_t __REVSH(int32_t value) -{ - int32_t result; - - __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} +#define __REVSH(value) (int16_t)__builtin_bswap16(value) /** @@ -849,8 +873,13 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __REVSH(int32_t value) \param [in] op2 Number of Bits to rotate \return Rotated value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) { + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } return (op1 >> op2) | (op1 << (32U - op2)); } @@ -858,11 +887,11 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint /** \brief Breakpoint \details Causes the processor to enter Debug state. - Debug tools can use this to investigate system state when the instruction at a particular address is reached. - \param [in] value is ignored by the processor. - If required, a debugger can use it to store additional information about the breakpoint. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. */ -#define __BKPT(value) __ASM volatile ("bkpt "#value) +#define __BKPT(value) __ASM volatile ("bkpt "#value) /** @@ -871,28 +900,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint \param [in] value Value to reverse \return Reversed value */ - /* ToDo: ARMCC_V6: check if __builtin_arm_rbit is supported */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) -{ - uint32_t result; - -#if ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=3 */ - __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); -#else - int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */ - - result = value; /* r will be reversed bits of v; first get LSB of v */ - for (value >>= 1U; value; value >>= 1U) - { - result <<= 1U; - result |= value & 1U; - s--; - } - result <<= s; /* shift when v's highest bits are zero */ -#endif - return(result); -} - +#define __RBIT __builtin_arm_rbit /** \brief Count leading zeros @@ -900,11 +908,13 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) \param [in] value Value to count the leading zeros \return number of leading zeros in value */ -#define __CLZ __builtin_clz - +#define __CLZ (uint8_t)__builtin_clz -#if ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=3 */ +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) /** \brief LDR Exclusive (8 bit) \details Executes a exclusive LDR instruction for 8 bit value. @@ -971,6 +981,15 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) */ #define __CLREX __builtin_arm_clrex +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) /** \brief Signed Saturate @@ -979,13 +998,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) \param [in] sat Bit position to saturate to (1..32) \return Saturated value */ -/*#define __SSAT __builtin_arm_ssat*/ -#define __SSAT(ARG1,ARG2) \ -({ \ - int32_t __RES, __ARG1 = (ARG1); \ - __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) +#define __SSAT __builtin_arm_ssat /** @@ -996,14 +1009,6 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) \return Saturated value */ #define __USAT __builtin_arm_usat -#if 0 -#define __USAT(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) -#endif /** @@ -1013,7 +1018,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) \param [in] value Value to rotate \return Rotated value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RRX(uint32_t value) +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) { uint32_t result; @@ -1028,12 +1033,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RRX(uint32_t value) \param [in] ptr Pointer to data \return value of type uint8_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDRBT(volatile uint8_t *ptr) +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint8_t) result); /* Add explicit type cast here */ + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); /* Add explicit type cast here */ } @@ -1043,12 +1048,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint8_t __LDRBT(volatile uint8_t \param [in] ptr Pointer to data \return value of type uint16_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDRHT(volatile uint16_t *ptr) +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint16_t) result); /* Add explicit type cast here */ + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); /* Add explicit type cast here */ } @@ -1058,12 +1063,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint16_t __LDRHT(volatile uint16_ \param [in] ptr Pointer to data \return value of type uint32_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDRT(volatile uint32_t *ptr) +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); - return(result); + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); } @@ -1073,9 +1078,9 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __LDRT(volatile uint32_t \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) { - __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); } @@ -1085,9 +1090,9 @@ __attribute__((always_inline)) __STATIC_INLINE void __STRBT(uint8_t value, volat \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) { - __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); } @@ -1097,28 +1102,83 @@ __attribute__((always_inline)) __STATIC_INLINE void __STRHT(uint16_t value, vola \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) { - __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; } -#endif /* ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) */ +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ -#if (__ARM_ARCH_8M__ == 1U) +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) /** \brief Load-Acquire (8 bit) \details Executes a LDAB instruction for 8 bit value. \param [in] ptr Pointer to data \return value of type uint8_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDAB(volatile uint8_t *ptr) +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint8_t) result); + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); } @@ -1128,12 +1188,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint8_t __LDAB(volatile uint8_t * \param [in] ptr Pointer to data \return value of type uint16_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDAH(volatile uint16_t *ptr) +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint16_t) result); + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); } @@ -1143,12 +1203,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint16_t __LDAH(volatile uint16_t \param [in] ptr Pointer to data \return value of type uint32_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDA(volatile uint32_t *ptr) +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) ); - return(result); + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); } @@ -1158,9 +1218,9 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __LDA(volatile uint32_t \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) { - __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); } @@ -1170,9 +1230,9 @@ __attribute__((always_inline)) __STATIC_INLINE void __STLB(uint8_t value, volati \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) { - __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); } @@ -1182,9 +1242,9 @@ __attribute__((always_inline)) __STATIC_INLINE void __STLH(uint16_t value, volat \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STL(uint32_t value, volatile uint32_t *ptr) +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) { - __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); } @@ -1247,7 +1307,8 @@ __attribute__((always_inline)) __STATIC_INLINE void __STL(uint32_t value, volati */ #define __STLEX (uint32_t)__builtin_arm_stlex -#endif /* (__ARM_ARCH_8M__ == 1U) */ +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ /*@}*/ /* end of group CMSIS_Core_InstructionInterface */ @@ -1258,9 +1319,9 @@ __attribute__((always_inline)) __STATIC_INLINE void __STL(uint32_t value, volati @{ */ -#if (__ARM_FEATURE_DSP == 1U) /* ToDo: ARMCC_V6: This should be ARCH >= ARMv7-M + SIMD */ +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1268,7 +1329,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SADD8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1276,7 +1337,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QADD8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1284,7 +1345,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHADD8(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1292,7 +1353,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UADD8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1300,7 +1361,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQADD8(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1309,7 +1370,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHADD8(uint32_t op1, u } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1317,7 +1378,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SSUB8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1325,7 +1386,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QSUB8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1333,7 +1394,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSUB8(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1341,7 +1402,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USUB8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1349,7 +1410,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSUB8(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1358,7 +1419,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSUB8(uint32_t op1, u } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1366,7 +1427,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SADD16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1374,7 +1435,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QADD16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1382,7 +1443,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHADD16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1390,7 +1451,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UADD16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1398,7 +1459,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQADD16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1406,7 +1467,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHADD16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1414,7 +1475,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SSUB16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1422,7 +1483,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QSUB16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1430,7 +1491,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSUB16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1438,7 +1499,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USUB16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1446,7 +1507,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSUB16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1454,7 +1515,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSUB16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1462,7 +1523,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SASX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1470,7 +1531,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QASX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1478,7 +1539,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHASX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1486,7 +1547,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UASX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1494,7 +1555,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQASX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1502,7 +1563,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHASX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1510,7 +1571,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SSAX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1518,7 +1579,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QSAX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1526,7 +1587,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSAX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1534,7 +1595,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USAX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1542,7 +1603,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSAX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1550,7 +1611,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSAX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1558,7 +1619,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USAD8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) { uint32_t result; @@ -1568,7 +1629,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, u #define __SSAT16(ARG1,ARG2) \ ({ \ - uint32_t __RES, __ARG1 = (ARG1); \ + int32_t __RES, __ARG1 = (ARG1); \ __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ __RES; \ }) @@ -1580,7 +1641,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, u __RES; \ }) -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UXTB16(uint32_t op1) +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) { uint32_t result; @@ -1588,7 +1649,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UXTB16(uint32_t op1) return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1596,7 +1657,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UXTAB16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SXTB16(uint32_t op1) +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) { uint32_t result; @@ -1604,7 +1665,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SXTB16(uint32_t op1) return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1612,7 +1673,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SXTAB16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) { uint32_t result; @@ -1620,7 +1681,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUAD (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) { uint32_t result; @@ -1628,7 +1689,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUADX (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) { uint32_t result; @@ -1636,7 +1697,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLAD (uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) { uint32_t result; @@ -1644,7 +1705,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLADX (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) { union llreg_u{ uint32_t w32[2]; @@ -1661,7 +1722,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLALD (uint32_t op1, return(llr.w64); } -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) { union llreg_u{ uint32_t w32[2]; @@ -1678,7 +1739,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLALDX (uint32_t op1, return(llr.w64); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) { uint32_t result; @@ -1686,7 +1747,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUSD (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) { uint32_t result; @@ -1694,7 +1755,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUSDX (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) { uint32_t result; @@ -1702,7 +1763,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLSD (uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) { uint32_t result; @@ -1710,7 +1771,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLSDX (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) { union llreg_u{ uint32_t w32[2]; @@ -1727,7 +1788,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLSLD (uint32_t op1, return(llr.w64); } -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) { union llreg_u{ uint32_t w32[2]; @@ -1744,7 +1805,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLSLDX (uint32_t op1, return(llr.w64); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) { uint32_t result; @@ -1752,7 +1813,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SEL (uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE int32_t __QADD( int32_t op1, int32_t op2) +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) { int32_t result; @@ -1760,7 +1821,7 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __QADD( int32_t op1, in return(result); } -__attribute__((always_inline)) __STATIC_INLINE int32_t __QSUB( int32_t op1, int32_t op2) +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) { int32_t result; @@ -1768,6 +1829,7 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __QSUB( int32_t op1, in return(result); } +#if 0 #define __PKHBT(ARG1,ARG2,ARG3) \ ({ \ uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ @@ -1784,17 +1846,24 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __QSUB( int32_t op1, in __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ __RES; \ }) +#endif + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) { - int32_t result; + int32_t result; - __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); - return(result); + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); } -#endif /* (__ARM_FEATURE_DSP == 1U) */ +#endif /* (__ARM_FEATURE_DSP == 1) */ /*@} end of group CMSIS_SIMD_intrinsics */ -#endif /* __CMSIS_ARMCC_V6_H */ +#endif /* __CMSIS_ARMCLANG_H */ diff --git a/Firmware/ThirdParty/CMSIS/Include/cmsis_compiler.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_compiler.h new file mode 100644 index 000000000..94212eb87 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_compiler.h @@ -0,0 +1,266 @@ +/**************************************************************************//** + * @file cmsis_compiler.h + * @brief CMSIS compiler generic header file + * @version V5.0.4 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#ifndef __CMSIS_COMPILER_H +#define __CMSIS_COMPILER_H + +#include + +/* + * Arm Compiler 4/5 + */ +#if defined ( __CC_ARM ) + #include "cmsis_armcc.h" + + +/* + * Arm Compiler 6 (armclang) + */ +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #include "cmsis_armclang.h" + + +/* + * GNU Compiler + */ +#elif defined ( __GNUC__ ) + #include "cmsis_gcc.h" + + +/* + * IAR Compiler + */ +#elif defined ( __ICCARM__ ) + #include + + +/* + * TI Arm Compiler + */ +#elif defined ( __TI_ARM__ ) + #include + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __attribute__((packed)) + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed)) + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed)) + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void*)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + + +/* + * TASKING Compiler + */ +#elif defined ( __TASKING__ ) + /* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __packed__ + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __packed__ + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __packed__ + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __packed__ T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __align(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + + +/* + * COSMIC Compiler + */ +#elif defined ( __CSMC__ ) + #include + + #ifndef __ASM + #define __ASM _asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + // NO RETURN is automatically detected hence no warning here + #define __NO_RETURN + #endif + #ifndef __USED + #warning No compiler specific solution for __USED. __USED is ignored. + #define __USED + #endif + #ifndef __WEAK + #define __WEAK __weak + #endif + #ifndef __PACKED + #define __PACKED @packed + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT @packed struct + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION @packed union + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + @packed struct T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #warning No compiler specific solution for __ALIGNED. __ALIGNED is ignored. + #define __ALIGNED(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + + +#else + #error Unknown compiler. +#endif + + +#endif /* __CMSIS_COMPILER_H */ + diff --git a/Firmware/ThirdParty/CMSIS/Include/cmsis_gcc.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_gcc.h new file mode 100644 index 000000000..2d9db15a5 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_gcc.h @@ -0,0 +1,2085 @@ +/**************************************************************************//** + * @file cmsis_gcc.h + * @brief CMSIS compiler GCC header file + * @version V5.0.4 + * @date 09. April 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#ifndef __CMSIS_GCC_H +#define __CMSIS_GCC_H + +/* ignore some GCC warnings */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +/* Fallback for __has_builtin */ +#ifndef __has_builtin + #define __has_builtin(x) (0) +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) :: "memory"); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) :: "memory"); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +__STATIC_FORCEINLINE uint32_t __get_FPSCR(void) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_get_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + return __builtin_arm_get_fpscr(); +#else + uint32_t result; + + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + return(result); +#endif +#else + return(0U); +#endif +} + + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_FORCEINLINE void __set_FPSCR(uint32_t fpscr) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_set_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + __builtin_arm_set_fpscr(fpscr); +#else + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc", "memory"); +#endif +#else + (void)fpscr; +#endif +} + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP() __ASM volatile ("nop") + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI() __ASM volatile ("wfi") + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE() __ASM volatile ("wfe") + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV() __ASM volatile ("sev") + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +__STATIC_FORCEINLINE void __ISB(void) +{ + __ASM volatile ("isb 0xF":::"memory"); +} + + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__STATIC_FORCEINLINE void __DSB(void) +{ + __ASM volatile ("dsb 0xF":::"memory"); +} + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__STATIC_FORCEINLINE void __DMB(void) +{ + __ASM volatile ("dmb 0xF":::"memory"); +} + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE int16_t __REVSH(int16_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (int16_t)__builtin_bswap16(value); +#else + int16_t result; + + __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); +#else + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ + + result = value; /* r will be reversed bits of v; first get LSB of v */ + for (value >>= 1U; value != 0U; value >>= 1U) + { + result <<= 1U; + result |= value & 1U; + s--; + } + result <<= s; /* shift when v's highest bits are zero */ +#endif + return result; +} + + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +#define __CLZ (uint8_t)__builtin_clz + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +__STATIC_FORCEINLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1,ARG2) \ +__extension__ \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1,ARG2) \ + __extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAEXB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexb %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAEXH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexh %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDAEX(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaex %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexb %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexh %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlex %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1,ARG2) \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +#define __USAT16(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +#if 0 +#define __PKHBT(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +#define __PKHTB(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + if (ARG3 == 0) \ + __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ + else \ + __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) +#endif + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#pragma GCC diagnostic pop + +#endif /* __CMSIS_GCC_H */ diff --git a/Firmware/ThirdParty/CMSIS/Include/cmsis_iccarm.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_iccarm.h new file mode 100644 index 000000000..11c4af0eb --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_iccarm.h @@ -0,0 +1,935 @@ +/**************************************************************************//** + * @file cmsis_iccarm.h + * @brief CMSIS compiler ICCARM (IAR Compiler for Arm) header file + * @version V5.0.7 + * @date 19. June 2018 + ******************************************************************************/ + +//------------------------------------------------------------------------------ +// +// Copyright (c) 2017-2018 IAR Systems +// +// 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. +// +//------------------------------------------------------------------------------ + + +#ifndef __CMSIS_ICCARM_H__ +#define __CMSIS_ICCARM_H__ + +#ifndef __ICCARM__ + #error This file should only be compiled by ICCARM +#endif + +#pragma system_include + +#define __IAR_FT _Pragma("inline=forced") __intrinsic + +#if (__VER__ >= 8000000) + #define __ICCARM_V8 1 +#else + #define __ICCARM_V8 0 +#endif + +#ifndef __ALIGNED + #if __ICCARM_V8 + #define __ALIGNED(x) __attribute__((aligned(x))) + #elif (__VER__ >= 7080000) + /* Needs IAR language extensions */ + #define __ALIGNED(x) __attribute__((aligned(x))) + #else + #warning No compiler specific solution for __ALIGNED.__ALIGNED is ignored. + #define __ALIGNED(x) + #endif +#endif + + +/* Define compiler macros for CPU architecture, used in CMSIS 5. + */ +#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__ || __ARM_ARCH_8M_BASE__ || __ARM_ARCH_8M_MAIN__ +/* Macros already defined */ +#else + #if defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #elif defined(__ARM8M_BASELINE__) + #define __ARM_ARCH_8M_BASE__ 1 + #elif defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M' + #if __ARM_ARCH == 6 + #define __ARM_ARCH_6M__ 1 + #elif __ARM_ARCH == 7 + #if __ARM_FEATURE_DSP + #define __ARM_ARCH_7EM__ 1 + #else + #define __ARM_ARCH_7M__ 1 + #endif + #endif /* __ARM_ARCH */ + #endif /* __ARM_ARCH_PROFILE == 'M' */ +#endif + +/* Alternativ core deduction for older ICCARM's */ +#if !defined(__ARM_ARCH_6M__) && !defined(__ARM_ARCH_7M__) && !defined(__ARM_ARCH_7EM__) && \ + !defined(__ARM_ARCH_8M_BASE__) && !defined(__ARM_ARCH_8M_MAIN__) + #if defined(__ARM6M__) && (__CORE__ == __ARM6M__) + #define __ARM_ARCH_6M__ 1 + #elif defined(__ARM7M__) && (__CORE__ == __ARM7M__) + #define __ARM_ARCH_7M__ 1 + #elif defined(__ARM7EM__) && (__CORE__ == __ARM7EM__) + #define __ARM_ARCH_7EM__ 1 + #elif defined(__ARM8M_BASELINE__) && (__CORE == __ARM8M_BASELINE__) + #define __ARM_ARCH_8M_BASE__ 1 + #elif defined(__ARM8M_MAINLINE__) && (__CORE == __ARM8M_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #elif defined(__ARM8EM_MAINLINE__) && (__CORE == __ARM8EM_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #else + #error "Unknown target." + #endif +#endif + + + +#if defined(__ARM_ARCH_6M__) && __ARM_ARCH_6M__==1 + #define __IAR_M0_FAMILY 1 +#elif defined(__ARM_ARCH_8M_BASE__) && __ARM_ARCH_8M_BASE__==1 + #define __IAR_M0_FAMILY 1 +#else + #define __IAR_M0_FAMILY 0 +#endif + + +#ifndef __ASM + #define __ASM __asm +#endif + +#ifndef __INLINE + #define __INLINE inline +#endif + +#ifndef __NO_RETURN + #if __ICCARM_V8 + #define __NO_RETURN __attribute__((__noreturn__)) + #else + #define __NO_RETURN _Pragma("object_attribute=__noreturn") + #endif +#endif + +#ifndef __PACKED + #if __ICCARM_V8 + #define __PACKED __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED __packed + #endif +#endif + +#ifndef __PACKED_STRUCT + #if __ICCARM_V8 + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED_STRUCT __packed struct + #endif +#endif + +#ifndef __PACKED_UNION + #if __ICCARM_V8 + #define __PACKED_UNION union __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED_UNION __packed union + #endif +#endif + +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif + +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif + +#ifndef __FORCEINLINE + #define __FORCEINLINE _Pragma("inline=forced") +#endif + +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __FORCEINLINE __STATIC_INLINE +#endif + +#ifndef __UNALIGNED_UINT16_READ +#pragma language=save +#pragma language=extended +__IAR_FT uint16_t __iar_uint16_read(void const *ptr) +{ + return *(__packed uint16_t*)(ptr); +} +#pragma language=restore +#define __UNALIGNED_UINT16_READ(PTR) __iar_uint16_read(PTR) +#endif + + +#ifndef __UNALIGNED_UINT16_WRITE +#pragma language=save +#pragma language=extended +__IAR_FT void __iar_uint16_write(void const *ptr, uint16_t val) +{ + *(__packed uint16_t*)(ptr) = val;; +} +#pragma language=restore +#define __UNALIGNED_UINT16_WRITE(PTR,VAL) __iar_uint16_write(PTR,VAL) +#endif + +#ifndef __UNALIGNED_UINT32_READ +#pragma language=save +#pragma language=extended +__IAR_FT uint32_t __iar_uint32_read(void const *ptr) +{ + return *(__packed uint32_t*)(ptr); +} +#pragma language=restore +#define __UNALIGNED_UINT32_READ(PTR) __iar_uint32_read(PTR) +#endif + +#ifndef __UNALIGNED_UINT32_WRITE +#pragma language=save +#pragma language=extended +__IAR_FT void __iar_uint32_write(void const *ptr, uint32_t val) +{ + *(__packed uint32_t*)(ptr) = val;; +} +#pragma language=restore +#define __UNALIGNED_UINT32_WRITE(PTR,VAL) __iar_uint32_write(PTR,VAL) +#endif + +#ifndef __UNALIGNED_UINT32 /* deprecated */ +#pragma language=save +#pragma language=extended +__packed struct __iar_u32 { uint32_t v; }; +#pragma language=restore +#define __UNALIGNED_UINT32(PTR) (((struct __iar_u32 *)(PTR))->v) +#endif + +#ifndef __USED + #if __ICCARM_V8 + #define __USED __attribute__((used)) + #else + #define __USED _Pragma("__root") + #endif +#endif + +#ifndef __WEAK + #if __ICCARM_V8 + #define __WEAK __attribute__((weak)) + #else + #define __WEAK _Pragma("__weak") + #endif +#endif + + +#ifndef __ICCARM_INTRINSICS_VERSION__ + #define __ICCARM_INTRINSICS_VERSION__ 0 +#endif + +#if __ICCARM_INTRINSICS_VERSION__ == 2 + + #if defined(__CLZ) + #undef __CLZ + #endif + #if defined(__REVSH) + #undef __REVSH + #endif + #if defined(__RBIT) + #undef __RBIT + #endif + #if defined(__SSAT) + #undef __SSAT + #endif + #if defined(__USAT) + #undef __USAT + #endif + + #include "iccarm_builtin.h" + + #define __disable_fault_irq __iar_builtin_disable_fiq + #define __disable_irq __iar_builtin_disable_interrupt + #define __enable_fault_irq __iar_builtin_enable_fiq + #define __enable_irq __iar_builtin_enable_interrupt + #define __arm_rsr __iar_builtin_rsr + #define __arm_wsr __iar_builtin_wsr + + + #define __get_APSR() (__arm_rsr("APSR")) + #define __get_BASEPRI() (__arm_rsr("BASEPRI")) + #define __get_CONTROL() (__arm_rsr("CONTROL")) + #define __get_FAULTMASK() (__arm_rsr("FAULTMASK")) + + #if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) + #define __get_FPSCR() (__arm_rsr("FPSCR")) + #define __set_FPSCR(VALUE) (__arm_wsr("FPSCR", (VALUE))) + #else + #define __get_FPSCR() ( 0 ) + #define __set_FPSCR(VALUE) ((void)VALUE) + #endif + + #define __get_IPSR() (__arm_rsr("IPSR")) + #define __get_MSP() (__arm_rsr("MSP")) + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + #define __get_MSPLIM() (0U) + #else + #define __get_MSPLIM() (__arm_rsr("MSPLIM")) + #endif + #define __get_PRIMASK() (__arm_rsr("PRIMASK")) + #define __get_PSP() (__arm_rsr("PSP")) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __get_PSPLIM() (0U) + #else + #define __get_PSPLIM() (__arm_rsr("PSPLIM")) + #endif + + #define __get_xPSR() (__arm_rsr("xPSR")) + + #define __set_BASEPRI(VALUE) (__arm_wsr("BASEPRI", (VALUE))) + #define __set_BASEPRI_MAX(VALUE) (__arm_wsr("BASEPRI_MAX", (VALUE))) + #define __set_CONTROL(VALUE) (__arm_wsr("CONTROL", (VALUE))) + #define __set_FAULTMASK(VALUE) (__arm_wsr("FAULTMASK", (VALUE))) + #define __set_MSP(VALUE) (__arm_wsr("MSP", (VALUE))) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + #define __set_MSPLIM(VALUE) ((void)(VALUE)) + #else + #define __set_MSPLIM(VALUE) (__arm_wsr("MSPLIM", (VALUE))) + #endif + #define __set_PRIMASK(VALUE) (__arm_wsr("PRIMASK", (VALUE))) + #define __set_PSP(VALUE) (__arm_wsr("PSP", (VALUE))) + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __set_PSPLIM(VALUE) ((void)(VALUE)) + #else + #define __set_PSPLIM(VALUE) (__arm_wsr("PSPLIM", (VALUE))) + #endif + + #define __TZ_get_CONTROL_NS() (__arm_rsr("CONTROL_NS")) + #define __TZ_set_CONTROL_NS(VALUE) (__arm_wsr("CONTROL_NS", (VALUE))) + #define __TZ_get_PSP_NS() (__arm_rsr("PSP_NS")) + #define __TZ_set_PSP_NS(VALUE) (__arm_wsr("PSP_NS", (VALUE))) + #define __TZ_get_MSP_NS() (__arm_rsr("MSP_NS")) + #define __TZ_set_MSP_NS(VALUE) (__arm_wsr("MSP_NS", (VALUE))) + #define __TZ_get_SP_NS() (__arm_rsr("SP_NS")) + #define __TZ_set_SP_NS(VALUE) (__arm_wsr("SP_NS", (VALUE))) + #define __TZ_get_PRIMASK_NS() (__arm_rsr("PRIMASK_NS")) + #define __TZ_set_PRIMASK_NS(VALUE) (__arm_wsr("PRIMASK_NS", (VALUE))) + #define __TZ_get_BASEPRI_NS() (__arm_rsr("BASEPRI_NS")) + #define __TZ_set_BASEPRI_NS(VALUE) (__arm_wsr("BASEPRI_NS", (VALUE))) + #define __TZ_get_FAULTMASK_NS() (__arm_rsr("FAULTMASK_NS")) + #define __TZ_set_FAULTMASK_NS(VALUE)(__arm_wsr("FAULTMASK_NS", (VALUE))) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __TZ_get_PSPLIM_NS() (0U) + #define __TZ_set_PSPLIM_NS(VALUE) ((void)(VALUE)) + #else + #define __TZ_get_PSPLIM_NS() (__arm_rsr("PSPLIM_NS")) + #define __TZ_set_PSPLIM_NS(VALUE) (__arm_wsr("PSPLIM_NS", (VALUE))) + #endif + + #define __TZ_get_MSPLIM_NS() (__arm_rsr("MSPLIM_NS")) + #define __TZ_set_MSPLIM_NS(VALUE) (__arm_wsr("MSPLIM_NS", (VALUE))) + + #define __NOP __iar_builtin_no_operation + + #define __CLZ __iar_builtin_CLZ + #define __CLREX __iar_builtin_CLREX + + #define __DMB __iar_builtin_DMB + #define __DSB __iar_builtin_DSB + #define __ISB __iar_builtin_ISB + + #define __LDREXB __iar_builtin_LDREXB + #define __LDREXH __iar_builtin_LDREXH + #define __LDREXW __iar_builtin_LDREX + + #define __RBIT __iar_builtin_RBIT + #define __REV __iar_builtin_REV + #define __REV16 __iar_builtin_REV16 + + __IAR_FT int16_t __REVSH(int16_t val) + { + return (int16_t) __iar_builtin_REVSH(val); + } + + #define __ROR __iar_builtin_ROR + #define __RRX __iar_builtin_RRX + + #define __SEV __iar_builtin_SEV + + #if !__IAR_M0_FAMILY + #define __SSAT __iar_builtin_SSAT + #endif + + #define __STREXB __iar_builtin_STREXB + #define __STREXH __iar_builtin_STREXH + #define __STREXW __iar_builtin_STREX + + #if !__IAR_M0_FAMILY + #define __USAT __iar_builtin_USAT + #endif + + #define __WFE __iar_builtin_WFE + #define __WFI __iar_builtin_WFI + + #if __ARM_MEDIA__ + #define __SADD8 __iar_builtin_SADD8 + #define __QADD8 __iar_builtin_QADD8 + #define __SHADD8 __iar_builtin_SHADD8 + #define __UADD8 __iar_builtin_UADD8 + #define __UQADD8 __iar_builtin_UQADD8 + #define __UHADD8 __iar_builtin_UHADD8 + #define __SSUB8 __iar_builtin_SSUB8 + #define __QSUB8 __iar_builtin_QSUB8 + #define __SHSUB8 __iar_builtin_SHSUB8 + #define __USUB8 __iar_builtin_USUB8 + #define __UQSUB8 __iar_builtin_UQSUB8 + #define __UHSUB8 __iar_builtin_UHSUB8 + #define __SADD16 __iar_builtin_SADD16 + #define __QADD16 __iar_builtin_QADD16 + #define __SHADD16 __iar_builtin_SHADD16 + #define __UADD16 __iar_builtin_UADD16 + #define __UQADD16 __iar_builtin_UQADD16 + #define __UHADD16 __iar_builtin_UHADD16 + #define __SSUB16 __iar_builtin_SSUB16 + #define __QSUB16 __iar_builtin_QSUB16 + #define __SHSUB16 __iar_builtin_SHSUB16 + #define __USUB16 __iar_builtin_USUB16 + #define __UQSUB16 __iar_builtin_UQSUB16 + #define __UHSUB16 __iar_builtin_UHSUB16 + #define __SASX __iar_builtin_SASX + #define __QASX __iar_builtin_QASX + #define __SHASX __iar_builtin_SHASX + #define __UASX __iar_builtin_UASX + #define __UQASX __iar_builtin_UQASX + #define __UHASX __iar_builtin_UHASX + #define __SSAX __iar_builtin_SSAX + #define __QSAX __iar_builtin_QSAX + #define __SHSAX __iar_builtin_SHSAX + #define __USAX __iar_builtin_USAX + #define __UQSAX __iar_builtin_UQSAX + #define __UHSAX __iar_builtin_UHSAX + #define __USAD8 __iar_builtin_USAD8 + #define __USADA8 __iar_builtin_USADA8 + #define __SSAT16 __iar_builtin_SSAT16 + #define __USAT16 __iar_builtin_USAT16 + #define __UXTB16 __iar_builtin_UXTB16 + #define __UXTAB16 __iar_builtin_UXTAB16 + #define __SXTB16 __iar_builtin_SXTB16 + #define __SXTAB16 __iar_builtin_SXTAB16 + #define __SMUAD __iar_builtin_SMUAD + #define __SMUADX __iar_builtin_SMUADX + #define __SMMLA __iar_builtin_SMMLA + #define __SMLAD __iar_builtin_SMLAD + #define __SMLADX __iar_builtin_SMLADX + #define __SMLALD __iar_builtin_SMLALD + #define __SMLALDX __iar_builtin_SMLALDX + #define __SMUSD __iar_builtin_SMUSD + #define __SMUSDX __iar_builtin_SMUSDX + #define __SMLSD __iar_builtin_SMLSD + #define __SMLSDX __iar_builtin_SMLSDX + #define __SMLSLD __iar_builtin_SMLSLD + #define __SMLSLDX __iar_builtin_SMLSLDX + #define __SEL __iar_builtin_SEL + #define __QADD __iar_builtin_QADD + #define __QSUB __iar_builtin_QSUB + #define __PKHBT __iar_builtin_PKHBT + #define __PKHTB __iar_builtin_PKHTB + #endif + +#else /* __ICCARM_INTRINSICS_VERSION__ == 2 */ + + #if __IAR_M0_FAMILY + /* Avoid clash between intrinsics.h and arm_math.h when compiling for Cortex-M0. */ + #define __CLZ __cmsis_iar_clz_not_active + #define __SSAT __cmsis_iar_ssat_not_active + #define __USAT __cmsis_iar_usat_not_active + #define __RBIT __cmsis_iar_rbit_not_active + #define __get_APSR __cmsis_iar_get_APSR_not_active + #endif + + + #if (!((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) )) + #define __get_FPSCR __cmsis_iar_get_FPSR_not_active + #define __set_FPSCR __cmsis_iar_set_FPSR_not_active + #endif + + #ifdef __INTRINSICS_INCLUDED + #error intrinsics.h is already included previously! + #endif + + #include + + #if __IAR_M0_FAMILY + /* Avoid clash between intrinsics.h and arm_math.h when compiling for Cortex-M0. */ + #undef __CLZ + #undef __SSAT + #undef __USAT + #undef __RBIT + #undef __get_APSR + + __STATIC_INLINE uint8_t __CLZ(uint32_t data) + { + if (data == 0U) { return 32U; } + + uint32_t count = 0U; + uint32_t mask = 0x80000000U; + + while ((data & mask) == 0U) + { + count += 1U; + mask = mask >> 1U; + } + return count; + } + + __STATIC_INLINE uint32_t __RBIT(uint32_t v) + { + uint8_t sc = 31U; + uint32_t r = v; + for (v >>= 1U; v; v >>= 1U) + { + r <<= 1U; + r |= v & 1U; + sc--; + } + return (r << sc); + } + + __STATIC_INLINE uint32_t __get_APSR(void) + { + uint32_t res; + __asm("MRS %0,APSR" : "=r" (res)); + return res; + } + + #endif + + #if (!((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) )) + #undef __get_FPSCR + #undef __set_FPSCR + #define __get_FPSCR() (0) + #define __set_FPSCR(VALUE) ((void)VALUE) + #endif + + #pragma diag_suppress=Pe940 + #pragma diag_suppress=Pe177 + + #define __enable_irq __enable_interrupt + #define __disable_irq __disable_interrupt + #define __NOP __no_operation + + #define __get_xPSR __get_PSR + + #if (!defined(__ARM_ARCH_6M__) || __ARM_ARCH_6M__==0) + + __IAR_FT uint32_t __LDREXW(uint32_t volatile *ptr) + { + return __LDREX((unsigned long *)ptr); + } + + __IAR_FT uint32_t __STREXW(uint32_t value, uint32_t volatile *ptr) + { + return __STREX(value, (unsigned long *)ptr); + } + #endif + + + /* __CORTEX_M is defined in core_cm0.h, core_cm3.h and core_cm4.h. */ + #if (__CORTEX_M >= 0x03) + + __IAR_FT uint32_t __RRX(uint32_t value) + { + uint32_t result; + __ASM("RRX %0, %1" : "=r"(result) : "r" (value) : "cc"); + return(result); + } + + __IAR_FT void __set_BASEPRI_MAX(uint32_t value) + { + __asm volatile("MSR BASEPRI_MAX,%0"::"r" (value)); + } + + + #define __enable_fault_irq __enable_fiq + #define __disable_fault_irq __disable_fiq + + + #endif /* (__CORTEX_M >= 0x03) */ + + __IAR_FT uint32_t __ROR(uint32_t op1, uint32_t op2) + { + return (op1 >> op2) | (op1 << ((sizeof(op1)*8)-op2)); + } + + #if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + + __IAR_FT uint32_t __get_MSPLIM(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,MSPLIM" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __set_MSPLIM(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR MSPLIM,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __get_PSPLIM(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,PSPLIM" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __set_PSPLIM(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR PSPLIM,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __TZ_get_CONTROL_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,CONTROL_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_CONTROL_NS(uint32_t value) + { + __asm volatile("MSR CONTROL_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_PSP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,PSP_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_PSP_NS(uint32_t value) + { + __asm volatile("MSR PSP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_MSP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,MSP_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_MSP_NS(uint32_t value) + { + __asm volatile("MSR MSP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_SP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,SP_NS" : "=r" (res)); + return res; + } + __IAR_FT void __TZ_set_SP_NS(uint32_t value) + { + __asm volatile("MSR SP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_PRIMASK_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,PRIMASK_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_PRIMASK_NS(uint32_t value) + { + __asm volatile("MSR PRIMASK_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_BASEPRI_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,BASEPRI_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_BASEPRI_NS(uint32_t value) + { + __asm volatile("MSR BASEPRI_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_FAULTMASK_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,FAULTMASK_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_FAULTMASK_NS(uint32_t value) + { + __asm volatile("MSR FAULTMASK_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_PSPLIM_NS(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,PSPLIM_NS" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __TZ_set_PSPLIM_NS(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR PSPLIM_NS,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __TZ_get_MSPLIM_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,MSPLIM_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_MSPLIM_NS(uint32_t value) + { + __asm volatile("MSR MSPLIM_NS,%0" :: "r" (value)); + } + + #endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */ + +#endif /* __ICCARM_INTRINSICS_VERSION__ == 2 */ + +#define __BKPT(value) __asm volatile ("BKPT %0" : : "i"(value)) + +#if __IAR_M0_FAMILY + __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat) + { + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; + } + + __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat) + { + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; + } +#endif + +#if (__CORTEX_M >= 0x03) /* __CORTEX_M is defined in core_cm0.h, core_cm3.h and core_cm4.h. */ + + __IAR_FT uint8_t __LDRBT(volatile uint8_t *addr) + { + uint32_t res; + __ASM("LDRBT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDRHT(volatile uint16_t *addr) + { + uint32_t res; + __ASM("LDRHT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDRT(volatile uint32_t *addr) + { + uint32_t res; + __ASM("LDRT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return res; + } + + __IAR_FT void __STRBT(uint8_t value, volatile uint8_t *addr) + { + __ASM("STRBT %1, [%0]" : : "r" (addr), "r" ((uint32_t)value) : "memory"); + } + + __IAR_FT void __STRHT(uint16_t value, volatile uint16_t *addr) + { + __ASM("STRHT %1, [%0]" : : "r" (addr), "r" ((uint32_t)value) : "memory"); + } + + __IAR_FT void __STRT(uint32_t value, volatile uint32_t *addr) + { + __ASM("STRT %1, [%0]" : : "r" (addr), "r" (value) : "memory"); + } + +#endif /* (__CORTEX_M >= 0x03) */ + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + + + __IAR_FT uint8_t __LDAB(volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAB %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDAH(volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAH %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDA(volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("LDA %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return res; + } + + __IAR_FT void __STLB(uint8_t value, volatile uint8_t *ptr) + { + __ASM volatile ("STLB %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT void __STLH(uint16_t value, volatile uint16_t *ptr) + { + __ASM volatile ("STLH %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT void __STL(uint32_t value, volatile uint32_t *ptr) + { + __ASM volatile ("STL %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT uint8_t __LDAEXB(volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEXB %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDAEXH(volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEXH %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDAEX(volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEX %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEXB %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEXH %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEX %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + +#endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */ + +#undef __IAR_FT +#undef __IAR_M0_FAMILY +#undef __ICCARM_V8 + +#pragma diag_default=Pe940 +#pragma diag_default=Pe177 + +#endif /* __CMSIS_ICCARM_H__ */ diff --git a/Firmware/ThirdParty/CMSIS/Include/cmsis_version.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_version.h new file mode 100644 index 000000000..660f612aa --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_version.h @@ -0,0 +1,39 @@ +/**************************************************************************//** + * @file cmsis_version.h + * @brief CMSIS Core(M) Version definitions + * @version V5.0.2 + * @date 19. April 2017 + ******************************************************************************/ +/* + * Copyright (c) 2009-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CMSIS_VERSION_H +#define __CMSIS_VERSION_H + +/* CMSIS Version definitions */ +#define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */ +#define __CM_CMSIS_VERSION_SUB ( 1U) /*!< [15:0] CMSIS Core(M) sub version */ +#define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \ + __CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */ +#endif diff --git a/Firmware/ThirdParty/CMSIS/Include/core_armv8mbl.h b/Firmware/ThirdParty/CMSIS/Include/core_armv8mbl.h new file mode 100644 index 000000000..251e4ede3 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/core_armv8mbl.h @@ -0,0 +1,1918 @@ +/**************************************************************************//** + * @file core_armv8mbl.h + * @brief CMSIS Armv8-M Baseline Core Peripheral Access Layer Header File + * @version V5.0.7 + * @date 22. June 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_ARMV8MBL_H_GENERIC +#define __CORE_ARMV8MBL_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_ARMv8MBL + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS definitions */ +#define __ARMv8MBL_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __ARMv8MBL_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __ARMv8MBL_CMSIS_VERSION ((__ARMv8MBL_CMSIS_VERSION_MAIN << 16U) | \ + __ARMv8MBL_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M ( 2U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + This core does not support an FPU at all +*/ +#define __FPU_USED 0U + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_PCS_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_ARMV8MBL_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_ARMV8MBL_H_DEPENDANT +#define __CORE_ARMV8MBL_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __ARMv8MBL_REV + #define __ARMv8MBL_REV 0x0000U + #warning "__ARMv8MBL_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __SAUREGION_PRESENT + #define __SAUREGION_PRESENT 0U + #warning "__SAUREGION_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __VTOR_PRESENT + #define __VTOR_PRESENT 0U + #warning "__VTOR_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 2U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif + + #ifndef __ETM_PRESENT + #define __ETM_PRESENT 0U + #warning "__ETM_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MTB_PRESENT + #define __MTB_PRESENT 0U + #warning "__MTB_PRESENT not defined in device header file; using default!" + #endif + +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group ARMv8MBL */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core SAU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:28; /*!< bit: 0..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t _reserved1:3; /*!< bit: 25..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack-pointer select */ + uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[16U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[16U]; + __IOM uint32_t ICER[16U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[16U]; + __IOM uint32_t ISPR[16U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[16U]; + __IOM uint32_t ICPR[16U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[16U]; + __IOM uint32_t IABR[16U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[16U]; + __IOM uint32_t ITNS[16U]; /*!< Offset: 0x280 (R/W) Interrupt Non-Secure State Register */ + uint32_t RESERVED5[16U]; + __IOM uint32_t IPR[124U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register */ +} NVIC_Type; + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ +#else + uint32_t RESERVED0; +#endif + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + uint32_t RESERVED1; + __IOM uint32_t SHPR[2U]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_PENDNMISET_Pos 31U /*!< SCB ICSR: PENDNMISET Position */ +#define SCB_ICSR_PENDNMISET_Msk (1UL << SCB_ICSR_PENDNMISET_Pos) /*!< SCB ICSR: PENDNMISET Mask */ + +#define SCB_ICSR_NMIPENDSET_Pos SCB_ICSR_PENDNMISET_Pos /*!< SCB ICSR: NMIPENDSET Position, backward compatibility */ +#define SCB_ICSR_NMIPENDSET_Msk SCB_ICSR_PENDNMISET_Msk /*!< SCB ICSR: NMIPENDSET Mask, backward compatibility */ + +#define SCB_ICSR_PENDNMICLR_Pos 30U /*!< SCB ICSR: PENDNMICLR Position */ +#define SCB_ICSR_PENDNMICLR_Msk (1UL << SCB_ICSR_PENDNMICLR_Pos) /*!< SCB ICSR: PENDNMICLR Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_STTNS_Pos 24U /*!< SCB ICSR: STTNS Position (Security Extension) */ +#define SCB_ICSR_STTNS_Msk (1UL << SCB_ICSR_STTNS_Pos) /*!< SCB ICSR: STTNS Mask (Security Extension) */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#endif + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIS_Pos 14U /*!< SCB AIRCR: PRIS Position */ +#define SCB_AIRCR_PRIS_Msk (1UL << SCB_AIRCR_PRIS_Pos) /*!< SCB AIRCR: PRIS Mask */ + +#define SCB_AIRCR_BFHFNMINS_Pos 13U /*!< SCB AIRCR: BFHFNMINS Position */ +#define SCB_AIRCR_BFHFNMINS_Msk (1UL << SCB_AIRCR_BFHFNMINS_Pos) /*!< SCB AIRCR: BFHFNMINS Mask */ + +#define SCB_AIRCR_SYSRESETREQS_Pos 3U /*!< SCB AIRCR: SYSRESETREQS Position */ +#define SCB_AIRCR_SYSRESETREQS_Msk (1UL << SCB_AIRCR_SYSRESETREQS_Pos) /*!< SCB AIRCR: SYSRESETREQS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEPS_Pos 3U /*!< SCB SCR: SLEEPDEEPS Position */ +#define SCB_SCR_SLEEPDEEPS_Msk (1UL << SCB_SCR_SLEEPDEEPS_Pos) /*!< SCB SCR: SLEEPDEEPS Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_BP_Pos 18U /*!< SCB CCR: BP Position */ +#define SCB_CCR_BP_Msk (1UL << SCB_CCR_BP_Pos) /*!< SCB CCR: BP Mask */ + +#define SCB_CCR_IC_Pos 17U /*!< SCB CCR: IC Position */ +#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos) /*!< SCB CCR: IC Mask */ + +#define SCB_CCR_DC_Pos 16U /*!< SCB CCR: DC Position */ +#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos) /*!< SCB CCR: DC Mask */ + +#define SCB_CCR_STKOFHFNMIGN_Pos 10U /*!< SCB CCR: STKOFHFNMIGN Position */ +#define SCB_CCR_STKOFHFNMIGN_Msk (1UL << SCB_CCR_STKOFHFNMIGN_Pos) /*!< SCB CCR: STKOFHFNMIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_HARDFAULTPENDED_Pos 21U /*!< SCB SHCSR: HARDFAULTPENDED Position */ +#define SCB_SHCSR_HARDFAULTPENDED_Msk (1UL << SCB_SHCSR_HARDFAULTPENDED_Pos) /*!< SCB SHCSR: HARDFAULTPENDED Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_NMIACT_Pos 5U /*!< SCB SHCSR: NMIACT Position */ +#define SCB_SHCSR_NMIACT_Msk (1UL << SCB_SHCSR_NMIACT_Pos) /*!< SCB SHCSR: NMIACT Mask */ + +#define SCB_SHCSR_HARDFAULTACT_Pos 2U /*!< SCB SHCSR: HARDFAULTACT Position */ +#define SCB_SHCSR_HARDFAULTACT_Msk (1UL << SCB_SHCSR_HARDFAULTACT_Pos) /*!< SCB SHCSR: HARDFAULTACT Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + uint32_t RESERVED0[6U]; + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + uint32_t RESERVED3[1U]; + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED4[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + uint32_t RESERVED5[1U]; + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED6[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + uint32_t RESERVED7[1U]; + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ + uint32_t RESERVED8[1U]; + __IOM uint32_t COMP4; /*!< Offset: 0x060 (R/W) Comparator Register 4 */ + uint32_t RESERVED9[1U]; + __IOM uint32_t FUNCTION4; /*!< Offset: 0x068 (R/W) Function Register 4 */ + uint32_t RESERVED10[1U]; + __IOM uint32_t COMP5; /*!< Offset: 0x070 (R/W) Comparator Register 5 */ + uint32_t RESERVED11[1U]; + __IOM uint32_t FUNCTION5; /*!< Offset: 0x078 (R/W) Function Register 5 */ + uint32_t RESERVED12[1U]; + __IOM uint32_t COMP6; /*!< Offset: 0x080 (R/W) Comparator Register 6 */ + uint32_t RESERVED13[1U]; + __IOM uint32_t FUNCTION6; /*!< Offset: 0x088 (R/W) Function Register 6 */ + uint32_t RESERVED14[1U]; + __IOM uint32_t COMP7; /*!< Offset: 0x090 (R/W) Comparator Register 7 */ + uint32_t RESERVED15[1U]; + __IOM uint32_t FUNCTION7; /*!< Offset: 0x098 (R/W) Function Register 7 */ + uint32_t RESERVED16[1U]; + __IOM uint32_t COMP8; /*!< Offset: 0x0A0 (R/W) Comparator Register 8 */ + uint32_t RESERVED17[1U]; + __IOM uint32_t FUNCTION8; /*!< Offset: 0x0A8 (R/W) Function Register 8 */ + uint32_t RESERVED18[1U]; + __IOM uint32_t COMP9; /*!< Offset: 0x0B0 (R/W) Comparator Register 9 */ + uint32_t RESERVED19[1U]; + __IOM uint32_t FUNCTION9; /*!< Offset: 0x0B8 (R/W) Function Register 9 */ + uint32_t RESERVED20[1U]; + __IOM uint32_t COMP10; /*!< Offset: 0x0C0 (R/W) Comparator Register 10 */ + uint32_t RESERVED21[1U]; + __IOM uint32_t FUNCTION10; /*!< Offset: 0x0C8 (R/W) Function Register 10 */ + uint32_t RESERVED22[1U]; + __IOM uint32_t COMP11; /*!< Offset: 0x0D0 (R/W) Comparator Register 11 */ + uint32_t RESERVED23[1U]; + __IOM uint32_t FUNCTION11; /*!< Offset: 0x0D8 (R/W) Function Register 11 */ + uint32_t RESERVED24[1U]; + __IOM uint32_t COMP12; /*!< Offset: 0x0E0 (R/W) Comparator Register 12 */ + uint32_t RESERVED25[1U]; + __IOM uint32_t FUNCTION12; /*!< Offset: 0x0E8 (R/W) Function Register 12 */ + uint32_t RESERVED26[1U]; + __IOM uint32_t COMP13; /*!< Offset: 0x0F0 (R/W) Comparator Register 13 */ + uint32_t RESERVED27[1U]; + __IOM uint32_t FUNCTION13; /*!< Offset: 0x0F8 (R/W) Function Register 13 */ + uint32_t RESERVED28[1U]; + __IOM uint32_t COMP14; /*!< Offset: 0x100 (R/W) Comparator Register 14 */ + uint32_t RESERVED29[1U]; + __IOM uint32_t FUNCTION14; /*!< Offset: 0x108 (R/W) Function Register 14 */ + uint32_t RESERVED30[1U]; + __IOM uint32_t COMP15; /*!< Offset: 0x110 (R/W) Comparator Register 15 */ + uint32_t RESERVED31[1U]; + __IOM uint32_t FUNCTION15; /*!< Offset: 0x118 (R/W) Function Register 15 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_ID_Pos 27U /*!< DWT FUNCTION: ID Position */ +#define DWT_FUNCTION_ID_Msk (0x1FUL << DWT_FUNCTION_ID_Pos) /*!< DWT FUNCTION: ID Mask */ + +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_ACTION_Pos 4U /*!< DWT FUNCTION: ACTION Position */ +#define DWT_FUNCTION_ACTION_Msk (0x3UL << DWT_FUNCTION_ACTION_Pos) /*!< DWT FUNCTION: ACTION Mask */ + +#define DWT_FUNCTION_MATCH_Pos 0U /*!< DWT FUNCTION: MATCH Position */ +#define DWT_FUNCTION_MATCH_Msk (0xFUL /*<< DWT_FUNCTION_MATCH_Pos*/) /*!< DWT FUNCTION: MATCH Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Sizes Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Sizes Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IOM uint32_t PSCR; /*!< Offset: 0x308 (R/W) Periodic Synchronization Control Register */ + uint32_t RESERVED3[809U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) Software Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) Software Lock Status Register */ + uint32_t RESERVED4[4U]; + __IM uint32_t TYPE; /*!< Offset: 0xFC8 (R/ ) Device Identifier Register */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) Device Type Register */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_SWOSCALER_Pos 0U /*!< TPI ACPR: SWOSCALER Position */ +#define TPI_ACPR_SWOSCALER_Msk (0xFFFFUL /*<< TPI_ACPR_SWOSCALER_Pos*/) /*!< TPI ACPR: SWOSCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_FOnMan_Pos 6U /*!< TPI FFCR: FOnMan Position */ +#define TPI_FFCR_FOnMan_Msk (0x1UL << TPI_FFCR_FOnMan_Pos) /*!< TPI FFCR: FOnMan Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI Periodic Synchronization Control Register Definitions */ +#define TPI_PSCR_PSCount_Pos 0U /*!< TPI PSCR: PSCount Position */ +#define TPI_PSCR_PSCount_Msk (0x1FUL /*<< TPI_PSCR_PSCount_Pos*/) /*!< TPI PSCR: TPSCount Mask */ + +/* TPI Software Lock Status Register Definitions */ +#define TPI_LSR_nTT_Pos 1U /*!< TPI LSR: Not thirty-two bit. Position */ +#define TPI_LSR_nTT_Msk (0x1UL << TPI_LSR_nTT_Pos) /*!< TPI LSR: Not thirty-two bit. Mask */ + +#define TPI_LSR_SLK_Pos 1U /*!< TPI LSR: Software Lock status Position */ +#define TPI_LSR_SLK_Msk (0x1UL << TPI_LSR_SLK_Pos) /*!< TPI LSR: Software Lock status Mask */ + +#define TPI_LSR_SLI_Pos 0U /*!< TPI LSR: Software Lock implemented Position */ +#define TPI_LSR_SLI_Msk (0x1UL /*<< TPI_LSR_SLI_Pos*/) /*!< TPI LSR: Software Lock implemented Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_FIFOSZ_Pos 6U /*!< TPI DEVID: FIFO depth Position */ +#define TPI_DEVID_FIFOSZ_Msk (0x7UL << TPI_DEVID_FIFOSZ_Pos) /*!< TPI DEVID: FIFO depth Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) MPU Region Limit Address Register */ + uint32_t RESERVED0[7U]; + union { + __IOM uint32_t MAIR[2]; + struct { + __IOM uint32_t MAIR0; /*!< Offset: 0x030 (R/W) MPU Memory Attribute Indirection Register 0 */ + __IOM uint32_t MAIR1; /*!< Offset: 0x034 (R/W) MPU Memory Attribute Indirection Register 1 */ + }; + }; +} MPU_Type; + +#define MPU_TYPE_RALIASES 1U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_BASE_Pos 5U /*!< MPU RBAR: BASE Position */ +#define MPU_RBAR_BASE_Msk (0x7FFFFFFUL << MPU_RBAR_BASE_Pos) /*!< MPU RBAR: BASE Mask */ + +#define MPU_RBAR_SH_Pos 3U /*!< MPU RBAR: SH Position */ +#define MPU_RBAR_SH_Msk (0x3UL << MPU_RBAR_SH_Pos) /*!< MPU RBAR: SH Mask */ + +#define MPU_RBAR_AP_Pos 1U /*!< MPU RBAR: AP Position */ +#define MPU_RBAR_AP_Msk (0x3UL << MPU_RBAR_AP_Pos) /*!< MPU RBAR: AP Mask */ + +#define MPU_RBAR_XN_Pos 0U /*!< MPU RBAR: XN Position */ +#define MPU_RBAR_XN_Msk (01UL /*<< MPU_RBAR_XN_Pos*/) /*!< MPU RBAR: XN Mask */ + +/* MPU Region Limit Address Register Definitions */ +#define MPU_RLAR_LIMIT_Pos 5U /*!< MPU RLAR: LIMIT Position */ +#define MPU_RLAR_LIMIT_Msk (0x7FFFFFFUL << MPU_RLAR_LIMIT_Pos) /*!< MPU RLAR: LIMIT Mask */ + +#define MPU_RLAR_AttrIndx_Pos 1U /*!< MPU RLAR: AttrIndx Position */ +#define MPU_RLAR_AttrIndx_Msk (0x7UL << MPU_RLAR_AttrIndx_Pos) /*!< MPU RLAR: AttrIndx Mask */ + +#define MPU_RLAR_EN_Pos 0U /*!< MPU RLAR: EN Position */ +#define MPU_RLAR_EN_Msk (1UL /*<< MPU_RLAR_EN_Pos*/) /*!< MPU RLAR: EN Mask */ + +/* MPU Memory Attribute Indirection Register 0 Definitions */ +#define MPU_MAIR0_Attr3_Pos 24U /*!< MPU MAIR0: Attr3 Position */ +#define MPU_MAIR0_Attr3_Msk (0xFFUL << MPU_MAIR0_Attr3_Pos) /*!< MPU MAIR0: Attr3 Mask */ + +#define MPU_MAIR0_Attr2_Pos 16U /*!< MPU MAIR0: Attr2 Position */ +#define MPU_MAIR0_Attr2_Msk (0xFFUL << MPU_MAIR0_Attr2_Pos) /*!< MPU MAIR0: Attr2 Mask */ + +#define MPU_MAIR0_Attr1_Pos 8U /*!< MPU MAIR0: Attr1 Position */ +#define MPU_MAIR0_Attr1_Msk (0xFFUL << MPU_MAIR0_Attr1_Pos) /*!< MPU MAIR0: Attr1 Mask */ + +#define MPU_MAIR0_Attr0_Pos 0U /*!< MPU MAIR0: Attr0 Position */ +#define MPU_MAIR0_Attr0_Msk (0xFFUL /*<< MPU_MAIR0_Attr0_Pos*/) /*!< MPU MAIR0: Attr0 Mask */ + +/* MPU Memory Attribute Indirection Register 1 Definitions */ +#define MPU_MAIR1_Attr7_Pos 24U /*!< MPU MAIR1: Attr7 Position */ +#define MPU_MAIR1_Attr7_Msk (0xFFUL << MPU_MAIR1_Attr7_Pos) /*!< MPU MAIR1: Attr7 Mask */ + +#define MPU_MAIR1_Attr6_Pos 16U /*!< MPU MAIR1: Attr6 Position */ +#define MPU_MAIR1_Attr6_Msk (0xFFUL << MPU_MAIR1_Attr6_Pos) /*!< MPU MAIR1: Attr6 Mask */ + +#define MPU_MAIR1_Attr5_Pos 8U /*!< MPU MAIR1: Attr5 Position */ +#define MPU_MAIR1_Attr5_Msk (0xFFUL << MPU_MAIR1_Attr5_Pos) /*!< MPU MAIR1: Attr5 Mask */ + +#define MPU_MAIR1_Attr4_Pos 0U /*!< MPU MAIR1: Attr4 Position */ +#define MPU_MAIR1_Attr4_Msk (0xFFUL /*<< MPU_MAIR1_Attr4_Pos*/) /*!< MPU MAIR1: Attr4 Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SAU Security Attribution Unit (SAU) + \brief Type definitions for the Security Attribution Unit (SAU) + @{ + */ + +/** + \brief Structure type to access the Security Attribution Unit (SAU). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SAU Control Register */ + __IM uint32_t TYPE; /*!< Offset: 0x004 (R/ ) SAU Type Register */ +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) SAU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) SAU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) SAU Region Limit Address Register */ +#endif +} SAU_Type; + +/* SAU Control Register Definitions */ +#define SAU_CTRL_ALLNS_Pos 1U /*!< SAU CTRL: ALLNS Position */ +#define SAU_CTRL_ALLNS_Msk (1UL << SAU_CTRL_ALLNS_Pos) /*!< SAU CTRL: ALLNS Mask */ + +#define SAU_CTRL_ENABLE_Pos 0U /*!< SAU CTRL: ENABLE Position */ +#define SAU_CTRL_ENABLE_Msk (1UL /*<< SAU_CTRL_ENABLE_Pos*/) /*!< SAU CTRL: ENABLE Mask */ + +/* SAU Type Register Definitions */ +#define SAU_TYPE_SREGION_Pos 0U /*!< SAU TYPE: SREGION Position */ +#define SAU_TYPE_SREGION_Msk (0xFFUL /*<< SAU_TYPE_SREGION_Pos*/) /*!< SAU TYPE: SREGION Mask */ + +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) +/* SAU Region Number Register Definitions */ +#define SAU_RNR_REGION_Pos 0U /*!< SAU RNR: REGION Position */ +#define SAU_RNR_REGION_Msk (0xFFUL /*<< SAU_RNR_REGION_Pos*/) /*!< SAU RNR: REGION Mask */ + +/* SAU Region Base Address Register Definitions */ +#define SAU_RBAR_BADDR_Pos 5U /*!< SAU RBAR: BADDR Position */ +#define SAU_RBAR_BADDR_Msk (0x7FFFFFFUL << SAU_RBAR_BADDR_Pos) /*!< SAU RBAR: BADDR Mask */ + +/* SAU Region Limit Address Register Definitions */ +#define SAU_RLAR_LADDR_Pos 5U /*!< SAU RLAR: LADDR Position */ +#define SAU_RLAR_LADDR_Msk (0x7FFFFFFUL << SAU_RLAR_LADDR_Pos) /*!< SAU RLAR: LADDR Mask */ + +#define SAU_RLAR_NSC_Pos 1U /*!< SAU RLAR: NSC Position */ +#define SAU_RLAR_NSC_Msk (1UL << SAU_RLAR_NSC_Pos) /*!< SAU RLAR: NSC Mask */ + +#define SAU_RLAR_ENABLE_Pos 0U /*!< SAU RLAR: ENABLE Position */ +#define SAU_RLAR_ENABLE_Msk (1UL /*<< SAU_RLAR_ENABLE_Pos*/) /*!< SAU RLAR: ENABLE Mask */ + +#endif /* defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) */ + +/*@} end of group CMSIS_SAU */ +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ + uint32_t RESERVED4[1U]; + __IOM uint32_t DAUTHCTRL; /*!< Offset: 0x014 (R/W) Debug Authentication Control Register */ + __IOM uint32_t DSCSR; /*!< Offset: 0x018 (R/W) Debug Security Control and Status Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESTART_ST_Pos 26U /*!< CoreDebug DHCSR: S_RESTART_ST Position */ +#define CoreDebug_DHCSR_S_RESTART_ST_Msk (1UL << CoreDebug_DHCSR_S_RESTART_ST_Pos) /*!< CoreDebug DHCSR: S_RESTART_ST Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register */ +#define CoreDebug_DEMCR_DWTENA_Pos 24U /*!< CoreDebug DEMCR: DWTENA Position */ +#define CoreDebug_DEMCR_DWTENA_Msk (1UL << CoreDebug_DEMCR_DWTENA_Pos) /*!< CoreDebug DEMCR: DWTENA Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/* Debug Authentication Control Register Definitions */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos 3U /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Position */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Mask */ + +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos 2U /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Msk (1UL << CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos) /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Mask */ + +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Pos 1U /*!< CoreDebug DAUTHCTRL: INTSPIDEN Position */ +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPIDEN Mask */ + +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Pos 0U /*!< CoreDebug DAUTHCTRL: SPIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Msk (1UL /*<< CoreDebug_DAUTHCTRL_SPIDENSEL_Pos*/) /*!< CoreDebug DAUTHCTRL: SPIDENSEL Mask */ + +/* Debug Security Control and Status Register Definitions */ +#define CoreDebug_DSCSR_CDS_Pos 16U /*!< CoreDebug DSCSR: CDS Position */ +#define CoreDebug_DSCSR_CDS_Msk (1UL << CoreDebug_DSCSR_CDS_Pos) /*!< CoreDebug DSCSR: CDS Mask */ + +#define CoreDebug_DSCSR_SBRSEL_Pos 1U /*!< CoreDebug DSCSR: SBRSEL Position */ +#define CoreDebug_DSCSR_SBRSEL_Msk (1UL << CoreDebug_DSCSR_SBRSEL_Pos) /*!< CoreDebug DSCSR: SBRSEL Mask */ + +#define CoreDebug_DSCSR_SBRSELEN_Pos 0U /*!< CoreDebug DSCSR: SBRSELEN Position */ +#define CoreDebug_DSCSR_SBRSELEN_Msk (1UL /*<< CoreDebug_DSCSR_SBRSELEN_Pos*/) /*!< CoreDebug DSCSR: SBRSELEN Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ + #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ + #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ + #define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ + #define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ + #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ + #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ + #define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + + + #define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ + #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ + #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + #define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ + #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ + #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE ) /*!< Core Debug configuration struct */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ + #endif + + #if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SAU_BASE (SCS_BASE + 0x0DD0UL) /*!< Security Attribution Unit */ + #define SAU ((SAU_Type *) SAU_BASE ) /*!< Security Attribution Unit */ + #endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SCS_BASE_NS (0xE002E000UL) /*!< System Control Space Base Address (non-secure address space) */ + #define CoreDebug_BASE_NS (0xE002EDF0UL) /*!< Core Debug Base Address (non-secure address space) */ + #define SysTick_BASE_NS (SCS_BASE_NS + 0x0010UL) /*!< SysTick Base Address (non-secure address space) */ + #define NVIC_BASE_NS (SCS_BASE_NS + 0x0100UL) /*!< NVIC Base Address (non-secure address space) */ + #define SCB_BASE_NS (SCS_BASE_NS + 0x0D00UL) /*!< System Control Block Base Address (non-secure address space) */ + + #define SCB_NS ((SCB_Type *) SCB_BASE_NS ) /*!< SCB configuration struct (non-secure address space) */ + #define SysTick_NS ((SysTick_Type *) SysTick_BASE_NS ) /*!< SysTick configuration struct (non-secure address space) */ + #define NVIC_NS ((NVIC_Type *) NVIC_BASE_NS ) /*!< NVIC configuration struct (non-secure address space) */ + #define CoreDebug_NS ((CoreDebug_Type *) CoreDebug_BASE_NS) /*!< Core Debug configuration struct (non-secure address space) */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE_NS (SCS_BASE_NS + 0x0D90UL) /*!< Memory Protection Unit (non-secure address space) */ + #define MPU_NS ((MPU_Type *) MPU_BASE_NS ) /*!< Memory Protection Unit (non-secure address space) */ + #endif + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* Special LR values for Secure/Non-Secure call handling and exception handling */ + +/* Function Return Payload (from ARMv8-M Architecture Reference Manual) LR value on entry from Secure BLXNS */ +#define FNC_RETURN (0xFEFFFFFFUL) /* bit [0] ignored when processing a branch */ + +/* The following EXC_RETURN mask values are used to evaluate the LR on exception entry */ +#define EXC_RETURN_PREFIX (0xFF000000UL) /* bits [31:24] set to indicate an EXC_RETURN value */ +#define EXC_RETURN_S (0x00000040UL) /* bit [6] stack used to push registers: 0=Non-secure 1=Secure */ +#define EXC_RETURN_DCRS (0x00000020UL) /* bit [5] stacking rules for called registers: 0=skipped 1=saved */ +#define EXC_RETURN_FTYPE (0x00000010UL) /* bit [4] allocate stack for floating-point context: 0=done 1=skipped */ +#define EXC_RETURN_MODE (0x00000008UL) /* bit [3] processor mode for return: 0=Handler mode 1=Thread mode */ +#define EXC_RETURN_SPSEL (0x00000002UL) /* bit [1] stack pointer used to restore context: 0=MSP 1=PSP */ +#define EXC_RETURN_ES (0x00000001UL) /* bit [0] security state exception was taken to: 0=Non-secure 1=Secure */ + +/* Integrity Signature (from ARMv8-M Architecture Reference Manual) for exception context stacking */ +#if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) /* Value for processors with floating-point extension: */ +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125AUL) /* bit [0] SFTC must match LR bit[4] EXC_RETURN_FTYPE */ +#else +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125BUL) /* Value for processors without floating-point extension */ +#endif + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ +/* The following MACROS handle generation of the register offset and byte masks */ +#define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) +#define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) +#define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) + +#define __NVIC_SetPriorityGrouping(X) (void)(X) +#define __NVIC_GetPriorityGrouping() (0U) + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Get Interrupt Target State + \details Reads the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + \return 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_GetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Target State + \details Sets the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_SetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] |= ((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Clear Interrupt Target State + \details Clears the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_ClearTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] &= ~((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IPR[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IPR[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } + else + { + SCB->SHPR[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHPR[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IPR[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return((uint32_t)(((SCB->SHPR[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + If VTOR is not present address 0 must be mapped to SRAM. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + SCB_AIRCR_SYSRESETREQ_Msk); + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Enable Interrupt (non-secure) + \details Enables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_EnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status (non-secure) + \details Returns a device specific interrupt enable status from the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetEnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt (non-secure) + \details Disables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_DisableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Pending Interrupt (non-secure) + \details Reads the NVIC pending register in the non-secure NVIC when in secure state and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt (non-secure) + \details Sets the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_SetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt (non-secure) + \details Clears the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_ClearPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt (non-secure) + \details Reads the active register in non-secure NVIC when in secure state and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetActive_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority (non-secure) + \details Sets the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every non-secure processor exception. + */ +__STATIC_INLINE void TZ_NVIC_SetPriority_NS(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->IPR[_IP_IDX(IRQn)] = ((uint32_t)(NVIC_NS->IPR[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } + else + { + SCB_NS->SHPR[_SHP_IDX(IRQn)] = ((uint32_t)(SCB_NS->SHPR[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } +} + + +/** + \brief Get Interrupt Priority (non-secure) + \details Reads the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriority_NS(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IPR[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return((uint32_t)(((SCB_NS->SHPR[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) &&(__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_NVICFunctions */ + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv8.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ########################## SAU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SAUFunctions SAU Functions + \brief Functions that configure the SAU. + @{ + */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + +/** + \brief Enable SAU + \details Enables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Enable(void) +{ + SAU->CTRL |= (SAU_CTRL_ENABLE_Msk); +} + + + +/** + \brief Disable SAU + \details Disables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Disable(void) +{ + SAU->CTRL &= ~(SAU_CTRL_ENABLE_Msk); +} + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_SAUFunctions */ + + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief System Tick Configuration (non-secure) + \details Initializes the non-secure System Timer and its interrupt when in secure state, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function TZ_SysTick_Config_NS is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t TZ_SysTick_Config_NS(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick_NS->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + TZ_NVIC_SetPriority_NS (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick_NS->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick_NS->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_ARMV8MBL_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/Firmware/ThirdParty/CMSIS/Include/core_armv8mml.h b/Firmware/ThirdParty/CMSIS/Include/core_armv8mml.h new file mode 100644 index 000000000..3a3148ea3 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/core_armv8mml.h @@ -0,0 +1,2927 @@ +/**************************************************************************//** + * @file core_armv8mml.h + * @brief CMSIS Armv8-M Mainline Core Peripheral Access Layer Header File + * @version V5.0.7 + * @date 06. July 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_ARMV8MML_H_GENERIC +#define __CORE_ARMV8MML_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_ARMv8MML + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS Armv8MML definitions */ +#define __ARMv8MML_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __ARMv8MML_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __ARMv8MML_CMSIS_VERSION ((__ARMv8MML_CMSIS_VERSION_MAIN << 16U) | \ + __ARMv8MML_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (81U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined(__ARM_FEATURE_DSP) + #if defined(__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_PCS_VFP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined(__ARM_FEATURE_DSP) + #if defined(__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined(__ARM_FEATURE_DSP) + #if defined(__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined(__ARM_FEATURE_DSP) + #if defined(__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_ARMV8MML_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_ARMV8MML_H_DEPENDANT +#define __CORE_ARMV8MML_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __ARMv8MML_REV + #define __ARMv8MML_REV 0x0000U + #warning "__ARMv8MML_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __SAUREGION_PRESENT + #define __SAUREGION_PRESENT 0U + #warning "__SAUREGION_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __DSP_PRESENT + #define __DSP_PRESENT 0U + #warning "__DSP_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 3U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group ARMv8MML */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core SAU Register + - Core FPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + +#define APSR_GE_Pos 16U /*!< APSR: GE Position */ +#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ +#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ +#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack-pointer select */ + uint32_t FPCA:1; /*!< bit: 2 Floating-point context active */ + uint32_t SFPA:1; /*!< bit: 3 Secure floating-point active */ + uint32_t _reserved1:28; /*!< bit: 4..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SFPA_Pos 3U /*!< CONTROL: SFPA Position */ +#define CONTROL_SFPA_Msk (1UL << CONTROL_SFPA_Pos) /*!< CONTROL: SFPA Mask */ + +#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ +#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ + +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[16U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[16U]; + __IOM uint32_t ICER[16U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[16U]; + __IOM uint32_t ISPR[16U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[16U]; + __IOM uint32_t ICPR[16U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[16U]; + __IOM uint32_t IABR[16U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[16U]; + __IOM uint32_t ITNS[16U]; /*!< Offset: 0x280 (R/W) Interrupt Non-Secure State Register */ + uint32_t RESERVED5[16U]; + __IOM uint8_t IPR[496U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED6[580U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHPR[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t ID_PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t ID_DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ID_ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t ID_MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ID_ISAR[6U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + __IM uint32_t CLIDR; /*!< Offset: 0x078 (R/ ) Cache Level ID register */ + __IM uint32_t CTR; /*!< Offset: 0x07C (R/ ) Cache Type register */ + __IM uint32_t CCSIDR; /*!< Offset: 0x080 (R/ ) Cache Size ID Register */ + __IOM uint32_t CSSELR; /*!< Offset: 0x084 (R/W) Cache Size Selection Register */ + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ + __IOM uint32_t NSACR; /*!< Offset: 0x08C (R/W) Non-Secure Access Control Register */ + uint32_t RESERVED3[92U]; + __OM uint32_t STIR; /*!< Offset: 0x200 ( /W) Software Triggered Interrupt Register */ + uint32_t RESERVED4[15U]; + __IM uint32_t MVFR0; /*!< Offset: 0x240 (R/ ) Media and VFP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x244 (R/ ) Media and VFP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x248 (R/ ) Media and VFP Feature Register 2 */ + uint32_t RESERVED5[1U]; + __OM uint32_t ICIALLU; /*!< Offset: 0x250 ( /W) I-Cache Invalidate All to PoU */ + uint32_t RESERVED6[1U]; + __OM uint32_t ICIMVAU; /*!< Offset: 0x258 ( /W) I-Cache Invalidate by MVA to PoU */ + __OM uint32_t DCIMVAC; /*!< Offset: 0x25C ( /W) D-Cache Invalidate by MVA to PoC */ + __OM uint32_t DCISW; /*!< Offset: 0x260 ( /W) D-Cache Invalidate by Set-way */ + __OM uint32_t DCCMVAU; /*!< Offset: 0x264 ( /W) D-Cache Clean by MVA to PoU */ + __OM uint32_t DCCMVAC; /*!< Offset: 0x268 ( /W) D-Cache Clean by MVA to PoC */ + __OM uint32_t DCCSW; /*!< Offset: 0x26C ( /W) D-Cache Clean by Set-way */ + __OM uint32_t DCCIMVAC; /*!< Offset: 0x270 ( /W) D-Cache Clean and Invalidate by MVA to PoC */ + __OM uint32_t DCCISW; /*!< Offset: 0x274 ( /W) D-Cache Clean and Invalidate by Set-way */ + uint32_t RESERVED7[6U]; + __IOM uint32_t ITCMCR; /*!< Offset: 0x290 (R/W) Instruction Tightly-Coupled Memory Control Register */ + __IOM uint32_t DTCMCR; /*!< Offset: 0x294 (R/W) Data Tightly-Coupled Memory Control Registers */ + __IOM uint32_t AHBPCR; /*!< Offset: 0x298 (R/W) AHBP Control Register */ + __IOM uint32_t CACR; /*!< Offset: 0x29C (R/W) L1 Cache Control Register */ + __IOM uint32_t AHBSCR; /*!< Offset: 0x2A0 (R/W) AHB Slave Control Register */ + uint32_t RESERVED8[1U]; + __IOM uint32_t ABFSR; /*!< Offset: 0x2A8 (R/W) Auxiliary Bus Fault Status Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_PENDNMISET_Pos 31U /*!< SCB ICSR: PENDNMISET Position */ +#define SCB_ICSR_PENDNMISET_Msk (1UL << SCB_ICSR_PENDNMISET_Pos) /*!< SCB ICSR: PENDNMISET Mask */ + +#define SCB_ICSR_NMIPENDSET_Pos SCB_ICSR_PENDNMISET_Pos /*!< SCB ICSR: NMIPENDSET Position, backward compatibility */ +#define SCB_ICSR_NMIPENDSET_Msk SCB_ICSR_PENDNMISET_Msk /*!< SCB ICSR: NMIPENDSET Mask, backward compatibility */ + +#define SCB_ICSR_PENDNMICLR_Pos 30U /*!< SCB ICSR: PENDNMICLR Position */ +#define SCB_ICSR_PENDNMICLR_Msk (1UL << SCB_ICSR_PENDNMICLR_Pos) /*!< SCB ICSR: PENDNMICLR Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_STTNS_Pos 24U /*!< SCB ICSR: STTNS Position (Security Extension) */ +#define SCB_ICSR_STTNS_Msk (1UL << SCB_ICSR_STTNS_Pos) /*!< SCB ICSR: STTNS Mask (Security Extension) */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIS_Pos 14U /*!< SCB AIRCR: PRIS Position */ +#define SCB_AIRCR_PRIS_Msk (1UL << SCB_AIRCR_PRIS_Pos) /*!< SCB AIRCR: PRIS Mask */ + +#define SCB_AIRCR_BFHFNMINS_Pos 13U /*!< SCB AIRCR: BFHFNMINS Position */ +#define SCB_AIRCR_BFHFNMINS_Msk (1UL << SCB_AIRCR_BFHFNMINS_Pos) /*!< SCB AIRCR: BFHFNMINS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQS_Pos 3U /*!< SCB AIRCR: SYSRESETREQS Position */ +#define SCB_AIRCR_SYSRESETREQS_Msk (1UL << SCB_AIRCR_SYSRESETREQS_Pos) /*!< SCB AIRCR: SYSRESETREQS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEPS_Pos 3U /*!< SCB SCR: SLEEPDEEPS Position */ +#define SCB_SCR_SLEEPDEEPS_Msk (1UL << SCB_SCR_SLEEPDEEPS_Pos) /*!< SCB SCR: SLEEPDEEPS Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_BP_Pos 18U /*!< SCB CCR: BP Position */ +#define SCB_CCR_BP_Msk (1UL << SCB_CCR_BP_Pos) /*!< SCB CCR: BP Mask */ + +#define SCB_CCR_IC_Pos 17U /*!< SCB CCR: IC Position */ +#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos) /*!< SCB CCR: IC Mask */ + +#define SCB_CCR_DC_Pos 16U /*!< SCB CCR: DC Position */ +#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos) /*!< SCB CCR: DC Mask */ + +#define SCB_CCR_STKOFHFNMIGN_Pos 10U /*!< SCB CCR: STKOFHFNMIGN Position */ +#define SCB_CCR_STKOFHFNMIGN_Msk (1UL << SCB_CCR_STKOFHFNMIGN_Pos) /*!< SCB CCR: STKOFHFNMIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_HARDFAULTPENDED_Pos 21U /*!< SCB SHCSR: HARDFAULTPENDED Position */ +#define SCB_SHCSR_HARDFAULTPENDED_Msk (1UL << SCB_SHCSR_HARDFAULTPENDED_Pos) /*!< SCB SHCSR: HARDFAULTPENDED Mask */ + +#define SCB_SHCSR_SECUREFAULTPENDED_Pos 20U /*!< SCB SHCSR: SECUREFAULTPENDED Position */ +#define SCB_SHCSR_SECUREFAULTPENDED_Msk (1UL << SCB_SHCSR_SECUREFAULTPENDED_Pos) /*!< SCB SHCSR: SECUREFAULTPENDED Mask */ + +#define SCB_SHCSR_SECUREFAULTENA_Pos 19U /*!< SCB SHCSR: SECUREFAULTENA Position */ +#define SCB_SHCSR_SECUREFAULTENA_Msk (1UL << SCB_SHCSR_SECUREFAULTENA_Pos) /*!< SCB SHCSR: SECUREFAULTENA Mask */ + +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_NMIACT_Pos 5U /*!< SCB SHCSR: NMIACT Position */ +#define SCB_SHCSR_NMIACT_Msk (1UL << SCB_SHCSR_NMIACT_Pos) /*!< SCB SHCSR: NMIACT Mask */ + +#define SCB_SHCSR_SECUREFAULTACT_Pos 4U /*!< SCB SHCSR: SECUREFAULTACT Position */ +#define SCB_SHCSR_SECUREFAULTACT_Msk (1UL << SCB_SHCSR_SECUREFAULTACT_Pos) /*!< SCB SHCSR: SECUREFAULTACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_HARDFAULTACT_Pos 2U /*!< SCB SHCSR: HARDFAULTACT Position */ +#define SCB_SHCSR_HARDFAULTACT_Msk (1UL << SCB_SHCSR_HARDFAULTACT_Pos) /*!< SCB SHCSR: HARDFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_STKOF_Pos (SCB_CFSR_USGFAULTSR_Pos + 4U) /*!< SCB CFSR (UFSR): STKOF Position */ +#define SCB_CFSR_STKOF_Msk (1UL << SCB_CFSR_STKOF_Pos) /*!< SCB CFSR (UFSR): STKOF Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/* SCB Non-Secure Access Control Register Definitions */ +#define SCB_NSACR_CP11_Pos 11U /*!< SCB NSACR: CP11 Position */ +#define SCB_NSACR_CP11_Msk (1UL << SCB_NSACR_CP11_Pos) /*!< SCB NSACR: CP11 Mask */ + +#define SCB_NSACR_CP10_Pos 10U /*!< SCB NSACR: CP10 Position */ +#define SCB_NSACR_CP10_Msk (1UL << SCB_NSACR_CP10_Pos) /*!< SCB NSACR: CP10 Mask */ + +#define SCB_NSACR_CPn_Pos 0U /*!< SCB NSACR: CPn Position */ +#define SCB_NSACR_CPn_Msk (1UL /*<< SCB_NSACR_CPn_Pos*/) /*!< SCB NSACR: CPn Mask */ + +/* SCB Cache Level ID Register Definitions */ +#define SCB_CLIDR_LOUU_Pos 27U /*!< SCB CLIDR: LoUU Position */ +#define SCB_CLIDR_LOUU_Msk (7UL << SCB_CLIDR_LOUU_Pos) /*!< SCB CLIDR: LoUU Mask */ + +#define SCB_CLIDR_LOC_Pos 24U /*!< SCB CLIDR: LoC Position */ +#define SCB_CLIDR_LOC_Msk (7UL << SCB_CLIDR_LOC_Pos) /*!< SCB CLIDR: LoC Mask */ + +/* SCB Cache Type Register Definitions */ +#define SCB_CTR_FORMAT_Pos 29U /*!< SCB CTR: Format Position */ +#define SCB_CTR_FORMAT_Msk (7UL << SCB_CTR_FORMAT_Pos) /*!< SCB CTR: Format Mask */ + +#define SCB_CTR_CWG_Pos 24U /*!< SCB CTR: CWG Position */ +#define SCB_CTR_CWG_Msk (0xFUL << SCB_CTR_CWG_Pos) /*!< SCB CTR: CWG Mask */ + +#define SCB_CTR_ERG_Pos 20U /*!< SCB CTR: ERG Position */ +#define SCB_CTR_ERG_Msk (0xFUL << SCB_CTR_ERG_Pos) /*!< SCB CTR: ERG Mask */ + +#define SCB_CTR_DMINLINE_Pos 16U /*!< SCB CTR: DminLine Position */ +#define SCB_CTR_DMINLINE_Msk (0xFUL << SCB_CTR_DMINLINE_Pos) /*!< SCB CTR: DminLine Mask */ + +#define SCB_CTR_IMINLINE_Pos 0U /*!< SCB CTR: ImInLine Position */ +#define SCB_CTR_IMINLINE_Msk (0xFUL /*<< SCB_CTR_IMINLINE_Pos*/) /*!< SCB CTR: ImInLine Mask */ + +/* SCB Cache Size ID Register Definitions */ +#define SCB_CCSIDR_WT_Pos 31U /*!< SCB CCSIDR: WT Position */ +#define SCB_CCSIDR_WT_Msk (1UL << SCB_CCSIDR_WT_Pos) /*!< SCB CCSIDR: WT Mask */ + +#define SCB_CCSIDR_WB_Pos 30U /*!< SCB CCSIDR: WB Position */ +#define SCB_CCSIDR_WB_Msk (1UL << SCB_CCSIDR_WB_Pos) /*!< SCB CCSIDR: WB Mask */ + +#define SCB_CCSIDR_RA_Pos 29U /*!< SCB CCSIDR: RA Position */ +#define SCB_CCSIDR_RA_Msk (1UL << SCB_CCSIDR_RA_Pos) /*!< SCB CCSIDR: RA Mask */ + +#define SCB_CCSIDR_WA_Pos 28U /*!< SCB CCSIDR: WA Position */ +#define SCB_CCSIDR_WA_Msk (1UL << SCB_CCSIDR_WA_Pos) /*!< SCB CCSIDR: WA Mask */ + +#define SCB_CCSIDR_NUMSETS_Pos 13U /*!< SCB CCSIDR: NumSets Position */ +#define SCB_CCSIDR_NUMSETS_Msk (0x7FFFUL << SCB_CCSIDR_NUMSETS_Pos) /*!< SCB CCSIDR: NumSets Mask */ + +#define SCB_CCSIDR_ASSOCIATIVITY_Pos 3U /*!< SCB CCSIDR: Associativity Position */ +#define SCB_CCSIDR_ASSOCIATIVITY_Msk (0x3FFUL << SCB_CCSIDR_ASSOCIATIVITY_Pos) /*!< SCB CCSIDR: Associativity Mask */ + +#define SCB_CCSIDR_LINESIZE_Pos 0U /*!< SCB CCSIDR: LineSize Position */ +#define SCB_CCSIDR_LINESIZE_Msk (7UL /*<< SCB_CCSIDR_LINESIZE_Pos*/) /*!< SCB CCSIDR: LineSize Mask */ + +/* SCB Cache Size Selection Register Definitions */ +#define SCB_CSSELR_LEVEL_Pos 1U /*!< SCB CSSELR: Level Position */ +#define SCB_CSSELR_LEVEL_Msk (7UL << SCB_CSSELR_LEVEL_Pos) /*!< SCB CSSELR: Level Mask */ + +#define SCB_CSSELR_IND_Pos 0U /*!< SCB CSSELR: InD Position */ +#define SCB_CSSELR_IND_Msk (1UL /*<< SCB_CSSELR_IND_Pos*/) /*!< SCB CSSELR: InD Mask */ + +/* SCB Software Triggered Interrupt Register Definitions */ +#define SCB_STIR_INTID_Pos 0U /*!< SCB STIR: INTID Position */ +#define SCB_STIR_INTID_Msk (0x1FFUL /*<< SCB_STIR_INTID_Pos*/) /*!< SCB STIR: INTID Mask */ + +/* SCB D-Cache Invalidate by Set-way Register Definitions */ +#define SCB_DCISW_WAY_Pos 30U /*!< SCB DCISW: Way Position */ +#define SCB_DCISW_WAY_Msk (3UL << SCB_DCISW_WAY_Pos) /*!< SCB DCISW: Way Mask */ + +#define SCB_DCISW_SET_Pos 5U /*!< SCB DCISW: Set Position */ +#define SCB_DCISW_SET_Msk (0x1FFUL << SCB_DCISW_SET_Pos) /*!< SCB DCISW: Set Mask */ + +/* SCB D-Cache Clean by Set-way Register Definitions */ +#define SCB_DCCSW_WAY_Pos 30U /*!< SCB DCCSW: Way Position */ +#define SCB_DCCSW_WAY_Msk (3UL << SCB_DCCSW_WAY_Pos) /*!< SCB DCCSW: Way Mask */ + +#define SCB_DCCSW_SET_Pos 5U /*!< SCB DCCSW: Set Position */ +#define SCB_DCCSW_SET_Msk (0x1FFUL << SCB_DCCSW_SET_Pos) /*!< SCB DCCSW: Set Mask */ + +/* SCB D-Cache Clean and Invalidate by Set-way Register Definitions */ +#define SCB_DCCISW_WAY_Pos 30U /*!< SCB DCCISW: Way Position */ +#define SCB_DCCISW_WAY_Msk (3UL << SCB_DCCISW_WAY_Pos) /*!< SCB DCCISW: Way Mask */ + +#define SCB_DCCISW_SET_Pos 5U /*!< SCB DCCISW: Set Position */ +#define SCB_DCCISW_SET_Msk (0x1FFUL << SCB_DCCISW_SET_Pos) /*!< SCB DCCISW: Set Mask */ + +/* Instruction Tightly-Coupled Memory Control Register Definitions */ +#define SCB_ITCMCR_SZ_Pos 3U /*!< SCB ITCMCR: SZ Position */ +#define SCB_ITCMCR_SZ_Msk (0xFUL << SCB_ITCMCR_SZ_Pos) /*!< SCB ITCMCR: SZ Mask */ + +#define SCB_ITCMCR_RETEN_Pos 2U /*!< SCB ITCMCR: RETEN Position */ +#define SCB_ITCMCR_RETEN_Msk (1UL << SCB_ITCMCR_RETEN_Pos) /*!< SCB ITCMCR: RETEN Mask */ + +#define SCB_ITCMCR_RMW_Pos 1U /*!< SCB ITCMCR: RMW Position */ +#define SCB_ITCMCR_RMW_Msk (1UL << SCB_ITCMCR_RMW_Pos) /*!< SCB ITCMCR: RMW Mask */ + +#define SCB_ITCMCR_EN_Pos 0U /*!< SCB ITCMCR: EN Position */ +#define SCB_ITCMCR_EN_Msk (1UL /*<< SCB_ITCMCR_EN_Pos*/) /*!< SCB ITCMCR: EN Mask */ + +/* Data Tightly-Coupled Memory Control Register Definitions */ +#define SCB_DTCMCR_SZ_Pos 3U /*!< SCB DTCMCR: SZ Position */ +#define SCB_DTCMCR_SZ_Msk (0xFUL << SCB_DTCMCR_SZ_Pos) /*!< SCB DTCMCR: SZ Mask */ + +#define SCB_DTCMCR_RETEN_Pos 2U /*!< SCB DTCMCR: RETEN Position */ +#define SCB_DTCMCR_RETEN_Msk (1UL << SCB_DTCMCR_RETEN_Pos) /*!< SCB DTCMCR: RETEN Mask */ + +#define SCB_DTCMCR_RMW_Pos 1U /*!< SCB DTCMCR: RMW Position */ +#define SCB_DTCMCR_RMW_Msk (1UL << SCB_DTCMCR_RMW_Pos) /*!< SCB DTCMCR: RMW Mask */ + +#define SCB_DTCMCR_EN_Pos 0U /*!< SCB DTCMCR: EN Position */ +#define SCB_DTCMCR_EN_Msk (1UL /*<< SCB_DTCMCR_EN_Pos*/) /*!< SCB DTCMCR: EN Mask */ + +/* AHBP Control Register Definitions */ +#define SCB_AHBPCR_SZ_Pos 1U /*!< SCB AHBPCR: SZ Position */ +#define SCB_AHBPCR_SZ_Msk (7UL << SCB_AHBPCR_SZ_Pos) /*!< SCB AHBPCR: SZ Mask */ + +#define SCB_AHBPCR_EN_Pos 0U /*!< SCB AHBPCR: EN Position */ +#define SCB_AHBPCR_EN_Msk (1UL /*<< SCB_AHBPCR_EN_Pos*/) /*!< SCB AHBPCR: EN Mask */ + +/* L1 Cache Control Register Definitions */ +#define SCB_CACR_FORCEWT_Pos 2U /*!< SCB CACR: FORCEWT Position */ +#define SCB_CACR_FORCEWT_Msk (1UL << SCB_CACR_FORCEWT_Pos) /*!< SCB CACR: FORCEWT Mask */ + +#define SCB_CACR_ECCEN_Pos 1U /*!< SCB CACR: ECCEN Position */ +#define SCB_CACR_ECCEN_Msk (1UL << SCB_CACR_ECCEN_Pos) /*!< SCB CACR: ECCEN Mask */ + +#define SCB_CACR_SIWT_Pos 0U /*!< SCB CACR: SIWT Position */ +#define SCB_CACR_SIWT_Msk (1UL /*<< SCB_CACR_SIWT_Pos*/) /*!< SCB CACR: SIWT Mask */ + +/* AHBS Control Register Definitions */ +#define SCB_AHBSCR_INITCOUNT_Pos 11U /*!< SCB AHBSCR: INITCOUNT Position */ +#define SCB_AHBSCR_INITCOUNT_Msk (0x1FUL << SCB_AHBPCR_INITCOUNT_Pos) /*!< SCB AHBSCR: INITCOUNT Mask */ + +#define SCB_AHBSCR_TPRI_Pos 2U /*!< SCB AHBSCR: TPRI Position */ +#define SCB_AHBSCR_TPRI_Msk (0x1FFUL << SCB_AHBPCR_TPRI_Pos) /*!< SCB AHBSCR: TPRI Mask */ + +#define SCB_AHBSCR_CTL_Pos 0U /*!< SCB AHBSCR: CTL Position*/ +#define SCB_AHBSCR_CTL_Msk (3UL /*<< SCB_AHBPCR_CTL_Pos*/) /*!< SCB AHBSCR: CTL Mask */ + +/* Auxiliary Bus Fault Status Register Definitions */ +#define SCB_ABFSR_AXIMTYPE_Pos 8U /*!< SCB ABFSR: AXIMTYPE Position*/ +#define SCB_ABFSR_AXIMTYPE_Msk (3UL << SCB_ABFSR_AXIMTYPE_Pos) /*!< SCB ABFSR: AXIMTYPE Mask */ + +#define SCB_ABFSR_EPPB_Pos 4U /*!< SCB ABFSR: EPPB Position*/ +#define SCB_ABFSR_EPPB_Msk (1UL << SCB_ABFSR_EPPB_Pos) /*!< SCB ABFSR: EPPB Mask */ + +#define SCB_ABFSR_AXIM_Pos 3U /*!< SCB ABFSR: AXIM Position*/ +#define SCB_ABFSR_AXIM_Msk (1UL << SCB_ABFSR_AXIM_Pos) /*!< SCB ABFSR: AXIM Mask */ + +#define SCB_ABFSR_AHBP_Pos 2U /*!< SCB ABFSR: AHBP Position*/ +#define SCB_ABFSR_AHBP_Msk (1UL << SCB_ABFSR_AHBP_Pos) /*!< SCB ABFSR: AHBP Mask */ + +#define SCB_ABFSR_DTCM_Pos 1U /*!< SCB ABFSR: DTCM Position*/ +#define SCB_ABFSR_DTCM_Msk (1UL << SCB_ABFSR_DTCM_Pos) /*!< SCB ABFSR: DTCM Mask */ + +#define SCB_ABFSR_ITCM_Pos 0U /*!< SCB ABFSR: ITCM Position*/ +#define SCB_ABFSR_ITCM_Msk (1UL /*<< SCB_ABFSR_ITCM_Pos*/) /*!< SCB ABFSR: ITCM Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ + __IOM uint32_t CPPWR; /*!< Offset: 0x00C (R/W) Coprocessor Power Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29U]; + __OM uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __IM uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IOM uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[1U]; + __IM uint32_t DEVARCH; /*!< Offset: 0xFBC (R/ ) ITM Device Architecture Register */ + uint32_t RESERVED6[4U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Stimulus Port Register Definitions */ +#define ITM_STIM_DISABLED_Pos 1U /*!< ITM STIM: DISABLED Position */ +#define ITM_STIM_DISABLED_Msk (0x1UL << ITM_STIM_DISABLED_Pos) /*!< ITM STIM: DISABLED Mask */ + +#define ITM_STIM_FIFOREADY_Pos 0U /*!< ITM STIM: FIFOREADY Position */ +#define ITM_STIM_FIFOREADY_Msk (0x1UL /*<< ITM_STIM_FIFOREADY_Pos*/) /*!< ITM STIM: FIFOREADY Mask */ + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TRACEBUSID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TRACEBUSID_Msk (0x7FUL << ITM_TCR_TRACEBUSID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPRESCALE_Pos 8U /*!< ITM TCR: TSPRESCALE Position */ +#define ITM_TCR_TSPRESCALE_Msk (3UL << ITM_TCR_TSPRESCALE_Pos) /*!< ITM TCR: TSPRESCALE Mask */ + +#define ITM_TCR_STALLENA_Pos 5U /*!< ITM TCR: STALLENA Position */ +#define ITM_TCR_STALLENA_Msk (1UL << ITM_TCR_STALLENA_Pos) /*!< ITM TCR: STALLENA Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0U /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL /*<< ITM_IWR_ATVALIDM_Pos*/) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0U /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL /*<< ITM_IRR_ATREADYM_Pos*/) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0U /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL /*<< ITM_IMCR_INTEGRATION_Pos*/) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + uint32_t RESERVED3[1U]; + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED4[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + uint32_t RESERVED5[1U]; + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED6[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + uint32_t RESERVED7[1U]; + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ + uint32_t RESERVED8[1U]; + __IOM uint32_t COMP4; /*!< Offset: 0x060 (R/W) Comparator Register 4 */ + uint32_t RESERVED9[1U]; + __IOM uint32_t FUNCTION4; /*!< Offset: 0x068 (R/W) Function Register 4 */ + uint32_t RESERVED10[1U]; + __IOM uint32_t COMP5; /*!< Offset: 0x070 (R/W) Comparator Register 5 */ + uint32_t RESERVED11[1U]; + __IOM uint32_t FUNCTION5; /*!< Offset: 0x078 (R/W) Function Register 5 */ + uint32_t RESERVED12[1U]; + __IOM uint32_t COMP6; /*!< Offset: 0x080 (R/W) Comparator Register 6 */ + uint32_t RESERVED13[1U]; + __IOM uint32_t FUNCTION6; /*!< Offset: 0x088 (R/W) Function Register 6 */ + uint32_t RESERVED14[1U]; + __IOM uint32_t COMP7; /*!< Offset: 0x090 (R/W) Comparator Register 7 */ + uint32_t RESERVED15[1U]; + __IOM uint32_t FUNCTION7; /*!< Offset: 0x098 (R/W) Function Register 7 */ + uint32_t RESERVED16[1U]; + __IOM uint32_t COMP8; /*!< Offset: 0x0A0 (R/W) Comparator Register 8 */ + uint32_t RESERVED17[1U]; + __IOM uint32_t FUNCTION8; /*!< Offset: 0x0A8 (R/W) Function Register 8 */ + uint32_t RESERVED18[1U]; + __IOM uint32_t COMP9; /*!< Offset: 0x0B0 (R/W) Comparator Register 9 */ + uint32_t RESERVED19[1U]; + __IOM uint32_t FUNCTION9; /*!< Offset: 0x0B8 (R/W) Function Register 9 */ + uint32_t RESERVED20[1U]; + __IOM uint32_t COMP10; /*!< Offset: 0x0C0 (R/W) Comparator Register 10 */ + uint32_t RESERVED21[1U]; + __IOM uint32_t FUNCTION10; /*!< Offset: 0x0C8 (R/W) Function Register 10 */ + uint32_t RESERVED22[1U]; + __IOM uint32_t COMP11; /*!< Offset: 0x0D0 (R/W) Comparator Register 11 */ + uint32_t RESERVED23[1U]; + __IOM uint32_t FUNCTION11; /*!< Offset: 0x0D8 (R/W) Function Register 11 */ + uint32_t RESERVED24[1U]; + __IOM uint32_t COMP12; /*!< Offset: 0x0E0 (R/W) Comparator Register 12 */ + uint32_t RESERVED25[1U]; + __IOM uint32_t FUNCTION12; /*!< Offset: 0x0E8 (R/W) Function Register 12 */ + uint32_t RESERVED26[1U]; + __IOM uint32_t COMP13; /*!< Offset: 0x0F0 (R/W) Comparator Register 13 */ + uint32_t RESERVED27[1U]; + __IOM uint32_t FUNCTION13; /*!< Offset: 0x0F8 (R/W) Function Register 13 */ + uint32_t RESERVED28[1U]; + __IOM uint32_t COMP14; /*!< Offset: 0x100 (R/W) Comparator Register 14 */ + uint32_t RESERVED29[1U]; + __IOM uint32_t FUNCTION14; /*!< Offset: 0x108 (R/W) Function Register 14 */ + uint32_t RESERVED30[1U]; + __IOM uint32_t COMP15; /*!< Offset: 0x110 (R/W) Comparator Register 15 */ + uint32_t RESERVED31[1U]; + __IOM uint32_t FUNCTION15; /*!< Offset: 0x118 (R/W) Function Register 15 */ + uint32_t RESERVED32[934U]; + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R ) Lock Status Register */ + uint32_t RESERVED33[1U]; + __IM uint32_t DEVARCH; /*!< Offset: 0xFBC (R/ ) Device Architecture Register */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCDISS_Pos 23U /*!< DWT CTRL: CYCDISS Position */ +#define DWT_CTRL_CYCDISS_Msk (0x1UL << DWT_CTRL_CYCDISS_Pos) /*!< DWT CTRL: CYCDISS Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_ID_Pos 27U /*!< DWT FUNCTION: ID Position */ +#define DWT_FUNCTION_ID_Msk (0x1FUL << DWT_FUNCTION_ID_Pos) /*!< DWT FUNCTION: ID Mask */ + +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_ACTION_Pos 4U /*!< DWT FUNCTION: ACTION Position */ +#define DWT_FUNCTION_ACTION_Msk (0x1UL << DWT_FUNCTION_ACTION_Pos) /*!< DWT FUNCTION: ACTION Mask */ + +#define DWT_FUNCTION_MATCH_Pos 0U /*!< DWT FUNCTION: MATCH Position */ +#define DWT_FUNCTION_MATCH_Msk (0xFUL /*<< DWT_FUNCTION_MATCH_Pos*/) /*!< DWT FUNCTION: MATCH Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Sizes Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Sizes Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IOM uint32_t PSCR; /*!< Offset: 0x308 (R/W) Periodic Synchronization Control Register */ + uint32_t RESERVED3[809U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) Software Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) Software Lock Status Register */ + uint32_t RESERVED4[4U]; + __IM uint32_t TYPE; /*!< Offset: 0xFC8 (R/ ) Device Identifier Register */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) Device Type Register */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_SWOSCALER_Pos 0U /*!< TPI ACPR: SWOSCALER Position */ +#define TPI_ACPR_SWOSCALER_Msk (0xFFFFUL /*<< TPI_ACPR_SWOSCALER_Pos*/) /*!< TPI ACPR: SWOSCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_FOnMan_Pos 6U /*!< TPI FFCR: FOnMan Position */ +#define TPI_FFCR_FOnMan_Msk (0x1UL << TPI_FFCR_FOnMan_Pos) /*!< TPI FFCR: FOnMan Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI Periodic Synchronization Control Register Definitions */ +#define TPI_PSCR_PSCount_Pos 0U /*!< TPI PSCR: PSCount Position */ +#define TPI_PSCR_PSCount_Msk (0x1FUL /*<< TPI_PSCR_PSCount_Pos*/) /*!< TPI PSCR: TPSCount Mask */ + +/* TPI Software Lock Status Register Definitions */ +#define TPI_LSR_nTT_Pos 1U /*!< TPI LSR: Not thirty-two bit. Position */ +#define TPI_LSR_nTT_Msk (0x1UL << TPI_LSR_nTT_Pos) /*!< TPI LSR: Not thirty-two bit. Mask */ + +#define TPI_LSR_SLK_Pos 1U /*!< TPI LSR: Software Lock status Position */ +#define TPI_LSR_SLK_Msk (0x1UL << TPI_LSR_SLK_Pos) /*!< TPI LSR: Software Lock status Mask */ + +#define TPI_LSR_SLI_Pos 0U /*!< TPI LSR: Software Lock implemented Position */ +#define TPI_LSR_SLI_Msk (0x1UL /*<< TPI_LSR_SLI_Pos*/) /*!< TPI LSR: Software Lock implemented Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_FIFOSZ_Pos 6U /*!< TPI DEVID: FIFO depth Position */ +#define TPI_DEVID_FIFOSZ_Msk (0x7UL << TPI_DEVID_FIFOSZ_Pos) /*!< TPI DEVID: FIFO depth Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) MPU Region Limit Address Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Region Base Address Register Alias 1 */ + __IOM uint32_t RLAR_A1; /*!< Offset: 0x018 (R/W) MPU Region Limit Address Register Alias 1 */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Region Base Address Register Alias 2 */ + __IOM uint32_t RLAR_A2; /*!< Offset: 0x020 (R/W) MPU Region Limit Address Register Alias 2 */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Region Base Address Register Alias 3 */ + __IOM uint32_t RLAR_A3; /*!< Offset: 0x028 (R/W) MPU Region Limit Address Register Alias 3 */ + uint32_t RESERVED0[1]; + union { + __IOM uint32_t MAIR[2]; + struct { + __IOM uint32_t MAIR0; /*!< Offset: 0x030 (R/W) MPU Memory Attribute Indirection Register 0 */ + __IOM uint32_t MAIR1; /*!< Offset: 0x034 (R/W) MPU Memory Attribute Indirection Register 1 */ + }; + }; +} MPU_Type; + +#define MPU_TYPE_RALIASES 4U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_BASE_Pos 5U /*!< MPU RBAR: BASE Position */ +#define MPU_RBAR_BASE_Msk (0x7FFFFFFUL << MPU_RBAR_BASE_Pos) /*!< MPU RBAR: BASE Mask */ + +#define MPU_RBAR_SH_Pos 3U /*!< MPU RBAR: SH Position */ +#define MPU_RBAR_SH_Msk (0x3UL << MPU_RBAR_SH_Pos) /*!< MPU RBAR: SH Mask */ + +#define MPU_RBAR_AP_Pos 1U /*!< MPU RBAR: AP Position */ +#define MPU_RBAR_AP_Msk (0x3UL << MPU_RBAR_AP_Pos) /*!< MPU RBAR: AP Mask */ + +#define MPU_RBAR_XN_Pos 0U /*!< MPU RBAR: XN Position */ +#define MPU_RBAR_XN_Msk (01UL /*<< MPU_RBAR_XN_Pos*/) /*!< MPU RBAR: XN Mask */ + +/* MPU Region Limit Address Register Definitions */ +#define MPU_RLAR_LIMIT_Pos 5U /*!< MPU RLAR: LIMIT Position */ +#define MPU_RLAR_LIMIT_Msk (0x7FFFFFFUL << MPU_RLAR_LIMIT_Pos) /*!< MPU RLAR: LIMIT Mask */ + +#define MPU_RLAR_AttrIndx_Pos 1U /*!< MPU RLAR: AttrIndx Position */ +#define MPU_RLAR_AttrIndx_Msk (0x7UL << MPU_RLAR_AttrIndx_Pos) /*!< MPU RLAR: AttrIndx Mask */ + +#define MPU_RLAR_EN_Pos 0U /*!< MPU RLAR: Region enable bit Position */ +#define MPU_RLAR_EN_Msk (1UL /*<< MPU_RLAR_EN_Pos*/) /*!< MPU RLAR: Region enable bit Disable Mask */ + +/* MPU Memory Attribute Indirection Register 0 Definitions */ +#define MPU_MAIR0_Attr3_Pos 24U /*!< MPU MAIR0: Attr3 Position */ +#define MPU_MAIR0_Attr3_Msk (0xFFUL << MPU_MAIR0_Attr3_Pos) /*!< MPU MAIR0: Attr3 Mask */ + +#define MPU_MAIR0_Attr2_Pos 16U /*!< MPU MAIR0: Attr2 Position */ +#define MPU_MAIR0_Attr2_Msk (0xFFUL << MPU_MAIR0_Attr2_Pos) /*!< MPU MAIR0: Attr2 Mask */ + +#define MPU_MAIR0_Attr1_Pos 8U /*!< MPU MAIR0: Attr1 Position */ +#define MPU_MAIR0_Attr1_Msk (0xFFUL << MPU_MAIR0_Attr1_Pos) /*!< MPU MAIR0: Attr1 Mask */ + +#define MPU_MAIR0_Attr0_Pos 0U /*!< MPU MAIR0: Attr0 Position */ +#define MPU_MAIR0_Attr0_Msk (0xFFUL /*<< MPU_MAIR0_Attr0_Pos*/) /*!< MPU MAIR0: Attr0 Mask */ + +/* MPU Memory Attribute Indirection Register 1 Definitions */ +#define MPU_MAIR1_Attr7_Pos 24U /*!< MPU MAIR1: Attr7 Position */ +#define MPU_MAIR1_Attr7_Msk (0xFFUL << MPU_MAIR1_Attr7_Pos) /*!< MPU MAIR1: Attr7 Mask */ + +#define MPU_MAIR1_Attr6_Pos 16U /*!< MPU MAIR1: Attr6 Position */ +#define MPU_MAIR1_Attr6_Msk (0xFFUL << MPU_MAIR1_Attr6_Pos) /*!< MPU MAIR1: Attr6 Mask */ + +#define MPU_MAIR1_Attr5_Pos 8U /*!< MPU MAIR1: Attr5 Position */ +#define MPU_MAIR1_Attr5_Msk (0xFFUL << MPU_MAIR1_Attr5_Pos) /*!< MPU MAIR1: Attr5 Mask */ + +#define MPU_MAIR1_Attr4_Pos 0U /*!< MPU MAIR1: Attr4 Position */ +#define MPU_MAIR1_Attr4_Msk (0xFFUL /*<< MPU_MAIR1_Attr4_Pos*/) /*!< MPU MAIR1: Attr4 Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SAU Security Attribution Unit (SAU) + \brief Type definitions for the Security Attribution Unit (SAU) + @{ + */ + +/** + \brief Structure type to access the Security Attribution Unit (SAU). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SAU Control Register */ + __IM uint32_t TYPE; /*!< Offset: 0x004 (R/ ) SAU Type Register */ +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) SAU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) SAU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) SAU Region Limit Address Register */ +#else + uint32_t RESERVED0[3]; +#endif + __IOM uint32_t SFSR; /*!< Offset: 0x014 (R/W) Secure Fault Status Register */ + __IOM uint32_t SFAR; /*!< Offset: 0x018 (R/W) Secure Fault Address Register */ +} SAU_Type; + +/* SAU Control Register Definitions */ +#define SAU_CTRL_ALLNS_Pos 1U /*!< SAU CTRL: ALLNS Position */ +#define SAU_CTRL_ALLNS_Msk (1UL << SAU_CTRL_ALLNS_Pos) /*!< SAU CTRL: ALLNS Mask */ + +#define SAU_CTRL_ENABLE_Pos 0U /*!< SAU CTRL: ENABLE Position */ +#define SAU_CTRL_ENABLE_Msk (1UL /*<< SAU_CTRL_ENABLE_Pos*/) /*!< SAU CTRL: ENABLE Mask */ + +/* SAU Type Register Definitions */ +#define SAU_TYPE_SREGION_Pos 0U /*!< SAU TYPE: SREGION Position */ +#define SAU_TYPE_SREGION_Msk (0xFFUL /*<< SAU_TYPE_SREGION_Pos*/) /*!< SAU TYPE: SREGION Mask */ + +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) +/* SAU Region Number Register Definitions */ +#define SAU_RNR_REGION_Pos 0U /*!< SAU RNR: REGION Position */ +#define SAU_RNR_REGION_Msk (0xFFUL /*<< SAU_RNR_REGION_Pos*/) /*!< SAU RNR: REGION Mask */ + +/* SAU Region Base Address Register Definitions */ +#define SAU_RBAR_BADDR_Pos 5U /*!< SAU RBAR: BADDR Position */ +#define SAU_RBAR_BADDR_Msk (0x7FFFFFFUL << SAU_RBAR_BADDR_Pos) /*!< SAU RBAR: BADDR Mask */ + +/* SAU Region Limit Address Register Definitions */ +#define SAU_RLAR_LADDR_Pos 5U /*!< SAU RLAR: LADDR Position */ +#define SAU_RLAR_LADDR_Msk (0x7FFFFFFUL << SAU_RLAR_LADDR_Pos) /*!< SAU RLAR: LADDR Mask */ + +#define SAU_RLAR_NSC_Pos 1U /*!< SAU RLAR: NSC Position */ +#define SAU_RLAR_NSC_Msk (1UL << SAU_RLAR_NSC_Pos) /*!< SAU RLAR: NSC Mask */ + +#define SAU_RLAR_ENABLE_Pos 0U /*!< SAU RLAR: ENABLE Position */ +#define SAU_RLAR_ENABLE_Msk (1UL /*<< SAU_RLAR_ENABLE_Pos*/) /*!< SAU RLAR: ENABLE Mask */ + +#endif /* defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) */ + +/* Secure Fault Status Register Definitions */ +#define SAU_SFSR_LSERR_Pos 7U /*!< SAU SFSR: LSERR Position */ +#define SAU_SFSR_LSERR_Msk (1UL << SAU_SFSR_LSERR_Pos) /*!< SAU SFSR: LSERR Mask */ + +#define SAU_SFSR_SFARVALID_Pos 6U /*!< SAU SFSR: SFARVALID Position */ +#define SAU_SFSR_SFARVALID_Msk (1UL << SAU_SFSR_SFARVALID_Pos) /*!< SAU SFSR: SFARVALID Mask */ + +#define SAU_SFSR_LSPERR_Pos 5U /*!< SAU SFSR: LSPERR Position */ +#define SAU_SFSR_LSPERR_Msk (1UL << SAU_SFSR_LSPERR_Pos) /*!< SAU SFSR: LSPERR Mask */ + +#define SAU_SFSR_INVTRAN_Pos 4U /*!< SAU SFSR: INVTRAN Position */ +#define SAU_SFSR_INVTRAN_Msk (1UL << SAU_SFSR_INVTRAN_Pos) /*!< SAU SFSR: INVTRAN Mask */ + +#define SAU_SFSR_AUVIOL_Pos 3U /*!< SAU SFSR: AUVIOL Position */ +#define SAU_SFSR_AUVIOL_Msk (1UL << SAU_SFSR_AUVIOL_Pos) /*!< SAU SFSR: AUVIOL Mask */ + +#define SAU_SFSR_INVER_Pos 2U /*!< SAU SFSR: INVER Position */ +#define SAU_SFSR_INVER_Msk (1UL << SAU_SFSR_INVER_Pos) /*!< SAU SFSR: INVER Mask */ + +#define SAU_SFSR_INVIS_Pos 1U /*!< SAU SFSR: INVIS Position */ +#define SAU_SFSR_INVIS_Msk (1UL << SAU_SFSR_INVIS_Pos) /*!< SAU SFSR: INVIS Mask */ + +#define SAU_SFSR_INVEP_Pos 0U /*!< SAU SFSR: INVEP Position */ +#define SAU_SFSR_INVEP_Msk (1UL /*<< SAU_SFSR_INVEP_Pos*/) /*!< SAU SFSR: INVEP Mask */ + +/*@} end of group CMSIS_SAU */ +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** + \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ +} FPU_Type; + +/* Floating-Point Context Control Register Definitions */ +#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_LSPENS_Pos 29U /*!< FPCCR: LSPENS Position */ +#define FPU_FPCCR_LSPENS_Msk (1UL << FPU_FPCCR_LSPENS_Pos) /*!< FPCCR: LSPENS bit Mask */ + +#define FPU_FPCCR_CLRONRET_Pos 28U /*!< FPCCR: CLRONRET Position */ +#define FPU_FPCCR_CLRONRET_Msk (1UL << FPU_FPCCR_CLRONRET_Pos) /*!< FPCCR: CLRONRET bit Mask */ + +#define FPU_FPCCR_CLRONRETS_Pos 27U /*!< FPCCR: CLRONRETS Position */ +#define FPU_FPCCR_CLRONRETS_Msk (1UL << FPU_FPCCR_CLRONRETS_Pos) /*!< FPCCR: CLRONRETS bit Mask */ + +#define FPU_FPCCR_TS_Pos 26U /*!< FPCCR: TS Position */ +#define FPU_FPCCR_TS_Msk (1UL << FPU_FPCCR_TS_Pos) /*!< FPCCR: TS bit Mask */ + +#define FPU_FPCCR_UFRDY_Pos 10U /*!< FPCCR: UFRDY Position */ +#define FPU_FPCCR_UFRDY_Msk (1UL << FPU_FPCCR_UFRDY_Pos) /*!< FPCCR: UFRDY bit Mask */ + +#define FPU_FPCCR_SPLIMVIOL_Pos 9U /*!< FPCCR: SPLIMVIOL Position */ +#define FPU_FPCCR_SPLIMVIOL_Msk (1UL << FPU_FPCCR_SPLIMVIOL_Pos) /*!< FPCCR: SPLIMVIOL bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_SFRDY_Pos 7U /*!< FPCCR: SFRDY Position */ +#define FPU_FPCCR_SFRDY_Msk (1UL << FPU_FPCCR_SFRDY_Pos) /*!< FPCCR: SFRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_S_Pos 2U /*!< FPCCR: Security status of the FP context bit Position */ +#define FPU_FPCCR_S_Msk (1UL << FPU_FPCCR_S_Pos) /*!< FPCCR: Security status of the FP context bit Mask */ + +#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register Definitions */ +#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register Definitions */ +#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 Definitions */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 Definitions */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ + +/*@} end of group CMSIS_FPU */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ + uint32_t RESERVED4[1U]; + __IOM uint32_t DAUTHCTRL; /*!< Offset: 0x014 (R/W) Debug Authentication Control Register */ + __IOM uint32_t DSCSR; /*!< Offset: 0x018 (R/W) Debug Security Control and Status Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESTART_ST_Pos 26U /*!< CoreDebug DHCSR: S_RESTART_ST Position */ +#define CoreDebug_DHCSR_S_RESTART_ST_Msk (1UL << CoreDebug_DHCSR_S_RESTART_ST_Pos) /*!< CoreDebug DHCSR: S_RESTART_ST Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/* Debug Authentication Control Register Definitions */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos 3U /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Position */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Mask */ + +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos 2U /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Msk (1UL << CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos) /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Mask */ + +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Pos 1U /*!< CoreDebug DAUTHCTRL: INTSPIDEN Position */ +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPIDEN Mask */ + +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Pos 0U /*!< CoreDebug DAUTHCTRL: SPIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Msk (1UL /*<< CoreDebug_DAUTHCTRL_SPIDENSEL_Pos*/) /*!< CoreDebug DAUTHCTRL: SPIDENSEL Mask */ + +/* Debug Security Control and Status Register Definitions */ +#define CoreDebug_DSCSR_CDS_Pos 16U /*!< CoreDebug DSCSR: CDS Position */ +#define CoreDebug_DSCSR_CDS_Msk (1UL << CoreDebug_DSCSR_CDS_Pos) /*!< CoreDebug DSCSR: CDS Mask */ + +#define CoreDebug_DSCSR_SBRSEL_Pos 1U /*!< CoreDebug DSCSR: SBRSEL Position */ +#define CoreDebug_DSCSR_SBRSEL_Msk (1UL << CoreDebug_DSCSR_SBRSEL_Pos) /*!< CoreDebug DSCSR: SBRSEL Mask */ + +#define CoreDebug_DSCSR_SBRSELEN_Pos 0U /*!< CoreDebug DSCSR: SBRSELEN Position */ +#define CoreDebug_DSCSR_SBRSELEN_Msk (1UL /*<< CoreDebug_DSCSR_SBRSELEN_Pos*/) /*!< CoreDebug DSCSR: SBRSELEN Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ + #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ + #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ + #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ + #define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ + #define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ + #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ + #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ + #define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + + #define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ + #define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ + #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ + #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + #define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ + #define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ + #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ + #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE ) /*!< Core Debug configuration struct */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ + #endif + + #if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SAU_BASE (SCS_BASE + 0x0DD0UL) /*!< Security Attribution Unit */ + #define SAU ((SAU_Type *) SAU_BASE ) /*!< Security Attribution Unit */ + #endif + + #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ + #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SCS_BASE_NS (0xE002E000UL) /*!< System Control Space Base Address (non-secure address space) */ + #define CoreDebug_BASE_NS (0xE002EDF0UL) /*!< Core Debug Base Address (non-secure address space) */ + #define SysTick_BASE_NS (SCS_BASE_NS + 0x0010UL) /*!< SysTick Base Address (non-secure address space) */ + #define NVIC_BASE_NS (SCS_BASE_NS + 0x0100UL) /*!< NVIC Base Address (non-secure address space) */ + #define SCB_BASE_NS (SCS_BASE_NS + 0x0D00UL) /*!< System Control Block Base Address (non-secure address space) */ + + #define SCnSCB_NS ((SCnSCB_Type *) SCS_BASE_NS ) /*!< System control Register not in SCB(non-secure address space) */ + #define SCB_NS ((SCB_Type *) SCB_BASE_NS ) /*!< SCB configuration struct (non-secure address space) */ + #define SysTick_NS ((SysTick_Type *) SysTick_BASE_NS ) /*!< SysTick configuration struct (non-secure address space) */ + #define NVIC_NS ((NVIC_Type *) NVIC_BASE_NS ) /*!< NVIC configuration struct (non-secure address space) */ + #define CoreDebug_NS ((CoreDebug_Type *) CoreDebug_BASE_NS) /*!< Core Debug configuration struct (non-secure address space) */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE_NS (SCS_BASE_NS + 0x0D90UL) /*!< Memory Protection Unit (non-secure address space) */ + #define MPU_NS ((MPU_Type *) MPU_BASE_NS ) /*!< Memory Protection Unit (non-secure address space) */ + #endif + + #define FPU_BASE_NS (SCS_BASE_NS + 0x0F30UL) /*!< Floating Point Unit (non-secure address space) */ + #define FPU_NS ((FPU_Type *) FPU_BASE_NS ) /*!< Floating Point Unit (non-secure address space) */ + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* Special LR values for Secure/Non-Secure call handling and exception handling */ + +/* Function Return Payload (from ARMv8-M Architecture Reference Manual) LR value on entry from Secure BLXNS */ +#define FNC_RETURN (0xFEFFFFFFUL) /* bit [0] ignored when processing a branch */ + +/* The following EXC_RETURN mask values are used to evaluate the LR on exception entry */ +#define EXC_RETURN_PREFIX (0xFF000000UL) /* bits [31:24] set to indicate an EXC_RETURN value */ +#define EXC_RETURN_S (0x00000040UL) /* bit [6] stack used to push registers: 0=Non-secure 1=Secure */ +#define EXC_RETURN_DCRS (0x00000020UL) /* bit [5] stacking rules for called registers: 0=skipped 1=saved */ +#define EXC_RETURN_FTYPE (0x00000010UL) /* bit [4] allocate stack for floating-point context: 0=done 1=skipped */ +#define EXC_RETURN_MODE (0x00000008UL) /* bit [3] processor mode for return: 0=Handler mode 1=Thread mode */ +#define EXC_RETURN_SPSEL (0x00000002UL) /* bit [1] stack pointer used to restore context: 0=MSP 1=PSP */ +#define EXC_RETURN_ES (0x00000001UL) /* bit [0] security state exception was taken to: 0=Non-secure 1=Secure */ + +/* Integrity Signature (from ARMv8-M Architecture Reference Manual) for exception context stacking */ +#if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) /* Value for processors with floating-point extension: */ +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125AUL) /* bit [0] SFTC must match LR bit[4] EXC_RETURN_FTYPE */ +#else +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125BUL) /* Value for processors without floating-point extension */ +#endif + + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Get Interrupt Target State + \details Reads the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + \return 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_GetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Target State + \details Sets the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_SetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] |= ((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Clear Interrupt Target State + \details Clears the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_ClearTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] &= ~((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IPR[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC->IPR[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Set Priority Grouping (non-secure) + \details Sets the non-secure priority grouping field when in secure state using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void TZ_NVIC_SetPriorityGrouping_NS(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB_NS->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + SCB_NS->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping (non-secure) + \details Reads the priority grouping field from the non-secure NVIC when in secure state. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriorityGrouping_NS(void) +{ + return ((uint32_t)((SCB_NS->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt (non-secure) + \details Enables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_EnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status (non-secure) + \details Returns a device specific interrupt enable status from the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetEnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt (non-secure) + \details Disables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_DisableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Pending Interrupt (non-secure) + \details Reads the NVIC pending register in the non-secure NVIC when in secure state and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt (non-secure) + \details Sets the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_SetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt (non-secure) + \details Clears the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_ClearPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt (non-secure) + \details Reads the active register in non-secure NVIC when in secure state and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetActive_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority (non-secure) + \details Sets the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every non-secure processor exception. + */ +__STATIC_INLINE void TZ_NVIC_SetPriority_NS(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->IPR[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB_NS->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority (non-secure) + \details Reads the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriority_NS(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC_NS->IPR[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB_NS->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) &&(__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_NVICFunctions */ + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv8.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + uint32_t mvfr0; + + mvfr0 = FPU->MVFR0; + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x220U) + { + return 2U; /* Double + Single precision FPU */ + } + else if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) + { + return 1U; /* Single precision FPU */ + } + else + { + return 0U; /* No FPU */ + } +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ########################## SAU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SAUFunctions SAU Functions + \brief Functions that configure the SAU. + @{ + */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + +/** + \brief Enable SAU + \details Enables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Enable(void) +{ + SAU->CTRL |= (SAU_CTRL_ENABLE_Msk); +} + + + +/** + \brief Disable SAU + \details Disables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Disable(void) +{ + SAU->CTRL &= ~(SAU_CTRL_ENABLE_Msk); +} + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_SAUFunctions */ + + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief System Tick Configuration (non-secure) + \details Initializes the non-secure System Timer and its interrupt when in secure state, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function TZ_SysTick_Config_NS is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t TZ_SysTick_Config_NS(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick_NS->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + TZ_NVIC_SetPriority_NS (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick_NS->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick_NS->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_ARMV8MML_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0.h b/Firmware/ThirdParty/CMSIS/Include/core_cm0.h similarity index 71% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0.h rename to Firmware/ThirdParty/CMSIS/Include/core_cm0.h index 711dad551..f929bba07 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm0.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_cm0.h * @brief CMSIS Cortex-M0 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.5 + * @date 28. May 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,53 +60,15 @@ @{ */ +#include "cmsis_version.h" + /* CMSIS CM0 definitions */ -#define __CM0_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM0_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __CM0_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM0_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __CM0_CMSIS_VERSION ((__CM0_CMSIS_VERSION_MAIN << 16U) | \ - __CM0_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x00U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline + __CM0_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_M (0U) /*!< Cortex-M Core */ /** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all @@ -128,7 +80,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -143,7 +95,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -160,8 +112,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -555,18 +507,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -578,7 +530,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M0 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ @@ -614,87 +566,177 @@ typedef struct @{ */ -/* Interrupt Priorities are WORD accessible only under ARMv6M */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ +/*#define NVIC_GetActive __NVIC_GetActive not available for Cortex-M0 */ + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ /* The following MACROS handle generation of the register offset and byte masks */ #define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) #define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) #define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) +#define __NVIC_SetPriorityGrouping(X) (void)(X) +#define __NVIC_GetPriorityGrouping() (0U) /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } else { - NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } } @@ -702,32 +744,116 @@ __STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); } else { - return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); } } +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + Address 0 must be mapped to SRAM. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)0x0U; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)0x0U; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -744,6 +870,31 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + /* ################################## SysTick function ############################################ */ /** @@ -753,7 +904,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0plus.h b/Firmware/ThirdParty/CMSIS/Include/core_cm0plus.h similarity index 74% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0plus.h rename to Firmware/ThirdParty/CMSIS/Include/core_cm0plus.h index b04aa3905..424011ac3 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0plus.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm0plus.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_cm0plus.h * @brief CMSIS Cortex-M0+ Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.6 + * @date 28. May 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,53 +60,15 @@ @{ */ +#include "cmsis_version.h" + /* CMSIS CM0+ definitions */ -#define __CM0PLUS_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM0PLUS_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __CM0PLUS_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM0PLUS_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __CM0PLUS_CMSIS_VERSION ((__CM0PLUS_CMSIS_VERSION_MAIN << 16U) | \ - __CM0PLUS_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x00U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline + __CM0PLUS_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#else - #error Unknown compiler -#endif +#define __CORTEX_M (0U) /*!< Cortex-M Core */ /** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all @@ -128,7 +80,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -143,7 +95,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -160,8 +112,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -404,7 +356,7 @@ typedef struct { __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ -#if (__VTOR_PRESENT == 1U) +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ #else uint32_t RESERVED0; @@ -461,7 +413,7 @@ typedef struct #define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ #define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ -#if (__VTOR_PRESENT == 1U) +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) /* SCB Interrupt Control State Register Definitions */ #define SCB_VTOR_TBLOFF_Pos 8U /*!< SCB VTOR: TBLOFF Position */ #define SCB_VTOR_TBLOFF_Msk (0xFFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ @@ -558,7 +510,7 @@ typedef struct /*@} end of group CMSIS_SysTick */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -578,6 +530,8 @@ typedef struct __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ } MPU_Type; +#define MPU_TYPE_RALIASES 1U + /* MPU Type Register Definitions */ #define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ #define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ @@ -667,18 +621,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -690,7 +644,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M0+ Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ @@ -700,7 +654,7 @@ typedef struct #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif @@ -730,87 +684,177 @@ typedef struct @{ */ -/* Interrupt Priorities are WORD accessible only under ARMv6M */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ +/*#define NVIC_GetActive __NVIC_GetActive not available for Cortex-M0+ */ + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ /* The following MACROS handle generation of the register offset and byte masks */ #define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) #define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) #define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) +#define __NVIC_SetPriorityGrouping(X) (void)(X) +#define __NVIC_GetPriorityGrouping() (0U) /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) { - NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } else { - NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } } @@ -818,32 +862,125 @@ __STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); } else { - return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); } } +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + If VTOR is not present address 0 must be mapped to SRAM. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; + +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -859,6 +996,38 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + /* ################################## SysTick function ############################################ */ @@ -869,7 +1038,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration diff --git a/Firmware/ThirdParty/CMSIS/Include/core_cm1.h b/Firmware/ThirdParty/CMSIS/Include/core_cm1.h new file mode 100644 index 000000000..0ed678e3b --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm1.h @@ -0,0 +1,976 @@ +/**************************************************************************//** + * @file core_cm1.h + * @brief CMSIS Cortex-M1 Core Peripheral Access Layer Header File + * @version V1.0.0 + * @date 23. July 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM1_H_GENERIC +#define __CORE_CM1_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M1 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS CM1 definitions */ +#define __CM1_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM1_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM1_CMSIS_VERSION ((__CM1_CMSIS_VERSION_MAIN << 16U) | \ + __CM1_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (1U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + This core does not support an FPU at all +*/ +#define __FPU_USED 0U + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_PCS_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM1_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM1_H_DEPENDANT +#define __CORE_CM1_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM1_REV + #define __CM1_REV 0x0100U + #warning "__CM1_REV not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 2U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M1 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:28; /*!< bit: 0..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t _reserved1:3; /*!< bit: 25..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t _reserved0:1; /*!< bit: 0 Reserved */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[1U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[31U]; + __IOM uint32_t ICER[1U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[31U]; + __IOM uint32_t ISPR[1U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[31U]; + __IOM uint32_t ICPR[1U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[31U]; + uint32_t RESERVED4[64U]; + __IOM uint32_t IP[8U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register */ +} NVIC_Type; + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + uint32_t RESERVED0; + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + uint32_t RESERVED1; + __IOM uint32_t SHP[2U]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +} SCnSCB_Type; + +/* Auxiliary Control Register Definitions */ +#define SCnSCB_ACTLR_ITCMUAEN_Pos 4U /*!< ACTLR: Instruction TCM Upper Alias Enable Position */ +#define SCnSCB_ACTLR_ITCMUAEN_Msk (1UL << SCnSCB_ACTLR_ITCMUAEN_Pos) /*!< ACTLR: Instruction TCM Upper Alias Enable Mask */ + +#define SCnSCB_ACTLR_ITCMLAEN_Pos 3U /*!< ACTLR: Instruction TCM Lower Alias Enable Position */ +#define SCnSCB_ACTLR_ITCMLAEN_Msk (1UL << SCnSCB_ACTLR_ITCMLAEN_Pos) /*!< ACTLR: Instruction TCM Lower Alias Enable Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Cortex-M1 Core Debug Registers (DCB registers, SHCSR, and DFSR) are only accessible over DAP and not via processor. + Therefore they are not covered by the Cortex-M1 header file. + @{ + */ +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ +/*#define NVIC_GetActive __NVIC_GetActive not available for Cortex-M1 */ + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ +/* The following MACROS handle generation of the register offset and byte masks */ +#define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) +#define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) +#define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) + +#define __NVIC_SetPriorityGrouping(X) (void)(X) +#define __NVIC_GetPriorityGrouping() (0U) + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } + else + { + SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + Address 0 must be mapped to SRAM. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)0x0U; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)0x0U; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + SCB_AIRCR_SYSRESETREQ_Msk); + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM1_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/Firmware/ThirdParty/CMSIS/Include/core_cm23.h b/Firmware/ThirdParty/CMSIS/Include/core_cm23.h new file mode 100644 index 000000000..acbc5dfea --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm23.h @@ -0,0 +1,1993 @@ +/**************************************************************************//** + * @file core_cm23.h + * @brief CMSIS Cortex-M23 Core Peripheral Access Layer Header File + * @version V5.0.7 + * @date 22. June 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM23_H_GENERIC +#define __CORE_CM23_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M23 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS definitions */ +#define __CM23_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM23_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM23_CMSIS_VERSION ((__CM23_CMSIS_VERSION_MAIN << 16U) | \ + __CM23_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (23U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + This core does not support an FPU at all +*/ +#define __FPU_USED 0U + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_PCS_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM23_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM23_H_DEPENDANT +#define __CORE_CM23_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM23_REV + #define __CM23_REV 0x0000U + #warning "__CM23_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __SAUREGION_PRESENT + #define __SAUREGION_PRESENT 0U + #warning "__SAUREGION_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __VTOR_PRESENT + #define __VTOR_PRESENT 0U + #warning "__VTOR_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 2U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif + + #ifndef __ETM_PRESENT + #define __ETM_PRESENT 0U + #warning "__ETM_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MTB_PRESENT + #define __MTB_PRESENT 0U + #warning "__MTB_PRESENT not defined in device header file; using default!" + #endif + +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M23 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core SAU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:28; /*!< bit: 0..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t _reserved1:3; /*!< bit: 25..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack-pointer select */ + uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[16U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[16U]; + __IOM uint32_t ICER[16U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[16U]; + __IOM uint32_t ISPR[16U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[16U]; + __IOM uint32_t ICPR[16U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[16U]; + __IOM uint32_t IABR[16U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[16U]; + __IOM uint32_t ITNS[16U]; /*!< Offset: 0x280 (R/W) Interrupt Non-Secure State Register */ + uint32_t RESERVED5[16U]; + __IOM uint32_t IPR[124U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register */ +} NVIC_Type; + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ +#else + uint32_t RESERVED0; +#endif + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + uint32_t RESERVED1; + __IOM uint32_t SHPR[2U]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_PENDNMISET_Pos 31U /*!< SCB ICSR: PENDNMISET Position */ +#define SCB_ICSR_PENDNMISET_Msk (1UL << SCB_ICSR_PENDNMISET_Pos) /*!< SCB ICSR: PENDNMISET Mask */ + +#define SCB_ICSR_NMIPENDSET_Pos SCB_ICSR_PENDNMISET_Pos /*!< SCB ICSR: NMIPENDSET Position, backward compatibility */ +#define SCB_ICSR_NMIPENDSET_Msk SCB_ICSR_PENDNMISET_Msk /*!< SCB ICSR: NMIPENDSET Mask, backward compatibility */ + +#define SCB_ICSR_PENDNMICLR_Pos 30U /*!< SCB ICSR: PENDNMICLR Position */ +#define SCB_ICSR_PENDNMICLR_Msk (1UL << SCB_ICSR_PENDNMICLR_Pos) /*!< SCB ICSR: PENDNMICLR Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_STTNS_Pos 24U /*!< SCB ICSR: STTNS Position (Security Extension) */ +#define SCB_ICSR_STTNS_Msk (1UL << SCB_ICSR_STTNS_Pos) /*!< SCB ICSR: STTNS Mask (Security Extension) */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#endif + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIS_Pos 14U /*!< SCB AIRCR: PRIS Position */ +#define SCB_AIRCR_PRIS_Msk (1UL << SCB_AIRCR_PRIS_Pos) /*!< SCB AIRCR: PRIS Mask */ + +#define SCB_AIRCR_BFHFNMINS_Pos 13U /*!< SCB AIRCR: BFHFNMINS Position */ +#define SCB_AIRCR_BFHFNMINS_Msk (1UL << SCB_AIRCR_BFHFNMINS_Pos) /*!< SCB AIRCR: BFHFNMINS Mask */ + +#define SCB_AIRCR_SYSRESETREQS_Pos 3U /*!< SCB AIRCR: SYSRESETREQS Position */ +#define SCB_AIRCR_SYSRESETREQS_Msk (1UL << SCB_AIRCR_SYSRESETREQS_Pos) /*!< SCB AIRCR: SYSRESETREQS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEPS_Pos 3U /*!< SCB SCR: SLEEPDEEPS Position */ +#define SCB_SCR_SLEEPDEEPS_Msk (1UL << SCB_SCR_SLEEPDEEPS_Pos) /*!< SCB SCR: SLEEPDEEPS Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_BP_Pos 18U /*!< SCB CCR: BP Position */ +#define SCB_CCR_BP_Msk (1UL << SCB_CCR_BP_Pos) /*!< SCB CCR: BP Mask */ + +#define SCB_CCR_IC_Pos 17U /*!< SCB CCR: IC Position */ +#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos) /*!< SCB CCR: IC Mask */ + +#define SCB_CCR_DC_Pos 16U /*!< SCB CCR: DC Position */ +#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos) /*!< SCB CCR: DC Mask */ + +#define SCB_CCR_STKOFHFNMIGN_Pos 10U /*!< SCB CCR: STKOFHFNMIGN Position */ +#define SCB_CCR_STKOFHFNMIGN_Msk (1UL << SCB_CCR_STKOFHFNMIGN_Pos) /*!< SCB CCR: STKOFHFNMIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_HARDFAULTPENDED_Pos 21U /*!< SCB SHCSR: HARDFAULTPENDED Position */ +#define SCB_SHCSR_HARDFAULTPENDED_Msk (1UL << SCB_SHCSR_HARDFAULTPENDED_Pos) /*!< SCB SHCSR: HARDFAULTPENDED Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_NMIACT_Pos 5U /*!< SCB SHCSR: NMIACT Position */ +#define SCB_SHCSR_NMIACT_Msk (1UL << SCB_SHCSR_NMIACT_Pos) /*!< SCB SHCSR: NMIACT Mask */ + +#define SCB_SHCSR_HARDFAULTACT_Pos 2U /*!< SCB SHCSR: HARDFAULTACT Position */ +#define SCB_SHCSR_HARDFAULTACT_Msk (1UL << SCB_SHCSR_HARDFAULTACT_Pos) /*!< SCB SHCSR: HARDFAULTACT Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + uint32_t RESERVED0[6U]; + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + uint32_t RESERVED3[1U]; + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED4[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + uint32_t RESERVED5[1U]; + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED6[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + uint32_t RESERVED7[1U]; + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ + uint32_t RESERVED8[1U]; + __IOM uint32_t COMP4; /*!< Offset: 0x060 (R/W) Comparator Register 4 */ + uint32_t RESERVED9[1U]; + __IOM uint32_t FUNCTION4; /*!< Offset: 0x068 (R/W) Function Register 4 */ + uint32_t RESERVED10[1U]; + __IOM uint32_t COMP5; /*!< Offset: 0x070 (R/W) Comparator Register 5 */ + uint32_t RESERVED11[1U]; + __IOM uint32_t FUNCTION5; /*!< Offset: 0x078 (R/W) Function Register 5 */ + uint32_t RESERVED12[1U]; + __IOM uint32_t COMP6; /*!< Offset: 0x080 (R/W) Comparator Register 6 */ + uint32_t RESERVED13[1U]; + __IOM uint32_t FUNCTION6; /*!< Offset: 0x088 (R/W) Function Register 6 */ + uint32_t RESERVED14[1U]; + __IOM uint32_t COMP7; /*!< Offset: 0x090 (R/W) Comparator Register 7 */ + uint32_t RESERVED15[1U]; + __IOM uint32_t FUNCTION7; /*!< Offset: 0x098 (R/W) Function Register 7 */ + uint32_t RESERVED16[1U]; + __IOM uint32_t COMP8; /*!< Offset: 0x0A0 (R/W) Comparator Register 8 */ + uint32_t RESERVED17[1U]; + __IOM uint32_t FUNCTION8; /*!< Offset: 0x0A8 (R/W) Function Register 8 */ + uint32_t RESERVED18[1U]; + __IOM uint32_t COMP9; /*!< Offset: 0x0B0 (R/W) Comparator Register 9 */ + uint32_t RESERVED19[1U]; + __IOM uint32_t FUNCTION9; /*!< Offset: 0x0B8 (R/W) Function Register 9 */ + uint32_t RESERVED20[1U]; + __IOM uint32_t COMP10; /*!< Offset: 0x0C0 (R/W) Comparator Register 10 */ + uint32_t RESERVED21[1U]; + __IOM uint32_t FUNCTION10; /*!< Offset: 0x0C8 (R/W) Function Register 10 */ + uint32_t RESERVED22[1U]; + __IOM uint32_t COMP11; /*!< Offset: 0x0D0 (R/W) Comparator Register 11 */ + uint32_t RESERVED23[1U]; + __IOM uint32_t FUNCTION11; /*!< Offset: 0x0D8 (R/W) Function Register 11 */ + uint32_t RESERVED24[1U]; + __IOM uint32_t COMP12; /*!< Offset: 0x0E0 (R/W) Comparator Register 12 */ + uint32_t RESERVED25[1U]; + __IOM uint32_t FUNCTION12; /*!< Offset: 0x0E8 (R/W) Function Register 12 */ + uint32_t RESERVED26[1U]; + __IOM uint32_t COMP13; /*!< Offset: 0x0F0 (R/W) Comparator Register 13 */ + uint32_t RESERVED27[1U]; + __IOM uint32_t FUNCTION13; /*!< Offset: 0x0F8 (R/W) Function Register 13 */ + uint32_t RESERVED28[1U]; + __IOM uint32_t COMP14; /*!< Offset: 0x100 (R/W) Comparator Register 14 */ + uint32_t RESERVED29[1U]; + __IOM uint32_t FUNCTION14; /*!< Offset: 0x108 (R/W) Function Register 14 */ + uint32_t RESERVED30[1U]; + __IOM uint32_t COMP15; /*!< Offset: 0x110 (R/W) Comparator Register 15 */ + uint32_t RESERVED31[1U]; + __IOM uint32_t FUNCTION15; /*!< Offset: 0x118 (R/W) Function Register 15 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_ID_Pos 27U /*!< DWT FUNCTION: ID Position */ +#define DWT_FUNCTION_ID_Msk (0x1FUL << DWT_FUNCTION_ID_Pos) /*!< DWT FUNCTION: ID Mask */ + +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_ACTION_Pos 4U /*!< DWT FUNCTION: ACTION Position */ +#define DWT_FUNCTION_ACTION_Msk (0x3UL << DWT_FUNCTION_ACTION_Pos) /*!< DWT FUNCTION: ACTION Mask */ + +#define DWT_FUNCTION_MATCH_Pos 0U /*!< DWT FUNCTION: MATCH Position */ +#define DWT_FUNCTION_MATCH_Msk (0xFUL /*<< DWT_FUNCTION_MATCH_Pos*/) /*!< DWT FUNCTION: MATCH Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IOM uint32_t PSCR; /*!< Offset: 0x308 (R/W) Periodic Synchronization Control Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ + __IM uint32_t ITFTTD0; /*!< Offset: 0xEEC (R/ ) Integration Test FIFO Test Data 0 Register */ + __IOM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/W) Integration Test ATB Control Register 2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) Integration Test ATB Control Register 0 */ + __IM uint32_t ITFTTD1; /*!< Offset: 0xEFC (R/ ) Integration Test FIFO Test Data 1 Register */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) Device Configuration Register */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) Device Type Identifier Register */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_FOnMan_Pos 6U /*!< TPI FFCR: FOnMan Position */ +#define TPI_FFCR_FOnMan_Msk (0x1UL << TPI_FFCR_FOnMan_Pos) /*!< TPI FFCR: FOnMan Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration Test FIFO Test Data 0 Register Definitions */ +#define TPI_ITFTTD0_ATB_IF2_ATVALID_Pos 29U /*!< TPI ITFTTD0: ATB Interface 2 ATVALIDPosition */ +#define TPI_ITFTTD0_ATB_IF2_ATVALID_Msk (0x3UL << TPI_ITFTTD0_ATB_IF2_ATVALID_Pos) /*!< TPI ITFTTD0: ATB Interface 2 ATVALID Mask */ + +#define TPI_ITFTTD0_ATB_IF2_bytecount_Pos 27U /*!< TPI ITFTTD0: ATB Interface 2 byte count Position */ +#define TPI_ITFTTD0_ATB_IF2_bytecount_Msk (0x3UL << TPI_ITFTTD0_ATB_IF2_bytecount_Pos) /*!< TPI ITFTTD0: ATB Interface 2 byte count Mask */ + +#define TPI_ITFTTD0_ATB_IF1_ATVALID_Pos 26U /*!< TPI ITFTTD0: ATB Interface 1 ATVALID Position */ +#define TPI_ITFTTD0_ATB_IF1_ATVALID_Msk (0x3UL << TPI_ITFTTD0_ATB_IF1_ATVALID_Pos) /*!< TPI ITFTTD0: ATB Interface 1 ATVALID Mask */ + +#define TPI_ITFTTD0_ATB_IF1_bytecount_Pos 24U /*!< TPI ITFTTD0: ATB Interface 1 byte count Position */ +#define TPI_ITFTTD0_ATB_IF1_bytecount_Msk (0x3UL << TPI_ITFTTD0_ATB_IF1_bytecount_Pos) /*!< TPI ITFTTD0: ATB Interface 1 byte countt Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data2_Pos 16U /*!< TPI ITFTTD0: ATB Interface 1 data2 Position */ +#define TPI_ITFTTD0_ATB_IF1_data2_Msk (0xFFUL << TPI_ITFTTD0_ATB_IF1_data1_Pos) /*!< TPI ITFTTD0: ATB Interface 1 data2 Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data1_Pos 8U /*!< TPI ITFTTD0: ATB Interface 1 data1 Position */ +#define TPI_ITFTTD0_ATB_IF1_data1_Msk (0xFFUL << TPI_ITFTTD0_ATB_IF1_data1_Pos) /*!< TPI ITFTTD0: ATB Interface 1 data1 Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data0_Pos 0U /*!< TPI ITFTTD0: ATB Interface 1 data0 Position */ +#define TPI_ITFTTD0_ATB_IF1_data0_Msk (0xFFUL /*<< TPI_ITFTTD0_ATB_IF1_data0_Pos*/) /*!< TPI ITFTTD0: ATB Interface 1 data0 Mask */ + +/* TPI Integration Test ATB Control Register 2 Register Definitions */ +#define TPI_ITATBCTR2_AFVALID2S_Pos 1U /*!< TPI ITATBCTR2: AFVALID2S Position */ +#define TPI_ITATBCTR2_AFVALID2S_Msk (0x1UL << TPI_ITATBCTR2_AFVALID2S_Pos) /*!< TPI ITATBCTR2: AFVALID2SS Mask */ + +#define TPI_ITATBCTR2_AFVALID1S_Pos 1U /*!< TPI ITATBCTR2: AFVALID1S Position */ +#define TPI_ITATBCTR2_AFVALID1S_Msk (0x1UL << TPI_ITATBCTR2_AFVALID1S_Pos) /*!< TPI ITATBCTR2: AFVALID1SS Mask */ + +#define TPI_ITATBCTR2_ATREADY2S_Pos 0U /*!< TPI ITATBCTR2: ATREADY2S Position */ +#define TPI_ITATBCTR2_ATREADY2S_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2S_Pos*/) /*!< TPI ITATBCTR2: ATREADY2S Mask */ + +#define TPI_ITATBCTR2_ATREADY1S_Pos 0U /*!< TPI ITATBCTR2: ATREADY1S Position */ +#define TPI_ITATBCTR2_ATREADY1S_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1S_Pos*/) /*!< TPI ITATBCTR2: ATREADY1S Mask */ + +/* TPI Integration Test FIFO Test Data 1 Register Definitions */ +#define TPI_ITFTTD1_ATB_IF2_ATVALID_Pos 29U /*!< TPI ITFTTD1: ATB Interface 2 ATVALID Position */ +#define TPI_ITFTTD1_ATB_IF2_ATVALID_Msk (0x3UL << TPI_ITFTTD1_ATB_IF2_ATVALID_Pos) /*!< TPI ITFTTD1: ATB Interface 2 ATVALID Mask */ + +#define TPI_ITFTTD1_ATB_IF2_bytecount_Pos 27U /*!< TPI ITFTTD1: ATB Interface 2 byte count Position */ +#define TPI_ITFTTD1_ATB_IF2_bytecount_Msk (0x3UL << TPI_ITFTTD1_ATB_IF2_bytecount_Pos) /*!< TPI ITFTTD1: ATB Interface 2 byte count Mask */ + +#define TPI_ITFTTD1_ATB_IF1_ATVALID_Pos 26U /*!< TPI ITFTTD1: ATB Interface 1 ATVALID Position */ +#define TPI_ITFTTD1_ATB_IF1_ATVALID_Msk (0x3UL << TPI_ITFTTD1_ATB_IF1_ATVALID_Pos) /*!< TPI ITFTTD1: ATB Interface 1 ATVALID Mask */ + +#define TPI_ITFTTD1_ATB_IF1_bytecount_Pos 24U /*!< TPI ITFTTD1: ATB Interface 1 byte count Position */ +#define TPI_ITFTTD1_ATB_IF1_bytecount_Msk (0x3UL << TPI_ITFTTD1_ATB_IF1_bytecount_Pos) /*!< TPI ITFTTD1: ATB Interface 1 byte countt Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data2_Pos 16U /*!< TPI ITFTTD1: ATB Interface 2 data2 Position */ +#define TPI_ITFTTD1_ATB_IF2_data2_Msk (0xFFUL << TPI_ITFTTD1_ATB_IF2_data1_Pos) /*!< TPI ITFTTD1: ATB Interface 2 data2 Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data1_Pos 8U /*!< TPI ITFTTD1: ATB Interface 2 data1 Position */ +#define TPI_ITFTTD1_ATB_IF2_data1_Msk (0xFFUL << TPI_ITFTTD1_ATB_IF2_data1_Pos) /*!< TPI ITFTTD1: ATB Interface 2 data1 Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data0_Pos 0U /*!< TPI ITFTTD1: ATB Interface 2 data0 Position */ +#define TPI_ITFTTD1_ATB_IF2_data0_Msk (0xFFUL /*<< TPI_ITFTTD1_ATB_IF2_data0_Pos*/) /*!< TPI ITFTTD1: ATB Interface 2 data0 Mask */ + +/* TPI Integration Test ATB Control Register 0 Definitions */ +#define TPI_ITATBCTR0_AFVALID2S_Pos 1U /*!< TPI ITATBCTR0: AFVALID2S Position */ +#define TPI_ITATBCTR0_AFVALID2S_Msk (0x1UL << TPI_ITATBCTR0_AFVALID2S_Pos) /*!< TPI ITATBCTR0: AFVALID2SS Mask */ + +#define TPI_ITATBCTR0_AFVALID1S_Pos 1U /*!< TPI ITATBCTR0: AFVALID1S Position */ +#define TPI_ITATBCTR0_AFVALID1S_Msk (0x1UL << TPI_ITATBCTR0_AFVALID1S_Pos) /*!< TPI ITATBCTR0: AFVALID1SS Mask */ + +#define TPI_ITATBCTR0_ATREADY2S_Pos 0U /*!< TPI ITATBCTR0: ATREADY2S Position */ +#define TPI_ITATBCTR0_ATREADY2S_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2S_Pos*/) /*!< TPI ITATBCTR0: ATREADY2S Mask */ + +#define TPI_ITATBCTR0_ATREADY1S_Pos 0U /*!< TPI ITATBCTR0: ATREADY1S Position */ +#define TPI_ITATBCTR0_ATREADY1S_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1S_Pos*/) /*!< TPI ITATBCTR0: ATREADY1S Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_FIFOSZ_Pos 6U /*!< TPI DEVID: FIFOSZ Position */ +#define TPI_DEVID_FIFOSZ_Msk (0x7UL << TPI_DEVID_FIFOSZ_Pos) /*!< TPI DEVID: FIFOSZ Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x3FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) MPU Region Limit Address Register */ + uint32_t RESERVED0[7U]; + union { + __IOM uint32_t MAIR[2]; + struct { + __IOM uint32_t MAIR0; /*!< Offset: 0x030 (R/W) MPU Memory Attribute Indirection Register 0 */ + __IOM uint32_t MAIR1; /*!< Offset: 0x034 (R/W) MPU Memory Attribute Indirection Register 1 */ + }; + }; +} MPU_Type; + +#define MPU_TYPE_RALIASES 1U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_BASE_Pos 5U /*!< MPU RBAR: BASE Position */ +#define MPU_RBAR_BASE_Msk (0x7FFFFFFUL << MPU_RBAR_BASE_Pos) /*!< MPU RBAR: BASE Mask */ + +#define MPU_RBAR_SH_Pos 3U /*!< MPU RBAR: SH Position */ +#define MPU_RBAR_SH_Msk (0x3UL << MPU_RBAR_SH_Pos) /*!< MPU RBAR: SH Mask */ + +#define MPU_RBAR_AP_Pos 1U /*!< MPU RBAR: AP Position */ +#define MPU_RBAR_AP_Msk (0x3UL << MPU_RBAR_AP_Pos) /*!< MPU RBAR: AP Mask */ + +#define MPU_RBAR_XN_Pos 0U /*!< MPU RBAR: XN Position */ +#define MPU_RBAR_XN_Msk (01UL /*<< MPU_RBAR_XN_Pos*/) /*!< MPU RBAR: XN Mask */ + +/* MPU Region Limit Address Register Definitions */ +#define MPU_RLAR_LIMIT_Pos 5U /*!< MPU RLAR: LIMIT Position */ +#define MPU_RLAR_LIMIT_Msk (0x7FFFFFFUL << MPU_RLAR_LIMIT_Pos) /*!< MPU RLAR: LIMIT Mask */ + +#define MPU_RLAR_AttrIndx_Pos 1U /*!< MPU RLAR: AttrIndx Position */ +#define MPU_RLAR_AttrIndx_Msk (0x7UL << MPU_RLAR_AttrIndx_Pos) /*!< MPU RLAR: AttrIndx Mask */ + +#define MPU_RLAR_EN_Pos 0U /*!< MPU RLAR: EN Position */ +#define MPU_RLAR_EN_Msk (1UL /*<< MPU_RLAR_EN_Pos*/) /*!< MPU RLAR: EN Mask */ + +/* MPU Memory Attribute Indirection Register 0 Definitions */ +#define MPU_MAIR0_Attr3_Pos 24U /*!< MPU MAIR0: Attr3 Position */ +#define MPU_MAIR0_Attr3_Msk (0xFFUL << MPU_MAIR0_Attr3_Pos) /*!< MPU MAIR0: Attr3 Mask */ + +#define MPU_MAIR0_Attr2_Pos 16U /*!< MPU MAIR0: Attr2 Position */ +#define MPU_MAIR0_Attr2_Msk (0xFFUL << MPU_MAIR0_Attr2_Pos) /*!< MPU MAIR0: Attr2 Mask */ + +#define MPU_MAIR0_Attr1_Pos 8U /*!< MPU MAIR0: Attr1 Position */ +#define MPU_MAIR0_Attr1_Msk (0xFFUL << MPU_MAIR0_Attr1_Pos) /*!< MPU MAIR0: Attr1 Mask */ + +#define MPU_MAIR0_Attr0_Pos 0U /*!< MPU MAIR0: Attr0 Position */ +#define MPU_MAIR0_Attr0_Msk (0xFFUL /*<< MPU_MAIR0_Attr0_Pos*/) /*!< MPU MAIR0: Attr0 Mask */ + +/* MPU Memory Attribute Indirection Register 1 Definitions */ +#define MPU_MAIR1_Attr7_Pos 24U /*!< MPU MAIR1: Attr7 Position */ +#define MPU_MAIR1_Attr7_Msk (0xFFUL << MPU_MAIR1_Attr7_Pos) /*!< MPU MAIR1: Attr7 Mask */ + +#define MPU_MAIR1_Attr6_Pos 16U /*!< MPU MAIR1: Attr6 Position */ +#define MPU_MAIR1_Attr6_Msk (0xFFUL << MPU_MAIR1_Attr6_Pos) /*!< MPU MAIR1: Attr6 Mask */ + +#define MPU_MAIR1_Attr5_Pos 8U /*!< MPU MAIR1: Attr5 Position */ +#define MPU_MAIR1_Attr5_Msk (0xFFUL << MPU_MAIR1_Attr5_Pos) /*!< MPU MAIR1: Attr5 Mask */ + +#define MPU_MAIR1_Attr4_Pos 0U /*!< MPU MAIR1: Attr4 Position */ +#define MPU_MAIR1_Attr4_Msk (0xFFUL /*<< MPU_MAIR1_Attr4_Pos*/) /*!< MPU MAIR1: Attr4 Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SAU Security Attribution Unit (SAU) + \brief Type definitions for the Security Attribution Unit (SAU) + @{ + */ + +/** + \brief Structure type to access the Security Attribution Unit (SAU). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SAU Control Register */ + __IM uint32_t TYPE; /*!< Offset: 0x004 (R/ ) SAU Type Register */ +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) SAU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) SAU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) SAU Region Limit Address Register */ +#endif +} SAU_Type; + +/* SAU Control Register Definitions */ +#define SAU_CTRL_ALLNS_Pos 1U /*!< SAU CTRL: ALLNS Position */ +#define SAU_CTRL_ALLNS_Msk (1UL << SAU_CTRL_ALLNS_Pos) /*!< SAU CTRL: ALLNS Mask */ + +#define SAU_CTRL_ENABLE_Pos 0U /*!< SAU CTRL: ENABLE Position */ +#define SAU_CTRL_ENABLE_Msk (1UL /*<< SAU_CTRL_ENABLE_Pos*/) /*!< SAU CTRL: ENABLE Mask */ + +/* SAU Type Register Definitions */ +#define SAU_TYPE_SREGION_Pos 0U /*!< SAU TYPE: SREGION Position */ +#define SAU_TYPE_SREGION_Msk (0xFFUL /*<< SAU_TYPE_SREGION_Pos*/) /*!< SAU TYPE: SREGION Mask */ + +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) +/* SAU Region Number Register Definitions */ +#define SAU_RNR_REGION_Pos 0U /*!< SAU RNR: REGION Position */ +#define SAU_RNR_REGION_Msk (0xFFUL /*<< SAU_RNR_REGION_Pos*/) /*!< SAU RNR: REGION Mask */ + +/* SAU Region Base Address Register Definitions */ +#define SAU_RBAR_BADDR_Pos 5U /*!< SAU RBAR: BADDR Position */ +#define SAU_RBAR_BADDR_Msk (0x7FFFFFFUL << SAU_RBAR_BADDR_Pos) /*!< SAU RBAR: BADDR Mask */ + +/* SAU Region Limit Address Register Definitions */ +#define SAU_RLAR_LADDR_Pos 5U /*!< SAU RLAR: LADDR Position */ +#define SAU_RLAR_LADDR_Msk (0x7FFFFFFUL << SAU_RLAR_LADDR_Pos) /*!< SAU RLAR: LADDR Mask */ + +#define SAU_RLAR_NSC_Pos 1U /*!< SAU RLAR: NSC Position */ +#define SAU_RLAR_NSC_Msk (1UL << SAU_RLAR_NSC_Pos) /*!< SAU RLAR: NSC Mask */ + +#define SAU_RLAR_ENABLE_Pos 0U /*!< SAU RLAR: ENABLE Position */ +#define SAU_RLAR_ENABLE_Msk (1UL /*<< SAU_RLAR_ENABLE_Pos*/) /*!< SAU RLAR: ENABLE Mask */ + +#endif /* defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) */ + +/*@} end of group CMSIS_SAU */ +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ + uint32_t RESERVED4[1U]; + __IOM uint32_t DAUTHCTRL; /*!< Offset: 0x014 (R/W) Debug Authentication Control Register */ + __IOM uint32_t DSCSR; /*!< Offset: 0x018 (R/W) Debug Security Control and Status Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESTART_ST_Pos 26U /*!< CoreDebug DHCSR: S_RESTART_ST Position */ +#define CoreDebug_DHCSR_S_RESTART_ST_Msk (1UL << CoreDebug_DHCSR_S_RESTART_ST_Pos) /*!< CoreDebug DHCSR: S_RESTART_ST Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register */ +#define CoreDebug_DEMCR_DWTENA_Pos 24U /*!< CoreDebug DEMCR: DWTENA Position */ +#define CoreDebug_DEMCR_DWTENA_Msk (1UL << CoreDebug_DEMCR_DWTENA_Pos) /*!< CoreDebug DEMCR: DWTENA Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/* Debug Authentication Control Register Definitions */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos 3U /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Position */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Mask */ + +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos 2U /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Msk (1UL << CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos) /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Mask */ + +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Pos 1U /*!< CoreDebug DAUTHCTRL: INTSPIDEN Position */ +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPIDEN Mask */ + +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Pos 0U /*!< CoreDebug DAUTHCTRL: SPIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Msk (1UL /*<< CoreDebug_DAUTHCTRL_SPIDENSEL_Pos*/) /*!< CoreDebug DAUTHCTRL: SPIDENSEL Mask */ + +/* Debug Security Control and Status Register Definitions */ +#define CoreDebug_DSCSR_CDS_Pos 16U /*!< CoreDebug DSCSR: CDS Position */ +#define CoreDebug_DSCSR_CDS_Msk (1UL << CoreDebug_DSCSR_CDS_Pos) /*!< CoreDebug DSCSR: CDS Mask */ + +#define CoreDebug_DSCSR_SBRSEL_Pos 1U /*!< CoreDebug DSCSR: SBRSEL Position */ +#define CoreDebug_DSCSR_SBRSEL_Msk (1UL << CoreDebug_DSCSR_SBRSEL_Pos) /*!< CoreDebug DSCSR: SBRSEL Mask */ + +#define CoreDebug_DSCSR_SBRSELEN_Pos 0U /*!< CoreDebug DSCSR: SBRSELEN Position */ +#define CoreDebug_DSCSR_SBRSELEN_Msk (1UL /*<< CoreDebug_DSCSR_SBRSELEN_Pos*/) /*!< CoreDebug DSCSR: SBRSELEN Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ + #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ + #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ + #define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ + #define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ + #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ + #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ + #define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + + + #define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ + #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ + #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + #define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ + #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ + #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE ) /*!< Core Debug configuration struct */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ + #endif + + #if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SAU_BASE (SCS_BASE + 0x0DD0UL) /*!< Security Attribution Unit */ + #define SAU ((SAU_Type *) SAU_BASE ) /*!< Security Attribution Unit */ + #endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SCS_BASE_NS (0xE002E000UL) /*!< System Control Space Base Address (non-secure address space) */ + #define CoreDebug_BASE_NS (0xE002EDF0UL) /*!< Core Debug Base Address (non-secure address space) */ + #define SysTick_BASE_NS (SCS_BASE_NS + 0x0010UL) /*!< SysTick Base Address (non-secure address space) */ + #define NVIC_BASE_NS (SCS_BASE_NS + 0x0100UL) /*!< NVIC Base Address (non-secure address space) */ + #define SCB_BASE_NS (SCS_BASE_NS + 0x0D00UL) /*!< System Control Block Base Address (non-secure address space) */ + + #define SCB_NS ((SCB_Type *) SCB_BASE_NS ) /*!< SCB configuration struct (non-secure address space) */ + #define SysTick_NS ((SysTick_Type *) SysTick_BASE_NS ) /*!< SysTick configuration struct (non-secure address space) */ + #define NVIC_NS ((NVIC_Type *) NVIC_BASE_NS ) /*!< NVIC configuration struct (non-secure address space) */ + #define CoreDebug_NS ((CoreDebug_Type *) CoreDebug_BASE_NS) /*!< Core Debug configuration struct (non-secure address space) */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE_NS (SCS_BASE_NS + 0x0D90UL) /*!< Memory Protection Unit (non-secure address space) */ + #define MPU_NS ((MPU_Type *) MPU_BASE_NS ) /*!< Memory Protection Unit (non-secure address space) */ + #endif + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else +/*#define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping not available for Cortex-M23 */ +/*#define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping not available for Cortex-M23 */ + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* Special LR values for Secure/Non-Secure call handling and exception handling */ + +/* Function Return Payload (from ARMv8-M Architecture Reference Manual) LR value on entry from Secure BLXNS */ +#define FNC_RETURN (0xFEFFFFFFUL) /* bit [0] ignored when processing a branch */ + +/* The following EXC_RETURN mask values are used to evaluate the LR on exception entry */ +#define EXC_RETURN_PREFIX (0xFF000000UL) /* bits [31:24] set to indicate an EXC_RETURN value */ +#define EXC_RETURN_S (0x00000040UL) /* bit [6] stack used to push registers: 0=Non-secure 1=Secure */ +#define EXC_RETURN_DCRS (0x00000020UL) /* bit [5] stacking rules for called registers: 0=skipped 1=saved */ +#define EXC_RETURN_FTYPE (0x00000010UL) /* bit [4] allocate stack for floating-point context: 0=done 1=skipped */ +#define EXC_RETURN_MODE (0x00000008UL) /* bit [3] processor mode for return: 0=Handler mode 1=Thread mode */ +#define EXC_RETURN_SPSEL (0x00000002UL) /* bit [1] stack pointer used to restore context: 0=MSP 1=PSP */ +#define EXC_RETURN_ES (0x00000001UL) /* bit [0] security state exception was taken to: 0=Non-secure 1=Secure */ + +/* Integrity Signature (from ARMv8-M Architecture Reference Manual) for exception context stacking */ +#if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) /* Value for processors with floating-point extension: */ +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125AUL) /* bit [0] SFTC must match LR bit[4] EXC_RETURN_FTYPE */ +#else +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125BUL) /* Value for processors without floating-point extension */ +#endif + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ +/* The following MACROS handle generation of the register offset and byte masks */ +#define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) +#define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) +#define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) + +#define __NVIC_SetPriorityGrouping(X) (void)(X) +#define __NVIC_GetPriorityGrouping() (0U) + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Get Interrupt Target State + \details Reads the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + \return 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_GetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Target State + \details Sets the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_SetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] |= ((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Clear Interrupt Target State + \details Clears the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_ClearTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] &= ~((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IPR[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IPR[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } + else + { + SCB->SHPR[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHPR[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IPR[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return((uint32_t)(((SCB->SHPR[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + If VTOR is not present address 0 must be mapped to SRAM. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + SCB_AIRCR_SYSRESETREQ_Msk); + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Enable Interrupt (non-secure) + \details Enables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_EnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status (non-secure) + \details Returns a device specific interrupt enable status from the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetEnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt (non-secure) + \details Disables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_DisableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Pending Interrupt (non-secure) + \details Reads the NVIC pending register in the non-secure NVIC when in secure state and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt (non-secure) + \details Sets the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_SetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt (non-secure) + \details Clears the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_ClearPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt (non-secure) + \details Reads the active register in non-secure NVIC when in secure state and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetActive_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority (non-secure) + \details Sets the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every non-secure processor exception. + */ +__STATIC_INLINE void TZ_NVIC_SetPriority_NS(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->IPR[_IP_IDX(IRQn)] = ((uint32_t)(NVIC_NS->IPR[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } + else + { + SCB_NS->SHPR[_SHP_IDX(IRQn)] = ((uint32_t)(SCB_NS->SHPR[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } +} + + +/** + \brief Get Interrupt Priority (non-secure) + \details Reads the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriority_NS(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IPR[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return((uint32_t)(((SCB_NS->SHPR[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) &&(__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_NVICFunctions */ + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv8.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ########################## SAU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SAUFunctions SAU Functions + \brief Functions that configure the SAU. + @{ + */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + +/** + \brief Enable SAU + \details Enables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Enable(void) +{ + SAU->CTRL |= (SAU_CTRL_ENABLE_Msk); +} + + + +/** + \brief Disable SAU + \details Disables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Disable(void) +{ + SAU->CTRL &= ~(SAU_CTRL_ENABLE_Msk); +} + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_SAUFunctions */ + + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief System Tick Configuration (non-secure) + \details Initializes the non-secure System Timer and its interrupt when in secure state, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function TZ_SysTick_Config_NS is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t TZ_SysTick_Config_NS(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick_NS->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + TZ_NVIC_SetPriority_NS (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick_NS->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick_NS->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM23_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm3.h b/Firmware/ThirdParty/CMSIS/Include/core_cm3.h similarity index 84% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_cm3.h rename to Firmware/ThirdParty/CMSIS/Include/core_cm3.h index b4ac4c7b0..74bff64be 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm3.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm3.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_cm3.h * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.8 + * @date 04. June 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,53 +60,15 @@ @{ */ +#include "cmsis_version.h" + /* CMSIS CM3 definitions */ -#define __CM3_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM3_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __CM3_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM3_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __CM3_CMSIS_VERSION ((__CM3_CMSIS_VERSION_MAIN << 16U) | \ - __CM3_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x03U) /*!< Cortex-M Core */ + __CM3_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_M (3U) /*!< Cortex-M Core */ /** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all @@ -128,7 +80,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -143,7 +95,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -160,8 +112,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -191,7 +143,7 @@ #endif #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 4U + #define __NVIC_PRIO_BITS 3U #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" #endif @@ -308,9 +260,11 @@ typedef union struct { uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ + uint32_t _reserved1:8; /*!< bit: 16..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ uint32_t C:1; /*!< bit: 29 Carry condition code flag */ @@ -336,12 +290,15 @@ typedef union #define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ #define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ #define xPSR_T_Pos 24U /*!< xPSR: T Position */ #define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + #define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ #define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ @@ -487,7 +444,7 @@ typedef struct #define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ /* SCB Vector Table Offset Register Definitions */ -#if (__CM3_REV < 0x0201U) /* core r2p1 */ +#if defined (__CM3_REV) && (__CM3_REV < 0x0201U) /* core r2p1 */ #define SCB_VTOR_TBLBASE_Pos 29U /*!< SCB VTOR: TBLBASE Position */ #define SCB_VTOR_TBLBASE_Msk (1UL << SCB_VTOR_TBLBASE_Pos) /*!< SCB VTOR: TBLBASE Mask */ @@ -602,6 +559,60 @@ typedef struct #define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ #define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + /* SCB Hard Fault Status Register Definitions */ #define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ #define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ @@ -645,7 +656,7 @@ typedef struct { uint32_t RESERVED0[1U]; __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ -#if ((defined __CM3_REV) && (__CM3_REV >= 0x200U)) +#if defined (__CM3_REV) && (__CM3_REV >= 0x200U) __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ #else uint32_t RESERVED1[1U]; @@ -770,7 +781,7 @@ typedef struct /* ITM Trace Privilege Register Definitions */ #define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ /* ITM Trace Control Register Definitions */ #define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ @@ -984,7 +995,7 @@ typedef struct */ typedef struct { - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ uint32_t RESERVED0[2U]; __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ @@ -995,7 +1006,7 @@ typedef struct __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ uint32_t RESERVED4[1U]; @@ -1065,8 +1076,11 @@ typedef struct #define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ /* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ /* TPI Integration ITM Data Register Definitions (FIFO1) */ #define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ @@ -1091,12 +1105,15 @@ typedef struct #define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ /* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ /* TPI Integration Mode Control Register Definitions */ #define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ /* TPI DEVID Register Definitions */ #define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ @@ -1118,16 +1135,16 @@ typedef struct #define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ /* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ #define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + /*@}*/ /* end of group CMSIS_TPI */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -1153,6 +1170,8 @@ typedef struct __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ } MPU_Type; +#define MPU_TYPE_RALIASES 4U + /* MPU Type Register Definitions */ #define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ #define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ @@ -1337,18 +1356,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -1360,7 +1379,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M3 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ @@ -1379,7 +1398,7 @@ typedef struct #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif @@ -1410,6 +1429,45 @@ typedef struct @{ */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + /** \brief Set Priority Grouping \details Sets the priority grouping field using the required unlock sequence. @@ -1419,7 +1477,7 @@ typedef struct priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. \param [in] PriorityGroup Priority grouping field. */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { uint32_t reg_value; uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ @@ -1428,7 +1486,7 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ reg_value = (reg_value | ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ SCB->AIRCR = reg_value; } @@ -1438,121 +1496,178 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) \details Reads the priority grouping field from the NVIC Interrupt Controller. \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) { return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); } /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not active. \return 1 Interrupt status is active. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } else { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } } /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); } else { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); } } @@ -1609,11 +1724,42 @@ __STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGr } +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -1630,6 +1776,38 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + /* ################################## SysTick function ############################################ */ @@ -1640,7 +1818,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration @@ -1683,8 +1861,8 @@ __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) @{ */ -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ /** diff --git a/Firmware/ThirdParty/CMSIS/Include/core_cm33.h b/Firmware/ThirdParty/CMSIS/Include/core_cm33.h new file mode 100644 index 000000000..6cd2db77f --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm33.h @@ -0,0 +1,3002 @@ +/**************************************************************************//** + * @file core_cm33.h + * @brief CMSIS Cortex-M33 Core Peripheral Access Layer Header File + * @version V5.0.9 + * @date 06. July 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM33_H_GENERIC +#define __CORE_CM33_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M33 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS CM33 definitions */ +#define __CM33_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM33_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM33_CMSIS_VERSION ((__CM33_CMSIS_VERSION_MAIN << 16U) | \ + __CM33_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (33U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined (__TARGET_FPU_VFP) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1U) + #if defined (__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined (__ARM_PCS_VFP) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1U) + #if defined (__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1U) + #if defined (__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __ICCARM__ ) + #if defined (__ARMVFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1U) + #if defined (__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __TI_ARM__ ) + #if defined (__TI_VFP_SUPPORT__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TASKING__ ) + #if defined (__FPU_VFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM33_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM33_H_DEPENDANT +#define __CORE_CM33_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM33_REV + #define __CM33_REV 0x0000U + #warning "__CM33_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __SAUREGION_PRESENT + #define __SAUREGION_PRESENT 0U + #warning "__SAUREGION_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __DSP_PRESENT + #define __DSP_PRESENT 0U + #warning "__DSP_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 3U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M33 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core SAU Register + - Core FPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + +#define APSR_GE_Pos 16U /*!< APSR: GE Position */ +#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ +#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ +#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack-pointer select */ + uint32_t FPCA:1; /*!< bit: 2 Floating-point context active */ + uint32_t SFPA:1; /*!< bit: 3 Secure floating-point active */ + uint32_t _reserved1:28; /*!< bit: 4..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SFPA_Pos 3U /*!< CONTROL: SFPA Position */ +#define CONTROL_SFPA_Msk (1UL << CONTROL_SFPA_Pos) /*!< CONTROL: SFPA Mask */ + +#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ +#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ + +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[16U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[16U]; + __IOM uint32_t ICER[16U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[16U]; + __IOM uint32_t ISPR[16U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[16U]; + __IOM uint32_t ICPR[16U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[16U]; + __IOM uint32_t IABR[16U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[16U]; + __IOM uint32_t ITNS[16U]; /*!< Offset: 0x280 (R/W) Interrupt Non-Secure State Register */ + uint32_t RESERVED5[16U]; + __IOM uint8_t IPR[496U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED6[580U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHPR[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t ID_PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t ID_DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ID_ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t ID_MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ID_ISAR[6U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + __IM uint32_t CLIDR; /*!< Offset: 0x078 (R/ ) Cache Level ID register */ + __IM uint32_t CTR; /*!< Offset: 0x07C (R/ ) Cache Type register */ + __IM uint32_t CCSIDR; /*!< Offset: 0x080 (R/ ) Cache Size ID Register */ + __IOM uint32_t CSSELR; /*!< Offset: 0x084 (R/W) Cache Size Selection Register */ + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ + __IOM uint32_t NSACR; /*!< Offset: 0x08C (R/W) Non-Secure Access Control Register */ + uint32_t RESERVED3[92U]; + __OM uint32_t STIR; /*!< Offset: 0x200 ( /W) Software Triggered Interrupt Register */ + uint32_t RESERVED4[15U]; + __IM uint32_t MVFR0; /*!< Offset: 0x240 (R/ ) Media and VFP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x244 (R/ ) Media and VFP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x248 (R/ ) Media and VFP Feature Register 2 */ + uint32_t RESERVED5[1U]; + __OM uint32_t ICIALLU; /*!< Offset: 0x250 ( /W) I-Cache Invalidate All to PoU */ + uint32_t RESERVED6[1U]; + __OM uint32_t ICIMVAU; /*!< Offset: 0x258 ( /W) I-Cache Invalidate by MVA to PoU */ + __OM uint32_t DCIMVAC; /*!< Offset: 0x25C ( /W) D-Cache Invalidate by MVA to PoC */ + __OM uint32_t DCISW; /*!< Offset: 0x260 ( /W) D-Cache Invalidate by Set-way */ + __OM uint32_t DCCMVAU; /*!< Offset: 0x264 ( /W) D-Cache Clean by MVA to PoU */ + __OM uint32_t DCCMVAC; /*!< Offset: 0x268 ( /W) D-Cache Clean by MVA to PoC */ + __OM uint32_t DCCSW; /*!< Offset: 0x26C ( /W) D-Cache Clean by Set-way */ + __OM uint32_t DCCIMVAC; /*!< Offset: 0x270 ( /W) D-Cache Clean and Invalidate by MVA to PoC */ + __OM uint32_t DCCISW; /*!< Offset: 0x274 ( /W) D-Cache Clean and Invalidate by Set-way */ + uint32_t RESERVED7[6U]; + __IOM uint32_t ITCMCR; /*!< Offset: 0x290 (R/W) Instruction Tightly-Coupled Memory Control Register */ + __IOM uint32_t DTCMCR; /*!< Offset: 0x294 (R/W) Data Tightly-Coupled Memory Control Registers */ + __IOM uint32_t AHBPCR; /*!< Offset: 0x298 (R/W) AHBP Control Register */ + __IOM uint32_t CACR; /*!< Offset: 0x29C (R/W) L1 Cache Control Register */ + __IOM uint32_t AHBSCR; /*!< Offset: 0x2A0 (R/W) AHB Slave Control Register */ + uint32_t RESERVED8[1U]; + __IOM uint32_t ABFSR; /*!< Offset: 0x2A8 (R/W) Auxiliary Bus Fault Status Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_PENDNMISET_Pos 31U /*!< SCB ICSR: PENDNMISET Position */ +#define SCB_ICSR_PENDNMISET_Msk (1UL << SCB_ICSR_PENDNMISET_Pos) /*!< SCB ICSR: PENDNMISET Mask */ + +#define SCB_ICSR_NMIPENDSET_Pos SCB_ICSR_PENDNMISET_Pos /*!< SCB ICSR: NMIPENDSET Position, backward compatibility */ +#define SCB_ICSR_NMIPENDSET_Msk SCB_ICSR_PENDNMISET_Msk /*!< SCB ICSR: NMIPENDSET Mask, backward compatibility */ + +#define SCB_ICSR_PENDNMICLR_Pos 30U /*!< SCB ICSR: PENDNMICLR Position */ +#define SCB_ICSR_PENDNMICLR_Msk (1UL << SCB_ICSR_PENDNMICLR_Pos) /*!< SCB ICSR: PENDNMICLR Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_STTNS_Pos 24U /*!< SCB ICSR: STTNS Position (Security Extension) */ +#define SCB_ICSR_STTNS_Msk (1UL << SCB_ICSR_STTNS_Pos) /*!< SCB ICSR: STTNS Mask (Security Extension) */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIS_Pos 14U /*!< SCB AIRCR: PRIS Position */ +#define SCB_AIRCR_PRIS_Msk (1UL << SCB_AIRCR_PRIS_Pos) /*!< SCB AIRCR: PRIS Mask */ + +#define SCB_AIRCR_BFHFNMINS_Pos 13U /*!< SCB AIRCR: BFHFNMINS Position */ +#define SCB_AIRCR_BFHFNMINS_Msk (1UL << SCB_AIRCR_BFHFNMINS_Pos) /*!< SCB AIRCR: BFHFNMINS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQS_Pos 3U /*!< SCB AIRCR: SYSRESETREQS Position */ +#define SCB_AIRCR_SYSRESETREQS_Msk (1UL << SCB_AIRCR_SYSRESETREQS_Pos) /*!< SCB AIRCR: SYSRESETREQS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEPS_Pos 3U /*!< SCB SCR: SLEEPDEEPS Position */ +#define SCB_SCR_SLEEPDEEPS_Msk (1UL << SCB_SCR_SLEEPDEEPS_Pos) /*!< SCB SCR: SLEEPDEEPS Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_BP_Pos 18U /*!< SCB CCR: BP Position */ +#define SCB_CCR_BP_Msk (1UL << SCB_CCR_BP_Pos) /*!< SCB CCR: BP Mask */ + +#define SCB_CCR_IC_Pos 17U /*!< SCB CCR: IC Position */ +#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos) /*!< SCB CCR: IC Mask */ + +#define SCB_CCR_DC_Pos 16U /*!< SCB CCR: DC Position */ +#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos) /*!< SCB CCR: DC Mask */ + +#define SCB_CCR_STKOFHFNMIGN_Pos 10U /*!< SCB CCR: STKOFHFNMIGN Position */ +#define SCB_CCR_STKOFHFNMIGN_Msk (1UL << SCB_CCR_STKOFHFNMIGN_Pos) /*!< SCB CCR: STKOFHFNMIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_HARDFAULTPENDED_Pos 21U /*!< SCB SHCSR: HARDFAULTPENDED Position */ +#define SCB_SHCSR_HARDFAULTPENDED_Msk (1UL << SCB_SHCSR_HARDFAULTPENDED_Pos) /*!< SCB SHCSR: HARDFAULTPENDED Mask */ + +#define SCB_SHCSR_SECUREFAULTPENDED_Pos 20U /*!< SCB SHCSR: SECUREFAULTPENDED Position */ +#define SCB_SHCSR_SECUREFAULTPENDED_Msk (1UL << SCB_SHCSR_SECUREFAULTPENDED_Pos) /*!< SCB SHCSR: SECUREFAULTPENDED Mask */ + +#define SCB_SHCSR_SECUREFAULTENA_Pos 19U /*!< SCB SHCSR: SECUREFAULTENA Position */ +#define SCB_SHCSR_SECUREFAULTENA_Msk (1UL << SCB_SHCSR_SECUREFAULTENA_Pos) /*!< SCB SHCSR: SECUREFAULTENA Mask */ + +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_NMIACT_Pos 5U /*!< SCB SHCSR: NMIACT Position */ +#define SCB_SHCSR_NMIACT_Msk (1UL << SCB_SHCSR_NMIACT_Pos) /*!< SCB SHCSR: NMIACT Mask */ + +#define SCB_SHCSR_SECUREFAULTACT_Pos 4U /*!< SCB SHCSR: SECUREFAULTACT Position */ +#define SCB_SHCSR_SECUREFAULTACT_Msk (1UL << SCB_SHCSR_SECUREFAULTACT_Pos) /*!< SCB SHCSR: SECUREFAULTACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_HARDFAULTACT_Pos 2U /*!< SCB SHCSR: HARDFAULTACT Position */ +#define SCB_SHCSR_HARDFAULTACT_Msk (1UL << SCB_SHCSR_HARDFAULTACT_Pos) /*!< SCB SHCSR: HARDFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_STKOF_Pos (SCB_CFSR_USGFAULTSR_Pos + 4U) /*!< SCB CFSR (UFSR): STKOF Position */ +#define SCB_CFSR_STKOF_Msk (1UL << SCB_CFSR_STKOF_Pos) /*!< SCB CFSR (UFSR): STKOF Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/* SCB Non-Secure Access Control Register Definitions */ +#define SCB_NSACR_CP11_Pos 11U /*!< SCB NSACR: CP11 Position */ +#define SCB_NSACR_CP11_Msk (1UL << SCB_NSACR_CP11_Pos) /*!< SCB NSACR: CP11 Mask */ + +#define SCB_NSACR_CP10_Pos 10U /*!< SCB NSACR: CP10 Position */ +#define SCB_NSACR_CP10_Msk (1UL << SCB_NSACR_CP10_Pos) /*!< SCB NSACR: CP10 Mask */ + +#define SCB_NSACR_CPn_Pos 0U /*!< SCB NSACR: CPn Position */ +#define SCB_NSACR_CPn_Msk (1UL /*<< SCB_NSACR_CPn_Pos*/) /*!< SCB NSACR: CPn Mask */ + +/* SCB Cache Level ID Register Definitions */ +#define SCB_CLIDR_LOUU_Pos 27U /*!< SCB CLIDR: LoUU Position */ +#define SCB_CLIDR_LOUU_Msk (7UL << SCB_CLIDR_LOUU_Pos) /*!< SCB CLIDR: LoUU Mask */ + +#define SCB_CLIDR_LOC_Pos 24U /*!< SCB CLIDR: LoC Position */ +#define SCB_CLIDR_LOC_Msk (7UL << SCB_CLIDR_LOC_Pos) /*!< SCB CLIDR: LoC Mask */ + +/* SCB Cache Type Register Definitions */ +#define SCB_CTR_FORMAT_Pos 29U /*!< SCB CTR: Format Position */ +#define SCB_CTR_FORMAT_Msk (7UL << SCB_CTR_FORMAT_Pos) /*!< SCB CTR: Format Mask */ + +#define SCB_CTR_CWG_Pos 24U /*!< SCB CTR: CWG Position */ +#define SCB_CTR_CWG_Msk (0xFUL << SCB_CTR_CWG_Pos) /*!< SCB CTR: CWG Mask */ + +#define SCB_CTR_ERG_Pos 20U /*!< SCB CTR: ERG Position */ +#define SCB_CTR_ERG_Msk (0xFUL << SCB_CTR_ERG_Pos) /*!< SCB CTR: ERG Mask */ + +#define SCB_CTR_DMINLINE_Pos 16U /*!< SCB CTR: DminLine Position */ +#define SCB_CTR_DMINLINE_Msk (0xFUL << SCB_CTR_DMINLINE_Pos) /*!< SCB CTR: DminLine Mask */ + +#define SCB_CTR_IMINLINE_Pos 0U /*!< SCB CTR: ImInLine Position */ +#define SCB_CTR_IMINLINE_Msk (0xFUL /*<< SCB_CTR_IMINLINE_Pos*/) /*!< SCB CTR: ImInLine Mask */ + +/* SCB Cache Size ID Register Definitions */ +#define SCB_CCSIDR_WT_Pos 31U /*!< SCB CCSIDR: WT Position */ +#define SCB_CCSIDR_WT_Msk (1UL << SCB_CCSIDR_WT_Pos) /*!< SCB CCSIDR: WT Mask */ + +#define SCB_CCSIDR_WB_Pos 30U /*!< SCB CCSIDR: WB Position */ +#define SCB_CCSIDR_WB_Msk (1UL << SCB_CCSIDR_WB_Pos) /*!< SCB CCSIDR: WB Mask */ + +#define SCB_CCSIDR_RA_Pos 29U /*!< SCB CCSIDR: RA Position */ +#define SCB_CCSIDR_RA_Msk (1UL << SCB_CCSIDR_RA_Pos) /*!< SCB CCSIDR: RA Mask */ + +#define SCB_CCSIDR_WA_Pos 28U /*!< SCB CCSIDR: WA Position */ +#define SCB_CCSIDR_WA_Msk (1UL << SCB_CCSIDR_WA_Pos) /*!< SCB CCSIDR: WA Mask */ + +#define SCB_CCSIDR_NUMSETS_Pos 13U /*!< SCB CCSIDR: NumSets Position */ +#define SCB_CCSIDR_NUMSETS_Msk (0x7FFFUL << SCB_CCSIDR_NUMSETS_Pos) /*!< SCB CCSIDR: NumSets Mask */ + +#define SCB_CCSIDR_ASSOCIATIVITY_Pos 3U /*!< SCB CCSIDR: Associativity Position */ +#define SCB_CCSIDR_ASSOCIATIVITY_Msk (0x3FFUL << SCB_CCSIDR_ASSOCIATIVITY_Pos) /*!< SCB CCSIDR: Associativity Mask */ + +#define SCB_CCSIDR_LINESIZE_Pos 0U /*!< SCB CCSIDR: LineSize Position */ +#define SCB_CCSIDR_LINESIZE_Msk (7UL /*<< SCB_CCSIDR_LINESIZE_Pos*/) /*!< SCB CCSIDR: LineSize Mask */ + +/* SCB Cache Size Selection Register Definitions */ +#define SCB_CSSELR_LEVEL_Pos 1U /*!< SCB CSSELR: Level Position */ +#define SCB_CSSELR_LEVEL_Msk (7UL << SCB_CSSELR_LEVEL_Pos) /*!< SCB CSSELR: Level Mask */ + +#define SCB_CSSELR_IND_Pos 0U /*!< SCB CSSELR: InD Position */ +#define SCB_CSSELR_IND_Msk (1UL /*<< SCB_CSSELR_IND_Pos*/) /*!< SCB CSSELR: InD Mask */ + +/* SCB Software Triggered Interrupt Register Definitions */ +#define SCB_STIR_INTID_Pos 0U /*!< SCB STIR: INTID Position */ +#define SCB_STIR_INTID_Msk (0x1FFUL /*<< SCB_STIR_INTID_Pos*/) /*!< SCB STIR: INTID Mask */ + +/* SCB D-Cache Invalidate by Set-way Register Definitions */ +#define SCB_DCISW_WAY_Pos 30U /*!< SCB DCISW: Way Position */ +#define SCB_DCISW_WAY_Msk (3UL << SCB_DCISW_WAY_Pos) /*!< SCB DCISW: Way Mask */ + +#define SCB_DCISW_SET_Pos 5U /*!< SCB DCISW: Set Position */ +#define SCB_DCISW_SET_Msk (0x1FFUL << SCB_DCISW_SET_Pos) /*!< SCB DCISW: Set Mask */ + +/* SCB D-Cache Clean by Set-way Register Definitions */ +#define SCB_DCCSW_WAY_Pos 30U /*!< SCB DCCSW: Way Position */ +#define SCB_DCCSW_WAY_Msk (3UL << SCB_DCCSW_WAY_Pos) /*!< SCB DCCSW: Way Mask */ + +#define SCB_DCCSW_SET_Pos 5U /*!< SCB DCCSW: Set Position */ +#define SCB_DCCSW_SET_Msk (0x1FFUL << SCB_DCCSW_SET_Pos) /*!< SCB DCCSW: Set Mask */ + +/* SCB D-Cache Clean and Invalidate by Set-way Register Definitions */ +#define SCB_DCCISW_WAY_Pos 30U /*!< SCB DCCISW: Way Position */ +#define SCB_DCCISW_WAY_Msk (3UL << SCB_DCCISW_WAY_Pos) /*!< SCB DCCISW: Way Mask */ + +#define SCB_DCCISW_SET_Pos 5U /*!< SCB DCCISW: Set Position */ +#define SCB_DCCISW_SET_Msk (0x1FFUL << SCB_DCCISW_SET_Pos) /*!< SCB DCCISW: Set Mask */ + +/* Instruction Tightly-Coupled Memory Control Register Definitions */ +#define SCB_ITCMCR_SZ_Pos 3U /*!< SCB ITCMCR: SZ Position */ +#define SCB_ITCMCR_SZ_Msk (0xFUL << SCB_ITCMCR_SZ_Pos) /*!< SCB ITCMCR: SZ Mask */ + +#define SCB_ITCMCR_RETEN_Pos 2U /*!< SCB ITCMCR: RETEN Position */ +#define SCB_ITCMCR_RETEN_Msk (1UL << SCB_ITCMCR_RETEN_Pos) /*!< SCB ITCMCR: RETEN Mask */ + +#define SCB_ITCMCR_RMW_Pos 1U /*!< SCB ITCMCR: RMW Position */ +#define SCB_ITCMCR_RMW_Msk (1UL << SCB_ITCMCR_RMW_Pos) /*!< SCB ITCMCR: RMW Mask */ + +#define SCB_ITCMCR_EN_Pos 0U /*!< SCB ITCMCR: EN Position */ +#define SCB_ITCMCR_EN_Msk (1UL /*<< SCB_ITCMCR_EN_Pos*/) /*!< SCB ITCMCR: EN Mask */ + +/* Data Tightly-Coupled Memory Control Register Definitions */ +#define SCB_DTCMCR_SZ_Pos 3U /*!< SCB DTCMCR: SZ Position */ +#define SCB_DTCMCR_SZ_Msk (0xFUL << SCB_DTCMCR_SZ_Pos) /*!< SCB DTCMCR: SZ Mask */ + +#define SCB_DTCMCR_RETEN_Pos 2U /*!< SCB DTCMCR: RETEN Position */ +#define SCB_DTCMCR_RETEN_Msk (1UL << SCB_DTCMCR_RETEN_Pos) /*!< SCB DTCMCR: RETEN Mask */ + +#define SCB_DTCMCR_RMW_Pos 1U /*!< SCB DTCMCR: RMW Position */ +#define SCB_DTCMCR_RMW_Msk (1UL << SCB_DTCMCR_RMW_Pos) /*!< SCB DTCMCR: RMW Mask */ + +#define SCB_DTCMCR_EN_Pos 0U /*!< SCB DTCMCR: EN Position */ +#define SCB_DTCMCR_EN_Msk (1UL /*<< SCB_DTCMCR_EN_Pos*/) /*!< SCB DTCMCR: EN Mask */ + +/* AHBP Control Register Definitions */ +#define SCB_AHBPCR_SZ_Pos 1U /*!< SCB AHBPCR: SZ Position */ +#define SCB_AHBPCR_SZ_Msk (7UL << SCB_AHBPCR_SZ_Pos) /*!< SCB AHBPCR: SZ Mask */ + +#define SCB_AHBPCR_EN_Pos 0U /*!< SCB AHBPCR: EN Position */ +#define SCB_AHBPCR_EN_Msk (1UL /*<< SCB_AHBPCR_EN_Pos*/) /*!< SCB AHBPCR: EN Mask */ + +/* L1 Cache Control Register Definitions */ +#define SCB_CACR_FORCEWT_Pos 2U /*!< SCB CACR: FORCEWT Position */ +#define SCB_CACR_FORCEWT_Msk (1UL << SCB_CACR_FORCEWT_Pos) /*!< SCB CACR: FORCEWT Mask */ + +#define SCB_CACR_ECCEN_Pos 1U /*!< SCB CACR: ECCEN Position */ +#define SCB_CACR_ECCEN_Msk (1UL << SCB_CACR_ECCEN_Pos) /*!< SCB CACR: ECCEN Mask */ + +#define SCB_CACR_SIWT_Pos 0U /*!< SCB CACR: SIWT Position */ +#define SCB_CACR_SIWT_Msk (1UL /*<< SCB_CACR_SIWT_Pos*/) /*!< SCB CACR: SIWT Mask */ + +/* AHBS Control Register Definitions */ +#define SCB_AHBSCR_INITCOUNT_Pos 11U /*!< SCB AHBSCR: INITCOUNT Position */ +#define SCB_AHBSCR_INITCOUNT_Msk (0x1FUL << SCB_AHBPCR_INITCOUNT_Pos) /*!< SCB AHBSCR: INITCOUNT Mask */ + +#define SCB_AHBSCR_TPRI_Pos 2U /*!< SCB AHBSCR: TPRI Position */ +#define SCB_AHBSCR_TPRI_Msk (0x1FFUL << SCB_AHBPCR_TPRI_Pos) /*!< SCB AHBSCR: TPRI Mask */ + +#define SCB_AHBSCR_CTL_Pos 0U /*!< SCB AHBSCR: CTL Position*/ +#define SCB_AHBSCR_CTL_Msk (3UL /*<< SCB_AHBPCR_CTL_Pos*/) /*!< SCB AHBSCR: CTL Mask */ + +/* Auxiliary Bus Fault Status Register Definitions */ +#define SCB_ABFSR_AXIMTYPE_Pos 8U /*!< SCB ABFSR: AXIMTYPE Position*/ +#define SCB_ABFSR_AXIMTYPE_Msk (3UL << SCB_ABFSR_AXIMTYPE_Pos) /*!< SCB ABFSR: AXIMTYPE Mask */ + +#define SCB_ABFSR_EPPB_Pos 4U /*!< SCB ABFSR: EPPB Position*/ +#define SCB_ABFSR_EPPB_Msk (1UL << SCB_ABFSR_EPPB_Pos) /*!< SCB ABFSR: EPPB Mask */ + +#define SCB_ABFSR_AXIM_Pos 3U /*!< SCB ABFSR: AXIM Position*/ +#define SCB_ABFSR_AXIM_Msk (1UL << SCB_ABFSR_AXIM_Pos) /*!< SCB ABFSR: AXIM Mask */ + +#define SCB_ABFSR_AHBP_Pos 2U /*!< SCB ABFSR: AHBP Position*/ +#define SCB_ABFSR_AHBP_Msk (1UL << SCB_ABFSR_AHBP_Pos) /*!< SCB ABFSR: AHBP Mask */ + +#define SCB_ABFSR_DTCM_Pos 1U /*!< SCB ABFSR: DTCM Position*/ +#define SCB_ABFSR_DTCM_Msk (1UL << SCB_ABFSR_DTCM_Pos) /*!< SCB ABFSR: DTCM Mask */ + +#define SCB_ABFSR_ITCM_Pos 0U /*!< SCB ABFSR: ITCM Position*/ +#define SCB_ABFSR_ITCM_Msk (1UL /*<< SCB_ABFSR_ITCM_Pos*/) /*!< SCB ABFSR: ITCM Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ + __IOM uint32_t CPPWR; /*!< Offset: 0x00C (R/W) Coprocessor Power Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29U]; + __OM uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __IM uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IOM uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[1U]; + __IM uint32_t DEVARCH; /*!< Offset: 0xFBC (R/ ) ITM Device Architecture Register */ + uint32_t RESERVED6[4U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Stimulus Port Register Definitions */ +#define ITM_STIM_DISABLED_Pos 1U /*!< ITM STIM: DISABLED Position */ +#define ITM_STIM_DISABLED_Msk (0x1UL << ITM_STIM_DISABLED_Pos) /*!< ITM STIM: DISABLED Mask */ + +#define ITM_STIM_FIFOREADY_Pos 0U /*!< ITM STIM: FIFOREADY Position */ +#define ITM_STIM_FIFOREADY_Msk (0x1UL /*<< ITM_STIM_FIFOREADY_Pos*/) /*!< ITM STIM: FIFOREADY Mask */ + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TRACEBUSID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TRACEBUSID_Msk (0x7FUL << ITM_TCR_TRACEBUSID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPRESCALE_Pos 8U /*!< ITM TCR: TSPRESCALE Position */ +#define ITM_TCR_TSPRESCALE_Msk (3UL << ITM_TCR_TSPRESCALE_Pos) /*!< ITM TCR: TSPRESCALE Mask */ + +#define ITM_TCR_STALLENA_Pos 5U /*!< ITM TCR: STALLENA Position */ +#define ITM_TCR_STALLENA_Msk (1UL << ITM_TCR_STALLENA_Pos) /*!< ITM TCR: STALLENA Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0U /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL /*<< ITM_IWR_ATVALIDM_Pos*/) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0U /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL /*<< ITM_IRR_ATREADYM_Pos*/) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0U /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL /*<< ITM_IMCR_INTEGRATION_Pos*/) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + uint32_t RESERVED3[1U]; + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED4[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + uint32_t RESERVED5[1U]; + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED6[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + uint32_t RESERVED7[1U]; + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ + uint32_t RESERVED8[1U]; + __IOM uint32_t COMP4; /*!< Offset: 0x060 (R/W) Comparator Register 4 */ + uint32_t RESERVED9[1U]; + __IOM uint32_t FUNCTION4; /*!< Offset: 0x068 (R/W) Function Register 4 */ + uint32_t RESERVED10[1U]; + __IOM uint32_t COMP5; /*!< Offset: 0x070 (R/W) Comparator Register 5 */ + uint32_t RESERVED11[1U]; + __IOM uint32_t FUNCTION5; /*!< Offset: 0x078 (R/W) Function Register 5 */ + uint32_t RESERVED12[1U]; + __IOM uint32_t COMP6; /*!< Offset: 0x080 (R/W) Comparator Register 6 */ + uint32_t RESERVED13[1U]; + __IOM uint32_t FUNCTION6; /*!< Offset: 0x088 (R/W) Function Register 6 */ + uint32_t RESERVED14[1U]; + __IOM uint32_t COMP7; /*!< Offset: 0x090 (R/W) Comparator Register 7 */ + uint32_t RESERVED15[1U]; + __IOM uint32_t FUNCTION7; /*!< Offset: 0x098 (R/W) Function Register 7 */ + uint32_t RESERVED16[1U]; + __IOM uint32_t COMP8; /*!< Offset: 0x0A0 (R/W) Comparator Register 8 */ + uint32_t RESERVED17[1U]; + __IOM uint32_t FUNCTION8; /*!< Offset: 0x0A8 (R/W) Function Register 8 */ + uint32_t RESERVED18[1U]; + __IOM uint32_t COMP9; /*!< Offset: 0x0B0 (R/W) Comparator Register 9 */ + uint32_t RESERVED19[1U]; + __IOM uint32_t FUNCTION9; /*!< Offset: 0x0B8 (R/W) Function Register 9 */ + uint32_t RESERVED20[1U]; + __IOM uint32_t COMP10; /*!< Offset: 0x0C0 (R/W) Comparator Register 10 */ + uint32_t RESERVED21[1U]; + __IOM uint32_t FUNCTION10; /*!< Offset: 0x0C8 (R/W) Function Register 10 */ + uint32_t RESERVED22[1U]; + __IOM uint32_t COMP11; /*!< Offset: 0x0D0 (R/W) Comparator Register 11 */ + uint32_t RESERVED23[1U]; + __IOM uint32_t FUNCTION11; /*!< Offset: 0x0D8 (R/W) Function Register 11 */ + uint32_t RESERVED24[1U]; + __IOM uint32_t COMP12; /*!< Offset: 0x0E0 (R/W) Comparator Register 12 */ + uint32_t RESERVED25[1U]; + __IOM uint32_t FUNCTION12; /*!< Offset: 0x0E8 (R/W) Function Register 12 */ + uint32_t RESERVED26[1U]; + __IOM uint32_t COMP13; /*!< Offset: 0x0F0 (R/W) Comparator Register 13 */ + uint32_t RESERVED27[1U]; + __IOM uint32_t FUNCTION13; /*!< Offset: 0x0F8 (R/W) Function Register 13 */ + uint32_t RESERVED28[1U]; + __IOM uint32_t COMP14; /*!< Offset: 0x100 (R/W) Comparator Register 14 */ + uint32_t RESERVED29[1U]; + __IOM uint32_t FUNCTION14; /*!< Offset: 0x108 (R/W) Function Register 14 */ + uint32_t RESERVED30[1U]; + __IOM uint32_t COMP15; /*!< Offset: 0x110 (R/W) Comparator Register 15 */ + uint32_t RESERVED31[1U]; + __IOM uint32_t FUNCTION15; /*!< Offset: 0x118 (R/W) Function Register 15 */ + uint32_t RESERVED32[934U]; + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R ) Lock Status Register */ + uint32_t RESERVED33[1U]; + __IM uint32_t DEVARCH; /*!< Offset: 0xFBC (R/ ) Device Architecture Register */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCDISS_Pos 23U /*!< DWT CTRL: CYCDISS Position */ +#define DWT_CTRL_CYCDISS_Msk (0x1UL << DWT_CTRL_CYCDISS_Pos) /*!< DWT CTRL: CYCDISS Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_ID_Pos 27U /*!< DWT FUNCTION: ID Position */ +#define DWT_FUNCTION_ID_Msk (0x1FUL << DWT_FUNCTION_ID_Pos) /*!< DWT FUNCTION: ID Mask */ + +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_ACTION_Pos 4U /*!< DWT FUNCTION: ACTION Position */ +#define DWT_FUNCTION_ACTION_Msk (0x1UL << DWT_FUNCTION_ACTION_Pos) /*!< DWT FUNCTION: ACTION Mask */ + +#define DWT_FUNCTION_MATCH_Pos 0U /*!< DWT FUNCTION: MATCH Position */ +#define DWT_FUNCTION_MATCH_Msk (0xFUL /*<< DWT_FUNCTION_MATCH_Pos*/) /*!< DWT FUNCTION: MATCH Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IOM uint32_t PSCR; /*!< Offset: 0x308 (R/W) Periodic Synchronization Control Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ + __IM uint32_t ITFTTD0; /*!< Offset: 0xEEC (R/ ) Integration Test FIFO Test Data 0 Register */ + __IOM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/W) Integration Test ATB Control Register 2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) Integration Test ATB Control Register 0 */ + __IM uint32_t ITFTTD1; /*!< Offset: 0xEFC (R/ ) Integration Test FIFO Test Data 1 Register */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) Device Configuration Register */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) Device Type Identifier Register */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_FOnMan_Pos 6U /*!< TPI FFCR: FOnMan Position */ +#define TPI_FFCR_FOnMan_Msk (0x1UL << TPI_FFCR_FOnMan_Pos) /*!< TPI FFCR: FOnMan Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration Test FIFO Test Data 0 Register Definitions */ +#define TPI_ITFTTD0_ATB_IF2_ATVALID_Pos 29U /*!< TPI ITFTTD0: ATB Interface 2 ATVALIDPosition */ +#define TPI_ITFTTD0_ATB_IF2_ATVALID_Msk (0x3UL << TPI_ITFTTD0_ATB_IF2_ATVALID_Pos) /*!< TPI ITFTTD0: ATB Interface 2 ATVALID Mask */ + +#define TPI_ITFTTD0_ATB_IF2_bytecount_Pos 27U /*!< TPI ITFTTD0: ATB Interface 2 byte count Position */ +#define TPI_ITFTTD0_ATB_IF2_bytecount_Msk (0x3UL << TPI_ITFTTD0_ATB_IF2_bytecount_Pos) /*!< TPI ITFTTD0: ATB Interface 2 byte count Mask */ + +#define TPI_ITFTTD0_ATB_IF1_ATVALID_Pos 26U /*!< TPI ITFTTD0: ATB Interface 1 ATVALID Position */ +#define TPI_ITFTTD0_ATB_IF1_ATVALID_Msk (0x3UL << TPI_ITFTTD0_ATB_IF1_ATVALID_Pos) /*!< TPI ITFTTD0: ATB Interface 1 ATVALID Mask */ + +#define TPI_ITFTTD0_ATB_IF1_bytecount_Pos 24U /*!< TPI ITFTTD0: ATB Interface 1 byte count Position */ +#define TPI_ITFTTD0_ATB_IF1_bytecount_Msk (0x3UL << TPI_ITFTTD0_ATB_IF1_bytecount_Pos) /*!< TPI ITFTTD0: ATB Interface 1 byte countt Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data2_Pos 16U /*!< TPI ITFTTD0: ATB Interface 1 data2 Position */ +#define TPI_ITFTTD0_ATB_IF1_data2_Msk (0xFFUL << TPI_ITFTTD0_ATB_IF1_data1_Pos) /*!< TPI ITFTTD0: ATB Interface 1 data2 Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data1_Pos 8U /*!< TPI ITFTTD0: ATB Interface 1 data1 Position */ +#define TPI_ITFTTD0_ATB_IF1_data1_Msk (0xFFUL << TPI_ITFTTD0_ATB_IF1_data1_Pos) /*!< TPI ITFTTD0: ATB Interface 1 data1 Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data0_Pos 0U /*!< TPI ITFTTD0: ATB Interface 1 data0 Position */ +#define TPI_ITFTTD0_ATB_IF1_data0_Msk (0xFFUL /*<< TPI_ITFTTD0_ATB_IF1_data0_Pos*/) /*!< TPI ITFTTD0: ATB Interface 1 data0 Mask */ + +/* TPI Integration Test ATB Control Register 2 Register Definitions */ +#define TPI_ITATBCTR2_AFVALID2S_Pos 1U /*!< TPI ITATBCTR2: AFVALID2S Position */ +#define TPI_ITATBCTR2_AFVALID2S_Msk (0x1UL << TPI_ITATBCTR2_AFVALID2S_Pos) /*!< TPI ITATBCTR2: AFVALID2SS Mask */ + +#define TPI_ITATBCTR2_AFVALID1S_Pos 1U /*!< TPI ITATBCTR2: AFVALID1S Position */ +#define TPI_ITATBCTR2_AFVALID1S_Msk (0x1UL << TPI_ITATBCTR2_AFVALID1S_Pos) /*!< TPI ITATBCTR2: AFVALID1SS Mask */ + +#define TPI_ITATBCTR2_ATREADY2S_Pos 0U /*!< TPI ITATBCTR2: ATREADY2S Position */ +#define TPI_ITATBCTR2_ATREADY2S_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2S_Pos*/) /*!< TPI ITATBCTR2: ATREADY2S Mask */ + +#define TPI_ITATBCTR2_ATREADY1S_Pos 0U /*!< TPI ITATBCTR2: ATREADY1S Position */ +#define TPI_ITATBCTR2_ATREADY1S_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1S_Pos*/) /*!< TPI ITATBCTR2: ATREADY1S Mask */ + +/* TPI Integration Test FIFO Test Data 1 Register Definitions */ +#define TPI_ITFTTD1_ATB_IF2_ATVALID_Pos 29U /*!< TPI ITFTTD1: ATB Interface 2 ATVALID Position */ +#define TPI_ITFTTD1_ATB_IF2_ATVALID_Msk (0x3UL << TPI_ITFTTD1_ATB_IF2_ATVALID_Pos) /*!< TPI ITFTTD1: ATB Interface 2 ATVALID Mask */ + +#define TPI_ITFTTD1_ATB_IF2_bytecount_Pos 27U /*!< TPI ITFTTD1: ATB Interface 2 byte count Position */ +#define TPI_ITFTTD1_ATB_IF2_bytecount_Msk (0x3UL << TPI_ITFTTD1_ATB_IF2_bytecount_Pos) /*!< TPI ITFTTD1: ATB Interface 2 byte count Mask */ + +#define TPI_ITFTTD1_ATB_IF1_ATVALID_Pos 26U /*!< TPI ITFTTD1: ATB Interface 1 ATVALID Position */ +#define TPI_ITFTTD1_ATB_IF1_ATVALID_Msk (0x3UL << TPI_ITFTTD1_ATB_IF1_ATVALID_Pos) /*!< TPI ITFTTD1: ATB Interface 1 ATVALID Mask */ + +#define TPI_ITFTTD1_ATB_IF1_bytecount_Pos 24U /*!< TPI ITFTTD1: ATB Interface 1 byte count Position */ +#define TPI_ITFTTD1_ATB_IF1_bytecount_Msk (0x3UL << TPI_ITFTTD1_ATB_IF1_bytecount_Pos) /*!< TPI ITFTTD1: ATB Interface 1 byte countt Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data2_Pos 16U /*!< TPI ITFTTD1: ATB Interface 2 data2 Position */ +#define TPI_ITFTTD1_ATB_IF2_data2_Msk (0xFFUL << TPI_ITFTTD1_ATB_IF2_data1_Pos) /*!< TPI ITFTTD1: ATB Interface 2 data2 Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data1_Pos 8U /*!< TPI ITFTTD1: ATB Interface 2 data1 Position */ +#define TPI_ITFTTD1_ATB_IF2_data1_Msk (0xFFUL << TPI_ITFTTD1_ATB_IF2_data1_Pos) /*!< TPI ITFTTD1: ATB Interface 2 data1 Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data0_Pos 0U /*!< TPI ITFTTD1: ATB Interface 2 data0 Position */ +#define TPI_ITFTTD1_ATB_IF2_data0_Msk (0xFFUL /*<< TPI_ITFTTD1_ATB_IF2_data0_Pos*/) /*!< TPI ITFTTD1: ATB Interface 2 data0 Mask */ + +/* TPI Integration Test ATB Control Register 0 Definitions */ +#define TPI_ITATBCTR0_AFVALID2S_Pos 1U /*!< TPI ITATBCTR0: AFVALID2S Position */ +#define TPI_ITATBCTR0_AFVALID2S_Msk (0x1UL << TPI_ITATBCTR0_AFVALID2S_Pos) /*!< TPI ITATBCTR0: AFVALID2SS Mask */ + +#define TPI_ITATBCTR0_AFVALID1S_Pos 1U /*!< TPI ITATBCTR0: AFVALID1S Position */ +#define TPI_ITATBCTR0_AFVALID1S_Msk (0x1UL << TPI_ITATBCTR0_AFVALID1S_Pos) /*!< TPI ITATBCTR0: AFVALID1SS Mask */ + +#define TPI_ITATBCTR0_ATREADY2S_Pos 0U /*!< TPI ITATBCTR0: ATREADY2S Position */ +#define TPI_ITATBCTR0_ATREADY2S_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2S_Pos*/) /*!< TPI ITATBCTR0: ATREADY2S Mask */ + +#define TPI_ITATBCTR0_ATREADY1S_Pos 0U /*!< TPI ITATBCTR0: ATREADY1S Position */ +#define TPI_ITATBCTR0_ATREADY1S_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1S_Pos*/) /*!< TPI ITATBCTR0: ATREADY1S Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_FIFOSZ_Pos 6U /*!< TPI DEVID: FIFOSZ Position */ +#define TPI_DEVID_FIFOSZ_Msk (0x7UL << TPI_DEVID_FIFOSZ_Pos) /*!< TPI DEVID: FIFOSZ Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x3FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) MPU Region Limit Address Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Region Base Address Register Alias 1 */ + __IOM uint32_t RLAR_A1; /*!< Offset: 0x018 (R/W) MPU Region Limit Address Register Alias 1 */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Region Base Address Register Alias 2 */ + __IOM uint32_t RLAR_A2; /*!< Offset: 0x020 (R/W) MPU Region Limit Address Register Alias 2 */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Region Base Address Register Alias 3 */ + __IOM uint32_t RLAR_A3; /*!< Offset: 0x028 (R/W) MPU Region Limit Address Register Alias 3 */ + uint32_t RESERVED0[1]; + union { + __IOM uint32_t MAIR[2]; + struct { + __IOM uint32_t MAIR0; /*!< Offset: 0x030 (R/W) MPU Memory Attribute Indirection Register 0 */ + __IOM uint32_t MAIR1; /*!< Offset: 0x034 (R/W) MPU Memory Attribute Indirection Register 1 */ + }; + }; +} MPU_Type; + +#define MPU_TYPE_RALIASES 4U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_BASE_Pos 5U /*!< MPU RBAR: BASE Position */ +#define MPU_RBAR_BASE_Msk (0x7FFFFFFUL << MPU_RBAR_BASE_Pos) /*!< MPU RBAR: BASE Mask */ + +#define MPU_RBAR_SH_Pos 3U /*!< MPU RBAR: SH Position */ +#define MPU_RBAR_SH_Msk (0x3UL << MPU_RBAR_SH_Pos) /*!< MPU RBAR: SH Mask */ + +#define MPU_RBAR_AP_Pos 1U /*!< MPU RBAR: AP Position */ +#define MPU_RBAR_AP_Msk (0x3UL << MPU_RBAR_AP_Pos) /*!< MPU RBAR: AP Mask */ + +#define MPU_RBAR_XN_Pos 0U /*!< MPU RBAR: XN Position */ +#define MPU_RBAR_XN_Msk (01UL /*<< MPU_RBAR_XN_Pos*/) /*!< MPU RBAR: XN Mask */ + +/* MPU Region Limit Address Register Definitions */ +#define MPU_RLAR_LIMIT_Pos 5U /*!< MPU RLAR: LIMIT Position */ +#define MPU_RLAR_LIMIT_Msk (0x7FFFFFFUL << MPU_RLAR_LIMIT_Pos) /*!< MPU RLAR: LIMIT Mask */ + +#define MPU_RLAR_AttrIndx_Pos 1U /*!< MPU RLAR: AttrIndx Position */ +#define MPU_RLAR_AttrIndx_Msk (0x7UL << MPU_RLAR_AttrIndx_Pos) /*!< MPU RLAR: AttrIndx Mask */ + +#define MPU_RLAR_EN_Pos 0U /*!< MPU RLAR: Region enable bit Position */ +#define MPU_RLAR_EN_Msk (1UL /*<< MPU_RLAR_EN_Pos*/) /*!< MPU RLAR: Region enable bit Disable Mask */ + +/* MPU Memory Attribute Indirection Register 0 Definitions */ +#define MPU_MAIR0_Attr3_Pos 24U /*!< MPU MAIR0: Attr3 Position */ +#define MPU_MAIR0_Attr3_Msk (0xFFUL << MPU_MAIR0_Attr3_Pos) /*!< MPU MAIR0: Attr3 Mask */ + +#define MPU_MAIR0_Attr2_Pos 16U /*!< MPU MAIR0: Attr2 Position */ +#define MPU_MAIR0_Attr2_Msk (0xFFUL << MPU_MAIR0_Attr2_Pos) /*!< MPU MAIR0: Attr2 Mask */ + +#define MPU_MAIR0_Attr1_Pos 8U /*!< MPU MAIR0: Attr1 Position */ +#define MPU_MAIR0_Attr1_Msk (0xFFUL << MPU_MAIR0_Attr1_Pos) /*!< MPU MAIR0: Attr1 Mask */ + +#define MPU_MAIR0_Attr0_Pos 0U /*!< MPU MAIR0: Attr0 Position */ +#define MPU_MAIR0_Attr0_Msk (0xFFUL /*<< MPU_MAIR0_Attr0_Pos*/) /*!< MPU MAIR0: Attr0 Mask */ + +/* MPU Memory Attribute Indirection Register 1 Definitions */ +#define MPU_MAIR1_Attr7_Pos 24U /*!< MPU MAIR1: Attr7 Position */ +#define MPU_MAIR1_Attr7_Msk (0xFFUL << MPU_MAIR1_Attr7_Pos) /*!< MPU MAIR1: Attr7 Mask */ + +#define MPU_MAIR1_Attr6_Pos 16U /*!< MPU MAIR1: Attr6 Position */ +#define MPU_MAIR1_Attr6_Msk (0xFFUL << MPU_MAIR1_Attr6_Pos) /*!< MPU MAIR1: Attr6 Mask */ + +#define MPU_MAIR1_Attr5_Pos 8U /*!< MPU MAIR1: Attr5 Position */ +#define MPU_MAIR1_Attr5_Msk (0xFFUL << MPU_MAIR1_Attr5_Pos) /*!< MPU MAIR1: Attr5 Mask */ + +#define MPU_MAIR1_Attr4_Pos 0U /*!< MPU MAIR1: Attr4 Position */ +#define MPU_MAIR1_Attr4_Msk (0xFFUL /*<< MPU_MAIR1_Attr4_Pos*/) /*!< MPU MAIR1: Attr4 Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SAU Security Attribution Unit (SAU) + \brief Type definitions for the Security Attribution Unit (SAU) + @{ + */ + +/** + \brief Structure type to access the Security Attribution Unit (SAU). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SAU Control Register */ + __IM uint32_t TYPE; /*!< Offset: 0x004 (R/ ) SAU Type Register */ +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) SAU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) SAU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) SAU Region Limit Address Register */ +#else + uint32_t RESERVED0[3]; +#endif + __IOM uint32_t SFSR; /*!< Offset: 0x014 (R/W) Secure Fault Status Register */ + __IOM uint32_t SFAR; /*!< Offset: 0x018 (R/W) Secure Fault Address Register */ +} SAU_Type; + +/* SAU Control Register Definitions */ +#define SAU_CTRL_ALLNS_Pos 1U /*!< SAU CTRL: ALLNS Position */ +#define SAU_CTRL_ALLNS_Msk (1UL << SAU_CTRL_ALLNS_Pos) /*!< SAU CTRL: ALLNS Mask */ + +#define SAU_CTRL_ENABLE_Pos 0U /*!< SAU CTRL: ENABLE Position */ +#define SAU_CTRL_ENABLE_Msk (1UL /*<< SAU_CTRL_ENABLE_Pos*/) /*!< SAU CTRL: ENABLE Mask */ + +/* SAU Type Register Definitions */ +#define SAU_TYPE_SREGION_Pos 0U /*!< SAU TYPE: SREGION Position */ +#define SAU_TYPE_SREGION_Msk (0xFFUL /*<< SAU_TYPE_SREGION_Pos*/) /*!< SAU TYPE: SREGION Mask */ + +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) +/* SAU Region Number Register Definitions */ +#define SAU_RNR_REGION_Pos 0U /*!< SAU RNR: REGION Position */ +#define SAU_RNR_REGION_Msk (0xFFUL /*<< SAU_RNR_REGION_Pos*/) /*!< SAU RNR: REGION Mask */ + +/* SAU Region Base Address Register Definitions */ +#define SAU_RBAR_BADDR_Pos 5U /*!< SAU RBAR: BADDR Position */ +#define SAU_RBAR_BADDR_Msk (0x7FFFFFFUL << SAU_RBAR_BADDR_Pos) /*!< SAU RBAR: BADDR Mask */ + +/* SAU Region Limit Address Register Definitions */ +#define SAU_RLAR_LADDR_Pos 5U /*!< SAU RLAR: LADDR Position */ +#define SAU_RLAR_LADDR_Msk (0x7FFFFFFUL << SAU_RLAR_LADDR_Pos) /*!< SAU RLAR: LADDR Mask */ + +#define SAU_RLAR_NSC_Pos 1U /*!< SAU RLAR: NSC Position */ +#define SAU_RLAR_NSC_Msk (1UL << SAU_RLAR_NSC_Pos) /*!< SAU RLAR: NSC Mask */ + +#define SAU_RLAR_ENABLE_Pos 0U /*!< SAU RLAR: ENABLE Position */ +#define SAU_RLAR_ENABLE_Msk (1UL /*<< SAU_RLAR_ENABLE_Pos*/) /*!< SAU RLAR: ENABLE Mask */ + +#endif /* defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) */ + +/* Secure Fault Status Register Definitions */ +#define SAU_SFSR_LSERR_Pos 7U /*!< SAU SFSR: LSERR Position */ +#define SAU_SFSR_LSERR_Msk (1UL << SAU_SFSR_LSERR_Pos) /*!< SAU SFSR: LSERR Mask */ + +#define SAU_SFSR_SFARVALID_Pos 6U /*!< SAU SFSR: SFARVALID Position */ +#define SAU_SFSR_SFARVALID_Msk (1UL << SAU_SFSR_SFARVALID_Pos) /*!< SAU SFSR: SFARVALID Mask */ + +#define SAU_SFSR_LSPERR_Pos 5U /*!< SAU SFSR: LSPERR Position */ +#define SAU_SFSR_LSPERR_Msk (1UL << SAU_SFSR_LSPERR_Pos) /*!< SAU SFSR: LSPERR Mask */ + +#define SAU_SFSR_INVTRAN_Pos 4U /*!< SAU SFSR: INVTRAN Position */ +#define SAU_SFSR_INVTRAN_Msk (1UL << SAU_SFSR_INVTRAN_Pos) /*!< SAU SFSR: INVTRAN Mask */ + +#define SAU_SFSR_AUVIOL_Pos 3U /*!< SAU SFSR: AUVIOL Position */ +#define SAU_SFSR_AUVIOL_Msk (1UL << SAU_SFSR_AUVIOL_Pos) /*!< SAU SFSR: AUVIOL Mask */ + +#define SAU_SFSR_INVER_Pos 2U /*!< SAU SFSR: INVER Position */ +#define SAU_SFSR_INVER_Msk (1UL << SAU_SFSR_INVER_Pos) /*!< SAU SFSR: INVER Mask */ + +#define SAU_SFSR_INVIS_Pos 1U /*!< SAU SFSR: INVIS Position */ +#define SAU_SFSR_INVIS_Msk (1UL << SAU_SFSR_INVIS_Pos) /*!< SAU SFSR: INVIS Mask */ + +#define SAU_SFSR_INVEP_Pos 0U /*!< SAU SFSR: INVEP Position */ +#define SAU_SFSR_INVEP_Msk (1UL /*<< SAU_SFSR_INVEP_Pos*/) /*!< SAU SFSR: INVEP Mask */ + +/*@} end of group CMSIS_SAU */ +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** + \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ +} FPU_Type; + +/* Floating-Point Context Control Register Definitions */ +#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_LSPENS_Pos 29U /*!< FPCCR: LSPENS Position */ +#define FPU_FPCCR_LSPENS_Msk (1UL << FPU_FPCCR_LSPENS_Pos) /*!< FPCCR: LSPENS bit Mask */ + +#define FPU_FPCCR_CLRONRET_Pos 28U /*!< FPCCR: CLRONRET Position */ +#define FPU_FPCCR_CLRONRET_Msk (1UL << FPU_FPCCR_CLRONRET_Pos) /*!< FPCCR: CLRONRET bit Mask */ + +#define FPU_FPCCR_CLRONRETS_Pos 27U /*!< FPCCR: CLRONRETS Position */ +#define FPU_FPCCR_CLRONRETS_Msk (1UL << FPU_FPCCR_CLRONRETS_Pos) /*!< FPCCR: CLRONRETS bit Mask */ + +#define FPU_FPCCR_TS_Pos 26U /*!< FPCCR: TS Position */ +#define FPU_FPCCR_TS_Msk (1UL << FPU_FPCCR_TS_Pos) /*!< FPCCR: TS bit Mask */ + +#define FPU_FPCCR_UFRDY_Pos 10U /*!< FPCCR: UFRDY Position */ +#define FPU_FPCCR_UFRDY_Msk (1UL << FPU_FPCCR_UFRDY_Pos) /*!< FPCCR: UFRDY bit Mask */ + +#define FPU_FPCCR_SPLIMVIOL_Pos 9U /*!< FPCCR: SPLIMVIOL Position */ +#define FPU_FPCCR_SPLIMVIOL_Msk (1UL << FPU_FPCCR_SPLIMVIOL_Pos) /*!< FPCCR: SPLIMVIOL bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_SFRDY_Pos 7U /*!< FPCCR: SFRDY Position */ +#define FPU_FPCCR_SFRDY_Msk (1UL << FPU_FPCCR_SFRDY_Pos) /*!< FPCCR: SFRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_S_Pos 2U /*!< FPCCR: Security status of the FP context bit Position */ +#define FPU_FPCCR_S_Msk (1UL << FPU_FPCCR_S_Pos) /*!< FPCCR: Security status of the FP context bit Mask */ + +#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register Definitions */ +#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register Definitions */ +#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 Definitions */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 Definitions */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ + +/*@} end of group CMSIS_FPU */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ + uint32_t RESERVED4[1U]; + __IOM uint32_t DAUTHCTRL; /*!< Offset: 0x014 (R/W) Debug Authentication Control Register */ + __IOM uint32_t DSCSR; /*!< Offset: 0x018 (R/W) Debug Security Control and Status Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESTART_ST_Pos 26U /*!< CoreDebug DHCSR: S_RESTART_ST Position */ +#define CoreDebug_DHCSR_S_RESTART_ST_Msk (1UL << CoreDebug_DHCSR_S_RESTART_ST_Pos) /*!< CoreDebug DHCSR: S_RESTART_ST Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/* Debug Authentication Control Register Definitions */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos 3U /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Position */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Mask */ + +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos 2U /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Msk (1UL << CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos) /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Mask */ + +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Pos 1U /*!< CoreDebug DAUTHCTRL: INTSPIDEN Position */ +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPIDEN Mask */ + +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Pos 0U /*!< CoreDebug DAUTHCTRL: SPIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Msk (1UL /*<< CoreDebug_DAUTHCTRL_SPIDENSEL_Pos*/) /*!< CoreDebug DAUTHCTRL: SPIDENSEL Mask */ + +/* Debug Security Control and Status Register Definitions */ +#define CoreDebug_DSCSR_CDS_Pos 16U /*!< CoreDebug DSCSR: CDS Position */ +#define CoreDebug_DSCSR_CDS_Msk (1UL << CoreDebug_DSCSR_CDS_Pos) /*!< CoreDebug DSCSR: CDS Mask */ + +#define CoreDebug_DSCSR_SBRSEL_Pos 1U /*!< CoreDebug DSCSR: SBRSEL Position */ +#define CoreDebug_DSCSR_SBRSEL_Msk (1UL << CoreDebug_DSCSR_SBRSEL_Pos) /*!< CoreDebug DSCSR: SBRSEL Mask */ + +#define CoreDebug_DSCSR_SBRSELEN_Pos 0U /*!< CoreDebug DSCSR: SBRSELEN Position */ +#define CoreDebug_DSCSR_SBRSELEN_Msk (1UL /*<< CoreDebug_DSCSR_SBRSELEN_Pos*/) /*!< CoreDebug DSCSR: SBRSELEN Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ + #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ + #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ + #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ + #define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ + #define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ + #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ + #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ + #define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + + #define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ + #define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ + #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ + #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + #define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ + #define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ + #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ + #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE ) /*!< Core Debug configuration struct */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ + #endif + + #if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SAU_BASE (SCS_BASE + 0x0DD0UL) /*!< Security Attribution Unit */ + #define SAU ((SAU_Type *) SAU_BASE ) /*!< Security Attribution Unit */ + #endif + + #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ + #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SCS_BASE_NS (0xE002E000UL) /*!< System Control Space Base Address (non-secure address space) */ + #define CoreDebug_BASE_NS (0xE002EDF0UL) /*!< Core Debug Base Address (non-secure address space) */ + #define SysTick_BASE_NS (SCS_BASE_NS + 0x0010UL) /*!< SysTick Base Address (non-secure address space) */ + #define NVIC_BASE_NS (SCS_BASE_NS + 0x0100UL) /*!< NVIC Base Address (non-secure address space) */ + #define SCB_BASE_NS (SCS_BASE_NS + 0x0D00UL) /*!< System Control Block Base Address (non-secure address space) */ + + #define SCnSCB_NS ((SCnSCB_Type *) SCS_BASE_NS ) /*!< System control Register not in SCB(non-secure address space) */ + #define SCB_NS ((SCB_Type *) SCB_BASE_NS ) /*!< SCB configuration struct (non-secure address space) */ + #define SysTick_NS ((SysTick_Type *) SysTick_BASE_NS ) /*!< SysTick configuration struct (non-secure address space) */ + #define NVIC_NS ((NVIC_Type *) NVIC_BASE_NS ) /*!< NVIC configuration struct (non-secure address space) */ + #define CoreDebug_NS ((CoreDebug_Type *) CoreDebug_BASE_NS) /*!< Core Debug configuration struct (non-secure address space) */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE_NS (SCS_BASE_NS + 0x0D90UL) /*!< Memory Protection Unit (non-secure address space) */ + #define MPU_NS ((MPU_Type *) MPU_BASE_NS ) /*!< Memory Protection Unit (non-secure address space) */ + #endif + + #define FPU_BASE_NS (SCS_BASE_NS + 0x0F30UL) /*!< Floating Point Unit (non-secure address space) */ + #define FPU_NS ((FPU_Type *) FPU_BASE_NS ) /*!< Floating Point Unit (non-secure address space) */ + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* Special LR values for Secure/Non-Secure call handling and exception handling */ + +/* Function Return Payload (from ARMv8-M Architecture Reference Manual) LR value on entry from Secure BLXNS */ +#define FNC_RETURN (0xFEFFFFFFUL) /* bit [0] ignored when processing a branch */ + +/* The following EXC_RETURN mask values are used to evaluate the LR on exception entry */ +#define EXC_RETURN_PREFIX (0xFF000000UL) /* bits [31:24] set to indicate an EXC_RETURN value */ +#define EXC_RETURN_S (0x00000040UL) /* bit [6] stack used to push registers: 0=Non-secure 1=Secure */ +#define EXC_RETURN_DCRS (0x00000020UL) /* bit [5] stacking rules for called registers: 0=skipped 1=saved */ +#define EXC_RETURN_FTYPE (0x00000010UL) /* bit [4] allocate stack for floating-point context: 0=done 1=skipped */ +#define EXC_RETURN_MODE (0x00000008UL) /* bit [3] processor mode for return: 0=Handler mode 1=Thread mode */ +#define EXC_RETURN_SPSEL (0x00000002UL) /* bit [1] stack pointer used to restore context: 0=MSP 1=PSP */ +#define EXC_RETURN_ES (0x00000001UL) /* bit [0] security state exception was taken to: 0=Non-secure 1=Secure */ + +/* Integrity Signature (from ARMv8-M Architecture Reference Manual) for exception context stacking */ +#if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) /* Value for processors with floating-point extension: */ +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125AUL) /* bit [0] SFTC must match LR bit[4] EXC_RETURN_FTYPE */ +#else +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125BUL) /* Value for processors without floating-point extension */ +#endif + + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8U) ); /* Insert write key and priority group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Get Interrupt Target State + \details Reads the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + \return 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_GetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Target State + \details Sets the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_SetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] |= ((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Clear Interrupt Target State + \details Clears the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_ClearTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] &= ~((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IPR[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC->IPR[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Set Priority Grouping (non-secure) + \details Sets the non-secure priority grouping field when in secure state using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void TZ_NVIC_SetPriorityGrouping_NS(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB_NS->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ + SCB_NS->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping (non-secure) + \details Reads the priority grouping field from the non-secure NVIC when in secure state. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriorityGrouping_NS(void) +{ + return ((uint32_t)((SCB_NS->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt (non-secure) + \details Enables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_EnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status (non-secure) + \details Returns a device specific interrupt enable status from the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetEnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt (non-secure) + \details Disables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_DisableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Pending Interrupt (non-secure) + \details Reads the NVIC pending register in the non-secure NVIC when in secure state and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt (non-secure) + \details Sets the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_SetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt (non-secure) + \details Clears the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_ClearPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt (non-secure) + \details Reads the active register in non-secure NVIC when in secure state and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetActive_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority (non-secure) + \details Sets the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every non-secure processor exception. + */ +__STATIC_INLINE void TZ_NVIC_SetPriority_NS(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->IPR[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB_NS->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority (non-secure) + \details Reads the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriority_NS(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC_NS->IPR[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB_NS->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) &&(__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_NVICFunctions */ + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv8.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + uint32_t mvfr0; + + mvfr0 = FPU->MVFR0; + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x220U) + { + return 2U; /* Double + Single precision FPU */ + } + else if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) + { + return 1U; /* Single precision FPU */ + } + else + { + return 0U; /* No FPU */ + } +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ########################## SAU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SAUFunctions SAU Functions + \brief Functions that configure the SAU. + @{ + */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + +/** + \brief Enable SAU + \details Enables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Enable(void) +{ + SAU->CTRL |= (SAU_CTRL_ENABLE_Msk); +} + + + +/** + \brief Disable SAU + \details Disables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Disable(void) +{ + SAU->CTRL &= ~(SAU_CTRL_ENABLE_Msk); +} + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_SAUFunctions */ + + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief System Tick Configuration (non-secure) + \details Initializes the non-secure System Timer and its interrupt when in secure state, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function TZ_SysTick_Config_NS is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t TZ_SysTick_Config_NS(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick_NS->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + TZ_NVIC_SetPriority_NS (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick_NS->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick_NS->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM33_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm4.h b/Firmware/ThirdParty/CMSIS/Include/core_cm4.h similarity index 84% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_cm4.h rename to Firmware/ThirdParty/CMSIS/Include/core_cm4.h index dc840ebf2..7d5687353 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm4.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm4.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_cm4.h * @brief CMSIS Cortex-M4 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.8 + * @date 04. June 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,60 +60,22 @@ @{ */ -/* CMSIS CM4 definitions */ -#define __CM4_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM4_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ -#define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16U) | \ - __CM4_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x04U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline +#include "cmsis_version.h" -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline +/* CMSIS CM4 definitions */ +#define __CM4_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM4_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16U) | \ + __CM4_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#else - #error Unknown compiler -#endif +#define __CORTEX_M (4U) /*!< Cortex-M Core */ /** __FPU_USED indicates whether an FPU is used or not. For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. */ #if defined ( __CC_ARM ) #if defined __TARGET_FPU_VFP - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -133,9 +85,9 @@ #define __FPU_USED 0U #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP - #if (__FPU_PRESENT == 1) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -147,7 +99,7 @@ #elif defined ( __GNUC__ ) #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -159,7 +111,7 @@ #elif defined ( __ICCARM__ ) #if defined __ARMVFP__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -169,9 +121,9 @@ #define __FPU_USED 0U #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -183,7 +135,7 @@ #elif defined ( __TASKING__ ) #if defined __FPU_VFP__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -195,7 +147,7 @@ #elif defined ( __CSMC__ ) #if ( __CSMC__ & 0x400U) - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -207,9 +159,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ -#include "core_cmSimd.h" /* Compiler specific SIMD Intrinsics */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -244,7 +195,7 @@ #endif #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 4U + #define __NVIC_PRIO_BITS 3U #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" #endif @@ -367,11 +318,12 @@ typedef union struct { uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ uint32_t C:1; /*!< bit: 29 Carry condition code flag */ @@ -397,8 +349,8 @@ typedef union #define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ #define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ #define xPSR_T_Pos 24U /*!< xPSR: T Position */ #define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ @@ -406,6 +358,9 @@ typedef union #define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ #define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + #define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ #define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ @@ -662,6 +617,66 @@ typedef struct #define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ #define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + /* SCB Hard Fault Status Register Definitions */ #define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ #define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ @@ -831,7 +846,7 @@ typedef struct /* ITM Trace Privilege Register Definitions */ #define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ /* ITM Trace Control Register Definitions */ #define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ @@ -1045,7 +1060,7 @@ typedef struct */ typedef struct { - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ uint32_t RESERVED0[2U]; __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ @@ -1056,7 +1071,7 @@ typedef struct __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ uint32_t RESERVED4[1U]; @@ -1126,8 +1141,11 @@ typedef struct #define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ /* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ /* TPI Integration ITM Data Register Definitions (FIFO1) */ #define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ @@ -1152,12 +1170,15 @@ typedef struct #define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ /* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ /* TPI Integration Mode Control Register Definitions */ #define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ /* TPI DEVID Register Definitions */ #define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ @@ -1179,16 +1200,16 @@ typedef struct #define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ /* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ #define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + /*@}*/ /* end of group CMSIS_TPI */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -1214,6 +1235,8 @@ typedef struct __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ } MPU_Type; +#define MPU_TYPE_RALIASES 4U + /* MPU Type Register Definitions */ #define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ #define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ @@ -1280,10 +1303,9 @@ typedef struct #define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ /*@} end of group CMSIS_MPU */ -#endif +#endif /* defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) */ -#if (__FPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_FPU Floating Point Unit (FPU) @@ -1388,7 +1410,6 @@ typedef struct #define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ /*@} end of group CMSIS_FPU */ -#endif /** @@ -1506,18 +1527,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -1529,7 +1550,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M4 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ @@ -1548,15 +1569,13 @@ typedef struct #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif -#if (__FPU_PRESENT == 1U) - #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ - #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ -#endif +#define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ +#define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ /*@} */ @@ -1584,6 +1603,48 @@ typedef struct @{ */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ +#define EXC_RETURN_HANDLER_FPU (0xFFFFFFE1UL) /* return to Handler mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_MSP_FPU (0xFFFFFFE9UL) /* return to Thread mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_PSP_FPU (0xFFFFFFEDUL) /* return to Thread mode, uses PSP after return, restore floating-point state */ + + /** \brief Set Priority Grouping \details Sets the priority grouping field using the required unlock sequence. @@ -1593,7 +1654,7 @@ typedef struct priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. \param [in] PriorityGroup Priority grouping field. */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { uint32_t reg_value; uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ @@ -1602,7 +1663,7 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ reg_value = (reg_value | ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ SCB->AIRCR = reg_value; } @@ -1612,121 +1673,178 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) \details Reads the priority grouping field from the NVIC Interrupt Controller. \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) { return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); } /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not active. \return 1 Interrupt status is active. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } else { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } } /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); } else { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); } } @@ -1783,11 +1901,42 @@ __STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGr } +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -1804,6 +1953,49 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + uint32_t mvfr0; + + mvfr0 = FPU->MVFR0; + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) + { + return 1U; /* Single precision FPU */ + } + else + { + return 0U; /* No FPU */ + } +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + /* ################################## SysTick function ############################################ */ @@ -1814,7 +2006,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration @@ -1857,8 +2049,8 @@ __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) @{ */ -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ /** diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm7.h b/Firmware/ThirdParty/CMSIS/Include/core_cm7.h similarity index 85% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_cm7.h rename to Firmware/ThirdParty/CMSIS/Include/core_cm7.h index 3b7530ad5..a14dc623b 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm7.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm7.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_cm7.h * @brief CMSIS Cortex-M7 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.8 + * @date 04. June 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,60 +60,22 @@ @{ */ -/* CMSIS CM7 definitions */ -#define __CM7_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM7_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ -#define __CM7_CMSIS_VERSION ((__CM7_CMSIS_VERSION_MAIN << 16U) | \ - __CM7_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x07U) /*!< Cortex-M Core */ +#include "cmsis_version.h" +/* CMSIS CM7 definitions */ +#define __CM7_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM7_CMSIS_VERSION_SUB ( __CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM7_CMSIS_VERSION ((__CM7_CMSIS_VERSION_MAIN << 16U) | \ + __CM7_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_M (7U) /*!< Cortex-M Core */ /** __FPU_USED indicates whether an FPU is used or not. For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. */ #if defined ( __CC_ARM ) #if defined __TARGET_FPU_VFP - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -133,9 +85,9 @@ #define __FPU_USED 0U #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP - #if (__FPU_PRESENT == 1) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -147,7 +99,7 @@ #elif defined ( __GNUC__ ) #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -159,7 +111,7 @@ #elif defined ( __ICCARM__ ) #if defined __ARMVFP__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -169,9 +121,9 @@ #define __FPU_USED 0U #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -183,7 +135,7 @@ #elif defined ( __TASKING__ ) #if defined __FPU_VFP__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -195,7 +147,7 @@ #elif defined ( __CSMC__ ) #if ( __CSMC__ & 0x400U) - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -207,9 +159,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ -#include "core_cmSimd.h" /* Compiler specific SIMD Intrinsics */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -382,11 +333,12 @@ typedef union struct { uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ uint32_t C:1; /*!< bit: 29 Carry condition code flag */ @@ -412,8 +364,8 @@ typedef union #define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ #define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ #define xPSR_T_Pos 24U /*!< xPSR: T Position */ #define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ @@ -421,6 +373,9 @@ typedef union #define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ #define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + #define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ #define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ @@ -529,7 +484,7 @@ typedef struct uint32_t RESERVED4[15U]; __IM uint32_t MVFR0; /*!< Offset: 0x240 (R/ ) Media and VFP Feature Register 0 */ __IM uint32_t MVFR1; /*!< Offset: 0x244 (R/ ) Media and VFP Feature Register 1 */ - __IM uint32_t MVFR2; /*!< Offset: 0x248 (R/ ) Media and VFP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x248 (R/ ) Media and VFP Feature Register 2 */ uint32_t RESERVED5[1U]; __OM uint32_t ICIALLU; /*!< Offset: 0x250 ( /W) I-Cache Invalidate All to PoU */ uint32_t RESERVED6[1U]; @@ -715,6 +670,66 @@ typedef struct #define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ #define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + /* SCB Hard Fault Status Register Definitions */ #define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ #define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ @@ -1033,7 +1048,7 @@ typedef struct /* ITM Trace Privilege Register Definitions */ #define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ /* ITM Trace Control Register Definitions */ #define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ @@ -1250,7 +1265,7 @@ typedef struct */ typedef struct { - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ uint32_t RESERVED0[2U]; __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ @@ -1261,7 +1276,7 @@ typedef struct __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ uint32_t RESERVED4[1U]; @@ -1331,8 +1346,11 @@ typedef struct #define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ /* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ /* TPI Integration ITM Data Register Definitions (FIFO1) */ #define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ @@ -1357,12 +1375,15 @@ typedef struct #define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ /* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ /* TPI Integration Mode Control Register Definitions */ #define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ /* TPI DEVID Register Definitions */ #define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ @@ -1384,16 +1405,16 @@ typedef struct #define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ /* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ #define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + /*@}*/ /* end of group CMSIS_TPI */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -1419,6 +1440,8 @@ typedef struct __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ } MPU_Type; +#define MPU_TYPE_RALIASES 4U + /* MPU Type Register Definitions */ #define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ #define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ @@ -1485,10 +1508,9 @@ typedef struct #define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ /*@} end of group CMSIS_MPU */ -#endif +#endif /* defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) */ -#if (__FPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_FPU Floating Point Unit (FPU) @@ -1596,7 +1618,6 @@ typedef struct /* Media and FP Feature Register 2 Definitions */ /*@} end of group CMSIS_FPU */ -#endif /** @@ -1714,18 +1735,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -1737,7 +1758,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M4 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ @@ -1756,15 +1777,13 @@ typedef struct #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif -#if (__FPU_PRESENT == 1U) - #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ - #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ -#endif +#define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ +#define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ /*@} */ @@ -1792,6 +1811,48 @@ typedef struct @{ */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ +#define EXC_RETURN_HANDLER_FPU (0xFFFFFFE1UL) /* return to Handler mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_MSP_FPU (0xFFFFFFE9UL) /* return to Thread mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_PSP_FPU (0xFFFFFFEDUL) /* return to Thread mode, uses PSP after return, restore floating-point state */ + + /** \brief Set Priority Grouping \details Sets the priority grouping field using the required unlock sequence. @@ -1801,7 +1862,7 @@ typedef struct priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. \param [in] PriorityGroup Priority grouping field. */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { uint32_t reg_value; uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ @@ -1810,7 +1871,7 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ reg_value = (reg_value | ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ SCB->AIRCR = reg_value; } @@ -1820,121 +1881,178 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) \details Reads the priority grouping field from the NVIC Interrupt Controller. \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) { return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); } /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not active. \return 1 Interrupt status is active. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHPR[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } else { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } } /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return(((uint32_t)SCB->SHPR[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); } else { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); } } @@ -1991,11 +2109,42 @@ __STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGr } +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -2012,6 +2161,13 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif /* ########################## FPU functions #################################### */ /** @@ -2034,17 +2190,17 @@ __STATIC_INLINE uint32_t SCB_GetFPUType(void) uint32_t mvfr0; mvfr0 = SCB->MVFR0; - if ((mvfr0 & 0x00000FF0UL) == 0x220UL) + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x220U) { - return 2UL; /* Double + Single precision FPU */ + return 2U; /* Double + Single precision FPU */ } - else if ((mvfr0 & 0x00000FF0UL) == 0x020UL) + else if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) { - return 1UL; /* Single precision FPU */ + return 1U; /* Single precision FPU */ } else { - return 0UL; /* No FPU */ + return 0U; /* No FPU */ } } @@ -2072,10 +2228,12 @@ __STATIC_INLINE uint32_t SCB_GetFPUType(void) */ __STATIC_INLINE void SCB_EnableICache (void) { - #if (__ICACHE_PRESENT == 1U) + #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) __DSB(); __ISB(); SCB->ICIALLU = 0UL; /* invalidate I-Cache */ + __DSB(); + __ISB(); SCB->CCR |= (uint32_t)SCB_CCR_IC_Msk; /* enable I-Cache */ __DSB(); __ISB(); @@ -2089,7 +2247,7 @@ __STATIC_INLINE void SCB_EnableICache (void) */ __STATIC_INLINE void SCB_DisableICache (void) { - #if (__ICACHE_PRESENT == 1U) + #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) __DSB(); __ISB(); SCB->CCR &= ~(uint32_t)SCB_CCR_IC_Msk; /* disable I-Cache */ @@ -2106,7 +2264,7 @@ __STATIC_INLINE void SCB_DisableICache (void) */ __STATIC_INLINE void SCB_InvalidateICache (void) { - #if (__ICACHE_PRESENT == 1U) + #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) __DSB(); __ISB(); SCB->ICIALLU = 0UL; @@ -2122,12 +2280,12 @@ __STATIC_INLINE void SCB_InvalidateICache (void) */ __STATIC_INLINE void SCB_EnableDCache (void) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) uint32_t ccsidr; uint32_t sets; uint32_t ways; - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ + SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/ /* Level 1 data cache */ __DSB(); ccsidr = SCB->CCSIDR; @@ -2142,8 +2300,8 @@ __STATIC_INLINE void SCB_EnableDCache (void) #if defined ( __CC_ARM ) __schedule_barrier(); #endif - } while (ways--); - } while(sets--); + } while (ways-- != 0U); + } while(sets-- != 0U); __DSB(); SCB->CCR |= (uint32_t)SCB_CCR_DC_Msk; /* enable D-Cache */ @@ -2160,17 +2318,18 @@ __STATIC_INLINE void SCB_EnableDCache (void) */ __STATIC_INLINE void SCB_DisableDCache (void) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) uint32_t ccsidr; uint32_t sets; uint32_t ways; - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ + SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/ /* Level 1 data cache */ __DSB(); - ccsidr = SCB->CCSIDR; - SCB->CCR &= ~(uint32_t)SCB_CCR_DC_Msk; /* disable D-Cache */ + __DSB(); + + ccsidr = SCB->CCSIDR; /* clean & invalidate D-Cache */ sets = (uint32_t)(CCSIDR_SETS(ccsidr)); @@ -2182,8 +2341,8 @@ __STATIC_INLINE void SCB_DisableDCache (void) #if defined ( __CC_ARM ) __schedule_barrier(); #endif - } while (ways--); - } while(sets--); + } while (ways-- != 0U); + } while(sets-- != 0U); __DSB(); __ISB(); @@ -2197,12 +2356,12 @@ __STATIC_INLINE void SCB_DisableDCache (void) */ __STATIC_INLINE void SCB_InvalidateDCache (void) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) uint32_t ccsidr; uint32_t sets; uint32_t ways; - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ + SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/ /* Level 1 data cache */ __DSB(); ccsidr = SCB->CCSIDR; @@ -2217,8 +2376,8 @@ __STATIC_INLINE void SCB_InvalidateDCache (void) #if defined ( __CC_ARM ) __schedule_barrier(); #endif - } while (ways--); - } while(sets--); + } while (ways-- != 0U); + } while(sets-- != 0U); __DSB(); __ISB(); @@ -2232,13 +2391,13 @@ __STATIC_INLINE void SCB_InvalidateDCache (void) */ __STATIC_INLINE void SCB_CleanDCache (void) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) uint32_t ccsidr; uint32_t sets; uint32_t ways; - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ - __DSB(); + SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/ /* Level 1 data cache */ + __DSB(); ccsidr = SCB->CCSIDR; @@ -2252,8 +2411,8 @@ __STATIC_INLINE void SCB_CleanDCache (void) #if defined ( __CC_ARM ) __schedule_barrier(); #endif - } while (ways--); - } while(sets--); + } while (ways-- != 0U); + } while(sets-- != 0U); __DSB(); __ISB(); @@ -2267,12 +2426,12 @@ __STATIC_INLINE void SCB_CleanDCache (void) */ __STATIC_INLINE void SCB_CleanInvalidateDCache (void) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) uint32_t ccsidr; uint32_t sets; uint32_t ways; - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ + SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/ /* Level 1 data cache */ __DSB(); ccsidr = SCB->CCSIDR; @@ -2287,8 +2446,8 @@ __STATIC_INLINE void SCB_CleanInvalidateDCache (void) #if defined ( __CC_ARM ) __schedule_barrier(); #endif - } while (ways--); - } while(sets--); + } while (ways-- != 0U); + } while(sets-- != 0U); __DSB(); __ISB(); @@ -2304,17 +2463,17 @@ __STATIC_INLINE void SCB_CleanInvalidateDCache (void) */ __STATIC_INLINE void SCB_InvalidateDCache_by_Addr (uint32_t *addr, int32_t dsize) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) int32_t op_size = dsize; uint32_t op_addr = (uint32_t)addr; - int32_t linesize = 32U; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ + int32_t linesize = 32; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ __DSB(); while (op_size > 0) { SCB->DCIMVAC = op_addr; - op_addr += linesize; - op_size -= linesize; + op_addr += (uint32_t)linesize; + op_size -= linesize; } __DSB(); @@ -2331,17 +2490,17 @@ __STATIC_INLINE void SCB_InvalidateDCache_by_Addr (uint32_t *addr, int32_t dsize */ __STATIC_INLINE void SCB_CleanDCache_by_Addr (uint32_t *addr, int32_t dsize) { - #if (__DCACHE_PRESENT == 1) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) int32_t op_size = dsize; uint32_t op_addr = (uint32_t) addr; - int32_t linesize = 32U; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ + int32_t linesize = 32; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ __DSB(); while (op_size > 0) { SCB->DCCMVAC = op_addr; - op_addr += linesize; - op_size -= linesize; + op_addr += (uint32_t)linesize; + op_size -= linesize; } __DSB(); @@ -2358,17 +2517,17 @@ __STATIC_INLINE void SCB_CleanDCache_by_Addr (uint32_t *addr, int32_t dsize) */ __STATIC_INLINE void SCB_CleanInvalidateDCache_by_Addr (uint32_t *addr, int32_t dsize) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) int32_t op_size = dsize; uint32_t op_addr = (uint32_t) addr; - int32_t linesize = 32U; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ + int32_t linesize = 32; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ __DSB(); while (op_size > 0) { SCB->DCCIMVAC = op_addr; - op_addr += linesize; - op_size -= linesize; + op_addr += (uint32_t)linesize; + op_size -= linesize; } __DSB(); @@ -2389,7 +2548,7 @@ __STATIC_INLINE void SCB_CleanInvalidateDCache_by_Addr (uint32_t *addr, int32_t @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration @@ -2432,8 +2591,8 @@ __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) @{ */ -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ /** diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_sc000.h b/Firmware/ThirdParty/CMSIS/Include/core_sc000.h similarity index 81% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_sc000.h rename to Firmware/ThirdParty/CMSIS/Include/core_sc000.h index 514dbd81b..9b67c92f3 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_sc000.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_sc000.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_sc000.h * @brief CMSIS SC000 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.5 + * @date 28. May 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,53 +60,15 @@ @{ */ +#include "cmsis_version.h" + /* CMSIS SC000 definitions */ -#define __SC000_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __SC000_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __SC000_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __SC000_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __SC000_CMSIS_VERSION ((__SC000_CMSIS_VERSION_MAIN << 16U) | \ - __SC000_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_SC (000U) /*!< Cortex secure core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline + __SC000_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_SC (000U) /*!< Cortex secure core */ /** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all @@ -128,7 +80,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -143,7 +95,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -160,8 +112,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -569,7 +521,7 @@ typedef struct /*@} end of group CMSIS_SysTick */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -678,18 +630,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -701,7 +653,7 @@ typedef struct @{ */ -/* Memory mapping of SC000 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ @@ -712,7 +664,7 @@ typedef struct #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif @@ -742,7 +694,46 @@ typedef struct @{ */ -/* Interrupt Priorities are WORD accessible only under ARMv6M */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else +/*#define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping not available for SC000 */ +/*#define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping not available for SC000 */ + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ +/*#define NVIC_GetActive __NVIC_GetActive not available for SC000 */ + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ /* The following MACROS handle generation of the register offset and byte masks */ #define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) #define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) @@ -750,79 +741,128 @@ typedef struct /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } else { - NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } } @@ -830,32 +870,63 @@ __STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); } else { - return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); } } +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -872,6 +943,31 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + /* ################################## SysTick function ############################################ */ /** @@ -881,7 +977,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_sc300.h b/Firmware/ThirdParty/CMSIS/Include/core_sc300.h similarity index 84% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_sc300.h rename to Firmware/ThirdParty/CMSIS/Include/core_sc300.h index 8bd18aa31..3e8a47109 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_sc300.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_sc300.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_sc300.h * @brief CMSIS SC300 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.6 + * @date 04. June 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,53 +60,15 @@ @{ */ +#include "cmsis_version.h" + /* CMSIS SC300 definitions */ -#define __SC300_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __SC300_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __SC300_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __SC300_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __SC300_CMSIS_VERSION ((__SC300_CMSIS_VERSION_MAIN << 16U) | \ - __SC300_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_SC (300U) /*!< Cortex secure core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline + __SC300_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_SC (300U) /*!< Cortex secure core */ /** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all @@ -128,7 +80,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -143,7 +95,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -160,8 +112,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -191,7 +143,7 @@ #endif #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 4U + #define __NVIC_PRIO_BITS 3U #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" #endif @@ -308,9 +260,11 @@ typedef union struct { uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ + uint32_t _reserved1:8; /*!< bit: 16..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ uint32_t C:1; /*!< bit: 29 Carry condition code flag */ @@ -336,12 +290,15 @@ typedef union #define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ #define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ #define xPSR_T_Pos 24U /*!< xPSR: T Position */ #define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + #define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ #define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ @@ -599,6 +556,60 @@ typedef struct #define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ #define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + /* SCB Hard Fault Status Register Definitions */ #define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ #define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ @@ -966,7 +977,7 @@ typedef struct */ typedef struct { - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ uint32_t RESERVED0[2U]; __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ @@ -977,7 +988,7 @@ typedef struct __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ uint32_t RESERVED4[1U]; @@ -1047,8 +1058,11 @@ typedef struct #define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ /* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ /* TPI Integration ITM Data Register Definitions (FIFO1) */ #define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ @@ -1073,12 +1087,15 @@ typedef struct #define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ /* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ /* TPI Integration Mode Control Register Definitions */ #define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ /* TPI DEVID Register Definitions */ #define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ @@ -1100,16 +1117,16 @@ typedef struct #define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ /* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ #define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + /*@}*/ /* end of group CMSIS_TPI */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -1319,18 +1336,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -1342,7 +1359,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M3 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ @@ -1361,7 +1378,7 @@ typedef struct #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif @@ -1392,6 +1409,46 @@ typedef struct @{ */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + + /** \brief Set Priority Grouping \details Sets the priority grouping field using the required unlock sequence. @@ -1401,7 +1458,7 @@ typedef struct priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. \param [in] PriorityGroup Priority grouping field. */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { uint32_t reg_value; uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ @@ -1420,121 +1477,178 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) \details Reads the priority grouping field from the NVIC Interrupt Controller. \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) { return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); } /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not active. \return 1 Interrupt status is active. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } else { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } } /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); } else { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); } } @@ -1591,11 +1705,42 @@ __STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGr } +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -1613,6 +1758,31 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + /* ################################## SysTick function ############################################ */ /** @@ -1622,7 +1792,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration @@ -1665,8 +1835,8 @@ __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) @{ */ -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ /** diff --git a/Firmware/ThirdParty/CMSIS/Include/mpu_armv7.h b/Firmware/ThirdParty/CMSIS/Include/mpu_armv7.h new file mode 100644 index 000000000..01422033d --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/mpu_armv7.h @@ -0,0 +1,270 @@ +/****************************************************************************** + * @file mpu_armv7.h + * @brief CMSIS MPU API for Armv7-M MPU + * @version V5.0.4 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (c) 2017-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef ARM_MPU_ARMV7_H +#define ARM_MPU_ARMV7_H + +#define ARM_MPU_REGION_SIZE_32B ((uint8_t)0x04U) ///!< MPU Region Size 32 Bytes +#define ARM_MPU_REGION_SIZE_64B ((uint8_t)0x05U) ///!< MPU Region Size 64 Bytes +#define ARM_MPU_REGION_SIZE_128B ((uint8_t)0x06U) ///!< MPU Region Size 128 Bytes +#define ARM_MPU_REGION_SIZE_256B ((uint8_t)0x07U) ///!< MPU Region Size 256 Bytes +#define ARM_MPU_REGION_SIZE_512B ((uint8_t)0x08U) ///!< MPU Region Size 512 Bytes +#define ARM_MPU_REGION_SIZE_1KB ((uint8_t)0x09U) ///!< MPU Region Size 1 KByte +#define ARM_MPU_REGION_SIZE_2KB ((uint8_t)0x0AU) ///!< MPU Region Size 2 KBytes +#define ARM_MPU_REGION_SIZE_4KB ((uint8_t)0x0BU) ///!< MPU Region Size 4 KBytes +#define ARM_MPU_REGION_SIZE_8KB ((uint8_t)0x0CU) ///!< MPU Region Size 8 KBytes +#define ARM_MPU_REGION_SIZE_16KB ((uint8_t)0x0DU) ///!< MPU Region Size 16 KBytes +#define ARM_MPU_REGION_SIZE_32KB ((uint8_t)0x0EU) ///!< MPU Region Size 32 KBytes +#define ARM_MPU_REGION_SIZE_64KB ((uint8_t)0x0FU) ///!< MPU Region Size 64 KBytes +#define ARM_MPU_REGION_SIZE_128KB ((uint8_t)0x10U) ///!< MPU Region Size 128 KBytes +#define ARM_MPU_REGION_SIZE_256KB ((uint8_t)0x11U) ///!< MPU Region Size 256 KBytes +#define ARM_MPU_REGION_SIZE_512KB ((uint8_t)0x12U) ///!< MPU Region Size 512 KBytes +#define ARM_MPU_REGION_SIZE_1MB ((uint8_t)0x13U) ///!< MPU Region Size 1 MByte +#define ARM_MPU_REGION_SIZE_2MB ((uint8_t)0x14U) ///!< MPU Region Size 2 MBytes +#define ARM_MPU_REGION_SIZE_4MB ((uint8_t)0x15U) ///!< MPU Region Size 4 MBytes +#define ARM_MPU_REGION_SIZE_8MB ((uint8_t)0x16U) ///!< MPU Region Size 8 MBytes +#define ARM_MPU_REGION_SIZE_16MB ((uint8_t)0x17U) ///!< MPU Region Size 16 MBytes +#define ARM_MPU_REGION_SIZE_32MB ((uint8_t)0x18U) ///!< MPU Region Size 32 MBytes +#define ARM_MPU_REGION_SIZE_64MB ((uint8_t)0x19U) ///!< MPU Region Size 64 MBytes +#define ARM_MPU_REGION_SIZE_128MB ((uint8_t)0x1AU) ///!< MPU Region Size 128 MBytes +#define ARM_MPU_REGION_SIZE_256MB ((uint8_t)0x1BU) ///!< MPU Region Size 256 MBytes +#define ARM_MPU_REGION_SIZE_512MB ((uint8_t)0x1CU) ///!< MPU Region Size 512 MBytes +#define ARM_MPU_REGION_SIZE_1GB ((uint8_t)0x1DU) ///!< MPU Region Size 1 GByte +#define ARM_MPU_REGION_SIZE_2GB ((uint8_t)0x1EU) ///!< MPU Region Size 2 GBytes +#define ARM_MPU_REGION_SIZE_4GB ((uint8_t)0x1FU) ///!< MPU Region Size 4 GBytes + +#define ARM_MPU_AP_NONE 0U ///!< MPU Access Permission no access +#define ARM_MPU_AP_PRIV 1U ///!< MPU Access Permission privileged access only +#define ARM_MPU_AP_URO 2U ///!< MPU Access Permission unprivileged access read-only +#define ARM_MPU_AP_FULL 3U ///!< MPU Access Permission full access +#define ARM_MPU_AP_PRO 5U ///!< MPU Access Permission privileged access read-only +#define ARM_MPU_AP_RO 6U ///!< MPU Access Permission read-only access + +/** MPU Region Base Address Register Value +* +* \param Region The region to be configured, number 0 to 15. +* \param BaseAddress The base address for the region. +*/ +#define ARM_MPU_RBAR(Region, BaseAddress) \ + (((BaseAddress) & MPU_RBAR_ADDR_Msk) | \ + ((Region) & MPU_RBAR_REGION_Msk) | \ + (MPU_RBAR_VALID_Msk)) + +/** +* MPU Memory Access Attributes +* +* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +* \param IsShareable Region is shareable between multiple bus masters. +* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache. +* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +*/ +#define ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable) \ + ((((TypeExtField ) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk) | \ + (((IsShareable ) << MPU_RASR_S_Pos) & MPU_RASR_S_Msk) | \ + (((IsCacheable ) << MPU_RASR_C_Pos) & MPU_RASR_C_Msk) | \ + (((IsBufferable ) << MPU_RASR_B_Pos) & MPU_RASR_B_Msk)) + +/** +* MPU Region Attribute and Size Register Value +* +* \param DisableExec Instruction access disable bit, 1= disable instruction fetches. +* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param AccessAttributes Memory access attribution, see \ref ARM_MPU_ACCESS_. +* \param SubRegionDisable Sub-region disable field. +* \param Size Region size of the region to be configured, for example 4K, 8K. +*/ +#define ARM_MPU_RASR_EX(DisableExec, AccessPermission, AccessAttributes, SubRegionDisable, Size) \ + ((((DisableExec ) << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) | \ + (((AccessPermission) << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) | \ + (((AccessAttributes) ) & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk))) + +/** +* MPU Region Attribute and Size Register Value +* +* \param DisableExec Instruction access disable bit, 1= disable instruction fetches. +* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +* \param IsShareable Region is shareable between multiple bus masters. +* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache. +* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +* \param SubRegionDisable Sub-region disable field. +* \param Size Region size of the region to be configured, for example 4K, 8K. +*/ +#define ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size) \ + ARM_MPU_RASR_EX(DisableExec, AccessPermission, ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable), SubRegionDisable, Size) + +/** +* MPU Memory Access Attribute for strongly ordered memory. +* - TEX: 000b +* - Shareable +* - Non-cacheable +* - Non-bufferable +*/ +#define ARM_MPU_ACCESS_ORDERED ARM_MPU_ACCESS_(0U, 1U, 0U, 0U) + +/** +* MPU Memory Access Attribute for device memory. +* - TEX: 000b (if non-shareable) or 010b (if shareable) +* - Shareable or non-shareable +* - Non-cacheable +* - Bufferable (if shareable) or non-bufferable (if non-shareable) +* +* \param IsShareable Configures the device memory as shareable or non-shareable. +*/ +#define ARM_MPU_ACCESS_DEVICE(IsShareable) ((IsShareable) ? ARM_MPU_ACCESS_(0U, 1U, 0U, 1U) : ARM_MPU_ACCESS_(2U, 0U, 0U, 0U)) + +/** +* MPU Memory Access Attribute for normal memory. +* - TEX: 1BBb (reflecting outer cacheability rules) +* - Shareable or non-shareable +* - Cacheable or non-cacheable (reflecting inner cacheability rules) +* - Bufferable or non-bufferable (reflecting inner cacheability rules) +* +* \param OuterCp Configures the outer cache policy. +* \param InnerCp Configures the inner cache policy. +* \param IsShareable Configures the memory as shareable or non-shareable. +*/ +#define ARM_MPU_ACCESS_NORMAL(OuterCp, InnerCp, IsShareable) ARM_MPU_ACCESS_((4U | (OuterCp)), IsShareable, ((InnerCp) & 2U), ((InnerCp) & 1U)) + +/** +* MPU Memory Access Attribute non-cacheable policy. +*/ +#define ARM_MPU_CACHEP_NOCACHE 0U + +/** +* MPU Memory Access Attribute write-back, write and read allocate policy. +*/ +#define ARM_MPU_CACHEP_WB_WRA 1U + +/** +* MPU Memory Access Attribute write-through, no write allocate policy. +*/ +#define ARM_MPU_CACHEP_WT_NWA 2U + +/** +* MPU Memory Access Attribute write-back, no write allocate policy. +*/ +#define ARM_MPU_CACHEP_WB_NWA 3U + + +/** +* Struct for a single MPU Region +*/ +typedef struct { + uint32_t RBAR; //!< The region base address register value (RBAR) + uint32_t RASR; //!< The region attribute and size register value (RASR) \ref MPU_RASR +} ARM_MPU_Region_t; + +/** Enable the MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control) +{ + __DSB(); + __ISB(); + MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif +} + +/** Disable the MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable(void) +{ + __DSB(); + __ISB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; +} + +/** Clear and disable the given MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr) +{ + MPU->RNR = rnr; + MPU->RASR = 0U; +} + +/** Configure an MPU region. +* \param rbar Value for RBAR register. +* \param rsar Value for RSAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rbar, uint32_t rasr) +{ + MPU->RBAR = rbar; + MPU->RASR = rasr; +} + +/** Configure the given MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rsar Value for RSAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegionEx(uint32_t rnr, uint32_t rbar, uint32_t rasr) +{ + MPU->RNR = rnr; + MPU->RBAR = rbar; + MPU->RASR = rasr; +} + +/** Memcopy with strictly ordered memory access, e.g. for register targets. +* \param dst Destination data is copied to. +* \param src Source data is copied from. +* \param len Amount of data words to be copied. +*/ +__STATIC_INLINE void orderedCpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len) +{ + uint32_t i; + for (i = 0U; i < len; ++i) + { + dst[i] = src[i]; + } +} + +/** Load the given number of MPU regions from a table. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load(ARM_MPU_Region_t const* table, uint32_t cnt) +{ + const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U; + while (cnt > MPU_TYPE_RALIASES) { + orderedCpy(&(MPU->RBAR), &(table->RBAR), MPU_TYPE_RALIASES*rowWordSize); + table += MPU_TYPE_RALIASES; + cnt -= MPU_TYPE_RALIASES; + } + orderedCpy(&(MPU->RBAR), &(table->RBAR), cnt*rowWordSize); +} + +#endif diff --git a/Firmware/ThirdParty/CMSIS/Include/mpu_armv8.h b/Firmware/ThirdParty/CMSIS/Include/mpu_armv8.h new file mode 100644 index 000000000..62571da5b --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/mpu_armv8.h @@ -0,0 +1,333 @@ +/****************************************************************************** + * @file mpu_armv8.h + * @brief CMSIS MPU API for Armv8-M MPU + * @version V5.0.4 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (c) 2017-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef ARM_MPU_ARMV8_H +#define ARM_MPU_ARMV8_H + +/** \brief Attribute for device memory (outer only) */ +#define ARM_MPU_ATTR_DEVICE ( 0U ) + +/** \brief Attribute for non-cacheable, normal memory */ +#define ARM_MPU_ATTR_NON_CACHEABLE ( 4U ) + +/** \brief Attribute for normal memory (outer and inner) +* \param NT Non-Transient: Set to 1 for non-transient data. +* \param WB Write-Back: Set to 1 to use write-back update policy. +* \param RA Read Allocation: Set to 1 to use cache allocation on read miss. +* \param WA Write Allocation: Set to 1 to use cache allocation on write miss. +*/ +#define ARM_MPU_ATTR_MEMORY_(NT, WB, RA, WA) \ + (((NT & 1U) << 3U) | ((WB & 1U) << 2U) | ((RA & 1U) << 1U) | (WA & 1U)) + +/** \brief Device memory type non Gathering, non Re-ordering, non Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_nGnRnE (0U) + +/** \brief Device memory type non Gathering, non Re-ordering, Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_nGnRE (1U) + +/** \brief Device memory type non Gathering, Re-ordering, Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_nGRE (2U) + +/** \brief Device memory type Gathering, Re-ordering, Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_GRE (3U) + +/** \brief Memory Attribute +* \param O Outer memory attributes +* \param I O == ARM_MPU_ATTR_DEVICE: Device memory attributes, else: Inner memory attributes +*/ +#define ARM_MPU_ATTR(O, I) (((O & 0xFU) << 4U) | (((O & 0xFU) != 0U) ? (I & 0xFU) : ((I & 0x3U) << 2U))) + +/** \brief Normal memory non-shareable */ +#define ARM_MPU_SH_NON (0U) + +/** \brief Normal memory outer shareable */ +#define ARM_MPU_SH_OUTER (2U) + +/** \brief Normal memory inner shareable */ +#define ARM_MPU_SH_INNER (3U) + +/** \brief Memory access permissions +* \param RO Read-Only: Set to 1 for read-only memory. +* \param NP Non-Privileged: Set to 1 for non-privileged memory. +*/ +#define ARM_MPU_AP_(RO, NP) (((RO & 1U) << 1U) | (NP & 1U)) + +/** \brief Region Base Address Register value +* \param BASE The base address bits [31:5] of a memory region. The value is zero extended. Effective address gets 32 byte aligned. +* \param SH Defines the Shareability domain for this memory region. +* \param RO Read-Only: Set to 1 for a read-only memory region. +* \param NP Non-Privileged: Set to 1 for a non-privileged memory region. +* \oaram XN eXecute Never: Set to 1 for a non-executable memory region. +*/ +#define ARM_MPU_RBAR(BASE, SH, RO, NP, XN) \ + ((BASE & MPU_RBAR_BASE_Msk) | \ + ((SH << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk) | \ + ((ARM_MPU_AP_(RO, NP) << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk) | \ + ((XN << MPU_RBAR_XN_Pos) & MPU_RBAR_XN_Msk)) + +/** \brief Region Limit Address Register value +* \param LIMIT The limit address bits [31:5] for this memory region. The value is one extended. +* \param IDX The attribute index to be associated with this memory region. +*/ +#define ARM_MPU_RLAR(LIMIT, IDX) \ + ((LIMIT & MPU_RLAR_LIMIT_Msk) | \ + ((IDX << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | \ + (MPU_RLAR_EN_Msk)) + +/** +* Struct for a single MPU Region +*/ +typedef struct { + uint32_t RBAR; /*!< Region Base Address Register value */ + uint32_t RLAR; /*!< Region Limit Address Register value */ +} ARM_MPU_Region_t; + +/** Enable the MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control) +{ + __DSB(); + __ISB(); + MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif +} + +/** Disable the MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable(void) +{ + __DSB(); + __ISB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; +} + +#ifdef MPU_NS +/** Enable the Non-secure MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable_NS(uint32_t MPU_Control) +{ + __DSB(); + __ISB(); + MPU_NS->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB_NS->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif +} + +/** Disable the Non-secure MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable_NS(void) +{ + __DSB(); + __ISB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB_NS->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU_NS->CTRL &= ~MPU_CTRL_ENABLE_Msk; +} +#endif + +/** Set the memory attribute encoding to the given MPU. +* \param mpu Pointer to the MPU to be configured. +* \param idx The attribute index to be set [0-7] +* \param attr The attribute value to be set. +*/ +__STATIC_INLINE void ARM_MPU_SetMemAttrEx(MPU_Type* mpu, uint8_t idx, uint8_t attr) +{ + const uint8_t reg = idx / 4U; + const uint32_t pos = ((idx % 4U) * 8U); + const uint32_t mask = 0xFFU << pos; + + if (reg >= (sizeof(mpu->MAIR) / sizeof(mpu->MAIR[0]))) { + return; // invalid index + } + + mpu->MAIR[reg] = ((mpu->MAIR[reg] & ~mask) | ((attr << pos) & mask)); +} + +/** Set the memory attribute encoding. +* \param idx The attribute index to be set [0-7] +* \param attr The attribute value to be set. +*/ +__STATIC_INLINE void ARM_MPU_SetMemAttr(uint8_t idx, uint8_t attr) +{ + ARM_MPU_SetMemAttrEx(MPU, idx, attr); +} + +#ifdef MPU_NS +/** Set the memory attribute encoding to the Non-secure MPU. +* \param idx The attribute index to be set [0-7] +* \param attr The attribute value to be set. +*/ +__STATIC_INLINE void ARM_MPU_SetMemAttr_NS(uint8_t idx, uint8_t attr) +{ + ARM_MPU_SetMemAttrEx(MPU_NS, idx, attr); +} +#endif + +/** Clear and disable the given MPU region of the given MPU. +* \param mpu Pointer to MPU to be used. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegionEx(MPU_Type* mpu, uint32_t rnr) +{ + mpu->RNR = rnr; + mpu->RLAR = 0U; +} + +/** Clear and disable the given MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr) +{ + ARM_MPU_ClrRegionEx(MPU, rnr); +} + +#ifdef MPU_NS +/** Clear and disable the given Non-secure MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion_NS(uint32_t rnr) +{ + ARM_MPU_ClrRegionEx(MPU_NS, rnr); +} +#endif + +/** Configure the given MPU region of the given MPU. +* \param mpu Pointer to MPU to be used. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rlar Value for RLAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegionEx(MPU_Type* mpu, uint32_t rnr, uint32_t rbar, uint32_t rlar) +{ + mpu->RNR = rnr; + mpu->RBAR = rbar; + mpu->RLAR = rlar; +} + +/** Configure the given MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rlar Value for RLAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rnr, uint32_t rbar, uint32_t rlar) +{ + ARM_MPU_SetRegionEx(MPU, rnr, rbar, rlar); +} + +#ifdef MPU_NS +/** Configure the given Non-secure MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rlar Value for RLAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion_NS(uint32_t rnr, uint32_t rbar, uint32_t rlar) +{ + ARM_MPU_SetRegionEx(MPU_NS, rnr, rbar, rlar); +} +#endif + +/** Memcopy with strictly ordered memory access, e.g. for register targets. +* \param dst Destination data is copied to. +* \param src Source data is copied from. +* \param len Amount of data words to be copied. +*/ +__STATIC_INLINE void orderedCpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len) +{ + uint32_t i; + for (i = 0U; i < len; ++i) + { + dst[i] = src[i]; + } +} + +/** Load the given number of MPU regions from a table to the given MPU. +* \param mpu Pointer to the MPU registers to be used. +* \param rnr First region number to be configured. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_LoadEx(MPU_Type* mpu, uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt) +{ + const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U; + if (cnt == 1U) { + mpu->RNR = rnr; + orderedCpy(&(mpu->RBAR), &(table->RBAR), rowWordSize); + } else { + uint32_t rnrBase = rnr & ~(MPU_TYPE_RALIASES-1U); + uint32_t rnrOffset = rnr % MPU_TYPE_RALIASES; + + mpu->RNR = rnrBase; + while ((rnrOffset + cnt) > MPU_TYPE_RALIASES) { + uint32_t c = MPU_TYPE_RALIASES - rnrOffset; + orderedCpy(&(mpu->RBAR)+(rnrOffset*2U), &(table->RBAR), c*rowWordSize); + table += c; + cnt -= c; + rnrOffset = 0U; + rnrBase += MPU_TYPE_RALIASES; + mpu->RNR = rnrBase; + } + + orderedCpy(&(mpu->RBAR)+(rnrOffset*2U), &(table->RBAR), cnt*rowWordSize); + } +} + +/** Load the given number of MPU regions from a table. +* \param rnr First region number to be configured. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load(uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt) +{ + ARM_MPU_LoadEx(MPU, rnr, table, cnt); +} + +#ifdef MPU_NS +/** Load the given number of MPU regions from a table to the Non-secure MPU. +* \param rnr First region number to be configured. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load_NS(uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt) +{ + ARM_MPU_LoadEx(MPU_NS, rnr, table, cnt); +} +#endif + +#endif + diff --git a/Firmware/ThirdParty/CMSIS/Include/tz_context.h b/Firmware/ThirdParty/CMSIS/Include/tz_context.h new file mode 100644 index 000000000..0d09749f3 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/tz_context.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * @file tz_context.h + * @brief Context Management for Armv8-M TrustZone + * @version V1.0.1 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (c) 2017-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.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 + * + * 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. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef TZ_CONTEXT_H +#define TZ_CONTEXT_H + +#include + +#ifndef TZ_MODULEID_T +#define TZ_MODULEID_T +/// \details Data type that identifies secure software modules called by a process. +typedef uint32_t TZ_ModuleId_t; +#endif + +/// \details TZ Memory ID identifies an allocated memory slot. +typedef uint32_t TZ_MemoryId_t; + +/// Initialize secure context memory system +/// \return execution status (1: success, 0: error) +uint32_t TZ_InitContextSystem_S (void); + +/// Allocate context memory for calling secure software modules in TrustZone +/// \param[in] module identifies software modules called from non-secure mode +/// \return value != 0 id TrustZone memory slot identifier +/// \return value 0 no memory available or internal error +TZ_MemoryId_t TZ_AllocModuleContext_S (TZ_ModuleId_t module); + +/// Free context memory that was previously allocated with \ref TZ_AllocModuleContext_S +/// \param[in] id TrustZone memory slot identifier +/// \return execution status (1: success, 0: error) +uint32_t TZ_FreeModuleContext_S (TZ_MemoryId_t id); + +/// Load secure context (called on RTOS thread context switch) +/// \param[in] id TrustZone memory slot identifier +/// \return execution status (1: success, 0: error) +uint32_t TZ_LoadContext_S (TZ_MemoryId_t id); + +/// Store secure context (called on RTOS thread context switch) +/// \param[in] id TrustZone memory slot identifier +/// \return execution status (1: success, 0: error) +uint32_t TZ_StoreContext_S (TZ_MemoryId_t id); + +#endif // TZ_CONTEXT_H diff --git a/Firmware/Board/v3/Drivers/CMSIS/Lib/libarm_cortexM4lf_math.a b/Firmware/ThirdParty/CMSIS/Lib/GCC/libarm_cortexM4lf_math.a similarity index 100% rename from Firmware/Board/v3/Drivers/CMSIS/Lib/libarm_cortexM4lf_math.a rename to Firmware/ThirdParty/CMSIS/Lib/GCC/libarm_cortexM4lf_math.a diff --git a/Firmware/ThirdParty/CMSIS/Lib/GCC/libarm_cortexM7lfsp_math.a b/Firmware/ThirdParty/CMSIS/Lib/GCC/libarm_cortexM7lfsp_math.a new file mode 100644 index 000000000..36c7461ce Binary files /dev/null and b/Firmware/ThirdParty/CMSIS/Lib/GCC/libarm_cortexM7lfsp_math.a differ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c b/Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c similarity index 94% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c rename to Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c index 7828403b6..fa7f303e9 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c @@ -26,7 +26,7 @@ * *---------------------------------------------------------------------------- * - * Portions Copyright © 2016 STMicroelectronics International N.V. All rights reserved. + * Portions Copyright � 2016 STMicroelectronics International N.V. All rights reserved. * Portions Copyright (c) 2013 ARM LIMITED * All rights reserved. * Redistribution and use in source and binary forms, with or without @@ -53,49 +53,6 @@ * POSSIBILITY OF SUCH DAMAGE. *---------------------------------------------------------------------------*/ - /** - ****************************************************************************** - * @file cmsis_os.c - * @author MCD Application Team - * @date 13-July-2017 - * @brief CMSIS-RTOS API implementation for FreeRTOS V9.0.0 - ****************************************************************************** - * @attention - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted, provided that the following conditions are met: - * - * 1. Redistribution of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of other - * contributors to this software may be used to endorse or promote products - * derived from this software without specific written permission. - * 4. This software, including modifications and/or derivative works of this - * software, must execute solely and exclusively on microcontroller or - * microprocessor devices manufactured by or for STMicroelectronics. - * 5. Redistribution and use of this software other than as permitted under - * this license is void and will automatically terminate your rights under - * this license. - * - * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY - * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT - * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ - #include #include "cmsis_os.h" @@ -407,7 +364,7 @@ osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void 1, // period should be filled when starting the Timer using osTimerStart (type == osTimerPeriodic) ? pdTRUE : pdFALSE, (void *) argument, - (TaskFunction_t)timer_def->ptimer, + (TimerCallbackFunction_t)timer_def->ptimer, (StaticTimer_t *)timer_def->controlblock); } else { @@ -415,21 +372,21 @@ osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void 1, // period should be filled when starting the Timer using osTimerStart (type == osTimerPeriodic) ? pdTRUE : pdFALSE, (void *) argument, - (TaskFunction_t)timer_def->ptimer); + (TimerCallbackFunction_t)timer_def->ptimer); } #elif( configSUPPORT_STATIC_ALLOCATION == 1 ) return xTimerCreateStatic((const char *)"", 1, // period should be filled when starting the Timer using osTimerStart (type == osTimerPeriodic) ? pdTRUE : pdFALSE, (void *) argument, - (TaskFunction_t)timer_def->ptimer, + (TimerCallbackFunction_t)timer_def->ptimer, (StaticTimer_t *)timer_def->controlblock); #else return xTimerCreate((const char *)"", 1, // period should be filled when starting the Timer using osTimerStart (type == osTimerPeriodic) ? pdTRUE : pdFALSE, (void *) argument, - (TaskFunction_t)timer_def->ptimer); + (TimerCallbackFunction_t)timer_def->ptimer); #endif #else @@ -991,10 +948,7 @@ void *osPoolAlloc (osPoolId pool_id) } for (i = 0; i < pool_id->pool_sz; i++) { - index = pool_id->currentIndex + i; - if (index >= pool_id->pool_sz) { - index = 0; - } + index = (pool_id->currentIndex + i) % pool_id->pool_sz; if (pool_id->markers[index] == 0) { pool_id->markers[index] = 1; diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h b/Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h similarity index 95% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h rename to Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h index 89a105dca..2f24df0f8 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h @@ -53,51 +53,6 @@ * POSSIBILITY OF SUCH DAMAGE. *---------------------------------------------------------------------------*/ - /** - ****************************************************************************** - * @file cmsis_os.h - * @author MCD Application Team - * @date 13-July-2017 - * @brief Header of cmsis_os.c - * A new set of APIs are added in addition to existing ones, these APIs - * are specific to FreeRTOS. - ****************************************************************************** - * @attention - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted, provided that the following conditions are met: - * - * 1. Redistribution of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of other - * contributors to this software may be used to endorse or promote products - * derived from this software without specific written permission. - * 4. This software, including modifications and/or derivative works of this - * software, must execute solely and exclusively on microcontroller or - * microprocessor devices manufactured by or for STMicroelectronics. - * 5. Redistribution and use of this software other than as permitted under - * this license is void and will automatically terminate your rights under - * this license. - * - * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY - * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT - * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ - #include "FreeRTOS.h" #include "task.h" #include "timers.h" diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/croutine.c b/Firmware/ThirdParty/FreeRTOS/Source/croutine.c similarity index 74% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/croutine.c rename to Firmware/ThirdParty/FreeRTOS/Source/croutine.c index 993e09b29..56c8ac290 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/croutine.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/croutine.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #include "FreeRTOS.h" #include "task.h" @@ -302,7 +260,7 @@ CRCB_t *pxCRCB; ( void ) uxListRemove( &( pxCRCB->xGenericListItem ) ); /* Is the co-routine waiting on an event also? */ - if( pxCRCB->xEventListItem.pvContainer ) + if( pxCRCB->xEventListItem.pxContainer ) { ( void ) uxListRemove( &( pxCRCB->xEventListItem ) ); } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c b/Firmware/ThirdParty/FreeRTOS/Source/event_groups.c similarity index 75% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c rename to Firmware/ThirdParty/FreeRTOS/Source/event_groups.c index b23ecb1c9..65a5ff259 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/event_groups.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* Standard includes. */ #include @@ -81,11 +39,11 @@ task.h is included from an application file. */ #include "timers.h" #include "event_groups.h" -/* Lint e961 and e750 are suppressed as a MISRA exception justified because the -MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the -header files above, but not in this file, in order to generate the correct -privileged Vs unprivileged linkage and placement. */ -#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ +/* Lint e961, e750 and e9021 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021 See comment above. */ /* The following bit fields convey control information in a task's event list item value. It is important they don't clash with the @@ -102,7 +60,7 @@ taskEVENT_LIST_ITEM_VALUE_IN_USE definition. */ #define eventEVENT_BITS_CONTROL_BYTES 0xff000000UL #endif -typedef struct xEventGroupDefinition +typedef struct EventGroupDef_t { EventBits_t uxEventBits; List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */ @@ -126,7 +84,7 @@ typedef struct xEventGroupDefinition * wait condition is met if any of the bits set in uxBitsToWait for are also set * in uxCurrentEventBits. */ -PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ); +static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ) PRIVILEGED_FUNCTION; /*-----------------------------------------------------------*/ @@ -139,8 +97,18 @@ PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t ux /* A StaticEventGroup_t object must be provided. */ configASSERT( pxEventGroupBuffer ); + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticEventGroup_t equals the size of the real + event group structure. */ + volatile size_t xSize = sizeof( StaticEventGroup_t ); + configASSERT( xSize == sizeof( EventGroup_t ) ); + } /*lint !e529 xSize is referenced if configASSERT() is defined. */ + #endif /* configASSERT_DEFINED */ + /* The user has provided a statically allocated event group - use it. */ - pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 EventGroup_t and StaticEventGroup_t are guaranteed to have the same size and alignment requirement - checked by configASSERT(). */ + pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 !e9087 EventGroup_t and StaticEventGroup_t are deliberately aliased for data hiding purposes and guaranteed to have the same size and alignment requirement - checked by configASSERT(). */ if( pxEventBits != NULL ) { @@ -160,10 +128,13 @@ PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t ux } else { + /* xEventGroupCreateStatic should only ever be called with + pxEventGroupBuffer pointing to a pre-allocated (compile time + allocated) StaticEventGroup_t variable. */ traceEVENT_GROUP_CREATE_FAILED(); } - return ( EventGroupHandle_t ) pxEventBits; + return pxEventBits; } #endif /* configSUPPORT_STATIC_ALLOCATION */ @@ -175,8 +146,20 @@ PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t ux { EventGroup_t *pxEventBits; - /* Allocate the event group. */ - pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); + /* Allocate the event group. Justification for MISRA deviation as + follows: pvPortMalloc() always ensures returned memory blocks are + aligned per the requirements of the MCU stack. In this case + pvPortMalloc() must return a pointer that is guaranteed to meet the + alignment requirements of the EventGroup_t structure - which (if you + follow it through) is the alignment requirements of the TickType_t type + (EventBits_t being of TickType_t itself). Therefore, whenever the + stack alignment requirements are greater than or equal to the + TickType_t alignment requirements the cast is safe. In other cases, + where the natural word size of the architecture is less than + sizeof( TickType_t ), the TickType_t variables will be accessed in two + or more reads operations, and the alignment requirements is only that + of each individual read. */ + pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); /*lint !e9087 !e9079 see comment above. */ if( pxEventBits != NULL ) { @@ -196,10 +179,10 @@ PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t ux } else { - traceEVENT_GROUP_CREATE_FAILED(); + traceEVENT_GROUP_CREATE_FAILED(); /*lint !e9063 Else branch only exists to allow tracing and does not generate code if trace macros are not defined. */ } - return ( EventGroupHandle_t ) pxEventBits; + return pxEventBits; } #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ @@ -208,7 +191,7 @@ PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t ux EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) { EventBits_t uxOriginalBitValue, uxReturn; -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t *pxEventBits = xEventGroup; BaseType_t xAlreadyYielded; BaseType_t xTimeoutOccurred = pdFALSE; @@ -259,6 +242,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; /* The rendezvous bits were not set, but no block time was specified - just return the current event bit value. */ uxReturn = pxEventBits->uxEventBits; + xTimeoutOccurred = pdTRUE; } } } @@ -317,13 +301,16 @@ BaseType_t xTimeoutOccurred = pdFALSE; traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ); + /* Prevent compiler warnings when trace macros are not used. */ + ( void ) xTimeoutOccurred; + return uxReturn; } /*-----------------------------------------------------------*/ EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) { -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t *pxEventBits = xEventGroup; EventBits_t uxReturn, uxControlBits = 0; BaseType_t xWaitConditionMet, xAlreadyYielded; BaseType_t xTimeoutOccurred = pdFALSE; @@ -368,6 +355,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; /* The wait condition has not been met, but no block time was specified, so just return the current value. */ uxReturn = uxCurrentEventBits; + xTimeoutOccurred = pdTRUE; } else { @@ -449,11 +437,9 @@ BaseType_t xTimeoutOccurred = pdFALSE; { mtCOVERAGE_TEST_MARKER(); } + xTimeoutOccurred = pdTRUE; } taskEXIT_CRITICAL(); - - /* Prevent compiler warnings when trace macros are not used. */ - xTimeoutOccurred = pdFALSE; } else { @@ -465,13 +451,16 @@ BaseType_t xTimeoutOccurred = pdFALSE; } traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ); + /* Prevent compiler warnings when trace macros are not used. */ + ( void ) xTimeoutOccurred; + return uxReturn; } /*-----------------------------------------------------------*/ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) { -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t *pxEventBits = xEventGroup; EventBits_t uxReturn; /* Check the user is not attempting to clear the bits used by the kernel @@ -503,7 +492,7 @@ EventBits_t uxReturn; BaseType_t xReturn; traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ); - xReturn = xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); + xReturn = xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */ return xReturn; } @@ -514,7 +503,7 @@ EventBits_t uxReturn; EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) { UBaseType_t uxSavedInterruptStatus; -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t const * const pxEventBits = xEventGroup; EventBits_t uxReturn; uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); @@ -524,16 +513,16 @@ EventBits_t uxReturn; portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); return uxReturn; -} +} /*lint !e818 EventGroupHandle_t is a typedef used in other functions to so can't be pointer to const. */ /*-----------------------------------------------------------*/ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) { ListItem_t *pxListItem, *pxNext; ListItem_t const *pxListEnd; -List_t *pxList; +List_t const * pxList; EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits; -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t *pxEventBits = xEventGroup; BaseType_t xMatchFound = pdFALSE; /* Check the user is not attempting to set the bits used by the kernel @@ -542,7 +531,7 @@ BaseType_t xMatchFound = pdFALSE; configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); pxList = &( pxEventBits->xTasksWaitingForBits ); - pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ vTaskSuspendAll(); { traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ); @@ -602,7 +591,7 @@ BaseType_t xMatchFound = pdFALSE; eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows that is was unblocked due to its required bits matching, rather than because it timed out. */ - ( void ) xTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET ); + vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET ); } /* Move onto the next list item. Note pxListItem->pxNext is not @@ -623,7 +612,7 @@ BaseType_t xMatchFound = pdFALSE; void vEventGroupDelete( EventGroupHandle_t xEventGroup ) { -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t *pxEventBits = xEventGroup; const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); vTaskSuspendAll(); @@ -633,9 +622,9 @@ const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 ) { /* Unblock the task, returning 0 as the event list is being deleted - and cannot therefore have any bits set. */ - configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) ); - ( void ) xTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); + and cannot therefore have any bits set. */ + configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) ); + vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); } #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) @@ -667,7 +656,7 @@ const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); an interrupt. */ void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ) { - ( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet ); + ( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */ } /*-----------------------------------------------------------*/ @@ -675,7 +664,7 @@ void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet an interrupt. */ void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ) { - ( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear ); + ( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */ } /*-----------------------------------------------------------*/ @@ -721,7 +710,7 @@ BaseType_t xWaitConditionMet = pdFALSE; BaseType_t xReturn; traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ); - xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); + xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */ return xReturn; } @@ -734,7 +723,7 @@ BaseType_t xWaitConditionMet = pdFALSE; UBaseType_t uxEventGroupGetNumber( void* xEventGroup ) { UBaseType_t xReturn; - EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; + EventGroup_t const *pxEventBits = ( EventGroup_t * ) xEventGroup; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */ if( xEventGroup == NULL ) { @@ -748,5 +737,17 @@ BaseType_t xWaitConditionMet = pdFALSE; return xReturn; } -#endif +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vEventGroupSetNumber( void * xEventGroup, UBaseType_t uxEventGroupNumber ) + { + ( ( EventGroup_t * ) xEventGroup )->uxEventGroupNumber = uxEventGroupNumber; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */ + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h b/Firmware/ThirdParty/FreeRTOS/Source/include/FreeRTOS.h similarity index 69% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/FreeRTOS.h index f81172dbe..9d09d91af 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/FreeRTOS.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef INC_FREERTOS_H #define INC_FREERTOS_H @@ -126,6 +84,10 @@ extern "C" { #error Missing definition: configMAX_PRIORITIES must be defined in FreeRTOSConfig.h. See the Configuration section of the FreeRTOS API documentation for details. #endif +#if configMAX_PRIORITIES < 1 + #error configMAX_PRIORITIES must be defined to be greater than or equal to 1. +#endif + #ifndef configUSE_PREEMPTION #error Missing definition: configUSE_PREEMPTION must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. #endif @@ -142,10 +104,6 @@ extern "C" { #error Missing definition: configUSE_16_BIT_TICKS must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. #endif -#ifndef configMAX_PRIORITIES - #error configMAX_PRIORITIES must be defined to be greater than or equal to 1. -#endif - #ifndef configUSE_CO_ROUTINES #define configUSE_CO_ROUTINES 0 #endif @@ -198,6 +156,10 @@ extern "C" { #define INCLUDE_uxTaskGetStackHighWaterMark 0 #endif +#ifndef INCLUDE_uxTaskGetStackHighWaterMark2 + #define INCLUDE_uxTaskGetStackHighWaterMark2 0 +#endif + #ifndef INCLUDE_eTaskGetState #define INCLUDE_eTaskGetState 0 #endif @@ -279,6 +241,10 @@ extern "C" { #define configASSERT_DEFINED 1 #endif +#ifndef portMEMORY_BARRIER + #define portMEMORY_BARRIER() +#endif + /* The timers module relies on xTaskGetSchedulerState(). */ #if configUSE_TIMERS == 1 @@ -396,6 +362,14 @@ extern "C" { #define traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ) #endif +#ifndef traceBLOCKING_ON_QUEUE_PEEK + /* Task is about to block because it cannot read from a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the read was attempted. pxCurrentTCB points to the TCB of the + task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_PEEK( pxQueue ) +#endif + #ifndef traceBLOCKING_ON_QUEUE_SEND /* Task is about to block because it cannot write to a queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore @@ -408,6 +382,14 @@ extern "C" { #define configCHECK_FOR_STACK_OVERFLOW 0 #endif +#ifndef configRECORD_STACK_HIGH_ADDRESS + #define configRECORD_STACK_HIGH_ADDRESS 0 +#endif + +#ifndef configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H + #define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 0 +#endif + /* The following event macros are embedded in the kernel API calls. */ #ifndef traceMOVED_TASK_TO_READY_STATE @@ -474,6 +456,10 @@ extern "C" { #define traceQUEUE_PEEK( pxQueue ) #endif +#ifndef traceQUEUE_PEEK_FAILED + #define traceQUEUE_PEEK_FAILED( pxQueue ) +#endif + #ifndef traceQUEUE_PEEK_FROM_ISR #define traceQUEUE_PEEK_FROM_ISR( pxQueue ) #endif @@ -658,6 +644,58 @@ extern "C" { #define traceTASK_NOTIFY_GIVE_FROM_ISR() #endif +#ifndef traceSTREAM_BUFFER_CREATE_FAILED + #define traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_CREATE_STATIC_FAILED + #define traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xIsMessageBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_CREATE + #define traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xIsMessageBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_DELETE + #define traceSTREAM_BUFFER_DELETE( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RESET + #define traceSTREAM_BUFFER_RESET( xStreamBuffer ) +#endif + +#ifndef traceBLOCKING_ON_STREAM_BUFFER_SEND + #define traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND + #define traceSTREAM_BUFFER_SEND( xStreamBuffer, xBytesSent ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND_FAILED + #define traceSTREAM_BUFFER_SEND_FAILED( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND_FROM_ISR + #define traceSTREAM_BUFFER_SEND_FROM_ISR( xStreamBuffer, xBytesSent ) +#endif + +#ifndef traceBLOCKING_ON_STREAM_BUFFER_RECEIVE + #define traceBLOCKING_ON_STREAM_BUFFER_RECEIVE( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE + #define traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE_FAILED + #define traceSTREAM_BUFFER_RECEIVE_FAILED( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE_FROM_ISR + #define traceSTREAM_BUFFER_RECEIVE_FROM_ISR( xStreamBuffer, xReceivedLength ) +#endif + #ifndef configGENERATE_RUN_TIME_STATS #define configGENERATE_RUN_TIME_STATS 0 #endif @@ -708,6 +746,10 @@ extern "C" { #define configUSE_TICKLESS_IDLE 0 #endif +#ifndef configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING + #define configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( x ) +#endif + #ifndef configPRE_SLEEP_PROCESSING #define configPRE_SLEEP_PROCESSING( x ) #endif @@ -724,6 +766,14 @@ extern "C" { #define portTASK_USES_FLOATING_POINT() #endif +#ifndef portALLOCATE_SECURE_CONTEXT + #define portALLOCATE_SECURE_CONTEXT( ulSecureStackSize ) +#endif + +#ifndef portDONT_DISCARD + #define portDONT_DISCARD +#endif + #ifndef configUSE_TIME_SLICING #define configUSE_TIME_SLICING 1 #endif @@ -768,6 +818,10 @@ extern "C" { #define configUSE_TASK_NOTIFICATIONS 1 #endif +#ifndef configUSE_POSIX_ERRNO + #define configUSE_POSIX_ERRNO 0 +#endif + #ifndef portTICK_TYPE_IS_ATOMIC #define portTICK_TYPE_IS_ATOMIC 0 #endif @@ -782,6 +836,19 @@ extern "C" { #define configSUPPORT_DYNAMIC_ALLOCATION 1 #endif +#ifndef configSTACK_DEPTH_TYPE + /* Defaults to uint16_t for backward compatibility, but can be overridden + in FreeRTOSConfig.h if uint16_t is too restrictive. */ + #define configSTACK_DEPTH_TYPE uint16_t +#endif + +#ifndef configMESSAGE_BUFFER_LENGTH_TYPE + /* Defaults to size_t for backward compatibility, but can be overridden + in FreeRTOSConfig.h if lengths will always be less than the number of bytes + in a size_t. */ + #define configMESSAGE_BUFFER_LENGTH_TYPE size_t +#endif + /* Sanity check the configuration. */ #if( configUSE_TICKLESS_IDLE != 0 ) #if( INCLUDE_vTaskSuspend != 1 ) @@ -797,6 +864,10 @@ extern "C" { #error configUSE_MUTEXES must be set to 1 to use recursive mutexes #endif +#ifndef configINITIAL_TICK_COUNT + #define configINITIAL_TICK_COUNT 0 +#endif + #if( portTICK_TYPE_IS_ATOMIC == 0 ) /* Either variables of tick type cannot be read atomically, or portTICK_TYPE_IS_ATOMIC was not set - map the critical sections used when @@ -820,6 +891,32 @@ V8 if desired. */ #define configENABLE_BACKWARD_COMPATIBILITY 1 #endif +#ifndef configPRINTF + /* configPRINTF() was not defined, so define it away to nothing. To use + configPRINTF() then define it as follows (where MyPrintFunction() is + provided by the application writer): + + void MyPrintFunction(const char *pcFormat, ... ); + #define configPRINTF( X ) MyPrintFunction X + + Then call like a standard printf() function, but placing brackets around + all parameters so they are passed as a single parameter. For example: + configPRINTF( ("Value = %d", MyVariable) ); */ + #define configPRINTF( X ) +#endif + +#ifndef configMAX + /* The application writer has not provided their own MAX macro, so define + the following generic implementation. */ + #define configMAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#endif + +#ifndef configMIN + /* The application writer has not provided their own MAX macro, so define + the following generic implementation. */ + #define configMIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#endif + #if configENABLE_BACKWARD_COMPATIBILITY == 1 #define eTaskStateGet eTaskGetState #define portTickType TickType_t @@ -847,6 +944,10 @@ V8 if desired. */ #define pdTASK_CODE TaskFunction_t #define xListItem ListItem_t #define xList List_t + + /* For libraries that break the list data hiding, and access list structure + members directly (which is not supposed to be done). */ + #define pxContainer pvContainer #endif /* configENABLE_BACKWARD_COMPATIBILITY */ #if( configUSE_ALTERNATIVE_API != 0 ) @@ -861,6 +962,75 @@ point support. */ #define configUSE_TASK_FPU_SUPPORT 1 #endif +/* Set configENABLE_MPU to 1 to enable MPU support and 0 to disable it. This is +currently used in ARMv8M ports. */ +#ifndef configENABLE_MPU + #define configENABLE_MPU 0 +#endif + +/* Set configENABLE_FPU to 1 to enable FPU support and 0 to disable it. This is +currently used in ARMv8M ports. */ +#ifndef configENABLE_FPU + #define configENABLE_FPU 1 +#endif + +/* Set configENABLE_TRUSTZONE to 1 enable TrustZone support and 0 to disable it. +This is currently used in ARMv8M ports. */ +#ifndef configENABLE_TRUSTZONE + #define configENABLE_TRUSTZONE 1 +#endif + +/* Set configRUN_FREERTOS_SECURE_ONLY to 1 to run the FreeRTOS ARMv8M port on +the Secure Side only. */ +#ifndef configRUN_FREERTOS_SECURE_ONLY + #define configRUN_FREERTOS_SECURE_ONLY 0 +#endif + +/* Sometimes the FreeRTOSConfig.h settings only allow a task to be created using + * dynamically allocated RAM, in which case when any task is deleted it is known + * that both the task's stack and TCB need to be freed. Sometimes the + * FreeRTOSConfig.h settings only allow a task to be created using statically + * allocated RAM, in which case when any task is deleted it is known that neither + * the task's stack or TCB should be freed. Sometimes the FreeRTOSConfig.h + * settings allow a task to be created using either statically or dynamically + * allocated RAM, in which case a member of the TCB is used to record whether the + * stack and/or TCB were allocated statically or dynamically, so when a task is + * deleted the RAM that was allocated dynamically is freed again and no attempt is + * made to free the RAM that was allocated statically. + * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE is only true if it is possible for a + * task to be created using either statically or dynamically allocated RAM. Note + * that if portUSING_MPU_WRAPPERS is 1 then a protected task can be created with + * a statically allocated stack and a dynamically allocated TCB. + * + * The following table lists various combinations of portUSING_MPU_WRAPPERS, + * configSUPPORT_DYNAMIC_ALLOCATION and configSUPPORT_STATIC_ALLOCATION and + * when it is possible to have both static and dynamic allocation: + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + * | MPU | Dynamic | Static | Available Functions | Possible Allocations | Both Dynamic and | Need Free | + * | | | | | | Static Possible | | + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + * | 0 | 0 | 1 | xTaskCreateStatic | TCB - Static, Stack - Static | No | No | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 0 | 1 | 0 | xTaskCreate | TCB - Dynamic, Stack - Dynamic | No | Yes | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 0 | 1 | 1 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateStatic | 2. TCB - Static, Stack - Static | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 0 | 1 | xTaskCreateStatic, | TCB - Static, Stack - Static | No | No | + * | | | | xTaskCreateRestrictedStatic | | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 1 | 0 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateRestricted | 2. TCB - Dynamic, Stack - Static | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 1 | 1 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateStatic, | 2. TCB - Dynamic, Stack - Static | | | + * | | | | xTaskCreateRestricted, | 3. TCB - Static, Stack - Static | | | + * | | | | xTaskCreateRestrictedStatic | | | | + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + */ +#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE ( ( ( portUSING_MPU_WRAPPERS == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) || \ + ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) ) + /* * In line with software engineering best practice, FreeRTOS implements a strict * data hiding policy, so the real structures used by FreeRTOS to maintain the @@ -873,25 +1043,40 @@ point support. */ */ struct xSTATIC_LIST_ITEM { - TickType_t xDummy1; - void *pvDummy2[ 4 ]; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + TickType_t xDummy2; + void *pvDummy3[ 4 ]; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy4; + #endif }; typedef struct xSTATIC_LIST_ITEM StaticListItem_t; /* See the comments above the struct xSTATIC_LIST_ITEM definition. */ struct xSTATIC_MINI_LIST_ITEM { - TickType_t xDummy1; - void *pvDummy2[ 2 ]; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + TickType_t xDummy2; + void *pvDummy3[ 2 ]; }; typedef struct xSTATIC_MINI_LIST_ITEM StaticMiniListItem_t; /* See the comments above the struct xSTATIC_LIST_ITEM definition. */ typedef struct xSTATIC_LIST { - UBaseType_t uxDummy1; - void *pvDummy2; - StaticMiniListItem_t xDummy3; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + UBaseType_t uxDummy2; + void *pvDummy3; + StaticMiniListItem_t xDummy4; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy5; + #endif } StaticList_t; /* @@ -917,7 +1102,7 @@ typedef struct xSTATIC_TCB UBaseType_t uxDummy5; void *pxDummy6; uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; - #if ( portSTACK_GROWTH > 0 ) + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) void *pxDummy8; #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) @@ -945,10 +1130,16 @@ typedef struct xSTATIC_TCB uint32_t ulDummy18; uint8_t ucDummy19; #endif - #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) uint8_t uxDummy20; #endif + #if( INCLUDE_xTaskAbortDelay == 1 ) + uint8_t ucDummy21; + #endif + #if ( configUSE_POSIX_ERRNO == 1 ) + int iDummy22; + #endif } StaticTask_t; /* @@ -1043,17 +1234,41 @@ typedef struct xSTATIC_TIMER void *pvDummy1; StaticListItem_t xDummy2; TickType_t xDummy3; - UBaseType_t uxDummy4; - void *pvDummy5[ 2 ]; + void *pvDummy5; + TaskFunction_t pvDummy6; #if( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxDummy6; + UBaseType_t uxDummy7; #endif + uint8_t ucDummy8; - #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) - uint8_t ucDummy7; +} StaticTimer_t; + +/* +* In line with software engineering best practice, especially when supplying a +* library that is likely to change in future versions, FreeRTOS implements a +* strict data hiding policy. This means the stream buffer structure used +* internally by FreeRTOS is not accessible to application code. However, if +* the application writer wants to statically allocate the memory required to +* create a stream buffer then the size of the stream buffer object needs to be +* know. The StaticStreamBuffer_t structure below is provided for this purpose. +* Its size and alignment requirements are guaranteed to match those of the +* genuine structure, no matter which architecture is being used, and no matter +* how the values in FreeRTOSConfig.h are set. Its contents are somewhat +* obfuscated in the hope users will recognise that it would be unwise to make +* direct use of the structure members. +*/ +typedef struct xSTATIC_STREAM_BUFFER +{ + size_t uxDummy1[ 4 ]; + void * pvDummy2[ 3 ]; + uint8_t ucDummy3; + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy4; #endif +} StaticStreamBuffer_t; -} StaticTimer_t; +/* Message buffers are built on stream buffers. */ +typedef StaticStreamBuffer_t StaticMessageBuffer_t; #ifdef __cplusplus } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h b/Firmware/ThirdParty/FreeRTOS/Source/include/StackMacros.h similarity index 54% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/StackMacros.h index 13c6b829b..3ed8b22d1 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/StackMacros.h @@ -1,75 +1,37 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef STACK_MACROS_H #define STACK_MACROS_H +#ifndef _MSC_VER /* Visual Studio doesn't support #warning. */ + #warning The name of this file has changed to stack_macros.h. Please update your code accordingly. This source file (which has the original name) will be removed in future released. +#endif + /* * Call the stack overflow hook function if the stack of the task being swapped * out is currently overflowed, or looks like it might have overflowed in the diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h b/Firmware/ThirdParty/FreeRTOS/Source/include/croutine.h similarity index 86% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/croutine.h index 4f003a0ba..8b3b41b90 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/croutine.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef CO_ROUTINE_H #define CO_ROUTINE_H diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h b/Firmware/ThirdParty/FreeRTOS/Source/include/deprecated_definitions.h similarity index 61% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/deprecated_definitions.h index 4ea816ccf..9cece988f 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/deprecated_definitions.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef DEPRECATED_DEFINITIONS_H #define DEPRECATED_DEFINITIONS_H diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h b/Firmware/ThirdParty/FreeRTOS/Source/include/event_groups.h similarity index 83% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/event_groups.h index cab9d59ec..1f38bdb76 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/event_groups.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef EVENT_GROUPS_H #define EVENT_GROUPS_H @@ -120,7 +78,8 @@ extern "C" { * \defgroup EventGroupHandle_t EventGroupHandle_t * \ingroup EventGroup */ -typedef void * EventGroupHandle_t; +struct EventGroupDef_t; +typedef struct EventGroupDef_t * EventGroupHandle_t; /* * The type that holds event bits always matches TickType_t - therefore the @@ -185,7 +144,7 @@ typedef TickType_t EventBits_t; * \ingroup EventGroup */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) -PRIVILEGED_FUNCTION EventGroupHandle_t xEventGroupCreate( void ) ; + EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION; #endif /** @@ -238,7 +197,7 @@ PRIVILEGED_FUNCTION EventGroupHandle_t xEventGroupCreate( void ) ; */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) -PRIVILEGED_FUNCTION EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ); + EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION; #endif /** @@ -333,7 +292,7 @@ PRIVILEGED_FUNCTION EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup * \defgroup xEventGroupWaitBits xEventGroupWaitBits * \ingroup EventGroup */ -PRIVILEGED_FUNCTION EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ); +EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * event_groups.h @@ -390,7 +349,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGr * \defgroup xEventGroupClearBits xEventGroupClearBits * \ingroup EventGroup */ -PRIVILEGED_FUNCTION EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ); +EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; /** * event_groups.h @@ -446,7 +405,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventG * \ingroup EventGroup */ #if( configUSE_TRACE_FACILITY == 1 ) - PRIVILEGED_FUNCTION BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ); + BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; #else #define xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear ) xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ) #endif @@ -523,7 +482,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventG * \defgroup xEventGroupSetBits xEventGroupSetBits * \ingroup EventGroup */ -PRIVILEGED_FUNCTION EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ); +EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION; /** * event_groups.h @@ -598,7 +557,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGro * \ingroup EventGroup */ #if( configUSE_TRACE_FACILITY == 1 ) - PRIVILEGED_FUNCTION BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken ); + BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; #else #define xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ) xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ) #endif @@ -727,7 +686,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGro * \defgroup xEventGroupSync xEventGroupSync * \ingroup EventGroup */ -PRIVILEGED_FUNCTION EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ); +EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** @@ -763,7 +722,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, * \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR * \ingroup EventGroup */ -PRIVILEGED_FUNCTION EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ); +EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION; /** * event_groups.h @@ -777,15 +736,16 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xE * * @param xEventGroup The event group being deleted. */ -PRIVILEGED_FUNCTION void vEventGroupDelete( EventGroupHandle_t xEventGroup ); +void vEventGroupDelete( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION; /* For internal use only. */ -PRIVILEGED_FUNCTION void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ); -PRIVILEGED_FUNCTION void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ); +void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ) PRIVILEGED_FUNCTION; +void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION; #if (configUSE_TRACE_FACILITY == 1) - PRIVILEGED_FUNCTION UBaseType_t uxEventGroupGetNumber( void* xEventGroup ); + UBaseType_t uxEventGroupGetNumber( void* xEventGroup ) PRIVILEGED_FUNCTION; + void vEventGroupSetNumber( void* xEventGroup, UBaseType_t uxEventGroupNumber ) PRIVILEGED_FUNCTION; #endif #ifdef __cplusplus diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/list.h b/Firmware/ThirdParty/FreeRTOS/Source/include/list.h similarity index 77% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/list.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/list.h index e552625ea..2fb6775ff 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/list.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/list.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* * This is the list implementation used by the scheduler. While it is tailored @@ -178,6 +136,7 @@ use of FreeRTOS.*/ /* * Definition of the only type of object that a list can contain. */ +struct xLIST; struct xLIST_ITEM { listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ @@ -185,7 +144,7 @@ struct xLIST_ITEM struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */ struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */ void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ - void * configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */ + struct xLIST * configLIST_VOLATILE pxContainer; /*< Pointer to the list in which this list item is placed (if any). */ listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ }; typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */ @@ -205,7 +164,7 @@ typedef struct xMINI_LIST_ITEM MiniListItem_t; typedef struct xLIST { listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ - configLIST_VOLATILE UBaseType_t uxNumberOfItems; + volatile UBaseType_t uxNumberOfItems; ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */ MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ @@ -288,7 +247,7 @@ typedef struct xLIST * \page listLIST_IS_EMPTY listLIST_IS_EMPTY * \ingroup LinkedList */ -#define listLIST_IS_EMPTY( pxList ) ( ( BaseType_t ) ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ) +#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE ) /* * Access macro to return the number of items in the list. @@ -356,7 +315,7 @@ List_t * const pxConstList = ( pxList ); \ * @param pxListItem The list item we want to know if is in the list. * @return pdTRUE if the list item is in the list, otherwise pdFALSE. */ -#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( BaseType_t ) ( ( pxListItem )->pvContainer == ( void * ) ( pxList ) ) ) +#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) ) /* * Return the list a list item is contained within (referenced from). @@ -364,7 +323,7 @@ List_t * const pxConstList = ( pxList ); \ * @param pxListItem The list item being queried. * @return A pointer to the List_t object that references the pxListItem */ -#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pvContainer ) +#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer ) /* * This provides a crude means of knowing if a list has been initialised, as @@ -383,7 +342,7 @@ List_t * const pxConstList = ( pxList ); \ * \page vListInitialise vListInitialise * \ingroup LinkedList */ -PRIVILEGED_FUNCTION void vListInitialise( List_t * const pxList ); +void vListInitialise( List_t * const pxList ) PRIVILEGED_FUNCTION; /* * Must be called before a list item is used. This sets the list container to @@ -394,7 +353,7 @@ PRIVILEGED_FUNCTION void vListInitialise( List_t * const pxList ); * \page vListInitialiseItem vListInitialiseItem * \ingroup LinkedList */ -PRIVILEGED_FUNCTION void vListInitialiseItem( ListItem_t * const pxItem ); +void vListInitialiseItem( ListItem_t * const pxItem ) PRIVILEGED_FUNCTION; /* * Insert a list item into a list. The item will be inserted into the list in @@ -407,7 +366,7 @@ PRIVILEGED_FUNCTION void vListInitialiseItem( ListItem_t * const pxItem ); * \page vListInsert vListInsert * \ingroup LinkedList */ -PRIVILEGED_FUNCTION void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ); +void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION; /* * Insert a list item into a list. The item will be inserted in a position @@ -428,7 +387,7 @@ PRIVILEGED_FUNCTION void vListInsert( List_t * const pxList, ListItem_t * const * \page vListInsertEnd vListInsertEnd * \ingroup LinkedList */ -PRIVILEGED_FUNCTION void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ); +void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION; /* * Remove an item from a list. The list item has a pointer to the list that @@ -443,7 +402,7 @@ PRIVILEGED_FUNCTION void vListInsertEnd( List_t * const pxList, ListItem_t * con * \page uxListRemove uxListRemove * \ingroup LinkedList */ -PRIVILEGED_FUNCTION UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ); +UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) PRIVILEGED_FUNCTION; #ifdef __cplusplus } diff --git a/Firmware/ThirdParty/FreeRTOS/Source/include/message_buffer.h b/Firmware/ThirdParty/FreeRTOS/Source/include/message_buffer.h new file mode 100644 index 000000000..cfa08cb93 --- /dev/null +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/message_buffer.h @@ -0,0 +1,799 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +/* + * Message buffers build functionality on top of FreeRTOS stream buffers. + * Whereas stream buffers are used to send a continuous stream of data from one + * task or interrupt to another, message buffers are used to send variable + * length discrete messages from one task or interrupt to another. Their + * implementation is light weight, making them particularly suited for interrupt + * to task and core to core communication scenarios. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xMessageBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xMessageBufferRead()) inside a critical section and set the receive + * timeout to 0. + * + * Message buffers hold variable length messages. To enable that, when a + * message is written to the message buffer an additional sizeof( size_t ) bytes + * are also written to store the message's length (that happens internally, with + * the API function). sizeof( size_t ) is typically 4 bytes on a 32-bit + * architecture, so writing a 10 byte message to a message buffer on a 32-bit + * architecture will actually reduce the available space in the message buffer + * by 14 bytes (10 byte are used by the message, and 4 bytes to hold the length + * of the message). + */ + +#ifndef FREERTOS_MESSAGE_BUFFER_H +#define FREERTOS_MESSAGE_BUFFER_H + +/* Message buffers are built onto of stream buffers. */ +#include "stream_buffer.h" + +#if defined( __cplusplus ) +extern "C" { +#endif + +/** + * Type by which message buffers are referenced. For example, a call to + * xMessageBufferCreate() returns an MessageBufferHandle_t variable that can + * then be used as a parameter to xMessageBufferSend(), xMessageBufferReceive(), + * etc. + */ +typedef void * MessageBufferHandle_t; + +/*-----------------------------------------------------------*/ + +/** + * message_buffer.h + * +
+MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );
+
+ * + * Creates a new message buffer using dynamically allocated memory. See + * xMessageBufferCreateStatic() for a version that uses statically allocated + * memory (memory that is allocated at compile time). + * + * configSUPPORT_DYNAMIC_ALLOCATION must be set to 1 or left undefined in + * FreeRTOSConfig.h for xMessageBufferCreate() to be available. + * + * @param xBufferSizeBytes The total number of bytes (not messages) the message + * buffer will be able to hold at any one time. When a message is written to + * the message buffer an additional sizeof( size_t ) bytes are also written to + * store the message's length. sizeof( size_t ) is typically 4 bytes on a + * 32-bit architecture, so on most 32-bit architectures a 10 byte message will + * take up 14 bytes of message buffer space. + * + * @return If NULL is returned, then the message buffer cannot be created + * because there is insufficient heap memory available for FreeRTOS to allocate + * the message buffer data structures and storage area. A non-NULL value being + * returned indicates that the message buffer has been created successfully - + * the returned value should be stored as the handle to the created message + * buffer. + * + * Example use: +
+
+void vAFunction( void )
+{
+MessageBufferHandle_t xMessageBuffer;
+const size_t xMessageBufferSizeBytes = 100;
+
+    // Create a message buffer that can hold 100 bytes.  The memory used to hold
+    // both the message buffer structure and the messages themselves is allocated
+    // dynamically.  Each message added to the buffer consumes an additional 4
+    // bytes which are used to hold the lengh of the message.
+    xMessageBuffer = xMessageBufferCreate( xMessageBufferSizeBytes );
+
+    if( xMessageBuffer == NULL )
+    {
+        // There was not enough heap memory space available to create the
+        // message buffer.
+    }
+    else
+    {
+        // The message buffer was created successfully and can now be used.
+    }
+
+
+ * \defgroup xMessageBufferCreate xMessageBufferCreate + * \ingroup MessageBufferManagement + */ +#define xMessageBufferCreate( xBufferSizeBytes ) ( MessageBufferHandle_t ) xStreamBufferGenericCreate( xBufferSizeBytes, ( size_t ) 0, pdTRUE ) + +/** + * message_buffer.h + * +
+MessageBufferHandle_t xMessageBufferCreateStatic( size_t xBufferSizeBytes,
+                                                  uint8_t *pucMessageBufferStorageArea,
+                                                  StaticMessageBuffer_t *pxStaticMessageBuffer );
+
+ * Creates a new message buffer using statically allocated memory. See + * xMessageBufferCreate() for a version that uses dynamically allocated memory. + * + * @param xBufferSizeBytes The size, in bytes, of the buffer pointed to by the + * pucMessageBufferStorageArea parameter. When a message is written to the + * message buffer an additional sizeof( size_t ) bytes are also written to store + * the message's length. sizeof( size_t ) is typically 4 bytes on a 32-bit + * architecture, so on most 32-bit architecture a 10 byte message will take up + * 14 bytes of message buffer space. The maximum number of bytes that can be + * stored in the message buffer is actually (xBufferSizeBytes - 1). + * + * @param pucMessageBufferStorageArea Must point to a uint8_t array that is at + * least xBufferSizeBytes + 1 big. This is the array to which messages are + * copied when they are written to the message buffer. + * + * @param pxStaticMessageBuffer Must point to a variable of type + * StaticMessageBuffer_t, which will be used to hold the message buffer's data + * structure. + * + * @return If the message buffer is created successfully then a handle to the + * created message buffer is returned. If either pucMessageBufferStorageArea or + * pxStaticmessageBuffer are NULL then NULL is returned. + * + * Example use: +
+
+// Used to dimension the array used to hold the messages.  The available space
+// will actually be one less than this, so 999.
+#define STORAGE_SIZE_BYTES 1000
+
+// Defines the memory that will actually hold the messages within the message
+// buffer.
+static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
+
+// The variable used to hold the message buffer structure.
+StaticMessageBuffer_t xMessageBufferStruct;
+
+void MyFunction( void )
+{
+MessageBufferHandle_t xMessageBuffer;
+
+    xMessageBuffer = xMessageBufferCreateStatic( sizeof( ucBufferStorage ),
+                                                 ucBufferStorage,
+                                                 &xMessageBufferStruct );
+
+    // As neither the pucMessageBufferStorageArea or pxStaticMessageBuffer
+    // parameters were NULL, xMessageBuffer will not be NULL, and can be used to
+    // reference the created message buffer in other message buffer API calls.
+
+    // Other code that uses the message buffer can go here.
+}
+
+
+ * \defgroup xMessageBufferCreateStatic xMessageBufferCreateStatic + * \ingroup MessageBufferManagement + */ +#define xMessageBufferCreateStatic( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer ) ( MessageBufferHandle_t ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, 0, pdTRUE, pucMessageBufferStorageArea, pxStaticMessageBuffer ) + +/** + * message_buffer.h + * +
+size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer,
+                           const void *pvTxData,
+                           size_t xDataLengthBytes,
+                           TickType_t xTicksToWait );
+
+ *
+ * Sends a discrete message to the message buffer.  The message can be any
+ * length that fits within the buffer's free space, and is copied into the
+ * buffer.
+ *
+ * ***NOTE***:  Uniquely among FreeRTOS objects, the stream buffer
+ * implementation (so also the message buffer implementation, as message buffers
+ * are built on top of stream buffers) assumes there is only one task or
+ * interrupt that will write to the buffer (the writer), and only one task or
+ * interrupt that will read from the buffer (the reader).  It is safe for the
+ * writer and reader to be different tasks or interrupts, but, unlike other
+ * FreeRTOS objects, it is not safe to have multiple different writers or
+ * multiple different readers.  If there are to be multiple different writers
+ * then the application writer must place each call to a writing API function
+ * (such as xMessageBufferSend()) inside a critical section and set the send
+ * block time to 0.  Likewise, if there are to be multiple different readers
+ * then the application writer must place each call to a reading API function
+ * (such as xMessageBufferRead()) inside a critical section and set the receive
+ * block time to 0.
+ *
+ * Use xMessageBufferSend() to write to a message buffer from a task.  Use
+ * xMessageBufferSendFromISR() to write to a message buffer from an interrupt
+ * service routine (ISR).
+ *
+ * @param xMessageBuffer The handle of the message buffer to which a message is
+ * being sent.
+ *
+ * @param pvTxData A pointer to the message that is to be copied into the
+ * message buffer.
+ *
+ * @param xDataLengthBytes The length of the message.  That is, the number of
+ * bytes to copy from pvTxData into the message buffer.  When a message is
+ * written to the message buffer an additional sizeof( size_t ) bytes are also
+ * written to store the message's length.  sizeof( size_t ) is typically 4 bytes
+ * on a 32-bit architecture, so on most 32-bit architecture setting
+ * xDataLengthBytes to 20 will reduce the free space in the message buffer by 24
+ * bytes (20 bytes of message data and 4 bytes to hold the message length).
+ *
+ * @param xTicksToWait The maximum amount of time the calling task should remain
+ * in the Blocked state to wait for enough space to become available in the
+ * message buffer, should the message buffer have insufficient space when
+ * xMessageBufferSend() is called.  The calling task will never block if
+ * xTicksToWait is zero.  The block time is specified in tick periods, so the
+ * absolute time it represents is dependent on the tick frequency.  The macro
+ * pdMS_TO_TICKS() can be used to convert a time specified in milliseconds into
+ * a time specified in ticks.  Setting xTicksToWait to portMAX_DELAY will cause
+ * the task to wait indefinitely (without timing out), provided
+ * INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h.  Tasks do not use any
+ * CPU time when they are in the Blocked state.
+ *
+ * @return The number of bytes written to the message buffer.  If the call to
+ * xMessageBufferSend() times out before there was enough space to write the
+ * message into the message buffer then zero is returned.  If the call did not
+ * time out then xDataLengthBytes is returned.
+ *
+ * Example use:
+
+void vAFunction( MessageBufferHandle_t xMessageBuffer )
+{
+size_t xBytesSent;
+uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
+char *pcStringToSend = "String to send";
+const TickType_t x100ms = pdMS_TO_TICKS( 100 );
+
+    // Send an array to the message buffer, blocking for a maximum of 100ms to
+    // wait for enough space to be available in the message buffer.
+    xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
+
+    if( xBytesSent != sizeof( ucArrayToSend ) )
+    {
+        // The call to xMessageBufferSend() times out before there was enough
+        // space in the buffer for the data to be written.
+    }
+
+    // Send the string to the message buffer.  Return immediately if there is
+    // not enough space in the buffer.
+    xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
+
+    if( xBytesSent != strlen( pcStringToSend ) )
+    {
+        // The string could not be added to the message buffer because there was
+        // not enough free space in the buffer.
+    }
+}
+
+ * \defgroup xMessageBufferSend xMessageBufferSend + * \ingroup MessageBufferManagement + */ +#define xMessageBufferSend( xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) xStreamBufferSend( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) + +/** + * message_buffer.h + * +
+size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer,
+                                  const void *pvTxData,
+                                  size_t xDataLengthBytes,
+                                  BaseType_t *pxHigherPriorityTaskWoken );
+
+ *
+ * Interrupt safe version of the API function that sends a discrete message to
+ * the message buffer.  The message can be any length that fits within the
+ * buffer's free space, and is copied into the buffer.
+ *
+ * ***NOTE***:  Uniquely among FreeRTOS objects, the stream buffer
+ * implementation (so also the message buffer implementation, as message buffers
+ * are built on top of stream buffers) assumes there is only one task or
+ * interrupt that will write to the buffer (the writer), and only one task or
+ * interrupt that will read from the buffer (the reader).  It is safe for the
+ * writer and reader to be different tasks or interrupts, but, unlike other
+ * FreeRTOS objects, it is not safe to have multiple different writers or
+ * multiple different readers.  If there are to be multiple different writers
+ * then the application writer must place each call to a writing API function
+ * (such as xMessageBufferSend()) inside a critical section and set the send
+ * block time to 0.  Likewise, if there are to be multiple different readers
+ * then the application writer must place each call to a reading API function
+ * (such as xMessageBufferRead()) inside a critical section and set the receive
+ * block time to 0.
+ *
+ * Use xMessageBufferSend() to write to a message buffer from a task.  Use
+ * xMessageBufferSendFromISR() to write to a message buffer from an interrupt
+ * service routine (ISR).
+ *
+ * @param xMessageBuffer The handle of the message buffer to which a message is
+ * being sent.
+ *
+ * @param pvTxData A pointer to the message that is to be copied into the
+ * message buffer.
+ *
+ * @param xDataLengthBytes The length of the message.  That is, the number of
+ * bytes to copy from pvTxData into the message buffer.  When a message is
+ * written to the message buffer an additional sizeof( size_t ) bytes are also
+ * written to store the message's length.  sizeof( size_t ) is typically 4 bytes
+ * on a 32-bit architecture, so on most 32-bit architecture setting
+ * xDataLengthBytes to 20 will reduce the free space in the message buffer by 24
+ * bytes (20 bytes of message data and 4 bytes to hold the message length).
+ *
+ * @param pxHigherPriorityTaskWoken  It is possible that a message buffer will
+ * have a task blocked on it waiting for data.  Calling
+ * xMessageBufferSendFromISR() can make data available, and so cause a task that
+ * was waiting for data to leave the Blocked state.  If calling
+ * xMessageBufferSendFromISR() causes a task to leave the Blocked state, and the
+ * unblocked task has a priority higher than the currently executing task (the
+ * task that was interrupted), then, internally, xMessageBufferSendFromISR()
+ * will set *pxHigherPriorityTaskWoken to pdTRUE.  If
+ * xMessageBufferSendFromISR() sets this value to pdTRUE, then normally a
+ * context switch should be performed before the interrupt is exited.  This will
+ * ensure that the interrupt returns directly to the highest priority Ready
+ * state task.  *pxHigherPriorityTaskWoken should be set to pdFALSE before it
+ * is passed into the function.  See the code example below for an example.
+ *
+ * @return The number of bytes actually written to the message buffer.  If the
+ * message buffer didn't have enough free space for the message to be stored
+ * then 0 is returned, otherwise xDataLengthBytes is returned.
+ *
+ * Example use:
+
+// A message buffer that has already been created.
+MessageBufferHandle_t xMessageBuffer;
+
+void vAnInterruptServiceRoutine( void )
+{
+size_t xBytesSent;
+char *pcStringToSend = "String to send";
+BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
+
+    // Attempt to send the string to the message buffer.
+    xBytesSent = xMessageBufferSendFromISR( xMessageBuffer,
+                                            ( void * ) pcStringToSend,
+                                            strlen( pcStringToSend ),
+                                            &xHigherPriorityTaskWoken );
+
+    if( xBytesSent != strlen( pcStringToSend ) )
+    {
+        // The string could not be added to the message buffer because there was
+        // not enough free space in the buffer.
+    }
+
+    // If xHigherPriorityTaskWoken was set to pdTRUE inside
+    // xMessageBufferSendFromISR() then a task that has a priority above the
+    // priority of the currently executing task was unblocked and a context
+    // switch should be performed to ensure the ISR returns to the unblocked
+    // task.  In most FreeRTOS ports this is done by simply passing
+    // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
+    // variables value, and perform the context switch if necessary.  Check the
+    // documentation for the port in use for port specific instructions.
+    taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+}
+
+ * \defgroup xMessageBufferSendFromISR xMessageBufferSendFromISR + * \ingroup MessageBufferManagement + */ +#define xMessageBufferSendFromISR( xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) xStreamBufferSendFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) + +/** + * message_buffer.h + * +
+size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer,
+                              void *pvRxData,
+                              size_t xBufferLengthBytes,
+                              TickType_t xTicksToWait );
+
+ * + * Receives a discrete message from a message buffer. Messages can be of + * variable length and are copied out of the buffer. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xMessageBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xMessageBufferRead()) inside a critical section and set the receive + * block time to 0. + * + * Use xMessageBufferReceive() to read from a message buffer from a task. Use + * xMessageBufferReceiveFromISR() to read from a message buffer from an + * interrupt service routine (ISR). + * + * @param xMessageBuffer The handle of the message buffer from which a message + * is being received. + * + * @param pvRxData A pointer to the buffer into which the received message is + * to be copied. + * + * @param xBufferLengthBytes The length of the buffer pointed to by the pvRxData + * parameter. This sets the maximum length of the message that can be received. + * If xBufferLengthBytes is too small to hold the next message then the message + * will be left in the message buffer and 0 will be returned. + * + * @param xTicksToWait The maximum amount of time the task should remain in the + * Blocked state to wait for a message, should the message buffer be empty. + * xMessageBufferReceive() will return immediately if xTicksToWait is zero and + * the message buffer is empty. The block time is specified in tick periods, so + * the absolute time it represents is dependent on the tick frequency. The + * macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds + * into a time specified in ticks. Setting xTicksToWait to portMAX_DELAY will + * cause the task to wait indefinitely (without timing out), provided + * INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. Tasks do not use any + * CPU time when they are in the Blocked state. + * + * @return The length, in bytes, of the message read from the message buffer, if + * any. If xMessageBufferReceive() times out before a message became available + * then zero is returned. If the length of the message is greater than + * xBufferLengthBytes then the message will be left in the message buffer and + * zero is returned. + * + * Example use: +
+void vAFunction( MessageBuffer_t xMessageBuffer )
+{
+uint8_t ucRxData[ 20 ];
+size_t xReceivedBytes;
+const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
+
+    // Receive the next message from the message buffer.  Wait in the Blocked
+    // state (so not using any CPU processing time) for a maximum of 100ms for
+    // a message to become available.
+    xReceivedBytes = xMessageBufferReceive( xMessageBuffer,
+                                            ( void * ) ucRxData,
+                                            sizeof( ucRxData ),
+                                            xBlockTime );
+
+    if( xReceivedBytes > 0 )
+    {
+        // A ucRxData contains a message that is xReceivedBytes long.  Process
+        // the message here....
+    }
+}
+
+ * \defgroup xMessageBufferReceive xMessageBufferReceive + * \ingroup MessageBufferManagement + */ +#define xMessageBufferReceive( xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) xStreamBufferReceive( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) + + +/** + * message_buffer.h + * +
+size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer,
+                                     void *pvRxData,
+                                     size_t xBufferLengthBytes,
+                                     BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * An interrupt safe version of the API function that receives a discrete + * message from a message buffer. Messages can be of variable length and are + * copied out of the buffer. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xMessageBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xMessageBufferRead()) inside a critical section and set the receive + * block time to 0. + * + * Use xMessageBufferReceive() to read from a message buffer from a task. Use + * xMessageBufferReceiveFromISR() to read from a message buffer from an + * interrupt service routine (ISR). + * + * @param xMessageBuffer The handle of the message buffer from which a message + * is being received. + * + * @param pvRxData A pointer to the buffer into which the received message is + * to be copied. + * + * @param xBufferLengthBytes The length of the buffer pointed to by the pvRxData + * parameter. This sets the maximum length of the message that can be received. + * If xBufferLengthBytes is too small to hold the next message then the message + * will be left in the message buffer and 0 will be returned. + * + * @param pxHigherPriorityTaskWoken It is possible that a message buffer will + * have a task blocked on it waiting for space to become available. Calling + * xMessageBufferReceiveFromISR() can make space available, and so cause a task + * that is waiting for space to leave the Blocked state. If calling + * xMessageBufferReceiveFromISR() causes a task to leave the Blocked state, and + * the unblocked task has a priority higher than the currently executing task + * (the task that was interrupted), then, internally, + * xMessageBufferReceiveFromISR() will set *pxHigherPriorityTaskWoken to pdTRUE. + * If xMessageBufferReceiveFromISR() sets this value to pdTRUE, then normally a + * context switch should be performed before the interrupt is exited. That will + * ensure the interrupt returns directly to the highest priority Ready state + * task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it is + * passed into the function. See the code example below for an example. + * + * @return The length, in bytes, of the message read from the message buffer, if + * any. + * + * Example use: +
+// A message buffer that has already been created.
+MessageBuffer_t xMessageBuffer;
+
+void vAnInterruptServiceRoutine( void )
+{
+uint8_t ucRxData[ 20 ];
+size_t xReceivedBytes;
+BaseType_t xHigherPriorityTaskWoken = pdFALSE;  // Initialised to pdFALSE.
+
+    // Receive the next message from the message buffer.
+    xReceivedBytes = xMessageBufferReceiveFromISR( xMessageBuffer,
+                                                  ( void * ) ucRxData,
+                                                  sizeof( ucRxData ),
+                                                  &xHigherPriorityTaskWoken );
+
+    if( xReceivedBytes > 0 )
+    {
+        // A ucRxData contains a message that is xReceivedBytes long.  Process
+        // the message here....
+    }
+
+    // If xHigherPriorityTaskWoken was set to pdTRUE inside
+    // xMessageBufferReceiveFromISR() then a task that has a priority above the
+    // priority of the currently executing task was unblocked and a context
+    // switch should be performed to ensure the ISR returns to the unblocked
+    // task.  In most FreeRTOS ports this is done by simply passing
+    // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
+    // variables value, and perform the context switch if necessary.  Check the
+    // documentation for the port in use for port specific instructions.
+    taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+}
+
+ * \defgroup xMessageBufferReceiveFromISR xMessageBufferReceiveFromISR + * \ingroup MessageBufferManagement + */ +#define xMessageBufferReceiveFromISR( xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) xStreamBufferReceiveFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) + +/** + * message_buffer.h + * +
+void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer );
+
+ * + * Deletes a message buffer that was previously created using a call to + * xMessageBufferCreate() or xMessageBufferCreateStatic(). If the message + * buffer was created using dynamic memory (that is, by xMessageBufferCreate()), + * then the allocated memory is freed. + * + * A message buffer handle must not be used after the message buffer has been + * deleted. + * + * @param xMessageBuffer The handle of the message buffer to be deleted. + * + */ +#define vMessageBufferDelete( xMessageBuffer ) vStreamBufferDelete( ( StreamBufferHandle_t ) xMessageBuffer ) + +/** + * message_buffer.h +
+BaseType_t xMessageBufferIsFull( MessageBufferHandle_t xMessageBuffer ) );
+
+ * + * Tests to see if a message buffer is full. A message buffer is full if it + * cannot accept any more messages, of any size, until space is made available + * by a message being removed from the message buffer. + * + * @param xMessageBuffer The handle of the message buffer being queried. + * + * @return If the message buffer referenced by xMessageBuffer is full then + * pdTRUE is returned. Otherwise pdFALSE is returned. + */ +#define xMessageBufferIsFull( xMessageBuffer ) xStreamBufferIsFull( ( StreamBufferHandle_t ) xMessageBuffer ) + +/** + * message_buffer.h +
+BaseType_t xMessageBufferIsEmpty( MessageBufferHandle_t xMessageBuffer ) );
+
+ * + * Tests to see if a message buffer is empty (does not contain any messages). + * + * @param xMessageBuffer The handle of the message buffer being queried. + * + * @return If the message buffer referenced by xMessageBuffer is empty then + * pdTRUE is returned. Otherwise pdFALSE is returned. + * + */ +#define xMessageBufferIsEmpty( xMessageBuffer ) xStreamBufferIsEmpty( ( StreamBufferHandle_t ) xMessageBuffer ) + +/** + * message_buffer.h +
+BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer );
+
+ * + * Resets a message buffer to its initial empty state, discarding any message it + * contained. + * + * A message buffer can only be reset if there are no tasks blocked on it. + * + * @param xMessageBuffer The handle of the message buffer being reset. + * + * @return If the message buffer was reset then pdPASS is returned. If the + * message buffer could not be reset because either there was a task blocked on + * the message queue to wait for space to become available, or to wait for a + * a message to be available, then pdFAIL is returned. + * + * \defgroup xMessageBufferReset xMessageBufferReset + * \ingroup MessageBufferManagement + */ +#define xMessageBufferReset( xMessageBuffer ) xStreamBufferReset( ( StreamBufferHandle_t ) xMessageBuffer ) + + +/** + * message_buffer.h +
+size_t xMessageBufferSpaceAvailable( MessageBufferHandle_t xMessageBuffer ) );
+
+ * Returns the number of bytes of free space in the message buffer. + * + * @param xMessageBuffer The handle of the message buffer being queried. + * + * @return The number of bytes that can be written to the message buffer before + * the message buffer would be full. When a message is written to the message + * buffer an additional sizeof( size_t ) bytes are also written to store the + * message's length. sizeof( size_t ) is typically 4 bytes on a 32-bit + * architecture, so if xMessageBufferSpacesAvailable() returns 10, then the size + * of the largest message that can be written to the message buffer is 6 bytes. + * + * \defgroup xMessageBufferSpaceAvailable xMessageBufferSpaceAvailable + * \ingroup MessageBufferManagement + */ +#define xMessageBufferSpaceAvailable( xMessageBuffer ) xStreamBufferSpacesAvailable( ( StreamBufferHandle_t ) xMessageBuffer ) +#define xMessageBufferSpacesAvailable( xMessageBuffer ) xStreamBufferSpacesAvailable( ( StreamBufferHandle_t ) xMessageBuffer ) /* Corrects typo in original macro name. */ + +/** + * message_buffer.h +
+ size_t xMessageBufferNextLengthBytes( MessageBufferHandle_t xMessageBuffer ) );
+ 
+ * Returns the length (in bytes) of the next message in a message buffer. + * Useful if xMessageBufferReceive() returned 0 because the size of the buffer + * passed into xMessageBufferReceive() was too small to hold the next message. + * + * @param xMessageBuffer The handle of the message buffer being queried. + * + * @return The length (in bytes) of the next message in the message buffer, or 0 + * if the message buffer is empty. + * + * \defgroup xMessageBufferNextLengthBytes xMessageBufferNextLengthBytes + * \ingroup MessageBufferManagement + */ +#define xMessageBufferNextLengthBytes( xMessageBuffer ) xStreamBufferNextMessageLengthBytes( ( StreamBufferHandle_t ) xMessageBuffer ) PRIVILEGED_FUNCTION; + +/** + * message_buffer.h + * +
+BaseType_t xMessageBufferSendCompletedFromISR( MessageBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * For advanced users only. + * + * The sbSEND_COMPLETED() macro is called from within the FreeRTOS APIs when + * data is sent to a message buffer or stream buffer. If there was a task that + * was blocked on the message or stream buffer waiting for data to arrive then + * the sbSEND_COMPLETED() macro sends a notification to the task to remove it + * from the Blocked state. xMessageBufferSendCompletedFromISR() does the same + * thing. It is provided to enable application writers to implement their own + * version of sbSEND_COMPLETED(), and MUST NOT BE USED AT ANY OTHER TIME. + * + * See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for + * additional information. + * + * @param xStreamBuffer The handle of the stream buffer to which data was + * written. + * + * @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be + * initialised to pdFALSE before it is passed into + * xMessageBufferSendCompletedFromISR(). If calling + * xMessageBufferSendCompletedFromISR() removes a task from the Blocked state, + * and the task has a priority above the priority of the currently running task, + * then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a + * context switch should be performed before exiting the ISR. + * + * @return If a task was removed from the Blocked state then pdTRUE is returned. + * Otherwise pdFALSE is returned. + * + * \defgroup xMessageBufferSendCompletedFromISR xMessageBufferSendCompletedFromISR + * \ingroup StreamBufferManagement + */ +#define xMessageBufferSendCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) xStreamBufferSendCompletedFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pxHigherPriorityTaskWoken ) + +/** + * message_buffer.h + * +
+BaseType_t xMessageBufferReceiveCompletedFromISR( MessageBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * For advanced users only. + * + * The sbRECEIVE_COMPLETED() macro is called from within the FreeRTOS APIs when + * data is read out of a message buffer or stream buffer. If there was a task + * that was blocked on the message or stream buffer waiting for data to arrive + * then the sbRECEIVE_COMPLETED() macro sends a notification to the task to + * remove it from the Blocked state. xMessageBufferReceiveCompletedFromISR() + * does the same thing. It is provided to enable application writers to + * implement their own version of sbRECEIVE_COMPLETED(), and MUST NOT BE USED AT + * ANY OTHER TIME. + * + * See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for + * additional information. + * + * @param xStreamBuffer The handle of the stream buffer from which data was + * read. + * + * @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be + * initialised to pdFALSE before it is passed into + * xMessageBufferReceiveCompletedFromISR(). If calling + * xMessageBufferReceiveCompletedFromISR() removes a task from the Blocked state, + * and the task has a priority above the priority of the currently running task, + * then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a + * context switch should be performed before exiting the ISR. + * + * @return If a task was removed from the Blocked state then pdTRUE is returned. + * Otherwise pdFALSE is returned. + * + * \defgroup xMessageBufferReceiveCompletedFromISR xMessageBufferReceiveCompletedFromISR + * \ingroup StreamBufferManagement + */ +#define xMessageBufferReceiveCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) xStreamBufferReceiveCompletedFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pxHigherPriorityTaskWoken ) + +#if defined( __cplusplus ) +} /* extern "C" */ +#endif + +#endif /* !defined( FREERTOS_MESSAGE_BUFFER_H ) */ diff --git a/Firmware/ThirdParty/FreeRTOS/Source/include/mpu_prototypes.h b/Firmware/ThirdParty/FreeRTOS/Source/include/mpu_prototypes.h new file mode 100644 index 000000000..5d7490719 --- /dev/null +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/mpu_prototypes.h @@ -0,0 +1,157 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * When the MPU is used the standard (non MPU) API functions are mapped to + * equivalents that start "MPU_", the prototypes for which are defined in this + * header files. This will cause the application code to call the MPU_ version + * which wraps the non-MPU version with privilege promoting then demoting code, + * so the kernel code always runs will full privileges. + */ + + +#ifndef MPU_PROTOTYPES_H +#define MPU_PROTOTYPES_H + +/* MPU versions of tasks.h API functions. */ +BaseType_t MPU_xTaskCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskCreateStatic( TaskFunction_t pxTaskCode, const char * const pcName, const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, StackType_t * const puxStackBuffer, StaticTask_t * const pxTaskBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskDelete( TaskHandle_t xTaskToDelete ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskDelay( const TickType_t xTicksToDelay ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskAbortDelay( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskPriorityGet( const TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +eTaskState MPU_eTaskGetState( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSuspend( TaskHandle_t xTaskToSuspend ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskResume( TaskHandle_t xTaskToResume ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskStartScheduler( void ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSuspendAll( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskResumeAll( void ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTaskGetTickCount( void ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskGetNumberOfTasks( void ) FREERTOS_SYSTEM_CALL; +char * MPU_pcTaskGetName( TaskHandle_t xTaskToQuery ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskGetHandle( const char *pcNameToQuery ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +configSTACK_DEPTH_TYPE MPU_uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) FREERTOS_SYSTEM_CALL; +TaskHookFunction_t MPU_xTaskGetApplicationTaskTag( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) FREERTOS_SYSTEM_CALL; +void * MPU_pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTaskGetIdleRunTimeCounter( void ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskList( char * pcWriteBuffer ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskGetRunTimeStats( char *pcWriteBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +uint32_t MPU_ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskNotifyStateClear( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskIncrementTick( void ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskMissedYield( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskGetSchedulerState( void ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of queue.h API functions. */ +BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxQueueMessagesWaiting( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxQueueSpacesAvailable( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueDelete( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateMutex( const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xQueueGetMutexHolder( QueueHandle_t xSemaphore ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcName ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueUnregisterQueue( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +const char * MPU_pcQueueGetName( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL; +QueueSetHandle_t MPU_xQueueCreateSet( const UBaseType_t uxEventQueueLength ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL; +QueueSetMemberHandle_t MPU_xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxQueueGetQueueNumber( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +uint8_t MPU_ucQueueGetQueueType( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of timers.h API functions. */ +TimerHandle_t MPU_xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) FREERTOS_SYSTEM_CALL; +TimerHandle_t MPU_xTimerCreateStatic( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, StaticTimer_t *pxTimerBuffer ) FREERTOS_SYSTEM_CALL; +void * MPU_pvTimerGetTimerID( const TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +void MPU_vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerIsTimerActive( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTimerGetTimerDaemonTaskHandle( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +const char * MPU_pcTimerGetName( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +void MPU_vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTimerGetPeriod( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTimerGetExpiryTime( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerCreateTimerTask( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of event_group.h API functions. */ +EventGroupHandle_t MPU_xEventGroupCreate( void ) FREERTOS_SYSTEM_CALL; +EventGroupHandle_t MPU_xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +void MPU_vEventGroupDelete( EventGroupHandle_t xEventGroup ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxEventGroupGetNumber( void* xEventGroup ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of message/stream_buffer.h API functions. */ +size_t MPU_xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +void MPU_vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ) FREERTOS_SYSTEM_CALL; +StreamBufferHandle_t MPU_xStreamBufferGenericCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer ) FREERTOS_SYSTEM_CALL; +StreamBufferHandle_t MPU_xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer, uint8_t * const pucStreamBufferStorageArea, StaticStreamBuffer_t * const pxStaticStreamBuffer ) FREERTOS_SYSTEM_CALL; + + + +#endif /* MPU_PROTOTYPES_H */ + diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h b/Firmware/ThirdParty/FreeRTOS/Source/include/mpu_wrappers.h similarity index 57% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/mpu_wrappers.h index 1a05c9fd5..711393f6a 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/mpu_wrappers.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef MPU_WRAPPERS_H #define MPU_WRAPPERS_H @@ -109,6 +67,7 @@ only for ports that are using the MPU. */ #define pcTaskGetName MPU_pcTaskGetName #define xTaskGetHandle MPU_xTaskGetHandle #define uxTaskGetStackHighWaterMark MPU_uxTaskGetStackHighWaterMark + #define uxTaskGetStackHighWaterMark2 MPU_uxTaskGetStackHighWaterMark2 #define vTaskSetApplicationTaskTag MPU_vTaskSetApplicationTaskTag #define xTaskGetApplicationTaskTag MPU_xTaskGetApplicationTaskTag #define vTaskSetThreadLocalStoragePointer MPU_vTaskSetThreadLocalStoragePointer @@ -118,6 +77,7 @@ only for ports that are using the MPU. */ #define uxTaskGetSystemState MPU_uxTaskGetSystemState #define vTaskList MPU_vTaskList #define vTaskGetRunTimeStats MPU_vTaskGetRunTimeStats + #define xTaskGetIdleRunTimeCounter MPU_xTaskGetIdleRunTimeCounter #define xTaskGenericNotify MPU_xTaskGenericNotify #define xTaskNotifyWait MPU_xTaskNotifyWait #define ulTaskNotifyTake MPU_ulTaskNotifyTake @@ -130,7 +90,9 @@ only for ports that are using the MPU. */ /* Map standard queue.h API functions to the MPU equivalents. */ #define xQueueGenericSend MPU_xQueueGenericSend - #define xQueueGenericReceive MPU_xQueueGenericReceive + #define xQueueReceive MPU_xQueueReceive + #define xQueuePeek MPU_xQueuePeek + #define xQueueSemaphoreTake MPU_xQueueSemaphoreTake #define uxQueueMessagesWaiting MPU_uxQueueMessagesWaiting #define uxQueueSpacesAvailable MPU_uxQueueSpacesAvailable #define vQueueDelete MPU_vQueueDelete @@ -164,6 +126,7 @@ only for ports that are using the MPU. */ #define xTimerGetTimerDaemonTaskHandle MPU_xTimerGetTimerDaemonTaskHandle #define xTimerPendFunctionCall MPU_xTimerPendFunctionCall #define pcTimerGetName MPU_pcTimerGetName + #define vTimerSetReloadMode MPU_vTimerSetReloadMode #define xTimerGetPeriod MPU_xTimerGetPeriod #define xTimerGetExpiryTime MPU_xTimerGetExpiryTime #define xTimerGenericCommand MPU_xTimerGenericCommand @@ -177,25 +140,35 @@ only for ports that are using the MPU. */ #define xEventGroupSync MPU_xEventGroupSync #define vEventGroupDelete MPU_vEventGroupDelete - /* Remove the privileged function macro. */ + /* Map standard message/stream_buffer.h API functions to the MPU + equivalents. */ + #define xStreamBufferSend MPU_xStreamBufferSend + #define xStreamBufferReceive MPU_xStreamBufferReceive + #define xStreamBufferNextMessageLengthBytes MPU_xStreamBufferNextMessageLengthBytes + #define vStreamBufferDelete MPU_vStreamBufferDelete + #define xStreamBufferIsFull MPU_xStreamBufferIsFull + #define xStreamBufferIsEmpty MPU_xStreamBufferIsEmpty + #define xStreamBufferReset MPU_xStreamBufferReset + #define xStreamBufferSpacesAvailable MPU_xStreamBufferSpacesAvailable + #define xStreamBufferBytesAvailable MPU_xStreamBufferBytesAvailable + #define xStreamBufferSetTriggerLevel MPU_xStreamBufferSetTriggerLevel + #define xStreamBufferGenericCreate MPU_xStreamBufferGenericCreate + #define xStreamBufferGenericCreateStatic MPU_xStreamBufferGenericCreateStatic + + + /* Remove the privileged function macro, but keep the PRIVILEGED_DATA + macro so applications can place data in privileged access sections + (useful when using statically allocated objects). */ #define PRIVILEGED_FUNCTION + #define PRIVILEGED_DATA __attribute__((section("privileged_data"))) + #define FREERTOS_SYSTEM_CALL #else /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ /* Ensure API functions go in the privileged execution section. */ -#if defined(__ICCARM__) - -#define PRIVILEGED_FUNCTION _Pragma("location= \"privileged_functions\"") -#define PRIVILEGED_DATA _Pragma("location= \"privileged_data\"") -#define PRIVILEGED_INITIALIZED_DATA _Pragma("location= \"privileged_initialized_data\"") - -#else - -#define PRIVILEGED_FUNCTION __attribute__((section("privileged_functions"))) -#define PRIVILEGED_DATA __attribute__((section("privileged_data"))) -#define PRIVILEGED_INITIALIZED_DATA PRIVILEGED_DATA - -#endif + #define PRIVILEGED_FUNCTION __attribute__((section("privileged_functions"))) + #define PRIVILEGED_DATA __attribute__((section("privileged_data"))) + #define FREERTOS_SYSTEM_CALL __attribute__((section( "freertos_system_calls"))) #endif /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ @@ -203,7 +176,7 @@ only for ports that are using the MPU. */ #define PRIVILEGED_FUNCTION #define PRIVILEGED_DATA - #define PRIVILEGED_INITIALIZED_DATA + #define FREERTOS_SYSTEM_CALL #define portUSING_MPU_WRAPPERS 0 #endif /* portUSING_MPU_WRAPPERS */ diff --git a/Firmware/ThirdParty/FreeRTOS/Source/include/portable.h b/Firmware/ThirdParty/FreeRTOS/Source/include/portable.h new file mode 100644 index 000000000..59e816947 --- /dev/null +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/portable.h @@ -0,0 +1,181 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/*----------------------------------------------------------- + * Portable layer API. Each function must be defined for each port. + *----------------------------------------------------------*/ + +#ifndef PORTABLE_H +#define PORTABLE_H + +/* Each FreeRTOS port has a unique portmacro.h header file. Originally a +pre-processor definition was used to ensure the pre-processor found the correct +portmacro.h file for the port being used. That scheme was deprecated in favour +of setting the compiler's include path such that it found the correct +portmacro.h file - removing the need for the constant and allowing the +portmacro.h file to be located anywhere in relation to the port being used. +Purely for reasons of backward compatibility the old method is still valid, but +to make it clear that new projects should not use it, support for the port +specific constants has been moved into the deprecated_definitions.h header +file. */ +#include "deprecated_definitions.h" + +/* If portENTER_CRITICAL is not defined then including deprecated_definitions.h +did not result in a portmacro.h header file being included - and it should be +included here. In this case the path to the correct portmacro.h header file +must be set in the compiler's include path. */ +#ifndef portENTER_CRITICAL + #include "portmacro.h" +#endif + +#if portBYTE_ALIGNMENT == 32 + #define portBYTE_ALIGNMENT_MASK ( 0x001f ) +#endif + +#if portBYTE_ALIGNMENT == 16 + #define portBYTE_ALIGNMENT_MASK ( 0x000f ) +#endif + +#if portBYTE_ALIGNMENT == 8 + #define portBYTE_ALIGNMENT_MASK ( 0x0007 ) +#endif + +#if portBYTE_ALIGNMENT == 4 + #define portBYTE_ALIGNMENT_MASK ( 0x0003 ) +#endif + +#if portBYTE_ALIGNMENT == 2 + #define portBYTE_ALIGNMENT_MASK ( 0x0001 ) +#endif + +#if portBYTE_ALIGNMENT == 1 + #define portBYTE_ALIGNMENT_MASK ( 0x0000 ) +#endif + +#ifndef portBYTE_ALIGNMENT_MASK + #error "Invalid portBYTE_ALIGNMENT definition" +#endif + +#ifndef portNUM_CONFIGURABLE_REGIONS + #define portNUM_CONFIGURABLE_REGIONS 1 +#endif + +#ifndef portHAS_STACK_OVERFLOW_CHECKING + #define portHAS_STACK_OVERFLOW_CHECKING 0 +#endif + +#ifndef portARCH_NAME + #define portARCH_NAME NULL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mpu_wrappers.h" + +/* + * Setup the stack of a new task so it is ready to be placed under the + * scheduler control. The registers have to be placed on the stack in + * the order that the port expects to find them. + * + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, StackType_t *pxEndOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) PRIVILEGED_FUNCTION; + #else + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) PRIVILEGED_FUNCTION; + #endif +#else + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, StackType_t *pxEndOfStack, TaskFunction_t pxCode, void *pvParameters ) PRIVILEGED_FUNCTION; + #else + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) PRIVILEGED_FUNCTION; + #endif +#endif + +/* Used by heap_5.c. */ +typedef struct HeapRegion +{ + uint8_t *pucStartAddress; + size_t xSizeInBytes; +} HeapRegion_t; + +/* + * Used to define multiple heap regions for use by heap_5.c. This function + * must be called before any calls to pvPortMalloc() - not creating a task, + * queue, semaphore, mutex, software timer, event group, etc. will result in + * pvPortMalloc being called. + * + * pxHeapRegions passes in an array of HeapRegion_t structures - each of which + * defines a region of memory that can be used as the heap. The array is + * terminated by a HeapRegions_t structure that has a size of 0. The region + * with the lowest start address must appear first in the array. + */ +void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) PRIVILEGED_FUNCTION; + + +/* + * Map to the memory management routines required for the port. + */ +void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION; +void vPortFree( void *pv ) PRIVILEGED_FUNCTION; +void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION; +size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION; +size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION; + +/* + * Setup the hardware ready for the scheduler to take control. This generally + * sets up a tick interrupt and sets timers for the correct tick frequency. + */ +BaseType_t xPortStartScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * Undo any hardware/ISR setup that was performed by xPortStartScheduler() so + * the hardware is left in its original condition after the scheduler stops + * executing. + */ +void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * The structures and methods of manipulating the MPU are contained within the + * port layer. + * + * Fills the xMPUSettings structure with the memory region information + * contained in xRegions. + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + struct xMEMORY_REGION; + void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t ulStackDepth ) PRIVILEGED_FUNCTION; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PORTABLE_H */ + diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h b/Firmware/ThirdParty/FreeRTOS/Source/include/projdefs.h similarity index 51% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/projdefs.h index 0b63fd8a9..e0458619e 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/projdefs.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef PROJDEFS_H #define PROJDEFS_H @@ -152,8 +110,13 @@ itself. */ /* The following endian values are used by FreeRTOS+ components, not FreeRTOS itself. */ -#define pdFREERTOS_LITTLE_ENDIAN 0 -#define pdFREERTOS_BIG_ENDIAN 1 +#define pdFREERTOS_LITTLE_ENDIAN 0 +#define pdFREERTOS_BIG_ENDIAN 1 + +/* Re-defining endian values for generic naming. */ +#define pdLITTLE_ENDIAN pdFREERTOS_LITTLE_ENDIAN +#define pdBIG_ENDIAN pdFREERTOS_BIG_ENDIAN + #endif /* PROJDEFS_H */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h b/Firmware/ThirdParty/FreeRTOS/Source/include/queue.h similarity index 82% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/queue.h index a64640c6f..3b9da937a 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/queue.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef QUEUE_H @@ -79,27 +37,29 @@ extern "C" { #endif +#include "task.h" /** * Type by which queues are referenced. For example, a call to xQueueCreate() * returns an QueueHandle_t variable that can then be used as a parameter to * xQueueSend(), xQueueReceive(), etc. */ -typedef void * QueueHandle_t; +struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */ +typedef struct QueueDefinition * QueueHandle_t; /** * Type by which queue sets are referenced. For example, a call to * xQueueCreateSet() returns an xQueueSet variable that can then be used as a * parameter to xQueueSelectFromSet(), xQueueAddToSet(), etc. */ -typedef void * QueueSetHandle_t; +typedef struct QueueDefinition * QueueSetHandle_t; /** * Queue sets can contain both queues and semaphores, so the * QueueSetMemberHandle_t is defined as a type to be used where a parameter or * return value can be either an QueueHandle_t or an SemaphoreHandle_t. */ -typedef void * QueueSetMemberHandle_t; +typedef struct QueueDefinition * QueueSetMemberHandle_t; /* For internal use only. */ #define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) @@ -282,8 +242,6 @@ typedef void * QueueSetMemberHandle_t; ); *
* - * This is a macro that calls xQueueGenericSend(). - * * Post an item to the front of a queue. The item is queued by copy, not by * reference. This function must not be called from an interrupt service * routine. See xQueueSendFromISR () for an alternative which may be used @@ -689,19 +647,17 @@ typedef void * QueueSetMemberHandle_t; * \defgroup xQueueSend xQueueSend * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ); +BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; /** * queue. h *
  BaseType_t xQueuePeek(
 							 QueueHandle_t xQueue,
-							 void *pvBuffer,
+							 void * const pvBuffer,
 							 TickType_t xTicksToWait
 						 );
* - * This is a macro that calls the xQueueGenericReceive() function. - * * Receive an item from a queue without removing the item from the queue. * The item is received by copy so a buffer of adequate size must be * provided. The number of bytes copied into the buffer was defined when @@ -782,10 +738,10 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const vo // ... Rest of task code. }
- * \defgroup xQueueReceive xQueueReceive + * \defgroup xQueuePeek xQueuePeek * \ingroup QueueManagement */ -#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE ) +BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * queue. h @@ -818,7 +774,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const vo * \defgroup xQueuePeekFromISR xQueuePeekFromISR * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ); +BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; /** * queue. h @@ -829,8 +785,6 @@ PRIVILEGED_FUNCTION BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * c TickType_t xTicksToWait );
* - * This is a macro that calls the xQueueGenericReceive() function. - * * Receive an item from a queue. The item is received by copy so a buffer of * adequate size must be provided. The number of bytes copied into the buffer * was defined when the queue was created. @@ -911,106 +865,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * c * \defgroup xQueueReceive xQueueReceive * \ingroup QueueManagement */ -#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE ) - - -/** - * queue. h - *
- BaseType_t xQueueGenericReceive(
-									   QueueHandle_t	xQueue,
-									   void	*pvBuffer,
-									   TickType_t	xTicksToWait
-									   BaseType_t	xJustPeek
-									);
- * - * It is preferred that the macro xQueueReceive() be used rather than calling - * this function directly. - * - * Receive an item from a queue. The item is received by copy so a buffer of - * adequate size must be provided. The number of bytes copied into the buffer - * was defined when the queue was created. - * - * This function must not be used in an interrupt service routine. See - * xQueueReceiveFromISR for an alternative that can. - * - * @param xQueue The handle to the queue from which the item is to be - * received. - * - * @param pvBuffer Pointer to the buffer into which the received item will - * be copied. - * - * @param xTicksToWait The maximum amount of time the task should block - * waiting for an item to receive should the queue be empty at the time - * of the call. The time is defined in tick periods so the constant - * portTICK_PERIOD_MS should be used to convert to real time if this is required. - * xQueueGenericReceive() will return immediately if the queue is empty and - * xTicksToWait is 0. - * - * @param xJustPeek When set to true, the item received from the queue is not - * actually removed from the queue - meaning a subsequent call to - * xQueueReceive() will return the same item. When set to false, the item - * being received from the queue is also removed from the queue. - * - * @return pdTRUE if an item was successfully received from the queue, - * otherwise pdFALSE. - * - * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- QueueHandle_t xQueue;
-
- // Task to create a queue and post a value.
- void vATask( void *pvParameters )
- {
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Send a pointer to a struct AMessage object.  Don't block if the
-	// queue is already full.
-	pxMessage = & xMessage;
-	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
-
-	// ... Rest of task code.
- }
-
- // Task to receive from the queue.
- void vADifferentTask( void *pvParameters )
- {
- struct AMessage *pxRxedMessage;
-
-	if( xQueue != 0 )
-	{
-		// Receive a message on the created queue.  Block for 10 ticks if a
-		// message is not immediately available.
-		if( xQueueGenericReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
-		{
-			// pcRxedMessage now points to the struct AMessage variable posted
-			// by vATask.
-		}
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueReceive xQueueReceive - * \ingroup QueueManagement - */ -PRIVILEGED_FUNCTION BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeek ); +BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * queue. h @@ -1025,7 +880,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ); +UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** * queue. h @@ -1042,7 +897,7 @@ PRIVILEGED_FUNCTION UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQue * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ); +UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** * queue. h @@ -1056,7 +911,7 @@ PRIVILEGED_FUNCTION UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQue * \defgroup vQueueDelete vQueueDelete * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION void vQueueDelete( QueueHandle_t xQueue ); +void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** * queue. h @@ -1437,8 +1292,8 @@ uint32_t ulVarToSend, ulValReceived; * \defgroup xQueueSendFromISR xQueueSendFromISR * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ); -PRIVILEGED_FUNCTION BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken ); +BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /** * queue. h @@ -1527,15 +1382,15 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ); +BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /* * Utilities to query queues that are safe to use from an ISR. These utilities * should be used only from witin an ISR, or within a critical section. */ -PRIVILEGED_FUNCTION BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ); -PRIVILEGED_FUNCTION BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ); -PRIVILEGED_FUNCTION UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ); +BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /* * The functions defined above are for passing data to and from tasks. The @@ -1556,18 +1411,20 @@ BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTi * xSemaphoreCreateCounting() or xSemaphoreGetMutexHolder() instead of calling * these functions directly. */ -PRIVILEGED_FUNCTION QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ); -PRIVILEGED_FUNCTION QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ); -PRIVILEGED_FUNCTION QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ); -PRIVILEGED_FUNCTION QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ); -PRIVILEGED_FUNCTION void* xQueueGetMutexHolder( QueueHandle_t xSemaphore ); +QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; +BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; +TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; /* * For internal use only. Use xSemaphoreTakeMutexRecursive() or * xSemaphoreGiveMutexRecursive() instead of calling these functions directly. */ -PRIVILEGED_FUNCTION BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ); -PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ); +BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) PRIVILEGED_FUNCTION; /* * Reset a queue back to its original empty state. The return value is now @@ -1598,7 +1455,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * preferably in ROM/Flash), not on the stack. */ #if( configQUEUE_REGISTRY_SIZE > 0 ) - PRIVILEGED_FUNCTION void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcName ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcQueueName ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #endif /* @@ -1612,7 +1469,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * @param xQueue The handle of the queue being removed from the registry. */ #if( configQUEUE_REGISTRY_SIZE > 0 ) - PRIVILEGED_FUNCTION void vQueueUnregisterQueue( QueueHandle_t xQueue ); + void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; #endif /* @@ -1627,7 +1484,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * returned. */ #if( configQUEUE_REGISTRY_SIZE > 0 ) - PRIVILEGED_FUNCTION const char *pcQueueGetName( QueueHandle_t xQueue ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const char *pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #endif /* @@ -1636,7 +1493,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * RTOS objects that use the queue structure as their base. */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - PRIVILEGED_FUNCTION QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ); + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; #endif /* @@ -1645,7 +1502,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * RTOS objects that use the queue structure as their base. */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - PRIVILEGED_FUNCTION QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ); + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; #endif /* @@ -1696,7 +1553,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * @return If the queue set is created successfully then a handle to the created * queue set is returned. Otherwise NULL is returned. */ -PRIVILEGED_FUNCTION QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ); +QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION; /* * Adds a queue or semaphore to a queue set that was previously created by a @@ -1720,7 +1577,7 @@ PRIVILEGED_FUNCTION QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQ * queue set because it is already a member of a different queue set then pdFAIL * is returned. */ -PRIVILEGED_FUNCTION BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); +BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; /* * Removes a queue or semaphore from a queue set. A queue or semaphore can only @@ -1739,7 +1596,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSe * then pdPASS is returned. If the queue was not in the queue set, or the * queue (or semaphore) was not empty, then pdFAIL is returned. */ -PRIVILEGED_FUNCTION BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); +BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; /* * xQueueSelectFromSet() selects from the members of a queue set a queue or @@ -1775,19 +1632,19 @@ PRIVILEGED_FUNCTION BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueu * in the queue set that is available, or NULL if no such queue or semaphore * exists before before the specified block time expires. */ -PRIVILEGED_FUNCTION QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ); +QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /* * A version of xQueueSelectFromSet() that can be used from an ISR. */ -PRIVILEGED_FUNCTION QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ); +QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; /* Not public API functions. */ -PRIVILEGED_FUNCTION void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) ; -PRIVILEGED_FUNCTION BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ); -PRIVILEGED_FUNCTION void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ); -PRIVILEGED_FUNCTION UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ); -PRIVILEGED_FUNCTION uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ); +void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) PRIVILEGED_FUNCTION; +void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; #ifdef __cplusplus diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h b/Firmware/ThirdParty/FreeRTOS/Source/include/semphr.h similarity index 90% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/semphr.h index a674b02a4..2c106eac0 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/semphr.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef SEMAPHORE_H #define SEMAPHORE_H @@ -328,7 +286,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup xSemaphoreTake xSemaphoreTake * \ingroup Semaphores */ -#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE ) +#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) ) /** * semphr. h @@ -392,23 +350,23 @@ typedef QueueHandle_t SemaphoreHandle_t; // ... // For some reason due to the nature of the code further calls to - // xSemaphoreTakeRecursive() are made on the same mutex. In real - // code these would not be just sequential calls as this would make - // no sense. Instead the calls are likely to be buried inside - // a more complex call structure. + // xSemaphoreTakeRecursive() are made on the same mutex. In real + // code these would not be just sequential calls as this would make + // no sense. Instead the calls are likely to be buried inside + // a more complex call structure. xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); // The mutex has now been 'taken' three times, so will not be - // available to another task until it has also been given back - // three times. Again it is unlikely that real code would have - // these calls sequentially, but instead buried in a more complex - // call structure. This is just for illustrative purposes. + // available to another task until it has also been given back + // three times. Again it is unlikely that real code would have + // these calls sequentially, but instead buried in a more complex + // call structure. This is just for illustrative purposes. + xSemaphoreGiveRecursive( xMutex ); + xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); - xSemaphoreGiveRecursive( xMutex ); - xSemaphoreGiveRecursive( xMutex ); - // Now the mutex can be taken by other tasks. + // Now the mutex can be taken by other tasks. } else { @@ -1154,6 +1112,17 @@ typedef QueueHandle_t SemaphoreHandle_t; */ #define xSemaphoreGetMutexHolder( xSemaphore ) xQueueGetMutexHolder( ( xSemaphore ) ) +/** + * semphr.h + *
TaskHandle_t xSemaphoreGetMutexHolderFromISR( SemaphoreHandle_t xMutex );
+ * + * If xMutex is indeed a mutex type semaphore, return the current mutex holder. + * If xMutex is not a mutex type semaphore, or the mutex is available (not held + * by a task), return NULL. + * + */ +#define xSemaphoreGetMutexHolderFromISR( xSemaphore ) xQueueGetMutexHolderFromISR( ( xSemaphore ) ) + /** * semphr.h *
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
diff --git a/Firmware/ThirdParty/FreeRTOS/Source/include/stack_macros.h b/Firmware/ThirdParty/FreeRTOS/Source/include/stack_macros.h new file mode 100644 index 000000000..18406bbf9 --- /dev/null +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/stack_macros.h @@ -0,0 +1,129 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef STACK_MACROS_H +#define STACK_MACROS_H + +/* + * Call the stack overflow hook function if the stack of the task being swapped + * out is currently overflowed, or looks like it might have overflowed in the + * past. + * + * Setting configCHECK_FOR_STACK_OVERFLOW to 1 will cause the macro to check + * the current stack state only - comparing the current top of stack value to + * the stack limit. Setting configCHECK_FOR_STACK_OVERFLOW to greater than 1 + * will also cause the last few stack bytes to be checked to ensure the value + * to which the bytes were set when the task was created have not been + * overwritten. Note this second test does not guarantee that an overflowed + * stack will always be recognised. + */ + +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH > 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) + + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \ + const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5; \ + \ + if( ( pulStack[ 0 ] != ulCheckValue ) || \ + ( pulStack[ 1 ] != ulCheckValue ) || \ + ( pulStack[ 2 ] != ulCheckValue ) || \ + ( pulStack[ 3 ] != ulCheckValue ) ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) ) + + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + int8_t *pcEndOfStack = ( int8_t * ) pxCurrentTCB->pxEndOfStack; \ + static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ + \ + \ + pcEndOfStack -= sizeof( ucExpectedStackBytes ); \ + \ + /* Has the extremity of the task stack ever been written over? */ \ + if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +/* Remove stack overflow macro if not being used. */ +#ifndef taskCHECK_FOR_STACK_OVERFLOW + #define taskCHECK_FOR_STACK_OVERFLOW() +#endif + + + +#endif /* STACK_MACROS_H */ + diff --git a/Firmware/ThirdParty/FreeRTOS/Source/include/stream_buffer.h b/Firmware/ThirdParty/FreeRTOS/Source/include/stream_buffer.h new file mode 100644 index 000000000..0f00119ee --- /dev/null +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/stream_buffer.h @@ -0,0 +1,855 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * Stream buffers are used to send a continuous stream of data from one task or + * interrupt to another. Their implementation is light weight, making them + * particularly suited for interrupt to task and core to core communication + * scenarios. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xStreamBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xStreamBufferRead()) inside a critical section section and set the + * receive block time to 0. + * + */ + +#ifndef STREAM_BUFFER_H +#define STREAM_BUFFER_H + +#if defined( __cplusplus ) +extern "C" { +#endif + +/** + * Type by which stream buffers are referenced. For example, a call to + * xStreamBufferCreate() returns an StreamBufferHandle_t variable that can + * then be used as a parameter to xStreamBufferSend(), xStreamBufferReceive(), + * etc. + */ +struct StreamBufferDef_t; +typedef struct StreamBufferDef_t * StreamBufferHandle_t; + + +/** + * message_buffer.h + * +
+StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes );
+
+ * + * Creates a new stream buffer using dynamically allocated memory. See + * xStreamBufferCreateStatic() for a version that uses statically allocated + * memory (memory that is allocated at compile time). + * + * configSUPPORT_DYNAMIC_ALLOCATION must be set to 1 or left undefined in + * FreeRTOSConfig.h for xStreamBufferCreate() to be available. + * + * @param xBufferSizeBytes The total number of bytes the stream buffer will be + * able to hold at any one time. + * + * @param xTriggerLevelBytes The number of bytes that must be in the stream + * buffer before a task that is blocked on the stream buffer to wait for data is + * moved out of the blocked state. For example, if a task is blocked on a read + * of an empty stream buffer that has a trigger level of 1 then the task will be + * unblocked when a single byte is written to the buffer or the task's block + * time expires. As another example, if a task is blocked on a read of an empty + * stream buffer that has a trigger level of 10 then the task will not be + * unblocked until the stream buffer contains at least 10 bytes or the task's + * block time expires. If a reading task's block time expires before the + * trigger level is reached then the task will still receive however many bytes + * are actually available. Setting a trigger level of 0 will result in a + * trigger level of 1 being used. It is not valid to specify a trigger level + * that is greater than the buffer size. + * + * @return If NULL is returned, then the stream buffer cannot be created + * because there is insufficient heap memory available for FreeRTOS to allocate + * the stream buffer data structures and storage area. A non-NULL value being + * returned indicates that the stream buffer has been created successfully - + * the returned value should be stored as the handle to the created stream + * buffer. + * + * Example use: +
+
+void vAFunction( void )
+{
+StreamBufferHandle_t xStreamBuffer;
+const size_t xStreamBufferSizeBytes = 100, xTriggerLevel = 10;
+
+    // Create a stream buffer that can hold 100 bytes.  The memory used to hold
+    // both the stream buffer structure and the data in the stream buffer is
+    // allocated dynamically.
+    xStreamBuffer = xStreamBufferCreate( xStreamBufferSizeBytes, xTriggerLevel );
+
+    if( xStreamBuffer == NULL )
+    {
+        // There was not enough heap memory space available to create the
+        // stream buffer.
+    }
+    else
+    {
+        // The stream buffer was created successfully and can now be used.
+    }
+}
+
+ * \defgroup xStreamBufferCreate xStreamBufferCreate + * \ingroup StreamBufferManagement + */ +#define xStreamBufferCreate( xBufferSizeBytes, xTriggerLevelBytes ) xStreamBufferGenericCreate( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE ) + +/** + * stream_buffer.h + * +
+StreamBufferHandle_t xStreamBufferCreateStatic( size_t xBufferSizeBytes,
+                                                size_t xTriggerLevelBytes,
+                                                uint8_t *pucStreamBufferStorageArea,
+                                                StaticStreamBuffer_t *pxStaticStreamBuffer );
+
+ * Creates a new stream buffer using statically allocated memory. See + * xStreamBufferCreate() for a version that uses dynamically allocated memory. + * + * configSUPPORT_STATIC_ALLOCATION must be set to 1 in FreeRTOSConfig.h for + * xStreamBufferCreateStatic() to be available. + * + * @param xBufferSizeBytes The size, in bytes, of the buffer pointed to by the + * pucStreamBufferStorageArea parameter. + * + * @param xTriggerLevelBytes The number of bytes that must be in the stream + * buffer before a task that is blocked on the stream buffer to wait for data is + * moved out of the blocked state. For example, if a task is blocked on a read + * of an empty stream buffer that has a trigger level of 1 then the task will be + * unblocked when a single byte is written to the buffer or the task's block + * time expires. As another example, if a task is blocked on a read of an empty + * stream buffer that has a trigger level of 10 then the task will not be + * unblocked until the stream buffer contains at least 10 bytes or the task's + * block time expires. If a reading task's block time expires before the + * trigger level is reached then the task will still receive however many bytes + * are actually available. Setting a trigger level of 0 will result in a + * trigger level of 1 being used. It is not valid to specify a trigger level + * that is greater than the buffer size. + * + * @param pucStreamBufferStorageArea Must point to a uint8_t array that is at + * least xBufferSizeBytes + 1 big. This is the array to which streams are + * copied when they are written to the stream buffer. + * + * @param pxStaticStreamBuffer Must point to a variable of type + * StaticStreamBuffer_t, which will be used to hold the stream buffer's data + * structure. + * + * @return If the stream buffer is created successfully then a handle to the + * created stream buffer is returned. If either pucStreamBufferStorageArea or + * pxStaticstreamBuffer are NULL then NULL is returned. + * + * Example use: +
+
+// Used to dimension the array used to hold the streams.  The available space
+// will actually be one less than this, so 999.
+#define STORAGE_SIZE_BYTES 1000
+
+// Defines the memory that will actually hold the streams within the stream
+// buffer.
+static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
+
+// The variable used to hold the stream buffer structure.
+StaticStreamBuffer_t xStreamBufferStruct;
+
+void MyFunction( void )
+{
+StreamBufferHandle_t xStreamBuffer;
+const size_t xTriggerLevel = 1;
+
+    xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucBufferStorage ),
+                                               xTriggerLevel,
+                                               ucBufferStorage,
+                                               &xStreamBufferStruct );
+
+    // As neither the pucStreamBufferStorageArea or pxStaticStreamBuffer
+    // parameters were NULL, xStreamBuffer will not be NULL, and can be used to
+    // reference the created stream buffer in other stream buffer API calls.
+
+    // Other code that uses the stream buffer can go here.
+}
+
+
+ * \defgroup xStreamBufferCreateStatic xStreamBufferCreateStatic + * \ingroup StreamBufferManagement + */ +#define xStreamBufferCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE, pucStreamBufferStorageArea, pxStaticStreamBuffer ) + +/** + * stream_buffer.h + * +
+size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
+                          const void *pvTxData,
+                          size_t xDataLengthBytes,
+                          TickType_t xTicksToWait );
+
+ * + * Sends bytes to a stream buffer. The bytes are copied into the stream buffer. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xStreamBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xStreamBufferRead()) inside a critical section and set the receive + * block time to 0. + * + * Use xStreamBufferSend() to write to a stream buffer from a task. Use + * xStreamBufferSendFromISR() to write to a stream buffer from an interrupt + * service routine (ISR). + * + * @param xStreamBuffer The handle of the stream buffer to which a stream is + * being sent. + * + * @param pvTxData A pointer to the buffer that holds the bytes to be copied + * into the stream buffer. + * + * @param xDataLengthBytes The maximum number of bytes to copy from pvTxData + * into the stream buffer. + * + * @param xTicksToWait The maximum amount of time the task should remain in the + * Blocked state to wait for enough space to become available in the stream + * buffer, should the stream buffer contain too little space to hold the + * another xDataLengthBytes bytes. The block time is specified in tick periods, + * so the absolute time it represents is dependent on the tick frequency. The + * macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds + * into a time specified in ticks. Setting xTicksToWait to portMAX_DELAY will + * cause the task to wait indefinitely (without timing out), provided + * INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. If a task times out + * before it can write all xDataLengthBytes into the buffer it will still write + * as many bytes as possible. A task does not use any CPU time when it is in + * the blocked state. + * + * @return The number of bytes written to the stream buffer. If a task times + * out before it can write all xDataLengthBytes into the buffer it will still + * write as many bytes as possible. + * + * Example use: +
+void vAFunction( StreamBufferHandle_t xStreamBuffer )
+{
+size_t xBytesSent;
+uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
+char *pcStringToSend = "String to send";
+const TickType_t x100ms = pdMS_TO_TICKS( 100 );
+
+    // Send an array to the stream buffer, blocking for a maximum of 100ms to
+    // wait for enough space to be available in the stream buffer.
+    xBytesSent = xStreamBufferSend( xStreamBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
+
+    if( xBytesSent != sizeof( ucArrayToSend ) )
+    {
+        // The call to xStreamBufferSend() times out before there was enough
+        // space in the buffer for the data to be written, but it did
+        // successfully write xBytesSent bytes.
+    }
+
+    // Send the string to the stream buffer.  Return immediately if there is not
+    // enough space in the buffer.
+    xBytesSent = xStreamBufferSend( xStreamBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
+
+    if( xBytesSent != strlen( pcStringToSend ) )
+    {
+        // The entire string could not be added to the stream buffer because
+        // there was not enough free space in the buffer, but xBytesSent bytes
+        // were sent.  Could try again to send the remaining bytes.
+    }
+}
+
+ * \defgroup xStreamBufferSend xStreamBufferSend + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, + const void *pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer,
+                                 const void *pvTxData,
+                                 size_t xDataLengthBytes,
+                                 BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * Interrupt safe version of the API function that sends a stream of bytes to + * the stream buffer. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xStreamBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xStreamBufferRead()) inside a critical section and set the receive + * block time to 0. + * + * Use xStreamBufferSend() to write to a stream buffer from a task. Use + * xStreamBufferSendFromISR() to write to a stream buffer from an interrupt + * service routine (ISR). + * + * @param xStreamBuffer The handle of the stream buffer to which a stream is + * being sent. + * + * @param pvTxData A pointer to the data that is to be copied into the stream + * buffer. + * + * @param xDataLengthBytes The maximum number of bytes to copy from pvTxData + * into the stream buffer. + * + * @param pxHigherPriorityTaskWoken It is possible that a stream buffer will + * have a task blocked on it waiting for data. Calling + * xStreamBufferSendFromISR() can make data available, and so cause a task that + * was waiting for data to leave the Blocked state. If calling + * xStreamBufferSendFromISR() causes a task to leave the Blocked state, and the + * unblocked task has a priority higher than the currently executing task (the + * task that was interrupted), then, internally, xStreamBufferSendFromISR() + * will set *pxHigherPriorityTaskWoken to pdTRUE. If + * xStreamBufferSendFromISR() sets this value to pdTRUE, then normally a + * context switch should be performed before the interrupt is exited. This will + * ensure that the interrupt returns directly to the highest priority Ready + * state task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it + * is passed into the function. See the example code below for an example. + * + * @return The number of bytes actually written to the stream buffer, which will + * be less than xDataLengthBytes if the stream buffer didn't have enough free + * space for all the bytes to be written. + * + * Example use: +
+// A stream buffer that has already been created.
+StreamBufferHandle_t xStreamBuffer;
+
+void vAnInterruptServiceRoutine( void )
+{
+size_t xBytesSent;
+char *pcStringToSend = "String to send";
+BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
+
+    // Attempt to send the string to the stream buffer.
+    xBytesSent = xStreamBufferSendFromISR( xStreamBuffer,
+                                           ( void * ) pcStringToSend,
+                                           strlen( pcStringToSend ),
+                                           &xHigherPriorityTaskWoken );
+
+    if( xBytesSent != strlen( pcStringToSend ) )
+    {
+        // There was not enough free space in the stream buffer for the entire
+        // string to be written, ut xBytesSent bytes were written.
+    }
+
+    // If xHigherPriorityTaskWoken was set to pdTRUE inside
+    // xStreamBufferSendFromISR() then a task that has a priority above the
+    // priority of the currently executing task was unblocked and a context
+    // switch should be performed to ensure the ISR returns to the unblocked
+    // task.  In most FreeRTOS ports this is done by simply passing
+    // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
+    // variables value, and perform the context switch if necessary.  Check the
+    // documentation for the port in use for port specific instructions.
+    taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+}
+
+ * \defgroup xStreamBufferSendFromISR xStreamBufferSendFromISR + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, + const void *pvTxData, + size_t xDataLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
+                             void *pvRxData,
+                             size_t xBufferLengthBytes,
+                             TickType_t xTicksToWait );
+
+ * + * Receives bytes from a stream buffer. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xStreamBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xStreamBufferRead()) inside a critical section and set the receive + * block time to 0. + * + * Use xStreamBufferReceive() to read from a stream buffer from a task. Use + * xStreamBufferReceiveFromISR() to read from a stream buffer from an + * interrupt service routine (ISR). + * + * @param xStreamBuffer The handle of the stream buffer from which bytes are to + * be received. + * + * @param pvRxData A pointer to the buffer into which the received bytes will be + * copied. + * + * @param xBufferLengthBytes The length of the buffer pointed to by the + * pvRxData parameter. This sets the maximum number of bytes to receive in one + * call. xStreamBufferReceive will return as many bytes as possible up to a + * maximum set by xBufferLengthBytes. + * + * @param xTicksToWait The maximum amount of time the task should remain in the + * Blocked state to wait for data to become available if the stream buffer is + * empty. xStreamBufferReceive() will return immediately if xTicksToWait is + * zero. The block time is specified in tick periods, so the absolute time it + * represents is dependent on the tick frequency. The macro pdMS_TO_TICKS() can + * be used to convert a time specified in milliseconds into a time specified in + * ticks. Setting xTicksToWait to portMAX_DELAY will cause the task to wait + * indefinitely (without timing out), provided INCLUDE_vTaskSuspend is set to 1 + * in FreeRTOSConfig.h. A task does not use any CPU time when it is in the + * Blocked state. + * + * @return The number of bytes actually read from the stream buffer, which will + * be less than xBufferLengthBytes if the call to xStreamBufferReceive() timed + * out before xBufferLengthBytes were available. + * + * Example use: +
+void vAFunction( StreamBuffer_t xStreamBuffer )
+{
+uint8_t ucRxData[ 20 ];
+size_t xReceivedBytes;
+const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
+
+    // Receive up to another sizeof( ucRxData ) bytes from the stream buffer.
+    // Wait in the Blocked state (so not using any CPU processing time) for a
+    // maximum of 100ms for the full sizeof( ucRxData ) number of bytes to be
+    // available.
+    xReceivedBytes = xStreamBufferReceive( xStreamBuffer,
+                                           ( void * ) ucRxData,
+                                           sizeof( ucRxData ),
+                                           xBlockTime );
+
+    if( xReceivedBytes > 0 )
+    {
+        // A ucRxData contains another xRecievedBytes bytes of data, which can
+        // be processed here....
+    }
+}
+
+ * \defgroup xStreamBufferReceive xStreamBufferReceive + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer,
+                                    void *pvRxData,
+                                    size_t xBufferLengthBytes,
+                                    BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * An interrupt safe version of the API function that receives bytes from a + * stream buffer. + * + * Use xStreamBufferReceive() to read bytes from a stream buffer from a task. + * Use xStreamBufferReceiveFromISR() to read bytes from a stream buffer from an + * interrupt service routine (ISR). + * + * @param xStreamBuffer The handle of the stream buffer from which a stream + * is being received. + * + * @param pvRxData A pointer to the buffer into which the received bytes are + * copied. + * + * @param xBufferLengthBytes The length of the buffer pointed to by the + * pvRxData parameter. This sets the maximum number of bytes to receive in one + * call. xStreamBufferReceive will return as many bytes as possible up to a + * maximum set by xBufferLengthBytes. + * + * @param pxHigherPriorityTaskWoken It is possible that a stream buffer will + * have a task blocked on it waiting for space to become available. Calling + * xStreamBufferReceiveFromISR() can make space available, and so cause a task + * that is waiting for space to leave the Blocked state. If calling + * xStreamBufferReceiveFromISR() causes a task to leave the Blocked state, and + * the unblocked task has a priority higher than the currently executing task + * (the task that was interrupted), then, internally, + * xStreamBufferReceiveFromISR() will set *pxHigherPriorityTaskWoken to pdTRUE. + * If xStreamBufferReceiveFromISR() sets this value to pdTRUE, then normally a + * context switch should be performed before the interrupt is exited. That will + * ensure the interrupt returns directly to the highest priority Ready state + * task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it is + * passed into the function. See the code example below for an example. + * + * @return The number of bytes read from the stream buffer, if any. + * + * Example use: +
+// A stream buffer that has already been created.
+StreamBuffer_t xStreamBuffer;
+
+void vAnInterruptServiceRoutine( void )
+{
+uint8_t ucRxData[ 20 ];
+size_t xReceivedBytes;
+BaseType_t xHigherPriorityTaskWoken = pdFALSE;  // Initialised to pdFALSE.
+
+    // Receive the next stream from the stream buffer.
+    xReceivedBytes = xStreamBufferReceiveFromISR( xStreamBuffer,
+                                                  ( void * ) ucRxData,
+                                                  sizeof( ucRxData ),
+                                                  &xHigherPriorityTaskWoken );
+
+    if( xReceivedBytes > 0 )
+    {
+        // ucRxData contains xReceivedBytes read from the stream buffer.
+        // Process the stream here....
+    }
+
+    // If xHigherPriorityTaskWoken was set to pdTRUE inside
+    // xStreamBufferReceiveFromISR() then a task that has a priority above the
+    // priority of the currently executing task was unblocked and a context
+    // switch should be performed to ensure the ISR returns to the unblocked
+    // task.  In most FreeRTOS ports this is done by simply passing
+    // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
+    // variables value, and perform the context switch if necessary.  Check the
+    // documentation for the port in use for port specific instructions.
+    taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+}
+
+ * \defgroup xStreamBufferReceiveFromISR xStreamBufferReceiveFromISR + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Deletes a stream buffer that was previously created using a call to + * xStreamBufferCreate() or xStreamBufferCreateStatic(). If the stream + * buffer was created using dynamic memory (that is, by xStreamBufferCreate()), + * then the allocated memory is freed. + * + * A stream buffer handle must not be used after the stream buffer has been + * deleted. + * + * @param xStreamBuffer The handle of the stream buffer to be deleted. + * + * \defgroup vStreamBufferDelete vStreamBufferDelete + * \ingroup StreamBufferManagement + */ +void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Queries a stream buffer to see if it is full. A stream buffer is full if it + * does not have any free space, and therefore cannot accept any more data. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return If the stream buffer is full then pdTRUE is returned. Otherwise + * pdFALSE is returned. + * + * \defgroup xStreamBufferIsFull xStreamBufferIsFull + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Queries a stream buffer to see if it is empty. A stream buffer is empty if + * it does not contain any data. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return If the stream buffer is empty then pdTRUE is returned. Otherwise + * pdFALSE is returned. + * + * \defgroup xStreamBufferIsEmpty xStreamBufferIsEmpty + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Resets a stream buffer to its initial, empty, state. Any data that was in + * the stream buffer is discarded. A stream buffer can only be reset if there + * are no tasks blocked waiting to either send to or receive from the stream + * buffer. + * + * @param xStreamBuffer The handle of the stream buffer being reset. + * + * @return If the stream buffer is reset then pdPASS is returned. If there was + * a task blocked waiting to send to or read from the stream buffer then the + * stream buffer is not reset and pdFAIL is returned. + * + * \defgroup xStreamBufferReset xStreamBufferReset + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Queries a stream buffer to see how much free space it contains, which is + * equal to the amount of data that can be sent to the stream buffer before it + * is full. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return The number of bytes that can be written to the stream buffer before + * the stream buffer would be full. + * + * \defgroup xStreamBufferSpacesAvailable xStreamBufferSpacesAvailable + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Queries a stream buffer to see how much data it contains, which is equal to + * the number of bytes that can be read from the stream buffer before the stream + * buffer would be empty. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return The number of bytes that can be read from the stream buffer before + * the stream buffer would be empty. + * + * \defgroup xStreamBufferBytesAvailable xStreamBufferBytesAvailable + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel );
+
+ * + * A stream buffer's trigger level is the number of bytes that must be in the + * stream buffer before a task that is blocked on the stream buffer to + * wait for data is moved out of the blocked state. For example, if a task is + * blocked on a read of an empty stream buffer that has a trigger level of 1 + * then the task will be unblocked when a single byte is written to the buffer + * or the task's block time expires. As another example, if a task is blocked + * on a read of an empty stream buffer that has a trigger level of 10 then the + * task will not be unblocked until the stream buffer contains at least 10 bytes + * or the task's block time expires. If a reading task's block time expires + * before the trigger level is reached then the task will still receive however + * many bytes are actually available. Setting a trigger level of 0 will result + * in a trigger level of 1 being used. It is not valid to specify a trigger + * level that is greater than the buffer size. + * + * A trigger level is set when the stream buffer is created, and can be modified + * using xStreamBufferSetTriggerLevel(). + * + * @param xStreamBuffer The handle of the stream buffer being updated. + * + * @param xTriggerLevel The new trigger level for the stream buffer. + * + * @return If xTriggerLevel was less than or equal to the stream buffer's length + * then the trigger level will be updated and pdTRUE is returned. Otherwise + * pdFALSE is returned. + * + * \defgroup xStreamBufferSetTriggerLevel xStreamBufferSetTriggerLevel + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * For advanced users only. + * + * The sbSEND_COMPLETED() macro is called from within the FreeRTOS APIs when + * data is sent to a message buffer or stream buffer. If there was a task that + * was blocked on the message or stream buffer waiting for data to arrive then + * the sbSEND_COMPLETED() macro sends a notification to the task to remove it + * from the Blocked state. xStreamBufferSendCompletedFromISR() does the same + * thing. It is provided to enable application writers to implement their own + * version of sbSEND_COMPLETED(), and MUST NOT BE USED AT ANY OTHER TIME. + * + * See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for + * additional information. + * + * @param xStreamBuffer The handle of the stream buffer to which data was + * written. + * + * @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be + * initialised to pdFALSE before it is passed into + * xStreamBufferSendCompletedFromISR(). If calling + * xStreamBufferSendCompletedFromISR() removes a task from the Blocked state, + * and the task has a priority above the priority of the currently running task, + * then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a + * context switch should be performed before exiting the ISR. + * + * @return If a task was removed from the Blocked state then pdTRUE is returned. + * Otherwise pdFALSE is returned. + * + * \defgroup xStreamBufferSendCompletedFromISR xStreamBufferSendCompletedFromISR + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * For advanced users only. + * + * The sbRECEIVE_COMPLETED() macro is called from within the FreeRTOS APIs when + * data is read out of a message buffer or stream buffer. If there was a task + * that was blocked on the message or stream buffer waiting for data to arrive + * then the sbRECEIVE_COMPLETED() macro sends a notification to the task to + * remove it from the Blocked state. xStreamBufferReceiveCompletedFromISR() + * does the same thing. It is provided to enable application writers to + * implement their own version of sbRECEIVE_COMPLETED(), and MUST NOT BE USED AT + * ANY OTHER TIME. + * + * See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for + * additional information. + * + * @param xStreamBuffer The handle of the stream buffer from which data was + * read. + * + * @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be + * initialised to pdFALSE before it is passed into + * xStreamBufferReceiveCompletedFromISR(). If calling + * xStreamBufferReceiveCompletedFromISR() removes a task from the Blocked state, + * and the task has a priority above the priority of the currently running task, + * then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a + * context switch should be performed before exiting the ISR. + * + * @return If a task was removed from the Blocked state then pdTRUE is returned. + * Otherwise pdFALSE is returned. + * + * \defgroup xStreamBufferReceiveCompletedFromISR xStreamBufferReceiveCompletedFromISR + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/* Functions below here are not part of the public API. */ +StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer ) PRIVILEGED_FUNCTION; + +StreamBufferHandle_t xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer, + uint8_t * const pucStreamBufferStorageArea, + StaticStreamBuffer_t * const pxStaticStreamBuffer ) PRIVILEGED_FUNCTION; + +size_t xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +#if( configUSE_TRACE_FACILITY == 1 ) + void vStreamBufferSetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer, UBaseType_t uxStreamBufferNumber ) PRIVILEGED_FUNCTION; + UBaseType_t uxStreamBufferGetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + uint8_t ucStreamBufferGetStreamBufferType( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; +#endif + +#if defined( __cplusplus ) +} +#endif + +#endif /* !defined( STREAM_BUFFER_H ) */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/task.h b/Firmware/ThirdParty/FreeRTOS/Source/include/task.h similarity index 82% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/task.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/task.h index cfa065eb5..f3cf118f8 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/task.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/task.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef INC_TASK_H @@ -85,11 +43,19 @@ extern "C" { * MACROS AND DEFINITIONS *----------------------------------------------------------*/ -#define tskKERNEL_VERSION_NUMBER "V9.0.0" -#define tskKERNEL_VERSION_MAJOR 9 -#define tskKERNEL_VERSION_MINOR 0 +#define tskKERNEL_VERSION_NUMBER "V10.2.0" +#define tskKERNEL_VERSION_MAJOR 10 +#define tskKERNEL_VERSION_MINOR 2 #define tskKERNEL_VERSION_BUILD 0 +/* MPU region parameters passed in ulParameters + * of MemoryRegion_t struct. */ +#define tskMPU_REGION_READ_ONLY ( 1UL << 0UL ) +#define tskMPU_REGION_READ_WRITE ( 1UL << 1UL ) +#define tskMPU_REGION_EXECUTE_NEVER ( 1UL << 2UL ) +#define tskMPU_REGION_NORMAL_MEMORY ( 1UL << 3UL ) +#define tskMPU_REGION_DEVICE_MEMORY ( 1UL << 4UL ) + /** * task. h * @@ -100,7 +66,8 @@ extern "C" { * \defgroup TaskHandle_t TaskHandle_t * \ingroup Tasks */ -typedef void * TaskHandle_t; +struct tskTaskControlBlock; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tskTaskControlBlock* TaskHandle_t; /* * Defines the prototype to which the application task hook function must @@ -116,7 +83,7 @@ typedef enum eBlocked, /* The task being queried is in the Blocked state. */ eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */ - eInvalid /* Used as an 'invalid state' value. */ + eInvalid /* Used as an 'invalid state' value. */ } eTaskState; /* Actions that can be performed when vTaskNotify() is called. */ @@ -155,11 +122,14 @@ typedef struct xTASK_PARAMETERS { TaskFunction_t pvTaskCode; const char * const pcName; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - uint16_t usStackDepth; + configSTACK_DEPTH_TYPE usStackDepth; void *pvParameters; UBaseType_t uxPriority; StackType_t *puxStackBuffer; MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ]; + #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + StaticTask_t * const pxTaskBuffer; + #endif } TaskParameters_t; /* Used with the uxTaskGetSystemState() function to return the state of each task @@ -174,7 +144,7 @@ typedef struct xTASK_STATUS UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */ - uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ + configSTACK_DEPTH_TYPE usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ } TaskStatus_t; /* Possible return values for eTaskConfirmSleepModeStatus(). */ @@ -269,7 +239,7 @@ is used in assert() statements. */ BaseType_t xTaskCreate( TaskFunction_t pvTaskCode, const char * const pcName, - uint16_t usStackDepth, + configSTACK_DEPTH_TYPE usStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pvCreatedTask @@ -357,12 +327,12 @@ is used in assert() statements. */ * \ingroup Tasks */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - PRIVILEGED_FUNCTION BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, - const char * const pcName, - const uint16_t usStackDepth, + BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const configSTACK_DEPTH_TYPE usStackDepth, void * const pvParameters, UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; #endif /** @@ -414,9 +384,9 @@ is used in assert() statements. */ * memory to be allocated dynamically. * * @return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will - * be created and pdPASS is returned. If either pxStackBuffer or pxTaskBuffer - * are NULL then the task will not be created and - * errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned. + * be created and a handle to the created task is returned. If either + * pxStackBuffer or pxTaskBuffer are NULL then the task will not be created and + * NULL is returned. * * Example usage:
@@ -473,13 +443,13 @@ is used in assert() statements. */
  * \ingroup Tasks
  */
 #if( configSUPPORT_STATIC_ALLOCATION == 1 )
-	PRIVILEGED_FUNCTION TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,
-									const char * const pcName,
+	TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,
+									const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
 									const uint32_t ulStackDepth,
 									void * const pvParameters,
 									UBaseType_t uxPriority,
 									StackType_t * const puxStackBuffer,
-									StaticTask_t * const pxTaskBuffer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
+									StaticTask_t * const pxTaskBuffer ) PRIVILEGED_FUNCTION;
 #endif /* configSUPPORT_STATIC_ALLOCATION */
 
 /**
@@ -487,6 +457,8 @@ is used in assert() statements. */
  *
  BaseType_t xTaskCreateRestricted( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
* + * Only available when configSUPPORT_DYNAMIC_ALLOCATION is set to 1. + * * xTaskCreateRestricted() should only be used in systems that include an MPU * implementation. * @@ -494,6 +466,9 @@ is used in assert() statements. */ * The function parameters define the memory regions and associated access * permissions allocated to the task. * + * See xTaskCreateRestrictedStatic() for a version that does not use any + * dynamic memory allocation. + * * @param pxTaskDefinition Pointer to a structure that contains a member * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API * documentation) plus an optional stack buffer and the memory region @@ -550,7 +525,95 @@ TaskHandle_t xHandle; * \ingroup Tasks */ #if( portUSING_MPU_WRAPPERS == 1 ) - PRIVILEGED_FUNCTION BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ); + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + *
+ BaseType_t xTaskCreateRestrictedStatic( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
+ * + * Only available when configSUPPORT_STATIC_ALLOCATION is set to 1. + * + * xTaskCreateRestrictedStatic() should only be used in systems that include an + * MPU implementation. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreateRestricted() then the stack is provided by the application writer, + * and the memory used to hold the task's data structure is automatically + * dynamically allocated inside the xTaskCreateRestricted() function. If a task + * is created using xTaskCreateRestrictedStatic() then the application writer + * must provide the memory used to hold the task's data structures too. + * xTaskCreateRestrictedStatic() therefore allows a memory protected task to be + * created without using any dynamic memory allocation. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. If configSUPPORT_STATIC_ALLOCATION is set to 1 the structure + * contains an additional member, which is used to point to a variable of type + * StaticTask_t - which is then used to hold the task's data structure. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: +
+// Create an TaskParameters_t structure that defines the task to be created.
+// The StaticTask_t variable is only included in the structure when
+// configSUPPORT_STATIC_ALLOCATION is set to 1.  The PRIVILEGED_DATA macro can
+// be used to force the variable into the RTOS kernel's privileged data area.
+static PRIVILEGED_DATA StaticTask_t xTaskBuffer;
+static const TaskParameters_t xCheckTaskParameters =
+{
+	vATask,		// pvTaskCode - the function that implements the task.
+	"ATask",	// pcName - just a text name for the task to assist debugging.
+	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
+	NULL,		// pvParameters - passed into the task function as the function parameters.
+	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
+	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
+
+	// xRegions - Allocate up to three separate memory regions for access by
+	// the task, with appropriate access permissions.  Different processors have
+	// different memory alignment requirements - refer to the FreeRTOS documentation
+	// for full information.
+	{
+		// Base address					Length	Parameters
+        { cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
+        { cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
+        { cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
+	}
+
+	&xTaskBuffer; // Holds the task's data structure.
+};
+
+int main( void )
+{
+TaskHandle_t xHandle;
+
+	// Create a task from the const structure defined above.  The task handle
+	// is requested (the second parameter is not NULL) but in this case just for
+	// demonstration purposes as its not actually used.
+	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
+
+	// Start the scheduler.
+	vTaskStartScheduler();
+
+	// Will only get here if there was insufficient memory to create the idle
+	// and/or timer task.
+	for( ;; );
+}
+   
+ * \defgroup xTaskCreateRestrictedStatic xTaskCreateRestrictedStatic + * \ingroup Tasks + */ +#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; #endif /** @@ -599,7 +662,7 @@ void vATask( void *pvParameters ) * \defgroup xTaskCreateRestricted xTaskCreateRestricted * \ingroup Tasks */ -PRIVILEGED_FUNCTION void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ); +void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ) PRIVILEGED_FUNCTION; /** * task. h @@ -640,7 +703,7 @@ PRIVILEGED_FUNCTION void vTaskAllocateMPURegions( TaskHandle_t xTask, const Memo * \defgroup vTaskDelete vTaskDelete * \ingroup Tasks */ -PRIVILEGED_FUNCTION void vTaskDelete( TaskHandle_t xTaskToDelete ); +void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; /*----------------------------------------------------------- * TASK CONTROL API @@ -692,7 +755,7 @@ PRIVILEGED_FUNCTION void vTaskDelete( TaskHandle_t xTaskToDelete ); * \defgroup vTaskDelay vTaskDelay * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskDelay( const TickType_t xTicksToDelay ); +void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; /** * task. h @@ -751,7 +814,7 @@ PRIVILEGED_FUNCTION void vTaskDelay( const TickType_t xTicksToDelay ); * \defgroup vTaskDelayUntil vTaskDelayUntil * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ); +void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION; /** * task. h @@ -776,11 +839,11 @@ PRIVILEGED_FUNCTION void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, * \defgroup xTaskAbortDelay xTaskAbortDelay * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION BaseType_t xTaskAbortDelay( TaskHandle_t xTask ); +BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** * task. h - *
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask );
+ *
UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );
* * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be available. * See the configuration section for more information. @@ -823,15 +886,15 @@ PRIVILEGED_FUNCTION BaseType_t xTaskAbortDelay( TaskHandle_t xTask ); * \defgroup uxTaskPriorityGet uxTaskPriorityGet * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ); +UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** * task. h - *
UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask );
+ *
UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask );
* * A version of uxTaskPriorityGet() that can be used from an ISR. */ -PRIVILEGED_FUNCTION UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask ); +UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** * task. h @@ -849,7 +912,7 @@ PRIVILEGED_FUNCTION UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask ); * state of the task might change between the function being called, and the * functions return value being tested by the calling task. */ -PRIVILEGED_FUNCTION eTaskState eTaskGetState( TaskHandle_t xTask ); +eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** * task. h @@ -905,7 +968,7 @@ PRIVILEGED_FUNCTION eTaskState eTaskGetState( TaskHandle_t xTask ); * \defgroup vTaskGetInfo vTaskGetInfo * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ); +void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ) PRIVILEGED_FUNCTION; /** * task. h @@ -947,7 +1010,7 @@ PRIVILEGED_FUNCTION void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskS * \defgroup vTaskPrioritySet vTaskPrioritySet * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ); +void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION; /** * task. h @@ -998,7 +1061,7 @@ PRIVILEGED_FUNCTION void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNew * \defgroup vTaskSuspend vTaskSuspend * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskSuspend( TaskHandle_t xTaskToSuspend ); +void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; /** * task. h @@ -1047,7 +1110,7 @@ PRIVILEGED_FUNCTION void vTaskSuspend( TaskHandle_t xTaskToSuspend ); * \defgroup vTaskResume vTaskResume * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskResume( TaskHandle_t xTaskToResume ); +void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; /** * task. h @@ -1076,7 +1139,7 @@ PRIVILEGED_FUNCTION void vTaskResume( TaskHandle_t xTaskToResume ); * \defgroup vTaskResumeFromISR vTaskResumeFromISR * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ); +BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; /*----------------------------------------------------------- * SCHEDULER CONTROL @@ -1109,7 +1172,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ); * \defgroup vTaskStartScheduler vTaskStartScheduler * \ingroup SchedulerControl */ -PRIVILEGED_FUNCTION void vTaskStartScheduler( void ); +void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1165,7 +1228,7 @@ PRIVILEGED_FUNCTION void vTaskStartScheduler( void ); * \defgroup vTaskEndScheduler vTaskEndScheduler * \ingroup SchedulerControl */ -PRIVILEGED_FUNCTION void vTaskEndScheduler( void ); +void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1216,7 +1279,7 @@ PRIVILEGED_FUNCTION void vTaskEndScheduler( void ); * \defgroup vTaskSuspendAll vTaskSuspendAll * \ingroup SchedulerControl */ -PRIVILEGED_FUNCTION void vTaskSuspendAll( void ); +void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1270,7 +1333,7 @@ PRIVILEGED_FUNCTION void vTaskSuspendAll( void ); * \defgroup xTaskResumeAll xTaskResumeAll * \ingroup SchedulerControl */ -PRIVILEGED_FUNCTION BaseType_t xTaskResumeAll( void ); +BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; /*----------------------------------------------------------- * TASK UTILITIES @@ -1285,7 +1348,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskResumeAll( void ); * \defgroup xTaskGetTickCount xTaskGetTickCount * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION TickType_t xTaskGetTickCount( void ); +TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1301,7 +1364,7 @@ PRIVILEGED_FUNCTION TickType_t xTaskGetTickCount( void ); * \defgroup xTaskGetTickCountFromISR xTaskGetTickCountFromISR * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION TickType_t xTaskGetTickCountFromISR( void ); +TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1315,7 +1378,7 @@ PRIVILEGED_FUNCTION TickType_t xTaskGetTickCountFromISR( void ); * \defgroup uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION UBaseType_t uxTaskGetNumberOfTasks( void ); +UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1328,7 +1391,7 @@ PRIVILEGED_FUNCTION UBaseType_t uxTaskGetNumberOfTasks( void ); * \defgroup pcTaskGetName pcTaskGetName * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION char *pcTaskGetName( TaskHandle_t xTaskToQuery ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +char *pcTaskGetName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** * task. h @@ -1344,7 +1407,7 @@ PRIVILEGED_FUNCTION char *pcTaskGetName( TaskHandle_t xTaskToQuery ); /*lint !e9 * \defgroup pcTaskGetHandle pcTaskGetHandle * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** * task.h @@ -1358,6 +1421,39 @@ PRIVILEGED_FUNCTION TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ); /* * a value of 1 means 4 bytes) since the task started. The smaller the returned * number the closer the task has come to overflowing its stack. * + * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask );
+ * + * INCLUDE_uxTaskGetStackHighWaterMark2 must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * * @param xTask Handle of the task associated with the stack to be checked. * Set xTask to NULL to check the stack of the calling task. * @@ -1365,7 +1461,7 @@ PRIVILEGED_FUNCTION TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ); /* * actual spaces on the stack rather than bytes) since the task referenced by * xTask was created. */ -PRIVILEGED_FUNCTION UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ); +configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /* When using trace macros it is sometimes necessary to include task.h before FreeRTOS.h. When this is done TaskHookFunction_t will not yet have been defined, @@ -1383,15 +1479,26 @@ constant. */ * Passing xTask as NULL has the effect of setting the calling tasks hook * function. */ - PRIVILEGED_FUNCTION void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ); + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) PRIVILEGED_FUNCTION; /** * task.h *
void xTaskGetApplicationTaskTag( TaskHandle_t xTask );
* - * Returns the pxHookFunction value assigned to the task xTask. + * Returns the pxHookFunction value assigned to the task xTask. Do not + * call from an interrupt service routine - call + * xTaskGetApplicationTaskTagFromISR() instead. + */ + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + + /** + * task.h + *
void xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask );
+ * + * Returns the pxHookFunction value assigned to the task xTask. Can + * be called from an interrupt service routine. */ - PRIVILEGED_FUNCTION TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ); + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ #endif /* ifdef configUSE_APPLICATION_TASK_TAG */ @@ -1402,8 +1509,8 @@ constant. */ kernel does not use the pointers itself, so the application writer can use the pointers for any purpose they wish. The following two functions are used to set and query a pointer respectively. */ - PRIVILEGED_FUNCTION void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ); - PRIVILEGED_FUNCTION void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ); + void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) PRIVILEGED_FUNCTION; + void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) PRIVILEGED_FUNCTION; #endif @@ -1418,7 +1525,7 @@ constant. */ * wants. The return value is the value returned by the task hook function * registered by the user. */ -PRIVILEGED_FUNCTION BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ); +BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) PRIVILEGED_FUNCTION; /** * xTaskGetIdleTaskHandle() is only available if @@ -1427,7 +1534,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, * Simply returns the handle of the idle task. It is not valid to call * xTaskGetIdleTaskHandle() before the scheduler has been started. */ -PRIVILEGED_FUNCTION TaskHandle_t xTaskGetIdleTaskHandle( void ); +TaskHandle_t xTaskGetIdleTaskHandle( void ) PRIVILEGED_FUNCTION; /** * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for @@ -1526,7 +1633,7 @@ PRIVILEGED_FUNCTION TaskHandle_t xTaskGetIdleTaskHandle( void ); }
*/ -PRIVILEGED_FUNCTION UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ); +UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) PRIVILEGED_FUNCTION; /** * task. h @@ -1573,7 +1680,7 @@ PRIVILEGED_FUNCTION UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTas * \defgroup vTaskList vTaskList * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION void vTaskList( char * pcWriteBuffer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** * task. h @@ -1627,7 +1734,37 @@ PRIVILEGED_FUNCTION void vTaskList( char * pcWriteBuffer ); /*lint !e971 Unquali * \defgroup vTaskGetRunTimeStats vTaskGetRunTimeStats * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION void vTaskGetRunTimeStats( char *pcWriteBuffer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** +* task. h +*
TickType_t xTaskGetIdleRunTimeCounter( void );
+* +* configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS +* must both be defined as 1 for this function to be available. The application +* must also then provide definitions for +* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() +* to configure a peripheral timer/counter and return the timers current count +* value respectively. The counter should be at least 10 times the frequency of +* the tick count. +* +* Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total +* accumulated execution time being stored for each task. The resolution +* of the accumulated time value depends on the frequency of the timer +* configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. +* While uxTaskGetSystemState() and vTaskGetRunTimeStats() writes the total +* execution time of each task into a buffer, xTaskGetIdleRunTimeCounter() +* returns the total execution time of just the idle task. +* +* @return The total run time of the idle task. This is the amount of time the +* idle task has actually been executing. The unit of time is dependent on the +* frequency configured using the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and +* portGET_RUN_TIME_COUNTER_VALUE() macros. +* +* \defgroup xTaskGetIdleRunTimeCounter xTaskGetIdleRunTimeCounter +* \ingroup TaskUtils +*/ +TickType_t xTaskGetIdleRunTimeCounter( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1708,7 +1845,7 @@ PRIVILEGED_FUNCTION void vTaskGetRunTimeStats( char *pcWriteBuffer ); /*lint !e9 * \defgroup xTaskNotify xTaskNotify * \ingroup TaskNotifications */ -PRIVILEGED_FUNCTION BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ); +BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) PRIVILEGED_FUNCTION; #define xTaskNotify( xTaskToNotify, ulValue, eAction ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL ) #define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) ) @@ -1799,7 +1936,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, u * \defgroup xTaskNotify xTaskNotify * \ingroup TaskNotifications */ -PRIVILEGED_FUNCTION BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ); +BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; #define xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) ) #define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) ) @@ -1876,7 +2013,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNo * \defgroup xTaskNotifyWait xTaskNotifyWait * \ingroup TaskNotifications */ -PRIVILEGED_FUNCTION BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ); +BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * task. h @@ -1977,7 +2114,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, u * \defgroup xTaskNotifyWait xTaskNotifyWait * \ingroup TaskNotifications */ -PRIVILEGED_FUNCTION void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken ); +void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /** * task. h @@ -2046,7 +2183,7 @@ PRIVILEGED_FUNCTION void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, Bas * \defgroup ulTaskNotifyTake ulTaskNotifyTake * \ingroup TaskNotifications */ -PRIVILEGED_FUNCTION uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); +uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * task. h @@ -2083,7 +2220,7 @@ BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ); * + Time slicing is in use and there is a task of equal priority to the * currently running task. */ -PRIVILEGED_FUNCTION BaseType_t xTaskIncrementTick( void ); +BaseType_t xTaskIncrementTick( void ) PRIVILEGED_FUNCTION; /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN @@ -2116,8 +2253,8 @@ PRIVILEGED_FUNCTION BaseType_t xTaskIncrementTick( void ); * portTICK_PERIOD_MS can be used to convert kernel ticks into a real time * period. */ -PRIVILEGED_FUNCTION void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ); -PRIVILEGED_FUNCTION void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ); +void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN @@ -2130,7 +2267,7 @@ PRIVILEGED_FUNCTION void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, c * indefinitely, whereas vTaskPlaceOnEventList() does. * */ -PRIVILEGED_FUNCTION void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ); +void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN @@ -2141,14 +2278,14 @@ PRIVILEGED_FUNCTION void vTaskPlaceOnEventListRestricted( List_t * const pxEvent * Removes a task from both the specified event list and the list of blocked * tasks, and places it on a ready queue. * - * xTaskRemoveFromEventList()/xTaskRemoveFromUnorderedEventList() will be called + * xTaskRemoveFromEventList()/vTaskRemoveFromUnorderedEventList() will be called * if either an event occurs to unblock a task, or the block timeout period * expires. * * xTaskRemoveFromEventList() is used when the event list is in task priority * order. It removes the list item from the head of the event list as that will * have the highest priority owning task of all the tasks on the event list. - * xTaskRemoveFromUnorderedEventList() is used when the event list is not + * vTaskRemoveFromUnorderedEventList() is used when the event list is not * ordered and the event list items hold something other than the owning tasks * priority. In this case the event list item value is updated to the value * passed in the xItemValue parameter. @@ -2156,8 +2293,8 @@ PRIVILEGED_FUNCTION void vTaskPlaceOnEventListRestricted( List_t * const pxEvent * @return pdTRUE if the task being removed has a higher priority than the task * making the call, otherwise pdFALSE. */ -PRIVILEGED_FUNCTION BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ); -PRIVILEGED_FUNCTION BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ); +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) PRIVILEGED_FUNCTION; +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) PRIVILEGED_FUNCTION; /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY @@ -2167,64 +2304,74 @@ PRIVILEGED_FUNCTION BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * p * Sets the pointer to the current TCB to the TCB of the highest priority task * that is ready to run. */ -PRIVILEGED_FUNCTION void vTaskSwitchContext( void ); +portDONT_DISCARD void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION; /* * THESE FUNCTIONS MUST NOT BE USED FROM APPLICATION CODE. THEY ARE USED BY * THE EVENT BITS MODULE. */ -PRIVILEGED_FUNCTION TickType_t uxTaskResetEventItemValue( void ); +TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION; /* * Return the handle of the calling task. */ -PRIVILEGED_FUNCTION TaskHandle_t xTaskGetCurrentTaskHandle( void ); +TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; /* * Capture the current time status for future reference. */ -PRIVILEGED_FUNCTION void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ); +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; /* * Compare the time status now with that previously captured to see if the * timeout has expired. */ -PRIVILEGED_FUNCTION BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ); +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) PRIVILEGED_FUNCTION; /* * Shortcut used by the queue implementation to prevent unnecessary call to * taskYIELD(); */ -PRIVILEGED_FUNCTION void vTaskMissedYield( void ); +void vTaskMissedYield( void ) PRIVILEGED_FUNCTION; /* * Returns the scheduler state as taskSCHEDULER_RUNNING, * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. */ -PRIVILEGED_FUNCTION BaseType_t xTaskGetSchedulerState( void ); +BaseType_t xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION; /* * Raises the priority of the mutex holder to that of the calling task should * the mutex holder have a priority less than the calling task. */ -PRIVILEGED_FUNCTION void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ); +BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; /* * Set the priority of a task back to its proper priority in the case that it * inherited a higher priority while it was holding a semaphore. */ -PRIVILEGED_FUNCTION BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ); +BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * If a higher priority task attempting to obtain a mutex caused a lower + * priority task to inherit the higher priority task's priority - but the higher + * priority task then timed out without obtaining the mutex, then the lower + * priority task will disinherit the priority again - but only down as far as + * the highest priority task that is still waiting for the mutex (if there were + * more than one task waiting for the mutex). + */ +void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask ) PRIVILEGED_FUNCTION; /* * Get the uxTCBNumber assigned to the task referenced by the xTask parameter. */ -PRIVILEGED_FUNCTION UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ); +UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /* * Set the uxTaskNumber of the task referenced by the xTask parameter to * uxHandle. */ -PRIVILEGED_FUNCTION void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ); +void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) PRIVILEGED_FUNCTION; /* * Only available when configUSE_TICKLESS_IDLE is set to 1. @@ -2234,10 +2381,10 @@ PRIVILEGED_FUNCTION void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType * to date with the actual execution time by being skipped forward by a time * equal to the idle period. */ -PRIVILEGED_FUNCTION void vTaskStepTick( const TickType_t xTicksToJump ); +void vTaskStepTick( const TickType_t xTicksToJump ) PRIVILEGED_FUNCTION; /* - * Only avilable when configUSE_TICKLESS_IDLE is set to 1. + * Only available when configUSE_TICKLESS_IDLE is set to 1. * Provided for use within portSUPPRESS_TICKS_AND_SLEEP() to allow the port * specific sleep function to determine if it is ok to proceed with the sleep, * and if it is ok to proceed, if it is ok to sleep indefinitely. @@ -2250,13 +2397,20 @@ PRIVILEGED_FUNCTION void vTaskStepTick( const TickType_t xTicksToJump ); * critical section between the timer being stopped and the sleep mode being * entered to ensure it is ok to proceed into the sleep mode. */ -PRIVILEGED_FUNCTION eSleepModeStatus eTaskConfirmSleepModeStatus( void ); +eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; /* * For internal use only. Increment the mutex held count when a mutex is * taken and return the handle of the task that has taken the mutex. */ -PRIVILEGED_FUNCTION void *pvTaskIncrementMutexHeldCount( void ); +TaskHandle_t pvTaskIncrementMutexHeldCount( void ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Same as vTaskSetTimeOutState(), but without a critial + * section. + */ +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + #ifdef __cplusplus } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h b/Firmware/ThirdParty/FreeRTOS/Source/include/timers.h similarity index 90% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/timers.h index 2f430f373..cb721797f 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/timers.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef TIMERS_H @@ -75,10 +33,10 @@ #error "include FreeRTOS.h must appear in source files before include timers.h" #endif -/*lint -e537 This headers are only multiply included if the application code +/*lint -save -e537 This headers are only multiply included if the application code happens to also be including task.h. */ #include "task.h" -/*lint +e537 */ +/*lint -restore */ #ifdef __cplusplus extern "C" { @@ -115,7 +73,8 @@ or interrupt version of the queue send function should be used. */ * reference the subject timer in calls to other software timer API functions * (for example, xTimerStart(), xTimerReset(), etc.). */ -typedef void * TimerHandle_t; +struct tmrTimerControl; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tmrTimerControl * TimerHandle_t; /* * Defines the prototype to which timer callback functions must conform. @@ -266,11 +225,11 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * @endverbatim */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - PRIVILEGED_FUNCTION TimerHandle_t xTimerCreate( const char * const pcTimerName, + TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; #endif /** @@ -396,12 +355,12 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * @endverbatim */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - PRIVILEGED_FUNCTION TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, - StaticTimer_t *pxTimerBuffer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; #endif /* configSUPPORT_STATIC_ALLOCATION */ /** @@ -424,7 +383,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * * See the xTimerCreate() API function example usage scenario. */ -PRIVILEGED_FUNCTION void *pvTimerGetTimerID( const TimerHandle_t xTimer ); +void *pvTimerGetTimerID( const TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** * void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); @@ -445,7 +404,7 @@ PRIVILEGED_FUNCTION void *pvTimerGetTimerID( const TimerHandle_t xTimer ); * * See the xTimerCreate() API function example usage scenario. */ -PRIVILEGED_FUNCTION void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); +void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION; /** * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); @@ -482,7 +441,7 @@ PRIVILEGED_FUNCTION void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) * } * @endverbatim */ -PRIVILEGED_FUNCTION BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); +BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** * TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); @@ -490,7 +449,7 @@ PRIVILEGED_FUNCTION BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); * Simply returns the handle of the timer service/daemon task. It it not valid * to call xTimerGetTimerDaemonTaskHandle() before the scheduler has been started. */ -PRIVILEGED_FUNCTION TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); +TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) PRIVILEGED_FUNCTION; /** * BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); @@ -1225,7 +1184,7 @@ PRIVILEGED_FUNCTION TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); * } * @endverbatim */ -PRIVILEGED_FUNCTION BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ); +BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /** * BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, @@ -1259,7 +1218,7 @@ PRIVILEGED_FUNCTION BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t x * timer daemon task, otherwise pdFALSE is returned. * */ -PRIVILEGED_FUNCTION BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ); +BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * const char * const pcTimerGetName( TimerHandle_t xTimer ); @@ -1270,7 +1229,24 @@ PRIVILEGED_FUNCTION BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctio * * @return The name assigned to the timer specified by the xTimer parameter. */ -PRIVILEGED_FUNCTION const char * pcTimerGetName( TimerHandle_t xTimer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +const char * pcTimerGetName( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ); + * + * Updates a timer to be either an autoreload timer, in which case the timer + * automatically resets itself each time it expires, or a one shot timer, in + * which case the timer will only expire once unless it is manually restarted. + * + * @param xTimer The handle of the timer being updated. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the timer's period (see the + * xTimerPeriodInTicks parameter of the xTimerCreate() API function). If + * uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + */ +void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) PRIVILEGED_FUNCTION; /** * TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); @@ -1281,7 +1257,7 @@ PRIVILEGED_FUNCTION const char * pcTimerGetName( TimerHandle_t xTimer ); /*lint * * @return The period of the timer in ticks. */ -PRIVILEGED_FUNCTION TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); +TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** * TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ); @@ -1296,14 +1272,19 @@ PRIVILEGED_FUNCTION TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); * will next expire is returned. If the timer is not running then the return * value is undefined. */ -PRIVILEGED_FUNCTION TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ); +TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /* * Functions beyond this part are not part of the public API and are intended * for use by the kernel only. */ -PRIVILEGED_FUNCTION BaseType_t xTimerCreateTimerTask( void ); -PRIVILEGED_FUNCTION BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ); +BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION; +BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +#if( configUSE_TRACE_FACILITY == 1 ) + void vTimerSetTimerNumber( TimerHandle_t xTimer, UBaseType_t uxTimerNumber ) PRIVILEGED_FUNCTION; + UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; +#endif #ifdef __cplusplus } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/list.c b/Firmware/ThirdParty/FreeRTOS/Source/list.c similarity index 54% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/list.c rename to Firmware/ThirdParty/FreeRTOS/Source/list.c index 5e207c160..21dabdecd 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/list.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/list.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #include @@ -81,7 +39,7 @@ void vListInitialise( List_t * const pxList ) /* The list structure contains a list item which is used to mark the end of the list. To initialise the list the list end is inserted as the only list entry. */ - pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ /* The list end value is the highest possible value in the list to ensure it remains at the end of the list. */ @@ -89,8 +47,8 @@ void vListInitialise( List_t * const pxList ) /* The list end next and previous pointers point to itself so we know when the list is empty. */ - pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ - pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ pxList->uxNumberOfItems = ( UBaseType_t ) 0U; @@ -104,7 +62,7 @@ void vListInitialise( List_t * const pxList ) void vListInitialiseItem( ListItem_t * const pxItem ) { /* Make sure the list item is not recorded as being on a list. */ - pxItem->pvContainer = NULL; + pxItem->pxContainer = NULL; /* Write known values into the list item if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ @@ -136,7 +94,7 @@ ListItem_t * const pxIndex = pxList->pxIndex; pxIndex->pxPrevious = pxNewListItem; /* Remember which list the item is in. */ - pxNewListItem->pvContainer = ( void * ) pxList; + pxNewListItem->pxContainer = pxList; ( pxList->uxNumberOfItems )++; } @@ -156,7 +114,7 @@ const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; /* Insert the new list item into the list, sorted in xItemValue order. If the list already contains a list item with the same item value then the - new list item should be placed after it. This ensures that TCB's which are + new list item should be placed after it. This ensures that TCBs which are stored in ready lists (all of which have the same xItemValue value) get a share of the CPU. However, if the xItemValue is the same as the back marker the iteration loop below will not end. Therefore the value is checked @@ -169,18 +127,18 @@ const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; { /* *** NOTE *********************************************************** If you find your application is crashing here then likely causes are - listed below. In addition see http://www.freertos.org/FAQHelp.html for + listed below. In addition see https://www.freertos.org/FAQHelp.html for more tips, and ensure configASSERT() is defined! - http://www.freertos.org/a00110.html#configASSERT + https://www.freertos.org/a00110.html#configASSERT 1) Stack overflow - - see http://www.freertos.org/Stacks-and-stack-overflow-checking.html + see https://www.freertos.org/Stacks-and-stack-overflow-checking.html 2) Incorrect interrupt priority assignment, especially on Cortex-M parts where numerically high priority values denote low actual interrupt priorities, which can seem counter intuitive. See - http://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition + https://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition of configMAX_SYSCALL_INTERRUPT_PRIORITY on - http://www.freertos.org/a00110.html + https://www.freertos.org/a00110.html 3) Calling an API function from within a critical section or when the scheduler is suspended, or calling an API function that does not end in "FromISR" from an interrupt. @@ -189,7 +147,7 @@ const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; before vTaskStartScheduler() has been called?). **********************************************************************/ - for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */ { /* There is nothing to do here, just iterating to the wanted insertion position. */ @@ -203,7 +161,7 @@ const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; /* Remember which list the item is in. This allows fast removal of the item later. */ - pxNewListItem->pvContainer = ( void * ) pxList; + pxNewListItem->pxContainer = pxList; ( pxList->uxNumberOfItems )++; } @@ -213,7 +171,7 @@ UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) { /* The list item knows which list it is in. Obtain the list from the list item. */ -List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer; +List_t * const pxList = pxItemToRemove->pxContainer; pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; @@ -231,7 +189,7 @@ List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer; mtCOVERAGE_TEST_MARKER(); } - pxItemToRemove->pvContainer = NULL; + pxItemToRemove->pxContainer = NULL; ( pxList->uxNumberOfItems )--; return pxList->uxNumberOfItems; diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c similarity index 79% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c rename to Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c index d5feca9e5..e2282f40f 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /*----------------------------------------------------------- * Implementation of functions defined in portable.h for the ARM CM4F port. @@ -129,7 +87,7 @@ r0p1 port. */ /* Constants required to set up the initial stack. */ #define portINITIAL_XPSR ( 0x01000000 ) -#define portINITIAL_EXEC_RETURN ( 0xfffffffd ) +#define portINITIAL_EXC_RETURN ( 0xfffffffd ) /* The systick is a 24-bit counter. */ #define portMAX_24_BIT_NUMBER ( 0xffffffUL ) @@ -152,10 +110,6 @@ debugger. */ #define portTASK_RETURN_ADDRESS prvTaskExitError #endif -/* Each task maintains its own interrupt status in the critical nesting -variable. */ -static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; - /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -187,10 +141,14 @@ static void prvTaskExitError( void ); /*-----------------------------------------------------------*/ +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + /* * The number of SysTick increments that make up one tick period. */ -#if configUSE_TICKLESS_IDLE == 1 +#if( configUSE_TICKLESS_IDLE == 1 ) static uint32_t ulTimerCountsForOneTick = 0; #endif /* configUSE_TICKLESS_IDLE */ @@ -198,7 +156,7 @@ static void prvTaskExitError( void ); * The maximum number of tick periods that can be suppressed is limited by the * 24 bit resolution of the SysTick timer. */ -#if configUSE_TICKLESS_IDLE == 1 +#if( configUSE_TICKLESS_IDLE == 1 ) static uint32_t xMaximumPossibleSuppressedTicks = 0; #endif /* configUSE_TICKLESS_IDLE */ @@ -206,7 +164,7 @@ static void prvTaskExitError( void ); * Compensate for the CPU cycles that pass while the SysTick is stopped (low * power functionality only. */ -#if configUSE_TICKLESS_IDLE == 1 +#if( configUSE_TICKLESS_IDLE == 1 ) static uint32_t ulStoppedTimerCompensation = 0; #endif /* configUSE_TICKLESS_IDLE */ @@ -215,7 +173,7 @@ static void prvTaskExitError( void ); * FreeRTOS API functions are not called from interrupts that have been assigned * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. */ -#if ( configASSERT_DEFINED == 1 ) +#if( configASSERT_DEFINED == 1 ) static uint8_t ucMaxSysCallPriority = 0; static uint32_t ulMaxPRIGROUPValue = 0; static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16; @@ -248,7 +206,7 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px /* A save method is being used that requires each task to maintain its own exec return value. */ pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXEC_RETURN; + *pxTopOfStack = portINITIAL_EXC_RETURN; pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ @@ -258,6 +216,8 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px static void prvTaskExitError( void ) { +volatile uint32_t ulDummy = 0; + /* A function that implements a task must not exit or attempt to return to its caller as there is nothing to return to. If a task wants to exit it should instead call vTaskDelete( NULL ). @@ -266,7 +226,16 @@ static void prvTaskExitError( void ) defined, then stop here so application writers can catch the error. */ configASSERT( uxCriticalNesting == ~0UL ); portDISABLE_INTERRUPTS(); - for( ;; ); + while( ulDummy == 0 ) + { + /* This file calls prvTaskExitError() after the scheduler has been + started to remove a compiler warning about the function being defined + but never called. ulDummy is used purely to quieten other warnings + about code appearing after this function is called - making ulDummy + volatile makes the compiler think the function could return and + therefore not output an 'unreachable code' warning for code that appears + after it. */ + } } /*-----------------------------------------------------------*/ @@ -291,17 +260,24 @@ void vPortSVCHandler( void ) static void prvPortStartFirstTask( void ) { + /* Start the first task. This also clears the bit that indicates the FPU is + in use in case the FPU was used before the scheduler was started - which + would otherwise result in the unnecessary leaving of space in the SVC stack + for lazy saving of FPU registers. */ __asm volatile( " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */ " ldr r0, [r0] \n" " ldr r0, [r0] \n" " msr msp, r0 \n" /* Set the msp back to the start of the stack. */ + " mov r0, #0 \n" /* Clear the bit that indicates the FPU is in use, see comment above. */ + " msr control, r0 \n" " cpsie i \n" /* Globally enable interrupts. */ " cpsie f \n" " dsb \n" " isb \n" " svc 0 \n" /* System call to start first task. */ " nop \n" + " .ltorg \n" ); } /*-----------------------------------------------------------*/ @@ -354,6 +330,24 @@ BaseType_t xPortStartScheduler( void ) ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + priority bits matches the number of priority bits actually queried + from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + priority bits matches the number of priority bits actually queried + from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + /* Shift the priority group value back to its position within the AIRCR register. */ ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; @@ -388,7 +382,10 @@ BaseType_t xPortStartScheduler( void ) /* Should never get here as the tasks will now be executing! Call the task exit error function to prevent compiler warnings about a static function not being called in the case that the application writer overrides this - functionality by defining configTASK_RETURN_ADDRESS. */ + functionality by defining configTASK_RETURN_ADDRESS. Call + vTaskSwitchContext() so link time optimisation does not remove the + symbol. */ + vTaskSwitchContext(); prvTaskExitError(); /* Should not get here! */ @@ -449,10 +446,9 @@ void xPortPendSVHandler( void ) " vstmdbeq r0!, {s16-s31} \n" " \n" " stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */ - " \n" " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */ " \n" - " stmdb sp!, {r3} \n" + " stmdb sp!, {r0, r3} \n" " mov r0, %0 \n" " msr basepri, r0 \n" " dsb \n" @@ -460,7 +456,7 @@ void xPortPendSVHandler( void ) " bl vTaskSwitchContext \n" " mov r0, #0 \n" " msr basepri, r0 \n" - " ldmia sp!, {r3} \n" + " ldmia sp!, {r0, r3} \n" " \n" " ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */ " ldr r0, [r1] \n" @@ -510,11 +506,11 @@ void xPortSysTickHandler( void ) } /*-----------------------------------------------------------*/ -#if configUSE_TICKLESS_IDLE == 1 +#if( configUSE_TICKLESS_IDLE == 1 ) __attribute__((weak)) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -540,7 +536,7 @@ void xPortSysTickHandler( void ) /* Enter a critical section but don't use the taskENTER_CRITICAL() method as that will mask interrupts that should exit sleep mode. */ - __asm volatile( "cpsid i" ); + __asm volatile( "cpsid i" ::: "memory" ); __asm volatile( "dsb" ); __asm volatile( "isb" ); @@ -561,7 +557,7 @@ void xPortSysTickHandler( void ) /* Re-enable interrupts - see comments above the cpsid instruction() above. */ - __asm volatile( "cpsie i" ); + __asm volatile( "cpsie i" ::: "memory" ); } else { @@ -581,32 +577,50 @@ void xPortSysTickHandler( void ) should not be executed again. However, the original expected idle time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; - configPRE_SLEEP_PROCESSING( &xModifiableIdleTime ); + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); if( xModifiableIdleTime > 0 ) { - __asm volatile( "dsb" ); + __asm volatile( "dsb" ::: "memory" ); __asm volatile( "wfi" ); __asm volatile( "isb" ); } - configPOST_SLEEP_PROCESSING( &xExpectedIdleTime ); - - /* Stop SysTick. Again, the time the SysTick is stopped for is - accounted for as best it can be, but using the tickless mode will - inevitably result in some tiny drift of the time maintained by the - kernel with respect to calendar time. */ - ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; - portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); - - /* Re-enable interrupts - see comments above the cpsid instruction() - above. */ - __asm volatile( "cpsie i" ); - - if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); + + /* Re-enable interrupts to allow the interrupt that brought the MCU + out of sleep mode to execute immediately. see comments above + __disable_interrupt() call above. */ + __asm volatile( "cpsie i" ::: "memory" ); + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + /* Disable interrupts again because the clock is about to be stopped + and interrupts that execute while the clock is stopped will increase + any slippage between the time maintained by the RTOS and calendar + time. */ + __asm volatile( "cpsid i" ::: "memory" ); + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + /* Disable the SysTick clock without reading the + portNVIC_SYSTICK_CTRL_REG register to ensure the + portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. Again, + the time the SysTick is stopped for is accounted for as best it can + be, but using the tickless mode will inevitably result in some tiny + drift of the time maintained by the kernel with respect to calendar + time*/ + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT ); + + /* Determine if the SysTick clock has already counted to zero and + been set back to the current reload value (the reload back being + correct for the entire expected idle time) or if the SysTick is yet + to count to zero (in which case an interrupt other than the SysTick + must have brought the system out of sleep mode). */ + if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt has already executed, and the SysTick - count reloaded with ulReloadValue. Reset the + /* The tick interrupt is already pending, and the SysTick count + reloaded with ulReloadValue. Reset the portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); @@ -621,11 +635,9 @@ void xPortSysTickHandler( void ) portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; - /* The tick interrupt handler will already have pended the tick - processing in the kernel. As the pending tick will be - processed as soon as this function exits, the tick value - maintained by the tick is stepped forward by one less than the - time spent waiting. */ + /* As the pending tick will be processed as soon as this + function exits, the tick value maintained by the tick is stepped + forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else @@ -647,17 +659,14 @@ void xPortSysTickHandler( void ) /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, then set portNVIC_SYSTICK_LOAD_REG back to its standard - value. The critical section is used to ensure the tick interrupt - can only execute once in the case that the reload register is near - zero. */ + value. */ portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; - portENTER_CRITICAL(); - { - portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; - } - portEXIT_CRITICAL(); + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); } } @@ -671,7 +680,7 @@ void xPortSysTickHandler( void ) __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ - #if configUSE_TICKLESS_IDLE == 1 + #if( configUSE_TICKLESS_IDLE == 1 ) { ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; @@ -679,6 +688,10 @@ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) } #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and clear the SysTick. */ + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + /* Configure SysTick to interrupt at the requested rate. */ portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); @@ -695,7 +708,8 @@ static void vPortEnableVFP( void ) " \n" " orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */ " str r1, [r0] \n" - " bx r14 " + " bx r14 \n" + " .ltorg " ); } /*-----------------------------------------------------------*/ @@ -708,7 +722,7 @@ static void vPortEnableVFP( void ) uint8_t ucCurrentPriority; /* Obtain the number of the currently executing interrupt. */ - __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) ); + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" ); /* Is the interrupt number a user defined interrupt? */ if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) @@ -754,7 +768,7 @@ static void vPortEnableVFP( void ) devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the scheduler. Note however that some vendor specific peripheral libraries assume a non-zero priority group setting, in which cases using a value - of zero will result in unpredicable behaviour. */ + of zero will result in unpredictable behaviour. */ configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h similarity index 62% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h rename to Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h index d44fc9222..d0a566a7c 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef PORTMACRO_H @@ -125,7 +83,7 @@ typedef unsigned long UBaseType_t; \ /* Barriers are normally not required but do ensure the code is completely \ within the specified behaviour for the architecture. */ \ - __asm volatile( "dsb" ); \ + __asm volatile( "dsb" ::: "memory" ); \ __asm volatile( "isb" ); \ } @@ -173,7 +131,7 @@ not necessary for to use this port. They are defined so the common demo files { uint8_t ucReturn; - __asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) ); + __asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) : "memory" ); return ucReturn; } @@ -214,7 +172,7 @@ uint32_t ulCurrentInterrupt; BaseType_t xReturn; /* Obtain the number of the currently executing interrupt. */ - __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) ); + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" ); if( ulCurrentInterrupt == 0 ) { @@ -240,7 +198,7 @@ uint32_t ulNewBASEPRI; " msr basepri, %0 \n" \ " isb \n" \ " dsb \n" \ - :"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) + :"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" ); } @@ -257,7 +215,7 @@ uint32_t ulOriginalBASEPRI, ulNewBASEPRI; " msr basepri, %1 \n" \ " isb \n" \ " dsb \n" \ - :"=r" (ulOriginalBASEPRI), "=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) + :"=r" (ulOriginalBASEPRI), "=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" ); /* This return will not be reached but is necessary to prevent compiler @@ -270,11 +228,12 @@ portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue ) { __asm volatile ( - " msr basepri, %0 " :: "r" ( ulNewMaskValue ) + " msr basepri, %0 " :: "r" ( ulNewMaskValue ) : "memory" ); } /*-----------------------------------------------------------*/ +#define portMEMORY_BARRIER() __asm volatile( "" ::: "memory" ) #ifdef __cplusplus } diff --git a/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/port.c b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/port.c new file mode 100644 index 000000000..ce867ee67 --- /dev/null +++ b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/port.c @@ -0,0 +1,765 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the ARM CM4F port. + *----------------------------------------------------------*/ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#ifndef __VFP_FP__ + #error This port can only be used when the project options are configured to enable hardware floating point support. +#endif + +#ifndef configSYSTICK_CLOCK_HZ + #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ + /* Ensure the SysTick is clocked at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) +#else + /* The way the SysTick is clocked is not modified in case it is not the same + as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 0 ) +#endif + +/* Constants required to manipulate the core. Registers first... */ +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +/* ...then bits in the registers. */ +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) + +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to check the validity of an interrupt priority. */ +#define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) +#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) +#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) ) +#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff ) +#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 ) +#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 ) +#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) +#define portPRIGROUP_SHIFT ( 8UL ) + +/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */ +#define portVECTACTIVE_MASK ( 0xFFUL ) + +/* Constants required to manipulate the VFP. */ +#define portFPCCR ( ( volatile uint32_t * ) 0xe000ef34 ) /* Floating point context control register. */ +#define portASPEN_AND_LSPEN_BITS ( 0x3UL << 30UL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) +#define portINITIAL_EXC_RETURN ( 0xfffffffd ) + +/* The systick is a 24-bit counter. */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/* For strict compliance with the Cortex-M spec the task start address should +have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ +#define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) + +/* A fiddle factor to estimate the number of SysTick counts that would have +occurred while the SysTick counter is stopped during tickless idle +calculations. */ +#define portMISSED_COUNTS_FACTOR ( 45UL ) + +/* Let the user override the pre-loading of the initial LR with the address of +prvTaskExitError() in case it messes up unwinding of the stack in the +debugger. */ +#ifdef configTASK_RETURN_ADDRESS + #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS +#else + #define portTASK_RETURN_ADDRESS prvTaskExitError +#endif + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortPendSVHandler( void ) __attribute__ (( naked )); +void xPortSysTickHandler( void ); +void vPortSVCHandler( void ) __attribute__ (( naked )); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +static void prvPortStartFirstTask( void ) __attribute__ (( naked )); + +/* + * Function to enable the VFP. + */ +static void vPortEnableVFP( void ) __attribute__ (( naked )); + +/* + * Used to catch tasks that attempt to return from their implementing function. + */ +static void prvTaskExitError( void ); + +/*-----------------------------------------------------------*/ + +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * The number of SysTick increments that make up one tick period. + */ +#if( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t ulTimerCountsForOneTick = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * The maximum number of tick periods that can be suppressed is limited by the + * 24 bit resolution of the SysTick timer. + */ +#if( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t xMaximumPossibleSuppressedTicks = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Compensate for the CPU cycles that pass while the SysTick is stopped (low + * power functionality only. + */ +#if( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure + * FreeRTOS API functions are not called from interrupts that have been assigned + * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. + */ +#if( configASSERT_DEFINED == 1 ) + static uint8_t ucMaxSysCallPriority = 0; + static uint32_t ulMaxPRIGROUPValue = 0; + static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16; +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + + /* Offset added to account for the way the MCU uses the stack on entry/exit + of interrupts, and to ensure alignment. */ + pxTopOfStack--; + + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + + /* Save code space by skipping register initialisation. */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + + /* A save method is being used that requires each task to maintain its + own exec return value. */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; + + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +static void prvTaskExitError( void ) +{ +volatile uint32_t ulDummy = 0; + + /* A function that implements a task must not exit or attempt to return to + its caller as there is nothing to return to. If a task wants to exit it + should instead call vTaskDelete( NULL ). + + Artificially force an assert() to be triggered if configASSERT() is + defined, then stop here so application writers can catch the error. */ + configASSERT( uxCriticalNesting == ~0UL ); + portDISABLE_INTERRUPTS(); + while( ulDummy == 0 ) + { + /* This file calls prvTaskExitError() after the scheduler has been + started to remove a compiler warning about the function being defined + but never called. ulDummy is used purely to quieten other warnings + about code appearing after this function is called - making ulDummy + volatile makes the compiler think the function could return and + therefore not output an 'unreachable code' warning for code that appears + after it. */ + } +} +/*-----------------------------------------------------------*/ + +void vPortSVCHandler( void ) +{ + __asm volatile ( + " ldr r3, pxCurrentTCBConst2 \n" /* Restore the context. */ + " ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ + " ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldmia r0!, {r4-r11, r14} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ + " msr psp, r0 \n" /* Restore the task stack pointer. */ + " isb \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " bx r14 \n" + " \n" + " .align 4 \n" + "pxCurrentTCBConst2: .word pxCurrentTCB \n" + ); +} +/*-----------------------------------------------------------*/ + +static void prvPortStartFirstTask( void ) +{ + /* Start the first task. This also clears the bit that indicates the FPU is + in use in case the FPU was used before the scheduler was started - which + would otherwise result in the unnecessary leaving of space in the SVC stack + for lazy saving of FPU registers. */ + __asm volatile( + " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */ + " ldr r0, [r0] \n" + " ldr r0, [r0] \n" + " msr msp, r0 \n" /* Set the msp back to the start of the stack. */ + " mov r0, #0 \n" /* Clear the bit that indicates the FPU is in use, see comment above. */ + " msr control, r0 \n" + " cpsie i \n" /* Globally enable interrupts. */ + " cpsie f \n" + " dsb \n" + " isb \n" + " svc 0 \n" /* System call to start first task. */ + " nop \n" + ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + /* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. + See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); + + #if( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + functions can be called. ISR safe functions are those that end in + "FromISR". FreeRTOS maintains separate thread and ISR API functions to + ensure interrupt entry is as fast and simple as possible. + + Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) + { + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; + } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + priority bits matches the number of priority bits actually queried + from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + priority bits matches the number of priority bits actually queried + from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } + #endif /* conifgASSERT_DEFINED */ + + /* Make PendSV and SysTick the lowest priority interrupts. */ + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + vPortSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Ensure the VFP is enabled - it should be anyway. */ + vPortEnableVFP(); + + /* Lazy save always. */ + *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS; + + /* Start the first task. */ + prvPortStartFirstTask(); + + /* Should never get here as the tasks will now be executing! Call the task + exit error function to prevent compiler warnings about a static function + not being called in the case that the application writer overrides this + functionality by defining configTASK_RETURN_ADDRESS. Call + vTaskSwitchContext() so link time optimisation does not remove the + symbol. */ + vTaskSwitchContext(); + prvTaskExitError(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + + /* This is not the interrupt safe version of the enter critical function so + assert() if it is being called from an interrupt context. Only API + functions that end in "FromISR" can be used in an interrupt. Only assert if + the critical nesting count is 1 to protect against recursive calls if the + assert function also uses a critical section. */ + if( uxCriticalNesting == 1 ) + { + configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +void xPortPendSVHandler( void ) +{ + /* This is a naked function. */ + + __asm volatile + ( + " mrs r0, psp \n" + " isb \n" + " \n" + " ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */ + " ldr r2, [r3] \n" + " \n" + " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, push high vfp registers. */ + " it eq \n" + " vstmdbeq r0!, {s16-s31} \n" + " \n" + " stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */ + " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */ + " \n" + " stmdb sp!, {r0, r3} \n" + " mov r0, %0 \n" + " cpsid i \n" /* Errata workaround. */ + " msr basepri, r0 \n" + " dsb \n" + " isb \n" + " cpsie i \n" /* Errata workaround. */ + " bl vTaskSwitchContext \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " ldmia sp!, {r0, r3} \n" + " \n" + " ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldr r0, [r1] \n" + " \n" + " ldmia r0!, {r4-r11, r14} \n" /* Pop the core registers. */ + " \n" + " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, pop the high vfp registers too. */ + " it eq \n" + " vldmiaeq r0!, {s16-s31} \n" + " \n" + " msr psp, r0 \n" + " isb \n" + " \n" + #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */ + #if WORKAROUND_PMU_CM001 == 1 + " push { r14 } \n" + " pop { pc } \n" + #endif + #endif + " \n" + " bx r14 \n" + " \n" + " .align 4 \n" + "pxCurrentTCBConst: .word pxCurrentTCB \n" + ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY) + ); +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ + /* The SysTick runs at the lowest interrupt priority, so when this interrupt + executes all interrupts must be unmasked. There is therefore no need to + save and then restore the interrupt mask value as its value is already + known. */ + portDISABLE_INTERRUPTS(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* A context switch is required. Context switching is performed in + the PendSV interrupt. Pend the PendSV interrupt. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + } + } + portENABLE_INTERRUPTS(); +} +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + + __attribute__((weak)) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + xExpectedIdleTime = xMaximumPossibleSuppressedTicks; + } + + /* Stop the SysTick momentarily. The time the SysTick is stopped for + is accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; + + /* Calculate the reload value required to wait xExpectedIdleTime + tick periods. -1 is used because this code will execute part way + through one of the tick periods. */ + ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + + /* Enter a critical section but don't use the taskENTER_CRITICAL() + method as that will mask interrupts that should exit sleep mode. */ + __asm volatile( "cpsid i" ::: "memory" ); + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + /* If a context switch is pending or a task is waiting for the scheduler + to be unsuspended then abandon the low power entry. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + set its parameter to 0 to indicate that its implementation contains + its own wait for interrupt or wait for event instruction, and so wfi + should not be executed again. However, the original expected idle + time variable must remain unmodified, so a copy is taken. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( &xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + configPOST_SLEEP_PROCESSING( &xExpectedIdleTime ); + + /* Re-enable interrupts to allow the interrupt that brought the MCU + out of sleep mode to execute immediately. see comments above + __disable_interrupt() call above. */ + __asm volatile( "cpsie i" ::: "memory" ); + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + /* Disable interrupts again because the clock is about to be stopped + and interrupts that execute while the clock is stopped will increase + any slippage between the time maintained by the RTOS and calendar + time. */ + __asm volatile( "cpsid i" ::: "memory" ); + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + /* Disable the SysTick clock without reading the + portNVIC_SYSTICK_CTRL_REG register to ensure the + portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. Again, + the time the SysTick is stopped for is accounted for as best it can + be, but using the tickless mode will inevitably result in some tiny + drift of the time maintained by the kernel with respect to calendar + time*/ + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT ); + + /* Determine if the SysTick clock has already counted to zero and + been set back to the current reload value (the reload back being + correct for the entire expected idle time) or if the SysTick is yet + to count to zero (in which case an interrupt other than the SysTick + must have brought the system out of sleep mode). */ + if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + uint32_t ulCalculatedLoadValue; + + /* The tick interrupt is already pending, and the SysTick count + reloaded with ulReloadValue. Reset the + portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick + period. */ + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); + + /* Don't allow a tiny value, or values that have somehow + underflowed because the post sleep hook did something + that took too long. */ + if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; + + /* As the pending tick will be processed as soon as this + function exits, the tick value maintained by the tick is stepped + forward by one less than the time spent waiting. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + Work out how long the sleep lasted rounded to complete tick + periods (not the ulReload value which accounted for part + ticks). */ + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* How many complete tick periods passed while the processor + was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + period remains. */ + portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; + } + + /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG + again, then set portNVIC_SYSTICK_LOAD_REG back to its standard + value. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrpts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } + +#endif /* #if configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +__attribute__(( weak )) void vPortSetupTimerInterrupt( void ) +{ + /* Calculate the constants required to configure the tick interrupt. */ + #if( configUSE_TICKLESS_IDLE == 1 ) + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } + #endif /* configUSE_TICKLESS_IDLE */ + + /* Stop and clear the SysTick. */ + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Configure SysTick to interrupt at the requested rate. */ + portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); +} +/*-----------------------------------------------------------*/ + +/* This is a naked function. */ +static void vPortEnableVFP( void ) +{ + __asm volatile + ( + " ldr.w r0, =0xE000ED88 \n" /* The FPU enable bits are in the CPACR. */ + " ldr r1, [r0] \n" + " \n" + " orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */ + " str r1, [r0] \n" + " bx r14 " + ); +} +/*-----------------------------------------------------------*/ + +#if( configASSERT_DEFINED == 1 ) + + void vPortValidateInterruptPriority( void ) + { + uint32_t ulCurrentInterrupt; + uint8_t ucCurrentPriority; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" ); + + /* Is the interrupt number a user defined interrupt? */ + if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) + { + /* Look up the interrupt's priority. */ + ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; + + /* The following assertion will fail if a service routine (ISR) for + an interrupt that has been assigned a priority above + configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API + function. ISR safe FreeRTOS API functions must *only* be called + from interrupts that have been assigned a priority at or below + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Numerically low interrupt priority numbers represent logically high + interrupt priorities, therefore the priority of the interrupt must + be set to a value equal to or numerically *higher* than + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Interrupts that use the FreeRTOS API must not be left at their + default priority of zero as that is the highest possible priority, + which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, + and therefore also guaranteed to be invalid. + + FreeRTOS maintains separate thread and ISR API functions to ensure + interrupt entry is as fast and simple as possible. + + The following links provide detailed information: + http://www.freertos.org/RTOS-Cortex-M3-M4.html + http://www.freertos.org/FAQHelp.html */ + configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); + } + + /* Priority grouping: The interrupt controller (NVIC) allows the bits + that define each interrupt's priority to be split between bits that + define the interrupt's pre-emption priority bits and bits that define + the interrupt's sub-priority. For simplicity all bits must be defined + to be pre-emption priority bits. The following assertion will fail if + this is not the case (if some bits represent a sub-priority). + + If the application only uses CMSIS libraries for interrupt + configuration then the correct setting can be achieved on all Cortex-M + devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the + scheduler. Note however that some vendor specific peripheral libraries + assume a non-zero priority group setting, in which cases using a value + of zero will result in unpredictable behaviour. */ + configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); + } + +#endif /* configASSERT_DEFINED */ + + diff --git a/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/portmacro.h b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/portmacro.h new file mode 100644 index 000000000..62543ac74 --- /dev/null +++ b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/portmacro.h @@ -0,0 +1,247 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE uint32_t +#define portBASE_TYPE long + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff +#else + typedef uint32_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL + + /* 32-bit tick type on a 32-bit architecture, so reads of the tick count do + not need to be guarded with a critical section. */ + #define portTICK_TYPE_IS_ATOMIC 1 +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ +#define portYIELD() \ +{ \ + /* Set a PendSV to request a context switch. */ \ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \ + \ + /* Barriers are normally not required but do ensure the code is completely \ + within the specified behaviour for the architecture. */ \ + __asm volatile( "dsb" ::: "memory" ); \ + __asm volatile( "isb" ); \ +} + +#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) ) +#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD() +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); +#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x) +#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() +#define portENABLE_INTERRUPTS() vPortSetBASEPRI(0) +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() + +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are +not necessary for to use this port. They are defined so the common demo files +(which build with all the ports) will build. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +/* Tickless idle/low power functionality. */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specific optimisations. */ +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + + /* Generic helper function. */ + __attribute__( ( always_inline ) ) static inline uint8_t ucPortCountLeadingZeros( uint32_t ulBitmap ) + { + uint8_t ucReturn; + + __asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) : "memory" ); + return ucReturn; + } + + /* Check the configuration. */ + #if( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + + /* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) + + /*-----------------------------------------------------------*/ + + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) ucPortCountLeadingZeros( ( uxReadyPriorities ) ) ) + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/*-----------------------------------------------------------*/ + +#ifdef configASSERT + void vPortValidateInterruptPriority( void ); + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority() +#endif + +/* portNOP() is not required by this port. */ +#define portNOP() + +#define portINLINE __inline + +#ifndef portFORCE_INLINE + #define portFORCE_INLINE inline __attribute__(( always_inline)) +#endif + +portFORCE_INLINE static BaseType_t xPortIsInsideInterrupt( void ) +{ +uint32_t ulCurrentInterrupt; +BaseType_t xReturn; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" ); + + if( ulCurrentInterrupt == 0 ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} + +/*-----------------------------------------------------------*/ + +portFORCE_INLINE static void vPortRaiseBASEPRI( void ) +{ +uint32_t ulNewBASEPRI; + + __asm volatile + ( + " mov %0, %1 \n" \ + " cpsid i \n" \ + " msr basepri, %0 \n" \ + " isb \n" \ + " dsb \n" \ + " cpsie i \n" \ + :"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" + ); +} + +/*-----------------------------------------------------------*/ + +portFORCE_INLINE static uint32_t ulPortRaiseBASEPRI( void ) +{ +uint32_t ulOriginalBASEPRI, ulNewBASEPRI; + + __asm volatile + ( + " mrs %0, basepri \n" \ + " mov %1, %2 \n" \ + " cpsid i \n" \ + " msr basepri, %1 \n" \ + " isb \n" \ + " dsb \n" \ + " cpsie i \n" \ + :"=r" (ulOriginalBASEPRI), "=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" + ); + + /* This return will not be reached but is necessary to prevent compiler + warnings. */ + return ulOriginalBASEPRI; +} +/*-----------------------------------------------------------*/ + +portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue ) +{ + __asm volatile + ( + " msr basepri, %0 " :: "r" ( ulNewMaskValue ) : "memory" + ); +} +/*-----------------------------------------------------------*/ + +#define portMEMORY_BARRIER() __asm volatile( "" ::: "memory" ) + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c b/Firmware/ThirdParty/FreeRTOS/Source/portable/MemMang/heap_4.c similarity index 76% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c rename to Firmware/ThirdParty/FreeRTOS/Source/portable/MemMang/heap_4.c index e7c7ade68..d7cd8a5b4 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/portable/MemMang/heap_4.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* * A sample implementation of pvPortMalloc() and vPortFree() that combines diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/queue.c b/Firmware/ThirdParty/FreeRTOS/Source/queue.c similarity index 72% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/queue.c rename to Firmware/ThirdParty/FreeRTOS/Source/queue.c index 056d4be11..d882bf670 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/queue.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/queue.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #include #include @@ -83,11 +41,11 @@ task.h is included from an application file. */ #include "croutine.h" #endif -/* Lint e961 and e750 are suppressed as a MISRA exception justified because the -MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the -header files above, but not in this file, in order to generate the correct -privileged Vs unprivileged linkage and placement. */ -#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ /* Constants used with the cRxLock and cTxLock structure members. */ @@ -98,17 +56,26 @@ privileged Vs unprivileged linkage and placement. */ pcTail members are used as pointers into the queue storage area. When the Queue_t structure is used to represent a mutex pcHead and pcTail pointers are not necessary, and the pcHead pointer is set to NULL to indicate that the -pcTail pointer actually points to the mutex holder (if any). Map alternative -names to the pcHead and pcTail structure members to ensure the readability of -the code is maintained despite this dual use of two structure members. An -alternative implementation would be to use a union, but use of a union is -against the coding standard (although an exception to the standard has been -permitted where the dual use also significantly changes the type of the -structure member). */ -#define pxMutexHolder pcTail +structure instead holds a pointer to the mutex holder (if any). Map alternative +names to the pcHead and structure member to ensure the readability of the code +is maintained. The QueuePointers_t and SemaphoreData_t types are used to form +a union as their usage is mutually exclusive dependent on what the queue is +being used for. */ #define uxQueueType pcHead #define queueQUEUE_IS_MUTEX NULL +typedef struct QueuePointers +{ + int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ + int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */ +} QueuePointers_t; + +typedef struct SemaphoreData +{ + TaskHandle_t xMutexHolder; /*< The handle of the task that holds the mutex. */ + UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */ +} SemaphoreData_t; + /* Semaphores do not actually store or copy data, so have an item size of zero. */ #define queueSEMAPHORE_QUEUE_ITEM_LENGTH ( ( UBaseType_t ) 0 ) @@ -125,18 +92,17 @@ zero. */ /* * Definition of the queue used by the scheduler. * Items are queued by copy, not reference. See the following link for the - * rationale: http://www.freertos.org/Embedded-RTOS-Queues.html + * rationale: https://www.freertos.org/Embedded-RTOS-Queues.html */ -typedef struct QueueDefinition +typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { int8_t *pcHead; /*< Points to the beginning of the queue storage area. */ - int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ int8_t *pcWriteTo; /*< Points to the free next place in the storage area. */ - union /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */ + union { - int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */ - UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */ + QueuePointers_t xQueue; /*< Data required exclusively when this structure is used as a queue. */ + SemaphoreData_t xSemaphore; /*< Data required exclusively when this structure is used as a semaphore. */ } u; List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ @@ -205,46 +171,46 @@ typedef xQUEUE Queue_t; * to indicate that a task may require unblocking. When the queue in unlocked * these lock counts are inspected, and the appropriate action taken. */ -PRIVILEGED_FUNCTION static void prvUnlockQueue( Queue_t * const pxQueue ); +static void prvUnlockQueue( Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; /* * Uses a critical section to determine if there is any data in a queue. * * @return pdTRUE if the queue contains no items, otherwise pdFALSE. */ -PRIVILEGED_FUNCTION static BaseType_t prvIsQueueEmpty( const Queue_t *pxQueue ); +static BaseType_t prvIsQueueEmpty( const Queue_t *pxQueue ) PRIVILEGED_FUNCTION; /* * Uses a critical section to determine if there is any space in a queue. * * @return pdTRUE if there is no space, otherwise pdFALSE; */ -PRIVILEGED_FUNCTION static BaseType_t prvIsQueueFull( const Queue_t *pxQueue ); +static BaseType_t prvIsQueueFull( const Queue_t *pxQueue ) PRIVILEGED_FUNCTION; /* * Copies an item into the queue, either at the front of the queue or the * back of the queue. */ -PRIVILEGED_FUNCTION static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ); +static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ) PRIVILEGED_FUNCTION; /* * Copies an item out of a queue. */ -PRIVILEGED_FUNCTION static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer ); +static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; #if ( configUSE_QUEUE_SETS == 1 ) /* * Checks to see if a queue is a member of a queue set, and if so, notifies * the queue set that the queue contains data. */ - PRIVILEGED_FUNCTION static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition ); + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; #endif /* * Called after a Queue_t structure has been allocated either statically or * dynamically to fill in the structure's members. */ -PRIVILEGED_FUNCTION static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ); +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION; /* * Mutexes are a special type of queue. When a mutex is created, first the @@ -252,9 +218,19 @@ PRIVILEGED_FUNCTION static void prvInitialiseNewQueue( const UBaseType_t uxQueue * as a mutex. */ #if( configUSE_MUTEXES == 1 ) - PRIVILEGED_FUNCTION static void prvInitialiseMutex( Queue_t *pxNewQueue ); + static void prvInitialiseMutex( Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION; #endif +#if( configUSE_MUTEXES == 1 ) + /* + * If a task waiting for a mutex causes the mutex holder to inherit a + * priority, but the waiting task times out, then the holder should + * disinherit the priority - but only down to the highest priority of any + * other tasks that are waiting for the same mutex. This function returns + * that priority. + */ + static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; +#endif /*-----------------------------------------------------------*/ /* @@ -278,16 +254,16 @@ PRIVILEGED_FUNCTION static void prvInitialiseNewQueue( const UBaseType_t uxQueue BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) { -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); taskENTER_CRITICAL(); { - pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); + pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; pxQueue->pcWriteTo = pxQueue->pcHead; - pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 1U ) * pxQueue->uxItemSize ); + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ pxQueue->cRxLock = queueUNLOCKED; pxQueue->cTxLock = queueUNLOCKED; @@ -353,13 +329,14 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; the real queue and semaphore structures. */ volatile size_t xSize = sizeof( StaticQueue_t ); configASSERT( xSize == sizeof( Queue_t ) ); + ( void ) xSize; /* Keeps lint quiet when configASSERT() is not defined. */ } #endif /* configASSERT_DEFINED */ /* The address of a statically allocated queue was passed in, use it. The address of a statically allocated storage area was also passed in but is already set. */ - pxNewQueue = ( Queue_t * ) pxStaticQueue; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + pxNewQueue = ( Queue_t * ) pxStaticQueue; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ if( pxNewQueue != NULL ) { @@ -374,6 +351,11 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); } + else + { + traceQUEUE_CREATE_FAILED( ucQueueType ); + mtCOVERAGE_TEST_MARKER(); + } return pxNewQueue; } @@ -403,13 +385,23 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ } - pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); + /* Allocate the queue and storage area. Justification for MISRA + deviation as follows: pvPortMalloc() always ensures returned memory + blocks are aligned per the requirements of the MCU stack. In this case + pvPortMalloc() must return a pointer that is guaranteed to meet the + alignment requirements of the Queue_t structure - which in this case + is an int8_t *. Therefore, whenever the stack alignment requirements + are greater than or equal to the pointer to char requirements the cast + is safe. In other cases alignment requirements are not strict (one or + two bytes). */ + pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); /*lint !e9087 !e9079 see comment above. */ if( pxNewQueue != NULL ) { /* Jump past the queue structure to find the location of the queue storage area. */ - pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t ); + pucQueueStorage = ( uint8_t * ) pxNewQueue; + pucQueueStorage += sizeof( Queue_t ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) { @@ -422,6 +414,11 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); } + else + { + traceQUEUE_CREATE_FAILED( ucQueueType ); + mtCOVERAGE_TEST_MARKER(); + } return pxNewQueue; } @@ -481,11 +478,11 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT correctly for a generic queue, but this function is creating a mutex. Overwrite those members that need to be set differently - in particular the information required for priority inheritance. */ - pxNewQueue->pxMutexHolder = NULL; + pxNewQueue->u.xSemaphore.xMutexHolder = NULL; pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; /* In case this is a recursive mutex. */ - pxNewQueue->u.uxRecursiveCallCount = 0; + pxNewQueue->u.xSemaphore.uxRecursiveCallCount = 0; traceCREATE_MUTEX( pxNewQueue ); @@ -505,13 +502,13 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) { - Queue_t *pxNewQueue; + QueueHandle_t xNewQueue; const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; - pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); - prvInitialiseMutex( pxNewQueue ); + xNewQueue = xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); + prvInitialiseMutex( ( Queue_t * ) xNewQueue ); - return pxNewQueue; + return xNewQueue; } #endif /* configUSE_MUTEXES */ @@ -521,17 +518,17 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) { - Queue_t *pxNewQueue; + QueueHandle_t xNewQueue; const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; /* Prevent compiler warnings about unused parameters if configUSE_TRACE_FACILITY does not equal 1. */ ( void ) ucQueueType; - pxNewQueue = ( Queue_t * ) xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType ); - prvInitialiseMutex( pxNewQueue ); + xNewQueue = xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType ); + prvInitialiseMutex( ( Queue_t * ) xNewQueue ); - return pxNewQueue; + return xNewQueue; } #endif /* configUSE_MUTEXES */ @@ -539,9 +536,10 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT #if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) - void* xQueueGetMutexHolder( QueueHandle_t xSemaphore ) + TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) { - void *pxReturn; + TaskHandle_t pxReturn; + Queue_t * const pxSemaphore = ( Queue_t * ) xSemaphore; /* This function is called by xSemaphoreGetMutexHolder(), and should not be called directly. Note: This is a good way of determining if the @@ -550,9 +548,9 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT following critical section exiting and the function returning. */ taskENTER_CRITICAL(); { - if( ( ( Queue_t * ) xSemaphore )->uxQueueType == queueQUEUE_IS_MUTEX ) + if( pxSemaphore->uxQueueType == queueQUEUE_IS_MUTEX ) { - pxReturn = ( void * ) ( ( Queue_t * ) xSemaphore )->pxMutexHolder; + pxReturn = pxSemaphore->u.xSemaphore.xMutexHolder; } else { @@ -567,6 +565,32 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT #endif /*-----------------------------------------------------------*/ +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + + TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) + { + TaskHandle_t pxReturn; + + configASSERT( xSemaphore ); + + /* Mutexes cannot be used in interrupt service routines, so the mutex + holder should not change in an ISR, and therefore a critical section is + not required here. */ + if( ( ( Queue_t * ) xSemaphore )->uxQueueType == queueQUEUE_IS_MUTEX ) + { + pxReturn = ( ( Queue_t * ) xSemaphore )->u.xSemaphore.xMutexHolder; + } + else + { + pxReturn = NULL; + } + + return pxReturn; + } /*lint !e818 xSemaphore cannot be a pointer to const because it is a typedef. */ + +#endif +/*-----------------------------------------------------------*/ + #if ( configUSE_RECURSIVE_MUTEXES == 1 ) BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) @@ -576,25 +600,25 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT configASSERT( pxMutex ); - /* If this is the task that holds the mutex then pxMutexHolder will not + /* If this is the task that holds the mutex then xMutexHolder will not change outside of this task. If this task does not hold the mutex then pxMutexHolder can never coincidentally equal the tasks handle, and as this is the only condition we are interested in it does not matter if pxMutexHolder is accessed simultaneously by another task. Therefore no mutual exclusion is required to test the pxMutexHolder variable. */ - if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Not a redundant cast as TaskHandle_t is a typedef. */ + if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) { traceGIVE_MUTEX_RECURSIVE( pxMutex ); - /* uxRecursiveCallCount cannot be zero if pxMutexHolder is equal to + /* uxRecursiveCallCount cannot be zero if xMutexHolder is equal to the task handle, therefore no underflow check is required. Also, uxRecursiveCallCount is only modified by the mutex holder, and as there can only be one, no mutual exclusion is required to modify the uxRecursiveCallCount member. */ - ( pxMutex->u.uxRecursiveCallCount )--; + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )--; /* Has the recursive call count unwound to 0? */ - if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 ) + if( pxMutex->u.xSemaphore.uxRecursiveCallCount == ( UBaseType_t ) 0 ) { /* Return the mutex. This will automatically unblock any other task that might be waiting to access the mutex. */ @@ -636,21 +660,21 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT traceTAKE_MUTEX_RECURSIVE( pxMutex ); - if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */ + if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) { - ( pxMutex->u.uxRecursiveCallCount )++; + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++; xReturn = pdPASS; } else { - xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE ); + xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait ); /* pdPASS will only be returned if the mutex was successfully obtained. The calling task may have entered the Blocked state before reaching here. */ if( xReturn != pdFAIL ) { - ( pxMutex->u.uxRecursiveCallCount )++; + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++; } else { @@ -724,7 +748,7 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQ { BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired; TimeOut_t xTimeOut; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); @@ -736,9 +760,9 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; #endif - /* This function relaxes the coding standard somewhat to allow return - statements within the function itself. This is done in the interest - of execution time efficiency. */ + /*lint -save -e904 This function relaxes the coding standard somewhat to + allow return statements within the function itself. This is done in the + interest of execution time efficiency. */ for( ;; ) { taskENTER_CRITICAL(); @@ -750,13 +774,23 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) { traceQUEUE_SEND( pxQueue ); - xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); #if ( configUSE_QUEUE_SETS == 1 ) { + UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; + + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + if( pxQueue->pxQueueSetContainer != NULL ) { - if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE ) + if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ) + { + /* Do not notify the queue set as an existing item + was overwritten in the queue so the number of items + in the queue has not changed. */ + mtCOVERAGE_TEST_MARKER(); + } + else if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE ) { /* The queue is a member of a queue set, and posting to the queue set caused a higher priority task to @@ -803,6 +837,8 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } #else /* configUSE_QUEUE_SETS */ { + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + /* If there was a task waiting for data to arrive on the queue then unblock it now. */ if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) @@ -855,7 +891,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; { /* The queue was full and a block time was specified so configure the timeout structure. */ - vTaskSetTimeOutState( &xTimeOut ); + vTaskInternalSetTimeOutState( &xTimeOut ); xEntryTimeSet = pdTRUE; } else @@ -882,8 +918,8 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); /* Unlocking the queue means queue events can effect the - event list. It is possible that interrupts occurring now - remove this task from the event list again - but as the + event list. It is possible that interrupts occurring now + remove this task from the event list again - but as the scheduler is suspended the task will go onto the pending ready last instead of the actual ready list. */ prvUnlockQueue( pxQueue ); @@ -914,7 +950,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; traceQUEUE_SEND_FAILED( pxQueue ); return errQUEUE_FULL; } - } + } /*lint -restore */ } /*-----------------------------------------------------------*/ @@ -922,7 +958,7 @@ BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pv { BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); @@ -1073,7 +1109,7 @@ BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherP { BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; /* Similar to xQueueGenericSendFromISR() but used with semaphores where the item size is 0. Don't directly wake a task that was blocked on a queue @@ -1090,7 +1126,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; /* Normally a mutex would not be given from an interrupt, especially if there is a mutex holder, as priority inheritance makes no sense for an interrupts, only tasks. */ - configASSERT( !( ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) && ( pxQueue->pxMutexHolder != NULL ) ) ); + configASSERT( !( ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) && ( pxQueue->u.xSemaphore.xMutexHolder != NULL ) ) ); /* RTOS ports that support interrupt nesting have the concept of a maximum system call (or maximum API call) interrupt priority. Interrupts that are @@ -1127,7 +1163,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; can be assumed there is no mutex holder and no need to determine if priority disinheritance is needed. Simply increase the count of messages (semaphores) available. */ - pxQueue->uxMessagesWaiting = uxMessagesWaiting + 1; + pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1; /* The event list is not altered if the queue is locked. This will be done when the queue is unlocked later. */ @@ -1234,25 +1270,30 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } /*-----------------------------------------------------------*/ -BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking ) +BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) { BaseType_t xEntryTimeSet = pdFALSE; TimeOut_t xTimeOut; -int8_t *pcOriginalReadPosition; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; - configASSERT( pxQueue ); - configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + /* Check the pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* The buffer into which data is received can only be NULL if the data size + is zero (so no data is copied into the buffer. */ + configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* Cannot block if the scheduler is suspended. */ #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) { configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); } #endif - /* This function relaxes the coding standard somewhat to allow return - statements within the function itself. This is done in the interest - of execution time efficiency. */ + /*lint -save -e904 This function relaxes the coding standard somewhat to + allow return statements within the function itself. This is done in the + interest of execution time efficiency. */ for( ;; ) { taskENTER_CRITICAL(); @@ -1263,44 +1304,19 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; must be the highest priority task wanting to access the queue. */ if( uxMessagesWaiting > ( UBaseType_t ) 0 ) { - /* Remember the read position in case the queue is only being - peeked. */ - pcOriginalReadPosition = pxQueue->u.pcReadFrom; - + /* Data available, remove one item. */ prvCopyDataFromQueue( pxQueue, pvBuffer ); + traceQUEUE_RECEIVE( pxQueue ); + pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; - if( xJustPeeking == pdFALSE ) + /* There is now space in the queue, were any tasks waiting to + post to the queue? If so, unblock the highest priority waiting + task. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) { - traceQUEUE_RECEIVE( pxQueue ); - - /* Actually removing data, not just peeking. */ - pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1; - - #if ( configUSE_MUTEXES == 1 ) - { - if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) - { - /* Record the information required to implement - priority inheritance should it become necessary. */ - pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */ - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configUSE_MUTEXES */ - - if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) { - if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) - { - queueYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + queueYIELD_IF_USING_PREEMPTION(); } else { @@ -1309,31 +1325,170 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } else { - traceQUEUE_PEEK( pxQueue ); + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was empty and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + configure the timeout structure. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); - /* The data is not being removed, so reset the read - pointer. */ - pxQueue->u.pcReadFrom = pcOriginalReadPosition; + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ - /* The data is being left in the queue, so see if there are - any other tasks waiting for the data. */ - if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* The timeout has not expired. If the queue is still empty place + the task on the list of tasks waiting to receive from the queue. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The queue contains data again. Loop back to try and read the + data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* Timed out. If there is no data in the queue exit, otherwise loop + back and attempt to read the data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint -restore */ +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait ) +{ +BaseType_t xEntryTimeSet = pdFALSE; +TimeOut_t xTimeOut; +Queue_t * const pxQueue = xQueue; + +#if( configUSE_MUTEXES == 1 ) + BaseType_t xInheritanceOccurred = pdFALSE; +#endif + + /* Check the queue pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* Check this really is a semaphore, in which case the item size will be + 0. */ + configASSERT( pxQueue->uxItemSize == 0 ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + + /*lint -save -e904 This function relaxes the coding standard somewhat to allow return + statements within the function itself. This is done in the interest + of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Semaphores are queues with an item size of 0, and where the + number of messages in the queue is the semaphore's count value. */ + const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + must be the highest priority task wanting to access the queue. */ + if( uxSemaphoreCount > ( UBaseType_t ) 0 ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* Semaphores are queues with a data size of zero and where the + messages waiting is the semaphore's count. Reduce the count. */ + pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1; + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) { - if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) - { - /* The task waiting has a higher priority than this task. */ - queueYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + /* Record the information required to implement + priority inheritance should it become necessary. */ + pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount(); } else { mtCOVERAGE_TEST_MARKER(); } } + #endif /* configUSE_MUTEXES */ + + /* Check to see if other tasks are blocked waiting to give the + semaphore, and if so, unblock the highest priority such task. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } taskEXIT_CRITICAL(); return pdPASS; @@ -1342,17 +1497,26 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; { if( xTicksToWait == ( TickType_t ) 0 ) { - /* The queue was empty and no block time is specified (or - the block time has expired) so leave now. */ + /* For inheritance to have occurred there must have been an + initial timeout, and an adjusted timeout cannot become 0, as + if it were 0 the function would have exited. */ + #if( configUSE_MUTEXES == 1 ) + { + configASSERT( xInheritanceOccurred == pdFALSE ); + } + #endif /* configUSE_MUTEXES */ + + /* The semaphore count was 0 and no block time is specified + (or the block time has expired) so exit now. */ taskEXIT_CRITICAL(); traceQUEUE_RECEIVE_FAILED( pxQueue ); return errQUEUE_EMPTY; } else if( xEntryTimeSet == pdFALSE ) { - /* The queue was empty and a block time was specified so - configure the timeout structure. */ - vTaskSetTimeOutState( &xTimeOut ); + /* The semaphore count was 0 and a block time was specified + so configure the timeout structure ready to block. */ + vTaskInternalSetTimeOutState( &xTimeOut ); xEntryTimeSet = pdTRUE; } else @@ -1364,7 +1528,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } taskEXIT_CRITICAL(); - /* Interrupts and other tasks can send to and receive from the queue + /* Interrupts and other tasks can give to and take from the semaphore now the critical section has been exited. */ vTaskSuspendAll(); @@ -1373,6 +1537,10 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; /* Update the timeout state to see if it has expired yet. */ if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) { + /* A block time is specified and not expired. If the semaphore + count is 0 then enter the Blocked state to wait for a semaphore to + become available. As semaphores are implemented with queues the + queue being empty is equivalent to the semaphore count being 0. */ if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) { traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); @@ -1383,7 +1551,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; { taskENTER_CRITICAL(); { - vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); + xInheritanceOccurred = xTaskPriorityInherit( pxQueue->u.xSemaphore.xMutexHolder ); } taskEXIT_CRITICAL(); } @@ -1407,18 +1575,48 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } else { - /* Try again. */ + /* There was no timeout and the semaphore count was not 0, so + attempt to take the semaphore again. */ prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); } } else { + /* Timed out. */ prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); + /* If the semaphore count is 0 exit now as the timeout has + expired. Otherwise return to attempt to take the semaphore that is + known to be available. As semaphores are implemented by queues the + queue being empty is equivalent to the semaphore count being 0. */ if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) { + #if ( configUSE_MUTEXES == 1 ) + { + /* xInheritanceOccurred could only have be set if + pxQueue->uxQueueType == queueQUEUE_IS_MUTEX so no need to + test the mutex type again to check it is actually a mutex. */ + if( xInheritanceOccurred != pdFALSE ) + { + taskENTER_CRITICAL(); + { + UBaseType_t uxHighestWaitingPriority; + + /* This task blocking on the mutex caused another + task to inherit this task's priority. Now this task + has timed out the priority should be disinherited + again, but only as low as the next highest priority + task that is waiting for the same mutex. */ + uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue ); + vTaskPriorityDisinheritAfterTimeout( pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority ); + } + taskEXIT_CRITICAL(); + } + } + #endif /* configUSE_MUTEXES */ + traceQUEUE_RECEIVE_FAILED( pxQueue ); return errQUEUE_EMPTY; } @@ -1427,7 +1625,156 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; mtCOVERAGE_TEST_MARKER(); } } + } /*lint -restore */ +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) +{ +BaseType_t xEntryTimeSet = pdFALSE; +TimeOut_t xTimeOut; +int8_t *pcOriginalReadPosition; +Queue_t * const pxQueue = xQueue; + + /* Check the pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* The buffer into which data is received can only be NULL if the data size + is zero (so no data is copied into the buffer. */ + configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); } + #endif + + + /*lint -save -e904 This function relaxes the coding standard somewhat to + allow return statements within the function itself. This is done in the + interest of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + must be the highest priority task wanting to access the queue. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Remember the read position so it can be reset after the data + is read from the queue as this function is only peeking the + data, not removing it. */ + pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + traceQUEUE_PEEK( pxQueue ); + + /* The data is not being removed, so reset the read pointer. */ + pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + any other tasks waiting for the data. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was empty and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + traceQUEUE_PEEK_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + configure the timeout structure ready to enter the blocked + state. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* Timeout has not expired yet, check to see if there is data in the + queue now, and if not enter the Blocked state to wait for data. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_PEEK( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* There is data in the queue now, so don't enter the blocked + state, instead return to try and obtain the data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* The timeout has expired. If there is still no data in the queue + exit, otherwise go back and try to read the data again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceQUEUE_PEEK_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint -restore */ } /*-----------------------------------------------------------*/ @@ -1435,7 +1782,7 @@ BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, Ba { BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); @@ -1468,7 +1815,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; traceQUEUE_RECEIVE_FROM_ISR( pxQueue ); prvCopyDataFromQueue( pxQueue, pvBuffer ); - pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1; + pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; /* If the queue is locked the event list will not be modified. Instead update the lock count so the task that unlocks the queue @@ -1527,7 +1874,7 @@ BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; int8_t *pcOriginalReadPosition; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); @@ -1558,9 +1905,9 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; /* Remember the read position so it can be reset as nothing is actually being removed from the queue. */ - pcOriginalReadPosition = pxQueue->u.pcReadFrom; + pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; prvCopyDataFromQueue( pxQueue, pvBuffer ); - pxQueue->u.pcReadFrom = pcOriginalReadPosition; + pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; xReturn = pdPASS; } @@ -1595,9 +1942,8 @@ UBaseType_t uxReturn; UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) { UBaseType_t uxReturn; -Queue_t *pxQueue; +Queue_t * const pxQueue = xQueue; - pxQueue = ( Queue_t * ) xQueue; configASSERT( pxQueue ); taskENTER_CRITICAL(); @@ -1613,10 +1959,10 @@ Queue_t *pxQueue; UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) { UBaseType_t uxReturn; +Queue_t * const pxQueue = xQueue; - configASSERT( xQueue ); - - uxReturn = ( ( Queue_t * ) xQueue )->uxMessagesWaiting; + configASSERT( pxQueue ); + uxReturn = pxQueue->uxMessagesWaiting; return uxReturn; } /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ @@ -1624,7 +1970,7 @@ UBaseType_t uxReturn; void vQueueDelete( QueueHandle_t xQueue ) { -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); traceQUEUE_DELETE( pxQueue ); @@ -1694,6 +2040,33 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ +#if( configUSE_MUTEXES == 1 ) + + static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) + { + UBaseType_t uxHighestPriorityOfWaitingTasks; + + /* If a task waiting for a mutex causes the mutex holder to inherit a + priority, but the waiting task times out, then the holder should + disinherit the priority - but only down to the highest priority of any + other tasks that are waiting for the same mutex. For this purpose, + return the priority of the highest priority task that is waiting for the + mutex. */ + if( listCURRENT_LIST_LENGTH( &( pxQueue->xTasksWaitingToReceive ) ) > 0U ) + { + uxHighestPriorityOfWaitingTasks = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) listGET_ITEM_VALUE_OF_HEAD_ENTRY( &( pxQueue->xTasksWaitingToReceive ) ); + } + else + { + uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY; + } + + return uxHighestPriorityOfWaitingTasks; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ) { BaseType_t xReturn = pdFALSE; @@ -1710,8 +2083,8 @@ UBaseType_t uxMessagesWaiting; if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) { /* The mutex is no longer being held. */ - xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder ); - pxQueue->pxMutexHolder = NULL; + xReturn = xTaskPriorityDisinherit( pxQueue->u.xSemaphore.xMutexHolder ); + pxQueue->u.xSemaphore.xMutexHolder = NULL; } else { @@ -1722,9 +2095,9 @@ UBaseType_t uxMessagesWaiting; } else if( xPosition == queueSEND_TO_BACK ) { - ( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0. */ - pxQueue->pcWriteTo += pxQueue->uxItemSize; - if( pxQueue->pcWriteTo >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + ( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */ + pxQueue->pcWriteTo += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */ + if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ { pxQueue->pcWriteTo = pxQueue->pcHead; } @@ -1735,11 +2108,11 @@ UBaseType_t uxMessagesWaiting; } else { - ( void ) memcpy( ( void * ) pxQueue->u.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - pxQueue->u.pcReadFrom -= pxQueue->uxItemSize; - if( pxQueue->u.pcReadFrom < pxQueue->pcHead ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + ( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e9087 !e418 MISRA exception as the casts are only redundant for some ports. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. Assert checks null pointer only used when length is 0. */ + pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize; + if( pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ { - pxQueue->u.pcReadFrom = ( pxQueue->pcTail - pxQueue->uxItemSize ); + pxQueue->u.xQueue.pcReadFrom = ( pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize ); } else { @@ -1767,7 +2140,7 @@ UBaseType_t uxMessagesWaiting; } } - pxQueue->uxMessagesWaiting = uxMessagesWaiting + 1; + pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1; return xReturn; } @@ -1777,16 +2150,16 @@ static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer { if( pxQueue->uxItemSize != ( UBaseType_t ) 0 ) { - pxQueue->u.pcReadFrom += pxQueue->uxItemSize; - if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */ + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */ + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */ { - pxQueue->u.pcReadFrom = pxQueue->pcHead; + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; } else { mtCOVERAGE_TEST_MARKER(); } - ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports. Also previous logic ensures a null pointer can only be passed to memcpy() when the count is 0. */ + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports. Also previous logic ensures a null pointer can only be passed to memcpy() when the count is 0. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */ } } /*-----------------------------------------------------------*/ @@ -1935,9 +2308,10 @@ BaseType_t xReturn; BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) { BaseType_t xReturn; +Queue_t * const pxQueue = xQueue; - configASSERT( xQueue ); - if( ( ( Queue_t * ) xQueue )->uxMessagesWaiting == ( UBaseType_t ) 0 ) + configASSERT( pxQueue ); + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) { xReturn = pdTRUE; } @@ -1974,9 +2348,10 @@ BaseType_t xReturn; BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) { BaseType_t xReturn; +Queue_t * const pxQueue = xQueue; - configASSERT( xQueue ); - if( ( ( Queue_t * ) xQueue )->uxMessagesWaiting == ( ( Queue_t * ) xQueue )->uxLength ) + configASSERT( pxQueue ); + if( pxQueue->uxMessagesWaiting == pxQueue->uxLength ) { xReturn = pdTRUE; } @@ -1994,7 +2369,7 @@ BaseType_t xReturn; BaseType_t xQueueCRSend( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait ) { BaseType_t xReturn; - Queue_t * const pxQueue = ( Queue_t * ) xQueue; + Queue_t * const pxQueue = xQueue; /* If the queue is already full we may have to block. A critical section is required to prevent an interrupt removing something from the queue @@ -2071,7 +2446,7 @@ BaseType_t xReturn; BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait ) { BaseType_t xReturn; - Queue_t * const pxQueue = ( Queue_t * ) xQueue; + Queue_t * const pxQueue = xQueue; /* If the queue is already empty we may have to block. A critical section is required to prevent an interrupt adding something to the queue @@ -2108,17 +2483,17 @@ BaseType_t xReturn; if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) { /* Data is available from the queue. */ - pxQueue->u.pcReadFrom += pxQueue->uxItemSize; - if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) { - pxQueue->u.pcReadFrom = pxQueue->pcHead; + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; } else { mtCOVERAGE_TEST_MARKER(); } --( pxQueue->uxMessagesWaiting ); - ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); xReturn = pdPASS; @@ -2160,7 +2535,7 @@ BaseType_t xReturn; BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t xCoRoutinePreviouslyWoken ) { - Queue_t * const pxQueue = ( Queue_t * ) xQueue; + Queue_t * const pxQueue = xQueue; /* Cannot block within an ISR so if there is no space on the queue then exit without doing anything. */ @@ -2209,24 +2584,24 @@ BaseType_t xReturn; BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxCoRoutineWoken ) { BaseType_t xReturn; - Queue_t * const pxQueue = ( Queue_t * ) xQueue; + Queue_t * const pxQueue = xQueue; /* We cannot block from an ISR, so check there is data available. If not then just leave without doing anything. */ if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) { /* Copy the data from the queue. */ - pxQueue->u.pcReadFrom += pxQueue->uxItemSize; - if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) { - pxQueue->u.pcReadFrom = pxQueue->pcHead; + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; } else { mtCOVERAGE_TEST_MARKER(); } --( pxQueue->uxMessagesWaiting ); - ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); if( ( *pxCoRoutineWoken ) == pdFALSE ) { @@ -2316,7 +2691,7 @@ BaseType_t xReturn; } return pcReturn; - } + } /*lint !e818 xQueue cannot be a pointer to const because it is a typedef. */ #endif /* configQUEUE_REGISTRY_SIZE */ /*-----------------------------------------------------------*/ @@ -2357,7 +2732,7 @@ BaseType_t xReturn; void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) { - Queue_t * const pxQueue = ( Queue_t * ) xQueue; + Queue_t * const pxQueue = xQueue; /* This function should not be called by application code hence the 'Restricted' in its name. It is not part of the public API. It is @@ -2395,7 +2770,7 @@ BaseType_t xReturn; { QueueSetHandle_t pxQueue; - pxQueue = xQueueGenericCreate( uxEventQueueLength, sizeof( Queue_t * ), queueQUEUE_TYPE_SET ); + pxQueue = xQueueGenericCreate( uxEventQueueLength, ( UBaseType_t ) sizeof( Queue_t * ), queueQUEUE_TYPE_SET ); return pxQueue; } @@ -2478,7 +2853,7 @@ BaseType_t xReturn; { QueueSetMemberHandle_t xReturn = NULL; - ( void ) xQueueGenericReceive( ( QueueHandle_t ) xQueueSet, &xReturn, xTicksToWait, pdFALSE ); /*lint !e961 Casting from one typedef to another is not redundant. */ + ( void ) xQueueReceive( ( QueueHandle_t ) xQueueSet, &xReturn, xTicksToWait ); /*lint !e961 Casting from one typedef to another is not redundant. */ return xReturn; } diff --git a/Firmware/ThirdParty/FreeRTOS/Source/stream_buffer.c b/Firmware/ThirdParty/FreeRTOS/Source/stream_buffer.c new file mode 100644 index 000000000..85519707b --- /dev/null +++ b/Firmware/ThirdParty/FreeRTOS/Source/stream_buffer.c @@ -0,0 +1,1263 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* Standard includes. */ +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "stream_buffer.h" + +#if( configUSE_TASK_NOTIFICATIONS != 1 ) + #error configUSE_TASK_NOTIFICATIONS must be set to 1 to build stream_buffer.c +#endif + +/* Lint e961, e9021 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ + +/* If the user has not provided application specific Rx notification macros, +or #defined the notification macros away, them provide default implementations +that uses task notifications. */ +/*lint -save -e9026 Function like macros allowed and needed here so they can be overidden. */ +#ifndef sbRECEIVE_COMPLETED + #define sbRECEIVE_COMPLETED( pxStreamBuffer ) \ + vTaskSuspendAll(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) \ + { \ + ( void ) xTaskNotify( ( pxStreamBuffer )->xTaskWaitingToSend, \ + ( uint32_t ) 0, \ + eNoAction ); \ + ( pxStreamBuffer )->xTaskWaitingToSend = NULL; \ + } \ + } \ + ( void ) xTaskResumeAll(); +#endif /* sbRECEIVE_COMPLETED */ + +#ifndef sbRECEIVE_COMPLETED_FROM_ISR + #define sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, \ + pxHigherPriorityTaskWoken ) \ + { \ + UBaseType_t uxSavedInterruptStatus; \ + \ + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) \ + { \ + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToSend, \ + ( uint32_t ) 0, \ + eNoAction, \ + pxHigherPriorityTaskWoken ); \ + ( pxStreamBuffer )->xTaskWaitingToSend = NULL; \ + } \ + } \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \ + } +#endif /* sbRECEIVE_COMPLETED_FROM_ISR */ + +/* If the user has not provided an application specific Tx notification macro, +or #defined the notification macro away, them provide a default implementation +that uses task notifications. */ +#ifndef sbSEND_COMPLETED + #define sbSEND_COMPLETED( pxStreamBuffer ) \ + vTaskSuspendAll(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) \ + { \ + ( void ) xTaskNotify( ( pxStreamBuffer )->xTaskWaitingToReceive, \ + ( uint32_t ) 0, \ + eNoAction ); \ + ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; \ + } \ + } \ + ( void ) xTaskResumeAll(); +#endif /* sbSEND_COMPLETED */ + +#ifndef sbSEND_COMPLETE_FROM_ISR + #define sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ) \ + { \ + UBaseType_t uxSavedInterruptStatus; \ + \ + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) \ + { \ + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToReceive, \ + ( uint32_t ) 0, \ + eNoAction, \ + pxHigherPriorityTaskWoken ); \ + ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; \ + } \ + } \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \ + } +#endif /* sbSEND_COMPLETE_FROM_ISR */ +/*lint -restore (9026) */ + +/* The number of bytes used to hold the length of a message in the buffer. */ +#define sbBYTES_TO_STORE_MESSAGE_LENGTH ( sizeof( configMESSAGE_BUFFER_LENGTH_TYPE ) ) + +/* Bits stored in the ucFlags field of the stream buffer. */ +#define sbFLAGS_IS_MESSAGE_BUFFER ( ( uint8_t ) 1 ) /* Set if the stream buffer was created as a message buffer, in which case it holds discrete messages rather than a stream. */ +#define sbFLAGS_IS_STATICALLY_ALLOCATED ( ( uint8_t ) 2 ) /* Set if the stream buffer was created using statically allocated memory. */ + +/*-----------------------------------------------------------*/ + +/* Structure that hold state information on the buffer. */ +typedef struct StreamBufferDef_t /*lint !e9058 Style convention uses tag. */ +{ + volatile size_t xTail; /* Index to the next item to read within the buffer. */ + volatile size_t xHead; /* Index to the next item to write within the buffer. */ + size_t xLength; /* The length of the buffer pointed to by pucBuffer. */ + size_t xTriggerLevelBytes; /* The number of bytes that must be in the stream buffer before a task that is waiting for data is unblocked. */ + volatile TaskHandle_t xTaskWaitingToReceive; /* Holds the handle of a task waiting for data, or NULL if no tasks are waiting. */ + volatile TaskHandle_t xTaskWaitingToSend; /* Holds the handle of a task waiting to send data to a message buffer that is full. */ + uint8_t *pucBuffer; /* Points to the buffer itself - that is - the RAM that stores the data passed through the buffer. */ + uint8_t ucFlags; + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxStreamBufferNumber; /* Used for tracing purposes. */ + #endif +} StreamBuffer_t; + +/* + * The number of bytes available to be read from the buffer. + */ +static size_t prvBytesInBuffer( const StreamBuffer_t * const pxStreamBuffer ) PRIVILEGED_FUNCTION; + +/* + * Add xCount bytes from pucData into the pxStreamBuffer message buffer. + * Returns the number of bytes written, which will either equal xCount in the + * success case, or 0 if there was not enough space in the buffer (in which case + * no data is written into the buffer). + */ +static size_t prvWriteBytesToBuffer( StreamBuffer_t * const pxStreamBuffer, const uint8_t *pucData, size_t xCount ) PRIVILEGED_FUNCTION; + +/* + * If the stream buffer is being used as a message buffer, then reads an entire + * message out of the buffer. If the stream buffer is being used as a stream + * buffer then read as many bytes as possible from the buffer. + * prvReadBytesFromBuffer() is called to actually extract the bytes from the + * buffer's data storage area. + */ +static size_t prvReadMessageFromBuffer( StreamBuffer_t *pxStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + size_t xBytesAvailable, + size_t xBytesToStoreMessageLength ) PRIVILEGED_FUNCTION; + +/* + * If the stream buffer is being used as a message buffer, then writes an entire + * message to the buffer. If the stream buffer is being used as a stream + * buffer then write as many bytes as possible to the buffer. + * prvWriteBytestoBuffer() is called to actually send the bytes to the buffer's + * data storage area. + */ +static size_t prvWriteMessageToBuffer( StreamBuffer_t * const pxStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + size_t xSpace, + size_t xRequiredSpace ) PRIVILEGED_FUNCTION; + +/* + * Read xMaxCount bytes from the pxStreamBuffer message buffer and write them + * to pucData. + */ +static size_t prvReadBytesFromBuffer( StreamBuffer_t *pxStreamBuffer, + uint8_t *pucData, + size_t xMaxCount, + size_t xBytesAvailable ) PRIVILEGED_FUNCTION; + +/* + * Called by both pxStreamBufferCreate() and pxStreamBufferCreateStatic() to + * initialise the members of the newly created stream buffer structure. + */ +static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer, + uint8_t * const pucBuffer, + size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + uint8_t ucFlags ) PRIVILEGED_FUNCTION; + +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer ) + { + uint8_t *pucAllocatedMemory; + uint8_t ucFlags; + + /* In case the stream buffer is going to be used as a message buffer + (that is, it will hold discrete messages with a little meta data that + says how big the next message is) check the buffer will be large enough + to hold at least one message. */ + if( xIsMessageBuffer == pdTRUE ) + { + /* Is a message buffer but not statically allocated. */ + ucFlags = sbFLAGS_IS_MESSAGE_BUFFER; + configASSERT( xBufferSizeBytes > sbBYTES_TO_STORE_MESSAGE_LENGTH ); + } + else + { + /* Not a message buffer and not statically allocated. */ + ucFlags = 0; + configASSERT( xBufferSizeBytes > 0 ); + } + configASSERT( xTriggerLevelBytes <= xBufferSizeBytes ); + + /* A trigger level of 0 would cause a waiting task to unblock even when + the buffer was empty. */ + if( xTriggerLevelBytes == ( size_t ) 0 ) + { + xTriggerLevelBytes = ( size_t ) 1; + } + + /* A stream buffer requires a StreamBuffer_t structure and a buffer. + Both are allocated in a single call to pvPortMalloc(). The + StreamBuffer_t structure is placed at the start of the allocated memory + and the buffer follows immediately after. The requested size is + incremented so the free space is returned as the user would expect - + this is a quirk of the implementation that means otherwise the free + space would be reported as one byte smaller than would be logically + expected. */ + xBufferSizeBytes++; + pucAllocatedMemory = ( uint8_t * ) pvPortMalloc( xBufferSizeBytes + sizeof( StreamBuffer_t ) ); /*lint !e9079 malloc() only returns void*. */ + + if( pucAllocatedMemory != NULL ) + { + prvInitialiseNewStreamBuffer( ( StreamBuffer_t * ) pucAllocatedMemory, /* Structure at the start of the allocated memory. */ /*lint !e9087 Safe cast as allocated memory is aligned. */ /*lint !e826 Area is not too small and alignment is guaranteed provided malloc() behaves as expected and returns aligned buffer. */ + pucAllocatedMemory + sizeof( StreamBuffer_t ), /* Storage area follows. */ /*lint !e9016 Indexing past structure valid for uint8_t pointer, also storage area has no alignment requirement. */ + xBufferSizeBytes, + xTriggerLevelBytes, + ucFlags ); + + traceSTREAM_BUFFER_CREATE( ( ( StreamBuffer_t * ) pucAllocatedMemory ), xIsMessageBuffer ); + } + else + { + traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer ); + } + + return ( StreamBufferHandle_t ) pucAllocatedMemory; /*lint !e9087 !e826 Safe cast as allocated memory is aligned. */ + } + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + + StreamBufferHandle_t xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer, + uint8_t * const pucStreamBufferStorageArea, + StaticStreamBuffer_t * const pxStaticStreamBuffer ) + { + StreamBuffer_t * const pxStreamBuffer = ( StreamBuffer_t * ) pxStaticStreamBuffer; /*lint !e740 !e9087 Safe cast as StaticStreamBuffer_t is opaque Streambuffer_t. */ + StreamBufferHandle_t xReturn; + uint8_t ucFlags; + + configASSERT( pucStreamBufferStorageArea ); + configASSERT( pxStaticStreamBuffer ); + configASSERT( xTriggerLevelBytes <= xBufferSizeBytes ); + + /* A trigger level of 0 would cause a waiting task to unblock even when + the buffer was empty. */ + if( xTriggerLevelBytes == ( size_t ) 0 ) + { + xTriggerLevelBytes = ( size_t ) 1; + } + + if( xIsMessageBuffer != pdFALSE ) + { + /* Statically allocated message buffer. */ + ucFlags = sbFLAGS_IS_MESSAGE_BUFFER | sbFLAGS_IS_STATICALLY_ALLOCATED; + } + else + { + /* Statically allocated stream buffer. */ + ucFlags = sbFLAGS_IS_STATICALLY_ALLOCATED; + } + + /* In case the stream buffer is going to be used as a message buffer + (that is, it will hold discrete messages with a little meta data that + says how big the next message is) check the buffer will be large enough + to hold at least one message. */ + configASSERT( xBufferSizeBytes > sbBYTES_TO_STORE_MESSAGE_LENGTH ); + + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticStreamBuffer_t equals the size of the real + message buffer structure. */ + volatile size_t xSize = sizeof( StaticStreamBuffer_t ); + configASSERT( xSize == sizeof( StreamBuffer_t ) ); + } /*lint !e529 xSize is referenced is configASSERT() is defined. */ + #endif /* configASSERT_DEFINED */ + + if( ( pucStreamBufferStorageArea != NULL ) && ( pxStaticStreamBuffer != NULL ) ) + { + prvInitialiseNewStreamBuffer( pxStreamBuffer, + pucStreamBufferStorageArea, + xBufferSizeBytes, + xTriggerLevelBytes, + ucFlags ); + + /* Remember this was statically allocated in case it is ever deleted + again. */ + pxStreamBuffer->ucFlags |= sbFLAGS_IS_STATICALLY_ALLOCATED; + + traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xIsMessageBuffer ); + + xReturn = ( StreamBufferHandle_t ) pxStaticStreamBuffer; /*lint !e9087 Data hiding requires cast to opaque type. */ + } + else + { + xReturn = NULL; + traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xIsMessageBuffer ); + } + + return xReturn; + } + +#endif /* ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ +/*-----------------------------------------------------------*/ + +void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) +{ +StreamBuffer_t * pxStreamBuffer = xStreamBuffer; + + configASSERT( pxStreamBuffer ); + + traceSTREAM_BUFFER_DELETE( xStreamBuffer ); + + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) pdFALSE ) + { + #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Both the structure and the buffer were allocated using a single call + to pvPortMalloc(), hence only one call to vPortFree() is required. */ + vPortFree( ( void * ) pxStreamBuffer ); /*lint !e9087 Standard free() semantics require void *, plus pxStreamBuffer was allocated by pvPortMalloc(). */ + } + #else + { + /* Should not be possible to get here, ucFlags must be corrupt. + Force an assert. */ + configASSERT( xStreamBuffer == ( StreamBufferHandle_t ) ~0 ); + } + #endif + } + else + { + /* The structure and buffer were not allocated dynamically and cannot be + freed - just scrub the structure so future use will assert. */ + ( void ) memset( pxStreamBuffer, 0x00, sizeof( StreamBuffer_t ) ); + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +BaseType_t xReturn = pdFAIL; + +#if( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxStreamBufferNumber; +#endif + + configASSERT( pxStreamBuffer ); + + #if( configUSE_TRACE_FACILITY == 1 ) + { + /* Store the stream buffer number so it can be restored after the + reset. */ + uxStreamBufferNumber = pxStreamBuffer->uxStreamBufferNumber; + } + #endif + + /* Can only reset a message buffer if there are no tasks blocked on it. */ + taskENTER_CRITICAL(); + { + if( pxStreamBuffer->xTaskWaitingToReceive == NULL ) + { + if( pxStreamBuffer->xTaskWaitingToSend == NULL ) + { + prvInitialiseNewStreamBuffer( pxStreamBuffer, + pxStreamBuffer->pucBuffer, + pxStreamBuffer->xLength, + pxStreamBuffer->xTriggerLevelBytes, + pxStreamBuffer->ucFlags ); + xReturn = pdPASS; + + #if( configUSE_TRACE_FACILITY == 1 ) + { + pxStreamBuffer->uxStreamBufferNumber = uxStreamBufferNumber; + } + #endif + + traceSTREAM_BUFFER_RESET( xStreamBuffer ); + } + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +BaseType_t xReturn; + + configASSERT( pxStreamBuffer ); + + /* It is not valid for the trigger level to be 0. */ + if( xTriggerLevel == ( size_t ) 0 ) + { + xTriggerLevel = ( size_t ) 1; + } + + /* The trigger level is the number of bytes that must be in the stream + buffer before a task that is waiting for data is unblocked. */ + if( xTriggerLevel <= pxStreamBuffer->xLength ) + { + pxStreamBuffer->xTriggerLevelBytes = xTriggerLevel; + xReturn = pdPASS; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) +{ +const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xSpace; + + configASSERT( pxStreamBuffer ); + + xSpace = pxStreamBuffer->xLength + pxStreamBuffer->xTail; + xSpace -= pxStreamBuffer->xHead; + xSpace -= ( size_t ) 1; + + if( xSpace >= pxStreamBuffer->xLength ) + { + xSpace -= pxStreamBuffer->xLength; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xSpace; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) +{ +const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReturn; + + configASSERT( pxStreamBuffer ); + + xReturn = prvBytesInBuffer( pxStreamBuffer ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, + const void *pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReturn, xSpace = 0; +size_t xRequiredSpace = xDataLengthBytes; +TimeOut_t xTimeOut; + + configASSERT( pvTxData ); + configASSERT( pxStreamBuffer ); + + /* This send function is used to write to both message buffers and stream + buffers. If this is a message buffer then the space needed must be + increased by the amount of bytes needed to store the length of the + message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xRequiredSpace += sbBYTES_TO_STORE_MESSAGE_LENGTH; + + /* Overflow? */ + configASSERT( xRequiredSpace > xDataLengthBytes ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xTicksToWait != ( TickType_t ) 0 ) + { + vTaskSetTimeOutState( &xTimeOut ); + + do + { + /* Wait until the required number of bytes are free in the message + buffer. */ + taskENTER_CRITICAL(); + { + xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); + + if( xSpace < xRequiredSpace ) + { + /* Clear notification state as going to wait for space. */ + ( void ) xTaskNotifyStateClear( NULL ); + + /* Should only be one writer. */ + configASSERT( pxStreamBuffer->xTaskWaitingToSend == NULL ); + pxStreamBuffer->xTaskWaitingToSend = xTaskGetCurrentTaskHandle(); + } + else + { + taskEXIT_CRITICAL(); + break; + } + } + taskEXIT_CRITICAL(); + + traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer ); + ( void ) xTaskNotifyWait( ( uint32_t ) 0, ( uint32_t ) 0, NULL, xTicksToWait ); + pxStreamBuffer->xTaskWaitingToSend = NULL; + + } while( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xSpace == ( size_t ) 0 ) + { + xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = prvWriteMessageToBuffer( pxStreamBuffer, pvTxData, xDataLengthBytes, xSpace, xRequiredSpace ); + + if( xReturn > ( size_t ) 0 ) + { + traceSTREAM_BUFFER_SEND( xStreamBuffer, xReturn ); + + /* Was a task waiting for the data? */ + if( prvBytesInBuffer( pxStreamBuffer ) >= pxStreamBuffer->xTriggerLevelBytes ) + { + sbSEND_COMPLETED( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + traceSTREAM_BUFFER_SEND_FAILED( xStreamBuffer ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, + const void *pvTxData, + size_t xDataLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReturn, xSpace; +size_t xRequiredSpace = xDataLengthBytes; + + configASSERT( pvTxData ); + configASSERT( pxStreamBuffer ); + + /* This send function is used to write to both message buffers and stream + buffers. If this is a message buffer then the space needed must be + increased by the amount of bytes needed to store the length of the + message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xRequiredSpace += sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); + xReturn = prvWriteMessageToBuffer( pxStreamBuffer, pvTxData, xDataLengthBytes, xSpace, xRequiredSpace ); + + if( xReturn > ( size_t ) 0 ) + { + /* Was a task waiting for the data? */ + if( prvBytesInBuffer( pxStreamBuffer ) >= pxStreamBuffer->xTriggerLevelBytes ) + { + sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceSTREAM_BUFFER_SEND_FROM_ISR( xStreamBuffer, xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static size_t prvWriteMessageToBuffer( StreamBuffer_t * const pxStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + size_t xSpace, + size_t xRequiredSpace ) +{ + BaseType_t xShouldWrite; + size_t xReturn; + + if( xSpace == ( size_t ) 0 ) + { + /* Doesn't matter if this is a stream buffer or a message buffer, there + is no space to write. */ + xShouldWrite = pdFALSE; + } + else if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) == ( uint8_t ) 0 ) + { + /* This is a stream buffer, as opposed to a message buffer, so writing a + stream of bytes rather than discrete messages. Write as many bytes as + possible. */ + xShouldWrite = pdTRUE; + xDataLengthBytes = configMIN( xDataLengthBytes, xSpace ); + } + else if( xSpace >= xRequiredSpace ) + { + /* This is a message buffer, as opposed to a stream buffer, and there + is enough space to write both the message length and the message itself + into the buffer. Start by writing the length of the data, the data + itself will be written later in this function. */ + xShouldWrite = pdTRUE; + ( void ) prvWriteBytesToBuffer( pxStreamBuffer, ( const uint8_t * ) &( xDataLengthBytes ), sbBYTES_TO_STORE_MESSAGE_LENGTH ); + } + else + { + /* There is space available, but not enough space. */ + xShouldWrite = pdFALSE; + } + + if( xShouldWrite != pdFALSE ) + { + /* Writes the data itself. */ + xReturn = prvWriteBytesToBuffer( pxStreamBuffer, ( const uint8_t * ) pvTxData, xDataLengthBytes ); /*lint !e9079 Storage buffer is implemented as uint8_t for ease of sizing, alighment and access. */ + } + else + { + xReturn = 0; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReceivedLength = 0, xBytesAvailable, xBytesToStoreMessageLength; + + configASSERT( pvRxData ); + configASSERT( pxStreamBuffer ); + + /* This receive function is used by both message buffers, which store + discrete messages, and stream buffers, which store a continuous stream of + bytes. Discrete messages include an additional + sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the + message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + xBytesToStoreMessageLength = 0; + } + + if( xTicksToWait != ( TickType_t ) 0 ) + { + /* Checking if there is data and clearing the notification state must be + performed atomically. */ + taskENTER_CRITICAL(); + { + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + + /* If this function was invoked by a message buffer read then + xBytesToStoreMessageLength holds the number of bytes used to hold + the length of the next discrete message. If this function was + invoked by a stream buffer read then xBytesToStoreMessageLength will + be 0. */ + if( xBytesAvailable <= xBytesToStoreMessageLength ) + { + /* Clear notification state as going to wait for data. */ + ( void ) xTaskNotifyStateClear( NULL ); + + /* Should only be one reader. */ + configASSERT( pxStreamBuffer->xTaskWaitingToReceive == NULL ); + pxStreamBuffer->xTaskWaitingToReceive = xTaskGetCurrentTaskHandle(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + if( xBytesAvailable <= xBytesToStoreMessageLength ) + { + /* Wait for data to be available. */ + traceBLOCKING_ON_STREAM_BUFFER_RECEIVE( xStreamBuffer ); + ( void ) xTaskNotifyWait( ( uint32_t ) 0, ( uint32_t ) 0, NULL, xTicksToWait ); + pxStreamBuffer->xTaskWaitingToReceive = NULL; + + /* Recheck the data available after blocking. */ + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + } + + /* Whether receiving a discrete message (where xBytesToStoreMessageLength + holds the number of bytes used to store the message length) or a stream of + bytes (where xBytesToStoreMessageLength is zero), the number of bytes + available must be greater than xBytesToStoreMessageLength to be able to + read bytes from the buffer. */ + if( xBytesAvailable > xBytesToStoreMessageLength ) + { + xReceivedLength = prvReadMessageFromBuffer( pxStreamBuffer, pvRxData, xBufferLengthBytes, xBytesAvailable, xBytesToStoreMessageLength ); + + /* Was a task waiting for space in the buffer? */ + if( xReceivedLength != ( size_t ) 0 ) + { + traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength ); + sbRECEIVE_COMPLETED( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + traceSTREAM_BUFFER_RECEIVE_FAILED( xStreamBuffer ); + mtCOVERAGE_TEST_MARKER(); + } + + return xReceivedLength; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReturn, xBytesAvailable, xOriginalTail; +configMESSAGE_BUFFER_LENGTH_TYPE xTempReturn; + + configASSERT( pxStreamBuffer ); + + /* Ensure the stream buffer is being used as a message buffer. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + if( xBytesAvailable > sbBYTES_TO_STORE_MESSAGE_LENGTH ) + { + /* The number of bytes available is greater than the number of bytes + required to hold the length of the next message, so another message + is available. Return its length without removing the length bytes + from the buffer. A copy of the tail is stored so the buffer can be + returned to its prior state as the message is not actually being + removed from the buffer. */ + xOriginalTail = pxStreamBuffer->xTail; + ( void ) prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) &xTempReturn, sbBYTES_TO_STORE_MESSAGE_LENGTH, xBytesAvailable ); + xReturn = ( size_t ) xTempReturn; + pxStreamBuffer->xTail = xOriginalTail; + } + else + { + /* The minimum amount of bytes in a message buffer is + ( sbBYTES_TO_STORE_MESSAGE_LENGTH + 1 ), so if xBytesAvailable is + less than sbBYTES_TO_STORE_MESSAGE_LENGTH the only other valid + value is 0. */ + configASSERT( xBytesAvailable == 0 ); + xReturn = 0; + } + } + else + { + xReturn = 0; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReceivedLength = 0, xBytesAvailable, xBytesToStoreMessageLength; + + configASSERT( pvRxData ); + configASSERT( pxStreamBuffer ); + + /* This receive function is used by both message buffers, which store + discrete messages, and stream buffers, which store a continuous stream of + bytes. Discrete messages include an additional + sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the + message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + xBytesToStoreMessageLength = 0; + } + + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + + /* Whether receiving a discrete message (where xBytesToStoreMessageLength + holds the number of bytes used to store the message length) or a stream of + bytes (where xBytesToStoreMessageLength is zero), the number of bytes + available must be greater than xBytesToStoreMessageLength to be able to + read bytes from the buffer. */ + if( xBytesAvailable > xBytesToStoreMessageLength ) + { + xReceivedLength = prvReadMessageFromBuffer( pxStreamBuffer, pvRxData, xBufferLengthBytes, xBytesAvailable, xBytesToStoreMessageLength ); + + /* Was a task waiting for space in the buffer? */ + if( xReceivedLength != ( size_t ) 0 ) + { + sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceSTREAM_BUFFER_RECEIVE_FROM_ISR( xStreamBuffer, xReceivedLength ); + + return xReceivedLength; +} +/*-----------------------------------------------------------*/ + +static size_t prvReadMessageFromBuffer( StreamBuffer_t *pxStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + size_t xBytesAvailable, + size_t xBytesToStoreMessageLength ) +{ +size_t xOriginalTail, xReceivedLength, xNextMessageLength; +configMESSAGE_BUFFER_LENGTH_TYPE xTempNextMessageLength; + + if( xBytesToStoreMessageLength != ( size_t ) 0 ) + { + /* A discrete message is being received. First receive the length + of the message. A copy of the tail is stored so the buffer can be + returned to its prior state if the length of the message is too + large for the provided buffer. */ + xOriginalTail = pxStreamBuffer->xTail; + ( void ) prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) &xTempNextMessageLength, xBytesToStoreMessageLength, xBytesAvailable ); + xNextMessageLength = ( size_t ) xTempNextMessageLength; + + /* Reduce the number of bytes available by the number of bytes just + read out. */ + xBytesAvailable -= xBytesToStoreMessageLength; + + /* Check there is enough space in the buffer provided by the + user. */ + if( xNextMessageLength > xBufferLengthBytes ) + { + /* The user has provided insufficient space to read the message + so return the buffer to its previous state (so the length of + the message is in the buffer again). */ + pxStreamBuffer->xTail = xOriginalTail; + xNextMessageLength = 0; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* A stream of bytes is being received (as opposed to a discrete + message), so read as many bytes as possible. */ + xNextMessageLength = xBufferLengthBytes; + } + + /* Read the actual data. */ + xReceivedLength = prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) pvRxData, xNextMessageLength, xBytesAvailable ); /*lint !e9079 Data storage area is implemented as uint8_t array for ease of sizing, indexing and alignment. */ + + return xReceivedLength; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) +{ +const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +BaseType_t xReturn; +size_t xTail; + + configASSERT( pxStreamBuffer ); + + /* True if no bytes are available. */ + xTail = pxStreamBuffer->xTail; + if( pxStreamBuffer->xHead == xTail ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) +{ +BaseType_t xReturn; +size_t xBytesToStoreMessageLength; +const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + + configASSERT( pxStreamBuffer ); + + /* This generic version of the receive function is used by both message + buffers, which store discrete messages, and stream buffers, which store a + continuous stream of bytes. Discrete messages include an additional + sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + xBytesToStoreMessageLength = 0; + } + + /* True if the available space equals zero. */ + if( xStreamBufferSpacesAvailable( xStreamBuffer ) <= xBytesToStoreMessageLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; + + configASSERT( pxStreamBuffer ); + + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) + { + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToReceive, + ( uint32_t ) 0, + eNoAction, + pxHigherPriorityTaskWoken ); + ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; + + configASSERT( pxStreamBuffer ); + + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) + { + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToSend, + ( uint32_t ) 0, + eNoAction, + pxHigherPriorityTaskWoken ); + ( pxStreamBuffer )->xTaskWaitingToSend = NULL; + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static size_t prvWriteBytesToBuffer( StreamBuffer_t * const pxStreamBuffer, const uint8_t *pucData, size_t xCount ) +{ +size_t xNextHead, xFirstLength; + + configASSERT( xCount > ( size_t ) 0 ); + + xNextHead = pxStreamBuffer->xHead; + + /* Calculate the number of bytes that can be added in the first write - + which may be less than the total number of bytes that need to be added if + the buffer will wrap back to the beginning. */ + xFirstLength = configMIN( pxStreamBuffer->xLength - xNextHead, xCount ); + + /* Write as many bytes as can be written in the first write. */ + configASSERT( ( xNextHead + xFirstLength ) <= pxStreamBuffer->xLength ); + ( void ) memcpy( ( void* ) ( &( pxStreamBuffer->pucBuffer[ xNextHead ] ) ), ( const void * ) pucData, xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + + /* If the number of bytes written was less than the number that could be + written in the first write... */ + if( xCount > xFirstLength ) + { + /* ...then write the remaining bytes to the start of the buffer. */ + configASSERT( ( xCount - xFirstLength ) <= pxStreamBuffer->xLength ); + ( void ) memcpy( ( void * ) pxStreamBuffer->pucBuffer, ( const void * ) &( pucData[ xFirstLength ] ), xCount - xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xNextHead += xCount; + if( xNextHead >= pxStreamBuffer->xLength ) + { + xNextHead -= pxStreamBuffer->xLength; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxStreamBuffer->xHead = xNextHead; + + return xCount; +} +/*-----------------------------------------------------------*/ + +static size_t prvReadBytesFromBuffer( StreamBuffer_t *pxStreamBuffer, uint8_t *pucData, size_t xMaxCount, size_t xBytesAvailable ) +{ +size_t xCount, xFirstLength, xNextTail; + + /* Use the minimum of the wanted bytes and the available bytes. */ + xCount = configMIN( xBytesAvailable, xMaxCount ); + + if( xCount > ( size_t ) 0 ) + { + xNextTail = pxStreamBuffer->xTail; + + /* Calculate the number of bytes that can be read - which may be + less than the number wanted if the data wraps around to the start of + the buffer. */ + xFirstLength = configMIN( pxStreamBuffer->xLength - xNextTail, xCount ); + + /* Obtain the number of bytes it is possible to obtain in the first + read. Asserts check bounds of read and write. */ + configASSERT( xFirstLength <= xMaxCount ); + configASSERT( ( xNextTail + xFirstLength ) <= pxStreamBuffer->xLength ); + ( void ) memcpy( ( void * ) pucData, ( const void * ) &( pxStreamBuffer->pucBuffer[ xNextTail ] ), xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + + /* If the total number of wanted bytes is greater than the number + that could be read in the first read... */ + if( xCount > xFirstLength ) + { + /*...then read the remaining bytes from the start of the buffer. */ + configASSERT( xCount <= xMaxCount ); + ( void ) memcpy( ( void * ) &( pucData[ xFirstLength ] ), ( void * ) ( pxStreamBuffer->pucBuffer ), xCount - xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Move the tail pointer to effectively remove the data read from + the buffer. */ + xNextTail += xCount; + + if( xNextTail >= pxStreamBuffer->xLength ) + { + xNextTail -= pxStreamBuffer->xLength; + } + + pxStreamBuffer->xTail = xNextTail; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xCount; +} +/*-----------------------------------------------------------*/ + +static size_t prvBytesInBuffer( const StreamBuffer_t * const pxStreamBuffer ) +{ +/* Returns the distance between xTail and xHead. */ +size_t xCount; + + xCount = pxStreamBuffer->xLength + pxStreamBuffer->xHead; + xCount -= pxStreamBuffer->xTail; + if ( xCount >= pxStreamBuffer->xLength ) + { + xCount -= pxStreamBuffer->xLength; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xCount; +} +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer, + uint8_t * const pucBuffer, + size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + uint8_t ucFlags ) +{ + /* Assert here is deliberately writing to the entire buffer to ensure it can + be written to without generating exceptions, and is setting the buffer to a + known value to assist in development/debugging. */ + #if( configASSERT_DEFINED == 1 ) + { + /* The value written just has to be identifiable when looking at the + memory. Don't use 0xA5 as that is the stack fill value and could + result in confusion as to what is actually being observed. */ + const BaseType_t xWriteValue = 0x55; + configASSERT( memset( pucBuffer, ( int ) xWriteValue, xBufferSizeBytes ) == pucBuffer ); + } /*lint !e529 !e438 xWriteValue is only used if configASSERT() is defined. */ + #endif + + ( void ) memset( ( void * ) pxStreamBuffer, 0x00, sizeof( StreamBuffer_t ) ); /*lint !e9087 memset() requires void *. */ + pxStreamBuffer->pucBuffer = pucBuffer; + pxStreamBuffer->xLength = xBufferSizeBytes; + pxStreamBuffer->xTriggerLevelBytes = xTriggerLevelBytes; + pxStreamBuffer->ucFlags = ucFlags; +} + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxStreamBufferGetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer ) + { + return xStreamBuffer->uxStreamBufferNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vStreamBufferSetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer, UBaseType_t uxStreamBufferNumber ) + { + xStreamBuffer->uxStreamBufferNumber = uxStreamBufferNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + uint8_t ucStreamBufferGetStreamBufferType( StreamBufferHandle_t xStreamBuffer ) + { + return ( xStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ); + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c b/Firmware/ThirdParty/FreeRTOS/Source/tasks.c similarity index 77% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c rename to Firmware/ThirdParty/FreeRTOS/Source/tasks.c index 5c68c6a2f..e6130fc61 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/tasks.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* Standard includes. */ #include @@ -80,13 +38,13 @@ task.h is included from an application file. */ #include "FreeRTOS.h" #include "task.h" #include "timers.h" -#include "StackMacros.h" +#include "stack_macros.h" -/* Lint e961 and e750 are suppressed as a MISRA exception justified because the -MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the -header files above, but not in this file, in order to generate the correct -privileged Vs unprivileged linkage and placement. */ -#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ /* Set configUSE_STATS_FORMATTING_FUNCTIONS to 2 to include the stats formatting functions but without including stdio.h here. */ @@ -117,29 +75,24 @@ functions but without including stdio.h here. */ */ #define tskSTACK_FILL_BYTE ( 0xa5U ) -/* Sometimes the FreeRTOSConfig.h settings only allow a task to be created using -dynamically allocated RAM, in which case when any task is deleted it is known -that both the task's stack and TCB need to be freed. Sometimes the -FreeRTOSConfig.h settings only allow a task to be created using statically -allocated RAM, in which case when any task is deleted it is known that neither -the task's stack or TCB should be freed. Sometimes the FreeRTOSConfig.h -settings allow a task to be created using either statically or dynamically -allocated RAM, in which case a member of the TCB is used to record whether the -stack and/or TCB were allocated statically or dynamically, so when a task is -deleted the RAM that was allocated dynamically is freed again and no attempt is -made to free the RAM that was allocated statically. -tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE is only true if it is possible for a -task to be created using either statically or dynamically allocated RAM. Note -that if portUSING_MPU_WRAPPERS is 1 then a protected task can be created with -a statically allocated stack and a dynamically allocated TCB. */ -#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE ( ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) || ( portUSING_MPU_WRAPPERS == 1 ) ) +/* Bits used to recored how a task's stack and TCB were allocated. */ #define tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 0 ) #define tskSTATICALLY_ALLOCATED_STACK_ONLY ( ( uint8_t ) 1 ) #define tskSTATICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 2 ) +/* If any of the following are set then task stacks are filled with a known +value so the high water mark can be determined. If none of the following are +set then don't fill the stack so there is no unnecessary dependency on memset. */ +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 1 +#else + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 0 +#endif + /* * Macros used by vListTask to indicate which state a task is in. */ +#define tskRUNNING_CHAR ( 'X' ) #define tskBLOCKED_CHAR ( 'B' ) #define tskREADY_CHAR ( 'R' ) #define tskDELETED_CHAR ( 'D' ) @@ -153,6 +106,12 @@ a statically allocated stack and a dynamically allocated TCB. */ #define static #endif +/* The name allocated to the Idle task. This can be overridden by defining +configIDLE_TASK_NAME in FreeRTOSConfig.h. */ +#ifndef configIDLE_TASK_NAME + #define configIDLE_TASK_NAME "IDLE" +#endif + #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) /* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 then task selection is @@ -269,7 +228,7 @@ count overflows. */ * task should be used in place of the parameter. This macro simply checks to * see if the parameter is NULL and returns a pointer to the appropriate TCB. */ -#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) pxCurrentTCB : ( TCB_t * ) ( pxHandle ) ) +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) ) /* The item value of the event list item is normally used to hold the priority of the task to which it belongs (coded to allow it to be held in reverse @@ -290,7 +249,7 @@ to its original value when it is released. */ * and stores task state information, including a pointer to the task's context * (the task's run time environment, including register values) */ -typedef struct tskTaskControlBlock +typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ @@ -304,8 +263,8 @@ typedef struct tskTaskControlBlock StackType_t *pxStack; /*< Points to the start of the stack. */ char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - #if ( portSTACK_GROWTH > 0 ) - StackType_t *pxEndOfStack; /*< Points to the end of the stack on architectures where the stack grows up from low memory. */ + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + StackType_t *pxEndOfStack; /*< Points to the highest valid address for the stack. */ #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) @@ -327,7 +286,7 @@ typedef struct tskTaskControlBlock #endif #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) - void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; #endif #if( configGENERATE_RUN_TIME_STATS == 1 ) @@ -350,9 +309,9 @@ typedef struct tskTaskControlBlock volatile uint8_t ucNotifyState; #endif - /* See the comments above the definition of + /* See the comments in FreeRTOS.h with the definition of tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */ #endif @@ -360,49 +319,61 @@ typedef struct tskTaskControlBlock uint8_t ucDelayAborted; #endif + #if( configUSE_POSIX_ERRNO == 1 ) + int iTaskErrno; + #endif + } tskTCB; /* The old tskTCB name is maintained above then typedefed to the new TCB_t name below to enable the use of older kernel aware debuggers. */ typedef tskTCB TCB_t; -/*lint -e956 A manual analysis and inspection has been used to determine which -static variables must be declared volatile. */ - -PRIVILEGED_INITIALIZED_DATA TCB_t * volatile pxCurrentTCB = NULL; - -/* Lists for ready and blocked tasks. --------------------*/ -PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */ -PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */ -PRIVILEGED_DATA static List_t xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ -PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */ -PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ -PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ +/*lint -save -e956 A manual analysis and inspection has been used to determine +which static variables must be declared volatile. */ +__attribute__((used)) PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; + +/* Lists for ready and blocked tasks. -------------------- +xDelayedTaskList1 and xDelayedTaskList2 could be move to function scople but +doing so breaks some kernel aware debuggers and debuggers that rely on removing +the static qualifier. */ +PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ] = { 0 };/*< Prioritised ready tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList1 = { 0 }; /*< Delayed tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList2 = { 0 }; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList = NULL; /*< Points to the delayed task list currently being used. */ +PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList = NULL; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t xPendingReadyList = { 0 }; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ #if( INCLUDE_vTaskDelete == 1 ) - PRIVILEGED_DATA static List_t xTasksWaitingTermination; /*< Tasks that have been deleted - but their memory not yet freed. */ - PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static List_t xTasksWaitingTermination = { 0 }; /*< Tasks that have been deleted - but their memory not yet freed. */ + PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; #endif #if ( INCLUDE_vTaskSuspend == 1 ) - PRIVILEGED_DATA static List_t xSuspendedTaskList; /*< Tasks that are currently suspended. */ + PRIVILEGED_DATA static List_t xSuspendedTaskList = { 0 }; /*< Tasks that are currently suspended. */ + +#endif +/* Global POSIX errno. Its value is changed upon context switching to match +the errno of the currently running task. */ +#if ( configUSE_POSIX_ERRNO == 1 ) + int FreeRTOS_errno = 0; #endif /* Other file private variables. --------------------------------*/ -PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; -PRIVILEGED_INITIALIZED_DATA static volatile TickType_t xTickCount = ( TickType_t ) 0U; -PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; -PRIVILEGED_INITIALIZED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; -PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxPendedTicks = ( UBaseType_t ) 0U; -PRIVILEGED_INITIALIZED_DATA static volatile BaseType_t xYieldPending = pdFALSE; -PRIVILEGED_INITIALIZED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; -PRIVILEGED_INITIALIZED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; -PRIVILEGED_INITIALIZED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. */ -PRIVILEGED_INITIALIZED_DATA static TaskHandle_t xIdleTaskHandle = NULL; /*< Holds the handle of the idle task. The idle task is created automatically when the scheduler is started. */ +PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; +PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; +PRIVILEGED_DATA static volatile UBaseType_t uxPendedTicks = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE; +PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; +PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. */ +PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle = NULL; /*< Holds the handle of the idle task. The idle task is created automatically when the scheduler is started. */ /* Context switches are held pending while the scheduler is suspended. Also, interrupts must not manipulate the xStateListItem of a TCB, or any of the @@ -412,30 +383,38 @@ moves the task's event list item into the xPendingReadyList, ready for the kernel to move the task from the pending ready list into the real ready list when the scheduler is unsuspended. The pending ready list itself can only be accessed from a critical section. */ -PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) pdFALSE; +PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) pdFALSE; #if ( configGENERATE_RUN_TIME_STATS == 1 ) - PRIVILEGED_INITIALIZED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ - PRIVILEGED_INITIALIZED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */ + /* Do not move these variables to function scope as doing so prevents the + code working with debuggers that need to remove the static qualifier. */ + PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ + PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */ #endif -/*lint +e956 */ +/*lint -restore */ /*-----------------------------------------------------------*/ /* Callback function prototypes. --------------------------*/ #if( configCHECK_FOR_STACK_OVERFLOW > 0 ) + extern void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName ); + #endif #if( configUSE_TICK_HOOK > 0 ) - extern void vApplicationTickHook( void ); + + extern void vApplicationTickHook( void ); /*lint !e526 Symbol not defined as it is an application callback. */ + #endif #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - extern void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ); + + extern void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ); /*lint !e526 Symbol not defined as it is an application callback. */ + #endif /* File private functions. --------------------------------*/ @@ -446,14 +425,16 @@ PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( * is in any other state. */ #if ( INCLUDE_vTaskSuspend == 1 ) - PRIVILEGED_FUNCTION static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ); + + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + #endif /* INCLUDE_vTaskSuspend */ /* * Utility to ready all the lists used by the scheduler. This is called * automatically upon the creation of the first task. */ -PRIVILEGED_FUNCTION static void prvInitialiseTaskLists( void ); +static void prvInitialiseTaskLists( void ) PRIVILEGED_FUNCTION; /* * The idle task, which as all tasks is implemented as a never ending loop. @@ -477,7 +458,7 @@ static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); */ #if ( INCLUDE_vTaskDelete == 1 ) - PRIVILEGED_FUNCTION static void prvDeleteTCB( TCB_t *pxTCB ); + static void prvDeleteTCB( TCB_t *pxTCB ) PRIVILEGED_FUNCTION; #endif @@ -486,13 +467,13 @@ static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); * in the list of tasks waiting to be deleted. If so the task is cleaned up * and its TCB deleted. */ -PRIVILEGED_FUNCTION static void prvCheckTasksWaitingTermination( void ); +static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; /* * The currently executing task is entering the Blocked state. Add the task to * either the current or the overflow delayed task list. */ -PRIVILEGED_FUNCTION static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ); +static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ) PRIVILEGED_FUNCTION; /* * Fills an TaskStatus_t structure with information on each task that is @@ -504,7 +485,7 @@ PRIVILEGED_FUNCTION static void prvAddCurrentTaskToDelayedList( TickType_t xTick */ #if ( configUSE_TRACE_FACILITY == 1 ) - PRIVILEGED_FUNCTION static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ); + static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) PRIVILEGED_FUNCTION; #endif @@ -514,7 +495,7 @@ PRIVILEGED_FUNCTION static void prvAddCurrentTaskToDelayedList( TickType_t xTick */ #if ( INCLUDE_xTaskGetHandle == 1 ) - PRIVILEGED_FUNCTION static TCB_t *prvSearchForNameWithinSingleList( List_t *pxList, const char pcNameToQuery[] ); + static TCB_t *prvSearchForNameWithinSingleList( List_t *pxList, const char pcNameToQuery[] ) PRIVILEGED_FUNCTION; #endif @@ -523,9 +504,9 @@ PRIVILEGED_FUNCTION static void prvAddCurrentTaskToDelayedList( TickType_t xTick * This function determines the 'high water mark' of the task stack by * determining how much of the stack remains at the original preset value. */ -#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) - PRIVILEGED_FUNCTION static uint16_t prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ); + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) PRIVILEGED_FUNCTION; #endif @@ -540,7 +521,7 @@ PRIVILEGED_FUNCTION static void prvAddCurrentTaskToDelayedList( TickType_t xTick */ #if ( configUSE_TICKLESS_IDLE != 0 ) - PRIVILEGED_FUNCTION static TickType_t prvGetExpectedIdleTime( void ); + static TickType_t prvGetExpectedIdleTime( void ) PRIVILEGED_FUNCTION; #endif @@ -556,7 +537,7 @@ static void prvResetNextTaskUnblockTime( void ); * Helper function used to pad task names with spaces when printing out * human readable tables of task information. */ - PRIVILEGED_FUNCTION static char *prvWriteNameToBuffer( char *pcBuffer, const char *pcTaskName ); + static char *prvWriteNameToBuffer( char *pcBuffer, const char *pcTaskName ) PRIVILEGED_FUNCTION; #endif @@ -564,32 +545,43 @@ static void prvResetNextTaskUnblockTime( void ); * Called after a Task_t structure has been allocated either statically or * dynamically to fill in the structure's members. */ -PRIVILEGED_FUNCTION static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, - const char * const pcName, +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, TCB_t *pxNewTCB, - const MemoryRegion_t * const xRegions ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION; /* * Called after a new task has been created and initialised to place the task * under the control of the scheduler. */ -PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); +static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) PRIVILEGED_FUNCTION; + +/* + * freertos_tasks_c_additions_init() should only be called if the user definable + * macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is the only macro + * called by the function. + */ +#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + + static void freertos_tasks_c_additions_init( void ) PRIVILEGED_FUNCTION; + +#endif /*-----------------------------------------------------------*/ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, - const char * const pcName, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, StackType_t * const puxStackBuffer, - StaticTask_t * const pxTaskBuffer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + StaticTask_t * const pxTaskBuffer ) { TCB_t *pxNewTCB; TaskHandle_t xReturn; @@ -597,20 +589,32 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); configASSERT( puxStackBuffer != NULL ); configASSERT( pxTaskBuffer != NULL ); + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticTask_t equals the size of the real task + structure. */ + volatile size_t xSize = sizeof( StaticTask_t ); + configASSERT( xSize == sizeof( TCB_t ) ); + ( void ) xSize; /* Prevent lint warning when configASSERT() is not used. */ + } + #endif /* configASSERT_DEFINED */ + + if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ) { /* The memory used for the task's TCB and stack are passed into this function - use them. */ - pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ { /* Tasks can be created statically or dynamically, so note this task was created statically in case the task is later deleted. */ pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; } - #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL ); prvAddNewTaskToReadyList( pxNewTCB ); @@ -626,7 +630,53 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); #endif /* SUPPORT_STATIC_ALLOCATION */ /*-----------------------------------------------------------*/ -#if( portUSING_MPU_WRAPPERS == 1 ) +#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) + { + TCB_t *pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer != NULL ); + configASSERT( pxTaskDefinition->pxTaskBuffer != NULL ); + + if( ( pxTaskDefinition->puxStackBuffer != NULL ) && ( pxTaskDefinition->pxTaskBuffer != NULL ) ) + { + /* Allocate space for the TCB. Where the memory comes from depends + on the implementation of the port malloc function and whether or + not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pxTaskDefinition->pxTaskBuffer; + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + ( uint32_t ) pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + + return xReturn; + } + +#endif /* ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ +/*-----------------------------------------------------------*/ + +#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) { @@ -647,10 +697,14 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); /* Store the stack location in the TCB. */ pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; - /* Tasks can be created statically or dynamically, so note - this task had a statically allocated stack in case it is - later deleted. The TCB was allocated dynamically. */ - pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note + this task had a statically allocated stack in case it is + later deleted. The TCB was allocated dynamically. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, pxTaskDefinition->pcName, @@ -674,11 +728,11 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, - const char * const pcName, - const uint16_t usStackDepth, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const configSTACK_DEPTH_TYPE usStackDepth, void * const pvParameters, UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + TaskHandle_t * const pxCreatedTask ) { TCB_t *pxNewTCB; BaseType_t xReturn; @@ -713,12 +767,12 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); StackType_t *pxStack; /* Allocate space for the stack used by the task being created. */ - pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */ if( pxStack != NULL ) { /* Allocate space for the TCB. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */ if( pxNewTCB != NULL ) { @@ -741,13 +795,13 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); if( pxNewTCB != NULL ) { - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */ { /* Tasks can be created statically or dynamically, so note this task was created dynamically in case it is later deleted. */ pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; } - #endif /* configSUPPORT_STATIC_ALLOCATION */ + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); prvAddNewTaskToReadyList( pxNewTCB ); @@ -765,13 +819,13 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); /*-----------------------------------------------------------*/ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, - const char * const pcName, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, TCB_t *pxNewTCB, - const MemoryRegion_t * const xRegions ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const MemoryRegion_t * const xRegions ) { StackType_t *pxTopOfStack; UBaseType_t x; @@ -791,12 +845,12 @@ UBaseType_t x; #endif /* portUSING_MPU_WRAPPERS == 1 */ /* Avoid dependency on memset() if it is not required. */ - #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + #if( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ) { /* Fill the stack with a known value to assist debugging. */ ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); } - #endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */ + #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */ /* Calculate the top of stack address. This depends on whether the stack grows from high memory to low (as per the 80x86) or vice versa. @@ -804,11 +858,19 @@ UBaseType_t x; by the port. */ #if( portSTACK_GROWTH < 0 ) { - pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); - pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. */ + pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] ); + pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 !e9033 !e9078 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. Checked by assert(). */ /* Check the alignment of the calculated top of stack is correct. */ configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + #if( configRECORD_STACK_HIGH_ADDRESS == 1 ) + { + /* Also record the stack's high address, which may assist + debugging. */ + pxNewTCB->pxEndOfStack = pxTopOfStack; + } + #endif /* configRECORD_STACK_HIGH_ADDRESS */ } #else /* portSTACK_GROWTH */ { @@ -824,26 +886,35 @@ UBaseType_t x; #endif /* portSTACK_GROWTH */ /* Store the task name in the TCB. */ - for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + if( pcName != NULL ) { - pxNewTCB->pcTaskName[ x ] = pcName[ x ]; - - /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than - configMAX_TASK_NAME_LEN characters just in case the memory after the - string is not accessible (extremely unlikely). */ - if( pcName[ x ] == 0x00 ) + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) { - break; - } - else - { - mtCOVERAGE_TEST_MARKER(); + pxNewTCB->pcTaskName[ x ] = pcName[ x ]; + + /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than + configMAX_TASK_NAME_LEN characters just in case the memory after the + string is not accessible (extremely unlikely). */ + if( pcName[ x ] == ( char ) 0x00 ) + { + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } } - } - /* Ensure the name string is terminated in the case that the string length - was greater or equal to configMAX_TASK_NAME_LEN. */ - pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; + /* Ensure the name string is terminated in the case that the string length + was greater or equal to configMAX_TASK_NAME_LEN. */ + pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; + } + else + { + /* The task has not been given a name, so just ensure there is a NULL + terminator when it is read out. */ + pxNewTCB->pcTaskName[ 0 ] = 0x00; + } /* This is used as an array index so must ensure it's not too large. First remove the privilege bit if one is present. */ @@ -936,18 +1007,56 @@ UBaseType_t x; /* Initialize the TCB stack to look as if the task was already running, but had been interrupted by the scheduler. The return address is set to the start of the task function. Once the stack has been initialised - the top of stack variable is updated. */ + the top of stack variable is updated. */ #if( portUSING_MPU_WRAPPERS == 1 ) { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + /* If the port has capability to detect stack overflow, + pass the stack end address to the stack initialization + function as well. */ + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ } #else /* portUSING_MPU_WRAPPERS */ { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + /* If the port has capability to detect stack overflow, + pass the stack end address to the stack initialization + function as well. */ + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ } #endif /* portUSING_MPU_WRAPPERS */ - if( ( void * ) pxCreatedTask != NULL ) + if( pxCreatedTask != NULL ) { /* Pass the handle out in an anonymous way. The handle can be used to change the created task's priority, delete the created task, etc.*/ @@ -1264,13 +1373,13 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) #endif /* INCLUDE_vTaskDelay */ /*-----------------------------------------------------------*/ -#if( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) ) +#if( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_xTaskAbortDelay == 1 ) ) eTaskState eTaskGetState( TaskHandle_t xTask ) { eTaskState eReturn; - List_t *pxStateList; - const TCB_t * const pxTCB = ( TCB_t * ) xTask; + List_t const * pxStateList, *pxDelayedList, *pxOverflowedDelayedList; + const TCB_t * const pxTCB = xTask; configASSERT( pxTCB ); @@ -1283,11 +1392,13 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) { taskENTER_CRITICAL(); { - pxStateList = ( List_t * ) listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); + pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); + pxDelayedList = pxDelayedTaskList; + pxOverflowedDelayedList = pxOverflowDelayedTaskList; } taskEXIT_CRITICAL(); - if( ( pxStateList == pxDelayedTaskList ) || ( pxStateList == pxOverflowDelayedTaskList ) ) + if( ( pxStateList == pxDelayedList ) || ( pxStateList == pxOverflowedDelayedList ) ) { /* The task being queried is referenced from one of the Blocked lists. */ @@ -1298,11 +1409,30 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) else if( pxStateList == &xSuspendedTaskList ) { /* The task being queried is referenced from the suspended - list. Is it genuinely suspended or is it block + list. Is it genuinely suspended or is it blocked indefinitely? */ if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ) { - eReturn = eSuspended; + #if( configUSE_TASK_NOTIFICATIONS == 1 ) + { + /* The task does not appear on the event list item of + and of the RTOS objects, but could still be in the + blocked state if it is waiting on its notification + rather than waiting on an object. */ + if( pxTCB->ucNotifyState == taskWAITING_NOTIFICATION ) + { + eReturn = eBlocked; + } + else + { + eReturn = eSuspended; + } + } + #else + { + eReturn = eSuspended; + } + #endif } else { @@ -1337,15 +1467,15 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) #if ( INCLUDE_uxTaskPriorityGet == 1 ) - UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ) + UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) { - TCB_t *pxTCB; + TCB_t const *pxTCB; UBaseType_t uxReturn; taskENTER_CRITICAL(); { - /* If null is passed in here then it is the priority of the that - called uxTaskPriorityGet() that is being queried. */ + /* If null is passed in here then it is the priority of the task + that called uxTaskPriorityGet() that is being queried. */ pxTCB = prvGetTCBFromHandle( xTask ); uxReturn = pxTCB->uxPriority; } @@ -1359,9 +1489,9 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) #if ( INCLUDE_uxTaskPriorityGet == 1 ) - UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask ) + UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) { - TCB_t *pxTCB; + TCB_t const *pxTCB; UBaseType_t uxReturn, uxSavedInterruptState; /* RTOS ports that support interrupt nesting have the concept of a @@ -1379,7 +1509,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) separate interrupt safe API to ensure interrupt entry is as fast and as simple as possible. More information (albeit Cortex-M specific) is provided on the following link: - http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + https://www.freertos.org/RTOS-Cortex-M3-M4.html */ portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); uxSavedInterruptState = portSET_INTERRUPT_MASK_FROM_ISR(); @@ -1515,14 +1645,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) } /* If the task is in the blocked or suspended list we need do - nothing more than change it's priority variable. However, if + nothing more than change its priority variable. However, if the task is in a ready list it needs to be removed and placed in the list appropriate to its new priority. */ if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) { - /* The task is currently in its ready list - remove before adding - it to it's new ready list. As we are in a critical section we - can do this even if the scheduler is suspended. */ + /* The task is currently in its ready list - remove before + adding it to it's new ready list. As we are in a critical + section we can do this even if the scheduler is suspended. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { /* It is known that the task is in its ready list so @@ -1597,6 +1727,17 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) } vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); + + #if( configUSE_TASK_NOTIFICATIONS == 1 ) + { + if( pxTCB->ucNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task was blocked to wait for a notification, but is + now suspended, so no notification was received. */ + pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; + } + } + #endif } taskEXIT_CRITICAL(); @@ -1628,7 +1769,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) /* The scheduler is not running, but the task that was pointed to by pxCurrentTCB has just been suspended and pxCurrentTCB must be adjusted to point to a different task. */ - if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) + if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */ { /* No other tasks are ready, so set pxCurrentTCB back to NULL so when the next task is created pxCurrentTCB will @@ -1656,7 +1797,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) { BaseType_t xReturn = pdFALSE; - const TCB_t * const pxTCB = ( TCB_t * ) xTask; + const TCB_t * const pxTCB = xTask; /* Accesses xPendingReadyList so must be called from a critical section. */ @@ -1672,7 +1813,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) { /* Is it in the suspended list because it is in the Suspended state, or because is is blocked with no timeout? */ - if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) + if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) /*lint !e961. The cast is only redundant when NULL is used. */ { xReturn = pdTRUE; } @@ -1701,14 +1842,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) void vTaskResume( TaskHandle_t xTaskToResume ) { - TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume; + TCB_t * const pxTCB = xTaskToResume; /* It does not make sense to resume the calling task. */ configASSERT( xTaskToResume ); /* The parameter cannot be NULL as it is impossible to resume the currently executing task. */ - if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) ) + if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) ) { taskENTER_CRITICAL(); { @@ -1716,12 +1857,12 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) { traceTASK_RESUME( pxTCB ); - /* As we are in a critical section we can access the ready - lists even if the scheduler is suspended. */ + /* The ready list can be accessed even if the scheduler is + suspended because this is inside a critical section. */ ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); prvAddTaskToReadyList( pxTCB ); - /* We may have just resumed a higher priority task. */ + /* A higher priority task may have just been resumed. */ if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) { /* This yield may not cause the task just resumed to run, @@ -1756,7 +1897,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) { BaseType_t xYieldRequired = pdFALSE; - TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume; + TCB_t * const pxTCB = xTaskToResume; UBaseType_t uxSavedInterruptStatus; configASSERT( xTaskToResume ); @@ -1776,7 +1917,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) separate interrupt safe API to ensure interrupt entry is as fast and as simple as possible. More information (albeit Cortex-M specific) is provided on the following link: - http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + https://www.freertos.org/RTOS-Cortex-M3-M4.html */ portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); @@ -1838,10 +1979,10 @@ BaseType_t xReturn; address of the RAM then create the idle task. */ vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize ); xIdleTaskHandle = xTaskCreateStatic( prvIdleTask, - "IDLE", + configIDLE_TASK_NAME, ulIdleTaskStackSize, - ( void * ) NULL, - ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), + ( void * ) NULL, /*lint !e961. The cast is not redundant for all compilers. */ + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ pxIdleTaskStackBuffer, pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ @@ -1858,9 +1999,10 @@ BaseType_t xReturn; { /* The Idle task is being created using dynamically allocated RAM. */ xReturn = xTaskCreate( prvIdleTask, - "IDLE", configMINIMAL_STACK_SIZE, + configIDLE_TASK_NAME, + configMINIMAL_STACK_SIZE, ( void * ) NULL, - ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ } #endif /* configSUPPORT_STATIC_ALLOCATION */ @@ -1880,6 +2022,15 @@ BaseType_t xReturn; if( xReturn == pdPASS ) { + /* freertos_tasks_c_additions_init() should only be called if the user + definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is + the only macro called by the function. */ + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + { + freertos_tasks_c_additions_init(); + } + #endif + /* Interrupts are turned off here, to ensure a tick does not occur before or during the call to xPortStartScheduler(). The stacks of the created tasks contain a status word with interrupts switched on @@ -1897,13 +2048,18 @@ BaseType_t xReturn; xNextTaskUnblockTime = portMAX_DELAY; xSchedulerRunning = pdTRUE; - xTickCount = ( TickType_t ) 0U; + xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; /* If configGENERATE_RUN_TIME_STATS is defined then the following macro must be defined to configure the timer/counter used to generate - the run time counter time base. */ + the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS + is set to 0 and the following line fails to build then ensure you do not + have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your + FreeRTOSConfig.h file. */ portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); + traceTASK_SWITCHED_IN(); + /* Setting up the timer tick is hardware specific and thus in the portable interface. */ if( xPortStartScheduler() != pdFALSE ) @@ -1948,6 +2104,7 @@ void vTaskSuspendAll( void ) post in the FreeRTOS support forum before reporting this as a bug! - http://goo.gl/wu4acr */ ++uxSchedulerSuspended; + portMEMORY_BARRIER(); } /*----------------------------------------------------------*/ @@ -2040,7 +2197,7 @@ BaseType_t xAlreadyYielded = pdFALSE; appropriate ready list. */ while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) { - pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); prvAddTaskToReadyList( pxTCB ); @@ -2157,7 +2314,7 @@ UBaseType_t uxSavedInterruptStatus; system call interrupt priority. FreeRTOS maintains a separate interrupt safe API to ensure interrupt entry is as fast and as simple as possible. More information (albeit Cortex-M specific) is provided on the following - link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + link: https://www.freertos.org/RTOS-Cortex-M3-M4.html */ portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); uxSavedInterruptStatus = portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR(); @@ -2197,19 +2354,21 @@ TCB_t *pxTCB; TCB_t *pxNextTCB, *pxFirstTCB, *pxReturn = NULL; UBaseType_t x; char cNextChar; + BaseType_t xBreakLoop; /* This function is called with the scheduler suspended. */ if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) { - listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ do { - listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ /* Check each character in the name looking for a match or mismatch. */ + xBreakLoop = pdFALSE; for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) { cNextChar = pxNextTCB->pcTaskName[ x ]; @@ -2217,19 +2376,24 @@ TCB_t *pxTCB; if( cNextChar != pcNameToQuery[ x ] ) { /* Characters didn't match. */ - break; + xBreakLoop = pdTRUE; } - else if( cNextChar == 0x00 ) + else if( cNextChar == ( char ) 0x00 ) { /* Both strings terminated, a match must have been found. */ pxReturn = pxNextTCB; - break; + xBreakLoop = pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } + + if( xBreakLoop != pdFALSE ) + { + break; + } } if( pxReturn != NULL ) @@ -2310,7 +2474,7 @@ TCB_t *pxTCB; } ( void ) xTaskResumeAll(); - return ( TaskHandle_t ) pxTCB; + return pxTCB; } #endif /* INCLUDE_xTaskGetHandle */ @@ -2426,8 +2590,8 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) { - TCB_t *pxTCB = ( TCB_t * ) xTask; - BaseType_t xReturn = pdFALSE; + TCB_t *pxTCB = xTask; + BaseType_t xReturn; configASSERT( pxTCB ); @@ -2437,6 +2601,8 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than it is actually in the Blocked state. */ if( eTaskGetState( xTask ) == eBlocked ) { + xReturn = pdPASS; + /* Remove the reference to the task from the blocked list. An interrupt won't touch the xStateListItem because the scheduler is suspended. */ @@ -2485,10 +2651,10 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than } else { - mtCOVERAGE_TEST_MARKER(); + xReturn = pdFAIL; } } - xTaskResumeAll(); + ( void ) xTaskResumeAll(); return xReturn; } @@ -2510,13 +2676,13 @@ BaseType_t xSwitchRequired = pdFALSE; { /* Minor optimisation. The tick count cannot change in this block. */ - const TickType_t xConstTickCount = xTickCount + 1; + const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1; /* Increment the RTOS tick, switching the delayed and overflowed delayed lists if it wraps to 0. */ xTickCount = xConstTickCount; - if( xConstTickCount == ( TickType_t ) 0U ) + if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */ { taskSWITCH_DELAYED_LISTS(); } @@ -2549,7 +2715,7 @@ BaseType_t xSwitchRequired = pdFALSE; item at the head of the delayed list. This is the time at which the task at the head of the delayed list must be removed from the Blocked state. */ - pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) ); if( xConstTickCount < xItemValue ) @@ -2560,7 +2726,7 @@ BaseType_t xSwitchRequired = pdFALSE; state - so record the item value in xNextTaskUnblockTime. */ xNextTaskUnblockTime = xItemValue; - break; + break; /*lint !e9011 Code structure here is deedmed easier to understand with multiple breaks. */ } else { @@ -2682,13 +2848,15 @@ BaseType_t xSwitchRequired = pdFALSE; } else { - xTCB = ( TCB_t * ) xTask; + xTCB = xTask; } /* Save the hook function in the TCB. A critical section is required as the value can be accessed from an interrupt. */ taskENTER_CRITICAL(); + { xTCB->pxTaskTag = pxHookFunction; + } taskEXIT_CRITICAL(); } @@ -2699,24 +2867,17 @@ BaseType_t xSwitchRequired = pdFALSE; TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) { - TCB_t *xTCB; + TCB_t *pxTCB; TaskHookFunction_t xReturn; - /* If xTask is NULL then we are setting our own task hook. */ - if( xTask == NULL ) - { - xTCB = ( TCB_t * ) pxCurrentTCB; - } - else - { - xTCB = ( TCB_t * ) xTask; - } + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); /* Save the hook function in the TCB. A critical section is required as the value can be accessed from an interrupt. */ taskENTER_CRITICAL(); { - xReturn = xTCB->pxTaskTag; + xReturn = pxTCB->pxTaskTag; } taskEXIT_CRITICAL(); @@ -2726,6 +2887,31 @@ BaseType_t xSwitchRequired = pdFALSE; #endif /* configUSE_APPLICATION_TASK_TAG */ /*-----------------------------------------------------------*/ +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + TaskHookFunction_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + xReturn = pxTCB->pxTaskTag; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) @@ -2736,11 +2922,11 @@ BaseType_t xSwitchRequired = pdFALSE; /* If xTask is NULL then we are calling our own task hook. */ if( xTask == NULL ) { - xTCB = ( TCB_t * ) pxCurrentTCB; + xTCB = pxCurrentTCB; } else { - xTCB = ( TCB_t * ) xTask; + xTCB = xTask; } if( xTCB->pxTaskTag != NULL ) @@ -2773,39 +2959,53 @@ void vTaskSwitchContext( void ) #if ( configGENERATE_RUN_TIME_STATS == 1 ) { - #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE - portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime ); - #else - ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); - #endif + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime ); + #else + ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif - /* Add the amount of time the task has been running to the - accumulated time so far. The time the task started running was - stored in ulTaskSwitchedInTime. Note that there is no overflow - protection here so count values are only valid until the timer - overflows. The guard against negative values is to protect - against suspect run time stat counter implementations - which - are provided by the application, not the kernel. */ - if( ulTotalRunTime > ulTaskSwitchedInTime ) - { - pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - ulTaskSwitchedInTime = ulTotalRunTime; + /* Add the amount of time the task has been running to the + accumulated time so far. The time the task started running was + stored in ulTaskSwitchedInTime. Note that there is no overflow + protection here so count values are only valid until the timer + overflows. The guard against negative values is to protect + against suspect run time stat counter implementations - which + are provided by the application, not the kernel. */ + if( ulTotalRunTime > ulTaskSwitchedInTime ) + { + pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + ulTaskSwitchedInTime = ulTotalRunTime; } #endif /* configGENERATE_RUN_TIME_STATS */ /* Check for stack overflow, if configured. */ taskCHECK_FOR_STACK_OVERFLOW(); + /* Before the currently running task is switched out, save its errno. */ + #if( configUSE_POSIX_ERRNO == 1 ) + { + pxCurrentTCB->iTaskErrno = FreeRTOS_errno; + } + #endif + /* Select a new task to run using either the generic C or port optimised asm code. */ - taskSELECT_HIGHEST_PRIORITY_TASK(); + taskSELECT_HIGHEST_PRIORITY_TASK(); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ traceTASK_SWITCHED_IN(); + /* After the new task is switched in, update the global errno. */ + #if( configUSE_POSIX_ERRNO == 1 ) + { + FreeRTOS_errno = pxCurrentTCB->iTaskErrno; + } + #endif + #if ( configUSE_NEWLIB_REENTRANT == 1 ) { /* Switch Newlib's _impure_ptr variable to point to the _reent @@ -2909,7 +3109,7 @@ BaseType_t xReturn; This function assumes that a check has already been made to ensure that pxEventList is not empty. */ - pxUnblockedTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ configASSERT( pxUnblockedTCB ); ( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) ); @@ -2917,6 +3117,20 @@ BaseType_t xReturn; { ( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) ); prvAddTaskToReadyList( pxUnblockedTCB ); + + #if( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked on a kernel object then xNextTaskUnblockTime + might be set to the blocked task's time out time. If the task is + unblocked for a reason other than a timeout xNextTaskUnblockTime is + normally left unchanged, because it is automatically reset to a new + value when the tick count equals xNextTaskUnblockTime. However if + tickless idling is used it might be more important to enter sleep mode + at the earliest possible time - so reset xNextTaskUnblockTime here to + ensure it is updated at the earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif } else { @@ -2941,28 +3155,13 @@ BaseType_t xReturn; xReturn = pdFALSE; } - #if( configUSE_TICKLESS_IDLE != 0 ) - { - /* If a task is blocked on a kernel object then xNextTaskUnblockTime - might be set to the blocked task's time out time. If the task is - unblocked for a reason other than a timeout xNextTaskUnblockTime is - normally left unchanged, because it is automatically reset to a new - value when the tick count equals xNextTaskUnblockTime. However if - tickless idling is used it might be more important to enter sleep mode - at the earliest possible time - so reset xNextTaskUnblockTime here to - ensure it is updated at the earliest possible time. */ - prvResetNextTaskUnblockTime(); - } - #endif - return xReturn; } /*-----------------------------------------------------------*/ -BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) { TCB_t *pxUnblockedTCB; -BaseType_t xReturn; /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by the event flags implementation. */ @@ -2973,7 +3172,7 @@ BaseType_t xReturn; /* Remove the event list form the event flag. Interrupts do not access event flags. */ - pxUnblockedTCB = ( TCB_t * ) listGET_LIST_ITEM_OWNER( pxEventListItem ); + pxUnblockedTCB = listGET_LIST_ITEM_OWNER( pxEventListItem ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ configASSERT( pxUnblockedTCB ); ( void ) uxListRemove( pxEventListItem ); @@ -2985,28 +3184,30 @@ BaseType_t xReturn; if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) { - /* Return true if the task removed from the event list has - a higher priority than the calling task. This allows - the calling task to know if it should force a context - switch now. */ - xReturn = pdTRUE; - - /* Mark that a yield is pending in case the user is not using the - "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + /* The unblocked task has a priority above that of the calling task, so + a context switch is required. This function is called with the + scheduler suspended so xYieldPending is set so the context switch + occurs immediately that the scheduler is resumed (unsuspended). */ xYieldPending = pdTRUE; } - else - { - xReturn = pdFALSE; - } - - return xReturn; } /*-----------------------------------------------------------*/ void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) { configASSERT( pxTimeOut ); + taskENTER_CRITICAL(); + { + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) +{ + /* For internal use only as it does not use a critical section. */ pxTimeOut->xOverflowCount = xNumOfOverflows; pxTimeOut->xTimeOnEntering = xTickCount; } @@ -3023,9 +3224,10 @@ BaseType_t xReturn; { /* Minor optimisation. The tick count cannot change in this block. */ const TickType_t xConstTickCount = xTickCount; + const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering; #if( INCLUDE_xTaskAbortDelay == 1 ) - if( pxCurrentTCB->ucDelayAborted != pdFALSE ) + if( pxCurrentTCB->ucDelayAborted != ( uint8_t ) pdFALSE ) { /* The delay was aborted, which is not the same as a time out, but has the same result. */ @@ -3055,15 +3257,16 @@ BaseType_t xReturn; was called. */ xReturn = pdTRUE; } - else if( ( ( TickType_t ) ( xConstTickCount - pxTimeOut->xTimeOnEntering ) ) < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */ + else if( xElapsedTime < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */ { /* Not a genuine timeout. Adjust parameters for time remaining. */ - *pxTicksToWait -= ( xConstTickCount - pxTimeOut->xTimeOnEntering ); - vTaskSetTimeOutState( pxTimeOut ); + *pxTicksToWait -= xElapsedTime; + vTaskInternalSetTimeOutState( pxTimeOut ); xReturn = pdFALSE; } else { + *pxTicksToWait = 0; xReturn = pdTRUE; } } @@ -3084,11 +3287,11 @@ void vTaskMissedYield( void ) UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) { UBaseType_t uxReturn; - TCB_t *pxTCB; + TCB_t const *pxTCB; if( xTask != NULL ) { - pxTCB = ( TCB_t * ) xTask; + pxTCB = xTask; uxReturn = pxTCB->uxTaskNumber; } else @@ -3106,11 +3309,11 @@ void vTaskMissedYield( void ) void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) { - TCB_t *pxTCB; + TCB_t * pxTCB; if( xTask != NULL ) { - pxTCB = ( TCB_t * ) xTask; + pxTCB = xTask; pxTCB->uxTaskNumber = uxHandle; } } @@ -3136,6 +3339,11 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) /** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE SCHEDULER IS STARTED. **/ + /* In case a task that has a secure context deletes itself, in which case + the idle task is responsible for deleting the task's secure context, if + any. */ + portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE ); + for( ;; ) { /* See if any tasks have deleted themselves - if so then the idle task @@ -3212,6 +3420,11 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) configASSERT( xNextTaskUnblockTime >= xTickCount ); xExpectedIdleTime = prvGetExpectedIdleTime(); + /* Define the following macro to set xExpectedIdleTime to 0 + if the application does not want + portSUPPRESS_TICKS_AND_SLEEP() to be called. */ + configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime ); + if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { traceLOW_POWER_IDLE_BEGIN(); @@ -3369,37 +3582,22 @@ static void prvCheckTasksWaitingTermination( void ) #if ( INCLUDE_vTaskDelete == 1 ) { - BaseType_t xListIsEmpty; + TCB_t *pxTCB; - /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called - too often in the idle task. */ + /* uxDeletedTasksWaitingCleanUp is used to prevent taskENTER_CRITICAL() + being called too often in the idle task. */ while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ) { - vTaskSuspendAll(); + taskENTER_CRITICAL(); { - xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + --uxCurrentNumberOfTasks; + --uxDeletedTasksWaitingCleanUp; } - ( void ) xTaskResumeAll(); - - if( xListIsEmpty == pdFALSE ) - { - TCB_t *pxTCB; - - taskENTER_CRITICAL(); - { - pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - --uxCurrentNumberOfTasks; - --uxDeletedTasksWaitingCleanUp; - } - taskEXIT_CRITICAL(); + taskEXIT_CRITICAL(); - prvDeleteTCB( pxTCB ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + prvDeleteTCB( pxTCB ); } } #endif /* INCLUDE_vTaskDelete */ @@ -3421,25 +3619,6 @@ static void prvCheckTasksWaitingTermination( void ) pxTaskStatus->pxStackBase = pxTCB->pxStack; pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber; - #if ( INCLUDE_vTaskSuspend == 1 ) - { - /* If the task is in the suspended list then there is a chance it is - actually just blocked indefinitely - so really it should be reported as - being in the Blocked state. */ - if( pxTaskStatus->eCurrentState == eSuspended ) - { - vTaskSuspendAll(); - { - if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) - { - pxTaskStatus->eCurrentState = eBlocked; - } - } - xTaskResumeAll(); - } - } - #endif /* INCLUDE_vTaskSuspend */ - #if ( configUSE_MUTEXES == 1 ) { pxTaskStatus->uxBasePriority = pxTCB->uxBasePriority; @@ -3460,16 +3639,42 @@ static void prvCheckTasksWaitingTermination( void ) } #endif - /* Obtaining the task state is a little fiddly, so is only done if the value - of eState passed into this function is eInvalid - otherwise the state is - just set to whatever is passed in. */ + /* Obtaining the task state is a little fiddly, so is only done if the + value of eState passed into this function is eInvalid - otherwise the + state is just set to whatever is passed in. */ if( eState != eInvalid ) { - pxTaskStatus->eCurrentState = eState; + if( pxTCB == pxCurrentTCB ) + { + pxTaskStatus->eCurrentState = eRunning; + } + else + { + pxTaskStatus->eCurrentState = eState; + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* If the task is in the suspended list then there is a + chance it is actually just blocked indefinitely - so really + it should be reported as being in the Blocked state. */ + if( eState == eSuspended ) + { + vTaskSuspendAll(); + { + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + pxTaskStatus->eCurrentState = eBlocked; + } + } + ( void ) xTaskResumeAll(); + } + } + #endif /* INCLUDE_vTaskSuspend */ + } } else { - pxTaskStatus->eCurrentState = eTaskGetState( xTask ); + pxTaskStatus->eCurrentState = eTaskGetState( pxTCB ); } /* Obtaining the stack space takes some time, so the xGetFreeStackSpace @@ -3499,12 +3704,12 @@ static void prvCheckTasksWaitingTermination( void ) static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) { - volatile TCB_t *pxNextTCB, *pxFirstTCB; + configLIST_VOLATILE TCB_t *pxNextTCB, *pxFirstTCB; UBaseType_t uxTask = 0; if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) { - listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ /* Populate an TaskStatus_t structure within the pxTaskStatusArray array for each task that is referenced from @@ -3512,7 +3717,7 @@ static void prvCheckTasksWaitingTermination( void ) meaning of each TaskStatus_t structure member. */ do { - listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ vTaskGetInfo( ( TaskHandle_t ) pxNextTCB, &( pxTaskStatusArray[ uxTask ] ), pdTRUE, eState ); uxTask++; } while( pxNextTCB != pxFirstTCB ); @@ -3528,9 +3733,9 @@ static void prvCheckTasksWaitingTermination( void ) #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ -#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) - static uint16_t prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) { uint32_t ulCount = 0U; @@ -3542,10 +3747,50 @@ static void prvCheckTasksWaitingTermination( void ) ulCount /= ( uint32_t ) sizeof( StackType_t ); /*lint !e961 Casting is not redundant on smaller architectures. */ - return ( uint16_t ) ulCount; + return ( configSTACK_DEPTH_TYPE ) ulCount; + } + +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) + + /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + same except for their return type. Using configSTACK_DEPTH_TYPE allows the + user to determine the return type. It gets around the problem of the value + overflowing on 8-bit types without breaking backward compatibility for + applications that expect an 8-bit return type. */ + configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + uint8_t *pucEndOfStack; + configSTACK_DEPTH_TYPE uxReturn; + + /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are + the same except for their return type. Using configSTACK_DEPTH_TYPE + allows the user to determine the return type. It gets around the + problem of the value overflowing on 8-bit types without breaking + backward compatibility for applications that expect an 8-bit return + type. */ + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = prvTaskCheckFreeStackSpace( pucEndOfStack ); + + return uxReturn; } -#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) */ +#endif /* INCLUDE_uxTaskGetStackHighWaterMark2 */ /*-----------------------------------------------------------*/ #if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) @@ -3600,7 +3845,7 @@ static void prvCheckTasksWaitingTermination( void ) vPortFree( pxTCB->pxStack ); vPortFree( pxTCB ); } - #elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 ) + #elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ { /* The task could have been allocated statically or dynamically, so check what was statically allocated before trying to free the @@ -3622,7 +3867,7 @@ static void prvCheckTasksWaitingTermination( void ) { /* Neither the stack nor the TCB were allocated dynamically, so nothing needs to be freed. */ - configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ) + configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ); mtCOVERAGE_TEST_MARKER(); } } @@ -3650,7 +3895,7 @@ TCB_t *pxTCB; the item at the head of the delayed list. This is the time at which the task at the head of the delayed list should be removed from the Blocked state. */ - ( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); + ( pxTCB ) = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) ); } } @@ -3703,25 +3948,27 @@ TCB_t *pxTCB; #if ( configUSE_MUTEXES == 1 ) - void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) + BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) { - TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder; + TCB_t * const pxMutexHolderTCB = pxMutexHolder; + BaseType_t xReturn = pdFALSE; /* If the mutex was given back by an interrupt while the queue was - locked then the mutex holder might now be NULL. */ + locked then the mutex holder might now be NULL. _RB_ Is this still + needed as interrupts can no longer use mutexes? */ if( pxMutexHolder != NULL ) { /* If the holder of the mutex has a priority below the priority of the task attempting to obtain the mutex then it will temporarily inherit the priority of the task attempting to obtain the mutex. */ - if( pxTCB->uxPriority < pxCurrentTCB->uxPriority ) + if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority ) { /* Adjust the mutex holder state to account for its new priority. Only reset the event list item value if the value is - not being used for anything else. */ - if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + not being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) { - listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ } else { @@ -3730,11 +3977,11 @@ TCB_t *pxTCB; /* If the task being modified is in the ready state it will need to be moved into a new list. */ - if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE ) { - if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { - taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + taskRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority ); } else { @@ -3742,26 +3989,45 @@ TCB_t *pxTCB; } /* Inherit the priority before being moved into the new list. */ - pxTCB->uxPriority = pxCurrentTCB->uxPriority; - prvAddTaskToReadyList( pxTCB ); + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; + prvAddTaskToReadyList( pxMutexHolderTCB ); } else { /* Just inherit the priority. */ - pxTCB->uxPriority = pxCurrentTCB->uxPriority; + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; } - traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority ); + traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority ); + + /* Inheritance occurred. */ + xReturn = pdTRUE; } else { - mtCOVERAGE_TEST_MARKER(); + if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority ) + { + /* The base priority of the mutex holder is lower than the + priority of the task attempting to take the mutex, but the + current priority of the mutex holder is not lower than the + priority of the task attempting to take the mutex. + Therefore the mutex holder must have already inherited a + priority, but inheritance would have occurred if that had + not been the case. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } } } else { mtCOVERAGE_TEST_MARKER(); } + + return xReturn; } #endif /* configUSE_MUTEXES */ @@ -3771,7 +4037,7 @@ TCB_t *pxTCB; BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) { - TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder; + TCB_t * const pxTCB = pxMutexHolder; BaseType_t xReturn = pdFALSE; if( pxMutexHolder != NULL ) @@ -3781,7 +4047,6 @@ TCB_t *pxTCB; interrupt, and if a mutex is given by the holding task then it must be the running state task. */ configASSERT( pxTCB == pxCurrentTCB ); - configASSERT( pxTCB->uxMutexesHeld ); ( pxTCB->uxMutexesHeld )--; @@ -3795,8 +4060,8 @@ TCB_t *pxTCB; /* A task can only have an inherited priority if it holds the mutex. If the mutex is held by a task then it cannot be given from an interrupt, and if a mutex is given by the - holding task then it must be the running state task. Remove - the holding task from the ready list. */ + holding task then it must be the running state task. Remove + the holding task from the ready list. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { taskRESET_READY_PRIORITY( pxTCB->uxPriority ); @@ -3848,6 +4113,108 @@ TCB_t *pxTCB; #endif /* configUSE_MUTEXES */ /*-----------------------------------------------------------*/ +#if ( configUSE_MUTEXES == 1 ) + + void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask ) + { + TCB_t * const pxTCB = pxMutexHolder; + UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse; + const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1; + + if( pxMutexHolder != NULL ) + { + /* If pxMutexHolder is not NULL then the holder must hold at least + one mutex. */ + configASSERT( pxTCB->uxMutexesHeld ); + + /* Determine the priority to which the priority of the task that + holds the mutex should be set. This will be the greater of the + holding task's base priority and the priority of the highest + priority task that is waiting to obtain the mutex. */ + if( pxTCB->uxBasePriority < uxHighestPriorityWaitingTask ) + { + uxPriorityToUse = uxHighestPriorityWaitingTask; + } + else + { + uxPriorityToUse = pxTCB->uxBasePriority; + } + + /* Does the priority need to change? */ + if( pxTCB->uxPriority != uxPriorityToUse ) + { + /* Only disinherit if no other mutexes are held. This is a + simplification in the priority inheritance implementation. If + the task that holds the mutex is also holding other mutexes then + the other mutexes may have caused the priority inheritance. */ + if( pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld ) + { + /* If a task has timed out because it already holds the + mutex it was trying to obtain then it cannot of inherited + its own priority. */ + configASSERT( pxTCB != pxCurrentTCB ); + + /* Disinherit the priority, remembering the previous + priority to facilitate determining the subject task's + state. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); + uxPriorityUsedOnEntry = pxTCB->uxPriority; + pxTCB->uxPriority = uxPriorityToUse; + + /* Only reset the event list item value if the value is not + being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriorityToUse ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the running task is not the task that holds the mutex + then the task that holds the mutex could be in either the + Ready, Blocked or Suspended states. Only remove the task + from its current state list if it is in the Ready state as + the task's priority is going to change and there is one + Ready list per priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvAddTaskToReadyList( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) void vTaskEnterCritical( void ) @@ -3928,7 +4295,7 @@ TCB_t *pxTCB; } /* Terminate. */ - pcBuffer[ x ] = 0x00; + pcBuffer[ x ] = ( char ) 0x00; /* Return the new end of string. */ return &( pcBuffer[ x ] ); @@ -3937,12 +4304,12 @@ TCB_t *pxTCB; #endif /* ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) */ /*-----------------------------------------------------------*/ -#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) void vTaskList( char * pcWriteBuffer ) { TaskStatus_t *pxTaskStatusArray; - volatile UBaseType_t uxArraySize, x; + UBaseType_t uxArraySize, x; char cStatus; /* @@ -3971,7 +4338,7 @@ TCB_t *pxTCB; /* Make sure the write buffer does not contain a string. */ - *pcWriteBuffer = 0x00; + *pcWriteBuffer = ( char ) 0x00; /* Take a snapshot of the number of tasks in case it changes while this function is executing. */ @@ -3980,7 +4347,7 @@ TCB_t *pxTCB; /* Allocate an array index for each task. NOTE! if configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will equate to NULL. */ - pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ if( pxTaskStatusArray != NULL ) { @@ -3992,6 +4359,9 @@ TCB_t *pxTCB; { switch( pxTaskStatusArray[ x ].eCurrentState ) { + case eRunning: cStatus = tskRUNNING_CHAR; + break; + case eReady: cStatus = tskREADY_CHAR; break; @@ -4004,9 +4374,10 @@ TCB_t *pxTCB; case eDeleted: cStatus = tskDELETED_CHAR; break; + case eInvalid: /* Fall through. */ default: /* Should not get here, but it is included to prevent static checking errors. */ - cStatus = 0x00; + cStatus = ( char ) 0x00; break; } @@ -4015,8 +4386,8 @@ TCB_t *pxTCB; pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); /* Write the rest of the string. */ - sprintf( pcWriteBuffer, "\t%c\t%u\t%u\t%u\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber ); - pcWriteBuffer += strlen( pcWriteBuffer ); + sprintf( pcWriteBuffer, "\t%c\t%u\t%u\t%u\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ } /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION @@ -4029,15 +4400,15 @@ TCB_t *pxTCB; } } -#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */ +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ /*----------------------------------------------------------*/ -#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) void vTaskGetRunTimeStats( char *pcWriteBuffer ) { TaskStatus_t *pxTaskStatusArray; - volatile UBaseType_t uxArraySize, x; + UBaseType_t uxArraySize, x; uint32_t ulTotalTime, ulStatsAsPercentage; #if( configUSE_TRACE_FACILITY != 1 ) @@ -4072,7 +4443,7 @@ TCB_t *pxTCB; */ /* Make sure the write buffer does not contain a string. */ - *pcWriteBuffer = 0x00; + *pcWriteBuffer = ( char ) 0x00; /* Take a snapshot of the number of tasks in case it changes while this function is executing. */ @@ -4081,7 +4452,7 @@ TCB_t *pxTCB; /* Allocate an array index for each task. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will equate to NULL. */ - pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ if( pxTaskStatusArray != NULL ) { @@ -4092,7 +4463,7 @@ TCB_t *pxTCB; ulTotalTime /= 100UL; /* Avoid divide by zero errors. */ - if( ulTotalTime > 0 ) + if( ulTotalTime > 0UL ) { /* Create a human readable table from the binary data. */ for( x = 0; x < uxArraySize; x++ ) @@ -4117,7 +4488,7 @@ TCB_t *pxTCB; { /* sizeof( int ) == sizeof( long ) so a smaller printf() library can be used. */ - sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); + sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ } #endif } @@ -4133,12 +4504,12 @@ TCB_t *pxTCB; { /* sizeof( int ) == sizeof( long ) so a smaller printf() library can be used. */ - sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); + sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ } #endif } - pcWriteBuffer += strlen( pcWriteBuffer ); + pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ } } else @@ -4156,7 +4527,7 @@ TCB_t *pxTCB; } } -#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */ +#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) */ /*-----------------------------------------------------------*/ TickType_t uxTaskResetEventItemValue( void ) @@ -4175,7 +4546,7 @@ TickType_t uxReturn; #if ( configUSE_MUTEXES == 1 ) - void *pvTaskIncrementMutexHeldCount( void ) + TaskHandle_t pvTaskIncrementMutexHeldCount( void ) { /* If xSemaphoreCreateMutex() is called before any tasks have been created then pxCurrentTCB will be NULL. */ @@ -4240,7 +4611,7 @@ TickType_t uxReturn; } else { - pxCurrentTCB->ulNotifiedValue = ulReturn - 1; + pxCurrentTCB->ulNotifiedValue = ulReturn - ( uint32_t ) 1; } } else @@ -4315,7 +4686,7 @@ TickType_t uxReturn; blocked state (because a notification was already pending) or the task unblocked because of a notification. Otherwise the task unblocked because of a timeout. */ - if( pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION ) + if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ) { /* A notification was not received. */ xReturn = pdFALSE; @@ -4347,7 +4718,7 @@ TickType_t uxReturn; uint8_t ucOriginalNotifyState; configASSERT( xTaskToNotify ); - pxTCB = ( TCB_t * ) xTaskToNotify; + pxTCB = xTaskToNotify; taskENTER_CRITICAL(); { @@ -4390,6 +4761,14 @@ TickType_t uxReturn; /* The task is being notified without its notify value being updated. */ break; + + default: + /* Should not get here if all enums are handled. + Artificially force an assert by testing a value the + compiler can't assume is const. */ + configASSERT( pxTCB->ulNotifiedValue == ~0UL ); + + break; } traceTASK_NOTIFY(); @@ -4473,7 +4852,7 @@ TickType_t uxReturn; http://www.freertos.org/RTOS-Cortex-M3-M4.html */ portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - pxTCB = ( TCB_t * ) xTaskToNotify; + pxTCB = xTaskToNotify; uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); { @@ -4515,6 +4894,13 @@ TickType_t uxReturn; /* The task is being notified without its notify value being updated. */ break; + + default: + /* Should not get here if all enums are handled. + Artificially force an assert by testing a value the + compiler can't assume is const. */ + configASSERT( pxTCB->ulNotifiedValue == ~0UL ); + break; } traceTASK_NOTIFY_FROM_ISR(); @@ -4546,13 +4932,11 @@ TickType_t uxReturn; { *pxHigherPriorityTaskWoken = pdTRUE; } - else - { - /* Mark that a yield is pending in case the user is not - using the "xHigherPriorityTaskWoken" parameter to an ISR - safe FreeRTOS function. */ - xYieldPending = pdTRUE; - } + + /* Mark that a yield is pending in case the user is not + using the "xHigherPriorityTaskWoken" parameter to an ISR + safe FreeRTOS function. */ + xYieldPending = pdTRUE; } else { @@ -4596,7 +4980,7 @@ TickType_t uxReturn; http://www.freertos.org/RTOS-Cortex-M3-M4.html */ portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - pxTCB = ( TCB_t * ) xTaskToNotify; + pxTCB = xTaskToNotify; uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); { @@ -4636,13 +5020,11 @@ TickType_t uxReturn; { *pxHigherPriorityTaskWoken = pdTRUE; } - else - { - /* Mark that a yield is pending in case the user is not - using the "xHigherPriorityTaskWoken" parameter in an ISR - safe FreeRTOS function. */ - xYieldPending = pdTRUE; - } + + /* Mark that a yield is pending in case the user is not + using the "xHigherPriorityTaskWoken" parameter in an ISR + safe FreeRTOS function. */ + xYieldPending = pdTRUE; } else { @@ -4688,6 +5070,13 @@ TickType_t uxReturn; #endif /* configUSE_TASK_NOTIFICATIONS */ /*-----------------------------------------------------------*/ +#if( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) + TickType_t xTaskGetIdleRunTimeCounter( void ) + { + return xIdleTaskHandle->ulRunTimeCounter; + } +#endif +/*-----------------------------------------------------------*/ static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ) { @@ -4709,7 +5098,7 @@ const TickType_t xConstTickCount = xTickCount; { /* The current task must be in a ready list, so there is no need to check, and the port reset macro can be called directly. */ - portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task. pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */ } else { @@ -4800,8 +5189,26 @@ const TickType_t xConstTickCount = xTickCount; #endif /* INCLUDE_vTaskSuspend */ } +/* Code below here allows additional code to be inserted into this source file, +especially where access to file scope functions and data is needed (for example +when performing module tests). */ #ifdef FREERTOS_MODULE_TEST #include "tasks_test_access_functions.h" #endif + +#if( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) + + #include "freertos_tasks_c_additions.h" + + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + static void freertos_tasks_c_additions_init( void ) + { + FREERTOS_TASKS_C_ADDITIONS_INIT(); + } + #endif + +#endif + + diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/timers.c b/Firmware/ThirdParty/FreeRTOS/Source/timers.c similarity index 75% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/timers.c rename to Firmware/ThirdParty/FreeRTOS/Source/timers.c index 44cb477ef..59b3840d7 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/timers.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/timers.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* Standard includes. */ #include @@ -84,11 +42,11 @@ task.h is included from an application file. */ #error configUSE_TIMERS must be set to 1 to make the xTimerPendFunctionCall() function available. #endif -/* Lint e961 and e750 are suppressed as a MISRA exception justified because the -MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the -header files above, but not in this file, in order to generate the correct -privileged Vs unprivileged linkage and placement. */ -#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e9021 !e961 !e750. */ /* This entire source file will be skipped if the application is not configured @@ -100,22 +58,29 @@ configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ /* Misc definitions. */ #define tmrNO_DELAY ( TickType_t ) 0U +/* The name assigned to the timer service task. This can be overridden by +defining trmTIMER_SERVICE_TASK_NAME in FreeRTOSConfig.h. */ +#ifndef configTIMER_SERVICE_TASK_NAME + #define configTIMER_SERVICE_TASK_NAME "Tmr Svc" +#endif + +/* Bit definitions used in the ucStatus member of a timer structure. */ +#define tmrSTATUS_IS_ACTIVE ( ( uint8_t ) 0x01 ) +#define tmrSTATUS_IS_STATICALLY_ALLOCATED ( ( uint8_t ) 0x02 ) +#define tmrSTATUS_IS_AUTORELOAD ( ( uint8_t ) 0x04 ) + /* The definition of the timers themselves. */ -typedef struct tmrTimerControl +typedef struct tmrTimerControl /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { const char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */ TickType_t xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */ - UBaseType_t uxAutoReload; /*<< Set to pdTRUE if the timer should be automatically restarted once expired. Set to pdFALSE if the timer is, in effect, a one-shot timer. */ void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */ TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */ #if( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */ #endif - - #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) - uint8_t ucStaticallyAllocated; /*<< Set to pdTRUE if the timer was created statically so no attempt is made to free the memory again if the timer is later deleted. */ - #endif + uint8_t ucStatus; /*<< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */ } xTIMER; /* The old xTIMER name is maintained above then typedefed to the new Timer_t @@ -158,22 +123,25 @@ typedef struct tmrTimerQueueMessage } u; } DaemonTaskMessage_t; -/*lint -e956 A manual analysis and inspection has been used to determine which -static variables must be declared volatile. */ +/*lint -save -e956 A manual analysis and inspection has been used to determine +which static variables must be declared volatile. */ /* The list in which active timers are stored. Timers are referenced in expire time order, with the nearest expiry time at the front of the list. Only the -timer service task is allowed to access these lists. */ -PRIVILEGED_DATA static List_t xActiveTimerList1; -PRIVILEGED_DATA static List_t xActiveTimerList2; -PRIVILEGED_DATA static List_t *pxCurrentTimerList; -PRIVILEGED_DATA static List_t *pxOverflowTimerList; +timer service task is allowed to access these lists. +xActiveTimerList1 and xActiveTimerList2 could be at function scope but that +breaks some kernel aware debuggers, and debuggers that reply on removing the +static qualifier. */ +PRIVILEGED_DATA static List_t xActiveTimerList1 = { 0 }; +PRIVILEGED_DATA static List_t xActiveTimerList2 = { 0 }; +PRIVILEGED_DATA static List_t *pxCurrentTimerList = NULL; +PRIVILEGED_DATA static List_t *pxOverflowTimerList = NULL; /* A queue that is used to send commands to the timer service task. */ -PRIVILEGED_INITIALIZED_DATA static QueueHandle_t xTimerQueue = NULL; -PRIVILEGED_INITIALIZED_DATA static TaskHandle_t xTimerTaskHandle = NULL; +PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; +PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = NULL; -/*lint +e956 */ +/*lint -restore */ /*-----------------------------------------------------------*/ @@ -191,44 +159,44 @@ PRIVILEGED_INITIALIZED_DATA static TaskHandle_t xTimerTaskHandle = NULL; * Initialise the infrastructure used by the timer service task if it has not * been initialised already. */ -PRIVILEGED_FUNCTION static void prvCheckForValidListAndQueue( void ); +static void prvCheckForValidListAndQueue( void ) PRIVILEGED_FUNCTION; /* * The timer service task (daemon). Timer functionality is controlled by this * task. Other tasks communicate with the timer service task using the * xTimerQueue queue. */ -PRIVILEGED_FUNCTION static void prvTimerTask( void *pvParameters ); +static portTASK_FUNCTION_PROTO( prvTimerTask, pvParameters ) PRIVILEGED_FUNCTION; /* * Called by the timer service task to interpret and process a command it * received on the timer queue. */ -PRIVILEGED_FUNCTION static void prvProcessReceivedCommands( void ); +static void prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION; /* * Insert the timer into either xActiveTimerList1, or xActiveTimerList2, * depending on if the expire time causes a timer counter overflow. */ -PRIVILEGED_FUNCTION static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ); +static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ) PRIVILEGED_FUNCTION; /* * An active timer has reached its expire time. Reload the timer if it is an * auto reload timer, then call its callback. */ -PRIVILEGED_FUNCTION static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ); +static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) PRIVILEGED_FUNCTION; /* * The tick count has overflowed. Switch the timer lists after ensuring the * current timer list does not still reference some timers. */ -PRIVILEGED_FUNCTION static void prvSwitchTimerLists( void ); +static void prvSwitchTimerLists( void ) PRIVILEGED_FUNCTION; /* * Obtain the current tick count, setting *pxTimerListsWereSwitched to pdTRUE * if a tick count overflow occurred since prvSampleTimeNow() was last called. */ -PRIVILEGED_FUNCTION static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ); +static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) PRIVILEGED_FUNCTION; /* * If the timer list contains any active timers then return the expire time of @@ -236,24 +204,24 @@ PRIVILEGED_FUNCTION static TickType_t prvSampleTimeNow( BaseType_t * const pxTim * timer list does not contain any timers then return 0 and set *pxListWasEmpty * to pdTRUE. */ -PRIVILEGED_FUNCTION static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ); +static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) PRIVILEGED_FUNCTION; /* * If a timer has expired, process it. Otherwise, block the timer service task * until either a timer does expire or a command is received. */ -PRIVILEGED_FUNCTION static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty ); +static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty ) PRIVILEGED_FUNCTION; /* * Called after a Timer_t structure has been allocated either statically or * dynamically to fill in the structure's members. */ -PRIVILEGED_FUNCTION static void prvInitialiseNewTimer( const char * const pcTimerName, +static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, - Timer_t *pxNewTimer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + Timer_t *pxNewTimer ) PRIVILEGED_FUNCTION; /*-----------------------------------------------------------*/ BaseType_t xTimerCreateTimerTask( void ) @@ -276,7 +244,7 @@ BaseType_t xReturn = pdFAIL; vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize ); xTimerTaskHandle = xTaskCreateStatic( prvTimerTask, - "Tmr Svc", + configTIMER_SERVICE_TASK_NAME, ulTimerTaskStackSize, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, @@ -291,7 +259,7 @@ BaseType_t xReturn = pdFAIL; #else { xReturn = xTaskCreate( prvTimerTask, - "Tmr Svc", + configTIMER_SERVICE_TASK_NAME, configTIMER_TASK_STACK_DEPTH, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, @@ -311,44 +279,39 @@ BaseType_t xReturn = pdFAIL; #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - TimerHandle_t xTimerCreate( const char * const pcTimerName, + TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + TimerCallbackFunction_t pxCallbackFunction ) { Timer_t *pxNewTimer; - pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); + pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of Timer_t is always a pointer to the timer's mame. */ if( pxNewTimer != NULL ) { + /* Status is thus far zero as the timer is not created statically + and has not been started. The autoreload bit may get set in + prvInitialiseNewTimer. */ + pxNewTimer->ucStatus = 0x00; prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); - - #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - { - /* Timers can be created statically or dynamically, so note this - timer was created dynamically in case the timer is later - deleted. */ - pxNewTimer->ucStaticallyAllocated = pdFALSE; - } - #endif /* configSUPPORT_STATIC_ALLOCATION */ } return pxNewTimer; } -#endif /* configSUPPORT_STATIC_ALLOCATION */ +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ /*-----------------------------------------------------------*/ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, - StaticTimer_t *pxTimerBuffer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + StaticTimer_t *pxTimerBuffer ) { Timer_t *pxNewTimer; @@ -356,27 +319,25 @@ BaseType_t xReturn = pdFAIL; { /* Sanity check that the size of the structure used to declare a variable of type StaticTimer_t equals the size of the real timer - structures. */ + structure. */ volatile size_t xSize = sizeof( StaticTimer_t ); configASSERT( xSize == sizeof( Timer_t ) ); + ( void ) xSize; /* Keeps lint quiet when configASSERT() is not defined. */ } #endif /* configASSERT_DEFINED */ /* A pointer to a StaticTimer_t structure MUST be provided, use it. */ configASSERT( pxTimerBuffer ); - pxNewTimer = ( Timer_t * ) pxTimerBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + pxNewTimer = ( Timer_t * ) pxTimerBuffer; /*lint !e740 !e9087 StaticTimer_t is a pointer to a Timer_t, so guaranteed to be aligned and sized correctly (checked by an assert()), so this is safe. */ if( pxNewTimer != NULL ) { - prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); + /* Timers can be created statically or dynamically so note this + timer was created statically in case it is later deleted. The + autoreload bit may get set in prvInitialiseNewTimer(). */ + pxNewTimer->ucStatus = tmrSTATUS_IS_STATICALLY_ALLOCATED; - #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - /* Timers can be created statically or dynamically so note this - timer was created statically in case it is later deleted. */ - pxNewTimer->ucStaticallyAllocated = pdTRUE; - } - #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); } return pxNewTimer; @@ -385,12 +346,12 @@ BaseType_t xReturn = pdFAIL; #endif /* configSUPPORT_STATIC_ALLOCATION */ /*-----------------------------------------------------------*/ -static void prvInitialiseNewTimer( const char * const pcTimerName, +static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, - Timer_t *pxNewTimer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + Timer_t *pxNewTimer ) { /* 0 is not a valid value for xTimerPeriodInTicks. */ configASSERT( ( xTimerPeriodInTicks > 0 ) ); @@ -405,10 +366,13 @@ static void prvInitialiseNewTimer( const char * const pcTimerName, parameters. */ pxNewTimer->pcTimerName = pcTimerName; pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; - pxNewTimer->uxAutoReload = uxAutoReload; pxNewTimer->pvTimerID = pvTimerID; pxNewTimer->pxCallbackFunction = pxCallbackFunction; vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); + if( uxAutoReload != pdFALSE ) + { + pxNewTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD; + } traceTIMER_CREATE( pxNewTimer ); } } @@ -428,7 +392,7 @@ DaemonTaskMessage_t xMessage; /* Send a command to the timer service task to start the xTimer timer. */ xMessage.xMessageID = xCommandID; xMessage.u.xTimerParameters.xMessageValue = xOptionalValue; - xMessage.u.xTimerParameters.pxTimer = ( Timer_t * ) xTimer; + xMessage.u.xTimerParameters.pxTimer = xTimer; if( xCommandID < tmrFIRST_FROM_ISR_COMMAND ) { @@ -468,16 +432,36 @@ TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) { -Timer_t *pxTimer = ( Timer_t * ) xTimer; +Timer_t *pxTimer = xTimer; configASSERT( xTimer ); return pxTimer->xTimerPeriodInTicks; } /*-----------------------------------------------------------*/ +void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) +{ +Timer_t * pxTimer = xTimer; + + configASSERT( xTimer ); + taskENTER_CRITICAL(); + { + if( uxAutoReload != pdFALSE ) + { + pxTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD; + } + else + { + pxTimer->ucStatus &= ~tmrSTATUS_IS_AUTORELOAD; + } + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) { -Timer_t * pxTimer = ( Timer_t * ) xTimer; +Timer_t * pxTimer = xTimer; TickType_t xReturn; configASSERT( xTimer ); @@ -488,7 +472,7 @@ TickType_t xReturn; const char * pcTimerGetName( TimerHandle_t xTimer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { -Timer_t *pxTimer = ( Timer_t * ) xTimer; +Timer_t *pxTimer = xTimer; configASSERT( xTimer ); return pxTimer->pcTimerName; @@ -498,7 +482,7 @@ Timer_t *pxTimer = ( Timer_t * ) xTimer; static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) { BaseType_t xResult; -Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); +Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ /* Remove the timer from the list of active timers. A check has already been performed to ensure the list is not empty. */ @@ -507,7 +491,7 @@ Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTi /* If the timer is an auto reload timer then calculate the next expiry time and re-insert the timer in the list of active timers. */ - if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) { /* The timer is inserted into a list using a time relative to anything other than the current time. It will therefore be inserted into the @@ -527,6 +511,7 @@ Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTi } else { + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; mtCOVERAGE_TEST_MARKER(); } @@ -535,7 +520,7 @@ Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTi } /*-----------------------------------------------------------*/ -static void prvTimerTask( void *pvParameters ) +static portTASK_FUNCTION( prvTimerTask, pvParameters ) { TickType_t xNextExpireTime; BaseType_t xListWasEmpty; @@ -660,7 +645,7 @@ TickType_t xNextExpireTime; static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) { TickType_t xTimeNow; -PRIVILEGED_INITIALIZED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; /*lint !e956 Variable is only accessible to one task. */ +PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; /*lint !e956 Variable is only accessible to one task. */ xTimeNow = xTaskGetTickCount(); @@ -760,7 +745,7 @@ TickType_t xTimeNow; software timer. */ pxTimer = xMessage.u.xTimerParameters.pxTimer; - if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) + if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) /*lint !e961. The cast is only redundant when NULL is passed into the macro. */ { /* The timer is in a list, remove it. */ ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); @@ -783,11 +768,12 @@ TickType_t xTimeNow; switch( xMessage.xMessageID ) { case tmrCOMMAND_START : - case tmrCOMMAND_START_FROM_ISR : - case tmrCOMMAND_RESET : - case tmrCOMMAND_RESET_FROM_ISR : + case tmrCOMMAND_START_FROM_ISR : + case tmrCOMMAND_RESET : + case tmrCOMMAND_RESET_FROM_ISR : case tmrCOMMAND_START_DONT_TRACE : /* Start or restart a timer. */ + pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) != pdFALSE ) { /* The timer expired before it was added to the active @@ -795,7 +781,7 @@ TickType_t xTimeNow; pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); traceTIMER_EXPIRED( pxTimer ); - if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) { xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY ); configASSERT( xResult ); @@ -814,12 +800,13 @@ TickType_t xTimeNow; case tmrCOMMAND_STOP : case tmrCOMMAND_STOP_FROM_ISR : - /* The timer has already been removed from the active list. - There is nothing to do here. */ + /* The timer has already been removed from the active list. */ + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; break; case tmrCOMMAND_CHANGE_PERIOD : case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR : + pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue; configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) ); @@ -833,29 +820,28 @@ TickType_t xTimeNow; break; case tmrCOMMAND_DELETE : - /* The timer has already been removed from the active list, - just free up the memory if the memory was dynamically - allocated. */ - #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) { - /* The timer can only have been allocated dynamically - - free it again. */ - vPortFree( pxTimer ); - } - #elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) - { - /* The timer could have been allocated statically or - dynamically, so check before attempting to free the - memory. */ - if( pxTimer->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + /* The timer has already been removed from the active list, + just free up the memory if the memory was dynamically + allocated. */ + if( ( pxTimer->ucStatus & tmrSTATUS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) 0 ) { vPortFree( pxTimer ); } else { - mtCOVERAGE_TEST_MARKER(); + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; } } + #else + { + /* If dynamic allocation is not enabled, the memory + could not have been dynamically allocated. So there is + no need to free the memory - just mark the timer as + "not active". */ + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; + } #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ break; @@ -884,7 +870,7 @@ BaseType_t xResult; xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); /* Remove the timer from the list. */ - pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); + pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); traceTIMER_EXPIRED( pxTimer ); @@ -893,7 +879,7 @@ BaseType_t xResult; have not yet been switched. */ pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); - if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) { /* Calculate the reload value, and if the reload value results in the timer going into the same timer list then it has already expired @@ -945,10 +931,10 @@ static void prvCheckForValidListAndQueue( void ) { /* The timer queue is allocated statically in case configSUPPORT_DYNAMIC_ALLOCATION is 0. */ - static StaticQueue_t xStaticTimerQueue; - static uint8_t ucStaticTimerQueueStorage[ configTIMER_QUEUE_LENGTH * sizeof( DaemonTaskMessage_t ) ]; + static StaticQueue_t xStaticTimerQueue; /*lint !e956 Ok to declare in this manner to prevent additional conditional compilation guards in other locations. */ + static uint8_t ucStaticTimerQueueStorage[ ( size_t ) configTIMER_QUEUE_LENGTH * sizeof( DaemonTaskMessage_t ) ]; /*lint !e956 Ok to declare in this manner to prevent additional conditional compilation guards in other locations. */ - xTimerQueue = xQueueCreateStatic( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, sizeof( DaemonTaskMessage_t ), &( ucStaticTimerQueueStorage[ 0 ] ), &xStaticTimerQueue ); + xTimerQueue = xQueueCreateStatic( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, ( UBaseType_t ) sizeof( DaemonTaskMessage_t ), &( ucStaticTimerQueueStorage[ 0 ] ), &xStaticTimerQueue ); } #else { @@ -980,28 +966,32 @@ static void prvCheckForValidListAndQueue( void ) BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) { -BaseType_t xTimerIsInActiveList; -Timer_t *pxTimer = ( Timer_t * ) xTimer; +BaseType_t xReturn; +Timer_t *pxTimer = xTimer; configASSERT( xTimer ); /* Is the timer in the list of active timers? */ taskENTER_CRITICAL(); { - /* Checking to see if it is in the NULL list in effect checks to see if - it is referenced from either the current or the overflow timer lists in - one go, but the logic has to be reversed, hence the '!'. */ - xTimerIsInActiveList = ( BaseType_t ) !( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) ); + if( ( pxTimer->ucStatus & tmrSTATUS_IS_ACTIVE ) == 0 ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } } taskEXIT_CRITICAL(); - return xTimerIsInActiveList; + return xReturn; } /*lint !e818 Can't be pointer to const due to the typedef. */ /*-----------------------------------------------------------*/ void *pvTimerGetTimerID( const TimerHandle_t xTimer ) { -Timer_t * const pxTimer = ( Timer_t * ) xTimer; +Timer_t * const pxTimer = xTimer; void *pvReturn; configASSERT( xTimer ); @@ -1018,7 +1008,7 @@ void *pvReturn; void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) { -Timer_t * const pxTimer = ( Timer_t * ) xTimer; +Timer_t * const pxTimer = xTimer; configASSERT( xTimer ); @@ -1083,6 +1073,26 @@ Timer_t * const pxTimer = ( Timer_t * ) xTimer; #endif /* INCLUDE_xTimerPendFunctionCall */ /*-----------------------------------------------------------*/ +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) + { + return ( ( Timer_t * ) xTimer )->uxTimerNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTimerSetTimerNumber( TimerHandle_t xTimer, UBaseType_t uxTimerNumber ) + { + ( ( Timer_t * ) xTimer )->uxTimerNumber = uxTimerNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + /* This entire source file will be skipped if the application is not configured to include software timer functionality. If you want to include software timer functionality then ensure configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_can.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_can.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_can.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_can.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_cortex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_cortex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_cortex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_cortex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_def.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_def.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_def.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_def.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ramfunc.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ramfunc.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ramfunc.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ramfunc.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usb.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usb.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usb.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usb.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h b/Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h new file mode 100644 index 000000000..50f87c504 --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h @@ -0,0 +1,183 @@ +/** + ****************************************************************************** + * @file usbd_cdc.h + * @author MCD Application Team + * @brief header file for the usbd_cdc.c file. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USB_CDC_H +#define __USB_CDC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_ioreq.h" + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup usbd_cdc + * @brief This file is the Header file for usbd_cdc.c + * @{ + */ + + +/** @defgroup usbd_cdc_Exported_Defines + * @{ + */ +#define CDC_IN_EP 0x81U /* EP1 for data IN */ +#define CDC_OUT_EP 0x01U /* EP1 for data OUT */ +#define CDC_CMD_EP 0x82U /* EP2 for CDC commands */ +#define ODRIVE_IN_EP 0x83 /* EP3 IN: ODrive device TX endpoint */ +#define ODRIVE_OUT_EP 0x03 /* EP3 OUT: ODrive device RX endpoint */ + +#ifndef CDC_HS_BINTERVAL +#define CDC_HS_BINTERVAL 0x10U +#endif /* CDC_HS_BINTERVAL */ + +#ifndef CDC_FS_BINTERVAL +#define CDC_FS_BINTERVAL 0x10U +#endif /* CDC_FS_BINTERVAL */ + +/* CDC Endpoints parameters: you can fine tune these values depending on the needed baudrates and performance. */ +#define CDC_DATA_HS_MAX_PACKET_SIZE 64U /* Endpoint IN & OUT Packet size */ +#define CDC_DATA_FS_MAX_PACKET_SIZE 64U /* Endpoint IN & OUT Packet size */ +#define CDC_CMD_PACKET_SIZE 8U /* Control Endpoint Packet size */ + +#define USB_CDC_CONFIG_DESC_SIZ (67 + 39) +#define CDC_DATA_HS_IN_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE +#define CDC_DATA_HS_OUT_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE + +#define CDC_DATA_FS_IN_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE +#define CDC_DATA_FS_OUT_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE + +/*---------------------------------------------------------------------*/ +/* CDC definitions */ +/*---------------------------------------------------------------------*/ +#define CDC_SEND_ENCAPSULATED_COMMAND 0x00U +#define CDC_GET_ENCAPSULATED_RESPONSE 0x01U +#define CDC_SET_COMM_FEATURE 0x02U +#define CDC_GET_COMM_FEATURE 0x03U +#define CDC_CLEAR_COMM_FEATURE 0x04U +#define CDC_SET_LINE_CODING 0x20U +#define CDC_GET_LINE_CODING 0x21U +#define CDC_SET_CONTROL_LINE_STATE 0x22U +#define CDC_SEND_BREAK 0x23U + +/** + * @} + */ + + +/** @defgroup USBD_CORE_Exported_TypesDefinitions + * @{ + */ + +/** + * @} + */ +typedef struct +{ + uint32_t bitrate; + uint8_t format; + uint8_t paritytype; + uint8_t datatype; +} USBD_CDC_LineCodingTypeDef; + +typedef struct _USBD_CDC_Itf +{ + int8_t (* Init)(void); + int8_t (* DeInit)(void); + int8_t (* Control)(uint8_t cmd, uint8_t *pbuf, uint16_t length); + int8_t (* Receive)(uint8_t *Buf, uint32_t *Len, uint8_t endpoint_pair); + int8_t (* TransmitCplt)(uint8_t *Buf, uint32_t *Len, uint8_t epnum); +} USBD_CDC_ItfTypeDef; + +typedef struct +{ + uint8_t* Buffer; + uint32_t Length; + volatile uint8_t State; +} +USBD_CDC_EP_HandleTypeDef; + +typedef struct +{ + uint32_t data[CDC_DATA_HS_MAX_PACKET_SIZE / 4U]; /* Force 32bits alignment */ + uint8_t CmdOpCode; + uint8_t CmdLength; + + USBD_CDC_EP_HandleTypeDef CDC_Tx; + USBD_CDC_EP_HandleTypeDef CDC_Rx; + + USBD_CDC_EP_HandleTypeDef ODRIVE_Tx; + USBD_CDC_EP_HandleTypeDef ODRIVE_Rx; + +} USBD_CDC_HandleTypeDef; + + + +/** @defgroup USBD_CORE_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_CORE_Exported_Variables + * @{ + */ + +extern USBD_ClassTypeDef USBD_CDC; +#define USBD_CDC_CLASS &USBD_CDC +/** + * @} + */ + +/** @defgroup USB_CORE_Exported_Functions + * @{ + */ +uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev, + USBD_CDC_ItfTypeDef *fops); + +uint8_t USBD_CDC_SetTxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff, + uint32_t length, uint8_t endpoint_pair); + +uint8_t USBD_CDC_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff, uint8_t endpoint_pair); +uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev, uint8_t* buf, uint16_t len, uint8_t endpoint_num); +uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t* buf, size_t len, uint8_t endpoint_num); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USB_CDC_H */ +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c b/Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c similarity index 71% rename from Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c rename to Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c index 733135224..525dcee51 100644 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c @@ -2,23 +2,21 @@ ****************************************************************************** * @file usbd_cdc.c * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief This file provides the high layer firmware functions to manage the + * @brief This file provides the high layer firmware functions to manage the * following functionalities of the USB CDC Class: * - Initialization and Configuration of high and low layer * - Enumeration as CDC Device (and enumeration for each implemented memory interface) * - OUT/IN data transfer * - Command IN transfer (class requests management) * - Error management - * + * * @verbatim - * - * =================================================================== + * + * =================================================================== * CDC Class Driver Description - * =================================================================== + * =================================================================== * This driver manages the "Universal Serial Bus Class Definitions for Communications Devices - * Revision 1.2 November 16, 2007" and the sub-protocol specification of "Universal Serial Bus + * Revision 1.2 November 16, 2007" and the sub-protocol specification of "Universal Serial Bus * Communications Class Subclass Specification for PSTN Devices Revision 1.2 February 9, 2007" * This driver implements the following aspects of the specification: * - Device descriptor management @@ -28,35 +26,34 @@ * - Abstract Control Model compliant * - Union Functional collection (using 1 IN endpoint for control) * - Data interface class - * + * * These aspects may be enriched or modified for a specific user application. - * - * This driver doesn't implement the following aspects of the specification + * + * This driver doesn't implement the following aspects of the specification * (but it is possible to manage these features with some modifications on this driver): * - Any class-specific aspect relative to communication classes should be managed by user application. * - All communication classes other than PSTN are not managed - * + * * @endverbatim - * + * ****************************************************************************** * @attention * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

* - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 * ****************************************************************************** - */ + */ + +/* BSPDependencies +- "stm32xxxxx_{eval}{discovery}{nucleo_144}.c" +- "stm32xxxxx_{eval}{discovery}_io.c" +EndBSPDependencies */ /* Includes ------------------------------------------------------------------*/ #include "usbd_cdc.h" @@ -65,75 +62,59 @@ #include #include + /** @addtogroup STM32_USB_DEVICE_LIBRARY * @{ */ -/** @defgroup USBD_CDC +/** @defgroup USBD_CDC * @brief usbd core module * @{ - */ + */ /** @defgroup USBD_CDC_Private_TypesDefinitions * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_CDC_Private_Defines * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_CDC_Private_Macros * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_CDC_Private_FunctionPrototypes * @{ */ - -static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, - uint8_t cfgidx); - -static uint8_t USBD_CDC_DeInit (USBD_HandleTypeDef *pdev, - uint8_t cfgidx); - -static uint8_t USBD_CDC_Setup (USBD_HandleTypeDef *pdev, - USBD_SetupReqTypedef *req); - -static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, - uint8_t epnum); - -static uint8_t USBD_CDC_DataOut (USBD_HandleTypeDef *pdev, - uint8_t epnum); - -static uint8_t USBD_CDC_EP0_RxReady (USBD_HandleTypeDef *pdev); - -static uint8_t *USBD_CDC_GetFSCfgDesc (uint16_t *length); - -static uint8_t *USBD_CDC_GetHSCfgDesc (uint16_t *length); - -static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc (uint16_t *length); - -static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc (uint16_t *length); - -uint8_t *USBD_CDC_GetDeviceQualifierDescriptor (uint16_t *length); - +static uint8_t USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static uint8_t USBD_CDC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static uint8_t USBD_CDC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t USBD_CDC_EP0_RxReady(USBD_HandleTypeDef *pdev); + +static uint8_t *USBD_CDC_GetFSCfgDesc(uint16_t *length); +static uint8_t *USBD_CDC_GetHSCfgDesc(uint16_t *length); +static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc(uint16_t *length); +static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc(uint16_t *length); +uint8_t *USBD_CDC_GetDeviceQualifierDescriptor(uint16_t *length); static uint8_t USBD_WinUSBComm_SetupVendor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); -//static uint8_t * USBD_GetUsrStrDescriptor(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length); /* USB Standard Device Descriptor */ __ALIGN_BEGIN static uint8_t USBD_CDC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END = @@ -152,15 +133,15 @@ __ALIGN_BEGIN static uint8_t USBD_CDC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_ /** * @} - */ + */ /** @defgroup USBD_CDC_Private_Variables * @{ - */ + */ /* CDC interface class callbacks structure */ -USBD_ClassTypeDef USBD_CDC = +USBD_ClassTypeDef USBD_CDC = { USBD_CDC_Init, USBD_CDC_DeInit, @@ -171,10 +152,10 @@ USBD_ClassTypeDef USBD_CDC = USBD_CDC_DataOut, NULL, NULL, - NULL, - USBD_CDC_GetHSCfgDesc, - USBD_CDC_GetFSCfgDesc, - USBD_CDC_GetOtherSpeedCfgDesc, + NULL, + USBD_CDC_GetHSCfgDesc, + USBD_CDC_GetFSCfgDesc, + USBD_CDC_GetOtherSpeedCfgDesc, USBD_CDC_GetDeviceQualifierDescriptor, USBD_UsrStrDescriptor }; @@ -253,7 +234,7 @@ __ALIGN_BEGIN uint8_t USBD_CDC_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END = 0x03, /* bmAttributes: Interrupt */ LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */ HIBYTE(CDC_CMD_PACKET_SIZE), - 0x10, /* bInterval: */ + CDC_HS_BINTERVAL, /* bInterval: */ /*---------------------------------------------------------------------------*/ /*Data class interface descriptor*/ @@ -327,16 +308,15 @@ __ALIGN_BEGIN uint8_t USBD_CDC_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END = LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), 0x00, /* bInterval: ignore for Bulk transfer */ -} ; - +}; /** * @} - */ + */ /** @defgroup USBD_CDC_Private_Functions * @{ - */ + */ /** * @brief USBD_CDC_Init @@ -345,40 +325,54 @@ __ALIGN_BEGIN uint8_t USBD_CDC_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END = * @param cfgidx: Configuration index * @retval status */ -static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, - uint8_t cfgidx) +static uint8_t USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { - uint8_t ret = 0; - USBD_CDC_HandleTypeDef *hcdc; - - if(pdev->dev_speed == USBD_SPEED_HIGH ) - { + UNUSED(cfgidx); + USBD_CDC_HandleTypeDef *hcdc; + + hcdc = USBD_malloc(sizeof(USBD_CDC_HandleTypeDef)); + + if (hcdc == NULL) + { + pdev->pClassData = NULL; + return (uint8_t)USBD_EMEM; + } + + pdev->pClassData = (void *)hcdc; + + if (pdev->dev_speed == USBD_SPEED_HIGH) + { /* Open EP IN */ - USBD_LL_OpenEP(pdev, - CDC_IN_EP, - USBD_EP_TYPE_BULK, - CDC_DATA_HS_IN_PACKET_SIZE); - - /* Open EP OUT */ - USBD_LL_OpenEP(pdev, - CDC_OUT_EP, - USBD_EP_TYPE_BULK, - CDC_DATA_HS_OUT_PACKET_SIZE); - + (void)USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK, + CDC_DATA_HS_IN_PACKET_SIZE); + + pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U; + + /* Open EP OUT */ + (void)USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, + CDC_DATA_HS_OUT_PACKET_SIZE); + + pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U; + + /* Set bInterval for CDC CMD Endpoint */ + pdev->ep_in[CDC_CMD_EP & 0xFU].bInterval = CDC_HS_BINTERVAL; } else { /* Open EP IN */ - USBD_LL_OpenEP(pdev, - CDC_IN_EP, - USBD_EP_TYPE_BULK, - CDC_DATA_FS_IN_PACKET_SIZE); - - /* Open EP OUT */ - USBD_LL_OpenEP(pdev, - CDC_OUT_EP, - USBD_EP_TYPE_BULK, - CDC_DATA_FS_OUT_PACKET_SIZE); + (void)USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK, + CDC_DATA_FS_IN_PACKET_SIZE); + + pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U; + + /* Open EP OUT */ + (void)USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, + CDC_DATA_FS_OUT_PACKET_SIZE); + + pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U; + + /* Set bInterval for CMD Endpoint */ + pdev->ep_in[CDC_CMD_EP & 0xFU].bInterval = CDC_FS_BINTERVAL; } /* Open ODrive IN endpoint */ @@ -387,62 +381,30 @@ static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, USBD_EP_TYPE_BULK, pdev->dev_speed == USBD_SPEED_HIGH ? CDC_DATA_HS_IN_PACKET_SIZE : CDC_DATA_FS_IN_PACKET_SIZE); + pdev->ep_in[ODRIVE_IN_EP & 0xFU].is_used = 1U; + /* Open ODrive OUT endpoint */ USBD_LL_OpenEP(pdev, ODRIVE_OUT_EP, USBD_EP_TYPE_BULK, pdev->dev_speed == USBD_SPEED_HIGH ? CDC_DATA_HS_OUT_PACKET_SIZE : CDC_DATA_FS_OUT_PACKET_SIZE); + pdev->ep_out[ODRIVE_OUT_EP & 0xFU].is_used = 1U; + /* Open Command IN EP */ - USBD_LL_OpenEP(pdev, - CDC_CMD_EP, - USBD_EP_TYPE_INTR, - CDC_CMD_PACKET_SIZE); - - - pdev->pClassData = USBD_malloc(sizeof (USBD_CDC_HandleTypeDef)); - - if(pdev->pClassData == NULL) - { - ret = 1; - } - else - { - hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - - /* Init physical Interface components */ - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init(); - - /* Init Xfer states */ - hcdc->CDC_Tx.State =0; - hcdc->CDC_Rx.State =0; - hcdc->ODRIVE_Tx.State =0; - hcdc->ODRIVE_Rx.State =0; - - if(pdev->dev_speed == USBD_SPEED_HIGH ) - { - /* Prepare Out endpoint to receive next packet */ - USBD_LL_PrepareReceive(pdev, - CDC_OUT_EP, - hcdc->CDC_Rx.Buffer, - CDC_DATA_HS_OUT_PACKET_SIZE); - } - else - { - /* Prepare Out endpoint to receive next packet */ - USBD_LL_PrepareReceive(pdev, - CDC_OUT_EP, - hcdc->CDC_Rx.Buffer, - CDC_DATA_FS_OUT_PACKET_SIZE); - } - - /* Prepare ODrive Out endpoint to receive next packet */ - USBD_LL_PrepareReceive(pdev, - ODRIVE_OUT_EP, - hcdc->ODRIVE_Rx.Buffer, - CDC_DATA_FS_OUT_PACKET_SIZE); - } - return ret; + (void)USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE); + pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U; + + /* Init physical Interface components */ + ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init(); + + /* Init Xfer states */ + hcdc->CDC_Tx.State = 0; + hcdc->CDC_Rx.State = 0; + hcdc->ODRIVE_Tx.State = 0; + hcdc->ODRIVE_Rx.State = 0; + + return (uint8_t)USBD_OK; } /** @@ -452,40 +414,40 @@ static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, * @param cfgidx: Configuration index * @retval status */ -static uint8_t USBD_CDC_DeInit (USBD_HandleTypeDef *pdev, - uint8_t cfgidx) +static uint8_t USBD_CDC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { - uint8_t ret = 0; - + UNUSED(cfgidx); + uint8_t ret = 0U; + /* Close EP IN */ - USBD_LL_CloseEP(pdev, - CDC_IN_EP); - + (void)USBD_LL_CloseEP(pdev, CDC_IN_EP); + pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 0U; + /* Close EP OUT */ - USBD_LL_CloseEP(pdev, - CDC_OUT_EP); - + (void)USBD_LL_CloseEP(pdev, CDC_OUT_EP); + pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 0U; + /* Close Command IN EP */ - USBD_LL_CloseEP(pdev, - CDC_CMD_EP); - + (void)USBD_LL_CloseEP(pdev, CDC_CMD_EP); + pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 0U; + pdev->ep_in[CDC_CMD_EP & 0xFU].bInterval = 0U; + /* Close EP IN */ - USBD_LL_CloseEP(pdev, - ODRIVE_IN_EP); - + (void)USBD_LL_CloseEP(pdev, ODRIVE_IN_EP); + pdev->ep_in[ODRIVE_IN_EP & 0xFU].is_used = 0U; + /* Close EP OUT */ - USBD_LL_CloseEP(pdev, - ODRIVE_OUT_EP); - - + (void)USBD_LL_CloseEP(pdev, ODRIVE_OUT_EP); + pdev->ep_out[ODRIVE_OUT_EP & 0xFU].is_used = 0U; + /* DeInit physical Interface components */ - if(pdev->pClassData != NULL) + if (pdev->pClassData != NULL) { ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->DeInit(); - USBD_free(pdev->pClassData); + (void)USBD_free(pdev->pClassData); pdev->pClassData = NULL; } - + return ret; } @@ -496,65 +458,97 @@ static uint8_t USBD_CDC_DeInit (USBD_HandleTypeDef *pdev, * @param req: usb requests * @retval status */ -static uint8_t USBD_CDC_Setup (USBD_HandleTypeDef *pdev, - USBD_SetupReqTypedef *req) +static uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev, + USBD_SetupReqTypedef *req) { - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - static uint8_t ifalt = 0; - + USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; + uint8_t ifalt = 0U; + uint16_t status_info = 0U; + USBD_StatusTypeDef ret = USBD_OK; + switch (req->bmRequest & USB_REQ_TYPE_MASK) { - case USB_REQ_TYPE_CLASS : - if (req->wLength) + case USB_REQ_TYPE_CLASS: + if (req->wLength != 0U) { - if (req->bmRequest & 0x80) + if ((req->bmRequest & 0x80U) != 0U) { ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest, (uint8_t *)hcdc->data, req->wLength); - USBD_CtlSendData (pdev, - (uint8_t *)hcdc->data, - req->wLength); + + (void)USBD_CtlSendData(pdev, (uint8_t *)hcdc->data, req->wLength); } else { hcdc->CmdOpCode = req->bRequest; - hcdc->CmdLength = req->wLength; - - USBD_CtlPrepareRx (pdev, - (uint8_t *)hcdc->data, - req->wLength); + hcdc->CmdLength = (uint8_t)req->wLength; + + (void)USBD_CtlPrepareRx(pdev, (uint8_t *)hcdc->data, req->wLength); } - } else { ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest, - (uint8_t*)req, - 0); + (uint8_t *)req, 0U); } break; case USB_REQ_TYPE_STANDARD: switch (req->bRequest) - { - case USB_REQ_GET_INTERFACE : - USBD_CtlSendData (pdev, - &ifalt, - 1); + { + case USB_REQ_GET_STATUS: + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + (void)USBD_CtlSendData(pdev, (uint8_t *)&status_info, 2U); + } + else + { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } break; - - case USB_REQ_SET_INTERFACE : + + case USB_REQ_GET_INTERFACE: + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + (void)USBD_CtlSendData(pdev, &ifalt, 1U); + } + else + { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_SET_INTERFACE: + if (pdev->dev_state != USBD_STATE_CONFIGURED) + { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_CLEAR_FEATURE: + break; + + case USB_REQ_TYPE_VENDOR: + return USBD_WinUSBComm_SetupVendor(pdev, req); + + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; break; } + break; - case USB_REQ_TYPE_VENDOR: - return USBD_WinUSBComm_SetupVendor(pdev, req); - - default: + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; break; } - return USBD_OK; + + return (uint8_t)ret; } /** @@ -564,25 +558,42 @@ static uint8_t USBD_CDC_Setup (USBD_HandleTypeDef *pdev, * @param epnum: endpoint number * @retval status */ -static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum) +static uint8_t USBD_CDC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum) { - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - - if(pdev->pClassData != NULL) + USBD_CDC_HandleTypeDef *hcdc; + PCD_HandleTypeDef *hpcd = pdev->pData; + + if (pdev->pClassData == NULL) + { + return (uint8_t)USBD_FAIL; + } + + hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; + + if ((pdev->ep_in[epnum].total_length > 0U) && + ((pdev->ep_in[epnum].total_length % hpcd->IN_ep[epnum].maxpacket) == 0U)) + { + /* Update the packet total length */ + pdev->ep_in[epnum].total_length = 0U; + + /* Send ZLP */ + (void)USBD_LL_Transmit(pdev, epnum, NULL, 0U); + } + else { // NOTE: We would logically expect xx_IN_EP here, but we actually get the xx_OUT_EP - if (epnum == CDC_OUT_EP) + if (epnum == CDC_OUT_EP) { hcdc->CDC_Tx.State = 0; - if (epnum == ODRIVE_OUT_EP) + osMessagePut(usb_event_queue, 3, 0); + } + if (epnum == ODRIVE_OUT_EP) { hcdc->ODRIVE_Tx.State = 0; - //Note: We could use independent semaphores for simoultainous USB transmission. - osSemaphoreRelease(sem_usb_tx); + osMessagePut(usb_event_queue, 4, 0); + } return USBD_OK; } - else - { - return USBD_FAIL; - } + + return (uint8_t)USBD_OK; } /** @@ -592,8 +603,8 @@ static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum) * @param epnum: endpoint number * @retval status */ -static uint8_t USBD_CDC_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum) -{ +static uint8_t USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum) +{ USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; USBD_CDC_EP_HandleTypeDef* hEP_Rx; @@ -612,7 +623,7 @@ static uint8_t USBD_CDC_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum) NAKed till the end of the application Xfer */ if(pdev->pClassData != NULL) { - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hEP_Rx->Buffer, &hEP_Rx->Length, epnum); + ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(NULL, &hEP_Rx->Length, epnum); return USBD_OK; } @@ -622,78 +633,80 @@ static uint8_t USBD_CDC_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum) } } - - /** - * @brief USBD_CDC_DataOut - * Data received on non-control Out endpoint + * @brief USBD_CDC_EP0_RxReady + * Handle EP0 Rx Ready event * @param pdev: device instance - * @param epnum: endpoint number * @retval status */ -static uint8_t USBD_CDC_EP0_RxReady (USBD_HandleTypeDef *pdev) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - - if((pdev->pUserData != NULL) && (hcdc->CmdOpCode != 0xFF)) +static uint8_t USBD_CDC_EP0_RxReady(USBD_HandleTypeDef *pdev) +{ + USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; + + if ((pdev->pUserData != NULL) && (hcdc->CmdOpCode != 0xFFU)) { ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(hcdc->CmdOpCode, (uint8_t *)hcdc->data, - hcdc->CmdLength); - hcdc->CmdOpCode = 0xFF; - + (uint16_t)hcdc->CmdLength); + hcdc->CmdOpCode = 0xFFU; + } - return USBD_OK; + + return (uint8_t)USBD_OK; } /** - * @brief USBD_CDC_GetFSCfgDesc + * @brief USBD_CDC_GetFSCfgDesc * Return configuration descriptor * @param speed : current device speed * @param length : pointer data length * @retval pointer to descriptor buffer */ -static uint8_t *USBD_CDC_GetFSCfgDesc (uint16_t *length) +static uint8_t *USBD_CDC_GetFSCfgDesc(uint16_t *length) { - *length = sizeof (USBD_CDC_CfgDesc); + *length = (uint16_t)sizeof(USBD_CDC_CfgDesc); + return USBD_CDC_CfgDesc; } /** - * @brief USBD_CDC_GetHSCfgDesc + * @brief USBD_CDC_GetHSCfgDesc * Return configuration descriptor * @param speed : current device speed * @param length : pointer data length * @retval pointer to descriptor buffer */ -static uint8_t *USBD_CDC_GetHSCfgDesc (uint16_t *length) +static uint8_t *USBD_CDC_GetHSCfgDesc(uint16_t *length) { - *length = sizeof (USBD_CDC_CfgDesc); + *length = (uint16_t)sizeof(USBD_CDC_CfgDesc); + return USBD_CDC_CfgDesc; } /** - * @brief USBD_CDC_GetCfgDesc + * @brief USBD_CDC_GetCfgDesc * Return configuration descriptor * @param speed : current device speed * @param length : pointer data length * @retval pointer to descriptor buffer */ -static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc (uint16_t *length) +static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc(uint16_t *length) { - *length = sizeof (USBD_CDC_CfgDesc); + *length = (uint16_t)sizeof(USBD_CDC_CfgDesc); + return USBD_CDC_CfgDesc; } /** -* @brief DeviceQualifierDescriptor +* @brief DeviceQualifierDescriptor * return Device Qualifier descriptor * @param length : pointer data length * @retval pointer to descriptor buffer */ -uint8_t *USBD_CDC_GetDeviceQualifierDescriptor (uint16_t *length) +uint8_t *USBD_CDC_GetDeviceQualifierDescriptor(uint16_t *length) { - *length = sizeof (USBD_CDC_DeviceQualifierDesc); + *length = (uint16_t)sizeof(USBD_CDC_DeviceQualifierDesc); + return USBD_CDC_DeviceQualifierDesc; } @@ -703,96 +716,38 @@ uint8_t *USBD_CDC_GetDeviceQualifierDescriptor (uint16_t *length) * @param fops: CD Interface callback * @retval status */ -uint8_t USBD_CDC_RegisterInterface (USBD_HandleTypeDef *pdev, - USBD_CDC_ItfTypeDef *fops) +uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev, + USBD_CDC_ItfTypeDef *fops) { - uint8_t ret = USBD_FAIL; - - if(fops != NULL) + if (fops == NULL) { - pdev->pUserData= fops; - ret = USBD_OK; + return (uint8_t)USBD_FAIL; } - - return ret; -} -/** - * @brief USBD_CDC_SetTxBuffer - * @param pdev: device instance - * @param pbuff: Tx Buffer - * @retval status - */ -uint8_t USBD_CDC_SetTxBuffer (USBD_HandleTypeDef *pdev, - uint8_t *pbuff, - uint16_t length, - uint8_t endpoint_pair) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - - USBD_CDC_EP_HandleTypeDef* hEP_Tx; - if (endpoint_pair == CDC_OUT_EP) { - hEP_Tx = &hcdc->CDC_Tx; - } else if (endpoint_pair == ODRIVE_OUT_EP) { - hEP_Tx = &hcdc->ODRIVE_Tx; - } else { - return USBD_FAIL; - } + pdev->pUserData = fops; - hEP_Tx->Buffer = pbuff; - hEP_Tx->Length = length; - - return USBD_OK; + return (uint8_t)USBD_OK; } /** - * @brief USBD_CDC_SetRxBuffer + * @brief USBD_CDC_TransmitPacket + * Transmit packet on IN endpoint * @param pdev: device instance - * @param pbuff: Rx Buffer * @retval status */ -uint8_t USBD_CDC_SetRxBuffer (USBD_HandleTypeDef *pdev, - uint8_t *pbuff, uint8_t endpoint_pair) +uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t* buf, size_t len, uint8_t endpoint_num) { USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - - USBD_CDC_EP_HandleTypeDef* hEP_Rx; - if (endpoint_pair == CDC_OUT_EP) { - hEP_Rx = &hcdc->CDC_Rx; - } else if (endpoint_pair == ODRIVE_OUT_EP) { - hEP_Rx = &hcdc->ODRIVE_Rx; - } else { - return USBD_FAIL; - } - - hEP_Rx->Buffer = pbuff; - - return USBD_OK; -} - -/** - * @brief USBD_CDC_DataOut - * Data received on non-control Out endpoint - * @param pdev: device instance - * @param epnum: endpoint number - * @retval status - */ -uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; if(pdev->pClassData != NULL) { // Select Endpoint USBD_CDC_EP_HandleTypeDef* hEP_Tx; - uint8_t in_ep; - if (endpoint_pair == CDC_OUT_EP) { + if (endpoint_num == CDC_IN_EP) { hEP_Tx = &hcdc->CDC_Tx; - in_ep = CDC_IN_EP; - } else if (endpoint_pair == ODRIVE_OUT_EP) { + } else if (endpoint_num == ODRIVE_IN_EP) { hEP_Tx = &hcdc->ODRIVE_Tx; - in_ep = ODRIVE_IN_EP; } else { return USBD_FAIL; } @@ -804,9 +759,9 @@ uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair /* Transmit next packet */ USBD_LL_Transmit(pdev, - in_ep, - hEP_Tx->Buffer, - hEP_Tx->Length); + endpoint_num, + buf, + len); return USBD_OK; } @@ -828,31 +783,16 @@ uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair * @param pdev: device instance * @retval status */ -uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - +uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev, uint8_t* buf, uint16_t len, uint8_t endpoint_num) +{ /* Suspend or Resume USB Out process */ if(pdev->pClassData != NULL) { - // Select Endpoint - USBD_CDC_EP_HandleTypeDef* hEP_Rx; - uint8_t out_ep; - if (endpoint_pair == CDC_OUT_EP) { - hEP_Rx = &hcdc->CDC_Rx; - out_ep = CDC_OUT_EP; - } else if (endpoint_pair == ODRIVE_OUT_EP) { - hEP_Rx = &hcdc->ODRIVE_Rx; - out_ep = ODRIVE_OUT_EP; - } else { - return USBD_FAIL; - } - /* Prepare Out endpoint to receive next packet */ USBD_LL_PrepareReceive(pdev, - out_ep, - hEP_Rx->Buffer, - pdev->dev_speed == USBD_SPEED_HIGH ? CDC_DATA_HS_OUT_PACKET_SIZE : CDC_DATA_FS_OUT_PACKET_SIZE); + endpoint_num, + buf, + len); return USBD_OK; } @@ -1041,14 +981,14 @@ static uint8_t USBD_WinUSBComm_SetupVendor(USBD_HandleTypeDef *pdev, USBD_Setup /** * @} - */ + */ /** * @} - */ + */ /** * @} - */ + */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_core.h b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_core.h new file mode 100644 index 000000000..c7d2ba39e --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_core.h @@ -0,0 +1,158 @@ +/** + ****************************************************************************** + * @file usbd_core.h + * @author MCD Application Team + * @brief Header file for usbd_core.c file + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USBD_CORE_H +#define __USBD_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_conf.h" +#include "usbd_def.h" +#include "usbd_ioreq.h" +#include "usbd_ctlreq.h" + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup USBD_CORE + * @brief This file is the Header file for usbd_core.c file + * @{ + */ + + +/** @defgroup USBD_CORE_Exported_Defines + * @{ + */ +#ifndef USBD_DEBUG_LEVEL +#define USBD_DEBUG_LEVEL 0U +#endif /* USBD_DEBUG_LEVEL */ +/** + * @} + */ + + +/** @defgroup USBD_CORE_Exported_TypesDefinitions + * @{ + */ + + +/** + * @} + */ + + + +/** @defgroup USBD_CORE_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_CORE_Exported_Variables + * @{ + */ +#define USBD_SOF USBD_LL_SOF +/** + * @} + */ + +/** @defgroup USBD_CORE_Exported_FunctionsPrototype + * @{ + */ +USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id); +USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_Start(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_Stop(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass); + +USBD_StatusTypeDef USBD_RunTestMode(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx); + +USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup); +USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev, uint8_t epnum, uint8_t *pdata); +USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev, uint8_t epnum, uint8_t *pdata); + +USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, USBD_SpeedTypeDef speed); +USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev); + +USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); +USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); + +USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev); + +/* USBD Low Level Driver */ +USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_DeInit(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_Stop(USBD_HandleTypeDef *pdev); + +USBD_StatusTypeDef USBD_LL_OpenEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr, + uint8_t ep_type, uint16_t ep_mps); + +USBD_StatusTypeDef USBD_LL_CloseEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr); +USBD_StatusTypeDef USBD_LL_FlushEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr); +USBD_StatusTypeDef USBD_LL_StallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr); +USBD_StatusTypeDef USBD_LL_ClearStallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr); +USBD_StatusTypeDef USBD_LL_SetUSBAddress(USBD_HandleTypeDef *pdev, uint8_t dev_addr); + +USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, + uint8_t *pbuf, uint32_t size); + +USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, + uint8_t *pbuf, uint32_t size); + +uint8_t USBD_LL_IsStallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr); +uint32_t USBD_LL_GetRxDataSize(USBD_HandleTypeDef *pdev, uint8_t ep_addr); + +void USBD_LL_Delay(uint32_t Delay); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USBD_CORE_H */ + +/** + * @} + */ + +/** +* @} +*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + + + diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h new file mode 100644 index 000000000..f973a8b1b --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h @@ -0,0 +1,103 @@ +/** + ****************************************************************************** + * @file usbd_req.h + * @author MCD Application Team + * @brief Header file for the usbd_req.c file + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USB_REQUEST_H +#define __USB_REQUEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_def.h" + + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup USBD_REQ + * @brief header file for the usbd_req.c file + * @{ + */ + +/** @defgroup USBD_REQ_Exported_Defines + * @{ + */ +/** + * @} + */ + + +/** @defgroup USBD_REQ_Exported_Types + * @{ + */ +/** + * @} + */ + + + +/** @defgroup USBD_REQ_Exported_Macros + * @{ + */ +/** + * @} + */ + +/** @defgroup USBD_REQ_Exported_Variables + * @{ + */ +/** + * @} + */ + +/** @defgroup USBD_REQ_Exported_FunctionsPrototype + * @{ + */ + +USBD_StatusTypeDef USBD_StdDevReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +USBD_StatusTypeDef USBD_StdItfReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +USBD_StatusTypeDef USBD_StdEPReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); + +void USBD_CtlError(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +void USBD_ParseSetupRequest(USBD_SetupReqTypedef *req, uint8_t *pdata); +void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USB_REQUEST_H */ + +/** + * @} + */ + +/** +* @} +*/ + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_def.h b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_def.h new file mode 100644 index 000000000..7441ee65c --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_def.h @@ -0,0 +1,395 @@ +/** + ****************************************************************************** + * @file usbd_def.h + * @author MCD Application Team + * @brief General defines for the usb device library + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USBD_DEF_H +#define __USBD_DEF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_conf.h" + +/** @addtogroup STM32_USBD_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup USB_DEF + * @brief general defines for the usb device library file + * @{ + */ + +/** @defgroup USB_DEF_Exported_Defines + * @{ + */ + +#ifndef NULL +#define NULL 0U +#endif /* NULL */ + +#ifndef USBD_MAX_NUM_INTERFACES +#define USBD_MAX_NUM_INTERFACES 1U +#endif /* USBD_MAX_NUM_CONFIGURATION */ + +#ifndef USBD_MAX_NUM_CONFIGURATION +#define USBD_MAX_NUM_CONFIGURATION 1U +#endif /* USBD_MAX_NUM_CONFIGURATION */ + +#ifndef USBD_LPM_ENABLED +#define USBD_LPM_ENABLED 0U +#endif /* USBD_LPM_ENABLED */ + +#ifndef USBD_SELF_POWERED +#define USBD_SELF_POWERED 1U +#endif /*USBD_SELF_POWERED */ + +#ifndef USBD_SUPPORT_USER_STRING_DESC +#define USBD_SUPPORT_USER_STRING_DESC 0U +#endif /* USBD_SUPPORT_USER_STRING_DESC */ + +#ifndef USBD_CLASS_USER_STRING_DESC +#define USBD_CLASS_USER_STRING_DESC 0U +#endif /* USBD_CLASS_USER_STRING_DESC */ + +#define USB_LEN_DEV_QUALIFIER_DESC 0x0AU +#define USB_LEN_DEV_DESC 0x12U +#define USB_LEN_CFG_DESC 0x09U +#define USB_LEN_IF_DESC 0x09U +#define USB_LEN_EP_DESC 0x07U +#define USB_LEN_OTG_DESC 0x03U +#define USB_LEN_LANGID_STR_DESC 0x04U +#define USB_LEN_OTHER_SPEED_DESC_SIZ 0x09U + +#define USBD_IDX_LANGID_STR 0x00U +#define USBD_IDX_MFC_STR 0x01U +#define USBD_IDX_PRODUCT_STR 0x02U +#define USBD_IDX_SERIAL_STR 0x03U +#define USBD_IDX_CONFIG_STR 0x04U +#define USBD_IDX_INTERFACE_STR 0x05U +#define USBD_IDX_ODRIVE_INTF_STR 0x06 +#define USBD_IDX_MICROSOFT_DESC_STR 0xEE + +#define USB_REQ_TYPE_STANDARD 0x00U +#define USB_REQ_TYPE_CLASS 0x20U +#define USB_REQ_TYPE_VENDOR 0x40U +#define USB_REQ_TYPE_MASK 0x60U + +#define USB_REQ_RECIPIENT_DEVICE 0x00U +#define USB_REQ_RECIPIENT_INTERFACE 0x01U +#define USB_REQ_RECIPIENT_ENDPOINT 0x02U +#define USB_REQ_RECIPIENT_MASK 0x03U + +#define USB_REQ_GET_STATUS 0x00U +#define USB_REQ_CLEAR_FEATURE 0x01U +#define USB_REQ_SET_FEATURE 0x03U +#define USB_REQ_SET_ADDRESS 0x05U +#define USB_REQ_GET_DESCRIPTOR 0x06U +#define USB_REQ_SET_DESCRIPTOR 0x07U +#define USB_REQ_GET_CONFIGURATION 0x08U +#define USB_REQ_SET_CONFIGURATION 0x09U +#define USB_REQ_GET_INTERFACE 0x0AU +#define USB_REQ_SET_INTERFACE 0x0BU +#define USB_REQ_SYNCH_FRAME 0x0CU + +#define USB_DESC_TYPE_DEVICE 0x01U +#define USB_DESC_TYPE_CONFIGURATION 0x02U +#define USB_DESC_TYPE_STRING 0x03U +#define USB_DESC_TYPE_INTERFACE 0x04U +#define USB_DESC_TYPE_ENDPOINT 0x05U +#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06U +#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 0x07U +#define USB_DESC_TYPE_BOS 0x0FU + +#define USB_CONFIG_REMOTE_WAKEUP 0x02U +#define USB_CONFIG_SELF_POWERED 0x01U + +#define USB_FEATURE_EP_HALT 0x00U +#define USB_FEATURE_REMOTE_WAKEUP 0x01U +#define USB_FEATURE_TEST_MODE 0x02U + +#define USB_DEVICE_CAPABITY_TYPE 0x10U + +#define USB_HS_MAX_PACKET_SIZE 512U +#define USB_FS_MAX_PACKET_SIZE 64U +#define USB_MAX_EP0_SIZE 64U + +/* Device Status */ +#define USBD_STATE_DEFAULT 0x01U +#define USBD_STATE_ADDRESSED 0x02U +#define USBD_STATE_CONFIGURED 0x03U +#define USBD_STATE_SUSPENDED 0x04U + + +/* EP0 State */ +#define USBD_EP0_IDLE 0x00U +#define USBD_EP0_SETUP 0x01U +#define USBD_EP0_DATA_IN 0x02U +#define USBD_EP0_DATA_OUT 0x03U +#define USBD_EP0_STATUS_IN 0x04U +#define USBD_EP0_STATUS_OUT 0x05U +#define USBD_EP0_STALL 0x06U + +#define USBD_EP_TYPE_CTRL 0x00U +#define USBD_EP_TYPE_ISOC 0x01U +#define USBD_EP_TYPE_BULK 0x02U +#define USBD_EP_TYPE_INTR 0x03U + + +/** + * @} + */ + + +/** @defgroup USBD_DEF_Exported_TypesDefinitions + * @{ + */ + +typedef struct usb_setup_req +{ + uint8_t bmRequest; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} USBD_SetupReqTypedef; + +typedef struct +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t wDescriptorLengthLow; + uint8_t wDescriptorLengthHigh; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} USBD_ConfigDescTypedef; + +typedef struct +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCaps; +} USBD_BosDescTypedef; + + +struct _USBD_HandleTypeDef; + +typedef struct _Device_cb +{ + uint8_t (*Init)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx); + uint8_t (*DeInit)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx); + /* Control Endpoints*/ + uint8_t (*Setup)(struct _USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); + uint8_t (*EP0_TxSent)(struct _USBD_HandleTypeDef *pdev); + uint8_t (*EP0_RxReady)(struct _USBD_HandleTypeDef *pdev); + /* Class Specific Endpoints*/ + uint8_t (*DataIn)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); + uint8_t (*DataOut)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); + uint8_t (*SOF)(struct _USBD_HandleTypeDef *pdev); + uint8_t (*IsoINIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); + uint8_t (*IsoOUTIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); + + uint8_t *(*GetHSConfigDescriptor)(uint16_t *length); + uint8_t *(*GetFSConfigDescriptor)(uint16_t *length); + uint8_t *(*GetOtherSpeedConfigDescriptor)(uint16_t *length); + uint8_t *(*GetDeviceQualifierDescriptor)(uint16_t *length); +#if (USBD_SUPPORT_USER_STRING_DESC == 1U) + uint8_t *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length); +#endif + +} USBD_ClassTypeDef; + +/* Following USB Device Speed */ +typedef enum +{ + USBD_SPEED_HIGH = 0U, + USBD_SPEED_FULL = 1U, + USBD_SPEED_LOW = 2U, +} USBD_SpeedTypeDef; + +/* Following USB Device status */ +typedef enum +{ + USBD_OK = 0U, + USBD_BUSY, + USBD_EMEM, + USBD_FAIL, +} USBD_StatusTypeDef; + +/* USB Device descriptors structure */ +typedef struct +{ + uint8_t *(*GetDeviceDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetLangIDStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetManufacturerStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetProductStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetSerialStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetConfigurationStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetInterfaceStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); +#if (USBD_CLASS_USER_STRING_DESC == 1) + uint8_t *(*GetUserStrDescriptor)(USBD_SpeedTypeDef speed, uint8_t idx, uint16_t *length); +#endif +#if ((USBD_LPM_ENABLED == 1U) || (USBD_CLASS_BOS_ENABLED == 1)) + uint8_t *(*GetBOSDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); +#endif +} USBD_DescriptorsTypeDef; + +/* USB Device handle structure */ +typedef struct +{ + uint32_t status; + uint32_t total_length; + uint32_t rem_length; + uint32_t maxpacket; + uint16_t is_used; + uint16_t bInterval; +} USBD_EndpointTypeDef; + +/* USB Device handle structure */ +typedef struct _USBD_HandleTypeDef +{ + uint8_t id; + uint32_t dev_config; + uint32_t dev_default_config; + uint32_t dev_config_status; + USBD_SpeedTypeDef dev_speed; + USBD_EndpointTypeDef ep_in[16]; + USBD_EndpointTypeDef ep_out[16]; + uint32_t ep0_state; + uint32_t ep0_data_len; + uint8_t dev_state; + uint8_t dev_old_state; + uint8_t dev_address; + uint8_t dev_connection_status; + uint8_t dev_test_mode; + uint32_t dev_remote_wakeup; + uint8_t ConfIdx; + + USBD_SetupReqTypedef request; + USBD_DescriptorsTypeDef *pDesc; + USBD_ClassTypeDef *pClass; + void *pClassData; + void *pUserData; + void *pData; + void *pBosDesc; + void *pConfDesc; +} USBD_HandleTypeDef; + +/** + * @} + */ + + + +/** @defgroup USBD_DEF_Exported_Macros + * @{ + */ +__STATIC_INLINE uint16_t SWAPBYTE(uint8_t *addr) +{ + uint16_t _SwapVal, _Byte1, _Byte2; + uint8_t *_pbuff = addr; + + _Byte1 = *(uint8_t *)_pbuff; + _pbuff++; + _Byte2 = *(uint8_t *)_pbuff; + + _SwapVal = (_Byte2 << 8) | _Byte1; + + return _SwapVal; +} + +#define LOBYTE(x) ((uint8_t)((x) & 0x00FFU)) +#define HIBYTE(x) ((uint8_t)(((x) & 0xFF00U) >> 8U)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + +#if defined ( __GNUC__ ) +#ifndef __weak +#define __weak __attribute__((weak)) +#endif /* __weak */ +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif /* __packed */ +#endif /* __GNUC__ */ + + +/* In HS mode and when the DMA is used, all variables and data structures dealing + with the DMA during the transaction process should be 4-bytes aligned */ + +#if defined ( __GNUC__ ) && !defined (__CC_ARM) /* GNU Compiler */ +#ifndef __ALIGN_END +#define __ALIGN_END __attribute__ ((aligned (4U))) +#endif /* __ALIGN_END */ +#ifndef __ALIGN_BEGIN +#define __ALIGN_BEGIN +#endif /* __ALIGN_BEGIN */ +#else +#ifndef __ALIGN_END +#define __ALIGN_END +#endif /* __ALIGN_END */ +#ifndef __ALIGN_BEGIN +#if defined (__CC_ARM) /* ARM Compiler */ +#define __ALIGN_BEGIN __align(4U) +#elif defined (__ICCARM__) /* IAR Compiler */ +#define __ALIGN_BEGIN +#endif /* __CC_ARM */ +#endif /* __ALIGN_BEGIN */ +#endif /* __GNUC__ */ + + +/** + * @} + */ + +/** @defgroup USBD_DEF_Exported_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_DEF_Exported_FunctionsPrototype + * @{ + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USBD_DEF_H */ + +/** + * @} + */ + +/** +* @} +*/ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h new file mode 100644 index 000000000..b7159d53b --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h @@ -0,0 +1,114 @@ +/** + ****************************************************************************** + * @file usbd_ioreq.h + * @author MCD Application Team + * @brief Header file for the usbd_ioreq.c file + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USBD_IOREQ_H +#define __USBD_IOREQ_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_def.h" +#include "usbd_core.h" + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup USBD_IOREQ + * @brief header file for the usbd_ioreq.c file + * @{ + */ + +/** @defgroup USBD_IOREQ_Exported_Defines + * @{ + */ +/** + * @} + */ + + +/** @defgroup USBD_IOREQ_Exported_Types + * @{ + */ + + +/** + * @} + */ + + + +/** @defgroup USBD_IOREQ_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_IOREQ_Exported_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_IOREQ_Exported_FunctionsPrototype + * @{ + */ + +USBD_StatusTypeDef USBD_CtlSendData(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len); + +USBD_StatusTypeDef USBD_CtlContinueSendData(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len); + +USBD_StatusTypeDef USBD_CtlPrepareRx(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len); + +USBD_StatusTypeDef USBD_CtlContinueRx(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len); + +USBD_StatusTypeDef USBD_CtlSendStatus(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_CtlReceiveStatus(USBD_HandleTypeDef *pdev); + +uint32_t USBD_GetRxCount(USBD_HandleTypeDef *pdev, uint8_t ep_addr); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USBD_IOREQ_H */ + +/** + * @} + */ + +/** +* @} +*/ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_core.c b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_core.c new file mode 100644 index 000000000..3faed3520 --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_core.c @@ -0,0 +1,669 @@ +/** + ****************************************************************************** + * @file usbd_core.c + * @author MCD Application Team + * @brief This file provides all the USBD core functions. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_core.h" + +/** @addtogroup STM32_USBD_DEVICE_LIBRARY +* @{ +*/ + + +/** @defgroup USBD_CORE +* @brief usbd core module +* @{ +*/ + +/** @defgroup USBD_CORE_Private_TypesDefinitions +* @{ +*/ + +/** +* @} +*/ + + +/** @defgroup USBD_CORE_Private_Defines +* @{ +*/ + +/** +* @} +*/ + + +/** @defgroup USBD_CORE_Private_Macros +* @{ +*/ + +/** +* @} +*/ + + +/** @defgroup USBD_CORE_Private_FunctionPrototypes +* @{ +*/ + +/** +* @} +*/ + +/** @defgroup USBD_CORE_Private_Variables +* @{ +*/ + +/** +* @} +*/ + + +/** @defgroup USBD_CORE_Private_Functions +* @{ +*/ + +/** +* @brief USBD_Init +* Initializes the device stack and load the class driver +* @param pdev: device instance +* @param pdesc: Descriptor structure address +* @param id: Low level core index +* @retval None +*/ +USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, + USBD_DescriptorsTypeDef *pdesc, uint8_t id) +{ + USBD_StatusTypeDef ret; + + /* Check whether the USB Host handle is valid */ + if (pdev == NULL) + { +#if (USBD_DEBUG_LEVEL > 1U) + USBD_ErrLog("Invalid Device handle"); +#endif + return USBD_FAIL; + } + + /* Unlink previous class */ + if (pdev->pClass != NULL) + { + pdev->pClass = NULL; + } + + if (pdev->pConfDesc != NULL) + { + pdev->pConfDesc = NULL; + } + + /* Assign USBD Descriptors */ + if (pdesc != NULL) + { + pdev->pDesc = pdesc; + } + + /* Set Device initial State */ + pdev->dev_state = USBD_STATE_DEFAULT; + pdev->id = id; + + /* Initialize low level driver */ + ret = USBD_LL_Init(pdev); + + return ret; +} + +/** +* @brief USBD_DeInit +* Re-Initialize th device library +* @param pdev: device instance +* @retval status: status +*/ +USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev) +{ + USBD_StatusTypeDef ret; + + /* Set Default State */ + pdev->dev_state = USBD_STATE_DEFAULT; + + /* Free Class Resources */ + if (pdev->pClass != NULL) + { + pdev->pClass->DeInit(pdev, (uint8_t)pdev->dev_config); + } + + if (pdev->pConfDesc != NULL) + { + pdev->pConfDesc = NULL; + } + + /* Stop the low level driver */ + ret = USBD_LL_Stop(pdev); + + if (ret != USBD_OK) + { + return ret; + } + + /* Initialize low level driver */ + ret = USBD_LL_DeInit(pdev); + + return ret; +} + +/** + * @brief USBD_RegisterClass + * Link class driver to Device Core. + * @param pDevice : Device Handle + * @param pclass: Class handle + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass) +{ + uint16_t len = 0U; + + if (pclass == NULL) + { +#if (USBD_DEBUG_LEVEL > 1U) + USBD_ErrLog("Invalid Class handle"); +#endif + return USBD_FAIL; + } + + /* link the class to the USB Device handle */ + pdev->pClass = pclass; + + /* Get Device Configuration Descriptor */ +#ifdef USE_USB_FS + pdev->pConfDesc = (void *)pdev->pClass->GetFSConfigDescriptor(&len); +#else /* USE_USB_HS */ + pdev->pConfDesc = (void *)pdev->pClass->GetHSConfigDescriptor(&len); +#endif /* USE_USB_FS */ + + + return USBD_OK; +} + +/** + * @brief USBD_Start + * Start the USB Device Core. + * @param pdev: Device Handle + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_Start(USBD_HandleTypeDef *pdev) +{ + /* Start the low level driver */ + return USBD_LL_Start(pdev); +} + +/** + * @brief USBD_Stop + * Stop the USB Device Core. + * @param pdev: Device Handle + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_Stop(USBD_HandleTypeDef *pdev) +{ + USBD_StatusTypeDef ret; + + /* Free Class Resources */ + if (pdev->pClass != NULL) + { + pdev->pClass->DeInit(pdev, (uint8_t)pdev->dev_config); + } + + if (pdev->pConfDesc != NULL) + { + pdev->pConfDesc = NULL; + } + + /* Stop the low level driver */ + ret = USBD_LL_Stop(pdev); + + return ret; +} + +/** +* @brief USBD_RunTestMode +* Launch test mode process +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_RunTestMode(USBD_HandleTypeDef *pdev) +{ + /* Prevent unused argument compilation warning */ + UNUSED(pdev); + + return USBD_OK; +} + +/** +* @brief USBD_SetClassConfig +* Configure device and start the interface +* @param pdev: device instance +* @param cfgidx: configuration index +* @retval status +*/ + +USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx) +{ + USBD_StatusTypeDef ret = USBD_FAIL; + + if (pdev->pClass != NULL) + { + /* Set configuration and Start the Class */ + ret = (USBD_StatusTypeDef)pdev->pClass->Init(pdev, cfgidx); + } + + return ret; +} + +/** +* @brief USBD_ClrClassConfig +* Clear current configuration +* @param pdev: device instance +* @param cfgidx: configuration index +* @retval status: USBD_StatusTypeDef +*/ +USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx) +{ + /* Clear configuration and De-initialize the Class process */ + if (pdev->pClass != NULL) + { + pdev->pClass->DeInit(pdev, cfgidx); + } + + return USBD_OK; +} + + +/** +* @brief USBD_SetupStage +* Handle the setup stage +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup) +{ + USBD_StatusTypeDef ret; + + USBD_ParseSetupRequest(&pdev->request, psetup); + + pdev->ep0_state = USBD_EP0_SETUP; + + pdev->ep0_data_len = pdev->request.wLength; + + switch (pdev->request.bmRequest & 0x1FU) + { + case USB_REQ_RECIPIENT_DEVICE: + ret = USBD_StdDevReq(pdev, &pdev->request); + break; + + case USB_REQ_RECIPIENT_INTERFACE: + ret = USBD_StdItfReq(pdev, &pdev->request); + break; + + case USB_REQ_RECIPIENT_ENDPOINT: + ret = USBD_StdEPReq(pdev, &pdev->request); + break; + + default: + ret = USBD_LL_StallEP(pdev, (pdev->request.bmRequest & 0x80U)); + break; + } + + return ret; +} + +/** +* @brief USBD_DataOutStage +* Handle data OUT stage +* @param pdev: device instance +* @param epnum: endpoint index +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev, + uint8_t epnum, uint8_t *pdata) +{ + USBD_EndpointTypeDef *pep; + USBD_StatusTypeDef ret; + + if (epnum == 0U) + { + pep = &pdev->ep_out[0]; + + if (pdev->ep0_state == USBD_EP0_DATA_OUT) + { + if (pep->rem_length > pep->maxpacket) + { + pep->rem_length -= pep->maxpacket; + + (void)USBD_CtlContinueRx(pdev, pdata, MIN(pep->rem_length, pep->maxpacket)); + } + else + { + if ((pdev->pClass->EP0_RxReady != NULL) && + (pdev->dev_state == USBD_STATE_CONFIGURED)) + { + pdev->pClass->EP0_RxReady(pdev); + } + (void)USBD_CtlSendStatus(pdev); + } + } + else + { +#if 0 + if (pdev->ep0_state == USBD_EP0_STATUS_OUT) + { + /* + * STATUS PHASE completed, update ep0_state to idle + */ + pdev->ep0_state = USBD_EP0_IDLE; + (void)USBD_LL_StallEP(pdev, 0U); + } +#endif + } + } + else if ((pdev->pClass->DataOut != NULL) && + (pdev->dev_state == USBD_STATE_CONFIGURED)) + { + ret = (USBD_StatusTypeDef)pdev->pClass->DataOut(pdev, epnum); + + if (ret != USBD_OK) + { + return ret; + } + } + else + { + /* should never be in this condition */ + return USBD_FAIL; + } + + return USBD_OK; +} + +/** +* @brief USBD_DataInStage +* Handle data in stage +* @param pdev: device instance +* @param epnum: endpoint index +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev, + uint8_t epnum, uint8_t *pdata) +{ + USBD_EndpointTypeDef *pep; + USBD_StatusTypeDef ret; + + if (epnum == 0U) + { + pep = &pdev->ep_in[0]; + + if (pdev->ep0_state == USBD_EP0_DATA_IN) + { + if (pep->rem_length > pep->maxpacket) + { + pep->rem_length -= pep->maxpacket; + + (void)USBD_CtlContinueSendData(pdev, pdata, pep->rem_length); + + /* Prepare endpoint for premature end of transfer */ + (void)USBD_LL_PrepareReceive(pdev, 0U, NULL, 0U); + } + else + { + /* last packet is MPS multiple, so send ZLP packet */ + if ((pep->maxpacket == pep->rem_length) && + (pep->total_length >= pep->maxpacket) && + (pep->total_length < pdev->ep0_data_len)) + { + (void)USBD_CtlContinueSendData(pdev, NULL, 0U); + pdev->ep0_data_len = 0U; + + /* Prepare endpoint for premature end of transfer */ + (void)USBD_LL_PrepareReceive(pdev, 0U, NULL, 0U); + } + else + { + if ((pdev->pClass->EP0_TxSent != NULL) && + (pdev->dev_state == USBD_STATE_CONFIGURED)) + { + pdev->pClass->EP0_TxSent(pdev); + } + (void)USBD_LL_StallEP(pdev, 0x80U); + (void)USBD_CtlReceiveStatus(pdev); + } + } + } + else + { +#if 0 + if ((pdev->ep0_state == USBD_EP0_STATUS_IN) || + (pdev->ep0_state == USBD_EP0_IDLE)) + { + (void)USBD_LL_StallEP(pdev, 0x80U); + } +#endif + } + + if (pdev->dev_test_mode == 1U) + { + (void)USBD_RunTestMode(pdev); + pdev->dev_test_mode = 0U; + } + } + else if ((pdev->pClass->DataIn != NULL) && + (pdev->dev_state == USBD_STATE_CONFIGURED)) + { + ret = (USBD_StatusTypeDef)pdev->pClass->DataIn(pdev, epnum); + + if (ret != USBD_OK) + { + return ret; + } + } + else + { + /* should never be in this condition */ + return USBD_FAIL; + } + + return USBD_OK; +} + +/** +* @brief USBD_LL_Reset +* Handle Reset event +* @param pdev: device instance +* @retval status +*/ + +USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev) +{ + /* Upon Reset call user call back */ + pdev->dev_state = USBD_STATE_DEFAULT; + pdev->ep0_state = USBD_EP0_IDLE; + pdev->dev_config = 0U; + pdev->dev_remote_wakeup = 0U; + + if (pdev->pClassData != NULL) + { + pdev->pClass->DeInit(pdev, (uint8_t)pdev->dev_config); + } + + /* Open EP0 OUT */ + (void)USBD_LL_OpenEP(pdev, 0x00U, USBD_EP_TYPE_CTRL, USB_MAX_EP0_SIZE); + pdev->ep_out[0x00U & 0xFU].is_used = 1U; + + pdev->ep_out[0].maxpacket = USB_MAX_EP0_SIZE; + + /* Open EP0 IN */ + (void)USBD_LL_OpenEP(pdev, 0x80U, USBD_EP_TYPE_CTRL, USB_MAX_EP0_SIZE); + pdev->ep_in[0x80U & 0xFU].is_used = 1U; + + pdev->ep_in[0].maxpacket = USB_MAX_EP0_SIZE; + + return USBD_OK; +} + +/** +* @brief USBD_LL_Reset +* Handle Reset event +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, + USBD_SpeedTypeDef speed) +{ + pdev->dev_speed = speed; + + return USBD_OK; +} + +/** +* @brief USBD_Suspend +* Handle Suspend event +* @param pdev: device instance +* @retval status +*/ + +USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev) +{ + pdev->dev_old_state = pdev->dev_state; + pdev->dev_state = USBD_STATE_SUSPENDED; + + return USBD_OK; +} + +/** +* @brief USBD_Resume +* Handle Resume event +* @param pdev: device instance +* @retval status +*/ + +USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev) +{ + if (pdev->dev_state == USBD_STATE_SUSPENDED) + { + pdev->dev_state = pdev->dev_old_state; + } + + return USBD_OK; +} + +/** +* @brief USBD_SOF +* Handle SOF event +* @param pdev: device instance +* @retval status +*/ + +USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev) +{ + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + if (pdev->pClass->SOF != NULL) + { + pdev->pClass->SOF(pdev); + } + } + + return USBD_OK; +} + +/** +* @brief USBD_IsoINIncomplete +* Handle iso in incomplete event +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev, + uint8_t epnum) +{ + /* Prevent unused arguments compilation warning */ + UNUSED(pdev); + UNUSED(epnum); + + return USBD_OK; +} + +/** +* @brief USBD_IsoOUTIncomplete +* Handle iso out incomplete event +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev, + uint8_t epnum) +{ + /* Prevent unused arguments compilation warning */ + UNUSED(pdev); + UNUSED(epnum); + + return USBD_OK; +} + +/** +* @brief USBD_DevConnected +* Handle device connection event +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev) +{ + /* Prevent unused argument compilation warning */ + UNUSED(pdev); + + return USBD_OK; +} + +/** +* @brief USBD_DevDisconnected +* Handle device disconnection event +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev) +{ + /* Free Class Resources */ + pdev->dev_state = USBD_STATE_DEFAULT; + + if (pdev->pClass != NULL) + { + pdev->pClass->DeInit(pdev, (uint8_t)pdev->dev_config); + } + + return USBD_OK; +} +/** +* @} +*/ + + +/** +* @} +*/ + + +/** +* @} +*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c new file mode 100644 index 000000000..c31d40e0a --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c @@ -0,0 +1,944 @@ +/** + ****************************************************************************** + * @file usbd_req.c + * @author MCD Application Team + * @brief This file provides the standard USB requests following chapter 9. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_ctlreq.h" +#include "usbd_ioreq.h" + + +/** @addtogroup STM32_USBD_STATE_DEVICE_LIBRARY + * @{ + */ + + +/** @defgroup USBD_REQ + * @brief USB standard requests module + * @{ + */ + +/** @defgroup USBD_REQ_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_REQ_Private_Defines + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_REQ_Private_Macros + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_REQ_Private_Variables + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_REQ_Private_FunctionPrototypes + * @{ + */ +static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static void USBD_SetAddress(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static USBD_StatusTypeDef USBD_SetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static void USBD_GetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static void USBD_GetStatus(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static void USBD_SetFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static void USBD_ClrFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static uint8_t USBD_GetLen(uint8_t *buf); + +/** + * @} + */ + + +/** @defgroup USBD_REQ_Private_Functions + * @{ + */ + + +/** +* @brief USBD_StdDevReq +* Handle standard usb device requests +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +USBD_StatusTypeDef USBD_StdDevReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + USBD_StatusTypeDef ret = USBD_OK; + + switch (req->bmRequest & USB_REQ_TYPE_MASK) + { + case USB_REQ_TYPE_CLASS: + case USB_REQ_TYPE_VENDOR: + ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); + break; + + case USB_REQ_TYPE_STANDARD: + switch (req->bRequest) + { + case USB_REQ_GET_DESCRIPTOR: + USBD_GetDescriptor(pdev, req); + break; + + case USB_REQ_SET_ADDRESS: + USBD_SetAddress(pdev, req); + break; + + case USB_REQ_SET_CONFIGURATION: + ret = USBD_SetConfig(pdev, req); + break; + + case USB_REQ_GET_CONFIGURATION: + USBD_GetConfig(pdev, req); + break; + + case USB_REQ_GET_STATUS: + USBD_GetStatus(pdev, req); + break; + + case USB_REQ_SET_FEATURE: + USBD_SetFeature(pdev, req); + break; + + case USB_REQ_CLEAR_FEATURE: + USBD_ClrFeature(pdev, req); + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + + return ret; +} + +/** +* @brief USBD_StdItfReq +* Handle standard usb interface requests +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +USBD_StatusTypeDef USBD_StdItfReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + USBD_StatusTypeDef ret = USBD_OK; + + switch (req->bmRequest & USB_REQ_TYPE_MASK) + { + case USB_REQ_TYPE_CLASS: + case USB_REQ_TYPE_VENDOR: + case USB_REQ_TYPE_STANDARD: + switch (pdev->dev_state) + { + case USBD_STATE_DEFAULT: + case USBD_STATE_ADDRESSED: + case USBD_STATE_CONFIGURED: + + if (LOBYTE(req->wIndex) <= USBD_MAX_NUM_INTERFACES) + { + ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); + + if ((req->wLength == 0U) && (ret == USBD_OK)) + { + (void)USBD_CtlSendStatus(pdev); + } + } + else + { + USBD_CtlError(pdev, req); + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + + return ret; +} + +/** +* @brief USBD_StdEPReq +* Handle standard usb endpoint requests +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +USBD_StatusTypeDef USBD_StdEPReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + USBD_EndpointTypeDef *pep; + uint8_t ep_addr; + USBD_StatusTypeDef ret = USBD_OK; + ep_addr = LOBYTE(req->wIndex); + + switch (req->bmRequest & USB_REQ_TYPE_MASK) + { + case USB_REQ_TYPE_CLASS: + case USB_REQ_TYPE_VENDOR: + ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); + break; + + case USB_REQ_TYPE_STANDARD: + switch (req->bRequest) + { + case USB_REQ_SET_FEATURE: + switch (pdev->dev_state) + { + case USBD_STATE_ADDRESSED: + if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) + { + (void)USBD_LL_StallEP(pdev, ep_addr); + (void)USBD_LL_StallEP(pdev, 0x80U); + } + else + { + USBD_CtlError(pdev, req); + } + break; + + case USBD_STATE_CONFIGURED: + if (req->wValue == USB_FEATURE_EP_HALT) + { + if ((ep_addr != 0x00U) && (ep_addr != 0x80U) && (req->wLength == 0x00U)) + { + (void)USBD_LL_StallEP(pdev, ep_addr); + } + } + (void)USBD_CtlSendStatus(pdev); + + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + case USB_REQ_CLEAR_FEATURE: + + switch (pdev->dev_state) + { + case USBD_STATE_ADDRESSED: + if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) + { + (void)USBD_LL_StallEP(pdev, ep_addr); + (void)USBD_LL_StallEP(pdev, 0x80U); + } + else + { + USBD_CtlError(pdev, req); + } + break; + + case USBD_STATE_CONFIGURED: + if (req->wValue == USB_FEATURE_EP_HALT) + { + if ((ep_addr & 0x7FU) != 0x00U) + { + (void)USBD_LL_ClearStallEP(pdev, ep_addr); + } + (void)USBD_CtlSendStatus(pdev); + (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + case USB_REQ_GET_STATUS: + switch (pdev->dev_state) + { + case USBD_STATE_ADDRESSED: + if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) + { + USBD_CtlError(pdev, req); + break; + } + pep = ((ep_addr & 0x80U) == 0x80U) ? &pdev->ep_in[ep_addr & 0x7FU] : \ + &pdev->ep_out[ep_addr & 0x7FU]; + + pep->status = 0x0000U; + + (void)USBD_CtlSendData(pdev, (uint8_t *)&pep->status, 2U); + break; + + case USBD_STATE_CONFIGURED: + if ((ep_addr & 0x80U) == 0x80U) + { + if (pdev->ep_in[ep_addr & 0xFU].is_used == 0U) + { + USBD_CtlError(pdev, req); + break; + } + } + else + { + if (pdev->ep_out[ep_addr & 0xFU].is_used == 0U) + { + USBD_CtlError(pdev, req); + break; + } + } + + pep = ((ep_addr & 0x80U) == 0x80U) ? &pdev->ep_in[ep_addr & 0x7FU] : \ + &pdev->ep_out[ep_addr & 0x7FU]; + + if ((ep_addr == 0x00U) || (ep_addr == 0x80U)) + { + pep->status = 0x0000U; + } + else if (USBD_LL_IsStallEP(pdev, ep_addr) != 0U) + { + pep->status = 0x0001U; + } + else + { + pep->status = 0x0000U; + } + + (void)USBD_CtlSendData(pdev, (uint8_t *)&pep->status, 2U); + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + + return ret; +} + + +/** +* @brief USBD_GetDescriptor +* Handle Get Descriptor requests +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + uint16_t len = 0U; + uint8_t *pbuf = NULL; + uint8_t err = 0U; + + switch (req->wValue >> 8) + { +#if ((USBD_LPM_ENABLED == 1U) || (USBD_CLASS_BOS_ENABLED == 1U)) + case USB_DESC_TYPE_BOS: + if (pdev->pDesc->GetBOSDescriptor != NULL) + { + pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; +#endif + case USB_DESC_TYPE_DEVICE: + pbuf = pdev->pDesc->GetDeviceDescriptor(pdev->dev_speed, &len); + break; + + case USB_DESC_TYPE_CONFIGURATION: + if (pdev->dev_speed == USBD_SPEED_HIGH) + { + pbuf = pdev->pClass->GetHSConfigDescriptor(&len); + pbuf[1] = USB_DESC_TYPE_CONFIGURATION; + } + else + { + pbuf = pdev->pClass->GetFSConfigDescriptor(&len); + pbuf[1] = USB_DESC_TYPE_CONFIGURATION; + } + break; + + case USB_DESC_TYPE_STRING: + switch ((uint8_t)(req->wValue)) + { + case USBD_IDX_LANGID_STR: + if (pdev->pDesc->GetLangIDStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetLangIDStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USBD_IDX_MFC_STR: + if (pdev->pDesc->GetManufacturerStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetManufacturerStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USBD_IDX_PRODUCT_STR: + if (pdev->pDesc->GetProductStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetProductStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USBD_IDX_SERIAL_STR: + if (pdev->pDesc->GetSerialStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetSerialStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USBD_IDX_CONFIG_STR: + if (pdev->pDesc->GetConfigurationStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetConfigurationStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USBD_IDX_INTERFACE_STR: + if (pdev->pDesc->GetInterfaceStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetInterfaceStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + default: +#if (USBD_SUPPORT_USER_STRING_DESC == 1U) + if (pdev->pClass->GetUsrStrDescriptor != NULL) + { + pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue), &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } +#elif (USBD_CLASS_USER_STRING_DESC == 1U) + if (pdev->pDesc->GetUserStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetUserStrDescriptor(pdev->dev_speed, (req->wValue), &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } +#else + USBD_CtlError(pdev, req); + err++; +#endif + break; + } + break; + + case USB_DESC_TYPE_DEVICE_QUALIFIER: + if (pdev->dev_speed == USBD_SPEED_HIGH) + { + pbuf = pdev->pClass->GetDeviceQualifierDescriptor(&len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION: + if (pdev->dev_speed == USBD_SPEED_HIGH) + { + pbuf = pdev->pClass->GetOtherSpeedConfigDescriptor(&len); + pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION; + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + default: + USBD_CtlError(pdev, req); + err++; + break; + } + + if (err != 0U) + { + return; + } + else + { + if (req->wLength != 0U) + { + if (len != 0U) + { + len = MIN(len, req->wLength); + (void)USBD_CtlSendData(pdev, pbuf, len); + } + else + { + USBD_CtlError(pdev, req); + } + } + else + { + (void)USBD_CtlSendStatus(pdev); + } + } +} + +/** +* @brief USBD_SetAddress +* Set device address +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_SetAddress(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + uint8_t dev_addr; + + if ((req->wIndex == 0U) && (req->wLength == 0U) && (req->wValue < 128U)) + { + dev_addr = (uint8_t)(req->wValue) & 0x7FU; + + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + USBD_CtlError(pdev, req); + } + else + { + pdev->dev_address = dev_addr; + (void)USBD_LL_SetUSBAddress(pdev, dev_addr); + (void)USBD_CtlSendStatus(pdev); + + if (dev_addr != 0U) + { + pdev->dev_state = USBD_STATE_ADDRESSED; + } + else + { + pdev->dev_state = USBD_STATE_DEFAULT; + } + } + } + else + { + USBD_CtlError(pdev, req); + } +} + +/** +* @brief USBD_SetConfig +* Handle Set device configuration request +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static USBD_StatusTypeDef USBD_SetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + USBD_StatusTypeDef ret = USBD_OK; + static uint8_t cfgidx; + + cfgidx = (uint8_t)(req->wValue); + + if (cfgidx > USBD_MAX_NUM_CONFIGURATION) + { + USBD_CtlError(pdev, req); + return USBD_FAIL; + } + + switch (pdev->dev_state) + { + case USBD_STATE_ADDRESSED: + if (cfgidx != 0U) + { + pdev->dev_config = cfgidx; + + ret = USBD_SetClassConfig(pdev, cfgidx); + + if (ret != USBD_OK) + { + USBD_CtlError(pdev, req); + } + else + { + (void)USBD_CtlSendStatus(pdev); + pdev->dev_state = USBD_STATE_CONFIGURED; + } + } + else + { + (void)USBD_CtlSendStatus(pdev); + } + break; + + case USBD_STATE_CONFIGURED: + if (cfgidx == 0U) + { + pdev->dev_state = USBD_STATE_ADDRESSED; + pdev->dev_config = cfgidx; + (void)USBD_ClrClassConfig(pdev, cfgidx); + (void)USBD_CtlSendStatus(pdev); + } + else if (cfgidx != pdev->dev_config) + { + /* Clear old configuration */ + (void)USBD_ClrClassConfig(pdev, (uint8_t)pdev->dev_config); + + /* set new configuration */ + pdev->dev_config = cfgidx; + + ret = USBD_SetClassConfig(pdev, cfgidx); + + if (ret != USBD_OK) + { + USBD_CtlError(pdev, req); + (void)USBD_ClrClassConfig(pdev, (uint8_t)pdev->dev_config); + pdev->dev_state = USBD_STATE_ADDRESSED; + } + else + { + (void)USBD_CtlSendStatus(pdev); + } + } + else + { + (void)USBD_CtlSendStatus(pdev); + } + break; + + default: + USBD_CtlError(pdev, req); + (void)USBD_ClrClassConfig(pdev, cfgidx); + ret = USBD_FAIL; + break; + } + + return ret; +} + +/** +* @brief USBD_GetConfig +* Handle Get device configuration request +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_GetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + if (req->wLength != 1U) + { + USBD_CtlError(pdev, req); + } + else + { + switch (pdev->dev_state) + { + case USBD_STATE_DEFAULT: + case USBD_STATE_ADDRESSED: + pdev->dev_default_config = 0U; + (void)USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_default_config, 1U); + break; + + case USBD_STATE_CONFIGURED: + (void)USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_config, 1U); + break; + + default: + USBD_CtlError(pdev, req); + break; + } + } +} + +/** +* @brief USBD_GetStatus +* Handle Get Status request +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_GetStatus(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + switch (pdev->dev_state) + { + case USBD_STATE_DEFAULT: + case USBD_STATE_ADDRESSED: + case USBD_STATE_CONFIGURED: + if (req->wLength != 0x2U) + { + USBD_CtlError(pdev, req); + break; + } + +#if (USBD_SELF_POWERED == 1U) + pdev->dev_config_status = USB_CONFIG_SELF_POWERED; +#else + pdev->dev_config_status = 0U; +#endif + + if (pdev->dev_remote_wakeup != 0U) + { + pdev->dev_config_status |= USB_CONFIG_REMOTE_WAKEUP; + } + + (void)USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_config_status, 2U); + break; + + default: + USBD_CtlError(pdev, req); + break; + } +} + + +/** +* @brief USBD_SetFeature +* Handle Set device feature request +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_SetFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) + { + pdev->dev_remote_wakeup = 1U; + (void)USBD_CtlSendStatus(pdev); + } +} + + +/** +* @brief USBD_ClrFeature +* Handle clear device feature request +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_ClrFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + switch (pdev->dev_state) + { + case USBD_STATE_DEFAULT: + case USBD_STATE_ADDRESSED: + case USBD_STATE_CONFIGURED: + if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) + { + pdev->dev_remote_wakeup = 0U; + (void)USBD_CtlSendStatus(pdev); + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } +} + +/** +* @brief USBD_ParseSetupRequest +* Copy buffer into setup structure +* @param pdev: device instance +* @param req: usb request +* @retval None +*/ + +void USBD_ParseSetupRequest(USBD_SetupReqTypedef *req, uint8_t *pdata) +{ + uint8_t *pbuff = pdata; + + req->bmRequest = *(uint8_t *)(pbuff); + + pbuff++; + req->bRequest = *(uint8_t *)(pbuff); + + pbuff++; + req->wValue = SWAPBYTE(pbuff); + + pbuff++; + pbuff++; + req->wIndex = SWAPBYTE(pbuff); + + pbuff++; + pbuff++; + req->wLength = SWAPBYTE(pbuff); +} + +/** +* @brief USBD_CtlError +* Handle USB low level Error +* @param pdev: device instance +* @param req: usb request +* @retval None +*/ + +void USBD_CtlError(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + UNUSED(req); + + (void)USBD_LL_StallEP(pdev, 0x80U); + (void)USBD_LL_StallEP(pdev, 0U); +} + + +/** + * @brief USBD_GetString + * Convert Ascii string into unicode one + * @param desc : descriptor buffer + * @param unicode : Formatted string buffer (unicode) + * @param len : descriptor length + * @retval None + */ +void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len) +{ + uint8_t idx = 0U; + uint8_t *pdesc; + + if (desc == NULL) + { + return; + } + + pdesc = desc; + *len = ((uint16_t)USBD_GetLen(pdesc) * 2U) + 2U; + + unicode[idx] = *(uint8_t *)len; + idx++; + unicode[idx] = USB_DESC_TYPE_STRING; + idx++; + + while (*pdesc != (uint8_t)'\0') + { + unicode[idx] = *pdesc; + pdesc++; + idx++; + + unicode[idx] = 0U; + idx++; + } +} + +/** + * @brief USBD_GetLen + * return the string length + * @param buf : pointer to the ascii string buffer + * @retval string length + */ +static uint8_t USBD_GetLen(uint8_t *buf) +{ + uint8_t len = 0U; + uint8_t *pbuff = buf; + + while (*pbuff != (uint8_t)'\0') + { + len++; + pbuff++; + } + + return len; +} +/** + * @} + */ + + +/** + * @} + */ + + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c similarity index 51% rename from Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c rename to Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c index 093afad86..8ac5491ff 100644 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c @@ -2,28 +2,20 @@ ****************************************************************************** * @file usbd_ioreq.c * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 * @brief This file provides the IO requests APIs for control endpoints. ****************************************************************************** * @attention * - *

© COPYRIGHT 2015 STMicroelectronics

+ *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

* - * Licensed under MCD-ST Liberty SW License Agreement V2, (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.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 * ****************************************************************************** - */ + */ /* Includes ------------------------------------------------------------------*/ #include "usbd_ioreq.h" @@ -33,56 +25,56 @@ */ -/** @defgroup USBD_IOREQ +/** @defgroup USBD_IOREQ * @brief control I/O requests module * @{ - */ + */ /** @defgroup USBD_IOREQ_Private_TypesDefinitions * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_IOREQ_Private_Defines * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_IOREQ_Private_Macros * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_IOREQ_Private_Variables * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_IOREQ_Private_FunctionPrototypes * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_IOREQ_Private_Functions * @{ - */ + */ /** * @brief USBD_CtlSendData @@ -92,17 +84,17 @@ * @param len: length of data to be sent * @retval status */ -USBD_StatusTypeDef USBD_CtlSendData (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len) +USBD_StatusTypeDef USBD_CtlSendData(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len) { /* Set EP0 State */ - pdev->ep0_state = USBD_EP0_DATA_IN; + pdev->ep0_state = USBD_EP0_DATA_IN; pdev->ep_in[0].total_length = len; - pdev->ep_in[0].rem_length = len; - /* Start the transfer */ - USBD_LL_Transmit (pdev, 0x00, pbuf, len); - + pdev->ep_in[0].rem_length = len; + + /* Start the transfer */ + (void)USBD_LL_Transmit(pdev, 0x00U, pbuf, len); + return USBD_OK; } @@ -114,13 +106,12 @@ USBD_StatusTypeDef USBD_CtlSendData (USBD_HandleTypeDef *pdev, * @param len: length of data to be sent * @retval status */ -USBD_StatusTypeDef USBD_CtlContinueSendData (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len) +USBD_StatusTypeDef USBD_CtlContinueSendData(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len) { - /* Start the next transfer */ - USBD_LL_Transmit (pdev, 0x00, pbuf, len); - + /* Start the next transfer */ + (void)USBD_LL_Transmit(pdev, 0x00U, pbuf, len); + return USBD_OK; } @@ -132,20 +123,17 @@ USBD_StatusTypeDef USBD_CtlContinueSendData (USBD_HandleTypeDef *pdev, * @param len: length of data to be received * @retval status */ -USBD_StatusTypeDef USBD_CtlPrepareRx (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len) +USBD_StatusTypeDef USBD_CtlPrepareRx(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len) { /* Set EP0 State */ - pdev->ep0_state = USBD_EP0_DATA_OUT; + pdev->ep0_state = USBD_EP0_DATA_OUT; pdev->ep_out[0].total_length = len; - pdev->ep_out[0].rem_length = len; + pdev->ep_out[0].rem_length = len; + /* Start the transfer */ - USBD_LL_PrepareReceive (pdev, - 0, - pbuf, - len); - + (void)USBD_LL_PrepareReceive(pdev, 0U, pbuf, len); + return USBD_OK; } @@ -157,32 +145,28 @@ USBD_StatusTypeDef USBD_CtlPrepareRx (USBD_HandleTypeDef *pdev, * @param len: length of data to be received * @retval status */ -USBD_StatusTypeDef USBD_CtlContinueRx (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len) +USBD_StatusTypeDef USBD_CtlContinueRx(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len) { + (void)USBD_LL_PrepareReceive(pdev, 0U, pbuf, len); - USBD_LL_PrepareReceive (pdev, - 0, - pbuf, - len); return USBD_OK; } + /** * @brief USBD_CtlSendStatus * send zero lzngth packet on the ctl pipe * @param pdev: device instance * @retval status */ -USBD_StatusTypeDef USBD_CtlSendStatus (USBD_HandleTypeDef *pdev) +USBD_StatusTypeDef USBD_CtlSendStatus(USBD_HandleTypeDef *pdev) { - /* Set EP0 State */ pdev->ep0_state = USBD_EP0_STATUS_IN; - - /* Start the transfer */ - USBD_LL_Transmit (pdev, 0x00, NULL, 0); - + + /* Start the transfer */ + (void)USBD_LL_Transmit(pdev, 0x00U, NULL, 0U); + return USBD_OK; } @@ -192,21 +176,17 @@ USBD_StatusTypeDef USBD_CtlSendStatus (USBD_HandleTypeDef *pdev) * @param pdev: device instance * @retval status */ -USBD_StatusTypeDef USBD_CtlReceiveStatus (USBD_HandleTypeDef *pdev) +USBD_StatusTypeDef USBD_CtlReceiveStatus(USBD_HandleTypeDef *pdev) { /* Set EP0 State */ - pdev->ep0_state = USBD_EP0_STATUS_OUT; - - /* Start the transfer */ - USBD_LL_PrepareReceive ( pdev, - 0, - NULL, - 0); + pdev->ep0_state = USBD_EP0_STATUS_OUT; + + /* Start the transfer */ + (void)USBD_LL_PrepareReceive(pdev, 0U, NULL, 0U); return USBD_OK; } - /** * @brief USBD_GetRxCount * returns the received data length @@ -214,23 +194,23 @@ USBD_StatusTypeDef USBD_CtlReceiveStatus (USBD_HandleTypeDef *pdev) * @param ep_addr: endpoint address * @retval Rx Data blength */ -uint16_t USBD_GetRxCount (USBD_HandleTypeDef *pdev , uint8_t ep_addr) +uint32_t USBD_GetRxCount(USBD_HandleTypeDef *pdev, uint8_t ep_addr) { return USBD_LL_GetRxDataSize(pdev, ep_addr); } /** * @} - */ + */ /** * @} - */ + */ /** * @} - */ + */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 7f5380aed..f6ea54875 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -1,5 +1,13 @@ -tup.include('build.lua') +-- Utility functions ----------------------------------------------------------- + +function run_now(command) + local handle + handle = io.popen(command) + local output = handle:read("*a") + local rc = {handle:close()} + return rc[1], output +end -- If we simply invoke python or python3 on a pristine Windows 10, it will try -- to open the Microsoft Store which will not work and hang tup instead. The @@ -13,213 +21,431 @@ function find_python3() error("Python 3 not found.") end -python_command = find_python3() -print('Using python command "'..python_command..'"') +function add_pkg(pkg) + if pkg.is_included == true then + return + end + pkg.is_included = true + for _, file in pairs(pkg.code_files or {}) do + code_files += (pkg.root or '.')..'/'..file + end + for _, dir in pairs(pkg.include_dirs or {}) do + CFLAGS += '-I'..(pkg.root or '.')..'/'..dir + end + tup.append_table(CFLAGS, pkg.cflags or {}) + tup.append_table(LDFLAGS, pkg.ldflags or {}) + for _, pkg in pairs(pkg.include or {}) do + add_pkg(pkg) + end +end -tup.frule{inputs={'fibre/cpp/interfaces_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/interfaces.hpp'} -tup.frule{inputs={'fibre/cpp/function_stubs_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/function_stubs.hpp'} -tup.frule{inputs={'fibre/cpp/endpoints_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --generate-endpoints ODrive --template %f --output %o', outputs='autogen/endpoints.hpp'} -tup.frule{inputs={'fibre/cpp/type_info_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/type_info.hpp'} +function compile(src_file, obj_file) + compiler = (tup.ext(src_file) == 'c') and CC or CXX + tup.frule{ + inputs={src_file}, + extra_inputs = {'autogen/interfaces.hpp', 'autogen/function_stubs.hpp', 'autogen/endpoints.hpp', 'autogen/type_info.hpp'}, + command='^o^ '..compiler..' -c %f '..tostring(CFLAGS)..' -o %o', + outputs={obj_file} + } +end --- Note: we currently check this file into source control for two reasons: --- - Don't require tup to run in order to use odrivetool from the repo --- - On Windows, tup is unhappy with writing outside of the tup directory --- TODO: use CI to verify that on PRs the enums.py file is consistent with the YAML. ---tup.frule{command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template enums_template.j2 --output ../tools/odrive/enums.py'} +-- Packages -------------------------------------------------------------------- -tup.frule{ - command=python_command..' ../tools/odrive/version.py --output %o', - outputs={'autogen/version.c'} +tup.include('fibre-cpp/package.lua') +fibre_pkg = get_fibre_package({ + enable_server=true, + enable_client=false, + allow_heap=false, + max_log_verbosity=0, + pkgconf=false, +}) + +odrive_firmware_pkg = { + root = '.', + include_dirs = { + '.', + 'MotorControl', + 'fibre-cpp/include', + }, + code_files = { + 'syscalls.c', + 'MotorControl/utils.cpp', + 'MotorControl/arm_sin_f32.c', + 'MotorControl/arm_cos_f32.c', + 'MotorControl/low_level.cpp', + 'MotorControl/axis.cpp', + 'MotorControl/motor.cpp', + 'MotorControl/thermistor.cpp', + 'MotorControl/encoder.cpp', + 'MotorControl/endstop.cpp', + 'MotorControl/acim_estimator.cpp', + 'MotorControl/mechanical_brake.cpp', + 'MotorControl/controller.cpp', + 'MotorControl/foc.cpp', + 'MotorControl/open_loop_controller.cpp', + 'MotorControl/oscilloscope.cpp', + 'MotorControl/sensorless_estimator.cpp', + 'MotorControl/trapTraj.cpp', + 'MotorControl/pwm_input.cpp', + 'MotorControl/main.cpp', + 'Drivers/STM32/stm32_system.cpp', + 'Drivers/STM32/stm32_gpio.cpp', + 'Drivers/STM32/stm32_nvm.c', + 'Drivers/STM32/stm32_spi_arbiter.cpp', + 'communication/can/can_simple.cpp', + 'communication/can/odrive_can.cpp', + 'communication/communication.cpp', + 'communication/ascii_protocol.cpp', + 'communication/interface_uart.cpp', + 'communication/interface_usb.cpp', + 'communication/interface_i2c.cpp', + 'FreeRTOS-openocd.c', + 'autogen/version.c' + } +} + +stm32f4xx_hal_pkg = { + root = 'ThirdParty/STM32F4xx_HAL_Driver', + include_dirs = { + 'Inc', + }, + code_files = { + 'Src/stm32f4xx_hal.c', + 'Src/stm32f4xx_hal_adc.c', + 'Src/stm32f4xx_hal_adc_ex.c', + 'Src/stm32f4xx_hal_can.c', + 'Src/stm32f4xx_hal_cortex.c', + 'Src/stm32f4xx_hal_dma.c', + 'Src/stm32f4xx_hal_dma_ex.c', + 'Src/stm32f4xx_hal_flash.c', + 'Src/stm32f4xx_hal_flash_ex.c', + 'Src/stm32f4xx_hal_flash_ramfunc.c', + 'Src/stm32f4xx_hal_gpio.c', + 'Src/stm32f4xx_hal_i2c.c', + 'Src/stm32f4xx_hal_i2c_ex.c', + 'Src/stm32f4xx_hal_pcd.c', + 'Src/stm32f4xx_hal_pcd_ex.c', + 'Src/stm32f4xx_hal_pwr.c', + 'Src/stm32f4xx_hal_pwr_ex.c', + 'Src/stm32f4xx_hal_rcc.c', + 'Src/stm32f4xx_hal_rcc_ex.c', + 'Src/stm32f4xx_hal_spi.c', + 'Src/stm32f4xx_hal_tim.c', + 'Src/stm32f4xx_hal_tim_ex.c', + 'Src/stm32f4xx_hal_uart.c', + 'Src/stm32f4xx_ll_usb.c', + }, + cflags = {'-DARM_MATH_CM4', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16', '-DFPU_FPV4'} +} + +stm32f7xx_hal_pkg = { + root = 'Private/ThirdParty/STM32F7xx_HAL_Driver', + include_dirs = { + 'Inc', + }, + code_files = { + 'Src/stm32f7xx_hal.c', + 'Src/stm32f7xx_hal_adc.c', + 'Src/stm32f7xx_hal_adc_ex.c', + 'Src/stm32f7xx_hal_can.c', + 'Src/stm32f7xx_hal_cortex.c', + 'Src/stm32f7xx_hal_dma.c', + 'Src/stm32f7xx_hal_dma_ex.c', + 'Src/stm32f7xx_hal_exti.c', + 'Src/stm32f7xx_hal_flash.c', + 'Src/stm32f7xx_hal_flash_ex.c', + 'Src/stm32f7xx_hal_gpio.c', + 'Src/stm32f7xx_hal_i2c.c', + 'Src/stm32f7xx_hal_i2c_ex.c', + 'Src/stm32f7xx_hal_i2s.c', + 'Src/stm32f7xx_hal_pcd.c', + 'Src/stm32f7xx_hal_pcd_ex.c', + 'Src/stm32f7xx_hal_pwr.c', + 'Src/stm32f7xx_hal_pwr_ex.c', + 'Src/stm32f7xx_hal_rcc.c', + 'Src/stm32f7xx_hal_rcc_ex.c', + 'Src/stm32f7xx_hal_spi.c', + 'Src/stm32f7xx_hal_spi_ex.c', + 'Src/stm32f7xx_hal_tim.c', + 'Src/stm32f7xx_hal_tim_ex.c', + 'Src/stm32f7xx_hal_uart.c', + 'Src/stm32f7xx_hal_uart_ex.c', + 'Src/stm32f7xx_ll_usb.c', + }, + cflags = {'-DARM_MATH_CM7', '-mcpu=cortex-m7', '-mfpu=fpv5-sp-d16'} +} + +freertos_pkg = { + root = 'ThirdParty/FreeRTOS', + include_dirs = { + 'Source/include', + 'Source/CMSIS_RTOS', + }, + code_files = { + 'Source/croutine.c', + 'Source/event_groups.c', + 'Source/list.c', + 'Source/queue.c', + 'Source/stream_buffer.c', + 'Source/tasks.c', + 'Source/timers.c', + 'Source/CMSIS_RTOS/cmsis_os.c', + 'Source/portable/MemMang/heap_4.c', + } +} + +cmsis_pkg = { + root = 'ThirdParty/CMSIS', + include_dirs = { + 'Include', + 'Device/ST/STM32F7xx/Include', + 'Device/ST/STM32F4xx/Include' + }, + ldflags = {'-LThirdParty/CMSIS/Lib/GCC'}, +} + +stm32_usb_device_library_pkg = { + root = 'ThirdParty/STM32_USB_Device_Library', + include_dirs = { + 'Core/Inc', + 'Class/CDC/Inc', + }, + code_files = { + 'Core/Src/usbd_core.c', + 'Core/Src/usbd_ctlreq.c', + 'Core/Src/usbd_ioreq.c', + 'Class/CDC/Src/usbd_cdc.c', + } +} + +crypto_pkg = { + root = 'Private', + include_dirs = { + 'ThirdParty/sha-2', + 'ThirdParty/rsa_embedded', + }, + code_files = { + 'ThirdParty/sha-2/sha-256.c', + 'ThirdParty/rsa_embedded/rsa.c', + } +} + +board_v3 = { + root = 'Board/v3', + root_interface = 'ODrive3', + include = {stm32f4xx_hal_pkg}, + include_dirs = { + 'Inc', + '../../ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F', + }, + code_files = { + '../../ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c', + '../../Drivers/DRV8301/drv8301.cpp', + 'board.cpp', + 'startup_stm32f405xx.s', + 'Src/stm32f4xx_hal_timebase_TIM.c', + 'Src/tim.c', + 'Src/dma.c', + 'Src/freertos.c', + 'Src/main.c', + 'Src/usbd_conf.c', + 'Src/spi.c', + 'Src/usart.c', + 'Src/usbd_cdc_if.c', + 'Src/adc.c', + 'Src/stm32f4xx_hal_msp.c', + 'Src/usbd_desc.c', + 'Src/stm32f4xx_it.c', + 'Src/usb_device.c', + 'Src/can.c', + 'Src/system_stm32f4xx.c', + 'Src/gpio.c', + 'Src/i2c.c', + }, + cflags = {'-DSTM32F405xx', '-DHW_VERSION_MAJOR=3'}, + ldflags = { + '-TBoard/v3/STM32F405RGTx_FLASH.ld', + '-larm_cortexM4lf_math', + } +} + +board_v4 = { + root = 'Private/v4', + root_interface = 'ODrive4', + include = {stm32f7xx_hal_pkg, crypto_pkg}, + include_dirs = { + '..', + 'Inc', + '../../ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1', + }, + code_files = { + '../../ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/port.c', + '../Drivers/DRV8353/drv8353.cpp', + '../Drivers/status_led.cpp', + 'startup_stm32f722xx.s', + 'board.cpp', + 'Src/main.c', + 'Src/gpio.c', + 'Src/adc.c', + 'Src/can.c', + 'Src/dma.c', + 'Src/freertos.c', + 'Src/spi.c', + 'Src/tim.c', + 'Src/stm32f7xx_it.c', + 'Src/stm32f7xx_hal_msp.c', + 'Src/stm32f7xx_hal_timebase_tim.c', + 'Src/system_stm32f7xx.c', + 'Src/i2s.c', + 'Src/usart.c', + 'Src/usb_device.c', + 'Src/usbd_conf.c', + 'Src/usbd_desc.c', + 'Src/usbd_cdc_if.c', + 'Src/i2c.c', + }, + cflags = {'-DSTM32F722xx', '-DHW_VERSION_MAJOR=4'}, + ldflags = { + '-TPrivate/v4/STM32F722RETx_FLASH.ld', + '-larm_cortexM7lfsp_math', + } +} + +boards = { + ["v3.1"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=1 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.2"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=2 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.3"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=3 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.4-24V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=4 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.4-48V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=4 -DHW_VERSION_VOLTAGE=48"}}, + ["v3.5-24V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=5 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.5-48V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=5 -DHW_VERSION_VOLTAGE=48"}}, + ["v3.6-24V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=6 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.6-56V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=6 -DHW_VERSION_VOLTAGE=56"}}, + ["v4.0-56V"] = {include={board_v4}, cflags={"-DHW_VERSION_MINOR=0 -DHW_VERSION_VOLTAGE=56"}}, + ["v4.1-58V"] = {include={board_v4}, cflags={"-DHW_VERSION_MINOR=1 -DHW_VERSION_VOLTAGE=58"}}, } +-- Toolchain setup ------------------------------------------------------------- + +CC='arm-none-eabi-gcc -std=c99' +CXX='arm-none-eabi-g++ -std=c++17 -Wno-register' +LINKER='arm-none-eabi-g++' + +-- C-specific flags +CFLAGS += '-D__weak="__attribute__((weak))"' +CFLAGS += '-D__packed="__attribute__((__packed__))"' +CFLAGS += '-DUSE_HAL_DRIVER' + +CFLAGS += '-mthumb' +CFLAGS += '-mfloat-abi=hard' +CFLAGS += '-Wno-psabi' -- suppress unimportant note about ABI compatibility in GCC 10 +CFLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'} +CFLAGS += '-g' +CFLAGS += '-DFIBRE_ENABLE_SERVER' + +-- linker flags +LDFLAGS += '-flto -lc -lm -lnosys' -- libs +-- LDFLAGS += '-mthumb -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections' +LDFLAGS += '-mthumb -mfloat-abi=hard -specs=nosys.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections' +LDFLAGS += '-Wl,--undefined=uxTopUsedPriority' + + +-- Handle Configuration Options ------------------------------------------------ + -- Switch between board versions boardversion = tup.getconfig("BOARD_VERSION") -if boardversion == "v3.1" then - boarddir = 'Board/v3' -- currently all platform code is in the same v3.3 directory - FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=1" - FLAGS += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.2" then - boarddir = 'Board/v3' - FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=2" - FLAGS += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.3" then - boarddir = 'Board/v3' - FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=3" - FLAGS += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.4-24V" then - boarddir = 'Board/v3' - FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=4" - FLAGS += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.4-48V" then - boarddir = 'Board/v3' - FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=4" - FLAGS += "-DHW_VERSION_VOLTAGE=48" -elseif boardversion == "v3.5-24V" then - boarddir = 'Board/v3' - FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=5" - FLAGS += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.5-48V" then - boarddir = 'Board/v3' - FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=5" - FLAGS += "-DHW_VERSION_VOLTAGE=48" -elseif boardversion == "v3.6-24V" then - boarddir = 'Board/v3' - FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=6" - FLAGS += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.6-56V" then - boarddir = 'Board/v3' - FLAGS += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=6" - FLAGS += "-DHW_VERSION_VOLTAGE=56" -elseif boardversion == "" then +if boardversion == "" then error("board version not specified - take a look at tup.config.default") -else +elseif boards[boardversion] == nil then error("unknown board version "..boardversion) end -buildsuffix = boardversion - --- USB I/O settings -if tup.getconfig("USB_PROTOCOL") == "native" or tup.getconfig("USB_PROTOCOL") == "" then - FLAGS += "-DUSB_PROTOCOL_NATIVE" -elseif tup.getconfig("USB_PROTOCOL") == "native-stream" then - FLAGS += "-DUSB_PROTOCOL_NATIVE_STREAM_BASED" -elseif tup.getconfig("USB_PROTOCOL") == "stdout" then - FLAGS += "-DUSB_PROTOCOL_STDOUT" -elseif tup.getconfig("USB_PROTOCOL") == "none" then - FLAGS += "-DUSB_PROTOCOL_NONE" -else - error("unknown USB protocol") -end +board = boards[boardversion] --- UART I/O settings -if tup.getconfig("UART_PROTOCOL") == "native" then - FLAGS += "-DUART_PROTOCOL_NATIVE" -elseif tup.getconfig("UART_PROTOCOL") == "ascii" or tup.getconfig("UART_PROTOCOL") == "" then - FLAGS += "-DUART_PROTOCOL_ASCII" -elseif tup.getconfig("UART_PROTOCOL") == "stdout" then - FLAGS += "-DUART_PROTOCOL_STDOUT" -elseif tup.getconfig("UART_PROTOCOL") == "none" then - FLAGS += "-DUART_PROTOCOL_NONE" -else - error("unknown UART protocol "..tup.getconfig("UART_PROTOCOL")) +-- --not +-- TODO: remove this setting +if tup.getconfig("USB_PROTOCOL") ~= "native" and tup.getconfig("USB_PROTOCOL") ~= "" then + error("CONFIG_USB_PROTOCOL is deprecated") end --- GPIO settings -if tup.getconfig("STEP_DIR") == "y" then - if tup.getconfig("UART_PROTOCOL") == "none" then - FLAGS += "-DUSE_GPIO_MODE_STEP_DIR" - else - error("Step/dir mode conflicts with UART. Set CONFIG_UART_PROTOCOL to none.") - end +-- UART I/O settings +if tup.getconfig("UART_PROTOCOL") ~= "ascii" and tup.getconfig("UART_PROTOCOL") ~= "" then + error("CONFIG_UART_PROTOCOL is deprecated") end -- Compiler settings if tup.getconfig("STRICT") == "true" then - FLAGS += '-Werror' + CFLAGS += '-Werror' end --- C-specific flags -FLAGS += '-D__weak="__attribute__((weak))"' -FLAGS += '-D__packed="__attribute__((__packed__))"' -FLAGS += '-DUSE_HAL_DRIVER' -FLAGS += '-DSTM32F405xx' - -FLAGS += '-mthumb' -FLAGS += '-mcpu=cortex-m4' -FLAGS += '-mfpu=fpv4-sp-d16' -FLAGS += '-mfloat-abi=hard' -FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'} - --- linker flags -LDFLAGS += '-T'..boarddir..'/STM32F405RGTx_FLASH.ld' -LDFLAGS += '-L'..boarddir..'/Drivers/CMSIS/Lib' -- lib dir -LDFLAGS += '-lc -lm -lnosys -larm_cortexM4lf_math' -- libs -LDFLAGS += '-mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections' -LDFLAGS += '-Wl,--undefined=uxTopUsedPriority' +if tup.getconfig("NO_DRM") == "true" then + CFLAGS += '-DNO_DRM' +end -- debug build if tup.getconfig("DEBUG") == "true" then - FLAGS += '-g -gdwarf-2' - OPT += '-Og' + CFLAGS += '-gdwarf-2 -Og' else - OPT += '-O2' + CFLAGS += '-O2' end --- common flags for ASM, C and C++ -OPT += '-ffast-math -fno-finite-math-only' -tup.append_table(FLAGS, OPT) -tup.append_table(LDFLAGS, OPT) +if tup.getconfig("USE_LTO") == "true" then + CFLAGS += '-flto' +end -toolchain = GCCToolchain('arm-none-eabi-', 'build', FLAGS, LDFLAGS) +-- Generate Tup Rules ---------------------------------------------------------- --- Load list of source files Makefile that was autogenerated by CubeMX -vars = parse_makefile_vars(boarddir..'/Makefile') -all_stm_sources = (vars['C_SOURCES'] or '')..' '..(vars['CPP_SOURCES'] or '')..' '..(vars['ASM_SOURCES'] or '') -for src in string.gmatch(all_stm_sources, "%S+") do - stm_sources += boarddir..'/'..src -end -for src in string.gmatch(vars['C_INCLUDES'] or '', "%S+") do - stm_includes += boarddir..'/'..string.sub(src, 3, -1) -- remove "-I" from each include path -end +python_command = find_python3() +print('Using python command "'..python_command..'"') --- TODO: cleaner separation of the platform code and the rest -stm_includes += '.' -stm_includes += 'Drivers/DRV8301' -stm_sources += boarddir..'/Src/syscalls.c' -build{ - name='stm_platform', - type='objects', - toolchains={toolchain}, - packages={}, - sources=stm_sources, - includes=stm_includes +-- TODO: use CI to verify that on PRs the enums.py file is consistent with the YAML. +-- Note: we currently check this file into source control for two reasons: +-- - Don't require tup to run in order to use odrivetool from the repo +-- - On Windows, tup is unhappy with writing outside of the tup directory +--tup.frule{command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template enums_template.j2 --output ../tools/odrive/enums.py'} + +tup.frule{ + command=python_command..' ../tools/odrive/version.py --output %o', + outputs={'autogen/version.c'} } +-- Autogen files from YAML interface definitions +root_interface = board.include[1].root_interface +tup.frule{inputs={'fibre-cpp/interfaces_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/interfaces.hpp'} +tup.frule{inputs={'fibre-cpp/function_stubs_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/function_stubs.hpp'} +tup.frule{inputs={'fibre-cpp/endpoints_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --generate-endpoints '..root_interface..' --template %f --output %o', outputs='autogen/endpoints.hpp'} +tup.frule{inputs={'fibre-cpp/type_info_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/type_info.hpp'} -build{ - name='ODriveFirmware', - toolchains={toolchain}, - --toolchains={LLVMToolchain('x86_64', {'-Ofast'}, {'-flto'})}, - packages={'stm_platform'}, - sources={ - 'Drivers/DRV8301/drv8301.c', - 'MotorControl/utils.cpp', - 'MotorControl/arm_sin_f32.c', - 'MotorControl/arm_cos_f32.c', - 'MotorControl/low_level.cpp', - 'MotorControl/nvm.c', - 'MotorControl/axis.cpp', - 'MotorControl/motor.cpp', - 'MotorControl/thermistor.cpp', - 'MotorControl/encoder.cpp', - 'MotorControl/endstop.cpp', - 'MotorControl/controller.cpp', - 'MotorControl/sensorless_estimator.cpp', - 'MotorControl/trapTraj.cpp', - 'MotorControl/main.cpp', - 'communication/can_simple.cpp', - 'communication/communication.cpp', - 'communication/ascii_protocol.cpp', - 'communication/interface_uart.cpp', - 'communication/interface_usb.cpp', - 'communication/interface_can.cpp', - 'communication/interface_i2c.cpp', - 'fibre/cpp/protocol.cpp', - 'FreeRTOS-openocd.c', - 'autogen/version.c' - }, - includes={ - 'Drivers/DRV8301', - 'MotorControl', - 'fibre/cpp/include', - '.', - "doctest" - } + +add_pkg(freertos_pkg) +add_pkg(cmsis_pkg) +add_pkg(stm32_usb_device_library_pkg) +add_pkg(board) +add_pkg(fibre_pkg) +add_pkg(odrive_firmware_pkg) + + +for _, src_file in pairs(code_files) do + obj_file = "build/obj/"..src_file:gsub("/","_"):gsub("%.","")..".o" + object_files += obj_file + compile(src_file, obj_file) +end + +tup.frule{ + inputs=object_files, + command='^o^ '..LINKER..' %f '..tostring(CFLAGS)..' '..tostring(LDFLAGS).. + ' -Wl,-Map=%O.map -o %o', + outputs={'build/ODriveFirmware.elf', extra_outputs={'build/ODriveFirmware.map'}} } +-- display the size +tup.frule{inputs={'build/ODriveFirmware.elf'}, command='arm-none-eabi-size %f'} +-- create *.hex and *.bin output formats +tup.frule{inputs={'build/ODriveFirmware.elf'}, command='arm-none-eabi-objcopy -O ihex %f %o', outputs={'build/ODriveFirmware.hex'}} +tup.frule{inputs={'build/ODriveFirmware.elf'}, command='arm-none-eabi-objcopy -O binary -S %f %o', outputs={'build/ODriveFirmware.bin'}} if tup.getconfig('DOCTEST') == 'true' then - TEST_INCLUDES = '-I. -I./MotorControl -I./fibre/cpp/include -I./Drivers/DRV8301 -I./doctest' + TEST_INCLUDES = '-I. -I./MotorControl -I./fibre-cpp/include -I./Drivers/DRV8301 -I./doctest' tup.foreach_rule('Tests/*.cpp', 'g++ -O3 -std=c++17 '..TEST_INCLUDES..' -c %f -o %o', 'Tests/bin/%B.o') tup.frule{inputs='Tests/bin/*.o', command='g++ %f -o %o', outputs='Tests/test_runner.exe'} tup.frule{inputs='Tests/test_runner.exe', command='%f'} diff --git a/Firmware/Tuprules.lua b/Firmware/Tuprules.lua new file mode 100644 index 000000000..11c980d23 --- /dev/null +++ b/Firmware/Tuprules.lua @@ -0,0 +1,4 @@ + +-- Prevent tup from running `Firmware/fibre-cpp/Tupfile.lua` when we run it in +-- the `Firmware` folder. +no_libfibre = tup.getconfig("BUILD_LIBFIBRE") != "true" diff --git a/Firmware/build.lua b/Firmware/build.lua deleted file mode 100644 index e6d93e41e..000000000 --- a/Firmware/build.lua +++ /dev/null @@ -1,184 +0,0 @@ - --- This file contains support functions for Tupfile.lua - -function trim(s) - return (s:gsub("^%s*(.-)%s*$", "%1")) -end - -function string:split(sep) - local sep, fields = sep or ":", {} - local pattern = string.format("([^%s]+)", sep) - self:gsub(pattern, function(c) fields[#fields+1] = c end) - return fields -end - -function run_now(command) - local handle - handle = io.popen(command) - local output = handle:read("*a") - local rc = {handle:close()} - return rc[1], output -end - --- Very basic parser to retrieve variables from a Makefile -function parse_makefile_vars(makefile) - vars = {} - current_var = nil - for line in io.lines(tup.getcwd()..'/'..makefile) do - if current_var == nil then - i,j = string.find(line, "+=") - if not i then - i,j = string.find(line, "=") - end - if i then - current_var = trim(string.sub(line, 1, i-1)) - vars[current_var] = vars[current_var] or '' - line = string.sub(line, j+1, -1) - --print("varname: "..varname.." the rest: "..line) - end - end - - if current_var != nil then - --print("append chunk "..trim(line).." to "..current_var) - vars[current_var] = vars[current_var]..' '..trim(line) - if string.sub(vars[current_var], -1) == '\\' then - vars[current_var] = string.sub(vars[current_var], 1, -2) - else - current_var = nil - end - end - end - return vars -end - - - - -function GCCToolchain(prefix, builddir, compiler_flags, linker_flags) - - -- add some default compiler flags - -- -fstack-usage gives a warning for some functions containing inline assembly (prvPortStartFirstTask in particular) - -- so for now we just disable it - calculate_stack_usage = false - if calculate_stack_usage then - compiler_flags += '-fstack-usage' - end - - local gcc_generic_compiler = function(compiler, compiler_flags, gen_su_file, src, flags, includes, outputs) - -- convert include list to flags - inc_flags = {} - for _,inc in pairs(includes) do - inc_flags += "-I"..inc - end - -- todo: vary build directory - obj_file = builddir.."/obj/"..src:gsub("/","_")..".o" - outputs.object_files += obj_file - if gen_su_file then - su_file = builddir.."/"..src:gsub("/","_")..".su" - extra_outputs = { su_file } - outputs.su_files += su_file - else - extra_outputs = {} - end - extra_inputs = {'autogen/interfaces.hpp', 'autogen/function_stubs.hpp', 'autogen/endpoints.hpp', 'autogen/type_info.hpp'} -- TODO: fix hack - tup.frule{ - inputs= { src, extra_inputs=extra_inputs }, - command=compiler..' -c %f '.. - tostring(compiler_flags)..' '.. -- CFLAGS for this compiler - tostring(inc_flags)..' '.. -- CFLAGS for this translation unit - tostring(flags).. -- CFLAGS for this translation unit - ' -o %o', - outputs={obj_file,extra_outputs=extra_outputs} - } - end - return { - compile_c = function(src, flags, includes, outputs) gcc_generic_compiler(prefix..'gcc -std=c99', compiler_flags, calculate_stack_usage, src, flags, includes, outputs) end, - compile_cpp = function(src, flags, includes, outputs) gcc_generic_compiler(prefix..'g++ -std=c++17 -Wno-register', compiler_flags, calculate_stack_usage, src, flags, includes, outputs) end, - compile_asm = function(src, flags, includes, outputs) gcc_generic_compiler(prefix..'gcc -x assembler-with-cpp', compiler_flags, false, src, flags, includes, outputs) end, - link = function(objects, output_name) - output_name = builddir..'/'..output_name - tup.frule{ - inputs=objects, - command=prefix..'g++ %f '.. - tostring(linker_flags)..' '.. - '-Wl,-Map=%O.map'.. - ' -o %o', - outputs={output_name..'.elf', extra_outputs={output_name..'.map'}} - } - -- display the size - tup.frule{inputs={output_name..'.elf'}, command=prefix..'size %f'} - -- generate disassembly - tup.frule{inputs={output_name..'.elf'}, command=prefix..'objdump %f -dSC > %o', outputs={output_name..'.asm'}} - -- create *.hex and *.bin output formats - tup.frule{inputs={output_name..'.elf'}, command=prefix..'objcopy -O ihex %f %o', outputs={output_name..'.hex'}} - tup.frule{inputs={output_name..'.elf'}, command=prefix..'objcopy -O binary -S %f %o', outputs={output_name..'.bin'}} - end - } -end - -all_packages = {} - --- toolchains: Each element of this list is a collection of functions, such as compile_c, link, ... --- You can create a new toolchain object for each platform you want to build for. -function build(args) - if args.toolchain == nil then args.toolchain = {} end - if args.sources == nil then args.sources = {} end - if args.includes == nil then args.includes = {} end - if args.packages == nil then args.packages = {} end - if args.c_flags == nil then args.c_flags = {} end - if args.cpp_flags == nil then args.cpp_flags = {} end - if args.asm_flags == nil then args.asm_flags = {} end - if args.ld_flags == nil then args.ld_flags = {} end - if args.linker_objects == nil then args.linker_objects = {} end - - -- add includes of other packages - for _,pkg_name in pairs(args.packages) do - --print('depend on package '..pkg_name) - pkg = all_packages[pkg_name] - if pkg == nil then - error("unknown package "..pkg_name) - end - -- add path of each include - for _,inc in pairs(pkg.includes or {}) do - args.includes += tostring(inc) - end - tup.append_table(args.linker_objects, pkg.object_files) - end - - -- run everything once for every toolchain - for _,toolchain in pairs(args.toolchains) do - -- compile - outputs = {} - for _,src in pairs(args.sources) do - --print("compile "..src) - if tup.ext(src) == 'c' then - toolchain.compile_c(src, args.c_flags, args.includes, outputs) - elseif tup.ext(src) == 'cpp' then - toolchain.compile_cpp(src, args.cpp_flags, args.includes, outputs) - elseif tup.ext(src) == 's' or tup.ext(src) == 'asm' then - toolchain.compile_asm(src, args.asm_flags, args.includes, outputs) - else - error('unrecognized file ending') - end - end - - -- link - if outputs.object_files != nil and args.type != 'objects' then - tup.append_table(args.linker_objects, outputs.object_files) - toolchain.link(args.linker_objects, args.name) - end - - outputs.includes = {} - for _,inc in pairs(args.includes) do - table.insert(outputs.includes, inc) - end - if args.name != nil then - all_packages[args.name] = outputs - end - end - - --for k,v in pairs(all_packages) do - -- print('have package '..k) - --end -end - diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index 5efbed460..27cf11722 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -16,54 +16,66 @@ #include "autogen/type_info.hpp" #include "communication/interface_can.hpp" +using namespace fibre; + /* Private macros ------------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/ /* Global constant data ------------------------------------------------------*/ /* Global variables ----------------------------------------------------------*/ /* Private constant data -----------------------------------------------------*/ -#define MAX_LINE_LENGTH 256 #define TO_STR_INNER(s) #s #define TO_STR(s) TO_STR_INNER(s) /* Private variables ---------------------------------------------------------*/ -static Introspectable root_obj = ODriveTypeInfo::make_introspectable(odrv); +#if HW_VERSION_MAJOR == 3 +static Introspectable root_obj = ODrive3TypeInfo::make_introspectable(odrv); +#elif HW_VERSION_MAJOR == 4 +static Introspectable root_obj = ODrive4TypeInfo::make_introspectable(odrv); +#endif /* Private function prototypes -----------------------------------------------*/ + /* Function implementations --------------------------------------------------*/ // @brief Sends a line on the specified output. template -void respond(StreamSink& output, bool include_checksum, const char * fmt, TArgs&& ... args) { - char response[64]; // Hardcoded max buffer size. We silently truncate the output if it's too long for the buffer. - size_t len = snprintf(response, sizeof(response), fmt, std::forward(args)...); - len = std::min(len, sizeof(response)); - output.process_bytes((uint8_t*)response, len, nullptr); // TODO: use process_all instead +void AsciiProtocol::respond(bool include_checksum, const char * fmt, TArgs&& ... args) { + size_t len = snprintf(tx_buf_, sizeof(tx_buf_), fmt, std::forward(args)...); + + // Silently truncate the output if it's too long for the buffer. + len = std::min(len, sizeof(tx_buf_)); + if (include_checksum) { uint8_t checksum = 0; for (size_t i = 0; i < len; ++i) - checksum ^= response[i]; - len = snprintf(response, sizeof(response), "*%u", checksum); - len = std::min(len, sizeof(response)); - output.process_bytes((uint8_t*)response, len, nullptr); + checksum ^= tx_buf_[i]; + len += snprintf(tx_buf_ + len, sizeof(tx_buf_) - len, "*%u", checksum); + } else { + len += snprintf(tx_buf_ + len, sizeof(tx_buf_) - len, "\r\n"); } - output.process_bytes((const uint8_t*)"\r\n", 2, nullptr); + + // Silently truncate the output if it's too long for the buffer. + len = std::min(len, sizeof(tx_buf_)); + + tx_end_ = (const uint8_t*)tx_buf_ + len; + tx_channel_->start_write({(const uint8_t*)tx_buf_, tx_end_}, &tx_handle_, MEMBER_CB(this, on_write_finished)); } // @brief Executes an ASCII protocol command // @param buffer buffer of ASCII encoded characters // @param len size of the buffer -void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& response_channel) { +void AsciiProtocol::process_line(cbufptr_t buffer) { static_assert(sizeof(char) == sizeof(uint8_t)); - + // scan line to find beginning of checksum and prune comment uint8_t checksum = 0; size_t checksum_start = SIZE_MAX; - for (size_t i = 0; i < len; ++i) { - if (buffer[i] == ';') { // ';' is the comment start char - len = i; + for (size_t i = 0; i < buffer.size(); ++i) { + if (buffer.begin()[i] == ';') { // ';' is the comment start char + buffer = buffer.take(i); break; } if (checksum_start > i) { @@ -77,15 +89,15 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& // copy everything into a local buffer so we can insert null-termination char cmd[MAX_LINE_LENGTH + 1]; - if (len > MAX_LINE_LENGTH) len = MAX_LINE_LENGTH; - memcpy(cmd, buffer, len); + size_t len = std::min(buffer.size(), MAX_LINE_LENGTH); + memcpy(cmd, buffer.begin(), len); cmd[len] = 0; // null-terminate // optional checksum validation bool use_checksum = (checksum_start < len); if (use_checksum) { unsigned int received_checksum; - int numscan = sscanf((const char *)cmd + checksum_start, "%u", &received_checksum); + int numscan = sscanf(&cmd[checksum_start], "%u", &received_checksum); if ((numscan < 1) || (received_checksum != checksum)) return; len = checksum_start - 1; // prune checksum and asterisk @@ -94,220 +106,372 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& // check incoming packet type - if (cmd[0] == 'p') { // position control - unsigned motor_number; - float pos_setpoint, vel_feed_forward, torque_feed_forward; - int numscan = sscanf(cmd, "p %u %f %f %f", &motor_number, &pos_setpoint, &vel_feed_forward, &torque_feed_forward); - if (numscan < 2) { - respond(response_channel, use_checksum, "invalid command format"); - } else if (motor_number >= AXIS_COUNT) { - respond(response_channel, use_checksum, "invalid motor %u", motor_number); - } else { - Axis* axis = axes[motor_number]; - axis->controller_.config_.control_mode = Controller::CONTROL_MODE_POSITION_CONTROL; - axis->controller_.input_pos_ = pos_setpoint; - if (numscan >= 3) - axis->controller_.input_vel_ = vel_feed_forward; - if (numscan >= 4) - axis->controller_.input_torque_ = torque_feed_forward; - axis->controller_.input_pos_updated(); - axis->watchdog_feed(); - } + switch(cmd[0]) { + case 'p': cmd_set_position(cmd, use_checksum); break; // position control + case 'q': cmd_set_position_wl(cmd, use_checksum); break; // position control with limits + case 'v': cmd_set_velocity(cmd, use_checksum); break; // velocity control + case 'c': cmd_set_torque(cmd, use_checksum); break; // current control + case 't': cmd_set_trapezoid_trajectory(cmd, use_checksum); break; // trapezoidal trajectory + case 'f': cmd_get_feedback(cmd, use_checksum); break; // feedback + case 'h': cmd_help(cmd, use_checksum); break; // Help + case 'i': cmd_info_dump(cmd, use_checksum); break; // Dump device info + case 's': cmd_system_ctrl(cmd, use_checksum); break; // System + case 'r': cmd_read_property(cmd, use_checksum); break; // read property + case 'w': cmd_write_property(cmd, use_checksum); break; // write property + case 'u': cmd_update_axis_wdg(cmd, use_checksum); break; // Update axis watchdog. + case 'e': cmd_encoder(cmd, use_checksum); break; // Encoder commands + default : cmd_unknown(nullptr, use_checksum); break; + } +} - } else if (cmd[0] == 'q') { // position control with limits - unsigned motor_number; - float pos_setpoint, vel_limit, torque_lim; - int numscan = sscanf(cmd, "q %u %f %f %f", &motor_number, &pos_setpoint, &vel_limit, &torque_lim); - if (numscan < 2) { - respond(response_channel, use_checksum, "invalid command format"); - } else if (motor_number >= AXIS_COUNT) { - respond(response_channel, use_checksum, "invalid motor %u", motor_number); - } else { - Axis* axis = axes[motor_number]; - axis->controller_.config_.control_mode = Controller::CONTROL_MODE_POSITION_CONTROL; - axis->controller_.input_pos_ = pos_setpoint; - if (numscan >= 3) - axis->controller_.config_.vel_limit = vel_limit; - if (numscan >= 4) - axis->motor_.config_.torque_lim = torque_lim; - axis->controller_.input_pos_updated(); - axis->watchdog_feed(); - } +// @brief Executes the set position command +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_set_position(char * pStr, bool use_checksum) { + unsigned motor_number; + float pos_setpoint, vel_feed_forward, torque_feed_forward; - } else if (cmd[0] == 'v') { // velocity control - unsigned motor_number; - float vel_setpoint, torque_feed_forward; - int numscan = sscanf(cmd, "v %u %f %f", &motor_number, &vel_setpoint, &torque_feed_forward); - if (numscan < 2) { - respond(response_channel, use_checksum, "invalid command format"); - } else if (motor_number >= AXIS_COUNT) { - respond(response_channel, use_checksum, "invalid motor %u", motor_number); - } else { - Axis* axis = axes[motor_number]; - axis->controller_.config_.control_mode = Controller::CONTROL_MODE_VELOCITY_CONTROL; - axis->controller_.input_vel_ = vel_setpoint; - if (numscan >= 3) - axis->controller_.input_torque_ = torque_feed_forward; - axis->watchdog_feed(); - } + int numscan = sscanf(pStr, "p %u %f %f %f", &motor_number, &pos_setpoint, &vel_feed_forward, &torque_feed_forward); + if (numscan < 2) { + respond(use_checksum, "invalid command format"); + } else if (motor_number >= AXIS_COUNT) { + respond(use_checksum, "invalid motor %u", motor_number); + } else { + Axis& axis = axes[motor_number]; + axis.controller_.config_.control_mode = Controller::CONTROL_MODE_POSITION_CONTROL; + axis.controller_.input_pos_ = pos_setpoint; + if (numscan >= 3) + axis.controller_.input_vel_ = vel_feed_forward; + if (numscan >= 4) + axis.controller_.input_torque_ = torque_feed_forward; + axis.controller_.input_pos_updated(); + axis.watchdog_feed(); + } +} - } else if (cmd[0] == 'c') { // torque control - unsigned motor_number; - float torque_setpoint; - int numscan = sscanf(cmd, "c %u %f", &motor_number, &torque_setpoint); - if (numscan < 2) { - respond(response_channel, use_checksum, "invalid command format"); - } else if (motor_number >= AXIS_COUNT) { - respond(response_channel, use_checksum, "invalid motor %u", motor_number); - } else { - Axis* axis = axes[motor_number]; - axis->controller_.config_.control_mode = Controller::CONTROL_MODE_TORQUE_CONTROL; - axis->controller_.input_torque_ = torque_setpoint; - axis->watchdog_feed(); - } +// @brief Executes the set position with current and velocity limit command +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_set_position_wl(char * pStr, bool use_checksum) { + unsigned motor_number; + float pos_setpoint, vel_limit, torque_lim; - } else if (cmd[0] == 't') { // trapezoidal trajectory - unsigned motor_number; - float goal_point; - int numscan = sscanf(cmd, "t %u %f", &motor_number, &goal_point); - if (numscan < 2) { - respond(response_channel, use_checksum, "invalid command format"); - } else if (motor_number >= AXIS_COUNT) { - respond(response_channel, use_checksum, "invalid motor %u", motor_number); - } else { - Axis* axis = axes[motor_number]; - axis->controller_.config_.input_mode = Controller::INPUT_MODE_TRAP_TRAJ; - axis->controller_.config_.control_mode = Controller::CONTROL_MODE_POSITION_CONTROL; - axis->controller_.input_pos_ = goal_point; - axis->controller_.input_pos_updated(); - axis->watchdog_feed(); - } + int numscan = sscanf(pStr, "q %u %f %f %f", &motor_number, &pos_setpoint, &vel_limit, &torque_lim); + if (numscan < 2) { + respond(use_checksum, "invalid command format"); + } else if (motor_number >= AXIS_COUNT) { + respond(use_checksum, "invalid motor %u", motor_number); + } else { + Axis& axis = axes[motor_number]; + axis.controller_.config_.control_mode = Controller::CONTROL_MODE_POSITION_CONTROL; + axis.controller_.input_pos_ = pos_setpoint; + if (numscan >= 3) + axis.controller_.config_.vel_limit = vel_limit; + if (numscan >= 4) + axis.motor_.config_.torque_lim = torque_lim; + axis.controller_.input_pos_updated(); + axis.watchdog_feed(); + } +} + +// @brief Executes the set velocity command +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_set_velocity(char * pStr, bool use_checksum) { + unsigned motor_number; + float vel_setpoint, torque_feed_forward; + int numscan = sscanf(pStr, "v %u %f %f", &motor_number, &vel_setpoint, &torque_feed_forward); + if (numscan < 2) { + respond(use_checksum, "invalid command format"); + } else if (motor_number >= AXIS_COUNT) { + respond(use_checksum, "invalid motor %u", motor_number); + } else { + Axis& axis = axes[motor_number]; + axis.controller_.config_.control_mode = Controller::CONTROL_MODE_VELOCITY_CONTROL; + axis.controller_.input_vel_ = vel_setpoint; + if (numscan >= 3) + axis.controller_.input_torque_ = torque_feed_forward; + axis.watchdog_feed(); + } +} + +// @brief Executes the set torque control command +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_set_torque(char * pStr, bool use_checksum) { + unsigned motor_number; + float torque_setpoint; + + if (sscanf(pStr, "c %u %f", &motor_number, &torque_setpoint) < 2) { + respond(use_checksum, "invalid command format"); + } else if (motor_number >= AXIS_COUNT) { + respond(use_checksum, "invalid motor %u", motor_number); + } else { + Axis& axis = axes[motor_number]; + axis.controller_.config_.control_mode = Controller::CONTROL_MODE_TORQUE_CONTROL; + axis.controller_.input_torque_ = torque_setpoint; + axis.watchdog_feed(); + } +} + +// @brief Sets the encoder linear count +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_encoder(char * pStr, bool use_checksum) { + if (pStr[1] == 's') { + pStr += 2; // Substring two characters to the right (ok because we have guaranteed null termination after all chars) - } else if (cmd[0] == 'f') { // feedback unsigned motor_number; - int numscan = sscanf(cmd, "f %u", &motor_number); - if (numscan < 1) { - respond(response_channel, use_checksum, "invalid command format"); + int encoder_count; + + if (sscanf(pStr, "l %u %i", &motor_number, &encoder_count) < 2) { + respond(use_checksum, "invalid command format"); } else if (motor_number >= AXIS_COUNT) { - respond(response_channel, use_checksum, "invalid motor %u", motor_number); + respond(use_checksum, "invalid motor %u", motor_number); } else { - respond(response_channel, use_checksum, "%f %f", - (double)axes[motor_number]->encoder_.pos_estimate_, - (double)axes[motor_number]->encoder_.vel_estimate_); + Axis& axis = axes[motor_number]; + axis.encoder_.set_linear_count(encoder_count); + axis.watchdog_feed(); + respond(use_checksum, "encoder set to %u", encoder_count); } + } else { + respond(use_checksum, "invalid command format"); + } +} - } else if (cmd[0] == 'h') { // Help - respond(response_channel, use_checksum, "Please see documentation for more details"); - respond(response_channel, use_checksum, ""); - respond(response_channel, use_checksum, "Available commands syntax reference:"); - respond(response_channel, use_checksum, "Position: q axis pos vel-lim I-lim"); - respond(response_channel, use_checksum, "Position: p axis pos vel-ff I-ff"); - respond(response_channel, use_checksum, "Velocity: v axis vel I-ff"); - respond(response_channel, use_checksum, "Torque: c axis T"); - respond(response_channel, use_checksum, ""); - respond(response_channel, use_checksum, "Properties start at odrive root, such as axis0.requested_state"); - respond(response_channel, use_checksum, "Read: r property"); - respond(response_channel, use_checksum, "Write: w property value"); - respond(response_channel, use_checksum, ""); - respond(response_channel, use_checksum, "Save config: ss"); - respond(response_channel, use_checksum, "Erase config: se"); - respond(response_channel, use_checksum, "Reboot: sr"); - - } else if (cmd[0] == 'i'){ // Dump device info - // respond(response_channel, use_checksum, "Signature: %#x", STM_ID_GetSignature()); - // respond(response_channel, use_checksum, "Revision: %#x", STM_ID_GetRevision()); - // respond(response_channel, use_checksum, "Flash Size: %#x KiB", STM_ID_GetFlashSize()); - respond(response_channel, use_checksum, "Hardware version: %d.%d-%dV", odrv.hw_version_major_, odrv.hw_version_minor_, odrv.hw_version_variant_); - respond(response_channel, use_checksum, "Firmware version: %d.%d.%d", odrv.fw_version_major_, odrv.fw_version_minor_, odrv.fw_version_revision_); - respond(response_channel, use_checksum, "Serial number: %s", serial_number_str); - - } else if (cmd[0] == 's'){ // System - if(cmd[1] == 's') { // Save config - odrv.save_configuration(); - } else if (cmd[1] == 'e'){ // Erase config - odrv.erase_configuration(); - } else if (cmd[1] == 'r'){ // Reboot - odrv.reboot(); - } +// @brief Executes the set trapezoid trajectory command +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_set_trapezoid_trajectory(char* pStr, bool use_checksum) { + unsigned motor_number; + float goal_point; - } else if (cmd[0] == 'r') { // read property - char name[MAX_LINE_LENGTH]; - int numscan = sscanf(cmd, "r %255s", name); - if (numscan < 1) { - respond(response_channel, use_checksum, "invalid command format"); + if (sscanf(pStr, "t %u %f", &motor_number, &goal_point) < 2) { + respond(use_checksum, "invalid command format"); + } else if (motor_number >= AXIS_COUNT) { + respond(use_checksum, "invalid motor %u", motor_number); + } else { + Axis& axis = axes[motor_number]; + axis.controller_.config_.input_mode = Controller::INPUT_MODE_TRAP_TRAJ; + axis.controller_.config_.control_mode = Controller::CONTROL_MODE_POSITION_CONTROL; + axis.controller_.input_pos_ = goal_point; + axis.controller_.input_pos_updated(); + axis.watchdog_feed(); + } +} + +// @brief Executes the get position and velocity feedback command +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_get_feedback(char * pStr, bool use_checksum) { + unsigned motor_number; + + if (sscanf(pStr, "f %u", &motor_number) < 1) { + respond(use_checksum, "invalid command format"); + } else if (motor_number >= AXIS_COUNT) { + respond(use_checksum, "invalid motor %u", motor_number); + } else { + Axis& axis = axes[motor_number]; + respond(use_checksum, "%f %f", + (double)axis.encoder_.pos_estimate_.any().value_or(0.0f), + (double)axis.encoder_.vel_estimate_.any().value_or(0.0f)); + } +} + +// @brief Shows help text +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_help(char * pStr, bool use_checksum) { + (void)pStr; + respond(use_checksum, "Please see documentation for more details"); + respond(use_checksum, ""); + respond(use_checksum, "Available commands syntax reference:"); + respond(use_checksum, "Position: q axis pos vel-lim I-lim"); + respond(use_checksum, "Position: p axis pos vel-ff I-ff"); + respond(use_checksum, "Velocity: v axis vel I-ff"); + respond(use_checksum, "Torque: c axis T"); + respond(use_checksum, ""); + respond(use_checksum, "Properties start at odrive root, such as axis0.requested_state"); + respond(use_checksum, "Read: r property"); + respond(use_checksum, "Write: w property value"); + respond(use_checksum, ""); + respond(use_checksum, "Save config: ss"); + respond(use_checksum, "Erase config: se"); + respond(use_checksum, "Reboot: sr"); +} + +// @brief Gets the hardware, firmware and serial details +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_info_dump(char * pStr, bool use_checksum) { + // respond(use_checksum, "Signature: %#x", STM_ID_GetSignature()); + // respond(use_checksum, "Revision: %#x", STM_ID_GetRevision()); + // respond(use_checksum, "Flash Size: %#x KiB", STM_ID_GetFlashSize()); + respond(use_checksum, "Hardware version: %d.%d-%dV", odrv.hw_version_major_, odrv.hw_version_minor_, odrv.hw_version_variant_); + respond(use_checksum, "Firmware version: %d.%d.%d", odrv.fw_version_major_, odrv.fw_version_minor_, odrv.fw_version_revision_); + respond(use_checksum, "Serial number: %s", serial_number_str); +} + +// @brief Executes the system control command +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_system_ctrl(char * pStr, bool use_checksum) { + switch (pStr[1]) + { + case 's': odrv.save_configuration(); break; // Save config + case 'e': odrv.erase_configuration(); break; // Erase config + case 'r': odrv.reboot(); break; // Reboot + case 'c': odrv.clear_errors(); break; // clear all errors and rearm brake resistor if necessary + default: /* default */ break; + } +} + +// @brief Executes the read parameter command +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_read_property(char * pStr, bool use_checksum) { + char name[MAX_LINE_LENGTH]; + + if (sscanf(pStr, "r %255s", name) < 1) { + respond(use_checksum, "invalid command format"); + } else { + Introspectable property = root_obj.get_child(name, sizeof(name)); + const StringConvertibleTypeInfo* type_info = dynamic_cast(property.get_type_info()); + if (!type_info) { + respond(use_checksum, "invalid property"); } else { - Introspectable property = root_obj.get_child(name, sizeof(name)); - const StringConvertibleTypeInfo* type_info = dynamic_cast(property.get_type_info()); - if (!type_info) { - respond(response_channel, use_checksum, "invalid property"); - } else { - char response[10]; - bool success = type_info->get_string(property, response, sizeof(response)); - if (!success) - respond(response_channel, use_checksum, "not implemented"); - else - respond(response_channel, use_checksum, response); - } + char response[10]; + bool success = type_info->get_string(property, response, sizeof(response)); + respond(use_checksum, success ? response : "not implemented"); } + } +} + +// @brief Executes the set write position command +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_write_property(char * pStr, bool use_checksum) { + char name[MAX_LINE_LENGTH]; + char value[MAX_LINE_LENGTH]; - } else if (cmd[0] == 'w') { // write property - char name[MAX_LINE_LENGTH]; - char value[MAX_LINE_LENGTH]; - int numscan = sscanf(cmd, "w %255s %255s", name, value); - if (numscan < 1) { - respond(response_channel, use_checksum, "invalid command format"); + if (sscanf(pStr, "w %255s %255s", name, value) < 1) { + respond(use_checksum, "invalid command format"); + } else { + Introspectable property = root_obj.get_child(name, sizeof(name)); + const StringConvertibleTypeInfo* type_info = dynamic_cast(property.get_type_info()); + if (!type_info) { + respond(use_checksum, "invalid property"); } else { - Introspectable property = root_obj.get_child(name, sizeof(name)); - const StringConvertibleTypeInfo* type_info = dynamic_cast(property.get_type_info()); - if (!type_info) { - respond(response_channel, use_checksum, "invalid property"); - } else { - bool success = type_info->set_string(property, value, sizeof(value)); - if (!success) - respond(response_channel, use_checksum, "not implemented"); + bool success = type_info->set_string(property, value, sizeof(value)); + if (!success) { + respond(use_checksum, "not implemented"); } } + } +} - } else if (cmd[0] == 'u') { // Update axis watchdog. - unsigned motor_number; - int numscan = sscanf(cmd, "u %u", &motor_number); - if(numscan < 1){ - respond(response_channel, use_checksum, "invalid command format"); - } else if (motor_number >= AXIS_COUNT) { - respond(response_channel, use_checksum, "invalid motor %u", motor_number); - }else { - axes[motor_number]->watchdog_feed(); - } +// @brief Executes the motor watchdog update command +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_update_axis_wdg(char * pStr, bool use_checksum) { + unsigned motor_number; + + if (sscanf(pStr, "u %u", &motor_number) < 1) { + respond(use_checksum, "invalid command format"); + } else if (motor_number >= AXIS_COUNT) { + respond(use_checksum, "invalid motor %u", motor_number); + } else { + axes[motor_number].watchdog_feed(); + } +} + +// @brief Sends the unknown command response +// @param pStr buffer of ASCII encoded values +// @param response_channel reference to the stream to respond on +// @param use_checksum bool to indicate whether a checksum is required on response +void AsciiProtocol::cmd_unknown(char * pStr, bool use_checksum) { + (void)pStr; + respond(use_checksum, "unknown command"); +} - } else if (cmd[0] != 0) { - respond(response_channel, use_checksum, "unknown command"); + + +void AsciiProtocol::on_write_finished(WriteResult result) { + tx_handle_ = 0; + + if (result.status == kStreamOk && result.end < tx_end_) { + // Not everything was written. Try again. + tx_channel_->start_write({result.end, tx_end_}, &tx_handle_, MEMBER_CB(this, on_write_finished)); + return; + } + + if (rx_end_) { + uint8_t* rx_end = rx_end_; + rx_end_ = nullptr; + on_read_finished({kStreamOk, rx_end}); } } -void ASCII_protocol_parse_stream(const uint8_t* buffer, size_t len, StreamSink& response_channel) { - static uint8_t parse_buffer[MAX_LINE_LENGTH]; - static bool read_active = true; - static uint32_t parse_buffer_idx = 0; +void AsciiProtocol::on_read_finished(ReadResult result) { + if (result.status != kStreamOk) { + return; + } - while (len--) { - // if the line becomes too long, reset buffer and wait for the next line - if (parse_buffer_idx >= MAX_LINE_LENGTH) { - read_active = false; - parse_buffer_idx = 0; + for (;;) { + uint8_t* end_of_line = std::find_if(rx_buf_, result.end, [](uint8_t c) { + return c == '\r' || c == '\n' || c == '!'; + }); + + if (end_of_line >= result.end) { + break; } - // Fetch the next char - uint8_t c = *(buffer++); - bool is_end_of_line = (c == '\r' || c == '\n' || c == '!'); - if (is_end_of_line) { - if (read_active) - ASCII_protocol_process_line(parse_buffer, parse_buffer_idx, response_channel); - parse_buffer_idx = 0; - read_active = true; - } else { - if (read_active) { - parse_buffer[parse_buffer_idx++] = c; + if (read_active_) { + if (tx_handle_) { + // TX is busy - inhibit processing of the incoming data until + // on_write_finished() is invoked. + rx_end_ = result.end; + return; } + + process_line({rx_buf_, end_of_line}); + } else { + // Ignoring this line cause it didn't start at a new-line character + read_active_ = true; } + + // Discard the processed bytes and shift the remainder to the beginning of the buffer + size_t n_remaining = result.end - end_of_line - 1; + memmove(rx_buf_, end_of_line + 1, n_remaining); + result.end = rx_buf_ + n_remaining; } + + // No more new-line characters in buffer + + if (result.end >= rx_buf_ + sizeof(rx_buf_)) { + // If the line becomes too long, reset buffer and wait for the next line + result.end = rx_buf_; + read_active_ = false; + } + + TransferHandle dummy; + rx_channel_->start_read({result.end, rx_buf_ + sizeof(rx_buf_)}, &dummy, MEMBER_CB(this, on_read_finished)); +} + +void AsciiProtocol::start() { + TransferHandle dummy; + rx_channel_->start_read(rx_buf_, &dummy, MEMBER_CB(this, on_read_finished)); } diff --git a/Firmware/communication/ascii_protocol.hpp b/Firmware/communication/ascii_protocol.hpp index 145ee5e5c..fb15a4aea 100644 --- a/Firmware/communication/ascii_protocol.hpp +++ b/Firmware/communication/ascii_protocol.hpp @@ -1,22 +1,49 @@ -#ifndef __ASCII_PROTOCOL_H -#define __ASCII_PROTOCOL_H +#ifndef __ASCII_PROTOCOL_HPP +#define __ASCII_PROTOCOL_HPP +#include -/* Includes ------------------------------------------------------------------*/ -#include +#define MAX_LINE_LENGTH ((size_t)256) -#include -#include -#include +class AsciiProtocol { +public: + AsciiProtocol(fibre::AsyncStreamSource* rx_channel, fibre::AsyncStreamSink* tx_channel) + : rx_channel_(rx_channel), tx_channel_(tx_channel) {} -/* Exported types ------------------------------------------------------------*/ -/* Exported constants --------------------------------------------------------*/ -/* Exported variables --------------------------------------------------------*/ -/* Exported macro ------------------------------------------------------------*/ -/* Exported functions --------------------------------------------------------*/ + void start(); -/* Exported functions --------------------------------------------------------*/ -void ASCII_protocol_parse_stream(const uint8_t* buffer, size_t len, StreamSink& response_channel); +private: + void cmd_set_position(char * pStr, bool use_checksum); + void cmd_set_position_wl(char * pStr, bool use_checksum); + void cmd_set_velocity(char * pStr, bool use_checksum); + void cmd_set_torque(char * pStr, bool use_checksum); + void cmd_set_trapezoid_trajectory(char * pStr, bool use_checksum); + void cmd_get_feedback(char * pStr, bool use_checksum); + void cmd_help(char * pStr, bool use_checksum); + void cmd_info_dump(char * pStr, bool use_checksum); + void cmd_system_ctrl(char * pStr, bool use_checksum); + void cmd_read_property(char * pStr, bool use_checksum); + void cmd_write_property(char * pStr, bool use_checksum); + void cmd_update_axis_wdg(char * pStr, bool use_checksum); + void cmd_unknown(char * pStr, bool use_checksum); + void cmd_encoder(char * pStr, bool use_checksum); + template void respond(bool include_checksum, const char * fmt, TArgs&& ... args); + void process_line(fibre::cbufptr_t buffer); + void on_write_finished(fibre::WriteResult result); + void on_read_finished(fibre::ReadResult result); -#endif /* __ASCII_PROTOCOL_H */ + fibre::AsyncStreamSource* rx_channel_ = nullptr; + fibre::AsyncStreamSink* tx_channel_ = nullptr; + + fibre::TransferHandle tx_handle_ = 0; // non-zero while a TX operation is in progress + uint8_t* rx_end_ = nullptr; // non-zero if an RX operation has finished but wasn't handled yet because the TX channel was busy + const uint8_t* tx_end_ = nullptr; + + uint8_t rx_buf_[MAX_LINE_LENGTH]; + bool read_active_ = true; + + char tx_buf_[64]; +}; + +#endif // __ASCII_PROTOCOL_HPP diff --git a/Firmware/communication/can_helpers.hpp b/Firmware/communication/can/can_helpers.hpp similarity index 79% rename from Firmware/communication/can_helpers.hpp rename to Firmware/communication/can/can_helpers.hpp index 779b6a7a9..d1e212c82 100644 --- a/Firmware/communication/can_helpers.hpp +++ b/Firmware/communication/can/can_helpers.hpp @@ -21,12 +21,16 @@ struct can_Signal_t { const float offset; }; +struct can_Cyclic_t { + uint32_t cycleTime_ms; + uint32_t lastTime_ms; +}; #include template -T can_getSignal(can_Message_t msg, const uint8_t startBit, const uint8_t length, const bool isIntel) { +constexpr T can_getSignal(can_Message_t msg, const uint8_t startBit, const uint8_t length, const bool isIntel) { uint64_t tempVal = 0; - uint64_t mask = (1ULL << length) - 1; + uint64_t mask = length < 64 ? (1ULL << length) - 1ULL : -1ULL; if (isIntel) { std::memcpy(&tempVal, msg.buf, sizeof(tempVal)); @@ -42,19 +46,12 @@ T can_getSignal(can_Message_t msg, const uint8_t startBit, const uint8_t length, return retVal; } -template -float can_getSignal(can_Message_t msg, const uint8_t startBit, const uint8_t length, const bool isIntel, const float factor, const float offset) { - T retVal = can_getSignal(msg, startBit, length, isIntel); - return (retVal * factor) + offset; -} - template -void can_setSignal(can_Message_t& msg, const T& val, const uint8_t startBit, const uint8_t length, const bool isIntel, const float factor, const float offset) { - T scaledVal = (val - offset) / factor; +constexpr void can_setSignal(can_Message_t& msg, const T& val, const uint8_t startBit, const uint8_t length, const bool isIntel) { uint64_t valAsBits = 0; - std::memcpy(&valAsBits, &scaledVal, sizeof(scaledVal)); + std::memcpy(&valAsBits, &val, sizeof(val)); - uint64_t mask = (1ULL << length) - 1; + uint64_t mask = length < 64 ? (1ULL << length) - 1ULL : -1ULL; if (isIntel) { uint64_t data = 0; @@ -77,6 +74,18 @@ void can_setSignal(can_Message_t& msg, const T& val, const uint8_t startBit, con } } +template +void can_setSignal(can_Message_t& msg, const T& val, const uint8_t startBit, const uint8_t length, const bool isIntel, const float factor, const float offset) { + T scaledVal = static_cast((val - offset) / factor); + can_setSignal(msg, scaledVal, startBit, length, isIntel); +} + +template +float can_getSignal(can_Message_t msg, const uint8_t startBit, const uint8_t length, const bool isIntel, const float factor, const float offset) { + T retVal = can_getSignal(msg, startBit, length, isIntel); + return (retVal * factor) + offset; +} + template float can_getSignal(can_Message_t msg, const can_Signal_t& signal) { return can_getSignal(msg, signal.startBit, signal.length, signal.isIntel, signal.factor, signal.offset); diff --git a/Firmware/communication/can/can_simple.cpp b/Firmware/communication/can/can_simple.cpp new file mode 100644 index 000000000..1c47c3dca --- /dev/null +++ b/Firmware/communication/can/can_simple.cpp @@ -0,0 +1,382 @@ + +#include "can_simple.hpp" + +#include + +bool CANSimple::init() { + for (size_t i = 0; i < AXIS_COUNT; ++i) { + if (!renew_subscription(i)) { + return false; + } + } + + return true; +} + +bool CANSimple::renew_subscription(size_t i) { + Axis& axis = axes[i]; + + // TODO: remove these two lines (see comment in header) + node_ids_[i] = axis.config_.can.node_id; + extended_node_ids_[i] = axis.config_.can.is_extended; + + MsgIdFilterSpecs filter = { + .id = {}, + .mask = (uint32_t)(0xffffffff << NUM_CMD_ID_BITS) + }; + if (axis.config_.can.is_extended) { + filter.id = (uint32_t)(axis.config_.can.node_id << NUM_CMD_ID_BITS); + } else { + filter.id = (uint16_t)(axis.config_.can.node_id << NUM_CMD_ID_BITS); + } + + if (subscription_handles_[i]) { + canbus_->unsubscribe(subscription_handles_[i]); + } + + return canbus_->subscribe(filter, [](void* ctx, const can_Message_t& msg) { + ((CANSimple*)ctx)->handle_can_message(msg); + }, this, &subscription_handles_[i]); +} + +void CANSimple::handle_can_message(const can_Message_t& msg) { + // Frame + // nodeID | CMD + // 6 bits | 5 bits + uint32_t nodeID = get_node_id(msg.id); + + for (auto& axis : axes) { + if ((axis.config_.can.node_id == nodeID) && (axis.config_.can.is_extended == msg.isExt)) { + do_command(axis, msg); + return; + } + } +} + +void CANSimple::do_command(Axis& axis, const can_Message_t& msg) { + const uint32_t cmd = get_cmd_id(msg.id); + axis.watchdog_feed(); + switch (cmd) { + case MSG_CO_NMT_CTRL: + break; + case MSG_CO_HEARTBEAT_CMD: + break; + case MSG_ODRIVE_HEARTBEAT: + // We don't currently do anything to respond to ODrive heartbeat messages + break; + case MSG_ODRIVE_ESTOP: + estop_callback(axis, msg); + break; + case MSG_GET_MOTOR_ERROR: + if (msg.rtr) + get_motor_error_callback(axis); + break; + case MSG_GET_ENCODER_ERROR: + if (msg.rtr) + get_encoder_error_callback(axis); + break; + case MSG_GET_SENSORLESS_ERROR: + if (msg.rtr) + get_sensorless_error_callback(axis); + break; + case MSG_SET_AXIS_NODE_ID: + set_axis_nodeid_callback(axis, msg); + break; + case MSG_SET_AXIS_REQUESTED_STATE: + set_axis_requested_state_callback(axis, msg); + break; + case MSG_SET_AXIS_STARTUP_CONFIG: + set_axis_startup_config_callback(axis, msg); + break; + case MSG_GET_ENCODER_ESTIMATES: + if (msg.rtr) + get_encoder_estimates_callback(axis); + break; + case MSG_GET_ENCODER_COUNT: + if (msg.rtr) + get_encoder_count_callback(axis); + break; + case MSG_SET_INPUT_POS: + set_input_pos_callback(axis, msg); + break; + case MSG_SET_INPUT_VEL: + set_input_vel_callback(axis, msg); + break; + case MSG_SET_INPUT_TORQUE: + set_input_torque_callback(axis, msg); + break; + case MSG_SET_CONTROLLER_MODES: + set_controller_modes_callback(axis, msg); + break; + case MSG_SET_LIMITS: + set_limits_callback(axis, msg); + break; + case MSG_START_ANTICOGGING: + start_anticogging_callback(axis, msg); + break; + case MSG_SET_TRAJ_INERTIA: + set_traj_inertia_callback(axis, msg); + break; + case MSG_SET_TRAJ_ACCEL_LIMITS: + set_traj_accel_limits_callback(axis, msg); + break; + case MSG_SET_TRAJ_VEL_LIMIT: + set_traj_vel_limit_callback(axis, msg); + break; + case MSG_GET_IQ: + if (msg.rtr) + get_iq_callback(axis); + break; + case MSG_GET_SENSORLESS_ESTIMATES: + if (msg.rtr) + get_sensorless_estimates_callback(axis); + break; + case MSG_RESET_ODRIVE: + NVIC_SystemReset(); + break; + case MSG_GET_VBUS_VOLTAGE: + if (msg.rtr) + get_vbus_voltage_callback(axis); + break; + case MSG_CLEAR_ERRORS: + clear_errors_callback(axis, msg); + break; + default: + break; + } +} + +void CANSimple::nmt_callback(const Axis& axis, const can_Message_t& msg) { + // Not implemented +} + +void CANSimple::estop_callback(Axis& axis, const can_Message_t& msg) { + axis.error_ |= Axis::ERROR_ESTOP_REQUESTED; +} + +bool CANSimple::get_motor_error_callback(const Axis& axis) { + can_Message_t txmsg; + txmsg.id = axis.config_.can.node_id << NUM_CMD_ID_BITS; + txmsg.id += MSG_GET_MOTOR_ERROR; // heartbeat ID + txmsg.isExt = axis.config_.can.is_extended; + txmsg.len = 8; + + can_setSignal(txmsg, axis.motor_.error_, 0, 64, true); + + return canbus_->send_message(txmsg); +} + +bool CANSimple::get_encoder_error_callback(const Axis& axis) { + can_Message_t txmsg; + txmsg.id = axis.config_.can.node_id << NUM_CMD_ID_BITS; + txmsg.id += MSG_GET_ENCODER_ERROR; // heartbeat ID + txmsg.isExt = axis.config_.can.is_extended; + txmsg.len = 8; + + can_setSignal(txmsg, axis.encoder_.error_, 0, 32, true); + + return canbus_->send_message(txmsg); +} + +bool CANSimple::get_sensorless_error_callback(const Axis& axis) { + can_Message_t txmsg; + txmsg.id = axis.config_.can.node_id << NUM_CMD_ID_BITS; + txmsg.id += MSG_GET_SENSORLESS_ERROR; // heartbeat ID + txmsg.isExt = axis.config_.can.is_extended; + txmsg.len = 8; + + can_setSignal(txmsg, axis.sensorless_estimator_.error_, 0, 32, true); + + return canbus_->send_message(txmsg); +} + +void CANSimple::set_axis_nodeid_callback(Axis& axis, const can_Message_t& msg) { + axis.config_.can.node_id = can_getSignal(msg, 0, 32, true); +} + +void CANSimple::set_axis_requested_state_callback(Axis& axis, const can_Message_t& msg) { + axis.requested_state_ = static_cast(can_getSignal(msg, 0, 16, true)); +} + +void CANSimple::set_axis_startup_config_callback(Axis& axis, const can_Message_t& msg) { + // Not Implemented +} + +bool CANSimple::get_encoder_estimates_callback(const Axis& axis) { + can_Message_t txmsg; + txmsg.id = axis.config_.can.node_id << NUM_CMD_ID_BITS; + txmsg.id += MSG_GET_ENCODER_ESTIMATES; // heartbeat ID + txmsg.isExt = axis.config_.can.is_extended; + txmsg.len = 8; + + can_setSignal(txmsg, axis.encoder_.pos_estimate_.any().value_or(0.0f), 0, 32, true); + can_setSignal(txmsg, axis.encoder_.vel_estimate_.any().value_or(0.0f), 32, 32, true); + + return canbus_->send_message(txmsg); +} + +bool CANSimple::get_sensorless_estimates_callback(const Axis& axis) { + can_Message_t txmsg; + txmsg.id = axis.config_.can.node_id << NUM_CMD_ID_BITS; + txmsg.id += MSG_GET_SENSORLESS_ESTIMATES; // heartbeat ID + txmsg.isExt = axis.config_.can.is_extended; + txmsg.len = 8; + + static_assert(sizeof(float) == sizeof(axis.sensorless_estimator_.pll_pos_)); + + can_setSignal(txmsg, axis.sensorless_estimator_.pll_pos_, 0, 32, true); + can_setSignal(txmsg, axis.sensorless_estimator_.vel_estimate_.any().value_or(0.0f), 32, 32, true); + + return canbus_->send_message(txmsg); +} + +bool CANSimple::get_encoder_count_callback(const Axis& axis) { + can_Message_t txmsg; + txmsg.id = axis.config_.can.node_id << NUM_CMD_ID_BITS; + txmsg.id += MSG_GET_ENCODER_COUNT; + txmsg.isExt = axis.config_.can.is_extended; + txmsg.len = 8; + + can_setSignal(txmsg, axis.encoder_.shadow_count_, 0, 32, true); + can_setSignal(txmsg, axis.encoder_.count_in_cpr_, 32, 32, true); + return canbus_->send_message(txmsg); +} + +void CANSimple::set_input_pos_callback(Axis& axis, const can_Message_t& msg) { + axis.controller_.input_pos_ = can_getSignal(msg, 0, 32, true); + axis.controller_.input_vel_ = can_getSignal(msg, 32, 16, true, 0.001f, 0); + axis.controller_.input_torque_ = can_getSignal(msg, 48, 16, true, 0.001f, 0); + axis.controller_.input_pos_updated(); +} + +void CANSimple::set_input_vel_callback(Axis& axis, const can_Message_t& msg) { + axis.controller_.input_vel_ = can_getSignal(msg, 0, 32, true); + axis.controller_.input_torque_ = can_getSignal(msg, 32, 32, true); +} + +void CANSimple::set_input_torque_callback(Axis& axis, const can_Message_t& msg) { + axis.controller_.input_torque_ = can_getSignal(msg, 0, 32, true); +} + +void CANSimple::set_controller_modes_callback(Axis& axis, const can_Message_t& msg) { + axis.controller_.config_.control_mode = static_cast(can_getSignal(msg, 0, 32, true)); + axis.controller_.config_.input_mode = static_cast(can_getSignal(msg, 32, 32, true)); +} + +void CANSimple::set_limits_callback(Axis& axis, const can_Message_t& msg) { + axis.controller_.config_.vel_limit = can_getSignal(msg, 0, 32, true); + axis.motor_.config_.current_lim = can_getSignal(msg, 32, 32, true); +} + +void CANSimple::start_anticogging_callback(const Axis& axis, const can_Message_t& msg) { + axis.controller_.start_anticogging_calibration(); +} + +void CANSimple::set_traj_vel_limit_callback(Axis& axis, const can_Message_t& msg) { + axis.trap_traj_.config_.vel_limit = can_getSignal(msg, 0, 32, true); +} + +void CANSimple::set_traj_accel_limits_callback(Axis& axis, const can_Message_t& msg) { + axis.trap_traj_.config_.accel_limit = can_getSignal(msg, 0, 32, true); + axis.trap_traj_.config_.decel_limit = can_getSignal(msg, 32, 32, true); +} + +void CANSimple::set_traj_inertia_callback(Axis& axis, const can_Message_t& msg) { + axis.controller_.config_.inertia = can_getSignal(msg, 0, 32, true); +} + +void CANSimple::set_linear_count_callback(Axis& axis, const can_Message_t& msg){ + axis.encoder_.set_linear_count(can_getSignal(msg, 0, 32, true)); +} + +bool CANSimple::get_iq_callback(const Axis& axis) { + can_Message_t txmsg; + txmsg.id = axis.config_.can.node_id << NUM_CMD_ID_BITS; + txmsg.id += MSG_GET_IQ; + txmsg.isExt = axis.config_.can.is_extended; + txmsg.len = 8; + + std::optional Idq_setpoint = axis.motor_.current_control_.Idq_setpoint_; + if (!Idq_setpoint.has_value()) { + Idq_setpoint = {0.0f, 0.0f}; + } + + static_assert(sizeof(float) == sizeof(Idq_setpoint->first)); + static_assert(sizeof(float) == sizeof(Idq_setpoint->second)); + can_setSignal(txmsg, Idq_setpoint->first, 0, 32, true); + can_setSignal(txmsg, Idq_setpoint->second, 32, 32, true); + + return canbus_->send_message(txmsg); +} + +bool CANSimple::get_vbus_voltage_callback(const Axis& axis) { + can_Message_t txmsg; + + txmsg.id = axis.config_.can.node_id << NUM_CMD_ID_BITS; + txmsg.id += MSG_GET_VBUS_VOLTAGE; + txmsg.isExt = axis.config_.can.is_extended; + txmsg.len = 8; + + uint32_t floatBytes; + static_assert(sizeof(vbus_voltage) == sizeof(floatBytes)); + can_setSignal(txmsg, vbus_voltage, 0, 32, true); + + return canbus_->send_message(txmsg); +} + +void CANSimple::clear_errors_callback(Axis& axis, const can_Message_t& msg) { + odrv.clear_errors(); // TODO: might want to clear axis errors only +} + +uint32_t CANSimple::service_stack() { + uint32_t nextServiceTime = UINT32_MAX; + uint32_t now = HAL_GetTick(); + + // TODO: remove this polling loop and replace with protocol hook + for (size_t i = 0; i < AXIS_COUNT; ++i) { + bool node_id_changed = (axes[i].config_.can.node_id != node_ids_[i]) + || (axes[i].config_.can.is_extended != extended_node_ids_[i]); + if (node_id_changed) { + renew_subscription(i); + } + } + + for (auto& a: axes) { + MEASURE_TIME(a.task_times_.can_heartbeat) { + if (a.config_.can.heartbeat_rate_ms > 0) { + if ((now - a.can_.last_heartbeat) >= a.config_.can.heartbeat_rate_ms) { + if (send_heartbeat(a)) + a.can_.last_heartbeat = now; + } + + int nextAxisService = a.can_.last_heartbeat + a.config_.can.heartbeat_rate_ms - now; + nextServiceTime = std::min(nextServiceTime, static_cast(std::max(0, nextAxisService))); + } + + if (a.config_.can.encoder_rate_ms > 0) { + if ((now - a.can_.last_encoder) >= a.config_.can.encoder_rate_ms) { + if (get_encoder_estimates_callback(a)) + a.can_.last_encoder = now; + } + + int nextAxisService = a.can_.last_encoder + a.config_.can.encoder_rate_ms - now; + nextServiceTime = std::min(nextServiceTime, static_cast(std::max(0, nextAxisService))); + } + } + } + + return nextServiceTime; +} + +bool CANSimple::send_heartbeat(const Axis& axis) { + can_Message_t txmsg; + txmsg.id = axis.config_.can.node_id << NUM_CMD_ID_BITS; + txmsg.id += MSG_ODRIVE_HEARTBEAT; // heartbeat ID + txmsg.isExt = axis.config_.can.is_extended; + txmsg.len = 8; + + can_setSignal(txmsg, axis.error_, 0, 32, true); + can_setSignal(txmsg, axis.current_state_, 32, 32, true); + + return canbus_->send_message(txmsg); +} diff --git a/Firmware/communication/can/can_simple.hpp b/Firmware/communication/can/can_simple.hpp new file mode 100644 index 000000000..9c6ac96ce --- /dev/null +++ b/Firmware/communication/can/can_simple.hpp @@ -0,0 +1,104 @@ +#ifndef __CAN_SIMPLE_HPP_ +#define __CAN_SIMPLE_HPP_ + +#include "canbus.hpp" +#include "axis.hpp" + +class CANSimple { + public: + enum { + MSG_CO_NMT_CTRL = 0x000, // CANOpen NMT Message REC + MSG_ODRIVE_HEARTBEAT, + MSG_ODRIVE_ESTOP, + MSG_GET_MOTOR_ERROR, // Errors + MSG_GET_ENCODER_ERROR, + MSG_GET_SENSORLESS_ERROR, + MSG_SET_AXIS_NODE_ID, + MSG_SET_AXIS_REQUESTED_STATE, + MSG_SET_AXIS_STARTUP_CONFIG, + MSG_GET_ENCODER_ESTIMATES, + MSG_GET_ENCODER_COUNT, + MSG_SET_CONTROLLER_MODES, + MSG_SET_INPUT_POS, + MSG_SET_INPUT_VEL, + MSG_SET_INPUT_TORQUE, + MSG_SET_LIMITS, + MSG_START_ANTICOGGING, + MSG_SET_TRAJ_VEL_LIMIT, + MSG_SET_TRAJ_ACCEL_LIMITS, + MSG_SET_TRAJ_INERTIA, + MSG_GET_IQ, + MSG_GET_SENSORLESS_ESTIMATES, + MSG_RESET_ODRIVE, + MSG_GET_VBUS_VOLTAGE, + MSG_CLEAR_ERRORS, + MSG_CO_HEARTBEAT_CMD = 0x700, // CANOpen NMT Heartbeat SEND + }; + + CANSimple(CanBusBase* canbus) : canbus_(canbus) {} + + bool init(); + uint32_t service_stack(); + + private: + + bool renew_subscription(size_t i); + bool send_heartbeat(const Axis& axis); + + void handle_can_message(const can_Message_t& msg); + + void do_command(Axis& axis, const can_Message_t& cmd); + + // Get functions (msg.rtr bit must be set) + bool get_motor_error_callback(const Axis& axis); + bool get_encoder_error_callback(const Axis& axis); + bool get_controller_error_callback(const Axis& axis); + bool get_sensorless_error_callback(const Axis& axis); + bool get_encoder_estimates_callback(const Axis& axis); + bool get_encoder_count_callback(const Axis& axis); + bool get_iq_callback(const Axis& axis); + bool get_sensorless_estimates_callback(const Axis& axis); + bool get_vbus_voltage_callback(const Axis& axis); + + // Set functions + static void set_axis_nodeid_callback(Axis& axis, const can_Message_t& msg); + static void set_axis_requested_state_callback(Axis& axis, const can_Message_t& msg); + static void set_axis_startup_config_callback(Axis& axis, const can_Message_t& msg); + static void set_input_pos_callback(Axis& axis, const can_Message_t& msg); + static void set_input_vel_callback(Axis& axis, const can_Message_t& msg); + static void set_input_torque_callback(Axis& axis, const can_Message_t& msg); + static void set_controller_modes_callback(Axis& axis, const can_Message_t& msg); + static void set_limits_callback(Axis& axis, const can_Message_t& msg); + static void set_traj_vel_limit_callback(Axis& axis, const can_Message_t& msg); + static void set_traj_accel_limits_callback(Axis& axis, const can_Message_t& msg); + static void set_traj_inertia_callback(Axis& axis, const can_Message_t& msg); + static void set_linear_count_callback(Axis& axis, const can_Message_t& msg); + + // Other functions + static void nmt_callback(const Axis& axis, const can_Message_t& msg); + static void estop_callback(Axis& axis, const can_Message_t& msg); + static void clear_errors_callback(Axis& axis, const can_Message_t& msg); + static void start_anticogging_callback(const Axis& axis, const can_Message_t& msg); + + static constexpr uint8_t NUM_NODE_ID_BITS = 6; + static constexpr uint8_t NUM_CMD_ID_BITS = 11 - NUM_NODE_ID_BITS; + + // Utility functions + static constexpr uint32_t get_node_id(uint32_t msgID) { + return (msgID >> NUM_CMD_ID_BITS); // Upper 6 or more bits + }; + + static constexpr uint8_t get_cmd_id(uint32_t msgID) { + return (msgID & 0x01F); // Bottom 5 bits + } + + CanBusBase* canbus_; + CanBusBase::CanSubscription* subscription_handles_[AXIS_COUNT]; + + // TODO: we this is a hack but actually we should use protocol hooks to + // renew our filter when the node ID changes + uint32_t node_ids_[AXIS_COUNT]; + bool extended_node_ids_[AXIS_COUNT]; +}; + +#endif \ No newline at end of file diff --git a/Firmware/communication/can/canbus.hpp b/Firmware/communication/can/canbus.hpp new file mode 100644 index 000000000..fbc3df1e2 --- /dev/null +++ b/Firmware/communication/can/canbus.hpp @@ -0,0 +1,43 @@ +#ifndef __CANBUS_HPP +#define __CANBUS_HPP + +#include "can_helpers.hpp" +#include + +struct MsgIdFilterSpecs { + std::variant id; + uint32_t mask; +}; + +class CanBusBase { +public: + typedef void(*on_can_message_cb_t)(void* ctx, const can_Message_t& message); + struct CanSubscription {}; + + /** + * @brief Sends the specified CAN message. + * + * @returns: true on success or false otherwise (e.g. if the send queue is + * full). + */ + virtual bool send_message(const can_Message_t& message) = 0; + + /** + * @brief Registers a callback that will be invoked for every incoming CAN + * message that matches the filter. + * + * @param handle: On success this handle is set to an opaque pointer that + * can be used to cancel the subscription. + * + * @returns: true on success or false otherwise (e.g. if the maximum number + * of subscriptions has been reached). + */ + virtual bool subscribe(const MsgIdFilterSpecs& filter, on_can_message_cb_t callback, void* ctx, CanSubscription** handle) = 0; + + /** + * @brief Deregisters a callback that was previously registered with subscribe(). + */ + virtual bool unsubscribe(CanSubscription* handle) = 0; +}; + +#endif // __CANBUS_HPP \ No newline at end of file diff --git a/Firmware/communication/can/odrive_can.cpp b/Firmware/communication/can/odrive_can.cpp new file mode 100644 index 000000000..db6def57a --- /dev/null +++ b/Firmware/communication/can/odrive_can.cpp @@ -0,0 +1,238 @@ +#include "odrive_can.hpp" + +#include +#include + +#include "freertos_vars.h" +#include "utils.hpp" + +// Safer context handling via maps instead of arrays +// #include +// std::unordered_map ctxMap; + + +bool ODriveCAN::apply_config() { + config_.parent = this; + set_baud_rate(config_.baud_rate); + return true; +} + +bool ODriveCAN::reinit() { + HAL_CAN_Stop(handle_); + HAL_CAN_ResetError(handle_); + return (HAL_CAN_Init(handle_) == HAL_OK) + && (HAL_CAN_Start(handle_) == HAL_OK) + && (HAL_CAN_ActivateNotification(handle_, CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_RX_FIFO1_MSG_PENDING | CAN_IT_TX_MAILBOX_EMPTY) == HAL_OK); +} + +bool ODriveCAN::start_server(CAN_HandleTypeDef* handle) { + handle_ = handle; + + handle_->Init.Prescaler = CAN_FREQ / config_.baud_rate; + if (!reinit()) { + return false; + } + + auto wrapper = [](void* ctx) { + ((ODriveCAN*)ctx)->can_server_thread(); + }; + osThreadDef(can_server_thread_def, wrapper, osPriorityNormal, 0, stack_size_ / sizeof(StackType_t)); + thread_id_ = osThreadCreate(osThread(can_server_thread_def), this); + + return true; +} + +void ODriveCAN::can_server_thread() { + Protocol protocol = config_.protocol; + + if (protocol & PROTOCOL_SIMPLE) { + can_simple_.init(); + } + + for (;;) { + uint32_t status = HAL_CAN_GetError(handle_); + if (status == HAL_CAN_ERROR_NONE) { + uint32_t next_service_time = UINT32_MAX; + + if (protocol & PROTOCOL_SIMPLE) { + next_service_time = std::min(can_simple_.service_stack(), next_service_time); + } + + process_rx_fifo(CAN_RX_FIFO0); + process_rx_fifo(CAN_RX_FIFO1); + HAL_CAN_ActivateNotification(handle_, CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_RX_FIFO1_MSG_PENDING | CAN_IT_TX_MAILBOX_EMPTY); + + // wait at least 1ms to prevent busy-spin on failed sends + osSemaphoreWait(sem_can, std::max(next_service_time, 1UL)); + } else if (status == HAL_CAN_ERROR_TIMEOUT) { + HAL_CAN_ResetError(handle_); + status = HAL_CAN_Start(handle_); + if (status == HAL_OK) + status = HAL_CAN_ActivateNotification(handle_, CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_TX_MAILBOX_EMPTY); + } + } +} + +// Set one of only a few common baud rates. CAN doesn't do arbitrary baud rates well due to the time-quanta issue. +// 21 TQ allows for easy sampling at exactly 80% (recommended by Vector Informatik GmbH for high reliability systems) +// Conveniently, the CAN peripheral's 42MHz clock lets us easily create 21TQs for all common baud rates +bool ODriveCAN::set_baud_rate(uint32_t baud_rate) { + uint32_t prescaler = CAN_FREQ / baud_rate; + if (prescaler * baud_rate == CAN_FREQ) { + // valid baud rate + config_.baud_rate = baud_rate; + if (handle_) { + handle_->Init.Prescaler = prescaler; + return reinit(); + } + return true; + } else { + // invalid baud rate - ignore + return false; + } +} + +void ODriveCAN::process_rx_fifo(uint32_t fifo) { + while (HAL_CAN_GetRxFifoFillLevel(handle_, fifo)) { + CAN_RxHeaderTypeDef header; + can_Message_t rxmsg; + HAL_CAN_GetRxMessage(handle_, fifo, &header, rxmsg.buf); + + rxmsg.isExt = header.IDE; + rxmsg.id = rxmsg.isExt ? header.ExtId : header.StdId; // If it's an extended message, pass the extended ID + rxmsg.len = header.DLC; + rxmsg.rtr = header.RTR; + + // TODO: this could be optimized with an ahead-of-time computed + // index-to-filter map + + size_t fifo0_idx = 0; + size_t fifo1_idx = 0; + + // Find the triggered subscription item based on header.FilterMatchIndex + auto it = std::find_if(subscriptions_.begin(), subscriptions_.end(), [&](auto& s) { + size_t current_idx = (s.fifo == 0 ? fifo0_idx : fifo1_idx)++; + return (header.FilterMatchIndex == current_idx) && (s.fifo == fifo); + }); + + if (it == subscriptions_.end()) { + continue; + } + + it->callback(it->ctx, rxmsg); + } +} + +// Send a CAN message on the bus +bool ODriveCAN::send_message(const can_Message_t &txmsg) { + if (HAL_CAN_GetError(handle_) != HAL_CAN_ERROR_NONE) { + return false; + } + + CAN_TxHeaderTypeDef header; + header.StdId = txmsg.id; + header.ExtId = txmsg.id; + header.IDE = txmsg.isExt ? CAN_ID_EXT : CAN_ID_STD; + header.RTR = CAN_RTR_DATA; + header.DLC = txmsg.len; + header.TransmitGlobalTime = FunctionalState::DISABLE; + + uint32_t retTxMailbox = 0; + if (!HAL_CAN_GetTxMailboxesFreeLevel(handle_)) { + return false; + } + + return HAL_CAN_AddTxMessage(handle_, &header, (uint8_t*)txmsg.buf, &retTxMailbox) == HAL_OK; +} + +//void ODriveCAN::set_error(Error error) { +// error_ |= error; +//} + +bool ODriveCAN::subscribe(const MsgIdFilterSpecs& filter, on_can_message_cb_t callback, void* ctx, CanSubscription** handle) { + auto it = std::find_if(subscriptions_.begin(), subscriptions_.end(), [](auto& subscription) { + return subscription.fifo == kCanFifoNone; + }); + + if (it == subscriptions_.end()) { + return false; // all subscription slots in use + } + + it->callback = callback; + it->ctx = ctx; + it->fifo = CAN_RX_FIFO0; // TODO: make customizable + if (handle) { + *handle = &*it; + } + + bool is_extended = filter.id.index() == 1; + uint32_t id = is_extended ? + ((std::get<1>(filter.id) << 3) | (1 << 2)) : + (std::get<0>(filter.id) << 21); + uint32_t mask = (is_extended ? (filter.mask << 3) : (filter.mask << 21)) + | (1 << 2); // care about the is_extended bit + + CAN_FilterTypeDef hal_filter; + hal_filter.FilterActivation = ENABLE; + hal_filter.FilterBank = &*it - &subscriptions_[0]; + hal_filter.FilterFIFOAssignment = it->fifo; + hal_filter.FilterIdHigh = (id >> 16) & 0xffff; + hal_filter.FilterIdLow = id & 0xffff; + hal_filter.FilterMaskIdHigh = (mask >> 16) & 0xffff; + hal_filter.FilterMaskIdLow = mask & 0xffff; + hal_filter.FilterMode = CAN_FILTERMODE_IDMASK; + hal_filter.FilterScale = CAN_FILTERSCALE_32BIT; + + if (HAL_CAN_ConfigFilter(handle_, &hal_filter) != HAL_OK) { + return false; + } + return true; +} + +bool ODriveCAN::unsubscribe(CanSubscription* handle) { + ODriveCanSubscription* subscription = static_cast(handle); + if (subscription < subscriptions_.begin() || subscription >= subscriptions_.end()) { + return false; + } + if (subscription->fifo != kCanFifoNone) { + return false; // not in use + } + + subscription->fifo = kCanFifoNone; + + CAN_FilterTypeDef hal_filter = {}; + hal_filter.FilterActivation = DISABLE; + return HAL_CAN_ConfigFilter(handle_, &hal_filter) == HAL_OK; +} + +void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan) { + HAL_CAN_DeactivateNotification(hcan, CAN_IT_TX_MAILBOX_EMPTY); + osSemaphoreRelease(sem_can); +} +void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan) { + HAL_CAN_DeactivateNotification(hcan, CAN_IT_TX_MAILBOX_EMPTY); + osSemaphoreRelease(sem_can); +} +void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan) { + HAL_CAN_DeactivateNotification(hcan, CAN_IT_TX_MAILBOX_EMPTY); + osSemaphoreRelease(sem_can); +} +void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan) {} +void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan) {} +void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan) {} +void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { + HAL_CAN_DeactivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); + osSemaphoreRelease(sem_can); +} +void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan) { + HAL_CAN_DeactivateNotification(hcan, CAN_IT_RX_FIFO1_MSG_PENDING); + osSemaphoreRelease(sem_can); +} +void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) {} +void HAL_CAN_RxFifo1FullCallback(CAN_HandleTypeDef *hcan) {} +void HAL_CAN_SleepCallback(CAN_HandleTypeDef *hcan) {} +void HAL_CAN_WakeUpFromRxMsgCallback(CAN_HandleTypeDef *hcan) {} + +void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) { + //HAL_CAN_ResetError(hcan); +} diff --git a/Firmware/communication/can/odrive_can.hpp b/Firmware/communication/can/odrive_can.hpp new file mode 100644 index 000000000..5910c730d --- /dev/null +++ b/Firmware/communication/can/odrive_can.hpp @@ -0,0 +1,68 @@ +#ifndef __ODRIVE_CAN_HPP +#define __ODRIVE_CAN_HPP + +#include + +#include "canbus.hpp" +#include "can_simple.hpp" +#include + +#define CAN_CLK_HZ (42000000) +#define CAN_CLK_MHZ (42) + +// Anonymous enum for defining the most common CAN baud rates +enum { + CAN_BAUD_125K = 125000, + CAN_BAUD_250K = 250000, + CAN_BAUD_500K = 500000, + CAN_BAUD_1000K = 1000000, + CAN_BAUD_1M = 1000000 +}; + +class ODriveCAN : public CanBusBase, public ODriveIntf::CanIntf { +public: + struct Config_t { + uint32_t baud_rate = CAN_BAUD_250K; + Protocol protocol = PROTOCOL_SIMPLE; + + ODriveCAN* parent = nullptr; // set in apply_config() + void set_baud_rate(uint32_t value) { parent->set_baud_rate(value); } + }; + + ODriveCAN() {} + + bool apply_config(); + bool start_server(CAN_HandleTypeDef* handle); + + Error error_ = ERROR_NONE; + + Config_t config_; + CANSimple can_simple_{this}; + + osThreadId thread_id_; + const uint32_t stack_size_ = 1024; // Bytes + +private: + static const uint8_t kCanFifoNone = 0xff; + + struct ODriveCanSubscription : CanSubscription { + uint8_t fifo = kCanFifoNone; + on_can_message_cb_t callback; + void* ctx; + }; + + bool reinit(); + void can_server_thread(); + bool set_baud_rate(uint32_t baud_rate); + void process_rx_fifo(uint32_t fifo); + bool send_message(const can_Message_t& message) final; + bool subscribe(const MsgIdFilterSpecs& filter, on_can_message_cb_t callback, void* ctx, CanSubscription** handle) final; + bool unsubscribe(CanSubscription* handle) final; + + // Hardware supports at most 28 filters unless we do optimizations. For now + // we don't need that many. + std::array subscriptions_; + CAN_HandleTypeDef *handle_ = nullptr; +}; + +#endif // __ODRIVE_CAN_HPP diff --git a/Firmware/communication/can_simple.cpp b/Firmware/communication/can_simple.cpp deleted file mode 100644 index f21f4b900..000000000 --- a/Firmware/communication/can_simple.cpp +++ /dev/null @@ -1,412 +0,0 @@ - -#include "can_simple.hpp" -#include - -#include - -static constexpr uint8_t NUM_NODE_ID_BITS = 6; -static constexpr uint8_t NUM_CMD_ID_BITS = 11 - NUM_NODE_ID_BITS; - -void CANSimple::handle_can_message(can_Message_t& msg) { - // This functional way of handling the messages is neat and is much cleaner from - // a data security point of view, but it will require some tweaking to fix the syntax. - // - // auto func = callback_map.find(msg.id); - // if(func != callback_map.end()){ - // func->second(msg); - // } - - // Frame - // nodeID | CMD - // 6 bits | 5 bits - uint32_t nodeID = get_node_id(msg.id); - uint32_t cmd = get_cmd_id(msg.id); - - Axis* axis = nullptr; - - bool validAxis = false; - for (uint8_t i = 0; i < AXIS_COUNT; i++) { - if ((axes[i]->config_.can_node_id == nodeID) && (axes[i]->config_.can_node_id_extended == msg.isExt)) { - axis = axes[i]; - if (!validAxis) { - validAxis = true; - } else { - // Duplicate can IDs, don't assign to any axis - odCAN->set_error(ODriveCAN::ERROR_DUPLICATE_CAN_IDS); - validAxis = false; - break; - } - } - } - - if (validAxis) { - axis->watchdog_feed(); - switch (cmd) { - case MSG_CO_NMT_CTRL: - break; - case MSG_CO_HEARTBEAT_CMD: - break; - case MSG_ODRIVE_HEARTBEAT: - // We don't currently do anything to respond to ODrive heartbeat messages - break; - case MSG_ODRIVE_ESTOP: - estop_callback(axis, msg); - break; - case MSG_GET_MOTOR_ERROR: - get_motor_error_callback(axis, msg); - break; - case MSG_GET_ENCODER_ERROR: - get_encoder_error_callback(axis, msg); - break; - case MSG_GET_SENSORLESS_ERROR: - get_sensorless_error_callback(axis, msg); - break; - case MSG_SET_AXIS_NODE_ID: - set_axis_nodeid_callback(axis, msg); - break; - case MSG_SET_AXIS_REQUESTED_STATE: - set_axis_requested_state_callback(axis, msg); - break; - case MSG_SET_AXIS_STARTUP_CONFIG: - set_axis_startup_config_callback(axis, msg); - break; - case MSG_GET_ENCODER_ESTIMATES: - get_encoder_estimates_callback(axis, msg); - break; - case MSG_GET_ENCODER_COUNT: - get_encoder_count_callback(axis, msg); - break; - case MSG_SET_INPUT_POS: - set_input_pos_callback(axis, msg); - break; - case MSG_SET_INPUT_VEL: - set_input_vel_callback(axis, msg); - break; - case MSG_SET_INPUT_TORQUE: - set_input_torque_callback(axis, msg); - break; - case MSG_SET_CONTROLLER_MODES: - set_controller_modes_callback(axis, msg); - break; - case MSG_SET_VEL_LIMIT: - set_vel_limit_callback(axis, msg); - break; - case MSG_START_ANTICOGGING: - start_anticogging_callback(axis, msg); - break; - case MSG_SET_TRAJ_INERTIA: - set_traj_inertia_callback(axis, msg); - break; - case MSG_SET_TRAJ_ACCEL_LIMITS: - set_traj_accel_limits_callback(axis, msg); - break; - case MSG_SET_TRAJ_VEL_LIMIT: - set_traj_vel_limit_callback(axis, msg); - break; - case MSG_GET_IQ: - get_iq_callback(axis, msg); - break; - case MSG_GET_SENSORLESS_ESTIMATES: - get_sensorless_estimates_callback(axis, msg); - break; - case MSG_RESET_ODRIVE: - NVIC_SystemReset(); - break; - case MSG_GET_VBUS_VOLTAGE: - get_vbus_voltage_callback(axis, msg); - break; - case MSG_CLEAR_ERRORS: - clear_errors_callback(axis, msg); - break; - default: - break; - } - } -} - -void CANSimple::nmt_callback(Axis* axis, can_Message_t& msg) { - // Not implemented -} - -void CANSimple::estop_callback(Axis* axis, can_Message_t& msg) { - axis->error_ |= Axis::ERROR_ESTOP_REQUESTED; -} - -void CANSimple::get_motor_error_callback(Axis* axis, can_Message_t& msg) { - if (msg.rtr) { - can_Message_t txmsg; - txmsg.id = axis->config_.can_node_id << NUM_CMD_ID_BITS; - txmsg.id += MSG_GET_MOTOR_ERROR; // heartbeat ID - txmsg.isExt = axis->config_.can_node_id_extended; - txmsg.len = 8; - - txmsg.buf[0] = axis->motor_.error_; - txmsg.buf[1] = axis->motor_.error_ >> 8; - txmsg.buf[2] = axis->motor_.error_ >> 16; - txmsg.buf[3] = axis->motor_.error_ >> 24; - - odCAN->write(txmsg); - } -} - -void CANSimple::get_encoder_error_callback(Axis* axis, can_Message_t& msg) { - if (msg.rtr) { - can_Message_t txmsg; - txmsg.id = axis->config_.can_node_id << NUM_CMD_ID_BITS; - txmsg.id += MSG_GET_ENCODER_ERROR; // heartbeat ID - txmsg.isExt = axis->config_.can_node_id_extended; - txmsg.len = 8; - - txmsg.buf[0] = axis->encoder_.error_; - txmsg.buf[1] = axis->encoder_.error_ >> 8; - txmsg.buf[2] = axis->encoder_.error_ >> 16; - txmsg.buf[3] = axis->encoder_.error_ >> 24; - - odCAN->write(txmsg); - } -} - -void CANSimple::get_sensorless_error_callback(Axis* axis, can_Message_t& msg) { - if (msg.rtr) { - can_Message_t txmsg; - txmsg.id = axis->config_.can_node_id << NUM_CMD_ID_BITS; - txmsg.id += MSG_GET_SENSORLESS_ERROR; // heartbeat ID - txmsg.isExt = axis->config_.can_node_id_extended; - txmsg.len = 8; - - txmsg.buf[0] = axis->sensorless_estimator_.error_; - txmsg.buf[1] = axis->sensorless_estimator_.error_ >> 8; - txmsg.buf[2] = axis->sensorless_estimator_.error_ >> 16; - txmsg.buf[3] = axis->sensorless_estimator_.error_ >> 24; - - odCAN->write(txmsg); - } -} - -void CANSimple::set_axis_nodeid_callback(Axis* axis, can_Message_t& msg) { - axis->config_.can_node_id = can_getSignal(msg, 0, 32, true); -} - -void CANSimple::set_axis_requested_state_callback(Axis* axis, can_Message_t& msg) { - axis->requested_state_ = static_cast(can_getSignal(msg, 0, 16, true)); -} -void CANSimple::set_axis_startup_config_callback(Axis* axis, can_Message_t& msg) { - // Not Implemented -} - -void CANSimple::get_encoder_estimates_callback(Axis* axis, can_Message_t& msg) { - if (msg.rtr) { - can_Message_t txmsg; - txmsg.id = axis->config_.can_node_id << NUM_CMD_ID_BITS; - txmsg.id += MSG_GET_ENCODER_ESTIMATES; // heartbeat ID - txmsg.isExt = axis->config_.can_node_id_extended; - txmsg.len = 8; - - // Undefined behaviour! - // uint32_t floatBytes = *(reinterpret_cast(&(axis->encoder_.pos_estimate_))); - - uint32_t floatBytes; - static_assert(sizeof axis->encoder_.pos_estimate_ == sizeof floatBytes); - std::memcpy(&floatBytes, &axis->encoder_.pos_estimate_, sizeof floatBytes); - - txmsg.buf[0] = floatBytes; - txmsg.buf[1] = floatBytes >> 8; - txmsg.buf[2] = floatBytes >> 16; - txmsg.buf[3] = floatBytes >> 24; - - static_assert(sizeof floatBytes == sizeof axis->encoder_.vel_estimate_); - std::memcpy(&floatBytes, &axis->encoder_.vel_estimate_, sizeof floatBytes); - txmsg.buf[4] = floatBytes; - txmsg.buf[5] = floatBytes >> 8; - txmsg.buf[6] = floatBytes >> 16; - txmsg.buf[7] = floatBytes >> 24; - - odCAN->write(txmsg); - } -} - -void CANSimple::get_sensorless_estimates_callback(Axis* axis, can_Message_t& msg) { - if (msg.rtr) { - can_Message_t txmsg; - txmsg.id = axis->config_.can_node_id << NUM_CMD_ID_BITS; - txmsg.id += MSG_GET_SENSORLESS_ESTIMATES; // heartbeat ID - txmsg.isExt = axis->config_.can_node_id_extended; - txmsg.len = 8; - - // Undefined behaviour! - // uint32_t floatBytes = *(reinterpret_cast(&(axis->encoder_.pos_estimate_))); - - uint32_t floatBytes; - static_assert(sizeof axis->sensorless_estimator_.pll_pos_ == sizeof floatBytes); - std::memcpy(&floatBytes, &axis->sensorless_estimator_.pll_pos_, sizeof floatBytes); - - txmsg.buf[0] = floatBytes; - txmsg.buf[1] = floatBytes >> 8; - txmsg.buf[2] = floatBytes >> 16; - txmsg.buf[3] = floatBytes >> 24; - - static_assert(sizeof floatBytes == sizeof axis->sensorless_estimator_.vel_estimate_); - std::memcpy(&floatBytes, &axis->sensorless_estimator_.vel_estimate_, sizeof floatBytes); - txmsg.buf[4] = floatBytes; - txmsg.buf[5] = floatBytes >> 8; - txmsg.buf[6] = floatBytes >> 16; - txmsg.buf[7] = floatBytes >> 24; - - odCAN->write(txmsg); - } -} - -void CANSimple::get_encoder_count_callback(Axis* axis, can_Message_t& msg) { - if (msg.rtr) { - can_Message_t txmsg; - txmsg.id = axis->config_.can_node_id << NUM_CMD_ID_BITS; - txmsg.id += MSG_GET_ENCODER_COUNT; - txmsg.isExt = axis->config_.can_node_id_extended; - txmsg.len = 8; - - txmsg.buf[0] = axis->encoder_.shadow_count_; - txmsg.buf[1] = axis->encoder_.shadow_count_ >> 8; - txmsg.buf[2] = axis->encoder_.shadow_count_ >> 16; - txmsg.buf[3] = axis->encoder_.shadow_count_ >> 24; - - txmsg.buf[4] = axis->encoder_.count_in_cpr_; - txmsg.buf[5] = axis->encoder_.count_in_cpr_ >> 8; - txmsg.buf[6] = axis->encoder_.count_in_cpr_ >> 16; - txmsg.buf[7] = axis->encoder_.count_in_cpr_ >> 24; - - odCAN->write(txmsg); - } -} - -void CANSimple::set_input_pos_callback(Axis* axis, can_Message_t& msg) { - axis->controller_.input_pos_ = can_getSignal(msg, 0, 32, true); - axis->controller_.input_vel_ = can_getSignal(msg, 32, 16, true, 0.001f, 0); - axis->controller_.input_torque_ = can_getSignal(msg, 48, 16, true, 0.001f, 0); - axis->controller_.input_pos_updated(); -} - -void CANSimple::set_input_vel_callback(Axis* axis, can_Message_t& msg) { - axis->controller_.input_vel_ = can_getSignal(msg, 0, 32, true); - axis->controller_.input_torque_ = can_getSignal(msg, 32, 32, true); -} - -void CANSimple::set_input_torque_callback(Axis* axis, can_Message_t& msg) { - axis->controller_.input_torque_ = can_getSignal(msg, 0, 32, true); -} - -void CANSimple::set_controller_modes_callback(Axis* axis, can_Message_t& msg) { - axis->controller_.config_.control_mode = static_cast(can_getSignal(msg, 0, 32, true)); - axis->controller_.config_.input_mode = static_cast(can_getSignal(msg, 32, 32, true)); -} - -void CANSimple::set_vel_limit_callback(Axis* axis, can_Message_t& msg) { - axis->controller_.config_.vel_limit = can_getSignal(msg, 0, 32, true); -} - -void CANSimple::start_anticogging_callback(Axis* axis, can_Message_t& msg) { - axis->controller_.start_anticogging_calibration(); -} - -void CANSimple::set_traj_vel_limit_callback(Axis* axis, can_Message_t& msg) { - axis->trap_traj_.config_.vel_limit = can_getSignal(msg, 0, 32, true); -} - -void CANSimple::set_traj_accel_limits_callback(Axis* axis, can_Message_t& msg) { - axis->trap_traj_.config_.accel_limit = can_getSignal(msg, 0, 32, true); - axis->trap_traj_.config_.decel_limit = can_getSignal(msg, 32, 32, true); -} - -void CANSimple::set_traj_inertia_callback(Axis* axis, can_Message_t& msg) { - axis->controller_.config_.inertia = can_getSignal(msg, 0, 32, true); -} - -void CANSimple::get_iq_callback(Axis* axis, can_Message_t& msg) { - if (msg.rtr) { - can_Message_t txmsg; - txmsg.id = axis->config_.can_node_id << NUM_CMD_ID_BITS; - txmsg.id += MSG_GET_IQ; - txmsg.isExt = axis->config_.can_node_id_extended; - txmsg.len = 8; - - uint32_t floatBytes; - static_assert(sizeof axis->motor_.current_control_.Iq_setpoint == sizeof floatBytes); - std::memcpy(&floatBytes, &axis->motor_.current_control_.Iq_setpoint, sizeof floatBytes); - - txmsg.buf[0] = floatBytes; - txmsg.buf[1] = floatBytes >> 8; - txmsg.buf[2] = floatBytes >> 16; - txmsg.buf[3] = floatBytes >> 24; - - static_assert(sizeof floatBytes == sizeof axis->motor_.current_control_.Iq_measured); - std::memcpy(&floatBytes, &axis->motor_.current_control_.Iq_measured, sizeof floatBytes); - txmsg.buf[4] = floatBytes; - txmsg.buf[5] = floatBytes >> 8; - txmsg.buf[6] = floatBytes >> 16; - txmsg.buf[7] = floatBytes >> 24; - - odCAN->write(txmsg); - } -} - -void CANSimple::get_vbus_voltage_callback(Axis* axis, can_Message_t& msg) { - if (msg.rtr) { - can_Message_t txmsg; - - txmsg.id = axis->config_.can_node_id << NUM_CMD_ID_BITS; - txmsg.id += MSG_GET_VBUS_VOLTAGE; - txmsg.isExt = axis->config_.can_node_id_extended; - txmsg.len = 8; - - uint32_t floatBytes; - static_assert(sizeof vbus_voltage == sizeof floatBytes); - std::memcpy(&floatBytes, &vbus_voltage, sizeof floatBytes); - - // This also works in principle, but I don't have hardware to verify endianness - // std::memcpy(&txmsg.buf[0], &vbus_voltage, sizeof vbus_voltage); - - txmsg.buf[0] = floatBytes; - txmsg.buf[1] = floatBytes >> 8; - txmsg.buf[2] = floatBytes >> 16; - txmsg.buf[3] = floatBytes >> 24; - - txmsg.buf[4] = 0; - txmsg.buf[5] = 0; - txmsg.buf[6] = 0; - txmsg.buf[7] = 0; - - odCAN->write(txmsg); - } -} - -void CANSimple::clear_errors_callback(Axis* axis, can_Message_t& msg) { - axis->clear_errors(); -} - -void CANSimple::send_heartbeat(Axis* axis) { - can_Message_t txmsg; - txmsg.id = axis->config_.can_node_id << NUM_CMD_ID_BITS; - txmsg.id += MSG_ODRIVE_HEARTBEAT; // heartbeat ID - txmsg.isExt = axis->config_.can_node_id_extended; - txmsg.len = 8; - - // Axis errors in 1st 32-bit value - txmsg.buf[0] = axis->error_; - txmsg.buf[1] = axis->error_ >> 8; - txmsg.buf[2] = axis->error_ >> 16; - txmsg.buf[3] = axis->error_ >> 24; - - // Current state of axis in 2nd 32-bit value - txmsg.buf[4] = axis->current_state_; - txmsg.buf[5] = axis->current_state_ >> 8; - txmsg.buf[6] = axis->current_state_ >> 16; - txmsg.buf[7] = axis->current_state_ >> 24; - odCAN->write(txmsg); -} - -uint32_t CANSimple::get_node_id(uint32_t msgID) { - return (msgID >> NUM_CMD_ID_BITS); // Upper 6 or more bits -} - -uint8_t CANSimple::get_cmd_id(uint32_t msgID) { - return (msgID & 0x01F); // Bottom 5 bits -} \ No newline at end of file diff --git a/Firmware/communication/can_simple.hpp b/Firmware/communication/can_simple.hpp deleted file mode 100644 index 0168b9786..000000000 --- a/Firmware/communication/can_simple.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef __CAN_SIMPLE_HPP_ -#define __CAN_SIMPLE_HPP_ - -#include "interface_can.hpp" - -class CANSimple { - public: - enum { - MSG_CO_NMT_CTRL = 0x000, // CANOpen NMT Message REC - MSG_ODRIVE_HEARTBEAT, - MSG_ODRIVE_ESTOP, - MSG_GET_MOTOR_ERROR, // Errors - MSG_GET_ENCODER_ERROR, - MSG_GET_SENSORLESS_ERROR, - MSG_SET_AXIS_NODE_ID, - MSG_SET_AXIS_REQUESTED_STATE, - MSG_SET_AXIS_STARTUP_CONFIG, - MSG_GET_ENCODER_ESTIMATES, - MSG_GET_ENCODER_COUNT, - MSG_SET_CONTROLLER_MODES, - MSG_SET_INPUT_POS, - MSG_SET_INPUT_VEL, - MSG_SET_INPUT_TORQUE, - MSG_SET_VEL_LIMIT, - MSG_START_ANTICOGGING, - MSG_SET_TRAJ_VEL_LIMIT, - MSG_SET_TRAJ_ACCEL_LIMITS, - MSG_SET_TRAJ_INERTIA, - MSG_GET_IQ, - MSG_GET_SENSORLESS_ESTIMATES, - MSG_RESET_ODRIVE, - MSG_GET_VBUS_VOLTAGE, - MSG_CLEAR_ERRORS, - MSG_CO_HEARTBEAT_CMD = 0x700, // CANOpen NMT Heartbeat SEND - }; - - static void handle_can_message(can_Message_t& msg); - static void send_heartbeat(Axis* axis); - - private: - static void nmt_callback(Axis* axis, can_Message_t& msg); - static void estop_callback(Axis* axis, can_Message_t& msg); - static void get_motor_error_callback(Axis* axis, can_Message_t& msg); - static void get_encoder_error_callback(Axis* axis, can_Message_t& msg); - static void get_controller_error_callback(Axis* axis, can_Message_t& msg); - static void get_sensorless_error_callback(Axis* axis, can_Message_t& msg); - static void set_axis_nodeid_callback(Axis* axis, can_Message_t& msg); - static void set_axis_requested_state_callback(Axis* axis, can_Message_t& msg); - static void set_axis_startup_config_callback(Axis* axis, can_Message_t& msg); - static void get_encoder_estimates_callback(Axis* axis, can_Message_t& msg); - static void get_encoder_count_callback(Axis* axis, can_Message_t& msg); - static void set_input_pos_callback(Axis* axis, can_Message_t& msg); - static void set_input_vel_callback(Axis* axis, can_Message_t& msg); - static void set_input_torque_callback(Axis* axis, can_Message_t& msg); - static void set_controller_modes_callback(Axis* axis, can_Message_t& msg); - static void set_vel_limit_callback(Axis* axis, can_Message_t& msg); - static void start_anticogging_callback(Axis* axis, can_Message_t& msg); - static void set_traj_vel_limit_callback(Axis* axis, can_Message_t& msg); - static void set_traj_accel_limits_callback(Axis* axis, can_Message_t& msg); - static void set_traj_inertia_callback(Axis* axis, can_Message_t& msg); - static void get_iq_callback(Axis* axis, can_Message_t& msg); - static void get_sensorless_estimates_callback(Axis* axis, can_Message_t& msg); - static void get_vbus_voltage_callback(Axis* axis, can_Message_t& msg); - static void clear_errors_callback(Axis* axis, can_Message_t& msg); - - // Utility functions - static uint32_t get_node_id(uint32_t msgID); - static uint8_t get_cmd_id(uint32_t msgID); - - // Fetch a specific signal from the message - - // This functional way of handling the messages is neat and is much cleaner from - // a data security point of view, but it will require some tweaking - // - // const std::map> callback_map = { - // {0x000, std::bind(&CANSimple::heartbeat_callback, this, _1)} - // }; -}; - - - - - -#endif \ No newline at end of file diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index c9eaa589c..d83255ae3 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -11,7 +11,6 @@ #include "odrive_main.h" #include "freertos_vars.h" #include "utils.hpp" -#include "gpio_utils.hpp" #include #include @@ -33,62 +32,64 @@ char serial_number_str[13]; // 12 digits + null termination /* Private constant data -----------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ - -osThreadId comm_thread; -const uint32_t stack_size_comm_thread = 4096; // Bytes -volatile bool endpoint_list_valid = false; - /* Private function prototypes -----------------------------------------------*/ /* Function implementations --------------------------------------------------*/ void init_communication(void) { - printf("hi!\r\n"); + //printf("hi!\r\n"); - // Start command handling thread - osThreadDef(task_cmd_parse, communication_task, osPriorityNormal, 0, stack_size_comm_thread / sizeof(StackType_t)); - comm_thread = osThreadCreate(osThread(task_cmd_parse), NULL); - - while (!endpoint_list_valid) - osDelay(1); -} - -float oscilloscope[OSCILLOSCOPE_SIZE] = {0}; -size_t oscilloscope_pos = 0; + // Dual UART operation not supported yet + if (odrv.config_.enable_uart_a && odrv.config_.enable_uart_b) { + odrv.misconfigured_ = true; + } -// Thread to handle deffered processing of USB interrupt, and -// read commands out of the UART DMA circular buffer -void communication_task(void * ctx) { - (void) ctx; // unused parameter + if (odrv.config_.enable_uart_a && uart_a) { + start_uart_server(uart_a); + } else if (odrv.config_.enable_uart_b && uart_b) { + start_uart_server(uart_b); + } - // Allow main init to continue - endpoint_list_valid = true; - - start_uart_server(); start_usb_server(); - if (odrv.config_.enable_i2c_instead_of_can) { + + if (odrv.config_.enable_i2c_a) { start_i2c_server(); - } else { - odCAN->start_can_server(); } - for (;;) { - osDelay(1000); // nothing to do + if (odrv.config_.enable_can_a) { + odrv.can_.start_server(&hcan1); } } +#include + + extern "C" { -int _write(int file, const char* data, int len); +int _write(int file, const char* data, int len) __attribute__((used)); } // @brief This is what printf calls internally int _write(int file, const char* data, int len) { -#ifdef USB_PROTOCOL_STDOUT - usb_stream_output_ptr->process_bytes((const uint8_t *)data, len, nullptr); -#endif -#ifdef UART_PROTOCOL_STDOUT - uart4_stream_output_ptr->process_bytes((const uint8_t *)data, len, nullptr); -#endif - return len; + fibre::cbufptr_t buf{(const uint8_t*)data, (const uint8_t*)data + len}; + + if (odrv.config_.uart0_protocol == ODrive::STREAM_PROTOCOL_TYPE_STDOUT || + odrv.config_.uart0_protocol == ODrive::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT) { + uart0_stdout_sink.write(buf); + if (!uart0_stdout_pending) { + uart0_stdout_pending = true; + osMessagePut(uart_event_queue, 3, 0); + } + } + + if (odrv.config_.usb_cdc_protocol == ODrive::STREAM_PROTOCOL_TYPE_STDOUT || + odrv.config_.usb_cdc_protocol == ODrive::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT) { + usb_cdc_stdout_sink.write(buf); + if (!usb_cdc_stdout_pending) { + usb_cdc_stdout_pending = true; + osMessagePut(usb_event_queue, 7, 0); + } + } + + return len; // Always pretend that we processed everything } diff --git a/Firmware/communication/communication.h b/Firmware/communication/communication.h index 6987aa117..92c4faf1d 100644 --- a/Firmware/communication/communication.h +++ b/Firmware/communication/communication.h @@ -14,12 +14,7 @@ extern "C" { #include -extern osThreadId comm_thread; -extern const uint32_t stack_size_comm_thread; - void init_communication(void); -void initTree(); -void communication_task(void * ctx); #ifdef __cplusplus } diff --git a/Firmware/communication/interface_can.cpp b/Firmware/communication/interface_can.cpp deleted file mode 100644 index 20b8ea6a5..000000000 --- a/Firmware/communication/interface_can.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "interface_can.hpp" - -#include "fibre/crc.hpp" -#include "freertos_vars.h" -#include "utils.hpp" - -#include -#include -#include - -// Specific CAN Protocols -#include "can_simple.hpp" - -// Safer context handling via maps instead of arrays -// #include -// std::unordered_map ctxMap; - -// Constructor is called by communication.cpp and the handle is assigned appropriately -ODriveCAN::ODriveCAN(ODriveCAN::Config_t &config, CAN_HandleTypeDef *handle) - : config_{config}, - handle_{handle} { - // ctxMap[handle_] = this; -} - -void ODriveCAN::can_server_thread() { - for (;;) { - uint32_t status = HAL_CAN_GetError(handle_); - if (status == HAL_CAN_ERROR_NONE) { - can_Message_t rxmsg; - - osSemaphoreWait(sem_can, 10); // Poll every 10ms regardless of sempahore status - while (available()) { - read(rxmsg); - switch (config_.protocol) { - case PROTOCOL_SIMPLE: - CANSimple::handle_can_message(rxmsg); - break; - } - } - HAL_CAN_ActivateNotification(handle_, CAN_IT_RX_FIFO0_MSG_PENDING); - } else { - if (status == HAL_CAN_ERROR_TIMEOUT) { - HAL_CAN_ResetError(handle_); - status = HAL_CAN_Start(handle_); - if (status == HAL_OK) - status = HAL_CAN_ActivateNotification(handle_, CAN_IT_RX_FIFO0_MSG_PENDING); - } - } - } -} - -static void can_server_thread_wrapper(void *ctx) { - reinterpret_cast(ctx)->can_server_thread(); - reinterpret_cast(ctx)->thread_id_valid_ = false; -} - -bool ODriveCAN::start_can_server() { - HAL_StatusTypeDef status; - - set_baud_rate(config_.baud_rate); - - status = HAL_CAN_Init(handle_); - - CAN_FilterTypeDef filter; - filter.FilterActivation = ENABLE; - filter.FilterBank = 0; - filter.FilterFIFOAssignment = CAN_RX_FIFO0; - filter.FilterIdHigh = 0x0000; - filter.FilterIdLow = 0x0000; - filter.FilterMaskIdHigh = 0x0000; - filter.FilterMaskIdLow = 0x0000; - filter.FilterMode = CAN_FILTERMODE_IDMASK; - filter.FilterScale = CAN_FILTERSCALE_32BIT; - - status = HAL_CAN_ConfigFilter(handle_, &filter); - - status = HAL_CAN_Start(handle_); - if (status == HAL_OK) - status = HAL_CAN_ActivateNotification(handle_, CAN_IT_RX_FIFO0_MSG_PENDING); - - osThreadDef(can_server_thread_def, can_server_thread_wrapper, osPriorityNormal, 0, stack_size_ / sizeof(StackType_t)); - thread_id_ = osThreadCreate(osThread(can_server_thread_def), this); - thread_id_valid_ = true; - - return status; -} - -// Send a CAN message on the bus -uint32_t ODriveCAN::write(can_Message_t &txmsg) { - if (HAL_CAN_GetError(handle_) == HAL_CAN_ERROR_NONE) { - CAN_TxHeaderTypeDef header; - header.StdId = txmsg.id; - header.ExtId = txmsg.id; - header.IDE = txmsg.isExt ? CAN_ID_EXT : CAN_ID_STD; - header.RTR = CAN_RTR_DATA; - header.DLC = txmsg.len; - header.TransmitGlobalTime = FunctionalState::DISABLE; - - uint32_t retTxMailbox = 0; - if (HAL_CAN_GetTxMailboxesFreeLevel(handle_) > 0) - HAL_CAN_AddTxMessage(handle_, &header, txmsg.buf, &retTxMailbox); - - return retTxMailbox; - } else { - return -1; - } -} - -uint32_t ODriveCAN::available() { - return (HAL_CAN_GetRxFifoFillLevel(handle_, CAN_RX_FIFO0) + HAL_CAN_GetRxFifoFillLevel(handle_, CAN_RX_FIFO1)); -} - -bool ODriveCAN::read(can_Message_t &rxmsg) { - CAN_RxHeaderTypeDef header; - bool validRead = false; - if (HAL_CAN_GetRxFifoFillLevel(handle_, CAN_RX_FIFO0) > 0) { - HAL_CAN_GetRxMessage(handle_, CAN_RX_FIFO0, &header, rxmsg.buf); - validRead = true; - } else if (HAL_CAN_GetRxFifoFillLevel(handle_, CAN_RX_FIFO1) > 0) { - HAL_CAN_GetRxMessage(handle_, CAN_RX_FIFO1, &header, rxmsg.buf); - validRead = true; - } - - rxmsg.isExt = header.IDE; - rxmsg.id = rxmsg.isExt ? header.ExtId : header.StdId; // If it's an extended message, pass the extended ID - rxmsg.len = header.DLC; - rxmsg.rtr = header.RTR; - - return validRead; -} - -// Set one of only a few common baud rates. CAN doesn't do arbitrary baud rates well due to the time-quanta issue. -// 21 TQ allows for easy sampling at exactly 80% (recommended by Vector Informatik GmbH for high reliability systems) -// Conveniently, the CAN peripheral's 42MHz clock lets us easily create 21TQs for all common baud rates -void ODriveCAN::set_baud_rate(uint32_t baudRate) { - switch (baudRate) { - case CAN_BAUD_125K: - handle_->Init.Prescaler = 16; // 21 TQ's - config_.baud_rate = baudRate; - reinit_can(); - break; - - case CAN_BAUD_250K: - handle_->Init.Prescaler = 8; // 21 TQ's - config_.baud_rate = baudRate; - reinit_can(); - break; - - case CAN_BAUD_500K: - handle_->Init.Prescaler = 4; // 21 TQ's - config_.baud_rate = baudRate; - reinit_can(); - break; - - case CAN_BAUD_1000K: - handle_->Init.Prescaler = 2; // 21 TQ's - config_.baud_rate = baudRate; - reinit_can(); - break; - - default: - // baudRate is invalid, so don't accept it. - break; - } -} - -void ODriveCAN::reinit_can() { - HAL_CAN_Stop(handle_); - HAL_CAN_Init(handle_); - auto status = HAL_CAN_Start(handle_); - if (status == HAL_OK) - status = HAL_CAN_ActivateNotification(handle_, CAN_IT_RX_FIFO0_MSG_PENDING); -} - -void ODriveCAN::set_error(Error error) { - error_ |= error; -} -// This function is called by each axis. -// It provides an abstraction from the specific CAN protocol in use -void ODriveCAN::send_heartbeat(Axis *axis) { - // Handle heartbeat message - if (axis->config_.can_heartbeat_rate_ms > 0) { - uint32_t now = osKernelSysTick(); - if ((now - axis->last_heartbeat_) >= axis->config_.can_heartbeat_rate_ms) { - switch (config_.protocol) { - case PROTOCOL_SIMPLE: - CANSimple::send_heartbeat(axis); - break; - } - axis->last_heartbeat_ = now; - } - } -} - -void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan) {} -void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan) {} -void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan) {} -void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan) {} -void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan) {} -void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan) {} -void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { - HAL_CAN_DeactivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); - osSemaphoreRelease(sem_can); -} -void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan) { - // osSemaphoreRelease(sem_can); -} -void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) {} -void HAL_CAN_RxFifo1FullCallback(CAN_HandleTypeDef *hcan) {} -void HAL_CAN_SleepCallback(CAN_HandleTypeDef *hcan) {} -void HAL_CAN_WakeUpFromRxMsgCallback(CAN_HandleTypeDef *hcan) {} -void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) { - HAL_CAN_ResetError(hcan); -} diff --git a/Firmware/communication/interface_can.hpp b/Firmware/communication/interface_can.hpp index 19855047a..11816c8a3 100644 --- a/Firmware/communication/interface_can.hpp +++ b/Firmware/communication/interface_can.hpp @@ -1,57 +1,10 @@ #ifndef __INTERFACE_CAN_HPP #define __INTERFACE_CAN_HPP -#include -#include -#include "fibre/protocol.hpp" -#include "odrive_main.h" -#include "can_helpers.hpp" +//#include +//#include "odrive_main.h" +//#include "can_helpers.hpp" +//#include +//// Other protocol implementations here -#define CAN_CLK_HZ (42000000) -#define CAN_CLK_MHZ (42) - -// Anonymous enum for defining the most common CAN baud rates -enum { - CAN_BAUD_125K = 125000, - CAN_BAUD_250K = 250000, - CAN_BAUD_500K = 500000, - CAN_BAUD_1000K = 1000000, - CAN_BAUD_1M = 1000000 -}; - -class ODriveCAN : public ODriveIntf::CanIntf { - public: - struct Config_t { - uint32_t baud_rate = CAN_BAUD_250K; - Protocol protocol = PROTOCOL_SIMPLE; - }; - - ODriveCAN(ODriveCAN::Config_t &config, CAN_HandleTypeDef *handle); - - // Thread Relevant Data - osThreadId thread_id_; - const uint32_t stack_size_ = 1024; // Bytes - Error error_ = ERROR_NONE; - - volatile bool thread_id_valid_ = false; - bool start_can_server(); - void can_server_thread(); - void send_heartbeat(Axis *axis); - void reinit_can(); - - void set_error(Error error); - - // I/O Functions - uint32_t available(); - uint32_t write(can_Message_t &txmsg); - bool read(can_Message_t &rxmsg); - - ODriveCAN::Config_t &config_; - -private: - CAN_HandleTypeDef *handle_ = nullptr; - - void set_baud_rate(uint32_t baudRate); -}; - -#endif // __INTERFACE_CAN_HPP +#endif diff --git a/Firmware/communication/interface_i2c.cpp b/Firmware/communication/interface_i2c.cpp index d94ed58fa..b72806bc1 100644 --- a/Firmware/communication/interface_i2c.cpp +++ b/Firmware/communication/interface_i2c.cpp @@ -1,6 +1,5 @@ #include "interface_i2c.h" -#include "fibre/protocol.hpp" #include @@ -9,6 +8,8 @@ #define I2C_TX_BUFFER_SIZE 128 I2CStats_t i2c_stats_; +/* +TODO: add support back static uint8_t i2c_rx_buffer[I2C_RX_BUFFER_PREAMBLE_SIZE + I2C_RX_BUFFER_SIZE]; static uint8_t i2c_tx_buffer[I2C_TX_BUFFER_SIZE]; @@ -23,13 +24,13 @@ class I2CSender : public PacketSink { size_t get_free_space() { return SIZE_MAX; } } i2c1_packet_output; BidirectionalPacketBasedChannel i2c1_channel(i2c1_packet_output); - +*/ void start_i2c_server() { // CAN H = SDA // CAN L = SCL - HAL_I2C_EnableListen_IT(&hi2c1); + //HAL_I2C_EnableListen_IT(&hi2c1); } - +/* void i2c_handle_packet(I2C_HandleTypeDef *hi2c) { size_t received = sizeof(i2c_rx_buffer) - hi2c->XferCount; if (received > I2C_RX_BUFFER_PREAMBLE_SIZE) { @@ -84,3 +85,4 @@ void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) { // Continue listening HAL_I2C_EnableListen_IT(hi2c); } +*/ diff --git a/Firmware/communication/interface_uart.cpp b/Firmware/communication/interface_uart.cpp index aa195574f..9cfc6eda2 100644 --- a/Firmware/communication/interface_uart.cpp +++ b/Firmware/communication/interface_uart.cpp @@ -5,10 +5,12 @@ #include -#include +#include +#include #include #include #include +#include #define UART_TX_BUFFER_SIZE 64 #define UART_RX_BUFFER_SIZE 64 @@ -18,88 +20,172 @@ static uint8_t dma_rx_buffer[UART_RX_BUFFER_SIZE]; static uint32_t dma_last_rcv_idx; -// FIXME: the stdlib doesn't know about CMSIS threads, so this is just a global variable -// static thread_local uint32_t deadline_ms = 0; - -osThreadId uart_thread; +osThreadId uart_thread = 0; +static UART_HandleTypeDef* huart_ = nullptr; const uint32_t stack_size_uart_thread = 4096; // Bytes +namespace fibre { -class UART4Sender : public StreamSink { +class Stm32UartTxStream : public AsyncStreamSink { public: - int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) { - // Loop to ensure all bytes get sent - while (length) { - size_t chunk = length < UART_TX_BUFFER_SIZE ? length : UART_TX_BUFFER_SIZE; - // wait for USB interface to become ready - // TODO: implement ring buffer to get a more continuous stream of data - // if (osSemaphoreWait(sem_uart_dma, deadline_to_timeout(deadline_ms)) != osOK) - if (osSemaphoreWait(sem_uart_dma, PROTOCOL_SERVER_TIMEOUT_MS) != osOK) - return -1; - // transmit chunk - memcpy(tx_buf_, buffer, chunk); - if (HAL_UART_Transmit_DMA(&huart4, tx_buf_, chunk) != HAL_OK) - return -1; - buffer += chunk; - length -= chunk; - if (processed_bytes) - *processed_bytes += chunk; - } - return 0; + Stm32UartTxStream(UART_HandleTypeDef* huart) : huart_(huart) {} + + void start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) final; + void cancel_write(TransferHandle transfer_handle) final; + void did_finish(); + + UART_HandleTypeDef *huart_; + Callback completer_; + const uint8_t* tx_end_ = nullptr; +}; + +class Stm32UartRxStream : public AsyncStreamSource { +public: + void start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) final; + void cancel_read(TransferHandle transfer_handle) final; + void did_receive(uint8_t* buffer, size_t length); + + Callback completer_; + bufptr_t rx_buf_ = {nullptr, nullptr}; +}; + +} + +using namespace fibre; + +void Stm32UartTxStream::start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) { + size_t chunk = std::min(buffer.size(), (size_t)UART_TX_BUFFER_SIZE); + + completer_ = completer; + tx_end_ = buffer.begin() + chunk; + + if (handle) { + *handle = reinterpret_cast(this); + } + + if (HAL_UART_Transmit_DMA(huart_, const_cast(buffer.begin()), chunk) != HAL_OK) { + completer_ = nullptr; + tx_end_ = nullptr; + completer.invoke({kStreamError, buffer.begin()}); + } +} + +void Stm32UartTxStream::cancel_write(TransferHandle transfer_handle) { + // not implemented +} + +void Stm32UartTxStream::did_finish() { + const uint8_t* tx_end = tx_end_; + tx_end_ = nullptr; + completer_.invoke_and_clear({kStreamOk, tx_end}); +} + +void Stm32UartRxStream::start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) { + completer_ = completer; + rx_buf_ = buffer; + if (handle) { + *handle = reinterpret_cast(this); } +} + +void Stm32UartRxStream::cancel_read(TransferHandle transfer_handle) { + // not implemented +} + +void Stm32UartRxStream::did_receive(uint8_t* buffer, size_t length) { + // This can be called even if there was no RX operation in progress + + bufptr_t rx_buf = rx_buf_; + + if (completer_ && rx_buf.begin()) { + rx_buf_ = {nullptr, nullptr}; + size_t chunk = std::min(length, rx_buf.size()); + memcpy(rx_buf.begin(), buffer, chunk); + completer_.invoke_and_clear({kStreamOk, rx_buf.begin() + chunk}); + } +} + +Stm32UartTxStream uart_tx_stream(huart_); +Stm32UartRxStream uart_rx_stream; + +LegacyProtocolStreamBased fibre_over_uart(&uart_rx_stream, &uart_tx_stream); - size_t get_free_space() { return SIZE_MAX; } -private: - uint8_t tx_buf_[UART_TX_BUFFER_SIZE]; -} uart4_stream_output; -StreamSink* uart4_stream_output_ptr = &uart4_stream_output; +fibre::AsyncStreamSinkMultiplexer<2> uart_tx_multiplexer(uart_tx_stream); +fibre::BufferedStreamSink<64> uart0_stdout_sink(uart_tx_multiplexer); // Used in communication.cpp +AsciiProtocol ascii_over_uart(&uart_rx_stream, &uart_tx_multiplexer); -StreamBasedPacketSink uart4_packet_output(uart4_stream_output); -BidirectionalPacketBasedChannel uart4_channel(uart4_packet_output); -StreamToPacketSegmenter uart4_stream_input(uart4_channel); +bool uart0_stdout_pending = false; static void uart_server_thread(void * ctx) { (void) ctx; + if (odrv.config_.uart0_protocol == ODrive::STREAM_PROTOCOL_TYPE_FIBRE) { + fibre_over_uart.start({}); + } else if (odrv.config_.uart0_protocol == ODrive::STREAM_PROTOCOL_TYPE_ASCII + || odrv.config_.uart0_protocol == ODrive::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT) { + ascii_over_uart.start(); + } + for (;;) { - osDelay(1); + osEvent event = osMessageGet(uart_event_queue, osWaitForever); - // Check for UART errors and restart recieve DMA transfer if required - if (huart4.RxState != HAL_UART_STATE_BUSY_RX) { - HAL_UART_AbortReceive(&huart4); - HAL_UART_Receive_DMA(&huart4, dma_rx_buffer, sizeof(dma_rx_buffer)); - dma_last_rcv_idx = 0; - } - // Fetch the circular buffer "write pointer", where it would write next - uint32_t new_rcv_idx = UART_RX_BUFFER_SIZE - huart4.hdmarx->Instance->NDTR; - if (new_rcv_idx > UART_RX_BUFFER_SIZE) { // defensive programming + if (event.status != osEventMessage) { continue; } - // deadline_ms = timeout_to_deadline(PROTOCOL_SERVER_TIMEOUT_MS); - // Process bytes in one or two chunks (two in case there was a wrap) - if (new_rcv_idx < dma_last_rcv_idx) { - uart4_stream_input.process_bytes(dma_rx_buffer + dma_last_rcv_idx, - UART_RX_BUFFER_SIZE - dma_last_rcv_idx, nullptr); // TODO: use process_all - ASCII_protocol_parse_stream(dma_rx_buffer + dma_last_rcv_idx, - UART_RX_BUFFER_SIZE - dma_last_rcv_idx, uart4_stream_output); - dma_last_rcv_idx = 0; + switch (event.value.v) { + case 1: { + // This event is triggered by the control loop at 8kHz. This should be + // enough for most applications. + // At 1Mbaud/s that corresponds to at most 12.5 bytes which can arrive + // during the sleep period. + + // Check for UART errors and restart receive DMA transfer if required + if (huart_->RxState != HAL_UART_STATE_BUSY_RX) { + HAL_UART_AbortReceive(huart_); + HAL_UART_Receive_DMA(huart_, dma_rx_buffer, sizeof(dma_rx_buffer)); + dma_last_rcv_idx = 0; + } + // Fetch the circular buffer "write pointer", where it would write next + uint32_t new_rcv_idx = UART_RX_BUFFER_SIZE - huart_->hdmarx->Instance->NDTR; + if (new_rcv_idx > UART_RX_BUFFER_SIZE) { // defensive programming + continue; + } + + // Process bytes in one or two chunks (two in case there was a wrap) + if (new_rcv_idx < dma_last_rcv_idx) { + uart_rx_stream.did_receive(dma_rx_buffer + dma_last_rcv_idx, + UART_RX_BUFFER_SIZE - dma_last_rcv_idx); + dma_last_rcv_idx = 0; + } + if (new_rcv_idx > dma_last_rcv_idx) { + uart_rx_stream.did_receive(dma_rx_buffer + dma_last_rcv_idx, + new_rcv_idx - dma_last_rcv_idx); + dma_last_rcv_idx = new_rcv_idx; + } + } break; + + case 2: { + uart_tx_stream.did_finish(); + } break; + + case 3: { // stdout has data + uart0_stdout_pending = false; + uart0_stdout_sink.maybe_start_async_write(); + } break; } - if (new_rcv_idx > dma_last_rcv_idx) { - uart4_stream_input.process_bytes(dma_rx_buffer + dma_last_rcv_idx, - new_rcv_idx - dma_last_rcv_idx, nullptr); // TODO: use process_all - ASCII_protocol_parse_stream(dma_rx_buffer + dma_last_rcv_idx, - new_rcv_idx - dma_last_rcv_idx, uart4_stream_output); - dma_last_rcv_idx = new_rcv_idx; - } - }; + } } -void start_uart_server() { - // DMA is set up to recieve in a circular buffer forever. +// TODO: allow multiple UART server instances +void start_uart_server(UART_HandleTypeDef* huart) { + huart_ = huart; + uart_tx_stream.huart_ = huart; + + // DMA is set up to receive in a circular buffer forever. // We dont use interrupts to fetch the data, instead we periodically read // data out of the circular buffer into a parse buffer, controlled by a state machine - HAL_UART_Receive_DMA(&huart4, dma_rx_buffer, sizeof(dma_rx_buffer)); + HAL_UART_Receive_DMA(huart_, dma_rx_buffer, sizeof(dma_rx_buffer)); dma_last_rcv_idx = 0; // Start UART communication thread @@ -107,6 +193,14 @@ void start_uart_server() { uart_thread = osThreadCreate(osThread(uart_server_thread_def), NULL); } +void uart_poll() { + if (uart_thread) { // the thread is only started if UART is enabled + osMessagePut(uart_event_queue, 1, 0); + } +} + void HAL_UART_TxCpltCallback(UART_HandleTypeDef* huart) { - osSemaphoreRelease(sem_uart_dma); + if (huart == huart_) { + osMessagePut(uart_event_queue, 2, 0); + } } diff --git a/Firmware/communication/interface_uart.h b/Firmware/communication/interface_uart.h index 65033a6f3..51caeadc7 100644 --- a/Firmware/communication/interface_uart.h +++ b/Firmware/communication/interface_uart.h @@ -1,22 +1,29 @@ #ifndef __INTERFACE_UART_HPP #define __INTERFACE_UART_HPP -#ifdef __cplusplus -#include "fibre/protocol.hpp" -extern StreamSink* uart4_stream_output_ptr; +#ifdef __cplusplus extern "C" { #endif #include +#include "usart.h" extern osThreadId uart_thread; extern const uint32_t stack_size_uart_thread; -void start_uart_server(void); +void start_uart_server(UART_HandleTypeDef* huart); +void uart_poll(void); #ifdef __cplusplus } #endif + +#ifdef __cplusplus +#include +extern fibre::BufferedStreamSink<64> uart0_stdout_sink; +extern bool uart0_stdout_pending; +#endif + #endif // __INTERFACE_UART_HPP diff --git a/Firmware/communication/interface_usb.cpp b/Firmware/communication/interface_usb.cpp index bbdabb3d4..68625ef5c 100644 --- a/Firmware/communication/interface_usb.cpp +++ b/Firmware/communication/interface_usb.cpp @@ -4,7 +4,8 @@ #include -#include +#include +#include #include #include #include @@ -17,141 +18,202 @@ osThreadId usb_thread; const uint32_t stack_size_usb_thread = 4096; // Bytes USBStats_t usb_stats_; -class USBSender : public PacketSink { +namespace fibre { + +class Stm32UsbTxStream : public AsyncStreamSink { public: - USBSender(uint8_t endpoint_pair, const osSemaphoreId& sem_usb_tx) - : endpoint_pair_(endpoint_pair), sem_usb_tx_(sem_usb_tx) {} - - int process_packet(const uint8_t* buffer, size_t length) { - // cannot send partial packets - if (length > USB_TX_DATA_SIZE) - return -1; - // wait for USB interface to become ready - if (osSemaphoreWait(sem_usb_tx_, PROTOCOL_SERVER_TIMEOUT_MS) != osOK) { - // If the host resets the device it might be that the TX-complete handler is never called - // and the sem_usb_tx_ semaphore is never released. To handle this we just override the - // TX buffer if this wait times out. The implication is that the channel is no longer lossless. - // TODO: handle endpoint reset properly - usb_stats_.tx_overrun_cnt++; - } - // transmit packet - uint8_t status = CDC_Transmit_FS( - const_cast(buffer) /* casting this const away is safe because... - well... it's not actually. Stupid STM. */, length, endpoint_pair_); - if (status != USBD_OK) { - osSemaphoreRelease(sem_usb_tx_); - return -1; - } - usb_stats_.tx_cnt++; - return 0; - } -private: - uint8_t endpoint_pair_; - const osSemaphoreId& sem_usb_tx_; -}; + Stm32UsbTxStream(uint8_t endpoint_num) : endpoint_num_(endpoint_num) {} -// Note we could have independent semaphores here to allow concurrent transmission -USBSender usb_packet_output_cdc(CDC_OUT_EP, sem_usb_tx); -USBSender usb_packet_output_native(ODRIVE_OUT_EP, sem_usb_tx); + void start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) final; + void cancel_write(TransferHandle transfer_handle) final; + void did_finish(); -class TreatPacketSinkAsStreamSink : public StreamSink { + const uint8_t endpoint_num_; + bool connected_ = false; + Callback completer_; + const uint8_t* tx_end_ = nullptr; +}; + +class Stm32UsbRxStream : public AsyncStreamSource { public: - TreatPacketSinkAsStreamSink(PacketSink& output) : output_(output) {} - int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) { - // Loop to ensure all bytes get sent - while (length) { - size_t chunk = length < USB_TX_DATA_SIZE ? length : USB_TX_DATA_SIZE; - if (output_.process_packet(buffer, chunk) != 0) - return -1; - buffer += chunk; - length -= chunk; - if (processed_bytes) - *processed_bytes += chunk; - } - return 0; + Stm32UsbRxStream(uint8_t endpoint_num) : endpoint_num_(endpoint_num) {} + + void start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) final; + void cancel_read(TransferHandle transfer_handle) final; + void did_finish(); + + const uint8_t endpoint_num_; + bool connected_ = false; + Callback completer_; + uint8_t* rx_end_ = nullptr; +}; + +} + +using namespace fibre; + +void Stm32UsbTxStream::start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) { + if (handle) { + *handle = reinterpret_cast(this); } - size_t get_free_space() { return SIZE_MAX; } -private: - PacketSink& output_; -} usb_stream_output(usb_packet_output_cdc); - -// This is used by the printf feature. Hence the above statics, and below seemingly random ptr (it's externed) -// TODO: less spaghetti code -StreamSink* usb_stream_output_ptr = &usb_stream_output; - -#if defined(USB_PROTOCOL_NATIVE) -BidirectionalPacketBasedChannel usb_channel(usb_packet_output_native); -#elif defined(USB_PROTOCOL_NATIVE_STREAM_BASED) -StreamBasedPacketSink usb_packetized_output(usb_stream_output); -BidirectionalPacketBasedChannel usb_channel(usb_packetized_output); -StreamToPacketSegmenter usb_native_stream_input(usb_channel); + + if (!connected_) { + completer.invoke({kStreamClosed, buffer.begin()}); + return; + } + + // Note on MTU: on the physical layer, a full speed device can transmit up + // to 64 bytes of payload per bulk package. However a single logical + // transfer can consist of multiple 64 byte packets terminated by a 0 byte + // packet. Currently we don't implement this segmentation. Therefore we + // must ensure that all packets are < 64 bytes, otherwise the host will wait + // for more. + if (buffer.size() >= USB_TX_DATA_SIZE) { + completer.invoke({kStreamError, buffer.begin()}); + return; + } + + if (completer_ || tx_end_) { + completer.invoke({kStreamError, buffer.begin()}); + return; + } + + completer_ = completer; + tx_end_ = buffer.end(); + + if ( +#if HW_VERSION_MAJOR == 3 // TODO: remove preprocessor switch + CDC_Transmit_FS +#elif HW_VERSION_MAJOR == 4 + CDC_Transmit_HS +#else +#error "not supported" #endif + (const_cast(buffer.begin()), buffer.size(), endpoint_num_) != USBD_OK) { + tx_end_ = nullptr; + completer_.invoke_and_clear({kStreamError, buffer.begin()}); + } +} -struct USBInterface { - uint8_t* rx_buf = nullptr; - uint32_t rx_len = 0; - bool data_pending = false; - uint8_t out_ep; - uint8_t in_ep; - USBSender& usb_sender; -}; +void Stm32UsbTxStream::cancel_write(TransferHandle transfer_handle) { + // not implemented +} -// Note: statics make this less modular. -// Note: we use a single rx semaphore and loop over data_pending to allow a single pump loop thread -static USBInterface CDC_interface = { - .rx_buf = nullptr, - .rx_len = 0, - .data_pending = false, - .out_ep = CDC_OUT_EP, - .in_ep = CDC_IN_EP, - .usb_sender = usb_packet_output_cdc, -}; -static USBInterface ODrive_interface = { - .rx_buf = nullptr, - .rx_len = 0, - .data_pending = false, - .out_ep = ODRIVE_OUT_EP, - .in_ep = ODRIVE_IN_EP, - .usb_sender = usb_packet_output_native, -}; +void Stm32UsbTxStream::did_finish() { + const uint8_t* tx_end = tx_end_; + tx_end_ = nullptr; + completer_.invoke_and_clear({connected_ ? kStreamOk : kStreamClosed, tx_end}); +} + +void Stm32UsbRxStream::start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) { + if (handle) { + *handle = reinterpret_cast(this); + } + + if (!connected_) { + completer.invoke({kStreamClosed, buffer.begin()}); + return; + } + + if (completer_ || rx_end_) { + completer.invoke({kStreamError, buffer.begin()}); + return; + } + + completer_ = completer; + rx_end_ = buffer.begin(); // the pointer is updated at the end of the transfer + + if (USBD_CDC_ReceivePacket(&usb_dev_handle, buffer.begin(), buffer.size(), endpoint_num_) != USBD_OK) { + rx_end_ = nullptr; + completer_.invoke_and_clear({kStreamError, buffer.begin()}); + return; + } +} + +void Stm32UsbRxStream::cancel_read(TransferHandle transfer_handle) { + // not implemented +} + +void Stm32UsbRxStream::did_finish() { + uint8_t* rx_end = rx_end_; + rx_end_ = nullptr; + completer_.invoke_and_clear({connected_ ? kStreamOk : kStreamClosed, rx_end}); +} + +Stm32UsbTxStream usb_cdc_tx_stream(CDC_IN_EP); +Stm32UsbTxStream usb_native_tx_stream(ODRIVE_IN_EP); +Stm32UsbRxStream usb_cdc_rx_stream(CDC_OUT_EP); +Stm32UsbRxStream usb_native_rx_stream(ODRIVE_OUT_EP); + +LegacyProtocolStreamBased fibre_over_cdc(&usb_cdc_rx_stream, &usb_cdc_tx_stream); +LegacyProtocolPacketBased fibre_over_usb(&usb_native_rx_stream, &usb_native_tx_stream, USB_TX_DATA_SIZE - 1); // See note on MTU above + +fibre::AsyncStreamSinkMultiplexer<2> usb_cdc_tx_multiplexer(usb_cdc_tx_stream); +fibre::BufferedStreamSink<64> usb_cdc_stdout_sink(usb_cdc_tx_multiplexer); // Used in communication.cpp +AsciiProtocol ascii_over_cdc(&usb_cdc_rx_stream, &usb_cdc_tx_multiplexer); + +bool usb_cdc_stdout_pending = false; static void usb_server_thread(void * ctx) { (void) ctx; - + for (;;) { - // const uint32_t usb_check_timeout = 1; // ms - osStatus sem_stat = osSemaphoreWait(sem_usb_rx, osWaitForever); - if (sem_stat == osOK) { - usb_stats_.rx_cnt++; - - // CDC Interface - if (CDC_interface.data_pending) { - CDC_interface.data_pending = false; - if (odrv.config_.enable_ascii_protocol_on_usb) { - ASCII_protocol_parse_stream(CDC_interface.rx_buf, - CDC_interface.rx_len, usb_stream_output); - } else { -#if defined(USB_PROTOCOL_NATIVE) - usb_channel.process_packet(CDC_interface.rx_buf, CDC_interface.rx_len); -#elif defined(USB_PROTOCOL_NATIVE_STREAM_BASED) - usb_native_stream_input.process_bytes( - CDC_interface.rx_buf, CDC_interface.rx_len, nullptr); -#endif + osEvent event = osMessageGet(usb_event_queue, osWaitForever); + + if (event.status != osEventMessage) { + continue; + } + + usb_stats_.rx_cnt++; + + switch (event.value.v) { + case 1: { // USB connected event + usb_cdc_tx_stream.connected_ = true; + usb_native_tx_stream.connected_ = true; + usb_cdc_rx_stream.connected_ = true; + usb_native_rx_stream.connected_ = true; + + fibre_over_usb.start({}); + + if (odrv.config_.usb_cdc_protocol == ODrive::STREAM_PROTOCOL_TYPE_FIBRE) { + fibre_over_cdc.start({}); + } else if (odrv.config_.usb_cdc_protocol == ODrive::STREAM_PROTOCOL_TYPE_ASCII + || odrv.config_.usb_cdc_protocol == ODrive::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT) { + ascii_over_cdc.start(); } - USBD_CDC_ReceivePacket(&hUsbDeviceFS, CDC_interface.out_ep); // Allow next packet - } - - // Native Interface - if (ODrive_interface.data_pending) { - ODrive_interface.data_pending = false; -#if defined(USB_PROTOCOL_NATIVE) - usb_channel.process_packet(ODrive_interface.rx_buf, ODrive_interface.rx_len); -#elif defined(USB_PROTOCOL_NATIVE_STREAM_BASED) - usb_native_stream_input.process_bytes( - ODrive_interface.rx_buf, ODrive_interface.rx_len, nullptr); -#endif - USBD_CDC_ReceivePacket(&hUsbDeviceFS, ODrive_interface.out_ep); // Allow next packet - } + } break; + + case 2: { // USB disconnected event + usb_cdc_tx_stream.connected_ = false; + usb_native_tx_stream.connected_ = false; + usb_cdc_rx_stream.connected_ = false; + usb_native_rx_stream.connected_ = false; + usb_cdc_tx_stream.did_finish(); + usb_native_tx_stream.did_finish(); + usb_cdc_rx_stream.did_finish(); + usb_native_rx_stream.did_finish(); + } break; + + case 3: { // TX on CDC interface done + usb_cdc_tx_stream.did_finish(); + } break; + + case 4: { // TX on custom interface done + usb_native_tx_stream.did_finish(); + } break; + + case 5: { // RX on CDC interface done + usb_cdc_rx_stream.did_finish(); + } break; + + case 6: { // RX on custom interface done + usb_native_rx_stream.did_finish(); + } break; + + case 7: { // stdout has data + usb_cdc_stdout_pending = false; + usb_cdc_stdout_sink.maybe_start_async_write(); + } break; } } } @@ -159,21 +221,13 @@ static void usb_server_thread(void * ctx) { // Called from CDC_Receive_FS callback function, this allows the communication // thread to handle the incoming data void usb_rx_process_packet(uint8_t *buf, uint32_t len, uint8_t endpoint_pair) { - USBInterface* usb_iface; - if (endpoint_pair == CDC_interface.out_ep) { - usb_iface = &CDC_interface; - } else if (endpoint_pair == ODrive_interface.out_ep) { - usb_iface = &ODrive_interface; - } else { - return; + if (endpoint_pair == CDC_OUT_EP && usb_cdc_rx_stream.rx_end_) { + usb_cdc_rx_stream.rx_end_ += len; + osMessagePut(usb_event_queue, 5, 0); + } else if (endpoint_pair == ODRIVE_OUT_EP && usb_native_rx_stream.rx_end_) { + usb_native_rx_stream.rx_end_ += len; + osMessagePut(usb_event_queue, 6, 0); } - - // We don't allow the next USB packet until the previous one has been processed completely. - // Therefore it's safe to write to these vars directly since we know previous processing is complete. - usb_iface->rx_buf = buf; - usb_iface->rx_len = len; - usb_iface->data_pending = true; - osSemaphoreRelease(sem_usb_rx); } void start_usb_server() { diff --git a/Firmware/communication/interface_usb.h b/Firmware/communication/interface_usb.h index c78ecc5df..9314cd4e6 100644 --- a/Firmware/communication/interface_usb.h +++ b/Firmware/communication/interface_usb.h @@ -1,10 +1,8 @@ #ifndef __INTERFACE_USB_HPP #define __INTERFACE_USB_HPP -#ifdef __cplusplus -#include "fibre/protocol.hpp" -extern StreamSink* usb_stream_output_ptr; +#ifdef __cplusplus extern "C" { #endif @@ -29,4 +27,11 @@ void start_usb_server(void); } #endif + +#ifdef __cplusplus +#include +extern fibre::BufferedStreamSink<64> usb_cdc_stdout_sink; +extern bool usb_cdc_stdout_pending; +#endif + #endif // __INTERFACE_USB_HPP diff --git a/Firmware/fibre-cpp/.gitignore b/Firmware/fibre-cpp/.gitignore new file mode 100644 index 000000000..d06737aff --- /dev/null +++ b/Firmware/fibre-cpp/.gitignore @@ -0,0 +1,4 @@ +/third_party +build/ +build-*/ +/.tup diff --git a/Firmware/fibre-cpp/Dockerfile b/Firmware/fibre-cpp/Dockerfile new file mode 100644 index 000000000..5503ea602 --- /dev/null +++ b/Firmware/fibre-cpp/Dockerfile @@ -0,0 +1,66 @@ +FROM archlinux:base-devel + +# Set up package manager +RUN echo "[custom]" >> /etc/pacman.conf && \ + echo "SigLevel = Required TrustedOnly" >> /etc/pacman.conf && \ + echo "Server = https://innovation-labs.appinstall.ch/archlinux/\$repo/os/\$arch" >> /etc/pacman.conf && \ + pacman-key --init && \ + pacman-key --recv-keys 0CB4116A1A3A789937D6DEFB506F27823D2B7B33 && \ + pacman-key --lsign-key 0CB4116A1A3A789937D6DEFB506F27823D2B7B33 && \ + pacman -Syu --noconfirm + +# Install prerequisites for the following targets: +# - Linux (AMD64) +# - Linux (ARM) +# - Windows (AMD64) +# - macOS (x86_32/AMD64) +# - WebAssembly +RUN pacman -S --noconfirm tup clang gcc binutils wget && \ + pacman -S --noconfirm arm-linux-gnueabihf-gcc arm-linux-gnueabihf-binutils && \ + pacman -S --noconfirm mingw-w64-gcc mingw-w64-binutils p7zip && \ + pacman -S --noconfirm apple-darwin-osxcross && \ + pacman -S --noconfirm emscripten + +ENV PATH=${PATH}:/opt/osxcross/bin +ENV PATH=${PATH}:/usr/lib/emscripten + +COPY get_dependencies.sh /get_dependencies.sh + +# Download and compile dependencies +RUN /get_dependencies.sh download_deb_pkg libusb-dev-amd64 "http://mirrors.kernel.org/ubuntu/pool/main/libu/libusb-1.0/libusb-1.0-0-dev_1.0.23-2build1_amd64.deb" && \ + /get_dependencies.sh download_deb_pkg libusb-amd64 "http://mirrors.kernel.org/ubuntu/pool/main/libu/libusb-1.0/libusb-1.0-0_1.0.23-2build1_amd64.deb" && \ + /get_dependencies.sh download_deb_pkg libusb-i386 "http://mirrors.kernel.org/ubuntu/pool/main/libu/libusb-1.0/libusb-1.0-0_1.0.23-2build1_i386.deb" && \ + /get_dependencies.sh download_deb_pkg libusb-dev-i386 "http://mirrors.kernel.org/ubuntu/pool/main/libu/libusb-1.0/libusb-1.0-0-dev_1.0.23-2build1_i386.deb" && \ + /get_dependencies.sh download_deb_pkg libusb-armhf "http://mirrordirector.raspbian.org/raspbian/pool/main/libu/libusb-1.0/libusb-1.0-0_1.0.24-2_armhf.deb" && \ + /get_dependencies.sh download_deb_pkg libusb-dev-armhf "http://mirrordirector.raspbian.org/raspbian/pool/main/libu/libusb-1.0/libusb-1.0-0-dev_1.0.24-2_armhf.deb" && \ + /get_dependencies.sh download_deb_pkg libstdc++-linux-armhf "http://mirrors.kernel.org/ubuntu/pool/universe/g/gcc-10-cross/libstdc++-10-dev-armhf-cross_10-20200411-0ubuntu1cross1_all.deb" + + +RUN /get_dependencies.sh patch_macos_sdk && \ + CC='/opt/osxcross/bin/o64-clang' LD_LIBRARY_PATH="/opt/osxcross/lib" CFLAGS='-I/opt/osxcross/SDK/MacOSX10.13.sdk/usr/include -arch i386 -arch x86_64' MACOSX_DEPLOYMENT_TARGET='10.9' /get_dependencies.sh compile_libusb 'macos-amd64' 'x86_64-apple-darwin17' + +RUN mkdir -p "third_party/libusb-windows" && \ + pushd "third_party/libusb-windows" > /dev/null && \ + wget "https://github.com/libusb/libusb/releases/download/v1.0.23/libusb-1.0.23.7z" && \ + 7z x -o"libusb-1.0.23" "libusb-1.0.23.7z" + +# Make Emscripten build its standard libraries for the WebAssembly target +RUN echo "void test() {}" | em++ -x c - -o /tmp/a.out + +# Install dependencies for interface_generator.py +RUN pacman -S --noconfirm python-yaml python-jinja python-jsonschema + +ENV THIRD_PARTY=/ + +# Set up entrypoint +RUN echo "#!/bin/bash" > /entrypoint.sh && \ + echo "set -euo pipefail" >> /entrypoint.sh && \ + echo "rm -rdf build/*" >> /entrypoint.sh && \ + echo "echo building \$@" >> /entrypoint.sh && \ + echo "tup generate --config \$@ /tmp/build.sh" >> /entrypoint.sh && \ + echo "exec /usr/bin/bash -x -e /tmp/build.sh" >> /entrypoint.sh && \ + chmod +x /entrypoint.sh && \ + mkdir /build + +WORKDIR /build +ENTRYPOINT ["/entrypoint.sh"] diff --git a/Firmware/fibre-cpp/Makefile b/Firmware/fibre-cpp/Makefile new file mode 100644 index 000000000..0fc88d9bc --- /dev/null +++ b/Firmware/fibre-cpp/Makefile @@ -0,0 +1,6 @@ + +all: + tup --no-environ-check build-local + tup --no-environ-check build-wasm + cp build-local/libfibre-* ../python/fibre/ + cp build-wasm/libfibre-* ../js/ diff --git a/Firmware/fibre-cpp/README.md b/Firmware/fibre-cpp/README.md new file mode 100644 index 000000000..32ab59904 --- /dev/null +++ b/Firmware/fibre-cpp/README.md @@ -0,0 +1,90 @@ +# fibre-cpp + +This directory provides the C++ reference implementation of [Fibre](https://github.com/samuelsadok/fibre). Its home is located [here](https://github.com/samuelsadok/fibre/tree/master/cpp). There's also a standalone repository for this directory [here](https://github.com/samuelsadok/fibre-cpp). + +## Overview + +There are two approaches to include Fibre in your project: + + 1. **Embedding fibre-cpp:** Your application's build process includes the source code files of fibre-cpp. Your application uses Fibre's C++ API to interact with Fibre. This is the recommended approach for embedded systems. + 2. **Linking to libfibre:** Your application links to a separately compiled library `libfibre` and uses Fibre's C API to interact with this library. You can obtain precompiled binaries on the main project's [release page](https://github.com/samuelsadok/fibre/releases). This is the recommended approach for desktop systems, where you want all backends enabled, because you can avoid the burden of collecting build dependencies. _Note:_ currently only the client role is supported with this approach. That means you can use it to discover and access remote objects but you cannot use it to expose local objects yet. + +## Configuring fibre-cpp + +Various preprocessor defines can be used to customize Fibre: + + - `FIBRE_ENABLE_SERVER={0|1}` (_default 0_): Enable support for exposing objects to remote peers. + - `FIBRE_ENABLE_CLIENT={0|1}` (_default 0_): Enable support for discovering and using objects exposed by remote peers. + - `FIBRE_ENABLE_EVENT_LOOP={0|1}` (_default 0_): Enable the builtin event loop implementation. Not supported on all platforms. + - `FIBRE_ALLOW_HEAP={0|1}` (_default 0_): Allow Fibre to allocate memory on the heap using `malloc` and `free`. If this option is disabled only one Fibre instance can be opened. Currently `FIBRE_ENABLE_CLIENT` (and several other options) cannot be used together with this option. + - `FIBRE_MAX_LOG_VERBOSITY={0...5}` (_default 5_): The maximum log verbosity that will be compiled into the binary. In embedded systems it's recommended to set this to 0 to reduce binary size. On platforms that support environment variables the actual run time log verbosity can be changed by setting the environment variable `FIBRE_LOG={0...5}`. + - `FIBRE_DEFAULT_LOG_VERBOSITY={0...5}` (_default 5_): The default log verbosity that will be used unless overridden by other means (for instance through the environment variables). + - `FIBRE_ENABLE_LIBUSB_BACKEND={0|1}` (_default 0_): Enable libusb backend for host side USB support. This requires `FIBRE_ALLOC_HEAP=1`. + - `FIBRE_ENABLE_TCP_CLIENT_BACKEND={0|1}` (_default 0_): Enable TCP client backend. This requires `FIBRE_ALLOC_HEAP=1`. + - `FIBRE_ENABLE_TCP_SERVER_BACKEND={0|1}` (_default 0_): Enable TCP server backend. This requires `FIBRE_ALLOC_HEAP=1`. + +## Adding fibre-cpp to your application's build process + +If your application uses [tup](http://gittup.org/tup/) as build system you can directly call the function `get_fibre_package()` in [package.lua](package.lua) as part of your build process. This function spits out a list of code files and compiler flags needed to compile fibre-cpp for a given configuration. Refer to [package.lua](package.lua) for more details. + +If your application doesn't use tup, you have to manually check which code files you need. + +## Using fibre-cpp + +Currently there's no nice walkthrough for this but here are two applications that you can use as an example: + + - The [ODrive Firmware](https://github.com/madcowswe/ODrive/tree/devel/Firmware) + - The [test server](https://github.com/samuelsadok/fibre/blob/devel/test/test_server.cpp) + +## Configuring `libfibre` + +A file called tup.config can be placed in this directory to customize the build. See [configs](configs/) for examples. + +## Compiling `libfibre` + +Before you compile libfibre yourself consider if the [official releases](https://github.com/samuelsadok/fibre/releases) may be suitable for you instead. + +The recommended way for compiling libfibre is using Docker. You can use the same docker container to cross-compile for all supported targets. + +However if you're actively developing fibre you may want to compile natively for faster compile times. + +### Docker + +The following example compiles libfibre for the `linux-amd64` target. Refer to the "configs/" folder for a list of supported targets. You can also add new targets there but you may need to modify the Dockerfile to include tooling for your new target. + +``` +docker build -t fibre-compiler . +docker run -it -v /tmp/build:/build/cpp/build --entrypoint bash fibre-compiler -c "rm -rd /build/cpp/build/*" +docker run -it -v "$(pwd)":/build -v /tmp/build:/build/build -w /build fibre-compiler configs/linux-amd64.config +``` + +The output file is now located under `/tmp/build/libfibre-linux-amd64.so` on your host system. + +If something fails you can enter the container interactively with `docker run -it -v "$(pwd)":/build -v /tmp/build:/build/build -w /build --entrypoint bash fibre-compiler`. + +### Windows + 1. Download MinGW from [here](https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/installer/mingw-w64-install.exe/download) and install it. + 2. Add `C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin` (or similar) to your `PATH` environment variable. + 3. Download the libusb binaries from [here](`https://github.com/libusb/libusb/releases/download/v1.0.23/libusb-1.0.23.7z`) and unpack them to `third_party/libusb-windows` (such that the file `third_party/libusb-windows/libusb-1.0.23/MinGW64/static/libusb-1.0.a` exists). + 4. Navigate to this directory and run `make` + +### Ubuntu + 1. `sudo apt-get install libusb-1.0-0-dev` + 2. Navigate to this directory and run `make` + +### macOS + 1. `brew install libusb` + 2. Navigate to this directory and run `make` + +## Using `libfibre` + +The API is documented in [libfibre.h](include/fibre/libfibre.h). + +To compile your application you need to link against the libfibre binary (`-L/path/to/libfibre.so`) and add "libfibre.h" to your include path under a folder named "fibre", e.g. `-I/path/to/fibre-cpp/include`. + + +## Notes for Contributors + + - Fibre currently targets C++11 to maximize compatibility with other projects + - Notes on platform independent programming: + - Don't use the keyword `interface` (defined as a macro on Windows in `rpc.h`) diff --git a/Firmware/fibre-cpp/Tupfile.lua b/Firmware/fibre-cpp/Tupfile.lua new file mode 100644 index 000000000..679ca77bc --- /dev/null +++ b/Firmware/fibre-cpp/Tupfile.lua @@ -0,0 +1,144 @@ + +-- Projects that include fibre-cpp and also use tup can place a Tuprules.lua file +-- into their root directory with the line `no_libfibre = true` to prevent +-- libfibre from building. +if no_libfibre == true then + return +end + +tup.include('package.lua') + +CFLAGS = {'-fPIC -std=c++11 -DFIBRE_COMPILE -Wall'} +LDFLAGS = {'-static-libstdc++'} + + +if tup.getconfig("CC") == "" then + CXX = 'clang++' + LINKER = 'clang++' +else + CXX = tup.getconfig("CC") + LINKER = tup.getconfig("CC") +end + +function get_bool_config(name, default) + if tup.getconfig(name) == "" then + return default + elseif tup.getconfig(name) == "true" then + return true + elseif tup.getconfig(name) == "false" then + return false + else + error(name.." ("..tup.getconfig(name)..") must be 'true' or 'false'.") + end +end + +CFLAGS += tup.getconfig("CFLAGS") +LDFLAGS += tup.getconfig("LDFLAGS") +DEBUG = get_bool_config("DEBUG", true) +STRICT = get_bool_config("STRICT", false) + +machine = fibre_run_now(CXX..' -dumpmachine') -- works with both clang and GCC + +BUILD_TYPE='-shared' + +if string.find(machine, "x86_64.*%-linux%-.*") then + outname = 'libfibre-linux-amd64.so' + LDFLAGS += '-lpthread -Wl,--version-script=libfibre.version -Wl,--gc-sections' + STRIP = not DEBUG +elseif string.find(machine, "arm.*%-linux%-.*") then + outname = 'libfibre-linux-armhf.so' + LDFLAGS += '-lpthread -Wl,--version-script=libfibre.version -Wl,--gc-sections' + STRIP = false +elseif string.find(machine, "x86_64.*-mingw.*") then + outname = 'libfibre-windows-amd64.dll' + LDFLAGS += '-lpthread -Wl,--version-script=libfibre.version' + STRIP = not DEBUG +elseif string.find(machine, "x86_64.*-apple-.*") then + outname = 'libfibre-macos-x86.dylib' + STRIP = false +elseif string.find(machine, "wasm.*") then + outname = 'libfibre-wasm.js' + STRIP = false + BUILD_TYPE = '' +else + error('unknown machine identifier '..machine) +end + +LDFLAGS += BUILD_TYPE + +if DEBUG then + CFLAGS += '-O1 -g' +else + CFLAGS += '-O3' -- TODO: add back -lfto +end + +if STRICT then + CFLAGS += '-Werror' +end + +function compile(src_file) + obj_file = 'build/'..tup.file(src_file)..'.o' + tup.frule{ + inputs={src_file}, + command='^co^ '..CXX..' -c %f '..tostring(CFLAGS)..' -o %o', + outputs={obj_file} + } + return obj_file +end + +pkg = get_fibre_package({ + enable_server=false, + enable_client=true, + enable_tcp_server_backend=get_bool_config("ENABLE_TCP_SERVER_BACKEND", true), + enable_tcp_client_backend=get_bool_config("ENABLE_TCP_CLIENT_BACKEND", true), + enable_libusb_backend=get_bool_config("ENABLE_LIBUSB_BACKEND", true), + allow_heap=true, + pkgconf=(tup.getconfig("USE_PKGCONF") != "") and tup.getconfig("USE_PKGCONF") or nil +}) + +CFLAGS += pkg.cflags +LDFLAGS += pkg.ldflags + +for _, inc in pairs(pkg.include_dirs) do + CFLAGS += '-I./'..inc +end + +for _, src_file in pairs(pkg.code_files) do + object_files += compile(src_file) +end +object_files += compile('libfibre.cpp') + +outname = 'build/'..outname + +if not STRIP then + compile_outname=outname +else + compile_outname=outname..'.fat' +end + +if tup.ext(outname) == 'js' then + extra_outputs = {tup.base(compile_outname)..'.wasm'} +else + extra_outputs = {} +end + +tup.frule{ + inputs=object_files, + command='^c^ '..LINKER..' %f '..tostring(CFLAGS)..' '..tostring(LDFLAGS)..' -o %o', + outputs={compile_outname, extra_outputs=extra_outputs} +} + +if STRIP then + tup.frule{ + inputs={compile_outname}, + command='strip --strip-all --discard-all %f -o %o', + outputs={outname} + } +end + +if string.find(machine, "x86_64.*-apple-.*") then + tup.frule{ + inputs=outname, + command='^c^ chmod 644 %f', + } +end \ No newline at end of file diff --git a/Firmware/fibre-cpp/channel_discoverer.cpp b/Firmware/fibre-cpp/channel_discoverer.cpp new file mode 100644 index 000000000..8411868da --- /dev/null +++ b/Firmware/fibre-cpp/channel_discoverer.cpp @@ -0,0 +1,47 @@ + +#include +#include +#include +#include + +using namespace fibre; + +bool ChannelDiscoverer::try_parse_key(const char* begin, const char* end, const char* key, const char** val_begin, const char** val_end) { + ssize_t keylen = strlen(key); + + while (begin != end) { + const char* next_delim = std::find(begin, end, ','); + + if ((next_delim - begin >= keylen) && (memcmp(begin, key, keylen) == 0)) { + if (next_delim - begin == keylen) { + // The key exists but has no value + *val_begin = *val_end = next_delim; + return true; + } else if (begin[keylen] == '=') { + *val_begin = begin + keylen + 1; + *val_end = next_delim; + return true; + } + } + + begin = std::min(next_delim + 1, end); + } + + return false; // key not found +} + +bool ChannelDiscoverer::try_parse_key(const char* begin, const char* end, const char* key, int* val) { + const char* val_begin; + const char* val_end; + if (!try_parse_key(begin, end, key, &val_begin, &val_end)) { + return false; + } + + // Copy value to a null-terminated buffer + char buf[val_end - val_begin + 1]; + memcpy(buf, val_begin, val_end - val_begin); + buf[val_end - val_begin] = 0; + + return sscanf(buf, "0x%x", val) == 1 + || sscanf(buf, "%d", val) == 1; +} diff --git a/Firmware/fibre-cpp/configs/linux-amd64.config b/Firmware/fibre-cpp/configs/linux-amd64.config new file mode 100644 index 000000000..06bc2d83c --- /dev/null +++ b/Firmware/fibre-cpp/configs/linux-amd64.config @@ -0,0 +1,6 @@ +CONFIG_DEBUG=false +CONFIG_STRICT=true +CONFIG_CC="clang++" +CONFIG_CFLAGS="-I$THIRD_PARTY./third_party/libusb-dev-armhf/usr/include/libusb-1.0" +CONFIG_LDFLAGS="$THIRD_PARTY./third_party/libusb-amd64/lib/x86_64-linux-gnu/libusb-1.0.so.0.2.0" +CONFIG_USE_PKGCONF=false diff --git a/Firmware/fibre-cpp/configs/linux-armhf.config b/Firmware/fibre-cpp/configs/linux-armhf.config new file mode 100644 index 000000000..7b684f895 --- /dev/null +++ b/Firmware/fibre-cpp/configs/linux-armhf.config @@ -0,0 +1,6 @@ +CONFIG_DEBUG=false +CONFIG_STRICT=true +CONFIG_CC="arm-linux-gnueabihf-g++" +CONFIG_CFLAGS="-I$THIRD_PARTY./third_party/libusb-dev-armhf/usr/include/libusb-1.0" +CONFIG_LDFLAGS="-L$THIRD_PARTY./third_party/libstdc++-linux-armhf/usr/lib/gcc-cross/arm-linux-gnueabihf/10 $THIRD_PARTY./third_party/libusb-armhf/usr/lib/arm-linux-gnueabihf/libusb-1.0.so.0" +CONFIG_USE_PKGCONF=false diff --git a/Firmware/fibre-cpp/configs/macos-x86.config b/Firmware/fibre-cpp/configs/macos-x86.config new file mode 100644 index 000000000..4e9afec4b --- /dev/null +++ b/Firmware/fibre-cpp/configs/macos-x86.config @@ -0,0 +1,10 @@ +CONFIG_DEBUG=false +CONFIG_STRICT=true +CONFIG_CC="LD_LIBRARY_PATH=/opt/osxcross/lib MACOSX_DEPLOYMENT_TARGET=10.9 /opt/osxcross/bin/o64-clang++" +CONFIG_CFLAGS="-I$THIRD_PARTY./third_party/libusb-1.0.23/libusb -arch i386 -arch x86_64" +CONFIG_LDFLAGS="$THIRD_PARTY./third_party/libusb-1.0.23/build-macos-amd64/libusb/.libs/libusb-1.0.a -framework CoreFoundation -framework IOKit" +# not supported yet +CONFIG_ENABLE_TCP_SERVER_BACKEND=false +# not supported yet +CONFIG_ENABLE_TCP_CLIENT_BACKEND=false +CONFIG_USE_PKGCONF=false diff --git a/Firmware/fibre-cpp/configs/wasm.config b/Firmware/fibre-cpp/configs/wasm.config new file mode 100644 index 000000000..6a683e2d9 --- /dev/null +++ b/Firmware/fibre-cpp/configs/wasm.config @@ -0,0 +1,9 @@ +CONFIG_DEBUG=true +CONFIG_STRICT=true +CONFIG_CC=/usr/lib/emscripten/em++ +CONFIG_CFLAGS=-include emscripten.h -DFIBRE_PUBLIC=EMSCRIPTEN_KEEPALIVE -s RESERVED_FUNCTION_POINTERS=1 +CONFIG_LDFLAGS=-s EXPORT_ES6=1 -s MODULARIZE=1 -s USE_ES6_IMPORT_META=0 -s 'EXTRA_EXPORTED_RUNTIME_METHODS=[addFunction, stringToUTF8Array, UTF8ArrayToString, ENV]' +CONFIG_USE_PKGCONF=false +CONFIG_ENABLE_LIBUSB_BACKEND=false +CONFIG_ENABLE_TCP_SERVER_BACKEND=false +CONFIG_ENABLE_TCP_CLIENT_BACKEND=false diff --git a/Firmware/fibre-cpp/configs/windows-amd64.config b/Firmware/fibre-cpp/configs/windows-amd64.config new file mode 100644 index 000000000..2b26e9b53 --- /dev/null +++ b/Firmware/fibre-cpp/configs/windows-amd64.config @@ -0,0 +1,10 @@ +CONFIG_DEBUG=false +CONFIG_STRICT=true +CONFIG_CC="x86_64-w64-mingw32-g++" +CONFIG_CFLAGS="-I$THIRD_PARTY./third_party/libusb-windows/libusb-1.0.23/include/libusb-1.0" +CONFIG_LDFLAGS="-static-libgcc $THIRD_PARTY./third_party/libusb-windows/libusb-1.0.23/MinGW64/static/libusb-1.0.a" +# not supported yet +CONFIG_ENABLE_TCP_SERVER_BACKEND=false +# not supported yet +CONFIG_ENABLE_TCP_CLIENT_BACKEND=false +CONFIG_USE_PKGCONF=false diff --git a/Firmware/fibre/cpp/include/fibre/crc.hpp b/Firmware/fibre-cpp/crc.hpp similarity index 100% rename from Firmware/fibre/cpp/include/fibre/crc.hpp rename to Firmware/fibre-cpp/crc.hpp diff --git a/Firmware/fibre/cpp/endpoints_template.j2 b/Firmware/fibre-cpp/endpoints_template.j2 similarity index 96% rename from Firmware/fibre/cpp/endpoints_template.j2 rename to Firmware/fibre-cpp/endpoints_template.j2 index db9b8dbd2..459a2ca24 100644 --- a/Firmware/fibre/cpp/endpoints_template.j2 +++ b/Firmware/fibre-cpp/endpoints_template.j2 @@ -10,10 +10,12 @@ * a function-oriented approach and a more powerful object model. * */ -#ifndef __FIBRE_INTERFACES_HPP -#define __FIBRE_INTERFACES_HPP +#ifndef __FIBRE_ENDPOINTS_HPP +#define __FIBRE_ENDPOINTS_HPP #include +#include +#include // Note: with -Og the functions with large switch statements reserves a huge amount // of stack space because they reserves separate space for the stack frame of each @@ -87,4 +89,4 @@ bool set_endpoint_from_float(endpoint_ref_t endpoint_ref, float value) { #pragma GCC pop_options -#endif // __FIBRE_INTERFACES_HPP \ No newline at end of file +#endif // __FIBRE_ENDPOINTS_HPP \ No newline at end of file diff --git a/Firmware/fibre-cpp/fibre.cpp b/Firmware/fibre-cpp/fibre.cpp new file mode 100644 index 000000000..d85fa88f3 --- /dev/null +++ b/Firmware/fibre-cpp/fibre.cpp @@ -0,0 +1,270 @@ + +#include +#include "logging.hpp" +#include +#include "legacy_protocol.hpp" +#include "print_utils.hpp" +#include +#include +#include + +#if FIBRE_ALLOW_HEAP +#include +#include +#endif + +DEFINE_LOG_TOPIC(FIBRE); +USE_LOG_TOPIC(FIBRE); + +#if FIBRE_ENABLE_EVENT_LOOP +# ifdef __linux__ +# include "platform_support/epoll_event_loop.hpp" +using EventLoopImpl = fibre::EpollEventLoop; +# else +# error "No event loop implementation available for this operating system." +# endif +#endif + +using namespace fibre; + +struct DiscoveryContext { +}; + +#if FIBRE_ALLOW_HEAP + +template +T* my_alloc() { + return new T{}; +} + +template +void my_free(T* ctx) { + delete ctx; +} + +#else + +template +struct TheInstance { + static T instance; + static bool in_use; +}; + +template T TheInstance::instance{}; +template bool TheInstance::in_use = false; + +template +T* my_alloc() { + if (!TheInstance::in_use) { + TheInstance::in_use = true; + return &TheInstance::instance; + } else { + return nullptr; + } +} + +template +void my_free(T* ctx) { + if (ctx == &TheInstance::instance) { + TheInstance::in_use = false; + } else { + FIBRE_LOG(E) << "bad instance"; + } +} + +#endif + +bool fibre::launch_event_loop(Callback on_started) { +#if FIBRE_ENABLE_EVENT_LOOP + EventLoopImpl* event_loop = my_alloc(); // TODO: free + return event_loop->start([&](){ on_started.invoke(event_loop); }); +#else + return false; +#endif +} + +struct BackendInitializer { + template + bool operator()(T& backend) { + if (!backend.init(ctx->event_loop)) { + return false; + } + ctx->register_backend(backend.get_name(), &backend); + return true; + } + Context* ctx; +}; +struct BackendDeinitializer { + template + bool operator()(T& backend) { + ctx->deregister_backend(backend.get_name()); + return backend.deinit(); + } + Context* ctx; +}; + +template +bool all(std::tuple args, std::index_sequence) { + std::array arr = { std::get(args) ... }; + return std::all_of(arr.begin(), arr.end(), [](bool val) { return val; }); +} + +template +bool all(std::tuple args) { + return all(args, std::make_index_sequence()); +} + +Context* fibre::open(EventLoop* event_loop) { + Context* ctx = my_alloc(); + if (!ctx) { + FIBRE_LOG(E) << "already opened"; + return nullptr; + } + + ctx->event_loop = event_loop; + auto static_backends_good = for_each_in_tuple(BackendInitializer{ctx}, + ctx->static_backends); + + if (!all(static_backends_good)) { + // TODO: shutdown backends + FIBRE_LOG(E) << "some backends failed to initialize"; + return nullptr; + } + + return ctx; +} + +void fibre::close(Context* ctx) { + if (ctx->n_domains) { + FIBRE_LOG(W) <n_domains << " domains are still open"; + } + + for_each_in_tuple(BackendDeinitializer{ctx}, + ctx->static_backends); + + my_free(ctx); +} + +#if FIBRE_ALLOW_HEAP +Domain* Context::create_domain(std::string specs) { + FIBRE_LOG(D) << "creating domain with path \"" << specs << "\""; + + Domain* domain = new Domain(); // deleted in close_domain + domain->ctx = this; + + std::string::iterator prev_delim = specs.begin(); + while (prev_delim < specs.end()) { + auto next_delim = std::find(prev_delim, specs.end(), ';'); + auto colon = std::find(prev_delim, next_delim, ':'); + auto colon_end = std::min(colon + 1, next_delim); + + std::string name{prev_delim, colon}; + auto it = discoverers.find(name); + + if (it == discoverers.end()) { + FIBRE_LOG(W) << "transport layer \"" << name << "\" not implemented"; + } else { + domain->channel_discovery_handles[name] = nullptr; + it->second->start_channel_discovery(domain, + &*colon_end, next_delim - colon_end, + &domain->channel_discovery_handles[name]); + } + + prev_delim = std::min(next_delim + 1, specs.end()); + } + + n_domains++; + return domain; +} + +void Context::close_domain(Domain* domain) { + for (auto& it: domain->channel_discovery_handles) { + discoverers[it.first]->stop_channel_discovery(it.second); + } + domain->channel_discovery_handles.clear(); + delete domain; + n_domains--; +} + +void Context::register_backend(std::string name, ChannelDiscoverer* backend) { + if (discoverers.find(name) != discoverers.end()) { + FIBRE_LOG(W) << "Discoverer " << name << " already registered"; + return; // TODO: report status + } + + discoverers[name] = backend; +} + +void Context::deregister_backend(std::string name) { + auto it = discoverers.find(name); + if (it == discoverers.end()) { + FIBRE_LOG(W) << "Discoverer " << name << " not registered"; + return; // TODO: report status + } + + discoverers.erase(it); +} +#endif + +#if FIBRE_ENABLE_CLIENT +void Domain::start_discovery(Callback on_found_object, Callback on_lost_object) { + on_found_object_ = on_found_object; + on_lost_object_ = on_lost_object; + for (auto& it: root_objects_) { + on_found_object_.invoke(it.first, it.second); + } +} + +void Domain::stop_discovery() { + auto on_lost_object = on_lost_object_; + on_found_object_ = nullptr; + on_lost_object_ = nullptr; + for (auto& it: root_objects_) { + on_lost_object.invoke(it.first); + } +} +#endif + +void Domain::add_channels(ChannelDiscoveryResult result) { + FIBRE_LOG(D) << "found channels!"; + + if (result.status != kFibreOk) { + FIBRE_LOG(W) << "discoverer stopped"; + return; + } + + if (!result.rx_channel || !result.tx_channel) { + FIBRE_LOG(W) << "unidirectional operation not supported yet"; + return; + } + +#if FIBRE_ENABLE_CLIENT || FIBRE_ENABLE_SERVER + // Deleted during on_stopped() + auto protocol = new fibre::LegacyProtocolPacketBased(result.rx_channel, result.tx_channel, result.mtu); +#if FIBRE_ENABLE_CLIENT + protocol->start(MEMBER_CB(this, on_found_root_object), MEMBER_CB(this, on_lost_root_object), MEMBER_CB(this, on_stopped)); +#else + protocol->start(MEMBER_CB(this, on_stopped)); +#endif +#endif +} + +#if FIBRE_ENABLE_CLIENT +void Domain::on_found_root_object(LegacyObjectClient* obj_client, std::shared_ptr obj) { + Object* root_object = reinterpret_cast(obj.get()); + Interface* root_intf = reinterpret_cast(obj->intf.get()); + root_objects_[root_object] = root_intf; + on_found_object_.invoke(root_object, root_intf); +} + +void Domain::on_lost_root_object(LegacyObjectClient* obj_client, std::shared_ptr obj) { + Object* root_object = reinterpret_cast(obj.get()); + auto it = root_objects_.find(root_object); + root_objects_.erase(it); + on_lost_object_.invoke(root_object); +} +#endif + +void Domain::on_stopped(LegacyProtocolPacketBased* protocol, StreamStatus status) { + delete protocol; +} diff --git a/Firmware/fibre/cpp/function_stubs_template.j2 b/Firmware/fibre-cpp/function_stubs_template.j2 similarity index 86% rename from Firmware/fibre/cpp/function_stubs_template.j2 rename to Firmware/fibre-cpp/function_stubs_template.j2 index fb44fdaa1..ac25923f0 100644 --- a/Firmware/fibre/cpp/function_stubs_template.j2 +++ b/Firmware/fibre-cpp/function_stubs_template.j2 @@ -24,9 +24,9 @@ static inline bool [[func.fullname | to_snake_case]]([% for arg in func.in.value return false; } [%- if func.implementation %] - [% if func.out %]std::tuple<[% for arg in func.out.values() %][[arg.type.c_name]][[', ' if not loop.last]][% endfor %]> ret = [% endif %][[func.implementation]]([% for arg in func.in.values() %]in_[[arg.name]][% if not arg.optional %].value()[% endif %][[', ' if not loop.last]][% endfor %]); + [% if func.out %]std::tuple<[% for arg in func.out.values() %][[arg.type.c_name]][[', ' if not loop.last]][% endfor %]> ret = [% endif %][[func.implementation]]([% for arg in func.in.values() %](*in_[[arg.name]][% if not arg.optional %])[% endif %][[', ' if not loop.last]][% endfor %]); [%- else %] - [% if func.out %]std::tuple<[% for arg in func.out.values() %][[arg.type.c_name]][[', ' if not loop.last]][% endfor %]> ret = [% endif %]in_[[(func.in.values() | first).name]].value()->[[func.name]]([% for arg in func.in.values() | skip_first %]in_[[arg.name]][% if not arg.optional %].value()[% endif %][[', ' if not loop.last]][% endfor %]); + [% if func.out %]std::tuple<[% for arg in func.out.values() %][[arg.type.c_name]][[', ' if not loop.last]][% endfor %]> ret = [% endif %](*in_[[(func.in.values() | first).name]])->[[func.name]]([% for arg in func.in.values() | skip_first %][% if not arg.optional %]*[% endif %]in_[[arg.name]][[', ' if not loop.last]][% endfor %]); [%- endif %] [%- if func.out %] return [% for arg in func.out.values() %]((out_[[arg.name]] && ((*out_[[arg.name]] = std::get<[[loop.index0]]>(ret)), true)) || fibre::Codec<[[arg.type.c_name]]>::encode(std::get<[[loop.index0]]>(ret), output_buffer))[% if not loop.last %] diff --git a/Firmware/fibre-cpp/get_dependencies.sh b/Firmware/fibre-cpp/get_dependencies.sh new file mode 100755 index 000000000..fe05ca16b --- /dev/null +++ b/Firmware/fibre-cpp/get_dependencies.sh @@ -0,0 +1,77 @@ +#!/bin/bash +set -euo pipefail + +# Usage: download_deb_pkg destination-dir url +function download_deb_pkg() { + mkdir -p third_party + dir="$1" + url="$2" + file="$(sed 's|^.*/\([^/]*\)$|\1|' <<< "$url")" + + pushd third_party > /dev/null + if ! [ -f "${file}" ]; then + wget "${url}" + fi + if ! [ -d "${dir}/usr" ]; then + ar x "${file}" "data.tar.xz" + mkdir -p "${dir}" + tar -xvf "data.tar.xz" -C "${dir}" + fi + popd > /dev/null +} + +# Usage: compile_libusb arch-name arch +function compile_libusb() { + arch_name="$1" + arch="$2" + libusb_version=1.0.23 + + pushd third_party > /dev/null + if ! [ -f "libusb-${libusb_version}.tar.bz2" ]; then + wget "https://github.com/libusb/libusb/releases/download/v${libusb_version}/libusb-${libusb_version}.tar.bz2" + fi + if ! [ -d "libusb-${libusb_version}" ]; then + tar -xvf "libusb-${libusb_version}.tar.bz2" + fi + + mkdir -p "libusb-${libusb_version}/build-${arch_name}" + pushd "libusb-${libusb_version}/build-${arch_name}" > /dev/null + unset LDFLAGS + if ! [ -f "libusb/.libs/libusb-1.0.a" ]; then + ../configure --host="$arch" \ + --enable-static \ + --prefix=/opt/osxcross/ \ + --disable-dependency-tracking + # They broke parallel building in libusb 1.20 + make + fi + popd > /dev/null + popd > /dev/null +} + +function patch_macos_sdk() { + # Link are broken: + # …ions/Current/Headers $ ls -l IOReturn.h + # lrwxrwxrwx 1 root root 189 Dec 26 2019 IOReturn.h -> Users/phracker/Documents/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/IOKit/IOReturn.h + # Fix with: + # sudo ln -sf /opt/osxcross/SDK/MacOSX10.13.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/IOKit/IOReturn.h IOReturn.h + + oldprefix="Users/phracker/Documents/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" + newprefix="/opt/osxcross/SDK/MacOSX10.13.sdk" + while IFS= read -r link; do + destination="$(readlink "$link")" + pruned_destination="${destination#"$oldprefix"}" + if [ "${oldprefix}${pruned_destination}" == "${destination}" ]; then + sudo mv -T "${newprefix}${pruned_destination}" "$link" + fi + done <<< "$(find /opt/osxcross/SDK/MacOSX10.13.sdk/System/Library/Frameworks/IOKit.framework -xtype l)" +} + +cmd="$1" +shift +case "$cmd" in + download_deb_pkg) download_deb_pkg $@ ;; + compile_libusb) compile_libusb $@ ;; + patch_macos_sdk) patch_macos_sdk $@ ;; + *) echo "unknown command" && false ;; +esac diff --git a/Firmware/fibre-cpp/include/fibre/async_stream.hpp b/Firmware/fibre-cpp/include/fibre/async_stream.hpp new file mode 100644 index 000000000..c2efc039d --- /dev/null +++ b/Firmware/fibre-cpp/include/fibre/async_stream.hpp @@ -0,0 +1,145 @@ +#ifndef __FIBRE_ASYNC_STREAM_HPP +#define __FIBRE_ASYNC_STREAM_HPP + +#include +#include +#include + +namespace fibre { + +enum StreamStatus { + kStreamOk, + kStreamCancelled, + kStreamClosed, + kStreamError +}; + + +struct ReadResult { + StreamStatus status; + + /** + * @brief The pointer to one position after the last byte that was + * transferred. + * This must always be in [buffer.begin(), buffer.end()], even if the + * transfer was not succesful. + * If the status is kStreamError or kStreamCancelled then the accuracy + * of this field is not guaranteed. + */ + unsigned char* end; +}; + +struct WriteResult { + StreamStatus status; + + /** + * @brief The pointer to one position after the last byte that was + * transferred. + * This must always be in [buffer.begin(), buffer.end()], even if the + * transfer was not succesful. + * If the status is kStreamError or kStreamCancelled then the accuracy + * of this field is not guaranteed. + */ + const unsigned char* end; +}; + + +using TransferHandle = uintptr_t; + +/** + * @brief Base class for asynchronous stream sources. + */ +class AsyncStreamSource { +public: + /** + * @brief Starts a read operation. Once the read operation completes, + * on_finished.complete() is called. + * + * Most implementations only allow one transfer to be active at a time. + * + * TODO: specify if `completer` can be called directly within this function. + * + * @param buffer: The buffer where the data to be written shall be fetched from. + * Must remain valid until `completer` is satisfied. + * @param handle: The variable pointed to by this argument is set to an + * opaque transfer handle that can be passed to cancel_read() as + * long as the operation has not yet completed. + * If the completer is invoked directly from start_read() then the + * handle is not modified after this invokation. That means it's safe + * for the completion handler to reuse the handle variable. + * @param completer: The completer that will be completed once the operation + * finishes, whether successful or not. + * Must remain valid until it is satisfied. + */ + virtual void start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) = 0; + + /** + * @brief Cancels an operation that was previously started with start_read(). + * + * The transfer is cancelled asynchronously and the associated completer + * will eventually be completed with kStreamCancelled. Until then the + * transfer must be considered still in progress and associated resources + * must not be freed. + * + * TODO: specify if an implementation is allowed to return something other + * than kStreamCancelled when the transfer was cancelled. + * + * This function must not be called once the stream has started to invoke + * the associated completion handler. It must also not be called twice for + * the same transfer. + */ + virtual void cancel_read(TransferHandle transfer_handle) = 0; +}; + +/** + * @brief Base class for asynchronous stream sources. + * + * Thread-safety: Implementations are generally not required to provide thread + * safety. Users should only call the functions of this class on the same thread + * as the event loop on which the stream runs. + */ +class AsyncStreamSink { +public: + /** + * @brief Starts a write operation. Once the write operation completes, + * on_finished.complete() is called. + * + * Most implementations only allow one transfer to be active at a time. + * + * TODO: specify if `completer` can be called directly within this function. + * + * @param buffer: The buffer where the data to be written shall be fetched from. + * Must remain valid until `completer` is satisfied. + * @param handle: The variable pointed to by this argument is set to an + * opaque transfer handle that can be passed to cancel_write() as + * long as the operation has not yet completed. + * If the completer is invoked directly from start_write() then the + * handle is not modified after this invokation. That means it's safe + * for the completion handler to reuse the handle variable. + * @param completer: The completer that will be completed once the operation + * finishes, whether successful or not. + * Must remain valid until it is satisfied. + */ + virtual void start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) = 0; + + /** + * @brief Cancels an operation that was previously started with start_write(). + * + * The transfer is cancelled asynchronously and the associated completer + * will eventually be completed with kStreamCancelled. Until then the + * transfer must be considered still in progress and associated resources + * must not be freed. + * + * TODO: specify if an implementation is allowed to return something other + * than kStreamCancelled when the transfer was cancelled. + * + * This function must not be called once the stream has started to invoke + * the associated completion handler. It must also not be called twice for + * the same transfer. + */ + virtual void cancel_write(TransferHandle transfer_handle) = 0; +}; + +} + +#endif // __FIBRE_ASYNC_STREAM_HPP \ No newline at end of file diff --git a/Firmware/fibre/cpp/include/fibre/bufptr.hpp b/Firmware/fibre-cpp/include/fibre/bufptr.hpp similarity index 84% rename from Firmware/fibre/cpp/include/fibre/bufptr.hpp rename to Firmware/fibre-cpp/include/fibre/bufptr.hpp index 2ce3fefbc..07fbfbb10 100644 --- a/Firmware/fibre/cpp/include/fibre/bufptr.hpp +++ b/Firmware/fibre-cpp/include/fibre/bufptr.hpp @@ -1,6 +1,9 @@ #ifndef __FIBRE_BUFPTR_HPP #define __FIBRE_BUFPTR_HPP +#include +#include + namespace fibre { static inline bool soft_assert(bool expr) { return expr; } // TODO: implement @@ -24,11 +27,14 @@ struct generic_bufptr_t { template generic_bufptr_t(T (&begin)[I]) : generic_bufptr_t(begin, I) {} - generic_bufptr_t(const std::vector>& vector) + generic_bufptr_t(std::vector::type>& vector) + : generic_bufptr_t(vector.data(), vector.size()) {} + + generic_bufptr_t(const std::vector::type>& vector) : generic_bufptr_t(vector.data(), vector.size()) {} - generic_bufptr_t(const generic_bufptr_t>& other) - : generic_bufptr_t(other.begin_, other.end_) {} + generic_bufptr_t(const generic_bufptr_t::type>& other) + : generic_bufptr_t(other.begin(), other.end()) {} generic_bufptr_t& operator+=(size_t num) { if (!soft_assert(num <= size())) { @@ -81,6 +87,7 @@ struct generic_bufptr_t { T& back() const { return *(end() - 1); } T& operator[](size_t idx) { return *(begin() + idx); } +private: T* begin_; T* end_; }; diff --git a/Firmware/fibre-cpp/include/fibre/callback.hpp b/Firmware/fibre-cpp/include/fibre/callback.hpp new file mode 100644 index 000000000..52aaa76d5 --- /dev/null +++ b/Firmware/fibre-cpp/include/fibre/callback.hpp @@ -0,0 +1,126 @@ +#ifndef __CALLBACK_HPP +#define __CALLBACK_HPP + +#include +#include +#include +#include +#include + +namespace fibre { + +namespace detail { +template struct get_default { static T val() { return {}; } }; +template<> struct get_default { static void val() {} }; +} + +template +class Callback { +public: + Callback() : cb_(nullptr), ctx_(nullptr) {} + Callback(std::nullptr_t) : cb_(nullptr), ctx_(nullptr) {} + Callback(TRet(*callback)(void*, TArgs...), void* ctx) : + cb_(callback), ctx_(ctx) {} + + /** + * @brief Creates a copy of another Callback instance. + * + * This is only works it the other callback has identical template parameters. + * This constructor is templated so that construction from an incompatible + * callback gives a useful error message. + */ + //template + //Callback(const Callback& other) : cb_(other.cb_), ctx_(other.ctx_) { + // static_assert(std::is_same, Callback>::value, "incompatible callback type"); + //} + + Callback(const Callback& other) : cb_(other.cb_), ctx_(other.ctx_) {} + + // If you get a compile error "[...] invokes a deleted function" that points + // here then you're probably trying to assign a Callback with incompatible + // template arguments to another Callback. + template + Callback(const Callback& other) = delete; + + /** + * @brief Constructs a callback object from a functor. The functor must + * remain allocated throughout the lifetime of the Callback. + */ + template + Callback(const TFunc& func) : + cb_([](void* ctx, TArgs...args){ + return (*(const TFunc*)ctx)(args...); + }), ctx_((void*)&func) {} + + operator bool() { + return cb_; + } + + TRet invoke(TArgs ... arg) const { + if (cb_) { + return (*cb_)(ctx_, arg...); + } + return detail::get_default::val(); + } + + TRet invoke_and_clear(TArgs ... arg) { + void* ctx = ctx_; + auto cb = cb_; + ctx_ = nullptr; + cb_ = nullptr; + if (cb) { + return (*cb)(ctx, arg...); + } + return detail::get_default::val(); + } + + typedef TRet(*cb_t)(void*, TArgs...); + cb_t get_ptr() { return cb_; } + void* get_ctx() { return ctx_; } + +private: + TRet(*cb_)(void*, TArgs...); + void* ctx_; +}; + +template +struct function_traits { + using TRet = _TRet; + using TArgs = std::tuple<_TArgs...>; + using TObj = _TObj; +}; + +template +function_traits<_TRet, _TObj, _TArgs...> make_function_traits(_TRet (_TObj::*)(_TArgs...)) { + return {}; +} + +template +struct MemberCallback; + +template +struct MemberCallback> { + using cb_t = Callback; + static cb_t with(TObj* obj) { + return cb_t{[](void* obj, TArgs... arg) { + return (((TObj*)obj)->*func)(arg...); + }, obj}; + } +}; + +template> +typename MemCb::cb_t make_callback(typename TTraits::TObj* obj) { + return MemCb::with(obj); +} + +#define MEMBER_CB(obj, func) \ + fibre::make_callback< \ + decltype(&std::remove_reference_t::func), \ + &std::remove_reference_t::func \ + >(obj) + +} + +#endif // __CALLBACK_HPP \ No newline at end of file diff --git a/Firmware/fibre-cpp/include/fibre/channel_discoverer.hpp b/Firmware/fibre-cpp/include/fibre/channel_discoverer.hpp new file mode 100644 index 000000000..042a32ab1 --- /dev/null +++ b/Firmware/fibre-cpp/include/fibre/channel_discoverer.hpp @@ -0,0 +1,38 @@ +#ifndef __FIBRE_CHANNEL_DISCOVERER +#define __FIBRE_CHANNEL_DISCOVERER + +#include "async_stream.hpp" +#include +#include + +namespace fibre { + +struct ChannelDiscoveryResult { + Status status; + AsyncStreamSource* rx_channel; + AsyncStreamSink* tx_channel; + size_t mtu; +}; + +struct ChannelDiscoveryContext {}; + +class Domain; // defined in fibre.hpp + +class ChannelDiscoverer { +public: + // TODO: maybe we should remove "handle" because a discovery can also be + // uniquely identified by domain. + virtual void start_channel_discovery( + Domain* domain, + const char* specs, size_t specs_len, + ChannelDiscoveryContext** handle) = 0; + virtual int stop_channel_discovery(ChannelDiscoveryContext* handle) = 0; + +protected: + bool try_parse_key(const char* begin, const char* end, const char* key, const char** val_begin, const char** val_end); + bool try_parse_key(const char* begin, const char* end, const char* key, int* val); +}; + +} + +#endif // __FIBRE_CHANNEL_DISCOVERER \ No newline at end of file diff --git a/Firmware/fibre/cpp/include/fibre/cpp_utils.hpp b/Firmware/fibre-cpp/include/fibre/cpp_utils.hpp similarity index 94% rename from Firmware/fibre/cpp/include/fibre/cpp_utils.hpp rename to Firmware/fibre-cpp/include/fibre/cpp_utils.hpp index b09aa4004..262598dde 100644 --- a/Firmware/fibre/cpp/include/fibre/cpp_utils.hpp +++ b/Firmware/fibre-cpp/include/fibre/cpp_utils.hpp @@ -82,7 +82,7 @@ class WrapperClass { #include #include #include -#include +//#include /* Backport features from C++14 and C++17 ------------------------------------*/ @@ -439,8 +439,98 @@ T& get(std::variant& val) { return std::get(val); } + +/// Tag type to disengage optional objects. +struct nullopt_t { + // Do not user-declare default constructor at all for + // optional_value = {} syntax to work. + // nullopt_t() = delete; + + // Used for constructing nullopt. + enum class _Construct { _Token }; + + // Must be constexpr for nullopt_t to be literal. + explicit constexpr nullopt_t(_Construct) { } +}; + +constexpr nullopt_t nullopt { nullopt_t::_Construct::_Token }; + +template +class optional { +public: + using storage_t = char[sizeof(T)]; + + optional() : has_value_(false) {} + optional(nullopt_t val) : has_value_(false) {} + + optional(const optional & other) : has_value_(other.has_value_) { + if (has_value_) + new ((T*)content_) T{*(T*)other.content_}; + } + + optional(optional&& other) : has_value_(other.has_value_) { + if (has_value_) + new ((T*)content_) T{*(T*)other.content_}; + } + + optional(T& arg) { + new ((T*)content_) T{arg}; + has_value_ = true; + } + + optional(T&& arg) { + new ((T*)content_) T{std::forward(arg)}; + has_value_ = true; + } + + ~optional() { + if (has_value_) + ((T*)content_)->~T(); + } + + inline optional& operator=(const optional & other) { + (**this).~T(); + new (this) optional{other}; + return *this; + } + + inline bool operator==(const optional& rhs) const { + return (!has_value_ && !rhs.has_value_) || (*(T*)content_ == *(T*)rhs.content_); + } + + inline bool operator!=(const optional& rhs) const { + return !(*this == rhs); + } + + inline T& operator*() { + return *(T*)content_; + } + + inline T* operator->() { + return (T*)content_; + } + + storage_t content_; + size_t has_value_; + + size_t has_value() const { return has_value_; } +}; + +template +optional make_optional(T&& val) { + return optional{std::forward(val)}; +} + +template +optional make_optional(T& val) { + return optional{val}; +} + } // namespace std +#else +#include +#include #endif /* Stuff that should be in the STL but isn't ---------------------------------*/ @@ -707,7 +797,7 @@ struct sstring { static constexpr std::array as_array() { return {CHARS...}; } template - constexpr bool operator==(const sstring & other) { + constexpr bool operator==(const sstring & other) const { return as_array() == other.as_array(); } }; @@ -786,13 +876,13 @@ using sstring_builder_t = typename sstring_builder::type; */ #define MAKE_SSTRING(literal) sstring_builder_t -namespace std { +/*namespace std { template static std::ostream& operator<<(std::ostream& stream, const sstring& val) { stream << val.chars; return stream; } -} +}*/ template struct join_sstring_impl; @@ -917,10 +1007,10 @@ struct args_of { using type = std::tuple; }; -template -struct args_of> { - using type = std::tuple; -}; +//template +//struct args_of<_Mem_fn> { +// using type = std::tuple; +//}; template struct args_of { diff --git a/Firmware/fibre-cpp/include/fibre/event_loop.hpp b/Firmware/fibre-cpp/include/fibre/event_loop.hpp new file mode 100644 index 000000000..8b94c0f43 --- /dev/null +++ b/Firmware/fibre-cpp/include/fibre/event_loop.hpp @@ -0,0 +1,74 @@ +#ifndef __FIBRE_EVENT_LOOP_HPP +#define __FIBRE_EVENT_LOOP_HPP + +#include "callback.hpp" +#include + +namespace fibre { + +struct EventLoopTimer; + +/** + * @brief Base class for event loops. + * + * Thread-safety: The public functions of this class except for post() must not + * be assumed to be thread-safe. + * Generally the functions of an event loop are only safe to be called from the + * event loop's thread itself. + */ +class EventLoop { +public: + /** + * @brief Registers a callback for immediate execution on the event loop + * thread. + * + * This function must be thread-safe. + */ + virtual bool post(Callback callback) = 0; + + /** + * @brief Registers the given file descriptor on this event loop. + * + * This function is only implemented on Unix-like systems. + * + * @param fd: A waitable Unix file descriptor on which to listen for events. + * @param events: A bitfield that specifies the events to listen for. + * For instance EPOLLIN or EPOLLOUT. + * @param callback: The callback to invoke every time the event triggers. + * A bitfield is passed to the callback to indicate which events were + * triggered. This callback must remain valid until + * deregister_event() is called for the same file descriptor. + */ + virtual bool register_event(int fd, uint32_t events, Callback callback) = 0; + + /** + * @brief Deregisters the given event. + * + * Once this function returns, the associated callback will no longer be + * invoked and its resources can be freed. + */ + virtual bool deregister_event(int fd) = 0; + + /** + * @brief Registers a callback to be called at a later point in time. + * + * This returns an opaque handler which can be used to cancel the timer. + * + * @param delay: The delay from now in seconds. + * TOOD: specify if OS sleep time is counted in. + */ + virtual struct EventLoopTimer* call_later(float delay, Callback callback) = 0; + + /** + * @brief Cancels a timer which was previously started by call_later(). + * + * Must not be called after invokation of the callback has started. + * This also means that cancel_timer() must not be called from within the + * callback of the timer itself. + */ + virtual bool cancel_timer(EventLoopTimer* timer) = 0; +}; + +} + +#endif // __FIBRE_EVENT_LOOP_HPP \ No newline at end of file diff --git a/Firmware/fibre-cpp/include/fibre/fibre.hpp b/Firmware/fibre-cpp/include/fibre/fibre.hpp new file mode 100644 index 000000000..78b5f6804 --- /dev/null +++ b/Firmware/fibre-cpp/include/fibre/fibre.hpp @@ -0,0 +1,155 @@ +#ifndef __FIBRE_HPP +#define __FIBRE_HPP + +#include +#include +#include +#include +#include +#include +#include + +#if FIBRE_ENABLE_LIBUSB_BACKEND +#include "../../platform_support/libusb_transport.hpp" +#endif + +#if FIBRE_ENABLE_TCP_CLIENT_BACKEND +#include "../../platform_support/posix_tcp_backend.hpp" +#endif + +namespace fibre { + +struct CallBuffers { + Status status; + cbufptr_t tx_buf; + bufptr_t rx_buf; +}; + +struct CallBufferRelease { + Status status; + const uint8_t* tx_end; + uint8_t* rx_end; +}; + +struct Function { + virtual std::optional + call(void**, CallBuffers, Callback, CallBufferRelease>) = 0; +}; + +struct Object; +struct Interface; +class Domain; + +template +struct StaticBackend { + std::string name; + T impl; +}; + +struct Context { + size_t n_domains = 0; + EventLoop* event_loop; + + std::tuple< +#if FIBRE_ENABLE_LIBUSB_BACKEND + LibusbDiscoverer +#endif +#if FIBRE_ENABLE_LIBUSB_BACKEND && FIBRE_ENABLE_TCP_CLIENT_BACKEND + , // TODO: find a less awkward way to do this +#endif +#if FIBRE_ENABLE_TCP_CLIENT_BACKEND + PosixTcpClientBackend +#endif +#if FIBRE_ENABLE_TCP_CLIENT_BACKEND && FIBRE_ENABLE_TCP_SERVER_BACKEND + , // TODO: find a less awkward way to do this +#endif +#if FIBRE_ENABLE_TCP_SERVER_BACKEND + PosixTcpServerBackend +#endif + > static_backends; + +#if FIBRE_ALLOW_HEAP + std::unordered_map discoverers; +#endif + + /** + * @brief Creates a domain on which objects can subsequently be published + * and discovered. + * + * This potentially starts looking for channels on this domain. + */ + Domain* create_domain(std::string specs); + void close_domain(Domain* domain); + + void register_backend(std::string name, ChannelDiscoverer* backend); + void deregister_backend(std::string name); +}; + +// TODO: don't declare these types here +struct LegacyProtocolPacketBased; +class LegacyObjectClient; +struct LegacyObject; + +class Domain { + friend struct Context; +public: +#if FIBRE_ENABLE_CLIENT + // TODO: add interface argument + // TODO: support multiple discovery instances + void start_discovery(Callback on_found_object, Callback on_lost_object); + void stop_discovery(); +#endif + + void add_channels(ChannelDiscoveryResult result); + + Context* ctx; +private: +#if FIBRE_ENABLE_CLIENT + void on_found_root_object(LegacyObjectClient* obj_client, std::shared_ptr obj); + void on_lost_root_object(LegacyObjectClient* obj_client, std::shared_ptr obj); +#endif + void on_stopped(LegacyProtocolPacketBased* protocol, StreamStatus status); + +#if FIBRE_ALLOW_HEAP + std::unordered_map channel_discovery_handles; +#endif +#if FIBRE_ENABLE_CLIENT + Callback on_found_object_; + Callback on_lost_object_; + std::unordered_map root_objects_; +#endif +}; + +/** + * @brief Opens and initializes a Fibre context. + * + * If FIBRE_ALLOW_HEAP=0 only one Fibre context can be open at a time. + * + * @returns: A non-null pointer on success, null otherwise. + */ +Context* open(EventLoop* event_loop); + +void close(Context*); + +/** + * @brief Launches an event loop on the current thread. + * + * This function returns when the event loop becomes empty. + * + * If FIBRE_ALLOW_HEAP=0 only one event loop can be running at a time. + * + * This function returns false if Fibre was compiled with + * FIBRE_ENABLE_EVENT_LOOP=0. + * + * @param on_started: This function is the first event that is placed on the + * event loop. This function usually creates further events, for instance + * by calling open(). + * @returns: true if the event loop ran to completion. False if this function is + * not implemented on this operating system or if another error + * occurred. + */ +bool launch_event_loop(Callback on_started); + +} + +#endif // __FIBRE_HPP \ No newline at end of file diff --git a/Firmware/fibre/cpp/include/fibre/introspection.hpp b/Firmware/fibre-cpp/include/fibre/introspection.hpp similarity index 96% rename from Firmware/fibre/cpp/include/fibre/introspection.hpp rename to Firmware/fibre-cpp/include/fibre/introspection.hpp index f7e43f684..aae15ff0a 100644 --- a/Firmware/fibre/cpp/include/fibre/introspection.hpp +++ b/Firmware/fibre-cpp/include/fibre/introspection.hpp @@ -10,7 +10,7 @@ class TypeInfo; class Introspectable; -using introspectable_storage_t = std::aligned_storage<16, 4>::type; +using introspectable_storage_t = std::aligned_storage<4 * sizeof(uintptr_t), sizeof(uintptr_t)>::type; struct PropertyInfo { const char * name; @@ -116,11 +116,11 @@ class Introspectable { }; template T& TypeInfo::as(Introspectable& obj) { - static_assert(sizeof(T) <= sizeof(obj.storage_)); + static_assert(sizeof(T) <= sizeof(obj.storage_), "invalid size"); return *(T*)&obj.storage_; } template const T& TypeInfo::as(const Introspectable& obj) { - static_assert(sizeof(T) <= sizeof(obj.storage_)); + static_assert(sizeof(T) <= sizeof(obj.storage_), "invalid size"); return *(const T*)&obj.storage_; } template Introspectable TypeInfo::make_introspectable(T obj, const TypeInfo* type_info) { @@ -191,7 +191,7 @@ struct FibrePropertyTypeInfo> : FloatSettableTypeInfo, StringConvert } bool set_string(const Introspectable& obj, char* buffer, size_t length) const override { - maybe_underlying_type_t value; + maybe_underlying_type_t value{}; if (!from_string(buffer, length, &value, 0)) { return false; } @@ -200,7 +200,7 @@ struct FibrePropertyTypeInfo> : FloatSettableTypeInfo, StringConvert } bool set_float(const Introspectable& obj, float val) const override { - maybe_underlying_type_t value; + maybe_underlying_type_t value{}; if (!conversion::set_from_float(val, &value)) { return false; } diff --git a/Firmware/fibre-cpp/include/fibre/libfibre.h b/Firmware/fibre-cpp/include/fibre/libfibre.h new file mode 100644 index 000000000..55bf97b2a --- /dev/null +++ b/Firmware/fibre-cpp/include/fibre/libfibre.h @@ -0,0 +1,603 @@ +/** + * @brief Fibre C library + * + * The library is fully asynchronous and runs on an application-managed event + * loop. This integration happens with the call to libfibre_open(), where the + * application must pass a couple of functions that libfibre will use to put + * tasks on the event loop. + * + * Some general things to note: + * - None of the library's functions are blocking. + * - None of the library's functions can be expected to be thread-safe, they + * should not be invoked from any other thread than the one that runs the + * event loop. + * - Callbacks that the user passes to a libfibre function are always executed + * on the event loop thread. + * - All of the library's functions can be expected reentry-safe. That means + * you can call into any libfibre function from any callback handler that + * libfibre invokes. + */ + +#ifndef __LIBFIBRE_H +#define __LIBFIBRE_H + +#include +#include + +#if defined(_MSC_VER) +# define DLL_EXPORT __declspec(dllexport) +# define DLL_IMPORT __declspec(dllimport) +#elif defined(__GNUC__) +# define DLL_EXPORT __attribute__((visibility("default"))) +# define DLL_IMPORT +# if __GNUC__ > 4 +# define DLL_LOCAL __attribute__((visibility("hidden"))) +# else +# define DLL_LOCAL +# endif +#else +# error("Don't know how to export shared object libraries") +#endif + +#ifdef FIBRE_COMPILE +# ifndef FIBRE_PUBLIC +# define FIBRE_PUBLIC DLL_EXPORT +# endif +# define FIBRE_PRIVATE DLL_LOCAL +#else +# define FIBRE_PUBLIC DLL_IMPORT +#endif + + +#define FIBRE_PRIVATE DLL_LOCAL + +#ifdef __cplusplus +extern "C" { +#endif + +struct LibFibreCtx; +struct LibFibreDiscoveryCtx; +struct LibFibreCallContext; +struct LibFibreObject; +struct LibFibreInterface; +struct LibFibreFunction; +struct LibFibreAttribute; +struct LibFibreTxStream; +struct LibFibreRxStream; +struct LibFibreDomain; + +// This enum must remain identical to fibre::Status. +enum LibFibreStatus { + kFibreOk, + kFibreBusy, // libfibre + * Client Application <==== (tx_end, rx_end, status) ===== libfibre + * + * These tuples are exchanged through the input/output arguments of + * libfibre_call() or libfibre_call()'s callback. + * + * If during an ongoing call either of the two parties is unable to respond + * immediately it responds with kFibreBusy and will thus get the responsibility + * to resume the call when able. kFibreCancelled can be issued by either party + * at any time. + * + * Each party must make progress during every control transfer to the other + * party. + * + * For the application this means for every call to libfibre_call() and every + * return from libfibre_call()'s callback the arguments passed from application + * to libfibre must satisfy at least one of the following: + * + * - The call handle is NULL + * - tx_len is non-zero + * - rx_len is non-zero + * - The status is different from kFibreOk + * + * For libfibre this means every for return from libfibre_call() and every + * call to libfibre_call()'s callback the arguments passed from libfibre to + * application satisfy at least one of the following: + * + * - tx_end is larger than the corresponding tx_buf + * - rx_end is larger than the corresponding rx_buf + * - The status is different from kFibreOk + * + * @param func: A function handle that was obtained in the on_function_added() + * callback of libfibre_subscribe_to_interface(). + * @param handle: The variable being pointed to by this argument identifies the + * coroutine call. If the variable is NULL it will be set to a new opaque + * handle. If the variable is not NULL the active function call is + * continued or cancelled (depending on status). + * @param tx_buf: The buffer to transmit. If libfibre_call() returns kFibreBusy + * then this buffer must remain valid until `callback` is invoked. + * Otherwise it can be freed immediately after this call. + * @param tx_len: Length of tx_buf. Must be zero if tx_buf is NULL. + * @param rx_buf: The buffer into which the received data should be written. If + * libfibre_call() returns kFibreBusy then this buffer must remain + * allocated until `callback` is invoked. Otherwise it can be freed + * immediately after this call. + * @param rx_len: Length of rx_buf. Must be zero if rx_buf is NULL. + * @param tx_end: End of the range of data that was accepted by libfibre. This + * is always in the interval [tx_buf, tx_buf + tx_len] unless + * libfibre_call() returns kFibreBusy, in which case this is NULL. + * This value does not give any delivery guarantees. + * @param rx_end: End of the range of data that was returned by libfibre. This + * is always in the interval [rx_buf, rx_buf + rx_len] unless + * libfibre_call() returns kFibreBusy, in which case this is NULL. + * @param callback: Will be invoked eventually if and only if libfibre_call() + * returns kFibreBusy. This callback is never invoked from inside + * libfibre_call(). + * @param cb_ctx: An opaque application-defined handle that gets passed to + * `callback`. + * + * @retval kFibreOk: libfibre accepted some or all of the tx_buf or filled some + * or all of the rx_buf with data and can immediately accept more TX + * data or provide more RX data. + * @retval kFibreBusy: libfibre will complete the request asynchronously by + * calling `callback`. If this value is returned, then the application + * must not invoke libfibre_call() on the same call handle again until + * `callback` is invoked except for cancelling the call with a status + * of `kFibreCancelled`. + * @retval kFibreClosed: the remote server completed the call and will not + * accept or return any more data on this call. The application must not + * pass the closed call context handle to libfibre_call() anymore. + * @retval kFibreCancelled: the application's cancellation request was honored + * or the remote server cancelled the call. The application must not + * pass the cancelled call context handle to libfibre_call() anymore. + */ +FIBRE_PUBLIC LibFibreStatus libfibre_call(LibFibreFunction* func, LibFibreCallContext** handle, + LibFibreStatus status, + const unsigned char* tx_buf, size_t tx_len, + unsigned char* rx_buf, size_t rx_len, + const unsigned char** tx_end, + unsigned char** rx_end, + libfibre_call_cb_t callback, void* cb_ctx); + +/** + * @brief Starts sending data on the specified TX stream. + * + * The TX operation must be considered in progress until the on_completed + * callback is called. Until then the application must not start another TX + * operation on the same stream. In the meantime the application can call + * libfibre_cancel_tx() at any time to abort the operation. + * + * @param tx_stream: The stream on which to send data. + * @param tx_buf: The buffer to transmit. Must remain valid until the operation + * completes. + * @param tx_len: Length of tx_buf. + * @param on_completed: Called when the operation completes, whether successful + * or not. + * @param ctx: Arbitrary user data passed to the on_completed callback. + */ +FIBRE_PUBLIC void libfibre_start_tx(LibFibreTxStream* tx_stream, const uint8_t* tx_buf, size_t tx_len, on_tx_completed_cb_t on_completed, void* ctx); + +/** + * @brief Cancels an ongoing TX operation. + * + * Must only be called if there is actually a TX operation in progress for which + * cancellation has not yet been requested. + * The application must still wait for the on_complete callback to be called + * before the operation can be considered finished. The completion callback may + * be called with kFibreCancelled or any other status. + * + * TODO: specify if streams can be restarted (current doc of on_tx_completed_cb_t implies no) + * + * @param tx_stream: The TX stream on which to cancel the ongoing TX operation. + */ +FIBRE_PUBLIC void libfibre_cancel_tx(LibFibreTxStream* tx_stream); + +/** + * @brief Permanently close TX stream. + * + * Must not be called while a transfer is ongoing. + */ +FIBRE_PUBLIC void libfibre_close_tx(LibFibreTxStream* tx_stream, LibFibreStatus status); + +/** + * @brief Starts receiving data on the specified RX stream. + * + * The RX operation must be considered in progress until the on_completed + * callback is called. Until then the application must not start another RX + * operation on the same stream. In the meantime the application can call + * libfibre_cancel_rx() at any time to abort the operation. + * + * @param rx_stream: The stream on which to receive data. + * @param rx_buf: The buffer to receive to. Must remain valid until the + * operation completes. + * @param rx_len: Length of rx_buf. + * @param on_completed: Called when the operation completes, whether successful + * or not. + * @param ctx: Arbitrary user data passed to the on_completed callback. + */ +FIBRE_PUBLIC void libfibre_start_rx(LibFibreRxStream* rx_stream, uint8_t* rx_buf, size_t rx_len, on_rx_completed_cb_t on_completed, void* ctx); + +/** + * @brief Cancels an ongoing RX operation. + * + * Must only be called if there is actually a RX operation in progress for which + * cancellation has not yet been requested. + * The application must still wait for the on_complete callback to be called + * before the operation can be considered finished. The completion callback may + * be called with kFibreCancelled or any other status. + * + * TODO: specify if streams can be restarted (current doc of on_rx_completed_cb_t implies no) + * + * @param rx_stream: The RX stream on which to cancel the ongoing RX operation. + */ +FIBRE_PUBLIC void libfibre_cancel_rx(LibFibreRxStream* rx_stream); + +/** + * @brief Permanently close RX stream. + * + * Must not be called while a transfer is ongoing. + */ +FIBRE_PUBLIC void libfibre_close_rx(LibFibreRxStream* rx_stream, LibFibreStatus status); + +#ifdef __cplusplus +} +#endif + +#endif // __LIBFIBRE_H \ No newline at end of file diff --git a/Firmware/fibre/cpp/include/fibre/simple_serdes.hpp b/Firmware/fibre-cpp/include/fibre/simple_serdes.hpp similarity index 57% rename from Firmware/fibre/cpp/include/fibre/simple_serdes.hpp rename to Firmware/fibre-cpp/include/fibre/simple_serdes.hpp index 32c09f271..c3abec60c 100644 --- a/Firmware/fibre/cpp/include/fibre/simple_serdes.hpp +++ b/Firmware/fibre-cpp/include/fibre/simple_serdes.hpp @@ -1,8 +1,11 @@ #ifndef __FIBRE_SIMPLE_SERDES #define __FIBRE_SIMPLE_SERDES -//#include "stream.hpp" - +#include "cpp_utils.hpp" +#include "limits.h" +#include // TODO: make C++11 backport of this +#include +#include template struct SimpleSerializer; @@ -73,5 +76,52 @@ inline bool write_le(T value, fibre::bufptr_t* buffer) { return LittleEndianSerializer::write(value, &buffer->begin(), buffer->end()); } +template::value>> +inline size_t write_le(T value, uint8_t* buffer){ + //TODO: add static_assert that this is still a little endian machine + std::memcpy(&buffer[0], &value, sizeof(value)); + return sizeof(value); +} + +template +typename std::enable_if_t::value, size_t> +write_le(T value, uint8_t* buffer) { + return write_le>(value, buffer); +} + +template<> +inline size_t write_le(float value, uint8_t* buffer) { + static_assert(CHAR_BIT * sizeof(float) == 32, "32 bit floating point expected"); + static_assert(std::numeric_limits::is_iec559, "IEEE 754 floating point expected"); + uint32_t value_as_uint32; + std::memcpy(&value_as_uint32, &value, sizeof(uint32_t)); + return write_le(value_as_uint32, buffer); +} + +template +inline size_t read_le(T* value, const uint8_t* buffer){ + // TODO: add static_assert that this is still a little endian machine + std::memcpy(value, buffer, sizeof(*value)); + return sizeof(*value); +} + +template<> +inline size_t read_le(float* value, const uint8_t* buffer) { + static_assert(CHAR_BIT * sizeof(float) == 32, "32 bit floating point expected"); + static_assert(std::numeric_limits::is_iec559, "IEEE 754 floating point expected"); + return read_le(reinterpret_cast(value), buffer); +} + +// @brief Reads a value of type T from the buffer. +// @param buffer Pointer to the buffer to be read. The pointer is updated by the number of bytes that were read. +// @param length The number of available bytes in buffer. This value is updated to subtract the bytes that were read. +template +static inline T read_le(const uint8_t** buffer, size_t* length) { + T result; + size_t cnt = read_le(&result, *buffer); + *buffer += cnt; + *length -= cnt; + return result; +} #endif \ No newline at end of file diff --git a/Firmware/fibre-cpp/include/fibre/status.hpp b/Firmware/fibre-cpp/include/fibre/status.hpp new file mode 100644 index 000000000..c6a61bd72 --- /dev/null +++ b/Firmware/fibre-cpp/include/fibre/status.hpp @@ -0,0 +1,20 @@ +#ifndef __FIBRE_STATUS_HPP +#define __FIBRE_STATUS_HPP + +namespace fibre { + +enum Status { + kFibreOk, + kFibreBusy, // #pragma GCC push_options #pragma GCC optimize ("s") @@ -19,12 +25,12 @@ void [%- elif func.out | length == 1 -%] [[(func.out.values() | first).type.c_name]] [%- else -%] -[% for arg in func.out.values() %][[arg.type]][[', ' if not loop.last]][% endfor %] +std::tuple<[% for arg in func.out.values() %][[arg.type.c_name]][[', ' if not loop.last]][% endfor %]> [%- endif -%] [%- endmacro %] [%- macro render_interface(intf) %] -class [[intf.name | to_pascal_case]]Intf { +class [[intf.name | to_pascal_case]]Intf[% if intf.implements %] :[%- for base_intf in intf.implements %] public [[base_intf.c_name]][% endfor %][% endif %] { public: [%- for intf in intf.interfaces -%] [[render_interface(intf) | indent(4)]] @@ -32,7 +38,7 @@ public: [%- for enum in intf.enums %] enum [[enum.name | to_pascal_case]] { [%- for k, value in enum['values'].items() %] - [[((enum.name + k) | to_macro_case).ljust(32)]] = [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %], + [[((enum.name | to_macro_case) + "_" + (k | to_macro_case)).ljust(32)]] = [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %], [%- endfor %] }; [%- endfor %] @@ -92,3 +98,5 @@ inline [[enum.c_name]] operator ~ ([[enum.c_name]] a) { return static_cast<[[enu #pragma GCC pop_options + +#endif // __FIBRE_INTERFACES_HPP \ No newline at end of file diff --git a/Firmware/fibre-cpp/legacy_object_client.cpp b/Firmware/fibre-cpp/legacy_object_client.cpp new file mode 100644 index 000000000..d61ad216d --- /dev/null +++ b/Firmware/fibre-cpp/legacy_object_client.cpp @@ -0,0 +1,695 @@ + +#include "legacy_object_client.hpp" +#include "legacy_protocol.hpp" +#include +#include "logging.hpp" +#include "print_utils.hpp" +#include "crc.hpp" +#include +#include + +DEFINE_LOG_TOPIC(LEGACY_OBJ); +USE_LOG_TOPIC(LEGACY_OBJ); + +using namespace fibre; + +struct json_error { + const char* ptr; + std::string str; +}; + +struct json_value; +using json_list = std::vector>; +using json_dict = std::vector, std::shared_ptr>>; +using json_value_variant = std::variant; + +struct json_value : json_value_variant { + //json_value(const json_value_variant& v) : json_value_variant{v} {} + template json_value(T&& arg) : json_value_variant{std::forward(arg)} {} + //json_value_variant v; +}; + +// helper functions +bool json_is_str(json_value val) { return val.index() == 0; } +bool json_is_int(json_value val) { return val.index() == 1; } +bool json_is_list(json_value val) { return val.index() == 2; } +bool json_is_dict(json_value val) { return val.index() == 3; } +bool json_is_err(json_value val) { return val.index() == 4; } +std::string json_as_str(json_value val) { return std::get<0>(val); } +int json_as_int(json_value val) { return std::get<1>(val); } +json_list json_as_list(json_value val) { return std::get<2>(val); } +json_dict json_as_dict(json_value val) { return std::get<3>(val); } +json_error json_as_err(json_value val) { return std::get<4>(val); } + +json_value json_make_error(const char* ptr, std::string str) { + return {json_error{ptr, str}}; +} + + +void json_skip_whitespace(const char** begin, const char* end) { + while (*begin < end && std::isspace(**begin)) { + (*begin)++; + } +} + +bool json_comp(const char* begin, const char* end, char c) { + return begin < end && *begin == c; +} + +json_value json_parse(const char** begin, const char* end) { + // skip whitespace + + if (*begin >= end) { + return json_make_error(*begin, "expected value but got EOF"); + } + + if (json_comp(*begin, end, '{')) { + // parse dict + (*begin)++; // consume leading '{' + json_dict dict; + bool expect_comma = false; + + json_skip_whitespace(begin, end); + while (!json_comp(*begin, end, '}')) { + if (expect_comma) { + if (!json_comp(*begin, end, ',')) { + return json_make_error(*begin, "expected ',' or '}'"); + } + (*begin)++; // consume comma + json_skip_whitespace(begin, end); + } + expect_comma = true; + + // Parse key-value pair + json_value key = json_parse(begin, end); + if (json_is_err(key)) return key; + json_skip_whitespace(begin, end); + if (!json_comp(*begin, end, ':')) { + return json_make_error(*begin, "expected :"); + } + (*begin)++; + json_value val = json_parse(begin, end); + if (json_is_err(val)) return val; + dict.push_back({std::make_shared(key), std::make_shared(val)}); + + json_skip_whitespace(begin, end); + } + + (*begin)++; + return {dict}; + + } else if (json_comp(*begin, end, '[')) { + // parse list + (*begin)++; // consume leading '[' + json_list list; + bool expect_comma = false; + + json_skip_whitespace(begin, end); + while (!json_comp(*begin, end, ']')) { + if (expect_comma) { + if (!json_comp(*begin, end, ',')) { + return json_make_error(*begin, "expected ',' or ']'"); + } + (*begin)++; // consume comma + json_skip_whitespace(begin, end); + } + expect_comma = true; + + // Parse item + json_value val = json_parse(begin, end); + if (json_is_err(val)) return val; + list.push_back(std::make_shared(val)); + + json_skip_whitespace(begin, end); + } + + (*begin)++; // consume trailing ']' + return {list}; + + } else if (json_comp(*begin, end, '"')) { + // parse string + (*begin)++; // consume leading '"' + std::string str; + + while (!json_comp(*begin, end, '"')) { + if (*begin >= end) { + return json_make_error(*begin, "expected '\"' but got EOF"); + } + if (json_comp(*begin, end, '\\')) { + return json_make_error(*begin, "escaped strings not supported"); + } + str.push_back(**begin); + (*begin)++; + } + + (*begin)++; // consume trailing '"' + return {str}; + + } else if (std::isdigit(**begin)) { + // parse int + + std::string str; + while (*begin < end && std::isdigit(**begin)) { + str.push_back(**begin); + (*begin)++; + } + + return {std::stoi(str)}; // note: this can throw an exception if the int is too long + + } else { + return json_make_error(*begin, "unexpected character '" + std::string(*begin, *begin + 1) + "'"); + } +} + +json_value json_dict_find(json_dict dict, std::string key) { + auto it = std::find_if(dict.begin(), dict.end(), + [&](std::pair, std::shared_ptr>& kv){ + return json_is_str(*kv.first) && json_as_str(*kv.first) == key; + }); + return (it == dict.end()) ? json_make_error(nullptr, "key not found") : *it->second; +} + +// not sure if this function exists in the STL +template()(*std::declval()))> +TNum calc_sum(TIt begin, TIt end, TFunc func) { + TNum s = {}; + for (TIt it = begin; it != end; ++it) { + s += func(*it); + } + return s; +} + +std::unordered_map codecs = { + {"bool", 1}, + {"int8", 1}, + {"uint8", 1}, + {"int16", 2}, + {"uint16", 2}, + {"int32", 4}, + {"uint32", 4}, + {"int64", 8}, + {"uint64", 8}, + {"float", 4}, + {"endpoint_ref", 4} +}; + +size_t get_codec_size(std::string codec) { + auto it = codecs.find(codec); + return (it == codecs.end()) ? 0 : it->second; +} + +std::vector parse_arglist(const json_value& list_val) { + std::vector arglist; + + for (auto& arg : json_is_list(list_val) ? json_as_list(list_val) : json_list()) { + if (!json_is_dict(*arg)) { + FIBRE_LOG(W) << "arglist is invalid"; + continue; + } + auto dict = json_as_dict(*arg); + + json_value name_val = json_dict_find(dict, "name"); + json_value id_val = json_dict_find(dict, "id"); + json_value type_val = json_dict_find(dict, "type"); + + if (!json_is_str(name_val) || !json_is_int(id_val) || ((int)(size_t)json_as_int(id_val) != json_as_int(id_val)) || !json_is_str(type_val)) { + FIBRE_LOG(W) << "arglist is invalid"; + continue; + } + + + arglist.push_back({ + json_as_str(name_val), + json_as_str(type_val), + (json_as_str(type_val) == "endpoint_ref") ? "object_ref" : json_as_str(type_val), + get_codec_size(json_as_str(type_val)), + (json_as_str(type_val) == "endpoint_ref") ? sizeof(uintptr_t) : get_codec_size(json_as_str(type_val)), + (size_t)json_as_int(id_val), + }); + } + + return arglist; +} + +void LegacyObjectClient::start(Callback> on_found_root_object, Callback> on_lost_root_object) { + FIBRE_LOG(D) << "start"; + on_found_root_object_ = on_found_root_object; + on_lost_root_object_ = on_lost_root_object; + json_.clear(); + receive_more_json(); +} + +std::shared_ptr LegacyObjectClient::get_property_interfaces(std::string codec, bool write) { + auto& dict = write ? rw_property_interfaces : ro_property_interfaces; + + auto it = dict.find(codec); + if (it != dict.end()) { + return it->second; + } + + auto intf_ptr = std::make_shared(); + dict[codec] = intf_ptr; + FibreInterface& intf = *intf_ptr; + size_t size = get_codec_size(codec); + std::string app_codec = codec == "endpoint_ref" ? "object_ref" : codec; + size_t app_codec_size = codec == "endpoint_ref" ? sizeof(uintptr_t) : size; + + if (!size || !app_codec_size) { + FIBRE_LOG(W) << "unknown size for codec " << codec; + } + + intf.name = std::string{} + "fibre.Property<" + (write ? "readwrite" : "readonly") + " " + codec + ">"; + intf.functions.emplace("read", LegacyFunction{0, nullptr, {}, {{"value", codec, app_codec, size, app_codec_size, 0}}}); + if (write) { + intf.functions.emplace("exchange", LegacyFunction{0, nullptr, {{"newval", codec, app_codec, size, app_codec_size, 0}}, {{"oldval", codec, app_codec, size, app_codec_size, 0}}}); + } + + return intf_ptr; +} + +std::shared_ptr LegacyObjectClient::load_object(json_value list_val) { + + + if (!json_is_list(list_val)) { + FIBRE_LOG(W) << "interface members must be a list"; + return nullptr; + } + + LegacyObject obj{ + .client = this, + .ep_num = 0, + .intf = std::make_shared(), + .known_to_application = false + }; + auto obj_ptr = std::make_shared(obj); + FibreInterface& intf = *obj_ptr->intf; + + for (auto& item: json_as_list(list_val)) { + if (!json_is_dict(*item)) { + FIBRE_LOG(W) << "expected dict"; + continue; + } + auto dict = json_as_dict(*item); + + json_value type = json_dict_find(dict, "type"); + json_value name_val = json_dict_find(dict, "name"); + std::string name = json_is_str(name_val) ? json_as_str(name_val) : "[anonymous]"; + + if (json_is_str(type) && json_as_str(type) == "object") { + std::shared_ptr subobj = load_object(json_dict_find(dict, "members")); + intf.attributes[name] = {subobj}; + + } else if (json_is_str(type) && json_as_str(type) == "function") { + json_value id = json_dict_find(dict, "id"); + if (!json_is_int(id) || ((int)(size_t)json_as_int(id) != json_as_int(id))) { + continue; + } + intf.functions.emplace(name, LegacyFunction{ + (size_t)json_as_int(id), + obj_ptr.get(), + parse_arglist(json_dict_find(dict, "inputs")), + parse_arglist(json_dict_find(dict, "outputs")) + }); + + } else if (json_is_str(type) && json_as_str(type) == "json") { + // Ignore + + } else if (json_is_str(type)) { + std::string type_str = json_as_str(type); + json_value access = json_dict_find(dict, "access"); + std::string access_str = json_is_str(access) ? json_as_str(access) : "r"; + bool can_write = access_str.find('w') != std::string::npos; + + json_value id = json_dict_find(dict, "id"); + if (!json_is_int(id) || ((int)(size_t)json_as_int(id) != json_as_int(id))) { + continue; + } + + LegacyObject subobj{ + .client = this, + .ep_num = (size_t)json_as_int(id), + .intf = get_property_interfaces(type_str, can_write), + .known_to_application = false + }; + auto subobj_ptr = std::make_shared(subobj); + objects_.push_back(subobj_ptr); + intf.attributes[name] = {subobj_ptr}; + + } else { + FIBRE_LOG(W) << "unsupported codec"; + } + } + + objects_.push_back(obj_ptr); + return obj_ptr; +} + +void LegacyObjectClient::receive_more_json() { + write_le(json_.size(), tx_buf_); + json_.resize(json_.size() + 1024); + bufptr_t rx_buf = {json_.data() + json_.size() - 1024, json_.data() + json_.size()}; + protocol_->start_endpoint_operation(0, tx_buf_, rx_buf, &op_handle_, MEMBER_CB(this, on_received_json)); +} + +void LegacyObjectClient::on_received_json(EndpointOperationResult result) { + // The JSON read operation completed + + op_handle_ = 0; + + if (result.status == kStreamCancelled) { + return; + } else if (result.status == kStreamClosed) { + return; + } else if (result.status != kStreamOk) { + FIBRE_LOG(W) << "JSON read operation failed"; // TODO: add retry logic + return; + } + + size_t n_received = result.rx_end - json_.data() - json_.size() + 1024; + json_.resize(json_.size() - 1024 + n_received); + + if (n_received) { + receive_more_json(); + + } else { + + FIBRE_LOG(D) << "received JSON of length " << json_.size(); + //FIBRE_LOG(D) << "JSON: " << str{json_.data(), json_.data() + json_.size()}; + + const char *begin = reinterpret_cast(json_.data()); + auto val = json_parse(&begin, begin + json_.size()); + + if (json_is_err(val)) { + size_t pos = json_as_err(val).ptr - reinterpret_cast(json_.data()); + FIBRE_LOG(E) << "JSON parsing error: " << json_as_err(val).str << " at position " << pos; + return; + } else if (!json_is_list(val)) { + FIBRE_LOG(E) << "JSON data must be a list"; + return; + } + + FIBRE_LOG(D) << "sucessfully parsed JSON"; + root_obj_ = load_object(val); + json_crc_ = calc_crc16(PROTOCOL_VERSION, json_.data(), json_.size()); + if (root_obj_) { + on_found_root_object_.invoke_and_clear(this, root_obj_); + } + } +} + + +std::optional LegacyFunction::call(void** call_handle, + CallBuffers buffers, + Callback, CallBufferRelease> callback) { + + LegacyCallContext* ctx; + + if (!*call_handle) { + // Instantiate new call + ctx = new LegacyCallContext(); + ctx->func_ = this; + + size_t total_tx_decoded_size = sizeof(uintptr_t); + for (auto& arg: inputs) { + total_tx_decoded_size += arg.app_size; + } + + size_t total_rx_encoded_size = 0; + for (auto& arg: outputs) { + total_rx_encoded_size += arg.protocol_size; + } + + ctx->tx_buf_.resize(total_tx_decoded_size); + ctx->rx_buf_.resize(total_rx_encoded_size); + + *call_handle = ctx; + } else { + // Resume call + ctx = reinterpret_cast(*call_handle); + } + + + std::variant result = buffers; + + // Run endpoint operations for as long as we can do this synchronously. + for (;;) { + auto continuation = ctx->get_next_task(result); + if (continuation.index() == 0) { + return std::get<0>(continuation); + } else if (continuation.index() == 1) { + auto proto_continuation = std::get<1>(continuation); + proto_continuation.client->start_endpoint_operation( + proto_continuation.ep_num, proto_continuation.tx_buf, + proto_continuation.rx_buf, &ctx->op_handle_, + MEMBER_CB(ctx, resume_from_protocol)); + + if (!ctx->ep_result.has_value()) { + ctx->callback = callback; + return std::nullopt; // protocol will resume asynchronously + } + + result = *ctx->ep_result; + ctx->ep_result = std::nullopt; + + // TODO: ensure progress + } else { + return CallBufferRelease{kFibreInternalError, ctx->app_tx_end_, ctx->app_rx_buf_.begin()}; + } + } + + return ctx->resume_from_app(buffers, callback); +} + + +void LegacyCallContext::resume_from_protocol(EndpointOperationResult result) { + if (!callback) { + // No callback configured. This means that this function is being executed + // synchronously from inside LegacyFunction::call(). Set result and return. + ep_result = result; + return; + } + op_handle_ = 0; + + std::variant res = result; + + for (;;) { + auto continuation = get_next_task(res); + if (continuation.index() == 0) { + auto app_result = callback.invoke(std::get<0>(continuation)); + if (!app_result.has_value()) { + return; // app will resume asynchronously + } else if (std::get<0>(continuation).status != kFibreOk) { + if (app_result->status != kFibreClosed || app_result->rx_buf.size() || app_result->tx_buf.size()) { + FIBRE_LOG(W) << "app tried to continue a closed call"; + } + FIBRE_LOG(T) << "closing call"; + return; + } else { + res = *app_result; + } + } else if (continuation.index() == 1) { + auto proto_continuation = std::get<1>(continuation); + proto_continuation.client->start_endpoint_operation( + proto_continuation.ep_num, proto_continuation.tx_buf, + proto_continuation.rx_buf, &op_handle_, + MEMBER_CB(this, resume_from_protocol)); + return; // protocol will return asynchronously + } else { + callback.invoke({kFibreInternalError, app_tx_end_, app_rx_buf_.begin()}); + return; + } + } +} + +bool LegacyObjectClient::transcode(cbufptr_t src, bufptr_t dst, std::string src_codec, std::string dst_codec) { + if (src_codec == "object_ref" && dst_codec == "endpoint_ref") { + if (src.size() < sizeof(uintptr_t) || dst.size() < 4) { + return false; + } + + uintptr_t val = *reinterpret_cast(src.begin()); + LegacyObject* obj = reinterpret_cast(val); + write_le(obj ? obj->ep_num : 0, &dst); + write_le(obj ? obj->client->json_crc_ : 0, &dst); + + } else if (src_codec == "endpoint_ref" && dst_codec == "object_ref") { + if (src.size() < 4 || dst.size() < sizeof(uintptr_t)) { + return false; + } + + uint16_t ep_num = *read_le(&src); + uint16_t json_crc = *read_le(&src); + + LegacyObject* obj_ptr = nullptr; + + if (ep_num && json_crc == json_crc_) { + for (auto& known_obj: objects_) { + if (known_obj->ep_num == ep_num) { + obj_ptr = known_obj.get(); + } + } + } + + FIBRE_LOG(D) << "placing transcoded ptr " << reinterpret_cast(obj_ptr); + *reinterpret_cast(dst.begin()) = reinterpret_cast(obj_ptr); + + } else { + if (src.size() != dst.size()) { + return false; + } + + memcpy(dst.begin(), src.begin(), src.size()); + } + + return true; +} + + +std::variant LegacyCallContext::get_next_task(std::variant continue_from) { + if (progress == 0) { + if (continue_from.index() != 0) { + FIBRE_LOG(E) << "expected continuation from app"; + return InternalError{}; + } + + ResultFromApp result_from_app = std::get<0>(continue_from); + + size_t n_copy = std::min(tx_buf_.size() - tx_pos_, result_from_app.tx_buf.size()); + std::copy_n(result_from_app.tx_buf.begin(), n_copy, tx_buf_.begin() + tx_pos_); + result_from_app.tx_buf = result_from_app.tx_buf.skip(n_copy); + tx_pos_ += n_copy; + + app_tx_end_ = result_from_app.tx_buf.begin(); + app_rx_buf_ = result_from_app.rx_buf; + + if (tx_pos_ < tx_buf_.size()) { + // application specified kFibreOk? => return kFibreOk + // application specified kFibreClosed? => return kFibreClosed + return ContinueWithApp{result_from_app.status, app_tx_end_, app_rx_buf_.begin()}; + } + + } else if (progress <= func_->inputs.size() + 1 + func_->outputs.size()) { + if (continue_from.index() != 1) { + FIBRE_LOG(E) << "expected continuation from protocol"; + return InternalError{}; + } + + ResultFromProtocol result_from_protocol = std::get<1>(continue_from); + + if (result_from_protocol.status == kStreamClosed) { + return ContinueWithApp{kFibreHostUnreachable, app_tx_end_, app_rx_buf_.begin()}; + } else if (result_from_protocol.status != kStreamOk) { + FIBRE_LOG(W) << "protocol failed with " << result_from_protocol.status << " - propagating error to application"; + return ContinueWithApp{kFibreHostUnreachable, app_tx_end_, app_rx_buf_.begin()}; + } + + tx_pos_ = result_from_protocol.tx_end - tx_buf_.data(); + if (result_from_protocol.rx_end) { + rx_pos_ = result_from_protocol.rx_end - rx_buf_.data(); + } + + } else if (progress == func_->inputs.size() + 2 + func_->outputs.size()) { + if (continue_from.index() != 0) { + FIBRE_LOG(E) << "expected continuation from app"; + return InternalError{}; + } + + ResultFromApp result_from_app = std::get<0>(continue_from); + + if (result_from_app.status != kFibreOk && result_from_app.status != kFibreClosed) { + FIBRE_LOG(W) << "application failed with " << result_from_app.status << " - dropping this call"; + return InternalError{}; + } + + app_tx_end_ = result_from_app.tx_buf.begin(); + app_rx_buf_ = result_from_app.rx_buf; + } + + if (progress == 0) { + // Transcode from application codec to protocol codec + + obj_ = *reinterpret_cast(tx_buf_.data()); + FIBRE_LOG(T) << "object is " << as_hex(reinterpret_cast(obj_)); + FIBRE_LOG(T) << "tx buf is " << as_hex(cbufptr_t{tx_buf_}); + + std::vector transcoded; + size_t transcoded_size = calc_sum(func_->inputs.begin(), func_->inputs.end(), + [](LegacyFibreArg& arg) { return arg.protocol_size; }); + FIBRE_LOG(T) << "transcoding " << func_->inputs.size() << " inputs from " << tx_buf_.size() << " B to " << transcoded_size << " B"; + transcoded.resize(transcoded_size); + + tx_pos_ = sizeof(uintptr_t); + size_t transcoded_pos = 0; + for (auto& arg: func_->inputs) { + if (!obj_->client->transcode({tx_buf_.data() + tx_pos_, arg.app_size}, + {transcoded.data() + transcoded_pos, arg.protocol_size}, + arg.app_codec, arg.protocol_codec)) { + return ContinueWithApp{kFibreInternalError, app_tx_end_, app_rx_buf_.begin()}; + } + transcoded_pos += arg.protocol_size; + tx_pos_ += arg.app_size; + } + + tx_buf_ = transcoded; + tx_pos_ = 0; + + } else if (progress == func_->inputs.size() + 1 + func_->outputs.size()) { + // Transcode from protocol codec to application codec + + std::vector transcoded; + for (auto& arg: func_->outputs) { + FIBRE_LOG(T) << "arg size " << arg.app_size; + } + size_t transcoded_size = calc_sum(func_->outputs.begin(), func_->outputs.end(), + [](LegacyFibreArg& arg) { return arg.app_size; }); + FIBRE_LOG(T) << "transcoding " << func_->outputs.size() << " outputs from " << rx_buf_.size() << " B to " << transcoded_size << " B"; + transcoded.resize(transcoded_size); + + rx_pos_ = 0; + size_t transcoded_pos = 0; + for (auto& arg: func_->outputs) { + if (!obj_->client->transcode({rx_buf_.data() + rx_pos_, arg.protocol_size}, + {transcoded.data() + transcoded_pos, arg.app_size}, + arg.protocol_codec, arg.app_codec)) { + return ContinueWithApp{kFibreInternalError, app_tx_end_, app_rx_buf_.begin()}; + } + transcoded_pos += arg.app_size; + rx_pos_ += arg.protocol_size; + } + + rx_buf_ = transcoded; + rx_pos_ = 0; + + FIBRE_LOG(T) << "rx buf is " << as_hex(cbufptr_t{rx_buf_}); + } + + progress++; + + if (progress == 1 && obj_->ep_num) { + // Single Endpoint Function - exchange everything in one go + progress = func_->inputs.size() + 1 + func_->outputs.size(); + return ContinueWithProtocol{obj_->client->protocol_, obj_->ep_num, tx_buf_, rx_buf_}; + + } else if (progress <= func_->inputs.size()) { + // send arg + auto arg = func_->inputs[progress - 1]; + return ContinueWithProtocol{obj_->client->protocol_, arg.ep_num, {tx_buf_.data() + tx_pos_, arg.protocol_size}, {}}; + } else if (progress == func_->inputs.size() + 1) { + // send trigger + return ContinueWithProtocol{obj_->client->protocol_, func_->ep_num, {}, {}}; + } else if (progress <= func_->inputs.size() + 1 + func_->outputs.size()) { + // receive arg + auto arg = func_->outputs[progress - 2 - func_->inputs.size()]; + return ContinueWithProtocol{obj_->client->protocol_, arg.ep_num, {}, {rx_buf_.data() + rx_pos_, arg.protocol_size}}; + } else if (progress == func_->inputs.size() + 2 + func_->outputs.size()) { + // return data to application + size_t n_copy = std::min(rx_buf_.size() - rx_pos_, app_rx_buf_.size()); + std::copy_n(rx_buf_.data() + rx_pos_, n_copy, app_rx_buf_.begin()); + app_rx_buf_ = app_rx_buf_.skip(n_copy); + rx_pos_ += n_copy; + return ContinueWithApp{rx_pos_ == rx_buf_.size() ? kFibreClosed : kFibreOk, app_tx_end_, app_rx_buf_.begin()}; + } + + return InternalError{}; +} diff --git a/Firmware/fibre-cpp/legacy_object_client.hpp b/Firmware/fibre-cpp/legacy_object_client.hpp new file mode 100644 index 000000000..4f8ba9542 --- /dev/null +++ b/Firmware/fibre-cpp/legacy_object_client.hpp @@ -0,0 +1,152 @@ +#ifndef __FIBRE_LEGACY_OBJECT_MODEL_HPP +#define __FIBRE_LEGACY_OBJECT_MODEL_HPP + +#include +#include +#include +#include +#include +#include +#include // std::variant and std::optional C++ backport +#include + +struct json_value; + +namespace fibre { + +struct EndpointOperationResult { + StreamStatus status; + const uint8_t* tx_end; + uint8_t* rx_end; +}; + +// Lower 16 bits are the seqno. Upper 16 bits are all 1 for valid handles +// (such that seqno 0 doesn't cause the handle to be 0) +using EndpointOperationHandle = uint32_t; + +struct LegacyProtocolPacketBased; + +struct LegacyFibreArg { + std::string name; + std::string protocol_codec; + std::string app_codec; + size_t protocol_size; + size_t app_size; + size_t ep_num; +}; + +struct LegacyObject; + +struct LegacyFunction : Function { + LegacyFunction(std::vector inputs, std::vector outputs) + : ep_num(0), obj_(nullptr), inputs(inputs), outputs(outputs) {} + LegacyFunction(size_t ep_num, LegacyObject* obj, std::vector inputs, std::vector outputs) + : ep_num(ep_num), obj_(obj), inputs(inputs), outputs(outputs) {} + + std::optional + call(void**, CallBuffers, Callback, CallBufferRelease>) final; + + size_t ep_num; // 0 for property read/write/exchange functions + LegacyObject* obj_; // null for property read/write/exchange functions (all other functions are associated with one object only) + std::vector inputs; + std::vector outputs; +}; + +struct FibreInterface; +class LegacyObjectClient; + +struct LegacyFibreAttribute { + std::shared_ptr object; +}; + +struct FibreInterface { + std::string name; + std::unordered_map functions; + std::unordered_map attributes; +}; + +struct LegacyObject { + LegacyObjectClient* client; + size_t ep_num; + std::shared_ptr intf; + bool known_to_application; +}; + +struct LegacyCallContext { + LegacyFunction* func_; + + size_t progress = 0; //!< 0: expecting more tx data + //!< [1...n_inputs]: endpoint operations for sending inputs + //!< n_inputs + 1: trigger endpoint operation + //!< [n_inputs + 2, n_inputs + 2 + n_outputs]: endpoint operations for receiving outputs + //!< n_inputs + 3 + n_outputs: reporting outputs to application + + EndpointOperationHandle op_handle_ = 0; + + std::vector tx_buf_; + size_t tx_pos_ = 0; + std::vector rx_buf_; + size_t rx_pos_ = 0; + + const uint8_t* app_tx_end_; + bufptr_t app_rx_buf_; + + Callback, CallBufferRelease> callback; + std::optional ep_result; + + LegacyObject* obj_; + + std::optional + resume_from_app(CallBuffers, Callback, CallBufferRelease>); + + void resume_from_protocol(EndpointOperationResult result); + + struct ContinueWithProtocol { + LegacyProtocolPacketBased* client; + size_t ep_num; + cbufptr_t tx_buf; + bufptr_t rx_buf; + }; + + using ContinueWithApp = CallBufferRelease; + using ResultFromProtocol = EndpointOperationResult; + using ResultFromApp = CallBuffers; + struct InternalError {}; + + // Returns control either to the application or to the next endpoint operation + std::variant get_next_task(std::variant continue_from); +}; + +class LegacyObjectClient { +public: + LegacyObjectClient(LegacyProtocolPacketBased* protocol) : protocol_(protocol) {} + + void start(Callback> on_found_root_object, Callback> on_lost_root_object); + bool transcode(cbufptr_t src, bufptr_t dst, std::string src_codec, std::string dst_codec); + + // For direct access by LegacyProtocolPacketBased and libfibre.cpp + uint16_t json_crc_ = 0; + Callback> on_lost_root_object_; + std::shared_ptr root_obj_; + std::vector> objects_; + void* user_data_; // used by libfibre to store the libfibre context pointer + LegacyProtocolPacketBased* protocol_; + +private: + std::shared_ptr get_property_interfaces(std::string codec, bool write); + std::shared_ptr load_object(json_value list_val); + void receive_more_json(); + void on_received_json(EndpointOperationResult result); + + Callback> on_found_root_object_; + uint8_t tx_buf_[4] = {0xff, 0xff, 0xff, 0xff}; + EndpointOperationHandle op_handle_ = 0; + std::vector json_; + //std::vector pending_calls_; + std::unordered_map> rw_property_interfaces; + std::unordered_map> ro_property_interfaces; +}; + +} + +#endif // __FIBRE_LEGACY_OBJECT_MODEL_HPP \ No newline at end of file diff --git a/Firmware/fibre-cpp/legacy_protocol.cpp b/Firmware/fibre-cpp/legacy_protocol.cpp new file mode 100644 index 000000000..7afd13711 --- /dev/null +++ b/Firmware/fibre-cpp/legacy_protocol.cpp @@ -0,0 +1,567 @@ + + +#include "legacy_protocol.hpp" + +#include "protocol.hpp" +#include "crc.hpp" +#include "logging.hpp" +#include "print_utils.hpp" +#include +#include +#include +#include + +DEFINE_LOG_TOPIC(LEGACY_PROTOCOL); +USE_LOG_TOPIC(LEGACY_PROTOCOL); + +using namespace fibre; + + +/* PacketWrapper -------------------------------------------------------------*/ + +void PacketWrapper::start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) { + if (handle) { + *handle = reinterpret_cast(this); + } + + if (state_ != kStateIdle) { + completer.invoke({kStreamError, buffer.begin()}); + } + + // TODO: support buffer size >= 128 + if (buffer.size() >= 128) { + completer.invoke({kStreamError, buffer.begin()}); + } + + completer_ = completer; + + header_buf_[0] = CANONICAL_PREFIX; + header_buf_[1] = static_cast(buffer.size()); + header_buf_[2] = calc_crc8(CANONICAL_CRC8_INIT, header_buf_, 2); + + payload_buf_ = buffer; + + uint16_t crc16 = calc_crc16(CANONICAL_CRC16_INIT, buffer.begin(), buffer.size()); + trailer_buf_[0] = (uint8_t)((crc16 >> 8) & 0xff), + trailer_buf_[1] = (uint8_t)((crc16 >> 0) & 0xff); + + state_ = kStateSendingHeader; + expected_tx_end_ = header_buf_ + 3; + tx_channel_->start_write(header_buf_, &inner_transfer_handle_, MEMBER_CB(this, complete)); +} + +void PacketWrapper::cancel_write(TransferHandle transfer_handle) { + state_ = kStateCancelling; + tx_channel_->cancel_write(inner_transfer_handle_); +} + +void PacketWrapper::complete(WriteResult result) { + if (state_ == kStateCancelling) { + state_ = kStateIdle; + completer_.invoke_and_clear({kStreamCancelled, payload_buf_.begin()}); + return; + } + + if (result.status != kStreamOk) { + state_ = kStateIdle; + completer_.invoke_and_clear({result.status, payload_buf_.begin()}); + return; + } + + if (result.end < expected_tx_end_) { + tx_channel_->start_write({result.end, expected_tx_end_}, &inner_transfer_handle_, MEMBER_CB(this, complete)); + return; + } + + if (state_ == kStateSendingHeader) { + state_ = kStateSendingPayload; + expected_tx_end_ = payload_buf_.end(); + tx_channel_->start_write(payload_buf_, &inner_transfer_handle_, MEMBER_CB(this, complete)); + + } else if (state_ == kStateSendingPayload) { + state_ = kStateSendingTrailer; + expected_tx_end_ = trailer_buf_ + 2; + tx_channel_->start_write(trailer_buf_, &inner_transfer_handle_, MEMBER_CB(this, complete)); + + } else if (state_ == kStateSendingTrailer) { + state_ = kStateIdle; + completer_.invoke_and_clear({kStreamOk, payload_buf_.end()}); + } +} + + +/* PacketUnwrapper -----------------------------------------------------------*/ + +void PacketUnwrapper::start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) { + if (handle) { + *handle = reinterpret_cast(this); + } + + if (state_ != kStateIdle) { + completer.invoke({kStreamError, buffer.begin()}); + } + + completer_ = completer; + payload_buf_ = buffer; + + state_ = kStateReceivingHeader; + expected_rx_end_ = rx_buf_ + 3; + rx_channel_->start_read({rx_buf_, expected_rx_end_}, &inner_transfer_handle_, MEMBER_CB(this, complete)); +} + +void PacketUnwrapper::cancel_read(TransferHandle transfer_handle) { + state_ = kStateCancelling; + rx_channel_->cancel_read(inner_transfer_handle_); +} + +void PacketUnwrapper::complete(ReadResult result) { + // All code paths in this function must end with either of these two: + // - rx_channel_->start_read() to bounce back control to the underlying stream + // - safe_complete() to return control to the client + + if (state_ == kStateCancelling) { + state_ = kStateIdle; + completer_.invoke_and_clear({kStreamCancelled, payload_buf_.begin()}); + return; + } + + if (result.status != kStreamOk) { + state_ = kStateIdle; + completer_.invoke_and_clear({result.status, payload_buf_.begin()}); + return; + } + + if (result.end < expected_rx_end_) { + rx_channel_->start_read({result.end, expected_rx_end_}, &inner_transfer_handle_, MEMBER_CB(this, complete)); + return; + } + + if (state_ == kStateReceivingHeader) { + size_t n_discard; + + // Process header + if (rx_buf_[0] != CANONICAL_PREFIX) { + n_discard = 1; + } else if ((rx_buf_[1] & 0x80)) { + n_discard = 2; // TODO: support packets larger than 128 bytes + } else if (calc_crc8(CANONICAL_CRC8_INIT, rx_buf_, 3)) { + n_discard = 3; + } else { + state_ = kStateReceivingPayload; + payload_length_ = std::min(payload_buf_.size(), (size_t)rx_buf_[1]); + expected_rx_end_ = payload_buf_.begin() + payload_length_; + rx_channel_->start_read(payload_buf_.take(payload_length_), &inner_transfer_handle_, MEMBER_CB(this, complete)); + return; + } + + // Header was bad: discard the bad header bytes and receive more + memmove(rx_buf_, rx_buf_ + n_discard, sizeof(rx_buf_) - n_discard); + rx_channel_->start_read(bufptr_t{rx_buf_}.skip(3 - n_discard), &inner_transfer_handle_, MEMBER_CB(this, complete)); + + } else if (state_ == kStateReceivingPayload) { + expected_rx_end_ = rx_buf_ + 2; + state_ = kStateReceivingTrailer; + rx_channel_->start_read({rx_buf_, expected_rx_end_}, &inner_transfer_handle_, MEMBER_CB(this, complete)); + + } else if (state_ == kStateReceivingTrailer) { + uint16_t crc = calc_crc16(CANONICAL_CRC16_INIT, payload_buf_.begin(), payload_length_); + crc = calc_crc16(crc, rx_buf_, 2); + + if (!crc) { + state_ = kStateIdle; + completer_.invoke_and_clear({kStreamOk, payload_buf_.begin() + payload_length_}); + } else { + state_ = kStateReceivingHeader; + expected_rx_end_ = rx_buf_ + 3; + rx_channel_->start_read({rx_buf_, expected_rx_end_}, &inner_transfer_handle_, MEMBER_CB(this, complete)); + } + } +} + + +/* LegacyProtocolPacketBased -------------------------------------------------*/ + +#if FIBRE_ENABLE_CLIENT + +/** + * @brief Starts a remote endpoint operation. + * + * @param endpoint_id: The endpoint ID to invoke the operation on. + * @param tx_buf: The tx_buf to write to the endpoint. Must remain valid until + * the completer is invoked. + * @param rx_length: The desired number of bytes to read from the endpoint. The + * actual returned buffer may be smaller. + * @param completer: The completer that will be notified once the operation + * completes (whether successful or not). + * The buffer given to the completer is only valid if the status is + * kStreamOk and until the completer returns. + * @param handle: The variable pointed to by this argument is set to a handle + * that can be passed to cancel_endpoint_operation() to cancel the + * ongoing operation. If the completer is invoked directly from within + * this function then the handle is not set later than invoking the + * completer. + */ +void LegacyProtocolPacketBased::start_endpoint_operation(uint16_t endpoint_id, cbufptr_t tx_buf, bufptr_t rx_buf, EndpointOperationHandle* handle, Callback callback) { + outbound_seq_no_ = ((outbound_seq_no_ + 1) & 0x7fff); + + EndpointOperation op = { + .seqno = (uint16_t)(outbound_seq_no_ | 0x0080), // FIXME: we hardwire one bit of the seq-no to 1 to avoid conflicts with the ODrive ASCII protocol + .endpoint_id = endpoint_id, + .tx_buf = tx_buf, + .rx_buf = rx_buf, + .callback = callback + }; + + if (handle) { + *handle = op.seqno | 0xffff0000; + } + + if (tx_handle_) { + FIBRE_LOG(D) << "Endpoint operation already in progress. Enqueuing this one."; + + // A TX operation is already in progress. Enqueue this one. + pending_operations_.push_back(op); + return; + } + + start_endpoint_operation(op); +} + +void LegacyProtocolPacketBased::start_endpoint_operation(EndpointOperation op) { + write_le(op.seqno, tx_buf_); + write_le(op.endpoint_id | 0x8000, tx_buf_ + 2); + write_le(op.rx_buf.size(), tx_buf_ + 4); + + size_t mtu = std::min(sizeof(tx_buf_), tx_mtu_); + size_t n_payload = std::min(std::max(mtu, (size_t)8) - 8, op.tx_buf.size()); + + memcpy(tx_buf_ + 6, op.tx_buf.begin(), n_payload); + + uint16_t trailer = (op.endpoint_id & 0x7fff) == 0 ? + PROTOCOL_VERSION : client_.json_crc_; + + write_le(trailer, tx_buf_ + 6 + n_payload); + + expected_acks_[op.seqno] = op; + transmitting_op_ = op.seqno | 0xffff0000; + tx_channel_->start_write(cbufptr_t{tx_buf_}.take(8 + n_payload), &tx_handle_, MEMBER_CB(this, on_write_finished)); +} + + +void LegacyProtocolPacketBased::cancel_endpoint_operation(EndpointOperationHandle handle) { + if (!handle) { + return; + } + + uint16_t seqno = static_cast(handle & 0xffff); + + Callback callback; + const uint8_t* tx_end = nullptr; + uint8_t* rx_end = nullptr; + + auto it0 = std::find_if(pending_operations_.begin(), pending_operations_.end(), [&](EndpointOperation& op) { + return op.seqno == seqno; + }); + + if (it0 != pending_operations_.end()) { + callback = it0->callback; + tx_end = it0->tx_buf.begin(); + rx_end = it0->rx_buf.begin(); + pending_operations_.erase(it0); + } + + auto it1 = expected_acks_.find(seqno); + + if (it1 != expected_acks_.end()) { + callback = it1->second.callback; + tx_end = it1->second.tx_buf.begin(); + rx_end = it1->second.rx_buf.begin(); + expected_acks_.erase(it1); + } + + if (transmitting_op_ == handle) { + // Cancel the TX task because it belongs to the endpoint operation that + // is being cancelled. + tx_channel_->cancel_write(tx_handle_); + } else { + // Either we're waiting for an ack on this operation or it has not yet + // been sent. In both cases we can just complete immediately. + callback.invoke_and_clear({kStreamCancelled, tx_end, rx_end}); + } +} + +#endif + +#if FIBRE_ENABLE_SERVER + +// Returns part of the JSON interface definition. +bool fibre::endpoint0_handler(fibre::cbufptr_t* input_buffer, fibre::bufptr_t* output_buffer) { + // The request must contain a 32 bit integer to specify an offset + std::optional offset = read_le(input_buffer); + + if (!offset.has_value()) { + // Didn't receive any offset + return false; + } else if (*offset == 0xffffffff) { + // If the offset is special value 0xFFFFFFFF, send back the JSON version ID instead + return write_le(json_version_id_, output_buffer); + } else if (*offset >= embedded_json_length) { + // Attempt to read beyond the buffer end - return empty response + return true; + } else { + // Return part of the json file + size_t n_copy = std::min(output_buffer->size(), embedded_json_length - (size_t)*offset); + memcpy(output_buffer->begin(), embedded_json + *offset, n_copy); + *output_buffer = output_buffer->skip(n_copy); + return true; + } +} + +#endif + +void LegacyProtocolPacketBased::on_write_finished(WriteResult result) { + tx_handle_ = 0; + + if (rx_status_ != kStreamOk) { + on_rx_tx_closed(rx_status_); + return; + } + +#if FIBRE_ENABLE_CLIENT + if (transmitting_op_) { + uint16_t seqno = transmitting_op_ & 0xffff; + transmitting_op_ = 0; + + auto it = expected_acks_.find(seqno); + + size_t n_sent = std::max((size_t)(result.end - tx_buf_), (size_t)8) - 8; + it->second.tx_buf = it->second.tx_buf.skip(n_sent); + it->second.tx_done = true; + + if (it->second.rx_done) { + // It's possible that the RX operation completes before the TX operation + auto op = it->second; + expected_acks_.erase(it); + op.callback.invoke_and_clear({kStreamOk, op.tx_buf.begin(), op.rx_buf.begin()}); + } else if (result.status != kStreamOk) { + // If the TX task was a remote endpoint operation but didn't succeed + // we terminate that operation + auto op = it->second; + expected_acks_.erase(it); + op.callback.invoke_and_clear({result.status, result.end, op.rx_buf.begin()}); + } + + if (transmitting_op_) { + return; + } + } +#endif + + // TODO: should we prioritize the server or client side here? + +#if FIBRE_ENABLE_SERVER + if (rx_end_) { + // There is a write operation pending from the server side (i.e. an ack + // for a local endpoint operation). + uint8_t* rx_end = rx_end_; + rx_end_ = nullptr; + on_read_finished({kStreamOk, rx_end}); +#if FIBRE_ENABLE_CLIENT + if (transmitting_op_) { + return; + } +#endif + } +#endif + +#if FIBRE_ENABLE_CLIENT + if (pending_operations_.size() > 0) { + // There is a write operation pending from the client side (i.e. an + // outgoing remote endpoint operation). + EndpointOperation op = pending_operations_[0]; + pending_operations_.erase(pending_operations_.begin()); + start_endpoint_operation(op); + if (transmitting_op_) { + return; + } + } +#endif +} + +void LegacyProtocolPacketBased::on_read_finished(ReadResult result) { + TransferHandle dummy; + + if (result.status == kStreamClosed) { + FIBRE_LOG(D) << "RX stream closed."; + on_rx_closed(kStreamClosed); + return; + } else if (result.status == kStreamCancelled) { + FIBRE_LOG(W) << "RX operation cancelled."; + on_rx_closed(kStreamCancelled); + return; + } else if (result.status != kStreamOk) { + FIBRE_LOG(W) << "RX error. Not restarting."; + // TODO: we should distinguish between permanent and temporary errors. + // If we try to restart after a permanent error we might end up in a + // busy loop. + on_rx_closed(kStreamError); + return; + } + + cbufptr_t rx_buf = cbufptr_t{rx_buf_, result.end}; + //FIBRE_LOG(D) << "got packet of length " << (result.end - rx_buf_) /*<< ": " << as_hex(rx_buf)*/; + + std::optional seq_no = read_le(&rx_buf); + + if (!seq_no.has_value()) { + FIBRE_LOG(W) << "packet too short"; + + } else if (*seq_no & 0x8000) { + +#if FIBRE_ENABLE_CLIENT + + auto it = expected_acks_.find(*seq_no & 0x7fff); + + if (it == expected_acks_.end()) { + FIBRE_LOG(W) << "received unexpected ACK: " << (*seq_no & 0x7fff); + } else { + size_t n_copy = std::min((size_t)(result.end - rx_buf.begin()), it->second.rx_buf.size()); + memcpy(it->second.rx_buf.begin(), rx_buf.begin(), n_copy); + it->second.rx_buf = it->second.rx_buf.skip(n_copy); + it->second.rx_done = true; + FIBRE_LOG(T) << "received ACK: " << (*seq_no & 0x7fff); + + // It's possible that the RX operation completes before the TX operation + if (it->second.tx_done) { + auto op = it->second; + expected_acks_.erase(it); + op.callback.invoke_and_clear({kStreamOk, op.tx_buf.begin(), op.rx_buf.begin()}); + } + } + +#else + FIBRE_LOG(W) << "received ack but client support is not compiled in"; +#endif + + } else { + +#if FIBRE_ENABLE_SERVER + if (rx_buf.size() < 6) { + FIBRE_LOG(W) << "packet too short"; + rx_channel_->start_read(rx_buf_, &dummy, MEMBER_CB(this, on_read_finished)); + return; + } + + // TODO: think about some kind of ordering guarantees + // currently the seq_no is just used to associate a response with a request + + uint16_t endpoint_id = *read_le(&rx_buf); + bool expect_response = endpoint_id & 0x8000; + endpoint_id &= 0x7fff; + + if (expect_response && tx_handle_) { + // The operation expects a response but the output channel is still + // busy. Stop receiving for now. This function will be invoked again + // once the TX operation is finished. + rx_end_ = result.end; + return; + } + + // Verify packet trailer. The expected trailer value depends on the selected endpoint. + // For endpoint 0 this is just the protocol version, for all other endpoints it's a + // CRC over the entire JSON descriptor tree (this may change in future versions). + uint16_t expected_trailer = endpoint_id ? fibre::json_crc_ : PROTOCOL_VERSION; + uint16_t actual_trailer = *(rx_buf.end() - 2) | (*(rx_buf.end() - 1) << 8); + if (expected_trailer != actual_trailer) { + FIBRE_LOG(D) << "trailer mismatch for endpoint " << endpoint_id << ": expected " << as_hex(expected_trailer) << ", got " << as_hex(actual_trailer); + rx_channel_->start_read(rx_buf_, &dummy, MEMBER_CB(this, on_read_finished)); + return; + } + FIBRE_LOG(D) << "trailer ok for endpoint " << endpoint_id; + + // TODO: if more bytes than the MTU were requested, should we abort or just return as much as possible? + + uint16_t expected_response_length = *read_le(&rx_buf); + + // Limit response length according to our local TX buffer size + if (expected_response_length > tx_mtu_ - 2) + expected_response_length = tx_mtu_ - 2; + + fibre::cbufptr_t input_buffer{rx_buf.begin(), rx_buf.end() - 2}; + fibre::bufptr_t output_buffer{tx_buf_ + 2, expected_response_length}; + fibre::endpoint_handler(endpoint_id, &input_buffer, &output_buffer); + + // Send response + if (expect_response) { + size_t actual_response_length = expected_response_length - output_buffer.size() + 2; + write_le(*seq_no | 0x8000, tx_buf_); + + FIBRE_LOG(D) << "send packet: " << as_hex(cbufptr_t{tx_buf_, actual_response_length}); + tx_channel_->start_write({tx_buf_, actual_response_length}, &tx_handle_, MEMBER_CB(this, on_write_finished)); + } +#else + FIBRE_LOG(W) << "received request but server support is not compiled in"; +#endif + } + + rx_channel_->start_read(rx_buf_, &dummy, MEMBER_CB(this, on_read_finished)); +} + +void LegacyProtocolPacketBased::on_rx_closed(StreamStatus status) { + if (tx_handle_) { + // TX operation still in progress - cancel TX operation and defer closing + // the protocol instance until the TX operation has finished. + rx_status_ = status; + tx_channel_->cancel_write(tx_handle_); + } else { + // No TX operation in progress - close protocol instance immediately. + on_rx_tx_closed(status); + } +} + +void LegacyProtocolPacketBased::on_rx_tx_closed(StreamStatus status) { + if (status == kStreamClosed || status == kStreamCancelled) { + // TODO: handle app-initiated cancellation via cancel_endpoint_operation() (currently unused) + status = kStreamError; + } + +#if FIBRE_ENABLE_CLIENT + // Cancel pending endpoint operation + for (auto& op: pending_operations_) { + op.callback.invoke_and_clear({status, op.tx_buf.begin(), op.rx_buf.begin()}); + } + pending_operations_.clear(); + + // Cancel all ongoing endpoint operations + for (auto& item: expected_acks_) { + if (item.second.callback) { + item.second.callback.invoke_and_clear({status, item.second.tx_buf.begin(), item.second.rx_buf.begin()}); + } + } + expected_acks_.clear(); + + // Report that the root object was lost + if (client_.on_lost_root_object_ && client_.root_obj_) { + auto root_obj = client_.root_obj_; + client_.root_obj_ = nullptr; + client_.on_lost_root_object_.invoke(&client_, root_obj); + } +#endif + on_stopped_.invoke_and_clear(this, status); +} + +#if FIBRE_ENABLE_CLIENT +void LegacyProtocolPacketBased::start(Callback> on_found_root_object, Callback> on_lost_root_object, Callback on_stopped) { +#else +void LegacyProtocolPacketBased::start(Callback on_stopped) { +#endif + on_stopped_ = on_stopped; + TransferHandle dummy; + rx_channel_->start_read(rx_buf_, &dummy, MEMBER_CB(this, on_read_finished)); + +#if FIBRE_ENABLE_CLIENT + if (on_stopped_) { + client_.start(on_found_root_object, on_lost_root_object); + } +#endif +} diff --git a/Firmware/fibre-cpp/legacy_protocol.hpp b/Firmware/fibre-cpp/legacy_protocol.hpp new file mode 100644 index 000000000..962f41238 --- /dev/null +++ b/Firmware/fibre-cpp/legacy_protocol.hpp @@ -0,0 +1,173 @@ +#ifndef __FIBRE_LEGACY_PROTOCOL_HPP +#define __FIBRE_LEGACY_PROTOCOL_HPP + +#include + +#ifdef FIBRE_ENABLE_CLIENT +#include "legacy_object_client.hpp" +#include +#include +#include +#endif + +namespace fibre { + +// Default CRC-8 Polynomial: x^8 + x^5 + x^4 + x^2 + x + 1 +// Can protect a 4 byte payload against toggling of up to 5 bits +// source: https://users.ece.cmu.edu/~koopman/crc/index.html +constexpr uint8_t CANONICAL_CRC8_POLYNOMIAL = 0x37; +constexpr uint8_t CANONICAL_CRC8_INIT = 0x42; + +// Default CRC-16 Polynomial: 0x9eb2 x^16 + x^13 + x^12 + x^11 + x^10 + x^8 + x^6 + x^5 + x^2 + 1 +// Can protect a 135 byte payload against toggling of up to 5 bits +// source: https://users.ece.cmu.edu/~koopman/crc/index.html +// Also known as CRC-16-DNP +constexpr uint16_t CANONICAL_CRC16_POLYNOMIAL = 0x3d65; +constexpr uint16_t CANONICAL_CRC16_INIT = 0x1337; + +constexpr uint8_t CANONICAL_PREFIX = 0xAA; + +constexpr uint16_t PROTOCOL_VERSION = 1; + + +class PacketWrapper : public AsyncStreamSink { +public: + PacketWrapper(AsyncStreamSink* tx_channel) + : tx_channel_(tx_channel) {} + + void start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) final; + void cancel_write(TransferHandle transfer_handle) final; + +private: + void complete(WriteResult result); + + AsyncStreamSink* tx_channel_; + TransferHandle inner_transfer_handle_; + uint8_t header_buf_[3]; + uint8_t trailer_buf_[2]; + const uint8_t* expected_tx_end_; + cbufptr_t payload_buf_ = {nullptr, nullptr}; + Callback completer_; + + enum { + kStateIdle, + kStateCancelling, + kStateSendingHeader, + kStateSendingPayload, + kStateSendingTrailer + } state_ = kStateIdle; +}; + + +class PacketUnwrapper : public AsyncStreamSource { +public: + PacketUnwrapper(AsyncStreamSource* rx_channel) + : rx_channel_(rx_channel) {} + + void start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) final; + void cancel_read(TransferHandle transfer_handle) final; + +private: + void complete(ReadResult result); + + AsyncStreamSource* rx_channel_; + TransferHandle inner_transfer_handle_; + uint8_t rx_buf_[3]; + uint8_t* expected_rx_end_; + size_t payload_length_ = 0; + bufptr_t payload_buf_ = {nullptr, nullptr}; + Callback completer_; + + enum { + kStateIdle, + kStateCancelling, + kStateReceivingHeader, + kStateReceivingPayload, + kStateReceivingTrailer + } state_ = kStateIdle; +}; + + +struct LegacyProtocolPacketBased { +public: + LegacyProtocolPacketBased(AsyncStreamSource* rx_channel, AsyncStreamSink* tx_channel, size_t tx_mtu) + : rx_channel_(rx_channel), tx_channel_(tx_channel), tx_mtu_(std::min(tx_mtu, sizeof(tx_buf_))) {} + + AsyncStreamSource* rx_channel_ = nullptr; + AsyncStreamSink* tx_channel_ = nullptr; + size_t tx_mtu_; + uint8_t tx_buf_[128]; + uint8_t rx_buf_[128]; + + TransferHandle tx_handle_ = 0; // non-zero while a TX operation is in progress + uint8_t* rx_end_ = nullptr; // non-zero if an RX operation has finished but wasn't handled yet because the TX channel was busy + StreamStatus rx_status_ = kStreamOk; // non-ok if the RX process was terminated permanently. + // This signals to the TX process that it should close + // the protocol instance at the next possible instant. + + Callback on_stopped_ = nullptr; + +#if FIBRE_ENABLE_CLIENT + void start_endpoint_operation(uint16_t endpoint_id, cbufptr_t tx_buf, bufptr_t rx_buf, EndpointOperationHandle* handle, Callback callback); + void cancel_endpoint_operation(EndpointOperationHandle handle); + + LegacyObjectClient client_{this}; +#endif + +#if FIBRE_ENABLE_CLIENT + void start(Callback> on_found_root_object, Callback> on_lost_root_object, Callback on_stopped); +#else + void start(Callback on_stopped); +#endif + +private: + +#if FIBRE_ENABLE_CLIENT + struct EndpointOperation { + uint16_t seqno; + uint16_t endpoint_id; + cbufptr_t tx_buf; + bool tx_done; + bufptr_t rx_buf; + bool rx_done; + Callback callback; + }; + + void start_endpoint_operation(EndpointOperation op); + + uint16_t outbound_seq_no_ = 0; + std::vector pending_operations_; // operations that are waiting for TX + EndpointOperationHandle transmitting_op_ = 0; // operation that is in TX + std::unordered_map expected_acks_; // operations that are waiting for RX +#endif + + void on_write_finished(WriteResult result); + void on_read_finished(ReadResult result); + void on_rx_closed(StreamStatus status); + void on_rx_tx_closed(StreamStatus status); +}; + + +struct LegacyProtocolStreamBased { +public: + LegacyProtocolStreamBased(AsyncStreamSource* rx_channel, AsyncStreamSink* tx_channel) + : unwrapper_(rx_channel), wrapper_(tx_channel) {} + + +#if FIBRE_ENABLE_CLIENT + void start(Callback> on_found_root_object, Callback> on_lost_root_object, Callback on_stopped) { + inner_protocol_.start(on_found_root_object, on_lost_root_object, on_stopped); + } +#else + void start(Callback on_stopped) { inner_protocol_.start(on_stopped); } +#endif + +private: + PacketUnwrapper unwrapper_; + PacketWrapper wrapper_; + LegacyProtocolPacketBased inner_protocol_{&unwrapper_, &wrapper_, 127}; +}; + +} + +#endif // __FIBRE_LEGACY_PROTOCOL_HPP diff --git a/Firmware/fibre-cpp/libfibre.cpp b/Firmware/fibre-cpp/libfibre.cpp new file mode 100644 index 000000000..c92f00b23 --- /dev/null +++ b/Firmware/fibre-cpp/libfibre.cpp @@ -0,0 +1,586 @@ + +#include +#include +#include "logging.hpp" +#include "print_utils.hpp" +#include "legacy_protocol.hpp" // TODO: remove this include +#include "legacy_object_client.hpp" // TODO: remove this include +#include + +DEFINE_LOG_TOPIC(LIBFIBRE); +USE_LOG_TOPIC(LIBFIBRE); + + +struct LibFibreChannelDiscoveryCtx { + fibre::Domain* domain; +}; + +LibFibreFunction* to_c(fibre::Function* ptr) { + return reinterpret_cast(ptr); +} +fibre::Function* from_c(LibFibreFunction* ptr) { + return reinterpret_cast(ptr); +} +void** from_c(LibFibreCallContext** ptr) { + return reinterpret_cast(ptr); +} +LibFibreDomain* to_c(fibre::Domain* ptr) { + return reinterpret_cast(ptr); +} +fibre::Domain* from_c(LibFibreDomain* ptr) { + return reinterpret_cast(ptr); +} +LibFibreObject* to_c(fibre::Object* ptr) { + return reinterpret_cast(ptr); +} +fibre::Object* from_c(LibFibreObject* ptr) { + return reinterpret_cast(ptr); +} +LibFibreInterface* to_c(fibre::Interface* ptr) { + return reinterpret_cast(ptr); +} +fibre::Interface* from_c(LibFibreInterface* ptr) { + return reinterpret_cast(ptr); +} +LibFibreStatus to_c(fibre::Status status) { + return static_cast(status); +} +fibre::Status from_c(LibFibreStatus status) { + return static_cast(status); +} +LibFibreChannelDiscoveryCtx* to_c(fibre::ChannelDiscoveryContext* ptr) { + return reinterpret_cast(ptr); +} +fibre::ChannelDiscoveryContext* from_c(LibFibreChannelDiscoveryCtx* ptr) { + return reinterpret_cast(ptr); +} + + +static const struct LibFibreVersion libfibre_version = { 0, 1, 4 }; + +class FIBRE_PRIVATE ExternalEventLoop final : public fibre::EventLoop { +public: + ExternalEventLoop(LibFibreEventLoop impl) : impl_(impl) {} + + bool post(fibre::Callback callback) final { + return impl_.post && ((*impl_.post)(callback.get_ptr(), callback.get_ctx()) == 0); + } + + bool register_event(int event_fd, uint32_t events, fibre::Callback callback) final { + return impl_.register_event && ((*impl_.register_event)(event_fd, events, callback.get_ptr(), callback.get_ctx()) == 0); + } + + bool deregister_event(int event_fd) final { + return impl_.deregister_event && ((*impl_.deregister_event)(event_fd) == 0); + } + + struct fibre::EventLoopTimer* call_later(float delay, fibre::Callback callback) final { + if (!impl_.call_later) { + return nullptr; + } + return (fibre::EventLoopTimer*)(*impl_.call_later)(delay, callback.get_ptr(), callback.get_ctx()); + } + + bool cancel_timer(struct fibre::EventLoopTimer* timer) final { + return impl_.cancel_timer && ((*impl_.cancel_timer)((EventLoopTimer*)timer) == 0); + } + +private: + LibFibreEventLoop impl_; +}; + +class ExternalDiscoverer : public fibre::ChannelDiscoverer { + void start_channel_discovery( + fibre::Domain* domain, + const char* specs, size_t specs_len, + fibre::ChannelDiscoveryContext** handle) final; + int stop_channel_discovery(fibre::ChannelDiscoveryContext* handle) final; +public: + on_start_discovery_cb_t on_start_discovery; + on_stop_discovery_cb_t on_stop_discovery; + void* cb_ctx; +}; + + +void ExternalDiscoverer::start_channel_discovery(fibre::Domain* domain, const char* specs, size_t specs_len, fibre::ChannelDiscoveryContext** handle) { + LibFibreChannelDiscoveryCtx* ctx = new LibFibreChannelDiscoveryCtx{}; + if (handle) { + *handle = from_c(ctx); + } + if (on_start_discovery) { + (*on_start_discovery)(cb_ctx, to_c(domain), specs, specs_len); + } +} + +int ExternalDiscoverer::stop_channel_discovery(fibre::ChannelDiscoveryContext* handle) { + LibFibreChannelDiscoveryCtx* ctx = to_c(handle); + if (on_stop_discovery) { + (*on_stop_discovery)(cb_ctx, to_c(ctx->domain)); + } + delete ctx; + return 0; +} + +namespace fibre { + +class AsyncStreamLink final : public AsyncStreamSink, public AsyncStreamSource { +public: + void start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) final; + void cancel_write(TransferHandle transfer_handle) final; + void start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) final; + void cancel_read(TransferHandle transfer_handle) final; + void close(StreamStatus status); + + Callback read_completer_; + bufptr_t read_buf_; + Callback write_completer_; + cbufptr_t write_buf_; +}; + +void AsyncStreamLink::start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) { + if (read_completer_) { + size_t n_copy = std::min(read_buf_.size(), buffer.size()); + memcpy(read_buf_.begin(), buffer.begin(), n_copy); + read_completer_.invoke_and_clear({kStreamOk, read_buf_.begin() + n_copy}); + completer.invoke({kStreamOk, buffer.begin() + n_copy}); + } else { + if (handle) { + *handle = reinterpret_cast(this); + } + write_buf_ = buffer; + write_completer_ = completer; + } +} + +void AsyncStreamLink::cancel_write(TransferHandle transfer_handle) { + write_completer_.invoke_and_clear({kStreamCancelled, write_buf_.begin()}); +} + +void AsyncStreamLink::start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) { + if (write_completer_) { + size_t n_copy = std::min(buffer.size(), write_buf_.size()); + memcpy(buffer.begin(), write_buf_.begin(), n_copy); + write_completer_.invoke_and_clear({kStreamOk, write_buf_.begin() + n_copy}); + completer.invoke({kStreamOk, buffer.begin() + n_copy}); + } else { + if (handle) { + *handle = reinterpret_cast(this); + } + read_buf_ = buffer; + read_completer_ = completer; + } +} + +void AsyncStreamLink::cancel_read(TransferHandle transfer_handle) { + read_completer_.invoke_and_clear({kStreamCancelled, read_buf_.begin()}); +} + +void AsyncStreamLink::close(StreamStatus status) { + write_completer_.invoke_and_clear({status, write_buf_.begin()}); + read_completer_.invoke_and_clear({status, read_buf_.begin()}); +} + +} + +LibFibreStatus convert_status(fibre::StreamStatus status) { + switch (status) { + case fibre::kStreamOk: return kFibreOk; + case fibre::kStreamCancelled: return kFibreCancelled; + case fibre::kStreamClosed: return kFibreClosed; + default: return kFibreInternalError; // TODO: this may not always be appropriate + } +} + +fibre::StreamStatus convert_status(LibFibreStatus status) { + switch (status) { + case kFibreOk: return fibre::kStreamOk; + case kFibreCancelled: return fibre::kStreamCancelled; + case kFibreClosed: return fibre::kStreamClosed; + default: return fibre::kStreamError; // TODO: this may not always be appropriate + } +} + +struct FIBRE_PRIVATE LibFibreCtx { + ExternalEventLoop* event_loop; + //size_t n_discoveries = 0; + fibre::Context* fibre_ctx; + //std::unordered_map> discoverers; +}; + +struct FIBRE_PRIVATE LibFibreDiscoveryCtx { + void on_found_object(fibre::Object* obj, fibre::Interface* intf); + void on_lost_object(fibre::Object* obj); + + on_found_object_cb_t on_found_object_; + on_lost_object_cb_t on_lost_object_; + void* cb_ctx_; + fibre::Domain* domain_; +}; + +struct LibFibreTxStream { + void on_tx_done(fibre::WriteResult result) { + if (on_completed) { + (*on_completed)(ctx, this, convert_status(result.status), result.end); + } + } + + fibre::AsyncStreamSink* sink; + fibre::TransferHandle handle; + on_tx_completed_cb_t on_completed; + void* ctx; + void (*on_closed)(LibFibreTxStream*, void*, fibre::StreamStatus); + void* on_closed_ctx; +}; + +struct LibFibreRxStream { + void on_rx_done(fibre::ReadResult result) { + if (on_completed) { + (*on_completed)(ctx, this, convert_status(result.status), result.end); + } + } + + fibre::AsyncStreamSource* source; + fibre::TransferHandle handle; + on_rx_completed_cb_t on_completed; + void* ctx; + void (*on_closed)(LibFibreRxStream*, void*, fibre::StreamStatus); + void* on_closed_ctx; +}; + + +void LibFibreDiscoveryCtx::on_found_object(fibre::Object* obj, fibre::Interface* intf) { + if (on_found_object_) { + FIBRE_LOG(D) << "discovered object " << fibre::as_hex(reinterpret_cast(obj)); + (*on_found_object_)(cb_ctx_, to_c(obj), to_c(intf)); + } +} + +void LibFibreDiscoveryCtx::on_lost_object(fibre::Object* obj) { + if (on_lost_object_) { + FIBRE_LOG(D) << "lost object " << fibre::as_hex(reinterpret_cast(obj)); + (*on_lost_object_)(cb_ctx_, to_c(obj)); + } +} + +const struct LibFibreVersion* libfibre_get_version() { + return &libfibre_version; +} + +LibFibreCtx* libfibre_open(LibFibreEventLoop event_loop) { + LibFibreCtx* ctx = new LibFibreCtx(); + ctx->event_loop = new ExternalEventLoop(event_loop); + ctx->fibre_ctx = fibre::open(ctx->event_loop); + + if (!ctx->fibre_ctx) { + FIBRE_LOG(E) << "fibre::open failed"; + delete ctx->event_loop; + delete ctx; + return nullptr; + } + + return ctx; +} + +void libfibre_close(LibFibreCtx* ctx) { + if (!ctx) { + FIBRE_LOG(E) << "invalid argument"; + return; + } + + fibre::close(ctx->fibre_ctx); + ctx->fibre_ctx = nullptr; + + delete ctx->event_loop; + delete ctx; + + FIBRE_LOG(D) << "closed (" << fibre::as_hex((uintptr_t)ctx) << ")"; +} + + +void libfibre_register_backend(LibFibreCtx* ctx, const char* name, size_t name_length, on_start_discovery_cb_t on_start_discovery, on_stop_discovery_cb_t on_stop_discovery, void* cb_ctx) { + auto disc = new ExternalDiscoverer(); + disc->on_start_discovery = on_start_discovery; + disc->on_stop_discovery = on_stop_discovery; + disc->cb_ctx = cb_ctx; + ctx->fibre_ctx->register_backend({name, name + name_length}, disc); +} + +FIBRE_PUBLIC LibFibreDomain* libfibre_open_domain(LibFibreCtx* ctx, + const char* specs, size_t specs_len) { + if (!ctx) { + FIBRE_LOG(E) << "invalid context"; + return nullptr; + } else { + FIBRE_LOG(D) << "opening domain"; + return to_c(ctx->fibre_ctx->create_domain({specs, specs_len})); + } +} + +void libfibre_close_domain(LibFibreDomain* domain) { + if (!domain) { + FIBRE_LOG(E) << "invalid domain"; + return; + } + FIBRE_LOG(D) << "closing domain"; + + from_c(domain)->ctx->close_domain(from_c(domain)); +} + +void libfibre_add_channels(LibFibreDomain* domain, LibFibreRxStream** tx_channel, LibFibreTxStream** rx_channel, size_t mtu) { + fibre::AsyncStreamLink* tx_link = new fibre::AsyncStreamLink(); // libfibre => backend + fibre::AsyncStreamLink* rx_link = new fibre::AsyncStreamLink(); // backend => libfibre + LibFibreRxStream* tx = new LibFibreRxStream(); // libfibre => backend + LibFibreTxStream* rx = new LibFibreTxStream(); // backend => libfibre + tx->source = tx_link; + rx->sink = rx_link; + + tx->on_closed = [](LibFibreRxStream* stream, void* ctx, fibre::StreamStatus status) { + auto link = reinterpret_cast(ctx); + link->close(status); + delete link; + delete stream; + }; + tx->on_closed_ctx = tx_link; + rx->on_closed = [](LibFibreTxStream* stream, void* ctx, fibre::StreamStatus status) { + auto link = reinterpret_cast(ctx); + link->close(status); + delete link; + delete stream; + }; + rx->on_closed_ctx = rx_link; + + if (tx_channel) { + *tx_channel = tx; + } + + if (rx_channel) { + *rx_channel = rx; + } + + fibre::ChannelDiscoveryResult result = {fibre::kFibreOk, rx_link, tx_link, mtu}; + from_c(domain)->add_channels(result); +} + +void libfibre_start_discovery(LibFibreDomain* domain, LibFibreDiscoveryCtx** handle, + on_found_object_cb_t on_found_object, on_lost_object_cb_t on_lost_object, + on_stopped_cb_t on_stopped, void* cb_ctx) { + if (!domain) { + FIBRE_LOG(E) << "invalid argument"; + if (on_stopped) { + (*on_stopped)(cb_ctx, kFibreInvalidArgument); + } + return; + } + + // deleted in libfibre_stop_discovery() + LibFibreDiscoveryCtx* discovery_ctx = new LibFibreDiscoveryCtx(); + discovery_ctx->on_found_object_ = on_found_object; + discovery_ctx->on_lost_object_ = on_lost_object; + discovery_ctx->cb_ctx_ = cb_ctx; + discovery_ctx->domain_ = from_c(domain); + + if (handle) { + *handle = discovery_ctx; + } + + from_c(domain)->start_discovery(MEMBER_CB(discovery_ctx, on_found_object), + MEMBER_CB(discovery_ctx, on_lost_object)); +} + +void libfibre_stop_discovery(LibFibreDiscoveryCtx* handle) { + if (!handle) { + FIBRE_LOG(E) << "bad handle"; + return; + } + + handle->domain_->stop_discovery(); + delete handle; +} + + +void libfibre_subscribe_to_interface(LibFibreInterface* interface, + on_attribute_added_cb_t on_attribute_added, + on_attribute_removed_cb_t on_attribute_removed, + on_function_added_cb_t on_function_added, + on_function_removed_cb_t on_function_removed, + void* cb_ctx) +{ + auto intf = reinterpret_cast(interface); // corresponding reverse cast in LibFibreDiscoveryCtx::complete() and libfibre_subscribe_to_interface() + + for (auto& func: intf->functions) { + std::vector input_names = {"obj"}; + std::vector input_codecs = {"object_ref"}; + std::vector output_names; + std::vector output_codecs; + for (auto& arg: func.second.inputs) { + input_names.push_back(arg.name.data()); + input_codecs.push_back(arg.app_codec.data()); + } + for (auto& arg: func.second.outputs) { + output_names.push_back(arg.name.data()); + output_codecs.push_back(arg.app_codec.data()); + } + input_names.push_back(nullptr); + input_codecs.push_back(nullptr); + output_names.push_back(nullptr); + output_codecs.push_back(nullptr); + + if (on_function_added) { + (*on_function_added)(cb_ctx, + to_c(&func.second), + func.first.data(), func.first.size(), + input_names.data(), input_codecs.data(), + output_names.data(), output_codecs.data()); + } + } + + for (auto& attr: intf->attributes) { + if (on_attribute_added) { + (*on_attribute_added)(cb_ctx, + reinterpret_cast(&attr.second), // corresponding reverse cast in libfibre_get_attribute() + attr.first.data(), attr.first.size(), + reinterpret_cast(attr.second.object->intf.get()), // corresponding reverse cast in libfibre_subscribe_to_interface() + attr.second.object->intf->name.size() ? attr.second.object->intf->name.data() : nullptr, attr.second.object->intf->name.size() + ); + } + } +} + +LibFibreStatus libfibre_get_attribute(LibFibreObject* parent_obj, LibFibreAttribute* attr, LibFibreObject** child_obj_ptr) { + if (!parent_obj || !attr) { + return kFibreInvalidArgument; + } + + fibre::LegacyObject* parent_obj_cast = reinterpret_cast(parent_obj); + fibre::LegacyFibreAttribute* attr_cast = reinterpret_cast(attr); // corresponding reverse cast in libfibre_subscribe_to_interface() + auto& attributes = parent_obj_cast->intf->attributes; + + bool is_member = std::find_if(attributes.begin(), attributes.end(), + [&](std::pair& kv) { + return &kv.second == attr_cast; + }) != attributes.end(); + + if (!is_member) { + FIBRE_LOG(W) << "attempt to fetch attribute from an object that does not implement it"; + return kFibreInvalidArgument; + } + + //LibFibreCtx* libfibre_ctx = reinterpret_cast(parent_obj_cast->client->user_data_); + fibre::LegacyObject* child_obj = attr_cast->object.get(); + + if (!attr_cast->object->known_to_application) { + attr_cast->object->known_to_application = true; + + //if (libfibre_ctx->on_construct_object) { + // //FIBRE_LOG(D) << "constructing subobject " << fibre::as_hex(reinterpret_cast(child_obj)); + // (*libfibre_ctx->on_construct_object)(libfibre_ctx->cb_ctx, + // reinterpret_cast(child_obj), + // reinterpret_cast(child_obj->intf.get()), + // child_obj->intf->name.size() ? child_obj->intf->name.data() : nullptr, child_obj->intf->name.size()); + //} + } + + if (child_obj_ptr) { + *child_obj_ptr = reinterpret_cast(child_obj); + } + + return kFibreOk; +} + +/** + * @brief Inserts or removes the specified number of elements + * @param delta: Positive value: insert elements, negative value: remove elements + */ +void resize_at(std::vector& vec, size_t pos, ssize_t delta) { + if (delta > 0) { + std::fill_n(std::inserter(vec, vec.begin() + pos), delta, 0); + } else { + vec.erase(std::min(vec.begin() + pos, vec.end()), + std::min(vec.begin() + pos + -delta, vec.end())); + } +} + +LibFibreStatus libfibre_call(LibFibreFunction* func, LibFibreCallContext** handle, + LibFibreStatus status, + const unsigned char* tx_buf, size_t tx_len, + unsigned char* rx_buf, size_t rx_len, + const unsigned char** tx_end, + unsigned char** rx_end, + libfibre_call_cb_t callback, void* cb_ctx) { + bool valid_args = func && handle + && (!tx_len || tx_buf) // tx_buf valid + && (!rx_len || rx_buf) // rx_buf valid + && tx_end && rx_end // tx_end, rx_end valid + && ((status != kFibreOk) || tx_len || rx_len || !handle); // progress + if (!valid_args) { + FIBRE_LOG(E) << "invalid argument"; + return kFibreInvalidArgument; + } + + struct Ctx { libfibre_call_cb_t callback; void* ctx; }; + struct Ctx* ctx = new Ctx{callback, cb_ctx}; + + fibre::Callback, fibre::CallBufferRelease> cb{ + [](void* ctx_, fibre::CallBufferRelease result) -> std::optional { + auto ctx = reinterpret_cast(ctx_); + const unsigned char* tx_buf; + size_t tx_len; + unsigned char* rx_buf; + size_t rx_len; + auto status = ctx->callback(ctx->ctx, to_c(result.status), result.tx_end, result.rx_end, &tx_buf, &tx_len, &rx_buf, &rx_len); + if (status == kFibreBusy) { + delete ctx; + return std::nullopt; + } else { + return fibre::CallBuffers{from_c(status), {tx_buf, tx_len}, {rx_buf, rx_len}}; + } + }, ctx}; + + + auto response = from_c(func)->call(from_c(handle), {from_c(status), {tx_buf, tx_len}, {rx_buf, rx_len}}, cb); + + if (!response.has_value()) { + return kFibreBusy; + } else { + delete ctx; + *tx_end = response->tx_end; + *rx_end = response->rx_end; + return to_c(response->status); + } +} + +void libfibre_start_tx(LibFibreTxStream* tx_stream, + const uint8_t* tx_buf, size_t tx_len, on_tx_completed_cb_t on_completed, + void* ctx) { + tx_stream->on_completed = on_completed; + tx_stream->ctx = ctx; + tx_stream->sink->start_write({tx_buf, tx_len}, &tx_stream->handle, MEMBER_CB(tx_stream, on_tx_done)); +} + +void libfibre_cancel_tx(LibFibreTxStream* tx_stream) { + tx_stream->sink->cancel_write(tx_stream->handle); +} + +void libfibre_close_tx(LibFibreTxStream* tx_stream, LibFibreStatus status) { + if (tx_stream->on_closed) { + (tx_stream->on_closed)(tx_stream, tx_stream->on_closed_ctx, convert_status(status)); + } +} + +void libfibre_start_rx(LibFibreRxStream* rx_stream, + uint8_t* rx_buf, size_t rx_len, on_rx_completed_cb_t on_completed, + void* ctx) { + rx_stream->on_completed = on_completed; + rx_stream->ctx = ctx; + rx_stream->source->start_read({rx_buf, rx_len}, &rx_stream->handle, MEMBER_CB(rx_stream, on_rx_done)); +} + +void libfibre_cancel_rx(LibFibreRxStream* rx_stream) { + rx_stream->source->cancel_read(rx_stream->handle); +} + +void libfibre_close_rx(LibFibreRxStream* rx_stream, LibFibreStatus status) { + if (rx_stream->on_closed) { + (rx_stream->on_closed)(rx_stream, rx_stream->on_closed_ctx, convert_status(status)); + } +} diff --git a/Firmware/fibre-cpp/libfibre.version b/Firmware/fibre-cpp/libfibre.version new file mode 100644 index 000000000..ad4c4f471 --- /dev/null +++ b/Firmware/fibre-cpp/libfibre.version @@ -0,0 +1,4 @@ +LIBFIBREABI_0.1.0 { + global: libfibre_*; + local: *; +}; \ No newline at end of file diff --git a/Firmware/fibre-cpp/logging.cpp b/Firmware/fibre-cpp/logging.cpp new file mode 100644 index 000000000..8ed921800 --- /dev/null +++ b/Firmware/fibre-cpp/logging.cpp @@ -0,0 +1,20 @@ + +#include "logging.hpp" + +#if !defined(_WIN32) && !defined(_WIN64) && !defined(__linux__) && !defined(__APPLE__) && !defined(EMSCRIPTEN) + +namespace std { +StdoutStream cerr; +} + +#endif + +namespace fibre { + +Logger logger{}; + +Logger* get_logger() { + return &logger; +} + +} diff --git a/Firmware/fibre-cpp/logging.hpp b/Firmware/fibre-cpp/logging.hpp new file mode 100644 index 000000000..32fe97db9 --- /dev/null +++ b/Firmware/fibre-cpp/logging.hpp @@ -0,0 +1,341 @@ +/** + * @brief Provides logging facilities + * + * Log entries are associated with user defined topics. A user can define a + * topic using DEFINE_LOG_TOPIC(topicname) and activate the topic for the + * current scope using USE_LOG_TOPIC(topicname). + * + * Currently all log entries are posted to stderr in a thread-safe way. + * + * Whether an event is actually logged depends on the current log verbosity of + * the corresponding topic. The log verbosity is defined by the following + * sources (in order of their precedence). + * + * 0. maximum log verbosity setting (see below) + * 1. runtime environment variable "FIBRE_LOG_[topicname]" + * 2. runtime environment variable "FIBRE_LOG" + * 3. topic specific default log verbosity defined using CONFIG_LOG_TOPIC(...) + * 4. FIBRE_DEFAULT_LOG_VERBOSITY defined before this file (using #define or -D compiler flag) + * 5. FIBRE_DEFAULT_LOG_VERBOSITY defined in this file + * + * The maximum log verbosity can be defined separately from the default log + * verbosity. The maximum log verbosity bound always applies, regardless of how + * the actual log verbosity is specified. This allows keeping the binary small + * by optimizing away unnecessary log entries at compile time. + * The maximum log verbosity is defined by the following sources (in order of + * their precedence): + * + * 1. topic specific max log verbosity defined using CONFIG_LOG_TOPIC(...) + * 2. FIBRE_MAX_LOG_VERBOSITY defined before this file (using #define or -D compiler flag) + * 3. FIBRE_MAX_LOG_VERBOSITY defined in this file + * + * TODO: ensure that the optimizer can indeed strip the unused strings (currently not the case) + * + * + * Example: + * + * @code + * + * DEFINE_LOG_TOPIC(MAIN); + * USE_LOG_TOPIC(MAIN); + * + * int main(void) { + * FIBRE_LOG(D) << "Hello Log!"; + * if (open("inexistent_file", O_RDONLY) < 0) { + * FIBRE_LOG(E) << "Could not open file: " << sys_err(); + * } + * return 0; + * } + * + * @endcode + * + * Using logging in header files is possible but undocumented (TODO: fix) + */ + +#ifndef __FIBRE_LOGGING_HPP +#define __FIBRE_LOGGING_HPP + +/** + * @brief Tag type to print the last system error + * + * The statement `std::out << sys_err();` will print the last system error + * in the following format: "error description (errno)". + * This is based on `GetLastError()` (Windows) or `errno` (all other systems). + */ +struct sys_err {}; + + +// TODO: support lite-version of logging on embedded systems +#if FIBRE_MAX_LOG_VERBOSITY + +#include + +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include "windows.h" +#endif + +#if defined(_WIN32) || defined(_WIN64) || defined(__linux__) || defined(__APPLE__) || defined(EMSCRIPTEN) +#include +#include +#else + +// We don't want included on an embedded system as it makes the +// binary huge. + +struct StdoutStream : std::ostream { + void operator <<(const char * str) { + printf("%s", str); + } +}; + +namespace std { +extern StdoutStream cerr; +} + +#endif + +#if defined(_WIN32) || defined(_WIN64) || defined(__linux__) + +#include +using TMutex = std::mutex; +using TLock = std::unique_lock; + +#else + +using TMutex = int; +struct TLock { + TLock() {} + TLock(TMutex) {} +}; + +#endif + +namespace fibre { + +// Maximum log verbosity that should be compiled into the binary. +// Log entries with a higher verbosity should be optimized away. +#ifndef FIBRE_MAX_LOG_VERBOSITY +# define FIBRE_MAX_LOG_VERBOSITY LOG_LEVEL_T +#endif + +// Default log verbosity that should be used for all topic. This may be +// overridden by other sources, see description in the beginning of this file. +#ifndef FIBRE_DEFAULT_LOG_VERBOSITY +# define FIBRE_DEFAULT_LOG_VERBOSITY LOG_LEVEL_W +#endif + +/** + * @brief Generates one log entry. + * + * The log entry will be associates with the topic specified in "USE_LOG_TOPIC". + * + * Note that the log entry must only be used in the statement it is generated. (TODO: fix) + * + * @param level: Can be one of "E", "W", "D", or other levels defined in + * log_level_t. + * @returns a stream for writing into the log entry + */ +#define FIBRE_LOG(level) \ + fibre::make_log_entry( \ + fibre::get_file_name(MAKE_SSTRING(__FILE__){}), __LINE__, __func__ \ + ).get_stream() + +/** + * @brief Defines a log topic. A log topic must be defined exactly once in every + * translation unit it is used. + */ +#define DEFINE_LOG_TOPIC(name) \ + struct LOG_TOPIC_ ## name { \ + static const char * get_label() { \ + static const char label[] = #name; \ + return label; \ + } \ + } + +/** + * @brief Activates the use of the specified log topic for the current scope + * (and all subscopes) + */ +#define USE_LOG_TOPIC(name) using current_log_topic = LOG_TOPIC_ ## name + +/** + * @brief Overrides the general log verbosity settings for a specific topic. + * If used, this should be placed in the same scope as the corresponding + * DEFINE_LOG_TOPIC. + */ +#define CONFIG_LOG_TOPIC(topic, default_verbosity, max_verbosity) \ +template<> constexpr log_level_t get_default_log_verbosity() { return (default_verbosity); } \ +template<> constexpr log_level_t get_max_log_verbosity() { return (max_verbosity); } + + +/** @brief Log verbosity levels */ +enum log_level_t { + LOG_LEVEL_F = 0, // fatal + LOG_LEVEL_E = 1, // error + LOG_LEVEL_W = 2, // warning + LOG_LEVEL_I = 3, // info + LOG_LEVEL_D = 4, // debug + LOG_LEVEL_T = 5, // trace +}; + +class NullBuffer : public std::streambuf { +public: + int overflow(int c) { return c; } +}; + +// Source: https://stackoverflow.com/questions/15845505/how-to-get-higher-precision-fractions-of-a-second-in-a-printout-of-current-tim +static std::string get_local_time() { + auto now(std::chrono::system_clock::now()); + auto seconds_since_epoch( + std::chrono::duration_cast(now.time_since_epoch())); + + // Construct time_t using 'seconds_since_epoch' rather than 'now' since it is + // implementation-defined whether the value is rounded or truncated. + std::time_t now_t( + std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(seconds_since_epoch))); + + char temp[10]; + if (!std::strftime(temp, 10, "%H:%M:%S.", std::localtime(&now_t))) + return ""; + + return std::string(temp) + + std::to_string((now.time_since_epoch() - seconds_since_epoch).count()); +} + +class Logger { +public: + class Entry { + public: + Entry() : base_stream_(null_stream), lock_() {} + + Entry(std::ostream& base_stream, log_level_t level, const char* topic, const char* filename, size_t line_no, const char *funcname, TMutex& mutex) + : base_stream_(base_stream), lock_(mutex) + { + switch (level) { + case LOG_LEVEL_W: + base_stream << "\x1b[93;1m"; + break; + case LOG_LEVEL_E: + case LOG_LEVEL_F: + base_stream << "\x1b[91;1m"; + break; + default: + break; + } + base_stream << get_local_time() << " "; + base_stream << std::dec << "[" << topic << "] "; + //base_stream << std::dec << filename << ":" << line_no << " in " << funcname << "(): "; + } + ~Entry() { get_stream() << "\x1b[0m" << std::endl; } + std::ostream& get_stream() { return base_stream_; }; + private: + NullBuffer null_buffer{}; + std::ostream null_stream{&null_buffer}; + std::ostream& base_stream_; + TLock lock_; + }; + + TMutex mutex_; +}; + + + +template +constexpr log_level_t get_default_log_verbosity() { return (log_level_t)FIBRE_DEFAULT_LOG_VERBOSITY; } + +template +constexpr log_level_t get_max_log_verbosity() { return (log_level_t)FIBRE_MAX_LOG_VERBOSITY; } + +/** + * @brief Resolves the currently active log verbosity for the given topic. + * See top of this file for a detailed description of the algorithm. + */ +template +log_level_t get_current_log_verbosity() { + char var_name[sizeof("FIBRE_LOG_") + strlen(TOPIC::get_label())]; + strcpy(var_name, "FIBRE_LOG_"); + strcat(var_name, TOPIC::get_label()); + + // TODO: provide a way to disable the + const char * var_val = std::getenv(var_name); + if (!var_val) { + var_val = std::getenv("FIBRE_LOG"); + } + + log_level_t log_level = get_default_log_verbosity(); + if (var_val) { + unsigned long num = strtoul(var_val, nullptr, 10); + log_level = (log_level_t)num; + } + + if (log_level > get_max_log_verbosity()) { + log_level = get_max_log_verbosity(); + } + return log_level; +} + + + +/* +template +void send_to_stream(TStream&& stream); + +template +void send_to_stream(TStream&& stream) { } + +template +void send_to_stream(TStream&& stream, T&& value, Ts&&... values) { + send_to_stream(std::forward(stream) << std::forward(value), std::forward(values)...); +}*/ + + +Logger* get_logger(); // defined in logging.cpp + +template +Logger::Entry make_log_entry(const char *filename, size_t line_no, const char *funcname) { + if (get_current_log_verbosity() < LEVEL) { + return {}; + } else { + Logger* logger = get_logger(); + return { std::cerr, LEVEL, TOPIC::get_label(), filename, line_no, funcname, logger->mutex_ }; + } +} + +template +constexpr const char * get_file_name(TFilepath file_path) { + return (file_path /*file_path.after_last_index_of('/')*/).c_str(); // TODO: extract file name (without path) +} + +} + + +namespace std { +static inline std::ostream& operator<<(std::ostream& stream, const sys_err&) { +#if defined(_WIN32) || defined(_WIN64) + auto error_code = GetLastError(); +#else + auto error_code = errno; +#endif + return stream << strerror(error_code) << " (" << error_code << ")"; +} +} + + +#else + +#define DEFINE_LOG_TOPIC(topic) +#define USE_LOG_TOPIC(topic) + +struct NullStream { + template NullStream& operator<<(T val) { return *this; } +}; + +#define FIBRE_LOG(level) NullStream() + +#endif // FIBRE_MAX_LOG_VERBOSITY + +#endif // __FIBRE_LOGGING_HPP diff --git a/Firmware/fibre-cpp/package.lua b/Firmware/fibre-cpp/package.lua new file mode 100644 index 000000000..599e85b0a --- /dev/null +++ b/Firmware/fibre-cpp/package.lua @@ -0,0 +1,143 @@ + +fibre_root = tup.getcwd() + +-- Returns a table that contains the Fibre code files and the flags required to +-- compile and link those files. +-- +-- args: A dictionary containing the fibre options. Refer to the Compile Options +-- in README.md for a list of available options. For example the option +-- `FIBRE_ENABLE_SERVER` maps to the argument `args.enable_server`. +-- In addition: +-- args.pkgconf: Controls the use of the pkgconf or pkg-config utility that +-- shall be used to locate build dependencies. Can be one of the following: +-- - A string: Use the binary provided by the string. Fail if it doesn't +-- exist. +-- - true: Use "pkgconf" and fall back to "pkg-config" if "pkgconf" +-- doesn't exist. Fail if both don't exist. +-- - false: Don't use pkg-config. The user is responsible of determining +-- the required compile and link flags. +-- - nil: Try both "pkgconf" and "pkg-config". If both don't exist fall +-- back to a hardcoded list of well-known settings. +-- +-- Returns: A dictionary with the following items: +-- code_files: A list of strings that name the C++ code files to be compiled. +-- The names are relative to package.lua. +-- include_dirs: A list of directories that must be added to the include path +-- when compiling the code files. The paths are relative to +-- package.lua. +-- cflags: A list of flags that should be passed to the compiler/linker when +-- compiling and linking the code files. +-- ldflags: A list of linker flags that should be passed to the linker when +-- linking the object files. +function get_fibre_package(args) + pkg = { + root = fibre_root, + code_files = { + 'fibre.cpp', + 'channel_discoverer.cpp', + }, + include_dirs = {'include'}, + cflags = {}, + ldflags = {}, + } + + -- Select a pkgconf function + if args.pkgconf == true or args.pkgconf == nil then + -- Autodetect pkgconf + if test_pkgconf('pkgconf') then + print("using pkgconf") + pkgconf_file = 'pkgconf' + pkgconf = real_pkgconf + elseif test_pkgconf('pkg-config') then + print("using pkg-config") + pkgconf_file = 'pkg-config' + pkgconf = real_pkgconf + elseif args.pkgconf == nil then + print("using hardcoded pkgconf") + pkgconf = hardcoded_pkgconf + else + error("couldn't find pkgconf nor pkg-config") + end + + elseif args.pkgconf == false then + print("not using pkgconf") + pkgconf_file = nil + pkgconf = null_pkgconf + else + print("using pkgconf: "..args.pkgconf) + pkgconf_file = args.pkgconf + pkgconf = real_pkgconf + end + + pkg.cflags += '-DFIBRE_ENABLE_SERVER='..(args.enable_server and '1' or '0') + pkg.cflags += '-DFIBRE_ENABLE_CLIENT='..(args.enable_client and '1' or '0') + pkg.cflags += '-DFIBRE_ENABLE_EVENT_LOOP='..(args.enable_event_loop and '1' or '0') + pkg.cflags += '-DFIBRE_ALLOW_HEAP='..(args.allow_heap and '1' or '0') + pkg.cflags += '-DFIBRE_MAX_LOG_VERBOSITY='..(args.max_log_verbosity or '5') + pkg.cflags += '-DFIBRE_DEFAULT_LOG_VERBOSITY='..(args.default_log_verbosity or '2') + pkg.cflags += '-DFIBRE_ENABLE_LIBUSB_BACKEND='..(args.enable_libusb_backend and '1' or '0') + pkg.cflags += '-DFIBRE_ENABLE_TCP_SERVER_BACKEND='..(args.enable_tcp_server_backend and '1' or '0') + pkg.cflags += '-DFIBRE_ENABLE_TCP_CLIENT_BACKEND='..(args.enable_tcp_client_backend and '1' or '0') + + if args.enable_libusb_backend then + pkg.code_files += 'platform_support/libusb_transport.cpp' + pkgconf(pkg, "libusb-1.0") + + -- TODO: only add pthread on linux and windows + pkg.ldflags += '-lpthread' + end + if args.max_log_verbosity == nil or (args.max_log_verbosity > 0) then + pkg.code_files += 'logging.cpp' + end + if args.enable_client then + pkg.code_files += 'legacy_object_client.cpp' + end + if args.enable_client or args.enable_server then + pkg.code_files += 'legacy_protocol.cpp' + end + if args.enable_event_loop then + pkg.code_files += 'platform_support/epoll_event_loop.cpp' + end + if args.enable_tcp_client_backend or args.enable_tcp_server_backend then + -- TODO: chose between windows and posix backend + pkg.code_files += 'platform_support/posix_tcp_backend.cpp' + pkg.code_files += 'platform_support/posix_socket.cpp' + pkg.ldflags += '-lanl' + end + + return pkg +end + +-- Runs the specified shell command immediately (not as part of the dependency +-- graph). +-- Returns the values (return_code, stdout) where stdout has the trailing new +-- line removed. +function fibre_run_now(command) + local handle + handle = io.popen(command) + local output = handle:read("*a") + local rc = {handle:close()} + return string.sub(output, 0, -2), rc[1] +end + +function test_pkgconf(name) + local str, rc = fibre_run_now(name.." --version 2>&1 >/dev/null") + return rc +end + +function real_pkgconf(pkg, lib) + pkg.cflags += fibre_run_now(pkgconf_file..' '..lib..' --cflags') + pkg.ldflags += fibre_run_now(pkgconf_file..' '..lib..' --libs') +end + +function null_pkgconf(pkg, lib) + -- don't do anything +end + +function hardcoded_pkgconf(pkg, lib) + libs = { + ['libusb-1.0'] = {cflags = {}, ldflags = {}}, + } + tup.append_table(pkg.cflags, libs[lib].cflags) + tup.append_table(pkg.ldflags, libs[lib].ldflags) +end \ No newline at end of file diff --git a/Firmware/fibre-cpp/platform_support/epoll_event_loop.cpp b/Firmware/fibre-cpp/platform_support/epoll_event_loop.cpp new file mode 100644 index 000000000..dcb36eba2 --- /dev/null +++ b/Firmware/fibre-cpp/platform_support/epoll_event_loop.cpp @@ -0,0 +1,204 @@ + +#include "epoll_event_loop.hpp" +#include "../logging.hpp" + +#include +#include +#include +#include +#include + +using namespace fibre; + +DEFINE_LOG_TOPIC(EVENT_LOOP); +USE_LOG_TOPIC(EVENT_LOOP); + + +bool EpollEventLoop::start(Callback on_started) { + if (epoll_fd_ >= 0) { + FIBRE_LOG(E) << "already started"; + return false; + } + + epoll_fd_ = epoll_create1(0); + if (epoll_fd_ < 0) { + FIBRE_LOG(E) << "epoll_create1() failed"; + return false; + } + + bool ok = true; + + post_fd_ = eventfd(0, 0); + + bool post_fd_ok = (post_fd_ >= 0) + && register_event(post_fd_, EPOLLIN, MEMBER_CB(this, run_callbacks)) + && post(on_started); + + if (!post_fd_ok) { + FIBRE_LOG(E) << "failed to create an event for posting callbacks onto the event loop"; + ok = false; + } + + // Run for as long as there are callbacks pending posted or there's at least + // one file descriptor other than post_fd_ registerd. + while (pending_callbacks_.size() || (context_map_.size() > 1)) { + iterations_++; + + do { + FIBRE_LOG(D) << "epoll_wait..."; + n_triggered_events_ = epoll_wait(epoll_fd_, triggered_events_, max_triggered_events_, -1); + FIBRE_LOG(D) << "epoll_wait unblocked by " << n_triggered_events_ << " events"; + if (errno == EINTR) { + FIBRE_LOG(D) << "interrupted"; + } + } while (n_triggered_events_ < 0 && errno == EINTR); // ignore syscall interruptions. This happens for instance during suspend. + + if (n_triggered_events_ <= 0) { + FIBRE_LOG(E) << "epoll_wait() failed with " << n_triggered_events_ << ": " << sys_err() << " - Terminating worker thread."; + ok = false; + break; + } + + // Handle events + for (int i = 0; i < n_triggered_events_; ++i) { + EventContext* ctx = (EventContext*)triggered_events_[i].data.ptr; + if (ctx) { + try { // TODO: not sure if using "try" without throwing exceptions will do unwanted things with the stack + ctx->callback.invoke(triggered_events_[i].events); + } catch (...) { + FIBRE_LOG(E) << "worker callback threw an exception."; + } + } + } + } + + FIBRE_LOG(D) << "epoll loop exited"; + + if ((post_fd_ >= 0) && !deregister_event(post_fd_)) { + FIBRE_LOG(E) << "deregister_event() failed"; + ok = false; + } + + if ((post_fd_ >= 0) && close(post_fd_) != 0) { + FIBRE_LOG(E) << "close() failed: " << sys_err(); + ok = false; + } + post_fd_ = -1; + + if (close(epoll_fd_) != 0) { + FIBRE_LOG(E) << "close() failed: " << sys_err(); + ok = false; + } + epoll_fd_ = -1; + + return ok; +} + +bool EpollEventLoop::post(Callback callback) { + if (epoll_fd_ < 0) { + FIBRE_LOG(E) << "not started"; + return false; + } + + { + std::unique_lock lock(pending_callbacks_mutex_); + pending_callbacks_.push_back(callback); + } + + const uint64_t val = 1; + if (write(post_fd_, &val, sizeof(val)) != sizeof(val)) { + FIBRE_LOG(E) << "write() failed" << sys_err(); + return false; + } + return true; +} + +bool EpollEventLoop::register_event(int event_fd, uint32_t events, Callback callback) { + if (epoll_fd_ < 0) { + FIBRE_LOG(E) << "not initialized"; + return false; + } + + if (event_fd < 0) { + FIBRE_LOG(E) << "invalid argument"; + return false; + } + + EventContext* ctx = new EventContext{callback}; + struct epoll_event ev = { + .events = events, + .data = { .ptr = ctx } + }; + context_map_[event_fd] = ctx; + + if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, event_fd, &ev) != 0) { + FIBRE_LOG(E) << "epoll_ctl(" << event_fd << "...) failed: " << sys_err(); + delete ctx; + return false; + } + + FIBRE_LOG(D) << "registered epoll event " << event_fd; + + return true; +} + +bool EpollEventLoop::deregister_event(int event_fd) { + if (epoll_fd_ < 0) { + FIBRE_LOG(E) << "not running"; + return false; + } + + int result = true; + + if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, event_fd, nullptr) != 0) { + FIBRE_LOG(E) << "epoll_ctl() failed: " << sys_err(); + result = false; + } + + EventContext* callback = context_map_[event_fd]; + + auto it = context_map_.find(event_fd); + if (it == context_map_.end()) { + FIBRE_LOG(E) << "event context not found"; + return false; + } + + for (int i = 0; i < n_triggered_events_; ++i) { + if ((EventContext*)(triggered_events_[i].data.ptr) == it->second) { + triggered_events_[i].data.ptr = nullptr; + } + } + + context_map_.erase(it); + + return result; +} + +struct EventLoopTimer* EpollEventLoop::call_later(float delay, Callback callback) { + FIBRE_LOG(E) << "not implemented"; // TODO: implement + return nullptr; +} + +bool EpollEventLoop::cancel_timer(EventLoopTimer* timer) { + FIBRE_LOG(E) << "not implemented"; // TODO: implement + return false; +} + +void EpollEventLoop::run_callbacks(uint32_t) { + // TODO: warn if read fails + uint64_t val; + if (read(post_fd_, &val, sizeof(val)) != sizeof(val)) { + FIBRE_LOG(E) << "failed to read from post file descriptor"; + } + + std::vector> pending_callbacks; + + { + std::unique_lock lock(pending_callbacks_mutex_); + std::swap(pending_callbacks, pending_callbacks_); + } + + for (auto& cb: pending_callbacks) { + cb.invoke(); + } +} diff --git a/Firmware/fibre-cpp/platform_support/epoll_event_loop.hpp b/Firmware/fibre-cpp/platform_support/epoll_event_loop.hpp new file mode 100644 index 000000000..b900c98f7 --- /dev/null +++ b/Firmware/fibre-cpp/platform_support/epoll_event_loop.hpp @@ -0,0 +1,69 @@ +#ifndef __FIBRE_LINUX_EVENT_LOOP_HPP +#define __FIBRE_LINUX_EVENT_LOOP_HPP + +//#include +#include +#include +#include +#include +//#include + +#include + +namespace fibre { + +/** + * @brief Event loop based on the Linux-specific `epoll()` infrastructure. + * + * Thread safety: None of the public functions are thread-safe with respect to + * each other. However they are thread safe with respect to the internal event + * loop, that means register_event() and deregister_event() can be called from + * within an event callback (which executes on the event loop thread), provided + * those calls are properly synchronized with calls from other threads. + */ +class EpollEventLoop : public EventLoop { +public: + + /** + * @brief Starts the event loop on the current thread and places the + * specified start callback on the event queue. + * + * The function returns when the event loop becomes empty or if a platform + * error occurs. + */ + bool start(Callback on_started); + + bool post(Callback callback) final; + bool register_event(int fd, uint32_t events, Callback callback) final; + bool deregister_event(int fd) final; + struct EventLoopTimer* call_later(float delay, Callback callback) final; + bool cancel_timer(EventLoopTimer* timer) final; + +private: + struct EventContext { + //int fd; + Callback callback; + }; + + void run_callbacks(uint32_t); + + int epoll_fd_ = -1; + int post_fd_ = -1; + unsigned int iterations_ = 0; + + std::unordered_map context_map_; // required to deregister callbacks + + static const size_t max_triggered_events_ = 16; // max number of events that can be handled per iteration + int n_triggered_events_ = 0; + struct epoll_event triggered_events_[max_triggered_events_]; + + // List of callbacks that were submitted through post(). + std::vector> pending_callbacks_; + + // Mutex to protect pending_callbacks_ + std::mutex pending_callbacks_mutex_; +}; + +} + +#endif // __FIBRE_LINUX_EVENT_LOOP_HPP \ No newline at end of file diff --git a/Firmware/fibre-cpp/platform_support/libusb_transport.cpp b/Firmware/fibre-cpp/platform_support/libusb_transport.cpp new file mode 100644 index 000000000..078d19547 --- /dev/null +++ b/Firmware/fibre-cpp/platform_support/libusb_transport.cpp @@ -0,0 +1,684 @@ +/** + * @brief Transport provider: libusb + * + * Platform Compatibility: Linux, Windows, macOS + */ + +#include "libusb_transport.hpp" +#include "../logging.hpp" +#include "../print_utils.hpp" +#include + +#include +#include + +#if !FIBRE_ALLOW_HEAP +# error "The libusb backend requires heap allocation." +#endif + +using namespace fibre; + +DEFINE_LOG_TOPIC(USB); +USE_LOG_TOPIC(USB); + +// This probably has no noteworthy effect since we automatically restart +// timed out operations anyway. +constexpr unsigned int kBulkTimeoutMs = 10000; + +// Only relevant for platforms don't support hotplug detection and thus +// need polling. +constexpr unsigned int kPollingIntervalMs = 1000; + +/* LibusbDiscoverer ----------------------------------------------------------*/ + +/** + * @brief Initializes the discoverer. + * + * Asynchronous tasks will be executed on the provided event_loop. + * + * @param event_loop: The event loop that is used to execute background tasks. The + * pointer must be non-null and initialized when this function is called. + * It must remain initialized until deinit() of this discoverer was called. + */ +bool LibusbDiscoverer::init(EventLoop* event_loop) { + if (!event_loop) + return false; + event_loop_ = event_loop; + + if (libusb_init(&libusb_ctx_) != LIBUSB_SUCCESS) { + FIBRE_LOG(E) << "libusb_init() failed: " << sys_err(); + return deinit(0), false; + } + + // Fetch initial list of file-descriptors we have to monitor. + // Note: this will fail on Windows. Since this is used for epoll, we need a + // different approach for Windows anyway. + const struct libusb_pollfd** pollfds = libusb_get_pollfds(libusb_ctx_); + using_sparate_libusb_thread_ = !pollfds; + + if (!using_sparate_libusb_thread_) { + // This code path is taken on Linux + FIBRE_LOG(D) << "Using externally provided event loop"; + + // Check if libusb needs special time-based polling on this platform + if (libusb_pollfds_handle_timeouts(libusb_ctx_) == 0) { + FIBRE_LOG(D) << "Using time-based polling"; + } + + // libusb maintains a (dynamic) list of file descriptors that need to be + // monitored (via select/poll/epoll) so that I/O events can be processed when + // needed. Since we use the async libusb interface, we do the monitoring + // ourselves. That means we always need keep track of the libusb file + // descriptor list. + + // Subscribe to changes to the list of file-descriptors we have to monitor. + libusb_set_pollfd_notifiers(libusb_ctx_, + [](int fd, short events, void *user_data) { + ((LibusbDiscoverer*)user_data)->on_add_pollfd(fd, events); + }, + [](int fd, void *user_data) { + ((LibusbDiscoverer*)user_data)->on_remove_pollfd(fd); + }, this); + + // Fetch initial list of file-descriptors we have to monitor. + // Note: this will fail on Windows. Since this is used for epoll, we need a + // different approach for Windows anyway. + const struct libusb_pollfd** pollfds = libusb_get_pollfds(libusb_ctx_); + if (!pollfds) { + return deinit(2), false; + } + + for (size_t i = 0; pollfds[i]; ++i) { + on_add_pollfd(pollfds[i]->fd, pollfds[i]->events); + } + libusb_free_pollfds(pollfds); + pollfds = nullptr; + + } else { + FIBRE_LOG(D) << "Using internal event loop thread"; + + // This code path is taken on Windows (which does not support epoll) + run_internal_event_loop_ = true; + internal_event_loop_thread_ = new std::thread([](void* ctx) { + ((LibusbDiscoverer*)ctx)->internal_event_loop(); + }, this); + } + + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + // This code path is taken on Linux + FIBRE_LOG(D) << "Using libusb native hotplug detection"; + + // Subscribe to hotplug events + int result = libusb_hotplug_register_callback(libusb_ctx_, + (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), + LIBUSB_HOTPLUG_ENUMERATE /* trigger callback for all currently connected devices too */, + LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, + [](struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event, void *user_data){ + return ((LibusbDiscoverer*)user_data)->on_hotplug(dev, event); + }, this, &hotplug_callback_handle_); + if (LIBUSB_SUCCESS != result) { + FIBRE_LOG(E) << "Error subscribing to hotplug events"; + hotplug_callback_handle_ = 0; + return deinit(3), false; + } + + } else { + // This code path is taken on Windows + FIBRE_LOG(D) << "Using periodic polling to discover devices"; + + poll_devices_now(); // this will also start a timer to poll again periodically + } + + if (!pollfds && libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + // The hotplug callback handler above is not yet thread-safe. To make it thread-safe + // we'd need to post it on the application's event loop. + FIBRE_LOG(E) << "Hotplug detection with separate libusb thread will cause trouble."; + } + + return true; +} + +bool LibusbDiscoverer::deinit(int stage) { + // TODO: verify that all devices are closed and hotplug detection is disabled + + if (stage > 3 && libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + libusb_hotplug_deregister_callback(libusb_ctx_, hotplug_callback_handle_); + } + + if (stage > 3 && device_polling_timer_) { + event_loop_->cancel_timer(device_polling_timer_); + device_polling_timer_ = nullptr; + } + + if (stage > 2 && !run_internal_event_loop_) { + // Deregister libusb events from our event loop. + const struct libusb_pollfd** pollfds = libusb_get_pollfds(libusb_ctx_); + if (pollfds) { + for (size_t i = 0; pollfds[i]; ++i) { + on_remove_pollfd(pollfds[i]->fd); + } + libusb_free_pollfds(pollfds); + pollfds = nullptr; + } + } + + if (stage > 1 && !run_internal_event_loop_) { + libusb_set_pollfd_notifiers(libusb_ctx_, nullptr, nullptr, nullptr); + } + + if (stage > 0 && run_internal_event_loop_) { + run_internal_event_loop_ = false; + libusb_interrupt_event_handler(libusb_ctx_); + internal_event_loop_thread_->join(); + delete internal_event_loop_thread_; + internal_event_loop_thread_ = nullptr; + } + + if (stage > 0) { + // TODO: we should probably deinit and close all connected channels + for (auto& dev: known_devices_) { + libusb_unref_device(dev.second.dev); + } + } + + // FIXME: the libusb_hotplug_deregister_callback call will still trigger a + // usb_handler event. We need to wait until this has finished before we + // truly discard libusb resources + // Update: is this still relevant? + //usleep(100000); + + if (stage > 0) { + libusb_exit(libusb_ctx_); + libusb_ctx_ = nullptr; + } + + event_loop_ = nullptr; + + return true; +} + +/** + * @brief Starts looking for Fibre devices accessible through USB. + * + * Multiple discovery requests can be active at the same time but beware that a + * channel will be announced to all matching subscribers so be careful with access + * multiplexing. + * + * If the function succeeds, an opaque context pointer is returned which must be + * passed to stop_channel_discovery() to terminate this particular request. + * + * @param specs: See README of the main Fibre repository for details. + * (https://github.com/samuelsadok/fibre/tree/devel). + * + * @param on_found_channels: Invoked when a matching pair of RX/TX channels is found. + * This callback will also be called for any matching channels that already exist when + * the discovery is started. + */ +void LibusbDiscoverer::start_channel_discovery(Domain* domain, const char* specs, size_t specs_len, ChannelDiscoveryContext** handle) { + FIBRE_LOG(D) << "starting discovery with filter \"" << std::string(specs, specs_len) << "\""; + + InterfaceSpecs interface_specs; + + try_parse_key(specs, specs + specs_len, "bus", &interface_specs.bus); + try_parse_key(specs, specs + specs_len, "address", &interface_specs.address); + try_parse_key(specs, specs + specs_len, "idVendor", &interface_specs.vendor_id); + try_parse_key(specs, specs + specs_len, "idProduct", &interface_specs.product_id); + try_parse_key(specs, specs + specs_len, "bInterfaceClass", &interface_specs.interface_class); + try_parse_key(specs, specs + specs_len, "bInterfaceSubClass", &interface_specs.interface_subclass); + try_parse_key(specs, specs + specs_len, "bInterfaceProtocol", &interface_specs.interface_protocol); + + MyChannelDiscoveryContext* subscription = new MyChannelDiscoveryContext{}; + subscription->interface_specs = interface_specs; + subscription->domain = domain; + subscriptions_.push_back(subscription); + + for (auto& dev: known_devices_) { + consider_device(dev.second.dev, subscription); + } + + if (handle) { + *handle = subscription; + } + + return; +} + +/** + * @brief Stops an object discovery process that was started with start_channel_discovery(). + * + * Channels which were already discovered will remain open. However if the discovery is restarted + * it is possible that the same channels are returned again (their pointers need not match the old instance). + * + * The discovery must be considered still in progress until the callback is + * invoked with kFibreCancelled. + */ +int LibusbDiscoverer::stop_channel_discovery(ChannelDiscoveryContext* handle) { + auto it = std::find(subscriptions_.begin(), subscriptions_.end(), handle); + + if (it == subscriptions_.end()) { + FIBRE_LOG(E) << "Not an active subscription"; + return -1; + } + + subscriptions_.erase(it); + delete handle; + return 0; +} + +/** + * @brief Runs the event handling loop. This function blocks until + * run_internal_event_loop_ is false. + * + * This loop is only executed on Windows. On other platforms the provided EventLoop is used. + */ +void LibusbDiscoverer::internal_event_loop() { + while (run_internal_event_loop_) + libusb_handle_events(libusb_ctx_); +} + +void LibusbDiscoverer::on_event_loop_iteration() { + if (event_loop_timer_) { + FIBRE_LOG(D) << "cancelling event loop timer"; + event_loop_->cancel_timer(event_loop_timer_); + event_loop_timer_ = nullptr; + } + + timeval tv = { .tv_sec = 0, .tv_usec = 0 }; + if (libusb_handle_events_timeout(libusb_ctx_, &tv) != 0) { + FIBRE_LOG(E) << "libusb_handle_events_timeout() failed"; + } + + timeval timeout; + if (libusb_get_next_timeout(libusb_ctx_, &timeout)) { + float timeout_sec = (float)timeout.tv_sec + (float)timeout.tv_usec * 1e-6; + FIBRE_LOG(D) << "setting event loop timeout to " << timeout_sec << " s"; + event_loop_timer_ = event_loop_->call_later(timeout_sec, + MEMBER_CB(this, on_event_loop_iteration)); + } +} + +/** + * @brief Called when libusb wants to add a file descriptor to our event loop. + */ +void LibusbDiscoverer::on_add_pollfd(int fd, short events) { + event_loop_->register_event(fd, events, + MEMBER_CB(this, on_event_loop_iteration2)); +} + +/** + * @brief Called when libusb wants to remove a file descriptor to our event loop. + */ +void LibusbDiscoverer::on_remove_pollfd(int fd) { + event_loop_->deregister_event(fd); +} + +/** + * @brief Called by libusb when a USB device was plugged in or out. + * + * If this function returns a non-zero value, libusb removes this filter. + */ +int LibusbDiscoverer::on_hotplug(struct libusb_device *dev, + libusb_hotplug_event event) { + uint8_t bus_number = libusb_get_bus_number(dev); + uint8_t dev_number = libusb_get_device_address(dev); + + if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) { + FIBRE_LOG(D) << "device arrived: bus " << (int)bus_number << ", " << (int)dev_number; + + // add empty placeholder to the list of known devices + known_devices_[bus_number << 8 | dev_number] = { + .dev = libusb_ref_device(dev), + .handle = nullptr + }; + + for (auto& subscription: subscriptions_) { + consider_device(dev, subscription); + } + + } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) { + FIBRE_LOG(D) << "device left: bus " << (int)bus_number << ", " << (int)dev_number; + + auto it = known_devices_.find(bus_number << 8 | dev_number); + + if (it != known_devices_.end()) { + for (auto& ep: it->second.ep_in) { + ep->deinit(); + } + for (auto& ep: it->second.ep_out) { + ep->deinit(); + } + if (it->second.handle) { + libusb_close(it->second.handle); + } + + known_devices_.erase(it); + } + + libusb_unref_device(dev); + + } else { + FIBRE_LOG(W) << "Unexpected event: " << event; + } + + return 0; +} + +void LibusbDiscoverer::poll_devices_now() { + FIBRE_LOG(D) << "poll_devices_now() called."; + + device_polling_timer_ = nullptr; + + libusb_device** list = nullptr; + ssize_t n_devices = libusb_get_device_list(libusb_ctx_, &list); + std::unordered_map current_devices; + + if (n_devices < 0) { + FIBRE_LOG(W) << "libusb_get_device_list() failed."; + } else { + for (ssize_t i = 0; i < n_devices; ++i) { + uint8_t bus_number = libusb_get_bus_number(list[i]); + uint8_t dev_number = libusb_get_device_address(list[i]); + current_devices[bus_number << 8 | dev_number] = list[i]; + } + + // Call on_hotplug for all new devices + for (auto& dev: current_devices) { + if (known_devices_.find(dev.first) == known_devices_.end()) { + on_hotplug(dev.second, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); + + // Immediately forget about the devices that weren't opened on plugin. + // The reason is this: On Windows the device address and even the + // device pointer can remain equal across device reset. Since we don't + // poll at infinite frequency This means we could miss a device reset. + // To avoid this, we reinspect the all unopened devices on + // every polling iteration. + auto it = known_devices_.find(dev.first); + if (it->second.handle == nullptr) { + known_devices_.erase(it); + } + } + } + + // Call on_hotplug for all lost devices + + std::vector lost_devices; + + for (auto& dev: known_devices_) { + if (current_devices.find(dev.first) == current_devices.end()) { + lost_devices.push_back(dev.second.dev); + } + } + + for (auto& dev: lost_devices) { + on_hotplug(dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT); + } + + libusb_free_device_list(list, 1 /* unref the devices */); + } + + // It's possible that the discoverer was deinited during this function. + if (event_loop_) { + device_polling_timer_ = event_loop_->call_later(kPollingIntervalMs * 0.001f, + MEMBER_CB(this, poll_devices_now)); + } +} + +void LibusbDiscoverer::consider_device(struct libusb_device *device, MyChannelDiscoveryContext* subscription) { + uint8_t bus_number = libusb_get_bus_number(device); + uint8_t dev_number = libusb_get_device_address(device); + + bool mismatch = (subscription->interface_specs.bus != -1 && bus_number != subscription->interface_specs.bus) + || (subscription->interface_specs.address != -1 && dev_number != subscription->interface_specs.address); + + if (mismatch) { + return; + } + + if (subscription->interface_specs.vendor_id != -1 || subscription->interface_specs.product_id != -1) { + struct libusb_device_descriptor dev_desc; + int result = libusb_get_device_descriptor(device, &dev_desc); + if (result != LIBUSB_SUCCESS) { + FIBRE_LOG(W) << "Failed to get device descriptor: " << result; + } + + mismatch = (subscription->interface_specs.vendor_id != -1 && dev_desc.idVendor != subscription->interface_specs.vendor_id) + || (subscription->interface_specs.product_id != -1 && dev_desc.idProduct != subscription->interface_specs.product_id); + + if (mismatch) { + return; + } + } + + struct libusb_config_descriptor* config_desc = nullptr; + + if (libusb_get_active_config_descriptor(device, &config_desc) != LIBUSB_SUCCESS) { + FIBRE_LOG(E) << "Failed to get active config descriptor: " << sys_err(); + } else { + for (uint8_t i = 0; i < config_desc->bNumInterfaces; ++i) { + for (int j = 0; j < config_desc->interface[i].num_altsetting; ++j) { + // TODO: probably we should only chose one alt setting + const struct libusb_interface_descriptor* intf_desc = &(config_desc->interface[i].altsetting[j]); + + mismatch = (subscription->interface_specs.interface_class != -1 && intf_desc->bInterfaceClass != subscription->interface_specs.interface_class) + || (subscription->interface_specs.interface_subclass != -1 && intf_desc->bInterfaceSubClass != subscription->interface_specs.interface_subclass) + || (subscription->interface_specs.interface_protocol != -1 && intf_desc->bInterfaceProtocol != subscription->interface_specs.interface_protocol); + if (mismatch) { + continue; + } + + // We found a matching interface. Now find one bulk IN and one bulk OUT endpoint. + const libusb_endpoint_descriptor* libusb_ep_in = nullptr; + const libusb_endpoint_descriptor* libusb_ep_out = nullptr; + for (uint8_t k = 0; k < intf_desc->bNumEndpoints; ++k) { + if ((intf_desc->endpoint[k].bmAttributes & 0x03) == LIBUSB_TRANSFER_TYPE_BULK + && (intf_desc->endpoint[k].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { + libusb_ep_in = &intf_desc->endpoint[k]; + } else if ((intf_desc->endpoint[k].bmAttributes & 0x03) == LIBUSB_TRANSFER_TYPE_BULK + && (intf_desc->endpoint[k].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT) { + libusb_ep_out = &intf_desc->endpoint[k]; + } + } + + Device& my_dev = known_devices_[bus_number << 8 | dev_number]; + + // If the same device was already returned in a previous discovery + // then it will already be open. + + if (!my_dev.handle) { + int result = libusb_open(device, &my_dev.handle); + if (LIBUSB_SUCCESS != result) { + FIBRE_LOG(E) << "Could not open USB device: " << result; + continue; + } + } + + int result = libusb_claim_interface(my_dev.handle, i); + if (LIBUSB_SUCCESS != result) { + FIBRE_LOG(E) << "Could not claim interface " << i << " on USB device: " << result; + continue; + } + + size_t mtu = SIZE_MAX; + + LibusbBulkInEndpoint* ep_in = new LibusbBulkInEndpoint(); + if (libusb_ep_in && ep_in->init(this, my_dev.handle, libusb_ep_in->bEndpointAddress)) { + my_dev.ep_in.push_back(ep_in); + mtu = std::min(mtu, (size_t)libusb_ep_in->wMaxPacketSize); + } else { + delete ep_in; + ep_in = nullptr; + } + + LibusbBulkOutEndpoint* ep_out = new LibusbBulkOutEndpoint(); + if (libusb_ep_out && ep_out->init(this, my_dev.handle, libusb_ep_out->bEndpointAddress)) { + my_dev.ep_out.push_back(ep_out); + mtu = std::min(mtu, (size_t)libusb_ep_out->wMaxPacketSize); + } else { + delete ep_out; + ep_out = nullptr; + } + + subscription->domain->add_channels({kFibreOk, ep_in, ep_out, mtu}); + } + } + + libusb_free_config_descriptor(config_desc); + config_desc = nullptr; + } +} + + +/* LibusbBulkEndpoint --------------------------------------------------------*/ + +template +bool LibusbBulkEndpoint::init(LibusbDiscoverer* parent, libusb_device_handle* handle, uint8_t endpoint_id) { + parent_ = parent; + handle_ = handle; + transfer_ = libusb_alloc_transfer(0); + endpoint_id_ = endpoint_id; + return true; +} + +template +bool LibusbBulkEndpoint::deinit() { + if (completer_) { + FIBRE_LOG(E) << "Transfer on EP " << as_hex(endpoint_id_) << " still in progress. This is gonna be messy."; + } + + libusb_free_transfer(transfer_); + transfer_ = nullptr; + return true; +} + +template +void LibusbBulkEndpoint::start_transfer(bufptr_t buffer, TransferHandle* handle, Callback completer) { + if (handle) { + *handle = reinterpret_cast(this); + } + + if (completer_) { + FIBRE_LOG(E) << "transfer already in progress"; + completer.invoke({kStreamError, nullptr}); + return; + } + + if (!handle_) { + FIBRE_LOG(E) << "device not open"; + completer.invoke({kStreamError, nullptr}); + return; + } + + auto direct_callback = [](struct libusb_transfer* transfer){ + ((LibusbBulkEndpoint*)transfer->user_data)->on_transfer_finished(); + }; + + // This callback is used if we start our own libusb thread + // separate from the application's event loop thread + auto indirect_callback = [](struct libusb_transfer* transfer){ + auto ep = (LibusbBulkEndpoint*)transfer->user_data; + ep->parent_->event_loop_->post(MEMBER_CB(ep, on_transfer_finished)); + }; + + //FIBRE_LOG(D) << "transfer of size " << buffer.size(); + libusb_fill_bulk_transfer(transfer_, handle_, endpoint_id_, + buffer.begin(), buffer.size(), + parent_->using_sparate_libusb_thread_ ? indirect_callback : direct_callback, + this, kBulkTimeoutMs); + + completer_ = completer; + submit_transfer(); +} + +template +void LibusbBulkEndpoint::cancel_transfer(TransferHandle transfer_handle) { + if (!completer_) { + FIBRE_LOG(E) << "transfer not in progress"; + return; + } + + libusb_cancel_transfer(transfer_); +} + +template +void LibusbBulkEndpoint::submit_transfer() { + int result = libusb_submit_transfer(transfer_); + if (LIBUSB_SUCCESS == result) { + // ok + FIBRE_LOG(T) << "started USB transfer on EP " << as_hex(endpoint_id_); + } else if (LIBUSB_ERROR_NO_DEVICE == result) { + FIBRE_LOG(W) << "couldn't start USB transfer on EP " << as_hex(endpoint_id_) << ": " << libusb_error_name(result); + completer_.invoke_and_clear({kStreamClosed, nullptr}); + } else { + FIBRE_LOG(W) << "couldn't start USB transfer on EP " << as_hex(endpoint_id_) << ": " << libusb_error_name(result); + completer_.invoke_and_clear({kStreamError, nullptr}); + } +} + +template +void LibusbBulkEndpoint::on_transfer_finished() { + // We ignore timeouts here and just retry. If the application wishes to have + // a timeout on the transfer it can just call cancel_transfer() after a while. + if (transfer_->status == LIBUSB_TRANSFER_TIMED_OUT) { + submit_transfer(); + return; + } + + libusb_device* dev = libusb_get_device(handle_); + + StreamStatus status; + + if (transfer_->status == LIBUSB_TRANSFER_COMPLETED) { + status = kStreamOk; + } else if (transfer_->status == LIBUSB_TRANSFER_CANCELLED) { + status = kStreamCancelled; + } else { + // The error that we get on device removal tends to be inaccurate. + // Sometimes it's LIBUSB_TRANSFER_STALL, sometimes + // LIBUSB_TRANSFER_ERROR. Therefore we just check if the device + // is still present to determine which error code to return. + // TODO: this detection doesn't really work. The device is still in the + // device list at this point when it just got unplugged. For now we + // just ignore transfer errors. + + bool found = false; + + libusb_device** list; + ssize_t n_devices = libusb_get_device_list(parent_->libusb_ctx_, &list); + + if (n_devices >= 0) { + for (size_t i = 0; i < (size_t)n_devices; ++i) { + if (list[i] == dev) { + // found = true; + break; + } + } + libusb_free_device_list(list, 1); + } + + if (found) { + status = kStreamError; + } else { + FIBRE_LOG(D) << "device removed during transfer"; + status = kStreamClosed; + } + } + + (status == kStreamError ? FIBRE_LOG(W) : FIBRE_LOG(T)) + << "USB transfer on EP " << as_hex(endpoint_id_) << " finished with " << libusb_error_name(transfer_->status); + + if (status == kStreamClosed) { + handle_ = nullptr; // Ensure that no new transfer is started + } + + uint8_t* end = std::max(transfer_->buffer + transfer_->actual_length, transfer_->buffer); + completer_.invoke_and_clear({status, end}); + + // If libusb does hotplug detection itself then we don't need to handle + // device removal here. Libusb will call the corresponding hotplug callback. + if (status == kStreamClosed && !parent_->hotplug_callback_handle_) { + if (!parent_->using_sparate_libusb_thread_) { + FIBRE_LOG(E) << "It's not a good idea to unref the device from within this callback. This will probably hang."; + } + parent_->on_hotplug(dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT); + } +} diff --git a/Firmware/fibre-cpp/platform_support/libusb_transport.hpp b/Firmware/fibre-cpp/platform_support/libusb_transport.hpp new file mode 100644 index 000000000..63f7284e5 --- /dev/null +++ b/Firmware/fibre-cpp/platform_support/libusb_transport.hpp @@ -0,0 +1,125 @@ +#ifndef __FIBRE_USB_DISCOVERER_HPP +#define __FIBRE_USB_DISCOVERER_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace fibre { + +class LibusbBulkInEndpoint; +class LibusbBulkOutEndpoint; + +template class LibusbBulkEndpoint; + +class LibusbDiscoverer : public ChannelDiscoverer { +public: + + struct InterfaceSpecs { + int bus = -1; // -1 to ignore + int address = -1; // -1 to ignore + int vendor_id = -1; // -1 to ignore + int product_id = -1; // -1 to ignore + int interface_class = -1; // -1 to ignore + int interface_subclass = -1; // -1 to ignore + int interface_protocol = -1; // -1 to ignore + }; + + struct MyChannelDiscoveryContext : ChannelDiscoveryContext { + InterfaceSpecs interface_specs; + Domain* domain; + }; + + constexpr static const char* get_name() { return "usb"; } + bool init(EventLoop* event_loop); + bool deinit() { return deinit(INT_MAX); } + void start_channel_discovery(Domain* domain, const char* specs, size_t specs_len, ChannelDiscoveryContext** handle) final; + int stop_channel_discovery(ChannelDiscoveryContext* handle) final; + +private: + friend class LibusbBulkEndpoint; + friend class LibusbBulkEndpoint; + + struct Device { + struct libusb_device* dev; + struct libusb_device_handle* handle; + std::vector ep_in; + std::vector ep_out; + }; + + bool deinit(int stage); + void internal_event_loop(); + void on_event_loop_iteration(); + void on_event_loop_iteration2(uint32_t) { on_event_loop_iteration(); } + void on_add_pollfd(int fd, short events); + void on_remove_pollfd(int fd); + int on_hotplug(struct libusb_device *dev, libusb_hotplug_event event); + void poll_devices_now(); + void consider_device(struct libusb_device *device, MyChannelDiscoveryContext* subscription); + + EventLoop* event_loop_ = nullptr; + bool using_sparate_libusb_thread_; // true on Windows. Initialized in init() + libusb_context *libusb_ctx_ = nullptr; // libusb session + libusb_hotplug_callback_handle hotplug_callback_handle_ = 0; + bool run_internal_event_loop_ = false; + std::thread* internal_event_loop_thread_; + EventLoopTimer* device_polling_timer_; + EventLoopTimer* event_loop_timer_ = nullptr; + std::unordered_map known_devices_; // key: bus_number << 8 | dev_number + std::vector subscriptions_; +}; + +template +class LibusbBulkEndpoint { +public: + bool init(LibusbDiscoverer* parent, struct libusb_device_handle* handle, uint8_t endpoint_id); + bool deinit(); + +protected: + void start_transfer(bufptr_t buffer, TransferHandle* handle, Callback completer); + void cancel_transfer(TransferHandle transfer_handle); + +private: + void submit_transfer(); + void on_transfer_finished(); + + LibusbDiscoverer* parent_ = nullptr; + struct libusb_device_handle* handle_ = nullptr; + uint8_t endpoint_id_ = 0; + struct libusb_transfer* transfer_ = nullptr; + Callback completer_ = nullptr; +}; + +class LibusbBulkInEndpoint final : public LibusbBulkEndpoint, public AsyncStreamSource { +public: + void start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) final { + start_transfer(buffer, handle, completer); + } + + void cancel_read(TransferHandle transfer_handle) final { + cancel_transfer(transfer_handle); + } +}; + +class LibusbBulkOutEndpoint final : public LibusbBulkEndpoint, public AsyncStreamSink { +public: + void start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) final { + start_transfer({ + (unsigned char*)buffer.begin(), + buffer.size() + }, handle, completer); + } + + void cancel_write(TransferHandle transfer_handle) final { + cancel_transfer(transfer_handle); + } +}; + +} + +#endif // __FIBRE_USB_DISCOVERER_HPP \ No newline at end of file diff --git a/Firmware/fibre-cpp/platform_support/posix_socket.cpp b/Firmware/fibre-cpp/platform_support/posix_socket.cpp new file mode 100644 index 000000000..53475fcaf --- /dev/null +++ b/Firmware/fibre-cpp/platform_support/posix_socket.cpp @@ -0,0 +1,505 @@ + +#include "posix_socket.hpp" +#include "../logging.hpp" +#include "../print_utils.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DEFINE_LOG_TOPIC(SOCKET); +USE_LOG_TOPIC(SOCKET); + +#define MAX_CONCURRENT_CONNECTIONS 128 + +using namespace fibre; + +namespace fibre { +/** + * @brief Tag type to print the last socket error. + * + * This is very similar to sys_err(), except that on Windows it uses + * WSAGetLastError() instead of `errno` to fetch the last error code. + */ +struct sock_err { + sock_err() : +#if defined(_WIN32) || defined(_WIN64) + error_number(WSAGetLastError()) {} +#else + error_number(errno) {} +#endif + + sock_err(int error_number) : error_number(error_number) {} + + int error_number; +}; +} + +namespace std { +std::ostream& operator<<(std::ostream& stream, const struct sockaddr_storage& val) { + char buf[128]; + + if ((val.ss_family == AF_INET) && (inet_ntop(val.ss_family, ((struct sockaddr*)&val)->sa_data+2, buf, sizeof(buf)))) { + return stream << buf; + } else if ((val.ss_family == AF_INET6) && (inet_ntop(val.ss_family, ((struct sockaddr*)&val)->sa_data+6, buf, sizeof(buf)))) { + return stream << buf; + } else { + return stream << "(invalid address)"; + } +} + +std::ostream& operator<<(std::ostream& stream, const fibre::sock_err& err) { + return stream << strerror(err.error_number) << " (" << err.error_number << ")"; +} +} + +struct fibre::AddressResolutionContext { + struct addrinfo hints{}; + std::string address_str; + std::string port_str; + EventLoop* event_loop; + Callback> callback; + int cmpl_fd; + struct gaicb gaicb{}; + struct gaicb* list[1]; + + void on_gai_completed(); +}; + +bool fibre::start_resolving_address(EventLoop* event_loop, std::tuple address, bool passive, AddressResolutionContext** handle, Callback> callback) { + // deleted in on_gai_completed() + AddressResolutionContext* ctx = new AddressResolutionContext(); + + ctx->address_str = std::get<0>(address); + ctx->port_str = std::to_string(std::get<1>(address)); + ctx->event_loop = event_loop; + ctx->callback = callback; + + ctx->hints = { + .ai_flags = (passive ? AI_PASSIVE : 0), + .ai_family = AF_UNSPEC, + .ai_socktype = 0, // this makes apparently no difference for numerical addresses + }; + + ctx->gaicb = { + .ar_name = ctx->address_str.c_str(), + .ar_service = ctx->port_str.c_str(), + .ar_request = &ctx->hints + }; + ctx->list[0] = &ctx->gaicb; + + // An extra thread will be created once getaddrinfo_a() completes. This + // thread will post a callback onto the original event loop to do the actual + // handling of the result. This is of course exceedingly stupid but it's + // less bad than throwing around with actual signals that could hit threads + // that don't expect it. + + struct sigevent sig = { + .sigev_value = { .sival_ptr = ctx }, + //.sigev_signo = SIGRTMIN, + .sigev_notify = SIGEV_THREAD, + }; + sig.sigev_notify_function = [](union sigval sigval) { + auto ctx = ((AddressResolutionContext*)sigval.sival_ptr); + ctx->event_loop->post(MEMBER_CB(ctx, on_gai_completed)); + }; + + FIBRE_LOG(D) << "starting address resolution for " << ctx->address_str; + if (getaddrinfo_a(GAI_NOWAIT, ctx->list, 1, &sig) != 0) { + FIBRE_LOG(E) << "getaddrinfo_a() failed"; + delete ctx; + return false; + } + + return true; +} + +void fibre::cancel_resolving_address(AddressResolutionContext* handle) { + gai_cancel(&handle->gaicb); +} + +void AddressResolutionContext::on_gai_completed() { + FIBRE_LOG(D) << "address resolution complete"; + if (gai_error(&gaicb) != 0) { + FIBRE_LOG(W) << "failed to resolve " << address_str << ": " << sys_err(); + } else { + // this returns multiple addresses + for (struct addrinfo* addr = gaicb.ar_result; addr; addr = addr->ai_next) { + FIBRE_LOG(D) << "resolved IP: " << *(struct sockaddr_storage*)addr->ai_addr; + cbufptr_t buf = {(const uint8_t*)addr->ai_addr, (size_t)addr->ai_addrlen}; + callback.invoke(buf); + } + } + freeaddrinfo(gaicb.ar_result); + callback.invoke(std::nullopt); // Announce completion of the request + delete this; +} + +struct fibre::ConnectionContext { + EventLoop* event_loop; + socket_id_t socket_id; + Callback> callback; + + void on_connection_complete(uint32_t mask); + void on_accept(uint32_t mask); +}; + +bool fibre::start_connecting(EventLoop* event_loop, cbufptr_t addr, int type, int protocol, ConnectionContext** ctx, Callback> on_connected) { + auto the_addr = reinterpret_cast(addr.begin()); + + ConnectionContext* context = new ConnectionContext(); + context->event_loop = event_loop; + context->socket_id = socket(the_addr->sa_family, type | SOCK_NONBLOCK, protocol); + context->callback = on_connected; + + if (IS_INVALID_SOCKET(context->socket_id)) { + FIBRE_LOG(E) << "failed to open socket: " << sock_err(); + goto fail0; + } + + if (connect(context->socket_id, the_addr, addr.size()) == 0) { + if (errno != EINPROGRESS) { + FIBRE_LOG(E) << "connect() failed: " << sock_err(); + goto fail1; + } + } + + if (!event_loop->register_event(context->socket_id, EPOLLOUT, MEMBER_CB(context, on_connection_complete))) { + FIBRE_LOG(E) << "failed to register event: " << sock_err(); + goto fail1; + } + + if (ctx) { + *ctx = context; + } + + return true; + +fail1: + close(context->socket_id); +fail0: + delete context; + return false; +} + +void fibre::stop_connecting(ConnectionContext* ctx) { + if (!ctx->event_loop->deregister_event(ctx->socket_id)) { + FIBRE_LOG(W) << "failed to deregister event"; + } + if (close(ctx->socket_id) != 0) { + FIBRE_LOG(W) << "failed to close socket"; + } + ctx->socket_id = INVALID_SOCKET; + ctx->callback.invoke_and_clear(std::nullopt); + delete ctx; +} + +void fibre::ConnectionContext::on_connection_complete(uint32_t mask) { + bool failed; + int error_code; + socklen_t error_code_size = sizeof(error_code); + if (getsockopt(socket_id, SOL_SOCKET, SO_ERROR, &error_code, &error_code_size) != 0) { + FIBRE_LOG(W) << "connection failed (unknown error)"; + failed = true; + } else if (error_code != 0) { + FIBRE_LOG(W) << "connection failed: " << sock_err{error_code}; + failed = true; + } else { + failed = false; + } + + event_loop->deregister_event(socket_id); + callback.invoke(failed ? std::nullopt : std::make_optional(socket_id)); + close(socket_id); // The callback must duplicate the socket id if it intends + // to keep using it. + delete this; +} + +bool fibre::start_listening(EventLoop* event_loop, cbufptr_t addr, int type, int protocol, ConnectionContext** ctx, Callback> on_connected) { + auto the_addr = reinterpret_cast(addr.begin()); + int flag = 1; + + ConnectionContext* context = new ConnectionContext(); + context->event_loop = event_loop; + context->socket_id = socket(the_addr->sa_family, type | SOCK_NONBLOCK, protocol); + context->callback = on_connected; + + if (IS_INVALID_SOCKET(context->socket_id)) { + FIBRE_LOG(E) << "failed to open socket: " << sock_err(); + goto fail0; + } + + // Reuse local address. + // This helps reusing ports that were previously not closed cleanly and + // are therefore still lingering in the TIME_WAIT state. + if (setsockopt(context->socket_id, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag))) { + FIBRE_LOG(E) << "failed to make socket reuse addresses: " << sock_err(); + goto fail1; + } + + if (bind(context->socket_id, the_addr, addr.size())) { + FIBRE_LOG(E) << "failed to bind socket: " << sock_err(); + goto fail1; + } + + // make this socket a passive socket + if (listen(context->socket_id, MAX_CONCURRENT_CONNECTIONS) != 0) { + FIBRE_LOG(E) << "failed to listen on TCP: " << sys_err(); + goto fail1; + } + + if (!event_loop->register_event(context->socket_id, EPOLLIN, MEMBER_CB(context, on_accept))) { + FIBRE_LOG(E) << "failed to register event: " << sock_err(); + goto fail1; + } + + return true; + +fail1: + close(context->socket_id); +fail0: + delete context; + return false; +} + +void fibre::stop_listening(ConnectionContext* ctx) { + stop_connecting(ctx); // same implementation +} + +void fibre::ConnectionContext::on_accept(uint32_t mask) { + struct sockaddr_storage remote_addr; + socklen_t slen = sizeof(remote_addr); + + FIBRE_LOG(D) << "incoming TCP connection"; + int new_socket_id = accept(socket_id, reinterpret_cast(&remote_addr), &slen); + if (IS_INVALID_SOCKET(new_socket_id)) { + FIBRE_LOG(E) << "accept() returned invalid socket: " << sock_err(); + return; // ignore and wait for next incoming connection + } + + callback.invoke(std::make_optional(new_socket_id)); + close(new_socket_id); // The callback must duplicate the socket id if it intends + // to keep using it. +} + +bool PosixSocket::init(EventLoop* event_loop, socket_id_t socket_id) { + if (!IS_INVALID_SOCKET(socket_id_)) { + FIBRE_LOG(E) << "already initialized"; + return false; + } + + socket_id = dup(socket_id); + if (IS_INVALID_SOCKET(socket_id)) { + FIBRE_LOG(E) << "failed to duplicate socket: " << sock_err(); + return false; + } + + //if (!event_loop->register_event(socket_id, 0, MEMBER_CB(this, on_event))) { + // FIBRE_LOG(E) << "failed to register socket event"; + // close(socket_id); + // return false; + //} + + event_loop_ = event_loop; + socket_id_ = socket_id; + return true; +} + +bool PosixSocket::deinit() { + if (IS_INVALID_SOCKET(socket_id_)) { + FIBRE_LOG(E) << "not initialized"; + return false; + } + + bool result = true; + if (::close(socket_id_)) { + FIBRE_LOG(E) << "close() failed: " << sock_err(); + result = false; + } + + socket_id_ = INVALID_SOCKET; + return result; +} + +void PosixSocket::start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) { + if (rx_callback_) { + FIBRE_LOG(E) << "RX request already pending"; + completer.invoke({kStreamError}); + return; + } + + if (handle) { + *handle = reinterpret_cast(this); + } + + auto result = read_sync(buffer); + if (result.has_value()) { + completer.invoke(*result); + } else { + rx_buf_ = buffer; + rx_callback_ = completer; + update_subscription(); + } +} + +void PosixSocket::cancel_read(TransferHandle transfer_handle) { + if (transfer_handle != reinterpret_cast(this)) { + FIBRE_LOG(E) << "invalid handle"; + } else if (!rx_callback_) { + FIBRE_LOG(E) << "no RX pending"; + } else { + rx_callback_.invoke_and_clear({kStreamCancelled, rx_buf_.begin()}); + } +} + +void PosixSocket::start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) { + if (tx_callback_) { + FIBRE_LOG(E) << "TX request already pending"; + completer.invoke({kStreamError}); + return; + } + + if (handle) { + *handle = reinterpret_cast(this); + } + + auto result = write_sync(buffer); + if (result.has_value()) { + completer.invoke(*result); + } else { + tx_buf_ = buffer; + tx_callback_ = completer; + update_subscription(); + } +} + +void PosixSocket::cancel_write(TransferHandle transfer_handle) { + if (transfer_handle != reinterpret_cast(this)) { + FIBRE_LOG(E) << "invalid handle"; + } else if (!tx_callback_) { + FIBRE_LOG(E) << "no TX pending"; + } else { + tx_callback_.invoke_and_clear({kStreamCancelled, tx_buf_.begin()}); + } +} + +std::optional PosixSocket::read_sync(bufptr_t buffer) { + if (buffer.size() == 0) { + // Empty buffers mess with our socket-close detection + FIBRE_LOG(W) << "empty buffer not permitted"; + } + + socklen_t slen = sizeof(remote_addr_); + ssize_t n_received = recvfrom(socket_id_, buffer.begin(), buffer.size(), + MSG_DONTWAIT, reinterpret_cast(&remote_addr_), &slen); + + if (n_received < 0) { + // If recvfrom returns -1 an errno is set to indicate the error. + auto err = sock_err{}; + if (err.error_number == EAGAIN || err.error_number == EWOULDBLOCK) { + return std::nullopt; + } else { + FIBRE_LOG(E) << "Socket read failed: " << err; + return {{kStreamError, buffer.end()}}; // the function might have written to the buffer + } + + } else if ((size_t)n_received > buffer.size()) { + FIBRE_LOG(E) << "received too many bytes"; + return {{kStreamError, buffer.end()}}; + + } else if (n_received == 0) { + FIBRE_LOG(D) << "socket closed (RX half)"; + return {{kStreamClosed, buffer.begin()}}; + + } else { + FIBRE_LOG(D) << "Received " << n_received << " bytes from " << remote_addr_; + return {{kStreamOk, buffer.begin() + n_received}}; + } +} + +std::optional PosixSocket::write_sync(cbufptr_t buffer) { + if (buffer.size() == 0) { + // Empty buffers mess with our socket-close detection + FIBRE_LOG(W) << "empty buffer not permitted"; + } + + int n_sent = sendto(socket_id_, buffer.begin(), buffer.size(), MSG_DONTWAIT, + reinterpret_cast(&remote_addr_), sizeof(remote_addr_)); + if (n_sent < 0) { + // If sendto returns -1 an errno is set to indicate the error. + auto err = sock_err{}; + if (err.error_number == EAGAIN || err.error_number == EWOULDBLOCK) { + return std::nullopt; + } else { + FIBRE_LOG(E) << "Socket write failed: " << err; + return {{kStreamError, buffer.end()}}; // the function might have written to the buffer + } + + } else if ((size_t)n_sent > buffer.size()) { + FIBRE_LOG(E) << "sent too many bytes"; + return {{kStreamError, buffer.end()}}; + + } else if (n_sent == 0) { + FIBRE_LOG(D) << "socket closed (TX half)"; + return {{kStreamClosed, buffer.begin()}}; + + } else { + FIBRE_LOG(D) << "Sent " << n_sent << " bytes to " << remote_addr_; + return {{kStreamOk, buffer.begin() + n_sent}}; + } +} + +void PosixSocket::update_subscription() { + uint32_t new_mask = (tx_callback_ ? EPOLLOUT : 0) + | (rx_callback_ ? EPOLLIN : 0); + if (new_mask != mask_) { + if (mask_) { + event_loop_->deregister_event(socket_id_); + } + mask_ = new_mask; + if (new_mask) { + event_loop_->register_event(socket_id_, new_mask, MEMBER_CB(this, on_event)); + } + } +} + +void PosixSocket::on_event(uint32_t mask) { + + if (mask & EPOLLIN) { + // The socket is ready for RX. If an RX request is pending, handle it + // here, otherwise ignore the event. + + if (rx_callback_) { + auto result = read_sync(rx_buf_); + rx_buf_ = {}; + if (result.has_value()) { + rx_callback_.invoke_and_clear(*result); + } + } + } + + if (mask & EPOLLOUT) { + // The socket is ready for RX. If an RX request is pending, handle it + // here, otherwise ignore the event. + + if (tx_callback_) { + auto result = write_sync(tx_buf_); + tx_buf_ = {}; + if (result.has_value()) { + tx_callback_.invoke_and_clear(*result); + } + } + } + + if (mask & ~(EPOLLIN | EPOLLOUT)) { + FIBRE_LOG(E) << "unknown event mask: " << as_hex(mask); + } + + update_subscription(); +} diff --git a/Firmware/fibre-cpp/platform_support/posix_socket.hpp b/Firmware/fibre-cpp/platform_support/posix_socket.hpp new file mode 100644 index 000000000..457c1e637 --- /dev/null +++ b/Firmware/fibre-cpp/platform_support/posix_socket.hpp @@ -0,0 +1,173 @@ +#ifndef __FIBRE_POSIX_SOCKET_HPP +#define __FIBRE_POSIX_SOCKET_HPP + +#include +#include +#include +#include +#include +#include + +namespace fibre { + + +#if defined(__linux__) +//using PosixSocketWorker = LinuxWorker; // TODO: rename to EPollWorker or LinuxEPollWorker +using socket_id_t = int; +#elif defined(_WIN32) || defined(_WIN64) +//using PosixSocketWorker = PosixPollWorker; +using socket_id_t = SOCKET; +#else +//using PosixSocketWorker = KQueueWorker; +using socket_id_t = int; +#endif + +#if defined(_Win32) || defined(_Win64) +#define IS_INVALID_SOCKET(socket_id) (socket_id == INVALID_SOCKET) +#else +#define INVALID_SOCKET (-1) +#define IS_INVALID_SOCKET(socket_id) (socket_id < 0) +#endif + +struct AddressResolutionContext; +struct ConnectionContext; + +/** + * @brief Starts resolving a hostname (such as www.google.com) to one or + * multiple IP addresses. + * + * If available, both IPv4 and IPv6 addresses are returned. + * + * @param passive: If false, the returned address will be suitable for use with + * connect(2), sendto(2), or sendmsg(2). + * @param callback: Invoked for every address that is found. Invoked with null + * if no more addresses are available, including in case of an error or + * cancellation. + * + * @returns: false if the lookup could not be started. `callback` will not be + * called. + */ +bool start_resolving_address(EventLoop* event_loop, + std::tuple address, bool passive, + AddressResolutionContext** handle, + Callback> callback); + +/** + * @brief Cancels the ongoing address resolution. + * + * The cancellation is complete once the associated callback is invoked with + * null. + */ +void cancel_resolving_address(AddressResolutionContext* handle); + +/** + * @brief Starts connecting to the specified address + * + * @param addr: The address to connect to. Usually this buffer contains an + * address of the type `struct sockaddr`. The family parameter of this + * address will be passed as 1st argument to socket(). + * @param type: Will be passed as 2nd argument to socket(). Can be for + * instance SOCK_DGRAM or SOCKET_STREAM. + * @param protocol: Will be passed as 3rd argument to socket(). Can be for + * instance IPPROTO_UDP or IPPROTO_TCP. + * @param on_connected: Called when the connection attempt succeeds or fails. + * If the connection was established, the socket ID is passed to the + * callback. This socket ID will only be valid for the duration of the + * callback and must be duplicated (dup) if the application intends to + * keep using it. + * If the connection failed, std::nullopt is passed. + */ +bool start_connecting(EventLoop* event_loop, cbufptr_t addr, int type, int protocol, ConnectionContext** ctx, Callback> on_connected); +void stop_connecting(ConnectionContext* ctx); + +/** + * @brief Starts listening and accepting connections on the specified local + * address. + * + * @param addr: The local address to listen on. Usually this buffer contains an + * address of the type `struct sockaddr`. The family parameter of this + * address will be passed as 1st argument to socket(). + * @param type: Will be passed as 2nd argument to socket(). Can be for + * instance SOCK_DGRAM or SOCKET_STREAM. + * @param protocol: Will be passed as 3rd argument to socket(). Can be for + * instance IPPROTO_UDP or IPPROTO_TCP. + * @param on_connected: Called for every connection that is accepted. The new + * socket ID is passed to the callback. This socket ID will only be valid + * for the duration of the callback and must be duplicated (dup) if the + * application intends to keep using it. + * If the attempt to listen fails permanently or is cancelled, + * std::nullopt is passed. + */ +bool start_listening(EventLoop* event_loop, cbufptr_t addr, int type, int protocol, ConnectionContext** ctx, Callback> on_connected); +void stop_listening(ConnectionContext* ctx); + +/** + * @brief AsyncStreamSource and AsyncStreamSink based on a Posix or WinSock + * socket ID. + * + * Note: To make this work on Windows, a "poll"-based worker must be implemented. + */ +class PosixSocket final : public AsyncStreamSource, public AsyncStreamSink { +public: + /** + * @brief Initializes the object with the given socket ID. + * + * The socket must be bound to a local address before this function is + * called. + * + * @param socket_id: For Unix-like systems this should be a file descriptor, + * for Windows this should be a Windows Socket ID (as returned by + * socket()). + * The socket must be in non-blocking mode (opened with O_NONBLOCK). + * The socket will internally be duplicated using dup() so it can be + * closed after this call. + */ + bool init(EventLoop* event_loop, socket_id_t socket_id); + + /** + * @brief Deinits a socket that was initialized with init(). + */ + bool deinit(); + + void start_read(bufptr_t buffer, TransferHandle* handle, Callback completer) final; + void cancel_read(TransferHandle transfer_handle) final; + + void start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) final; + void cancel_write(TransferHandle transfer_handle) final; + + /** + * @brief Returns the remote address of this socket. + * + * For connectionless sockets this is origin of the most recently received + * data and it is only valid from the moment something was actually received. + * + * For connection-oriented sockets this address is valid as soon as the + * socket is initialized. + */ + struct sockaddr_storage get_remote_address() const { return remote_addr_; } + +private: + std::optional read_sync(bufptr_t buffer); + std::optional write_sync(cbufptr_t buffer); + void update_subscription(); + void on_event(uint32_t mask); + + int socket_id_ = INVALID_SOCKET; + EventLoop* event_loop_ = nullptr; + struct sockaddr_storage remote_addr_ = {0}; // updated after each RX event + uint32_t mask_ = 0; // current event subscription mask + bufptr_t rx_buf_{}; // valid while there is an RX request pending + cbufptr_t tx_buf_{}; // valid while there is a TX request pending + Callback rx_callback_; // valid while there is an RX request pending + Callback tx_callback_; // valid while there is a TX request pending +}; + +} + +#include + +namespace std { +std::ostream& operator<<(std::ostream& stream, const struct sockaddr_storage& val); +} + +#endif // __FIBRE_POSIX_SOCKET_HPP \ No newline at end of file diff --git a/Firmware/fibre-cpp/platform_support/posix_tcp_backend.cpp b/Firmware/fibre-cpp/platform_support/posix_tcp_backend.cpp new file mode 100644 index 000000000..dfec0392a --- /dev/null +++ b/Firmware/fibre-cpp/platform_support/posix_tcp_backend.cpp @@ -0,0 +1,138 @@ + +#include "posix_tcp_backend.hpp" +#include "posix_socket.hpp" +#include "../logging.hpp" +#include +#include +#include +#include +#include + +DEFINE_LOG_TOPIC(TCP); +USE_LOG_TOPIC(TCP); + +using namespace fibre; + +bool PosixTcpBackend::init(EventLoop* event_loop) { + if (event_loop_) { + FIBRE_LOG(E) << "already initialized"; + return false; + } + event_loop_ = event_loop; + return true; +} + +bool PosixTcpBackend::deinit() { + if (!event_loop_) { + FIBRE_LOG(E) << "not initialized"; + return false; + } + if (n_discoveries_) { + FIBRE_LOG(W) << "some discoveries still ongoing"; + } + event_loop_ = nullptr; + return true; +} + +void PosixTcpBackend::start_channel_discovery(Domain* domain, const char* specs, size_t specs_len, ChannelDiscoveryContext** handle) { + const char* address_begin; + const char* address_end; + int port; + + if (!event_loop_) { + FIBRE_LOG(E) << "not initialized"; + //on_found_channels.invoke({kFibreInvalidArgument, nullptr, nullptr, 0}); + return; // TODO: error reporting + } + + if (!try_parse_key(specs, specs + specs_len, "address", &address_begin, &address_end)) { + FIBRE_LOG(E) << "no address specified"; + //on_found_channels.invoke({kFibreInvalidArgument, nullptr, nullptr, 0}); + return; // TODO: error reporting + } + + if (!try_parse_key(specs, specs + specs_len, "port", &port)) { + FIBRE_LOG(E) << "no port specified"; + //on_found_channels.invoke({kFibreInvalidArgument, nullptr, nullptr, 0}); + return; // TODO: error reporting + } + + n_discoveries_++; + + TcpChannelDiscoveryContext* ctx = new TcpChannelDiscoveryContext(); // TODO: free + ctx->parent = this; + ctx->address = {{address_begin, address_end}, port}; + ctx->domain = domain; + ctx->resolve_address(); +} + +int PosixTcpBackend::stop_channel_discovery(ChannelDiscoveryContext* handle) { + // TODO + n_discoveries_--; + return 0; +} + +void PosixTcpBackend::TcpChannelDiscoveryContext::resolve_address() { + if (addr_resolution_ctx) { + FIBRE_LOG(E) << "already resolving"; + return; + } + + if (!start_resolving_address(parent->event_loop_, address, false, &addr_resolution_ctx, MEMBER_CB(this, on_found_address))) { + FIBRE_LOG(E) << "cannot start address resolution"; + return; + } +} + +void PosixTcpBackend::TcpChannelDiscoveryContext::on_found_address(std::optional addr) { + FIBRE_LOG(D) << "found address"; + + if (addr.has_value()) { + // Resolved an address. If it wasn't already known, try to connect to it. + std::vector vec{addr->begin(), addr->end()}; + bool is_known = std::find_if(known_addresses.begin(), known_addresses.end(), + [&](AddrContext& val){ return val.addr == vec; }) != known_addresses.end(); + + if (!is_known) { + AddrContext ctx = {.addr = vec}; + if (parent->start_opening_connections(parent->event_loop_, *addr, SOCK_STREAM, IPPROTO_TCP, &ctx.connection_ctx, MEMBER_CB(this, on_connected))) { + known_addresses.push_back(ctx); + } else { + // TODO + } + } + } else { + // No more addresses. + addr_resolution_ctx = nullptr; + if (known_addresses.size() == 0) { + // No addresses could be found. Try again using exponential backoff. + parent->event_loop_->call_later(lookup_period, MEMBER_CB(this, resolve_address)); + lookup_period = std::min(lookup_period * 3.0f, 3600.0f); // exponential backoff with at most 1h period + } else { + // Some addresses are known from this lookup or from a previous + // lookup. Resolve addresses again in 1h. + parent->event_loop_->call_later(3600.0, MEMBER_CB(this, resolve_address)); + } + } +} + +void PosixTcpBackend::TcpChannelDiscoveryContext::on_connected(std::optional socket_id) { + if (socket_id.has_value()) { + auto socket = new PosixSocket{}; // TODO: free + if (socket->init(parent->event_loop_, *socket_id)) { + domain->add_channels({kFibreOk, socket, socket, SIZE_MAX}); + return; + } + delete socket; + } + + FIBRE_LOG(D) << "not connected"; + // Try to reconnect soon + lookup_period = 1.0f; + resolve_address(); +} + +void PosixTcpBackend::TcpChannelDiscoveryContext::on_disconnected() { + lookup_period = 1.0f; // reset exponential backoff + resolve_address(); +} diff --git a/Firmware/fibre-cpp/platform_support/posix_tcp_backend.hpp b/Firmware/fibre-cpp/platform_support/posix_tcp_backend.hpp new file mode 100644 index 000000000..12e771c68 --- /dev/null +++ b/Firmware/fibre-cpp/platform_support/posix_tcp_backend.hpp @@ -0,0 +1,80 @@ +#ifndef __FIBRE_POSIX_TCP_BACKEND_HPP +#define __FIBRE_POSIX_TCP_BACKEND_HPP + +#include +#include "posix_socket.hpp" +#include +#include +#include + +namespace fibre { + +/** + * TCP client and TCP server implementations are identical up to the function + * that is used to convert an address to one or more connected socket IDs. + * The client uses the posix function `connect` to do so, while the server uses + * the posix functions `listen` and `accept`. + */ +class PosixTcpBackend : public ChannelDiscoverer { +public: + bool init(EventLoop* event_loop); + bool deinit(); + + void start_channel_discovery(Domain* domain, const char* specs, size_t specs_len, ChannelDiscoveryContext** handle) final; + int stop_channel_discovery(ChannelDiscoveryContext* handle) final; + +private: + struct TcpChannelDiscoveryContext { + PosixTcpBackend* parent; + std::tuple address; + Domain* domain; + AddressResolutionContext* addr_resolution_ctx; + ConnectionContext* connection_ctx; + float lookup_period = 1.0f; // wait 1s for next address resolution + + struct AddrContext { + std::vector addr; + ConnectionContext* connection_ctx; + }; + + std::vector known_addresses; + void resolve_address(); + void on_found_address(std::optional addr); + void on_connected(std::optional socket_id); + void on_disconnected(); + }; + + virtual bool start_opening_connections(EventLoop* event_loop, cbufptr_t addr, int type, int protocol, ConnectionContext** ctx, Callback> on_connected) = 0; + virtual void cancel_opening_connections(ConnectionContext* ctx) = 0; + + EventLoop* event_loop_ = nullptr; + size_t n_discoveries_ = 0; +}; + +class PosixTcpClientBackend : public PosixTcpBackend { +public: + constexpr static const char* get_name() { return "tcp-client"; } + + bool start_opening_connections(EventLoop* event_loop, cbufptr_t addr, int type, int protocol, ConnectionContext** ctx, Callback> on_connected) final { + return start_connecting(event_loop, addr, type, protocol, ctx, on_connected); + } + void cancel_opening_connections(ConnectionContext* ctx) final { + stop_connecting(ctx); + } +}; + +class PosixTcpServerBackend : public PosixTcpBackend { +public: + constexpr static const char* get_name() { return "tcp-server"; } + + bool start_opening_connections(EventLoop* event_loop, cbufptr_t addr, int type, int protocol, ConnectionContext** ctx, Callback> on_connected) final { + return start_listening(event_loop, addr, type, protocol, ctx, on_connected); + } + void cancel_opening_connections(ConnectionContext* ctx) final { + stop_listening(ctx); + } +}; + +} + +#endif // __FIBRE_POSIX_TCP_BACKEND_HPP \ No newline at end of file diff --git a/Firmware/fibre-cpp/print_utils.hpp b/Firmware/fibre-cpp/print_utils.hpp new file mode 100644 index 000000000..398256315 --- /dev/null +++ b/Firmware/fibre-cpp/print_utils.hpp @@ -0,0 +1,137 @@ +#ifndef __FIBRE_PRINT_UTILS_HPP +#define __FIBRE_PRINT_UTILS_HPP + +#include +#include +#include + +namespace fibre { + +template +constexpr size_t hex_digits() { + return (std::numeric_limits::digits + 3) / 4; +} + +/* @brief Converts a hexadecimal digit to a uint8_t. +* @param output If not null, the digit's value is stored in this output +* Returns true if the char is a valid hex digit, false otherwise +*/ +static inline bool hex_digit_to_byte(char ch, uint8_t* output) { + uint8_t nil_output = 0; + if (!output) + output = &nil_output; + if (ch >= '0' && ch <= '9') + return (*output) = ch - '0', true; + if (ch >= 'a' && ch <= 'f') + return (*output) = ch - 'a' + 10, true; + if (ch >= 'A' && ch <= 'F') + return (*output) = ch - 'A' + 10, true; + return false; +} + +/* @brief Converts a hex string to an integer +* @param output If not null, the result is stored in this output +* Returns true if the string represents a valid hex value, false otherwise. +*/ +template +bool hex_string_to_int(const char * str, size_t length, TInt* output) { + constexpr size_t N_DIGITS = hex_digits(); + TInt result = 0; + if (length > N_DIGITS) + length = N_DIGITS; + for (size_t i = 0; i < length && str[i]; i++) { + uint8_t digit = 0; + if (!hex_digit_to_byte(str[i], &digit)) + return false; + result <<= 4; + result += digit; + } + if (output) + *output = result; + return true; +} + +template +bool hex_string_to_int(const char * str, TInt* output) { + return hex_string_to_int(str, hex_digits(), output); +} + +template +bool hex_string_to_int_arr(const char * str, size_t length, TInt (&output)[ICount]) { + for (size_t i = 0; i < ICount; i++) { + if (!hex_string_to_int(&str[i * hex_digits()], &output[i])) + return false; + } + return true; +} + +template +bool hex_string_to_int_arr(const char * str, TInt (&output)[ICount]) { + return hex_string_to_int_arr(str, hex_digits() * ICount, output); +} + +// TODO: move to print_utils.hpp +template +class HexPrinter { +public: + HexPrinter(T val, bool prefix) : val_(val) /*, prefix_(prefix)*/ { + const char digits[] = "0123456789abcdef"; + size_t prefix_length = prefix ? 2 : 0; + if (prefix) { + str[0] = '0'; + str[1] = 'x'; + } + str[prefix_length + hex_digits()] = '\0'; + + for (size_t i = 0; i < hex_digits(); ++i) { + str[prefix_length + hex_digits() - i - 1] = digits[val & 0xf]; + val >>= 4; + } + } + std::string to_string() const { return str; } + void to_string(char* buf) const { + for (size_t i = 0; (i < sizeof(str)) && str[i]; ++i) + buf[i] = str[i]; + } + + T val_; + //bool prefix_; + char str[hex_digits() + 3]; // 3 additional characters 0x and \0 +}; + +template +std::ostream& operator<<(std::ostream& stream, const HexPrinter& printer) { + // TODO: specialize for char + return stream << printer.to_string(); +} + +template +HexPrinter as_hex(T val, bool prefix = true) { return HexPrinter(val, prefix); } + +template +class HexArrayPrinter { +public: + HexArrayPrinter(T* ptr, size_t length) : ptr_(ptr), length_(length) {} + T* ptr_; + size_t length_; +}; + +template +TStream& operator<<(TStream& stream, const HexArrayPrinter& printer) { + for (size_t pos = 0; pos < printer.length_; ++pos) { + stream << " " << as_hex(printer.ptr_[pos]); + if (((pos + 1) % 16) == 0) + stream << "\n"; + } + return stream; +} + +template +HexArrayPrinter as_hex(T (&val)[ILength]) { return HexArrayPrinter(val, ILength); } + +template +HexArrayPrinter as_hex(generic_bufptr_t buffer) { return HexArrayPrinter(buffer.begin(), buffer.size()); } + +} + +#endif // __FIBRE_PRINT_UTILS_HPP \ No newline at end of file diff --git a/Firmware/fibre-cpp/protocol.hpp b/Firmware/fibre-cpp/protocol.hpp new file mode 100644 index 000000000..f37d3c1c5 --- /dev/null +++ b/Firmware/fibre-cpp/protocol.hpp @@ -0,0 +1,301 @@ +/* +see protocol.md for the protocol specification +*/ + +#ifndef __PROTOCOL_HPP +#define __PROTOCOL_HPP + +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct { + uint16_t json_crc; + uint16_t endpoint_id; +} endpoint_ref_t; + + +namespace fibre { +// These symbols are defined in the autogenerated endpoints.hpp +extern const unsigned char embedded_json[]; +extern const size_t embedded_json_length; +extern const uint16_t json_crc_; +extern const uint32_t json_version_id_; +bool endpoint_handler(int idx, cbufptr_t* input_buffer, bufptr_t* output_buffer); +bool endpoint0_handler(cbufptr_t* input_buffer, bufptr_t* output_buffer); +bool is_endpoint_ref_valid(endpoint_ref_t endpoint_ref); +bool set_endpoint_from_float(endpoint_ref_t endpoint_ref, float value); +} + + + +namespace fibre { +template +struct Codec { + static std::optional decode(cbufptr_t* buffer) { return std::nullopt; } +}; + +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { return (buffer->begin() == buffer->end()) ? std::nullopt : std::make_optional((bool)*(buffer->begin()++)); } + static bool encode(bool value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } +}; +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } + static bool encode(int8_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } +}; +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } + static bool encode(uint8_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } +}; +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } + static bool encode(int16_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } +}; +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } + static bool encode(uint16_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } +}; +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } + static bool encode(int32_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } +}; +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } + static bool encode(uint32_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } +}; +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } + static bool encode(int64_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } +}; +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } + static bool encode(uint64_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } +}; +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { + std::optional int_val = Codec::decode(buffer); + return int_val.has_value() ? std::optional(*reinterpret_cast(&*int_val)) : std::nullopt; + } + static bool encode(float value, bufptr_t* buffer) { + void* ptr = &value; + return Codec::encode(*reinterpret_cast(ptr), buffer); + } +}; +template +struct Codec::value>> { + using int_type = std::underlying_type_t; + static std::optional decode(cbufptr_t* buffer) { + std::optional int_val = SimpleSerializer::read(&(buffer->begin()), buffer->end()); + return int_val.has_value() ? std::make_optional(static_cast(*int_val)) : std::nullopt; + } + static bool encode(T value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } +}; +template<> struct Codec { + static std::optional decode(cbufptr_t* buffer) { + std::optional val0 = SimpleSerializer::read(&(buffer->begin()), buffer->end()); + std::optional val1 = SimpleSerializer::read(&(buffer->begin()), buffer->end()); + return (val0.has_value() && val1.has_value()) ? std::make_optional(endpoint_ref_t{*val1, *val0}) : std::nullopt; + } + static bool encode(endpoint_ref_t value, bufptr_t* buffer) { + return SimpleSerializer::write(value.endpoint_id, &(buffer->begin()), buffer->end()) + && SimpleSerializer::write(value.json_crc, &(buffer->begin()), buffer->end()); + } +}; +} + + +/* ToString / FromString functions -------------------------------------------*/ +/* +* These functions are currently not used by Fibre and only here to +* support the ODrive ASCII protocol. +* TODO: find a general way for client code to augment endpoints with custom +* functions +*/ + +template +struct format_traits_t; + +// template<> struct format_traits_t { using type = void; +// static constexpr const char * fmt = "%f"; +// static constexpr const char * fmtp = "%f"; +// }; +template<> struct format_traits_t { using type = void; + static constexpr const char * fmt = "%lld"; + static constexpr const char * fmtp = "%lld"; +}; +template<> struct format_traits_t { using type = void; + static constexpr const char * fmt = "%llu"; + static constexpr const char * fmtp = "%llu"; +}; +template<> struct format_traits_t { using type = void; + static constexpr const char * fmt = "%ld"; + static constexpr const char * fmtp = "%ld"; +}; +template<> struct format_traits_t { using type = void; + static constexpr const char * fmt = "%lu"; + static constexpr const char * fmtp = "%lu"; +}; +template<> struct format_traits_t { using type = void; + static constexpr const char * fmt = "%d"; + static constexpr const char * fmtp = "%d"; +}; +template<> struct format_traits_t { using type = void; + static constexpr const char * fmt = "%ud"; + static constexpr const char * fmtp = "%ud"; +}; +template<> struct format_traits_t { using type = void; + static constexpr const char * fmt = "%hd"; + static constexpr const char * fmtp = "%hd"; +}; +template<> struct format_traits_t { using type = void; + static constexpr const char * fmt = "%hu"; + static constexpr const char * fmtp = "%hu"; +}; +template<> struct format_traits_t { using type = void; + static constexpr const char * fmt = "%hhd"; + static constexpr const char * fmtp = "%d"; +}; +template<> struct format_traits_t { using type = void; + static constexpr const char * fmt = "%hhu"; + static constexpr const char * fmtp = "%u"; +}; + +template::type> +static bool to_string(const T& value, char * buffer, size_t length, int) { + snprintf(buffer, length, format_traits_t::fmtp, value); + return true; +} +// Special case for float because printf promotes float to double, and we get warnings +template +static bool to_string(const float& value, char * buffer, size_t length, int) { + snprintf(buffer, length, "%f", (double)value); + return true; +} +template +static bool to_string(const bool& value, char * buffer, size_t length, int) { + buffer[0] = value ? '1' : '0'; + buffer[1] = 0; + return true; +} +template +static bool to_string(const T& value, char * buffer, size_t length, ...) { + return false; +} + +template::type> +static bool from_string(const char * buffer, size_t length, T* property, int) { + // Note for T == uint8_t: Even though we supposedly use the correct format + // string sscanf treats our pointer as pointer-to-int instead of + // pointer-to-uint8_t. To avoid an unexpected memory access we first read + // into a union. + union { T t; int i; } val; + if (sscanf(buffer, format_traits_t::fmt, &val.t) == 1) { + *property = val.t; + return true; + } else { + return false; + } +} +// Special case for float because printf promotes float to double, and we get warnings +template +static bool from_string(const char * buffer, size_t length, float* property, int) { + return sscanf(buffer, "%f", property) == 1; +} +template +static bool from_string(const char * buffer, size_t length, bool* property, int) { + int val; + if (sscanf(buffer, "%d", &val) != 1) + return false; + *property = val; + return true; +} +template +static bool from_string(const char * buffer, size_t length, T* property, ...) { + return false; +} + + +//template +//bool set_from_float_ex(float value, T* property) { +// return false; +//} + +namespace conversion { +//template +template +bool set_from_float_ex(float value, float* property, int) { + return *property = value, true; +} +template +bool set_from_float_ex(float value, bool* property, int) { + return *property = (value >= 0.0f), true; +} +template::value && !std::is_const::value>> +bool set_from_float_ex(float value, T* property, int) { + return *property = static_cast(std::round(value)), true; +} +template +bool set_from_float_ex(float value, T* property, ...) { + return false; +} +template +bool set_from_float(float value, T* property) { + return set_from_float_ex(value, property, 0); +} +} + + +template +struct Property { + Property(void* ctx, T(*getter)(void*), void(*setter)(void*, T)) + : ctx_(ctx), getter_(getter), setter_(setter) {} + Property(T* ctx) + : ctx_(ctx), getter_([](void* ctx){ return *(T*)ctx; }), setter_([](void* ctx, T val){ *(T*)ctx = val; }) {} + Property& operator*() { return *this; } + Property* operator->() { return this; } + + T read() const { + return (*getter_)(ctx_); + } + + T exchange(std::optional value) const { + T old_value = (*getter_)(ctx_); + if (value.has_value()) { + (*setter_)(ctx_, *value); + } + return old_value; + } + + void* ctx_; + T(*getter_)(void*); + void(*setter_)(void*, T); +}; + +template +struct Property { + Property(void* ctx, T(*getter)(void*)) + : ctx_(ctx), getter_(getter) {} + Property(const T* ctx) + : ctx_(const_cast(ctx)), getter_([](void* ctx){ return *(const T*)ctx; }) {} + Property& operator*() { return *this; } + Property* operator->() { return this; } + + T read() const { + return (*getter_)(ctx_); + } + + void* ctx_; + T(*getter_)(void*); +}; + + +#endif diff --git a/Firmware/fibre-cpp/stream_utils.hpp b/Firmware/fibre-cpp/stream_utils.hpp new file mode 100644 index 000000000..681a521ac --- /dev/null +++ b/Firmware/fibre-cpp/stream_utils.hpp @@ -0,0 +1,182 @@ +#ifndef __FIBRE_STREAM_UTILS_HPP +#define __FIBRE_STREAM_UTILS_HPP + +#include +#include + +namespace fibre { + +template +class BufferedStreamSink { +public: + BufferedStreamSink(AsyncStreamSink& sink) : sink_(sink) {} + + /** + * @brief Enqueues as much of the specified buffer as possible. + * + * Thread safety: only one write call is allowed at a time. The write call + * can be on a different thread from the underlying stream's event loop. + * (TODO: this is not true yet, see comment in function) + */ + void write(cbufptr_t buf) { + // We subtract 1 from the read index because we never want the write + // pointer to catch up with the read pointer, cause then + // `write_idx_ == read_idx_` could mean both "full" and "empty". + + size_t read_idx = (read_idx_ + I - 1) % I; // read_idx_ could change during this function + + if (write_idx_ > read_idx) { + size_t n_copy = std::min(I - write_idx_, buf.size()); + memcpy(buffer_ + write_idx_, buf.begin(), n_copy); + write_idx_ = (write_idx_ + n_copy) % I; + buf = buf.skip(n_copy); + } + + size_t n_copy = std::min(read_idx - write_idx_, buf.size()); + memcpy(buffer_ + write_idx_, buf.begin(), n_copy); + write_idx_ = (write_idx_ + n_copy) % I; + + //if (!__atomic_exchange_n(&is_active_, true, __ATOMIC_SEQ_CST)) { + // // TODO: calling the sink in here breaks the rule that async + // // functions must only be called on the event loop thread. + // // But to do that we need to implement a proper event loop where we + // // can enqueue calls. + // maybe_start_async_write(); + //} + } + + void maybe_start_async_write() { + if (is_active_) { + // nothing to do + } else if (read_idx_ < write_idx_) { + is_active_ = true; + sink_.start_write({buffer_ + read_idx_, buffer_ + write_idx_}, &transfer_handle_, MEMBER_CB(this, on_write_complete)); + } else if (read_idx_ > write_idx_) { + is_active_ = true; + sink_.start_write({buffer_ + read_idx_, buffer_ + I}, &transfer_handle_, MEMBER_CB(this, on_write_complete)); + } else { + // nothing to do + } + } + +private: + void on_write_complete(WriteResult result) { + is_active_ = false; + transfer_handle_ = 0; + + if (result.status == kStreamOk) { + if (result.end < buffer_ || result.end > (buffer_ + I)) { + for (;;) + transfer_handle_ = 0; + } + read_idx_ = (result.end - buffer_) % I; + maybe_start_async_write(); + } + } + + uint8_t buffer_[I]; + + // Both indices are in [0, I) + // They are equal if the buffer is empty (no valid data). + size_t write_idx_ = 0; // [0, I) + size_t read_idx_ = 0; // [0, I) + bool is_active_ = false; + TransferHandle transfer_handle_ = 0; + + AsyncStreamSink& sink_; +}; + +/** + * @brief Buffers up to NSlots concurrent async write requests. + * + * This can be used to wrap sinks that can only handle one concurrent write + * operation at a time but are written to by multiple independent sources. + */ +template +class AsyncStreamSinkMultiplexer : public AsyncStreamSink { +public: + AsyncStreamSinkMultiplexer(AsyncStreamSink& sink) : sink_(sink) {} + + void start_write(cbufptr_t buffer, TransferHandle* handle, Callback completer) final { + for (size_t i = 0; i < NSlots; ++i) { + auto& [slot_in_use, slot_buf, slot_completer] = slots_[i]; + if (!__atomic_exchange_n(&slot_in_use, true, __ATOMIC_SEQ_CST)) { + slot_buf = buffer; + slot_completer = completer; + + if (handle) { + *handle = i + 1; // returning a valid handle of 0 is not a good idea + } + + // If the underlying sink wasn't busy, start it now. + if (active_slot_ == 0) { + active_slot_ = i + 1; + sink_.start_write(slot_buf, &transfer_handle_, MEMBER_CB(this, on_write_complete)); + } + + return; + } + } + + if (handle) { + *handle = 0; + } + completer.invoke({kStreamError, buffer.begin()}); + } + + void cancel_write(TransferHandle transfer_handle) final { + if (transfer_handle == active_slot_) { + // This transfer is the one that the underlying sink is busy with. + sink_.cancel_write(transfer_handle_); + } else { + // This transfer is only enqueued but not yet started. + auto& [slot_in_use, slot_buf, slot_completer] = slots_[transfer_handle - 1]; + auto completer = slot_completer; + auto end = slot_buf.end(); + slot_in_use = false; + completer.invoke_and_clear({kStreamCancelled, end}); + } + } + +private: + void on_write_complete(fibre::WriteResult result) { + transfer_handle_ = 0; + + auto& [slot_in_use, slot_buf, slot_completer] = slots_[active_slot_ - 1]; + (void) slot_buf; + auto completer = slot_completer; + slot_in_use = false; + + completer.invoke_and_clear(result); + + // Select new slot before announcing completion of the old + size_t active_slot = 0; + for (size_t i = 0; i < NSlots; ++i) { + auto& [slot_in_use, slot_buf, slot_completer] = slots_[i]; + (void) slot_buf; + (void) slot_completer; + if (slot_in_use) { + active_slot = i + 1; + break; + } + } + + // Start next slot + active_slot_ = active_slot; + if (active_slot) { + auto& [slot_in_use, slot_buf, slot_completer] = slots_[active_slot - 1]; + (void) slot_in_use; + (void) slot_completer; + sink_.start_write(slot_buf, &transfer_handle_, MEMBER_CB(this, on_write_complete)); + } + } + + AsyncStreamSink& sink_; + std::tuple> slots_[NSlots]; + size_t active_slot_ = 0; + TransferHandle transfer_handle_ = 0; +}; + +} + +#endif // __FIBRE_STREAM_UTILS_HPP diff --git a/Firmware/fibre/cpp/type_info_template.j2 b/Firmware/fibre-cpp/type_info_template.j2 similarity index 95% rename from Firmware/fibre/cpp/type_info_template.j2 rename to Firmware/fibre-cpp/type_info_template.j2 index 7e6cda573..7efff78f2 100644 --- a/Firmware/fibre/cpp/type_info_template.j2 +++ b/Firmware/fibre-cpp/type_info_template.j2 @@ -26,7 +26,7 @@ struct [[intf.fullname | to_pascal_case]]TypeInfo : TypeInfo { T* ptr = *(T**)&obj; introspectable_storage_t res; switch (idx) { -[%- for property in intf.attributes.values() %] +[%- for property in intf.get_all_attributes().values() %] case [[loop.index0]]: *(decltype([[intf.c_name]]::get_[[property.name]](std::declval()))*)(&res) = [[intf.c_name]]::get_[[property.name]](ptr); break; [%- endfor %] } @@ -38,7 +38,7 @@ struct [[intf.fullname | to_pascal_case]]TypeInfo : TypeInfo { [% for intf in interfaces.values() %][% if not intf.builtin %] template const PropertyInfo [[intf.fullname | to_pascal_case]]TypeInfo::property_table[] = { -[%- for property in intf.attributes.values() %] +[%- for property in intf.get_all_attributes().values() %] {"[[property.name]]", &[[(property.type.purename or property.type.fullname) | to_pascal_case]]TypeInfo()))>>::singleton}, [%- endfor %] }; diff --git a/Firmware/fibre/.gitignore b/Firmware/fibre/.gitignore deleted file mode 100644 index d41efe558..000000000 --- a/Firmware/fibre/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -__pycache__ -.Trash* -/build -/.tup diff --git a/Firmware/fibre/README.md b/Firmware/fibre/README.md deleted file mode 100644 index 0b5f38481..000000000 --- a/Firmware/fibre/README.md +++ /dev/null @@ -1,127 +0,0 @@ -## Overview ## - -The goal of Fibre is to provide a framework to make suckless distributed applications easier to program. - -In particular: - - - Nobody likes boiler plate code. Using a remote object should feel almost - exactly as if it was local. No matter if it's in a different process, on - a USB device, connected via Bluetooth, over the Internet or all at the - same time. - All complexity arising from the system being distributed should be taken - care of by Fibre while still allowing the application developer to easily - fine-tune things. - - - Fibre has the ambition to run on most major platforms and provide bindings - for the most popular languages. Even bare metal embedded systems with very - limited resources. See the Compatibility section for the current status. - - - Once you deployed your application and want to change the interface, don't - worry about breaking other applications. With Fibre's object model, the - most common updates like adding methods, properties or arguments won't - break anything. Sometimes you can get away with removing methods if they - weren't used by other programs. **This is not implemented yet.** - -## Current Status ## - -The project is in an early stage and the focus so far was to get a minimum working implementation. - -* **C++**: Currently only supports the server side (i.e. publishing local objects). The C++ library comes with builtin support for TCP and UDP transport layers on Posix platforms. The library can easily be used with user provided transport layers. - -* **Python**: Currently only supports the client side (i.e. using remote objects). The Python library comes with builtin support for TCP, UDP, USB and UART transport layers. - -Support for more languages (most importantly JavaScript) will be added once the protocol matures. Feel free to add your contribution. - -## Show me some code - -Consider this program: - -``` -class TestClass { -public: - float property1; - float property2; - - float set_both(float arg1, float arg2) { - property1 = arg1; - property2 = arg2; - return property1 + property2; - } -}; - - -int main() { - TestClass test_object = TestClass(); - - while (1) { - printf("test_object.property1: %f\n", test_object.property1); - usleep(1000000 / 5); // 5 Hz - } -} -``` - -Say you want to publish `test_object` so that a remote Fibre node can use it. - -1. Add includes - ```C++ - #include - #include - ``` -1. Add Fibre export definitions to the exported class - ```C++ - class TestClass { - [...] - FIBRE_EXPORTS(TestClass, - make_protocol_property("property1", &property1), - make_protocol_property("property2", &property2), - make_protocol_function("set_both", *obj, &TestClass::set_both, "arg1", "arg2") - ); - }; - ``` - Note: in the future this will be generated from a YAML file using automatic code generation. - -1. Publish the object on Fibre - ```C++ - auto definitions = test_object.fibre_definitions; - fibre_publish(definitions); - ``` - Note: currently you must publish all objects at once. This will be fixed in the future. - -1. Start the TCP server - ```C++ - std::thread server_thread_tcp(serve_on_tcp, 9910); - ``` - Note: this step will be replaced by a simple `fibre_start()` call in the future. All builtin transport layers then will be started automatically. - -## Adding Fibre to your project ## - -We recommend Git subtrees if you want to include the Fibre source code in another project. -Other contributors don't need to know anything about subtrees, to them the Fibre repo will be like any other normal directory. - -#### Adding the repo -``` -git remote add fibre-origin git@github.com:samuelsadok/fibre.git -git fetch fibre-origin -git subtree add --prefix=fibre --squash fibre-origin master -``` -Instead of using the upstream remote, you might want to use your own fork for greater flexibility. - -#### Pulling updates from upstream -``` -git subtree pull --prefix=fibre --squash fibre-origin master -``` - -#### Contributing changes back to upstream -This requires push access to `fibre-origin`. -``` -git subtree push --prefix=fibre fibre-origin master -``` - -## Projects using Fibre ## - - - [ODrive](https://github.com/madcowswe/ODrive): High performance motor control - - [lightd](https://github.com/samuelsadok/lightd): Service that can be run on a Raspberry Pi (or similar) to control RGB LED strips - -## Contribute ## - -This project losely adheres to the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html). diff --git a/Firmware/fibre/cpp/include/fibre/decoders.hpp b/Firmware/fibre/cpp/include/fibre/decoders.hpp deleted file mode 100644 index 6d5d69fbb..000000000 --- a/Firmware/fibre/cpp/include/fibre/decoders.hpp +++ /dev/null @@ -1,336 +0,0 @@ - -#ifndef __DECODERS_HPP -#define __DECODERS_HPP - -#include "protocol.hpp" -#include "crc.hpp" -#include "cpp_utils.hpp" -#include - - -/* Base classes --------------------------------------------------------------*/ - -// @brief Base class for stream based decoders. -// A stream based decoder is a decoder that processes arbitrary length data blocks. -class StreamDecoder : public StreamSink { -public: - // @brief Returns 0 if no error ocurred, otherwise a non-zero error code. - // Once process_bytes returned an error, subsequent calls to get_status must return the same error. - // If the decoder is in an error state, the behavior of get_expected_bytes and process_bytes is undefined. - virtual int get_status() = 0; - - // @brief Returns the minimum number of bytes that are still needed to complete this decoder. - // If 0, the decoder is considered complete and any subsequent call to process_bytes must process - // exactly 0 bytes. - // process_bytes() must always process all provided bytes unless the decoder expects no more bytes - // afterwards - virtual size_t get_expected_bytes() = 0; -}; - -// @brief Base class for a decoder that is fed in a block-wise fashion. -// This base class is provided for convenience when implementing certain types of decoders. -// A StreamDecoder can be obtained from a BlockDecoder by using StreamDecoder_from_BlockDecoder. -template -class BlockDecoder { -public: - typedef std::integral_constant block_size; - - virtual int get_status() = 0; - virtual size_t get_expected_blocks() = 0; - virtual int process_block(const uint8_t block[BLOCKSIZE]) = 0; -private: -}; - -// @brief Base class for a decoder that is fed in a byte-wise fashion -// This base class is provided for convenience when implementing certain types of decoders. -// A StreamDecoder can be obtained from a ByteDecoder by using StreamDecoder_from_ByteDecoder. -class ByteDecoder { -public: - virtual int get_status() = 0; - virtual size_t get_expected_bytes() = 0; - virtual int process_byte(uint8_t byte) = 0; -}; - -/* Converter classes ---------------------------------------------------------*/ - -// @brief Encapsulates a BlockDecoder to make it look like a StreamDecoder -// @tparam T The encapsulated BlockDecoder type. -// Must inherit from BlockDecoder. -template::template all_are>())> -class StreamDecoder_from_BlockDecoder : public StreamDecoder { -public: - // @brief Imitates the constructor signature of the encapsulated type. - template::template first_is_not())> - explicit StreamDecoder_from_BlockDecoder(Args&& ... args) - : block_decoder_(std::forward(args)...) { - EXPECT_TYPE(T, BlockDecoder); - } - - inline int get_status() final { - return block_decoder_.get_status(); - } - - inline size_t get_expected_bytes() final { - size_t expected_bytes = block_decoder_.get_expected_blocks() * T::block_size::value; - return expected_bytes - std::min(expected_bytes, buffer_pos_); - } - - inline int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) final { - while (!get_status() && get_expected_bytes() && length) { - // use the incoming bytes to fill internal buffer to get a complete block - size_t n_copy = std::min(length, T::block_size::value - buffer_pos_); - memcpy(buffer_ + buffer_pos_, buffer, n_copy); - buffer += n_copy; - length -= n_copy; - if (processed_bytes) (*processed_bytes) += n_copy; - buffer_pos_ += n_copy; - - // if we have a full block, process it - if (buffer_pos_ == T::block_size::value) { - block_decoder_.process_block(buffer_); - buffer_pos_ = 0; - } - } - return get_status(); - } - - size_t get_free_space() { return SIZE_MAX; } // TODO: deprecate -private: - T block_decoder_; - size_t buffer_pos_ = 0; - uint8_t buffer_[T::block_size::value]; -}; - -// @brief Encapsulates a ByteDecoder to make it look like a BlockDecoder -// @tparam T The encapsulated ByteDecoder type. -// Must inherit from ByteDecoder. -template::template all_are())> -class BlockDecoder_from_ByteDecoder : public BlockDecoder<1> { -public: - // @brief Imitates the constructor signature of the encapsulated type. - template::template first_is_not())> - BlockDecoder_from_ByteDecoder(Args&& ... args) - : byte_decoder_(std::forward(args)...) { - EXPECT_TYPE(T, ByteDecoder); - } - - inline int get_status() final { - return byte_decoder_.get_status(); - } - inline size_t get_expected_blocks() final { - return byte_decoder_.get_expected_bytes(); - } - inline int process_block(const uint8_t block[1]) final { - int status = byte_decoder_.process_byte(*block); - return status; - } -private: - T byte_decoder_; -}; - -// @brief Encapsulates a ByteDecoder to make it look like a StreamDecoder -// @tparam T The encapsulated ByteDecoder type. -// Must inherit from ByteDecoder. -template::template all_are())> -class StreamDecoder_from_ByteDecoder : public StreamDecoder { -public: - // @brief Imitates the constructor signature of the encapsulated type. - template::template first_is_not())> - StreamDecoder_from_ByteDecoder(Args&& ... args) - : byte_decoder_(std::forward(args)...) { - EXPECT_TYPE(T, ByteDecoder); - } - - inline int get_status() final { - return byte_decoder_.get_status(); - } - inline size_t get_expected_bytes() final { - return byte_decoder_.get_expected_bytes(); - } - inline size_t get_free_space() { return SIZE_MAX; } - inline int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) final { - while (!byte_decoder_.get_status() && byte_decoder_.get_expected_bytes() && length) { - length--; - if (processed_bytes) (*processed_bytes)++; - byte_decoder_.process_byte(*(buffer++)); - } - return byte_decoder_.get_status(); - } -private: - T byte_decoder_; -}; - -/* Decoder implementations ---------------------------------------------------*/ - -template -class VarintByteDecoder : public ByteDecoder { -public: - static constexpr T BIT_WIDTH = (CHAR_BIT * sizeof(T)); - - VarintByteDecoder(T& state_variable) : - state_variable_(state_variable) - { - } - - size_t get_expected_bytes() final { - return done_ ? 0 : 1; - } - - int get_status() final { - return status_; - } - - int process_byte(uint8_t input_byte) final { - if (bit_pos_ == 0) { - LOG_FIBRE("start decoding varint, with 0x%02x => %zx\n", input_byte, (uintptr_t)&state_variable_); - state_variable_ = 0; - } - LOG_FIBRE("varint: decode %02x << %zu at %zx\n", input_byte, bit_pos_, &bit_pos_); - // we assume bit_pos_ < BIT_WIDTH - state_variable_ |= (static_cast(input_byte & 0x7f) << bit_pos_); - if (((state_variable_ >> bit_pos_) & 0x7f) != static_cast(input_byte & 0x7f)) { - LOG_FIBRE("varint overflow: tried to add %02x << %zu\n", input_byte, bit_pos_); - return (status_ = -1); // overflow - } - bit_pos_ += 7; - done_ = !(input_byte & 0x80); - return (status_ = (done_ || bit_pos_ < BIT_WIDTH) ? 0 : -1); - } - -private: - T& state_variable_; - // At all times where status_ != 0 the following statement holds: - // (done_ || bit_pos_ < BIT_WIDTH) - //size_t bit_pos_ = 0; // bit position - size_t bit_pos_ = 0; // bit position - int status_ = 0; - bool done_ = false; - int data[1024] = {0}; -}; - -template -using VarintStreamDecoder = StreamDecoder_from_ByteDecoder>; - -// This double nested type should work identically but makes it way harder for the compiler to optimize -//template -//using VarintBlockDecoder = BlockDecoder_from_ByteDecoder>; -//template -//using VarintStreamDecoder = StreamDecoder_from_BlockDecoder>; - -template -inline VarintStreamDecoder make_varint_decoder(T& variable) { - return VarintStreamDecoder(variable); -} - -inline VarintStreamDecoder make_endpoint_id_decoder(ReceiverState& state) { - return make_varint_decoder(state.endpoint_id); -} -inline VarintStreamDecoder make_length_decoder(ReceiverState& state) { - return make_varint_decoder(state.length); -} - - - -template::template all_are())> -class CRC8BlockDecoder : public BlockDecoder { -public: - CRC8BlockDecoder(TDecoder&& inner_decoder) : - inner_decoder_(std::forward(inner_decoder)) { - } - - int get_status() final { - return status_; - } - - size_t get_expected_blocks() final { - return (inner_decoder_.get_expected_bytes() + CRC8_BLOCKSIZE - 2) / (CRC8_BLOCKSIZE - 1); - } - - int process_block(const uint8_t input_block[4]) final { - current_crc_ = calc_crc8(current_crc_, input_block, CRC8_BLOCKSIZE - 1); - if (current_crc_ != input_block[CRC8_BLOCKSIZE - 1]) - return status_ = -1; - return status_ = inner_decoder_.process_bytes(input_block, CRC8_BLOCKSIZE - 1, nullptr); - } -private: - TDecoder inner_decoder_; - int status_ = 0; - uint8_t current_crc_ = INIT; -}; - -template -using CRC8StreamDecoder = StreamDecoder_from_BlockDecoder>; - -template -inline CRC8StreamDecoder make_crc8_decoder(TDecoder&& decoder) { - return CRC8StreamDecoder(std::forward(decoder)); -} - -// TODO: ENABLE_IF(TypeChecker::template all_are()) -template -class DecoderChain; - -template<> -class DecoderChain<> : public StreamDecoder { -public: - size_t get_expected_bytes() { return 0; } - int get_status() { return 0; } - int process_bytes(const uint8_t *input, size_t length, size_t* processed_bytes) { return 0; } - size_t get_free_space() { return SIZE_MAX; } // TODO: deprecate -}; - -template -class DecoderChain : public StreamDecoder { -public: - DecoderChain(TDecoder&& this_decoder, TDecoders&& ... subsequent_decoders) : - this_decoder_(std::forward(this_decoder)), - subsequent_decoders_(std::forward(subsequent_decoders)...) - { - EXPECT_TYPE(TDecoder, StreamDecoder); - } - - int get_status() final { - // If this decoder or any of the subsequent decoders failed, return error code. - int this_status = this_decoder_.get_status(); - int subsequent_status = subsequent_decoders_.get_status(); - if (this_status) - return this_status; - else if (subsequent_status) - return subsequent_status; - else - return 0; - } - - size_t get_expected_bytes() final { - return this_decoder_.get_expected_bytes() + subsequent_decoders_.get_expected_bytes(); - } - - int process_bytes(const uint8_t *input, size_t length, size_t* processed_bytes) final { - if (this_decoder_.get_expected_bytes()) { - LOG_FIBRE("decoder chain: process %zu bytes in segment %s\n", length, typeid(TDecoder).name()); - size_t chunk = 0; - int status = this_decoder_.process_bytes(input, length, &chunk); - input += chunk; - length -= chunk; - if (processed_bytes) (*processed_bytes) += chunk; - if (status) - return status; - if (!length) - return 0; - } - return subsequent_decoders_.process_bytes(input, length, processed_bytes); - } - - size_t get_free_space() { return SIZE_MAX; } // TODO: deprecate -private: - TDecoder this_decoder_; - DecoderChain subsequent_decoders_; -}; - -template -inline DecoderChain make_decoder_chain(TDecoders&& ... decoders) { - return DecoderChain(std::forward(decoders)...); -} - -#endif // __DECODERS_HPP diff --git a/Firmware/fibre/cpp/include/fibre/encoders.hpp b/Firmware/fibre/cpp/include/fibre/encoders.hpp deleted file mode 100644 index e296eeb06..000000000 --- a/Firmware/fibre/cpp/include/fibre/encoders.hpp +++ /dev/null @@ -1,323 +0,0 @@ - -#ifndef __ENCODERS_HPP -#define __ENCODERS_HPP - -#include "protocol.hpp" -#include "crc.hpp" -#include "cpp_utils.hpp" -#include - -struct Request { - endpoint_id_t endpoint_id; - size_t length; -}; - -/* Base classes --------------------------------------------------------------*/ - -// @brief Base class for all stream encoders -// A stream based encoder is an encoder that generates arbitrary length data blocks. -class StreamEncoder : public StreamSource { -public: - // @brief Returns 0 if no error ocurred, otherwise a non-zero error code. - // Once get_bytes returned an error, subsequent calls to get_status must return the same error. - // If the encoder is in an error state, the behavior of get_available_bytes and get_bytes is undefined. - virtual int get_status() = 0; - - // @brief Returns the minimum number of bytes that will still be generated by this encoder. - // If 0, the encoder is considered complete and any subsequent call to get_bytes must generate - // exactly 0 bytes. - // get_bytes() must always generate as many bytes as requested unless the encoder generates no more bytes - // afterwards - virtual size_t get_available_bytes() = 0; -}; - -// @brief Base class for an encoder that is fed in a block-wise fashion. -// This base class is provided for convenience when implementing certain types of encoders. -// A StreamEncoder can be obtained from a BlockEncoder by using StreamEncoder_from_BlockEncoder. -template -class BlockEncoder { -public: - typedef std::integral_constant block_size; - - virtual int get_status() = 0; - virtual size_t get_available_blocks() = 0; - virtual int get_block(uint8_t block[BLOCKSIZE]) = 0; -private: -}; - -// @brief Base class for an encoder that is fed in a byte-wise fashion -// This base class is provided for convenience when implementing certain types of encoders. -// A StreamEncoder can be obtained from a ByteEncoder by using StreamEncoder_from_ByteEncoder. -class ByteEncoder { -public: - virtual int get_status() = 0; - virtual size_t get_available_bytes() = 0; - virtual int get_byte(uint8_t *output_byte) = 0; -}; - -/* Converter classes ---------------------------------------------------------*/ - -// @brief Encapsulates a BlockEncoder to make it look like a StreamEncoder -// @tparam T The encapsulated BlockEncoder type. -// Must inherit from to BlockEncoder. -template::template all_are>())> -class StreamEncoder_from_BlockEncoder : public StreamEncoder { -public: - // @brief Imitates the constructor signature of the encapsulated type. - template::template first_is_not())> - explicit StreamEncoder_from_BlockEncoder(Args&& ... args) - : block_encoder_(std::forward(args)...) { - EXPECT_TYPE(T, BlockEncoder); - } - - inline int get_status() final { - return buffered_bytes_ ? 0 : block_encoder_.get_status(); - } - - inline size_t get_available_bytes() final { - size_t available_bytes = block_encoder_.get_available_blocks() * T::block_size::value; - return available_bytes + buffered_bytes_; - } - - inline int get_bytes(uint8_t* buffer, size_t length, size_t* generated_bytes) final { - while (!get_status() && get_available_bytes() && length) { - // if the buffer is empty, retrieve a new block from the encode - if (!buffered_bytes_) { - block_encoder_.get_block(buffer_); - buffered_bytes_ = T::block_size::value; - } - - // hand the buffered bytes to the encoder - size_t n_copy = std::min(buffered_bytes_, length); - memcpy(buffer, buffer_ + T::block_size::value - n_copy, n_copy); - length -= n_copy; - buffer += n_copy; - if (generated_bytes) (*generated_bytes) += n_copy; - buffered_bytes_ -= n_copy; - } - return get_status(); - } -private: - T block_encoder_; - size_t buffered_bytes_ = 0; - uint8_t buffer_[T::block_size::value]; -}; - -// @brief Encapsulates a ByteEncoder to make it look like a BlockEncoder -// @tparam T The encapsulated ByteEncoder type. -// Must inherit from ByteEncoder. -template::template all_are())> -class BlockEncoder_from_ByteEncoder : public BlockEncoder<1> { -public: - // @brief Imitates the constructor signature of the encapsulated type. - template::template first_is_not())> - BlockEncoder_from_ByteEncoder(Args&& ... args) - : byte_encoder_(std::forward(args)...) { - EXPECT_TYPE(T, ByteEncoder); - } - - inline int get_status() final { - return byte_encoder_.get_status(); - } - inline size_t get_available_blocks() final { - return byte_encoder_.get_available_bytes(); - } - inline int get_block(uint8_t block[1]) final { - int status = byte_encoder_.get_byte(*block); - return status; - } -private: - T byte_encoder_; -}; - -// @brief Encapsulates a ByteEncoder to make it look like a StreamEncoder -// @tparam T The encapsulated ByteEncoder type. -// Must inherit from ByteEncoder. -template::template all_are())> -class StreamEncoder_from_ByteEncoder : public StreamEncoder { -public: - // @brief Imitates the constructor signature of the encapsulated type. - template::template first_is_not())> - StreamEncoder_from_ByteEncoder(Args&& ... args) - : byte_encoder_(std::forward(args)...) { - EXPECT_TYPE(T, ByteEncoder); - } - - inline int get_status() final { - return byte_encoder_.get_status(); - } - inline size_t get_available_bytes() final { - return byte_encoder_.get_available_bytes(); - } - inline int get_bytes(uint8_t* buffer, size_t length, size_t* generated_bytes) final { - while (!byte_encoder_.get_status() && byte_encoder_.get_available_bytes() && length) { - length--; - if (generated_bytes) (*generated_bytes)++; - byte_encoder_.get_byte(buffer++); - } - return byte_encoder_.get_status(); - } -private: - T byte_encoder_; -}; - -/* Encoder implementations ---------------------------------------------------*/ - -template -class VarintByteEncoder : public ByteEncoder { -public: - static constexpr T BIT_WIDTH = (CHAR_BIT * sizeof(T)); - - VarintByteEncoder(const T& state_variable) : - state_variable_(state_variable) - {} - - size_t get_available_bytes() final { - return done_ ? 0 : 1; - } - - int get_status() final { - return 0; - } - - int get_byte(uint8_t *output_byte) final { - if (bit_pos_ == 0) - LOG_FIBRE("start encoding varint, from pos %d\n", bit_pos_); - *output_byte = (state_variable_ >> bit_pos_) & 0x7f; - bit_pos_ += 7; - if (bit_pos_ < BIT_WIDTH && (state_variable_ >> bit_pos_)) { - LOG_FIBRE("remainder: %x\n", state_variable_ >> bit_pos_); - *output_byte |= 0x80; - }else - done_ = true; - return 0; - } - -private: - const T& state_variable_; - size_t bit_pos_ = 0; // bit position - int status_ = 0; - bool done_ = false; -}; - -template -using VarintStreamEncoder = StreamEncoder_from_ByteEncoder>; - -template -VarintStreamEncoder make_varint_encoder(const T& variable) { - return VarintStreamEncoder(variable); -} - -VarintStreamEncoder make_endpoint_id_encoder(const Request& request) { - return make_varint_encoder(request.endpoint_id); -} -VarintStreamEncoder make_length_encoder(const Request& request) { - return make_varint_encoder(request.length); -} - -template::template all_are())> -class CRC8BlockEncoder : public BlockEncoder { -public: - CRC8BlockEncoder(TEncoder&& inner_encoder) - : inner_encoder_(std::forward(inner_encoder)) {} - - int get_status() final { - return status_; - } - - size_t get_available_blocks() final { - return (inner_encoder_.get_available_bytes() + CRC8_BLOCKSIZE - 2) / (CRC8_BLOCKSIZE - 1); - } - - int get_block(uint8_t block[4]) final { - size_t generated_bytes = 0; - status_ = inner_encoder_.get_bytes(block, CRC8_BLOCKSIZE - 1, &generated_bytes); - if (status_) - return status_; - - // zero out unused end of the block - while (generated_bytes < CRC8_BLOCKSIZE) - block[generated_bytes++] = 0; - - block[CRC8_BLOCKSIZE - 1] = current_crc_ = calc_crc8(current_crc_, block, CRC8_BLOCKSIZE - 1); - return 0; - } -private: - TEncoder inner_encoder_; - int status_ = 0; - uint8_t current_crc_ = INIT; -}; - -template -using CRC8StreamEncoder = StreamEncoder_from_BlockEncoder>; - -template -CRC8StreamEncoder make_crc8_encoder(TEncoder&& encoder) { - return CRC8StreamEncoder(std::forward(encoder)); -} - -template -class EncoderChain; - -template<> -class EncoderChain<> : public StreamEncoder { -public: - size_t get_available_bytes() final { return 0; } - int get_status() final { return 0; } - int get_bytes(uint8_t *output, size_t length, size_t* generated_bytes) final { return 0; } -}; - -template -class EncoderChain : public StreamEncoder { -public: - EncoderChain(TEncoder&& this_encoder, TEncoders&& ... subsequent_encoders) : - this_encoder_(std::forward(this_encoder)), - subsequent_encoders_(std::forward(subsequent_encoders)...) - { - EXPECT_TYPE(TEncoder, StreamEncoder); - } - - size_t get_available_bytes() final { - return this_encoder_.get_available_bytes() + subsequent_encoders_.get_available_bytes(); - } - - int get_status() final { - // If this encoder or any of the subsequent encoders failed, return error code. - int this_status = this_encoder_.get_status(); - int subsequent_status = subsequent_encoders_.get_status(); - if (this_status) - return this_status; - else if (subsequent_status) - return subsequent_status; - else - return 0; - } - - int get_bytes(uint8_t *output, size_t length, size_t* generated_bytes) final { - if (this_encoder_.get_available_bytes()) { - LOG_FIBRE("encoder chain: generate %zu bytes in segment %s\n", length, typeid(TEncoder).name()); - size_t chunk = 0; - int status = this_encoder_.get_bytes(output, length, &chunk); - if (status) - return status; - output += chunk; - length -= chunk; - if (generated_bytes) *generated_bytes += chunk; - if (!length) - return 0; - } - return subsequent_encoders_.get_bytes(output, length, generated_bytes); - } - -private: - TEncoder this_encoder_; - EncoderChain subsequent_encoders_; -}; - -template -EncoderChain make_encoder_chain(TEncoders&& ... encoders) { - return EncoderChain(std::forward(encoders)...); -} - -#endif // __ENCODERS_HPP diff --git a/Firmware/fibre/cpp/include/fibre/posix_tcp.hpp b/Firmware/fibre/cpp/include/fibre/posix_tcp.hpp deleted file mode 100644 index 3f6a7a07e..000000000 --- a/Firmware/fibre/cpp/include/fibre/posix_tcp.hpp +++ /dev/null @@ -1,4 +0,0 @@ - -#include "protocol.hpp" - -int serve_on_tcp(unsigned int port); diff --git a/Firmware/fibre/cpp/include/fibre/posix_udp.hpp b/Firmware/fibre/cpp/include/fibre/posix_udp.hpp deleted file mode 100644 index 7022f9de6..000000000 --- a/Firmware/fibre/cpp/include/fibre/posix_udp.hpp +++ /dev/null @@ -1,4 +0,0 @@ - -#include "protocol.hpp" - -int serve_on_udp(unsigned int port); diff --git a/Firmware/fibre/cpp/include/fibre/protocol.hpp b/Firmware/fibre/cpp/include/fibre/protocol.hpp deleted file mode 100644 index 2a01e2207..000000000 --- a/Firmware/fibre/cpp/include/fibre/protocol.hpp +++ /dev/null @@ -1,621 +0,0 @@ -/* -see protocol.md for the protocol specification -*/ - -#ifndef __PROTOCOL_HPP -#define __PROTOCOL_HPP - -// TODO: resolve assert -#define assert(expr) - -#include -#include -#include -//#include -#include -#include -#include -#include -#include "crc.hpp" -#include "cpp_utils.hpp" -#include "bufptr.hpp" -#include "simple_serdes.hpp" - -// Note that this option cannot be used to debug UART because it prints on UART -//#define DEBUG_FIBRE -#ifdef DEBUG_FIBRE -#define LOG_FIBRE(...) do { printf(__VA_ARGS__); } while (0) -#else -#define LOG_FIBRE(...) ((void) 0) -#endif - - -// Default CRC-8 Polynomial: x^8 + x^5 + x^4 + x^2 + x + 1 -// Can protect a 4 byte payload against toggling of up to 5 bits -// source: https://users.ece.cmu.edu/~koopman/crc/index.html -constexpr uint8_t CANONICAL_CRC8_POLYNOMIAL = 0x37; -constexpr uint8_t CANONICAL_CRC8_INIT = 0x42; - -constexpr size_t CRC8_BLOCKSIZE = 4; - -// Default CRC-16 Polynomial: 0x9eb2 x^16 + x^13 + x^12 + x^11 + x^10 + x^8 + x^6 + x^5 + x^2 + 1 -// Can protect a 135 byte payload against toggling of up to 5 bits -// source: https://users.ece.cmu.edu/~koopman/crc/index.html -// Also known as CRC-16-DNP -constexpr uint16_t CANONICAL_CRC16_POLYNOMIAL = 0x3d65; -constexpr uint16_t CANONICAL_CRC16_INIT = 0x1337; - -constexpr uint8_t CANONICAL_PREFIX = 0xAA; - - - - - -/* move to fibre_config.h ******************************/ - -typedef size_t endpoint_id_t; - -struct ReceiverState { - endpoint_id_t endpoint_id; - size_t length; - uint16_t seqno_thread; - uint16_t seqno; - bool expect_ack; - bool expect_response; - bool enforce_ordering; -}; - -/*******************************************************/ - - -constexpr uint16_t PROTOCOL_VERSION = 1; - -// This value must not be larger than USB_TX_DATA_SIZE defined in usbd_cdc_if.h -constexpr uint16_t TX_BUF_SIZE = 32; // does not work with 64 for some reason -constexpr uint16_t RX_BUF_SIZE = 128; // larger values than 128 have currently no effect because of protocol limitations - -// Maximum time we allocate for processing and responding to a request -constexpr uint32_t PROTOCOL_SERVER_TIMEOUT_MS = 10; - - -typedef struct { - uint16_t json_crc = 0; - uint16_t endpoint_id = 0; -} endpoint_ref_t; - - -namespace fibre { -// These symbols are defined in the autogenerated endpoints.hpp -extern const unsigned char embedded_json[]; -extern const size_t embedded_json_length; -extern const uint16_t json_crc_; -extern const uint32_t json_version_id_; -bool endpoint_handler(int idx, cbufptr_t* input_buffer, bufptr_t* output_buffer); -bool endpoint0_handler(cbufptr_t* input_buffer, bufptr_t* output_buffer); -bool is_endpoint_ref_valid(endpoint_ref_t endpoint_ref); -bool set_endpoint_from_float(endpoint_ref_t endpoint_ref, float value); -} - - -template::value>> -inline size_t write_le(T value, uint8_t* buffer){ - //TODO: add static_assert that this is still a little endian machine - std::memcpy(&buffer[0], &value, sizeof(value)); - return sizeof(value); -} - -template -typename std::enable_if_t::value, size_t> -write_le(T value, uint8_t* buffer) { - return write_le>(value, buffer); -} - -template<> -inline size_t write_le(float value, uint8_t* buffer) { - static_assert(CHAR_BIT * sizeof(float) == 32, "32 bit floating point expected"); - static_assert(std::numeric_limits::is_iec559, "IEEE 754 floating point expected"); - uint32_t value_as_uint32; - std::memcpy(&value_as_uint32, &value, sizeof(uint32_t)); - return write_le(value_as_uint32, buffer); -} - -template -inline size_t read_le(T* value, const uint8_t* buffer){ - // TODO: add static_assert that this is still a little endian machine - std::memcpy(value, buffer, sizeof(*value)); - return sizeof(*value); -} - -template<> -inline size_t read_le(float* value, const uint8_t* buffer) { - static_assert(CHAR_BIT * sizeof(float) == 32, "32 bit floating point expected"); - static_assert(std::numeric_limits::is_iec559, "IEEE 754 floating point expected"); - return read_le(reinterpret_cast(value), buffer); -} - -// @brief Reads a value of type T from the buffer. -// @param buffer Pointer to the buffer to be read. The pointer is updated by the number of bytes that were read. -// @param length The number of available bytes in buffer. This value is updated to subtract the bytes that were read. -template -static inline T read_le(const uint8_t** buffer, size_t* length) { - T result; - size_t cnt = read_le(&result, *buffer); - *buffer += cnt; - *length -= cnt; - return result; -} - -class PacketSink { -public: - // @brief Get the maximum packet length (aka maximum transmission unit) - // A packet size shall take no action and return an error code if the - // caller attempts to send an oversized packet. - //virtual size_t get_mtu() = 0; - - // @brief Processes a packet. - // The blocking behavior shall depend on the thread-local deadline_ms variable. - // @return: 0 on success, otherwise a non-zero error code - // TODO: define what happens when the packet is larger than what the implementation can handle. - virtual int process_packet(const uint8_t* buffer, size_t length) = 0; -}; - -class StreamSink { -public: - // @brief Processes a chunk of bytes that is part of a continuous stream. - // The blocking behavior shall depend on the thread-local deadline_ms variable. - // @param processed_bytes: if not NULL, shall be incremented by the number of - // bytes that were consumed. - // @return: 0 on success, otherwise a non-zero error code - virtual int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) = 0; - - // @brief Returns the number of bytes that can still be written to the stream. - // Shall return SIZE_MAX if the stream has unlimited lenght. - // TODO: deprecate - virtual size_t get_free_space() = 0; - - /*int process_bytes(const uint8_t* buffer, size_t length) { - size_t processed_bytes = 0; - return process_bytes(buffer, length, &processed_bytes); - }*/ -}; - -class StreamSource { -public: - // @brief Generate a chunk of bytes that are part of a continuous stream. - // The blocking behavior shall depend on the thread-local deadline_ms variable. - // @param generated_bytes: if not NULL, shall be incremented by the number of - // bytes that were written to buffer. - // @return: 0 on success, otherwise a non-zero error code - virtual int get_bytes(uint8_t* buffer, size_t length, size_t* generated_bytes) = 0; - - // @brief Returns the number of bytes that can still be written to the stream. - // Shall return SIZE_MAX if the stream has unlimited lenght. - // TODO: deprecate - //virtual size_t get_free_space() = 0; -}; - -class StreamToPacketSegmenter : public StreamSink { -public: - explicit StreamToPacketSegmenter(PacketSink& output) : - output_(output) - { - }; - - int process_bytes(const uint8_t *buffer, size_t length, size_t* processed_bytes) override; - - size_t get_free_space() { return SIZE_MAX; } - -private: - uint8_t header_buffer_[3] = {0}; - size_t header_index_ = 0; - uint8_t packet_buffer_[RX_BUF_SIZE] = {0}; - size_t packet_index_ = 0; - size_t packet_length_ = 0; - PacketSink& output_; -}; - - -class StreamBasedPacketSink : public PacketSink { -public: - explicit StreamBasedPacketSink(StreamSink& output) : - output_(output) - { - }; - - //size_t get_mtu() { return SIZE_MAX; } - int process_packet(const uint8_t *buffer, size_t length) override; - -private: - StreamSink& output_; -}; - -// @brief: Represents a stream sink that's based on an underlying packet sink. -// A single call to process_bytes may result in multiple packets being sent. -class PacketBasedStreamSink : public StreamSink { -public: - explicit PacketBasedStreamSink(PacketSink& packet_sink) : _packet_sink(packet_sink) {} - ~PacketBasedStreamSink() {} - - int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) override { - // Loop to ensure all bytes get sent - while (length) { - size_t chunk = length; - // send chunk as packet - if (_packet_sink.process_packet(buffer, chunk)) - return -1; - buffer += chunk; - length -= chunk; - if (processed_bytes) - *processed_bytes += chunk; - } - return 0; - } - - size_t get_free_space() { return SIZE_MAX; } - -private: - PacketSink& _packet_sink; -}; - -// Implements the StreamSink interface by writing into a fixed size -// memory buffer. -class MemoryStreamSink : public StreamSink { -public: - MemoryStreamSink(uint8_t *buffer, size_t length) : - buffer_(buffer), - buffer_length_(length) {} - - // Returns 0 on success and -1 if the buffer could not accept everything because it became full - int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) override { - size_t chunk = length < buffer_length_ ? length : buffer_length_; - memcpy(buffer_, buffer, chunk); - buffer_ += chunk; - buffer_length_ -= chunk; - if (processed_bytes) - *processed_bytes += chunk; - return chunk == length ? 0 : -1; - } - - size_t get_free_space() { return buffer_length_; } - -private: - uint8_t * buffer_; - size_t buffer_length_; -}; - -// Implements the StreamSink interface by discarding the first couple of bytes -// and then forwarding the rest to another stream. -class NullStreamSink : public StreamSink { -public: - NullStreamSink(size_t skip, StreamSink& follow_up_stream) : - skip_(skip), - follow_up_stream_(follow_up_stream) {} - - // Returns 0 on success and -1 if the buffer could not accept everything because it became full - int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) override { - if (skip_ < length) { - buffer += skip_; - length -= skip_; - if (processed_bytes) - *processed_bytes += skip_; - skip_ = 0; - return follow_up_stream_.process_bytes(buffer, length, processed_bytes); - } else { - skip_ -= length; - if (processed_bytes) - *processed_bytes += length; - return 0; - } - } - - size_t get_free_space() override { return skip_ + follow_up_stream_.get_free_space(); } - -private: - size_t skip_; - StreamSink& follow_up_stream_; -}; - - - -// Implements the StreamSink interface by calculating the CRC16 checksum -// on the data that is sent to it. -class CRC16Calculator : public StreamSink { -public: - explicit CRC16Calculator(uint16_t crc16_init) : - crc16_(crc16_init) {} - - int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) override{ - crc16_ = calc_crc16(crc16_, buffer, length); - if (processed_bytes) - *processed_bytes += length; - return 0; - } - - size_t get_free_space() override { return SIZE_MAX; } - - uint16_t get_crc16() { return crc16_; } -private: - uint16_t crc16_; -}; - - -namespace fibre { -template -struct Codec { - static std::optional decode(cbufptr_t* buffer) { return std::nullopt; } -}; - -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { return (buffer->begin() == buffer->end()) ? std::nullopt : std::make_optional((bool)*(buffer->begin()++)); } - static bool encode(bool value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } -}; -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } - static bool encode(int8_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } -}; -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } - static bool encode(uint8_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } -}; -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } - static bool encode(int16_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } -}; -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } - static bool encode(uint16_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } -}; -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } - static bool encode(int32_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } -}; -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } - static bool encode(uint32_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } -}; -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } - static bool encode(int64_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } -}; -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } - static bool encode(uint64_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } -}; -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { - std::optional int_val = Codec::decode(buffer); - return int_val.has_value() ? std::optional(*reinterpret_cast(&int_val.value())) : std::nullopt; - } - static bool encode(float value, bufptr_t* buffer) { - void* ptr = &value; - return Codec::encode(*reinterpret_cast(ptr), buffer); - } -}; -template -struct Codec::value>> { - static std::optional decode(cbufptr_t* buffer) { - std::optional int_val = SimpleSerializer::read(&(buffer->begin()), buffer->end()); - return int_val.has_value() ? std::make_optional(static_cast(int_val.value())) : std::nullopt; - } - static bool encode(T value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } -}; -template<> struct Codec { - static std::optional decode(cbufptr_t* buffer) { - std::optional val0 = SimpleSerializer::read(&(buffer->begin()), buffer->end()); - std::optional val1 = SimpleSerializer::read(&(buffer->begin()), buffer->end()); - return (val0.has_value() && val1.has_value()) ? std::make_optional(endpoint_ref_t{val1.value(), val0.value()}) : std::nullopt; - } - static bool encode(endpoint_ref_t value, bufptr_t* buffer) { - return SimpleSerializer::write(value.endpoint_id, &(buffer->begin()), buffer->end()) - && SimpleSerializer::write(value.json_crc, &(buffer->begin()), buffer->end()); - } -}; -} - - -/* @brief Handles the communication protocol on one channel. -* -* When instantiated with a list of endpoints and an output packet sink, -* objects of this class will handle packets passed into process_packet, -* pass the relevant data to the corresponding endpoints and dispatch response -* packets on the output. -*/ -class BidirectionalPacketBasedChannel : public PacketSink { -public: - explicit BidirectionalPacketBasedChannel(PacketSink& output) : - output_(output) - { } - - //size_t get_mtu() { - // return SIZE_MAX; - //} - int process_packet(const uint8_t* buffer, size_t length) override; -private: - PacketSink& output_; - uint8_t tx_buf_[TX_BUF_SIZE] = {0}; -}; - - -/* ToString / FromString functions -------------------------------------------*/ -/* -* These functions are currently not used by Fibre and only here to -* support the ODrive ASCII protocol. -* TODO: find a general way for client code to augment endpoints with custom -* functions -*/ - -template -struct format_traits_t; - -// template<> struct format_traits_t { using type = void; -// static constexpr const char * fmt = "%f"; -// static constexpr const char * fmtp = "%f"; -// }; -template<> struct format_traits_t { using type = void; - static constexpr const char * fmt = "%lld"; - static constexpr const char * fmtp = "%lld"; -}; -template<> struct format_traits_t { using type = void; - static constexpr const char * fmt = "%llu"; - static constexpr const char * fmtp = "%llu"; -}; -template<> struct format_traits_t { using type = void; - static constexpr const char * fmt = "%ld"; - static constexpr const char * fmtp = "%ld"; -}; -template<> struct format_traits_t { using type = void; - static constexpr const char * fmt = "%lu"; - static constexpr const char * fmtp = "%lu"; -}; -// TODO: change all overloads to fundamental int type space -template<> struct format_traits_t { using type = void; - static constexpr const char * fmt = "%ud"; - static constexpr const char * fmtp = "%ud"; -}; -template<> struct format_traits_t { using type = void; - static constexpr const char * fmt = "%hd"; - static constexpr const char * fmtp = "%hd"; -}; -template<> struct format_traits_t { using type = void; - static constexpr const char * fmt = "%hu"; - static constexpr const char * fmtp = "%hu"; -}; -template<> struct format_traits_t { using type = void; - static constexpr const char * fmt = "%hhd"; - static constexpr const char * fmtp = "%d"; -}; -template<> struct format_traits_t { using type = void; - static constexpr const char * fmt = "%hhu"; - static constexpr const char * fmtp = "%u"; -}; - -template::type> -static bool to_string(const T& value, char * buffer, size_t length, int) { - snprintf(buffer, length, format_traits_t::fmtp, value); - return true; -} -// Special case for float because printf promotes float to double, and we get warnings -template -static bool to_string(const float& value, char * buffer, size_t length, int) { - snprintf(buffer, length, "%f", (double)value); - return true; -} -template -static bool to_string(const bool& value, char * buffer, size_t length, int) { - buffer[0] = value ? '1' : '0'; - buffer[1] = 0; - return true; -} -template -static bool to_string(const T& value, char * buffer, size_t length, ...) { - return false; -} - -template::type> -static bool from_string(const char * buffer, size_t length, T* property, int) { - // Note for T == uint8_t: Even though we supposedly use the correct format - // string sscanf treats our pointer as pointer-to-int instead of - // pointer-to-uint8_t. To avoid an unexpected memory access we first read - // into a union. - union { T t; int i; } val; - if (sscanf(buffer, format_traits_t::fmt, &val.t) == 1) { - *property = val.t; - return true; - } else { - return false; - } -} -// Special case for float because printf promotes float to double, and we get warnings -template -static bool from_string(const char * buffer, size_t length, float* property, int) { - return sscanf(buffer, "%f", property) == 1; -} -template -static bool from_string(const char * buffer, size_t length, bool* property, int) { - int val; - if (sscanf(buffer, "%d", &val) != 1) - return false; - *property = val; - return true; -} -template -static bool from_string(const char * buffer, size_t length, T* property, ...) { - return false; -} - - -//template -//bool set_from_float_ex(float value, T* property) { -// return false; -//} - -namespace conversion { -//template -template -bool set_from_float_ex(float value, float* property, int) { - return *property = value, true; -} -template -bool set_from_float_ex(float value, bool* property, int) { - return *property = (value >= 0.0f), true; -} -template::value && !std::is_const::value>> -bool set_from_float_ex(float value, T* property, int) { - return *property = static_cast(std::round(value)), true; -} -template -bool set_from_float_ex(float value, T* property, ...) { - return false; -} -template -bool set_from_float(float value, T* property) { - return set_from_float_ex(value, property, 0); -} -} - - -template -struct Property { - Property(void* ctx, T(*getter)(void*), void(*setter)(void*, T)) - : ctx_(ctx), getter_(getter), setter_(setter) {} - Property(T* ctx) - : ctx_(ctx), getter_([](void* ctx){ return *(T*)ctx; }), setter_([](void* ctx, T val){ *(T*)ctx = val; }) {} - Property& operator*() { return *this; } - Property* operator->() { return this; } - - T read() const { - return (*getter_)(ctx_); - } - - T exchange(std::optional value) const { - T old_value = (*getter_)(ctx_); - if (value.has_value()) { - (*setter_)(ctx_, value.value()); - } - return old_value; - } - - void* ctx_; - T(*getter_)(void*); - void(*setter_)(void*, T); -}; - -template -struct Property { - Property(void* ctx, T(*getter)(void*)) - : ctx_(ctx), getter_(getter) {} - Property(const T* ctx) - : ctx_(const_cast(ctx)), getter_([](void* ctx){ return *(const T*)ctx; }) {} - Property& operator*() { return *this; } - Property* operator->() { return this; } - - T read() const { - return (*getter_)(ctx_); - } - - void* ctx_; - T(*getter_)(void*); -}; - - -#endif diff --git a/Firmware/fibre/cpp/package.lua b/Firmware/fibre/cpp/package.lua deleted file mode 100644 index 53101e1a1..000000000 --- a/Firmware/fibre/cpp/package.lua +++ /dev/null @@ -1,8 +0,0 @@ - -tup.include('../tupfiles/build.lua') - -fibre_package = define_package{ - sources={'protocol.cpp', 'posix_tcp.cpp', 'posix_udp.cpp'}, - libs={'pthread'}, - headers={'include'} -} diff --git a/Firmware/fibre/cpp/posix_tcp.cpp b/Firmware/fibre/cpp/posix_tcp.cpp deleted file mode 100644 index 57ce0df91..000000000 --- a/Firmware/fibre/cpp/posix_tcp.cpp +++ /dev/null @@ -1,108 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -#define TCP_RX_BUF_LEN 512 - -class TCPStreamSink : public StreamSink { -public: - TCPStreamSink(int socket_fd) : - socket_fd_(socket_fd) - {} - - int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) { - int bytes_sent = send(socket_fd_, buffer, length, 0); - if (processed_bytes) - *processed_bytes = (bytes_sent == -1) ? 0 : bytes_sent; - return (bytes_sent == -1) ? -1 : 0; - } - - size_t get_free_space() { return SIZE_MAX; } - -private: - int socket_fd_; -}; - - -int serve_client(int sock_fd) { - uint8_t buf[TCP_RX_BUF_LEN]; - - // initialize output stack for this client - TCPStreamSink tcp_packet_output(sock_fd); - StreamBasedPacketSink packet2stream(tcp_packet_output); - BidirectionalPacketBasedChannel channel(packet2stream); - - StreamToPacketSegmenter stream2packet(channel); - - // now listen for it - for (;;) { - memset(buf, 0, sizeof(buf)); - // returns as soon as there is some data - ssize_t n_received = recv(sock_fd, buf, sizeof(buf), 0); - - // -1 indicates error and 0 means that the client gracefully terminated - if (n_received == -1 || n_received == 0) { - close(sock_fd); - return n_received; - } - - // input processing stack - size_t processed = 0; - stream2packet.process_bytes(buf, n_received, &processed); - } -} - -// function to check if a worker thread handling a single client is done -template -bool future_is_ready(std::future& t){ - return t.wait_for(std::chrono::seconds(0)) == std::future_status::ready; -} - -int serve_on_tcp(unsigned int port) { - struct sockaddr_in6 si_me, si_other; - int s; - - - if ((s=socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) { - return -1; - } - - memset((char *) &si_me, 0, sizeof(si_me)); - si_me.sin6_family = AF_INET6; - si_me.sin6_port = htons(port); - si_me.sin6_flowinfo = 0; - si_me.sin6_addr = in6addr_any; - if (bind(s, reinterpret_cast(&si_me), sizeof(si_me)) == -1) { - return -1; - } - - listen(s, 128); // make this socket a passive socket - std::vector> serv_pool; - for (;;) { - memset(&si_other, 0, sizeof(si_other)); - - socklen_t silen = sizeof(si_other); - // TODO: Add a limit on accepting connections - int client_portal_fd = accept(s, reinterpret_cast(&si_other), &silen); // blocking call - serv_pool.push_back(std::async(std::launch::async, serve_client, client_portal_fd)); - // do a little clean up on the pool - for (std::vector>::iterator it = serv_pool.end()-1; it >= serv_pool.begin(); --it) { - if (future_is_ready(*it)) { - // we can erase this thread - serv_pool.erase(it); - } - } - } - - close(s); -} - diff --git a/Firmware/fibre/cpp/posix_udp.cpp b/Firmware/fibre/cpp/posix_udp.cpp deleted file mode 100644 index 0cc9d09c5..000000000 --- a/Firmware/fibre/cpp/posix_udp.cpp +++ /dev/null @@ -1,70 +0,0 @@ - -#include -#include -#include -#include -#include - -#include - -#define UDP_RX_BUF_LEN 512 -#define UDP_TX_BUF_LEN 512 - - -class UDPPacketSender : public PacketSink { -public: - UDPPacketSender(int socket_fd, struct sockaddr_in6 *si_other) : - _socket_fd(socket_fd), - _si_other(si_other) - {} - - size_t get_mtu() { return UDP_TX_BUF_LEN; } - - int process_packet(const uint8_t* buffer, size_t length) { - // cannot send partial packets - if (length > get_mtu()) - return -1; - - int status = sendto(_socket_fd, buffer, length, 0, reinterpret_cast(_si_other), sizeof(*_si_other)); - return (status == -1) ? -1 : 0; - } - -private: - int _socket_fd; - struct sockaddr_in6 *_si_other; -}; - - - -int serve_on_udp(unsigned int port) { - struct sockaddr_in6 si_me, si_other; - int s; - socklen_t slen = sizeof(si_other); - uint8_t buf[UDP_RX_BUF_LEN]; - - if ((s=socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1) - return -1; - - memset((char *) &si_me, 0, sizeof(si_me)); - si_me.sin6_family = AF_INET6; - si_me.sin6_port = htons(port); - si_me.sin6_flowinfo = 0; - si_me.sin6_addr= in6addr_any; - if (bind(s, reinterpret_cast(&si_me), sizeof(si_me)) == -1) - return -1; - - for (;;) { - ssize_t n_received = recvfrom(s, buf, sizeof(buf), 0, reinterpret_cast(&si_other), &slen); - if (n_received == -1) - return -1; - //printf("Received packet from %s:%d\nData: %s\n\n", - // inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), buf); - - UDPPacketSender udp_packet_output(s, &si_other); - BidirectionalPacketBasedChannel udp_channel(udp_packet_output); - udp_channel.process_packet(buf, n_received); - } - - close(s); -} - diff --git a/Firmware/fibre/cpp/protocol.cpp b/Firmware/fibre/cpp/protocol.cpp deleted file mode 100644 index e8285c879..000000000 --- a/Firmware/fibre/cpp/protocol.cpp +++ /dev/null @@ -1,187 +0,0 @@ - -/* Includes ------------------------------------------------------------------*/ - -#include -#include - -#include -#include - -/* Private defines -----------------------------------------------------------*/ -/* Private macros ------------------------------------------------------------*/ -/* Private typedef -----------------------------------------------------------*/ -/* Global constant data ------------------------------------------------------*/ -/* Global variables ----------------------------------------------------------*/ - -/* Private constant data -----------------------------------------------------*/ -/* Private variables ---------------------------------------------------------*/ -/* Private function prototypes -----------------------------------------------*/ - -static void hexdump(const uint8_t* buf, size_t len); - -/* Function implementations --------------------------------------------------*/ - -#if 0 -void hexdump(const uint8_t* buf, size_t len) { - for (size_t pos = 0; pos < len; ++pos) { - printf(" %02x", buf[pos]); - if ((((pos + 1) % 16) == 0) || ((pos + 1) == len)) - printf("\r\n"); - osDelay(2); - } -} -#else -void hexdump(const uint8_t* buf, size_t len) { - (void) buf; - (void) len; -} -#endif - - - -int StreamToPacketSegmenter::process_bytes(const uint8_t *buffer, size_t length, size_t* processed_bytes) { - int result = 0; - - while (length--) { - if (header_index_ < sizeof(header_buffer_)) { - // Process header byte - header_buffer_[header_index_++] = *buffer; - if (header_index_ == 1 && header_buffer_[0] != CANONICAL_PREFIX) { - header_index_ = 0; - } else if (header_index_ == 2 && (header_buffer_[1] & 0x80)) { - header_index_ = 0; // TODO: support packets larger than 128 bytes - } else if (header_index_ == 3 && calc_crc8(CANONICAL_CRC8_INIT, header_buffer_, 3)) { - header_index_ = 0; - } else if (header_index_ == 3) { - packet_length_ = header_buffer_[1] + 2; - } - } else if (packet_index_ < sizeof(packet_buffer_)) { - // Process payload byte - packet_buffer_[packet_index_++] = *buffer; - } - - // If both header and packet are fully received, hand it on to the packet processor - if (header_index_ == 3 && packet_index_ == packet_length_) { - if (calc_crc16(CANONICAL_CRC16_INIT, packet_buffer_, packet_length_) == 0) { - result |= output_.process_packet(packet_buffer_, packet_length_ - 2); - } - header_index_ = packet_index_ = packet_length_ = 0; - } - buffer++; - if (processed_bytes) - (*processed_bytes)++; - } - - return result; -} - -int StreamBasedPacketSink::process_packet(const uint8_t *buffer, size_t length) { - // TODO: support buffer size >= 128 - if (length >= 128) - return -1; - - LOG_FIBRE("send header\r\n"); - uint8_t header[] = { - CANONICAL_PREFIX, - static_cast(length), - 0 - }; - header[2] = calc_crc8(CANONICAL_CRC8_INIT, header, 2); - - if (output_.process_bytes(header, sizeof(header), nullptr)) - return -1; - LOG_FIBRE("send payload:\r\n"); - hexdump(buffer, length); - if (output_.process_bytes(buffer, length, nullptr)) - return -1; - - LOG_FIBRE("send crc16\r\n"); - uint16_t crc16 = calc_crc16(CANONICAL_CRC16_INIT, buffer, length); - uint8_t crc16_buffer[] = { - (uint8_t)((crc16 >> 8) & 0xff), - (uint8_t)((crc16 >> 0) & 0xff) - }; - if (output_.process_bytes(crc16_buffer, 2, nullptr)) - return -1; - LOG_FIBRE("sent!\r\n"); - return 0; -} - - -// Returns part of the JSON interface definition. -bool fibre::endpoint0_handler(fibre::cbufptr_t* input_buffer, fibre::bufptr_t* output_buffer) { - // The request must contain a 32 bit integer to specify an offset - std::optional offset = read_le(input_buffer); - - if (!offset.has_value()) { - // Didn't receive any offset - return false; - } else if (offset.value() == 0xffffffff) { - // If the offset is special value 0xFFFFFFFF, send back the JSON version ID instead - return write_le(json_version_id_, output_buffer); - } else if (offset.value() >= embedded_json_length) { - // Attempt to read beyond the buffer end - return empty response - return true; - } else { - // Return part of the json file - size_t n_copy = std::min(output_buffer->size(), embedded_json_length - (size_t)offset.value()); - memcpy(output_buffer->begin(), embedded_json + offset.value(), n_copy); - *output_buffer = output_buffer->skip(n_copy); - return true; - } -} - -int BidirectionalPacketBasedChannel::process_packet(const uint8_t* buffer, size_t length) { - LOG_FIBRE("got packet of length %d: \r\n", length); - hexdump(buffer, length); - if (length < 4) - return -1; - - uint16_t seq_no = read_le(&buffer, &length); - - if (seq_no & 0x8000) { - // TODO: ack handling - } else { - // TODO: think about some kind of ordering guarantees - // currently the seq_no is just used to associate a response with a request - - uint16_t endpoint_id = read_le(&buffer, &length); - bool expect_response = endpoint_id & 0x8000; - endpoint_id &= 0x7fff; - - // Verify packet trailer. The expected trailer value depends on the selected endpoint. - // For endpoint 0 this is just the protocol version, for all other endpoints it's a - // CRC over the entire JSON descriptor tree (this may change in future versions). - uint16_t expected_trailer = endpoint_id ? fibre::json_crc_ : PROTOCOL_VERSION; - uint16_t actual_trailer = buffer[length - 2] | (buffer[length - 1] << 8); - if (expected_trailer != actual_trailer) { - LOG_FIBRE("trailer mismatch for endpoint %d: expected %04x, got %04x\r\n", endpoint_id, expected_trailer, actual_trailer); - return -1; - } - LOG_FIBRE("trailer ok for endpoint %d\r\n", endpoint_id); - - // TODO: if more bytes than the MTU were requested, should we abort or just return as much as possible? - - uint16_t expected_response_length = read_le(&buffer, &length); - - // Limit response length according to our local TX buffer size - if (expected_response_length > sizeof(tx_buf_) - 2) - expected_response_length = sizeof(tx_buf_) - 2; - - fibre::cbufptr_t input_buffer{buffer, length - 2}; - fibre::bufptr_t output_buffer{tx_buf_ + 2, expected_response_length}; - fibre::endpoint_handler(endpoint_id, &input_buffer, &output_buffer); - - // Send response - if (expect_response) { - size_t actual_response_length = expected_response_length - output_buffer.size() + 2; - write_le(seq_no | 0x8000, tx_buf_); - - LOG_FIBRE("send packet:\r\n"); - hexdump(tx_buf_, actual_response_length); - output_.process_packet(tx_buf_, actual_response_length); - } - } - - return 0; -} diff --git a/Firmware/fibre/python/fibre/__init__.py b/Firmware/fibre/python/fibre/__init__.py deleted file mode 100644 index 0b8ed1b4b..000000000 --- a/Firmware/fibre/python/fibre/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ - -from .discovery import find_any, find_all -from .utils import Event, Logger, TimeoutError -from .protocol import ChannelBrokenException, ChannelDamagedException -from .shell import launch_shell diff --git a/Firmware/fibre/python/fibre/discovery.py b/Firmware/fibre/python/fibre/discovery.py deleted file mode 100644 index d8781c7c4..000000000 --- a/Firmware/fibre/python/fibre/discovery.py +++ /dev/null @@ -1,182 +0,0 @@ -""" -Provides functions for the discovery of Fibre nodes -""" - -import sys -import json -import time -import threading -import traceback -import struct -import fibre.protocol -import fibre.utils -import fibre.remote_object -from fibre.utils import Event, Logger -from fibre.protocol import ChannelBrokenException, TimeoutError -import appdirs -import os - -# Load all installed transport layers - -channel_types = {} - -try: - import fibre.usbbulk_transport - channel_types['usb'] = fibre.usbbulk_transport.discover_channels -except ImportError: - pass - -try: - import fibre.serial_transport - channel_types['serial'] = fibre.serial_transport.discover_channels -except ImportError: - pass - -try: - import fibre.tcp_transport - channel_types['tcp'] = fibre.tcp_transport.discover_channels -except ImportError: - pass - -try: - import fibre.udp_transport - channel_types['udp'] = fibre.udp_transport.discover_channels -except ImportError: - pass - -def noprint(text): - pass - -def find_all(path, serial_number, - did_discover_object_callback, - search_cancellation_token, - channel_termination_token, - logger): - """ - Starts scanning for Fibre nodes that match the specified path spec and calls - the callback for each Fibre node that is found. - This function is non-blocking. - """ - - def did_discover_channel(channel): - """ - Inits an object from a given channel and then calls did_discover_object_callback - with the created object - This queries the endpoint 0 on that channel to gain information - about the interface, which is then used to init the corresponding object. - """ - try: - logger.debug("Connecting to device on " + channel._name) - - cache_dir = appdirs.user_cache_dir("odrivetool") - cache_path = None - - # Fetch the json version tag to check cache (only supported on firmware v0.5 or later) - try: - json_version_tag = channel.remote_endpoint_operation(0, struct.pack("= int(find_multiple): - done_signal.set() - else: - done_signal.set() - - find_all(path, serial_number, did_discover_object, done_signal, channel_termination_token, logger) - try: - done_signal.wait(timeout=timeout) - except TimeoutError: - if not find_multiple: - return None - finally: - done_signal.set() # terminate find_all - - if find_multiple: - return result - else: - return result[0] if len(result) > 0 else None diff --git a/Firmware/fibre/python/fibre/remote_object.py b/Firmware/fibre/python/fibre/remote_object.py deleted file mode 100644 index 57364333d..000000000 --- a/Firmware/fibre/python/fibre/remote_object.py +++ /dev/null @@ -1,264 +0,0 @@ -""" -Provides functions for the discovery of Fibre nodes -""" - -import sys -import json -import struct -import threading -import fibre.protocol - -class ObjectDefinitionError(Exception): - pass - -codecs = {} - -class StructCodec(): - """ - Generic serializer/deserializer based on struct pack - """ - def __init__(self, struct_format, target_type): - self._struct_format = struct_format - self._target_type = target_type - def get_length(self): - return struct.calcsize(self._struct_format) - def serialize(self, value): - value = self._target_type(value) - return struct.pack(self._struct_format, value) - def deserialize(self, buffer): - value = struct.unpack(self._struct_format, buffer) - value = value[0] if len(value) == 1 else value - return self._target_type(value) - -class RemoteProperty(): - """ - Used internally by dynamically created objects to translate - property assignments and fetches into endpoint operations on the - object's associated channel - """ - def __init__(self, json_data, parent): - self._parent = parent - self.__channel__ = parent.__channel__ - id_str = json_data.get("id", None) - if id_str is None: - raise ObjectDefinitionError("unspecified endpoint ID") - self._id = int(id_str) - - self._name = json_data.get("name", None) - if self._name is None: - self._name = "[anonymous]" - - type_str = json_data.get("type", None) - if type_str is None: - raise ObjectDefinitionError("unspecified type") - - # Find all codecs that match the type_str and build a dictionary - # of the form {type1: codec1, type2: codec2} - eligible_types = {k: v[type_str] for (k,v) in codecs.items() if type_str in v} - - if not eligible_types: - raise ObjectDefinitionError("unsupported codec {}".format(type_str)) - - # TODO: better heuristics to select a matching type (i.e. prefer non lossless) - eligible_types = list(eligible_types.items()) - self._property_type = eligible_types[0][0] - self._codec = eligible_types[0][1] - - access_mode = json_data.get("access", "r") - self._can_read = 'r' in access_mode - self._can_write = 'w' in access_mode - - def get_value(self): - buffer = self._parent.__channel__.remote_endpoint_operation(self._id, None, True, self._codec.get_length()) - return self._codec.deserialize(buffer) - - def set_value(self, value): - buffer = self._codec.serialize(value) - # TODO: Currenly we wait for an ack here. Settle on the default guarantee. - self._parent.__channel__.remote_endpoint_operation(self._id, buffer, True, 0) - - def _dump(self): - if self._name == "serial_number": - # special case: serial number should be displayed in hex (TODO: generalize) - val_str = "{:012X}".format(self.get_value()) - elif self._name == "error": - # special case: errors should be displayed in hex (TODO: generalize) - val_str = "0x{:04X}".format(self.get_value()) - else: - val_str = str(self.get_value()) - return "{} = {} ({})".format(self._name, val_str, self._property_type.__name__) - -class EndpointRefCodec(): - """ - Serializer/deserializer for an endpoint reference - """ - def get_length(self): - return struct.calcsize(" 0: - return self._outputs[0].get_value() - - def _dump(self): - return "{}({})".format(self._name, ", ".join("{}: {}".format(x._name, x._property_type.__name__) for x in self._inputs)) - -class RemoteObject(object): - """ - Object with functions and properties that map to remote endpoints - """ - def __init__(self, json_data, parent, channel, logger): - """ - Creates an object that implements the specified JSON type description by - communicating over the provided channel - """ - # Directly write to __dict__ to avoid calling __setattr__ too early - object.__getattribute__(self, "__dict__")["_remote_attributes"] = {} - object.__getattribute__(self, "__dict__")["__sealed__"] = False - # Assign once more to make linter happy - self._remote_attributes = {} - self.__sealed__ = False - - self.__channel__ = channel - self.__parent__ = parent - - # Build attribute list from JSON - for member_json in json_data.get("members", []): - member_name = member_json.get("name", None) - if member_name is None: - logger.debug("ignoring unnamed attribute") - continue - - try: - type_str = member_json.get("type", None) - if type_str == "object": - attribute = RemoteObject(member_json, self, channel, logger) - elif type_str == "function": - attribute = RemoteFunction(member_json, self) - elif type_str != None: - attribute = RemoteProperty(member_json, self) - else: - raise ObjectDefinitionError("no type information") - except ObjectDefinitionError as ex: - logger.debug("malformed member {}: {}".format(member_name, str(ex))) - continue - - self._remote_attributes[member_name] = attribute - self.__dict__[member_name] = attribute - - # Ensure that from here on out assignments to undefined attributes - # raise an exception - self.__sealed__ = True - channel._channel_broken.subscribe(self._tear_down) - - def _dump(self, indent, depth): - if depth <= 0: - return "..." - lines = [] - for key, val in self._remote_attributes.items(): - if isinstance(val, RemoteObject): - val_str = indent + key + (": " if depth == 1 else ":\n") + val._dump(indent + " ", depth - 1) - else: - val_str = indent + val._dump() - lines.append(val_str) - return "\n".join(lines) - - def __str__(self): - return self._dump("", depth=2) - - def __repr__(self): - return self.__str__() - - def __getattribute__(self, name): - attr = object.__getattribute__(self, "_remote_attributes").get(name, None) - if isinstance(attr, RemoteProperty): - if attr._can_read: - return attr.get_value() - else: - raise Exception("Cannot read from property {}".format(name)) - elif attr != None: - return attr - else: - return object.__getattribute__(self, name) - #raise AttributeError("Attribute {} not found".format(name)) - - def __setattr__(self, name, value): - attr = object.__getattribute__(self, "_remote_attributes").get(name, None) - if isinstance(attr, RemoteProperty): - if attr._can_write: - attr.set_value(value) - else: - raise Exception("Cannot write to property {}".format(name)) - elif not object.__getattribute__(self, "__sealed__") or name in object.__getattribute__(self, "__dict__"): - object.__getattribute__(self, "__dict__")[name] = value - else: - raise AttributeError("Attribute {} not found".format(name)) - - def _tear_down(self): - # Clear all remote members - for k in self._remote_attributes.keys(): - self.__dict__.pop(k) - self._remote_attributes = {} diff --git a/Firmware/fibre/python/fibre/serial_transport.py b/Firmware/fibre/python/fibre/serial_transport.py deleted file mode 100644 index e737dfcab..000000000 --- a/Firmware/fibre/python/fibre/serial_transport.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -Provides classes that implement the StreamSource/StreamSink and -PacketSource/PacketSink interfaces for serial ports. -""" - -import os -import re -import time -import traceback -import serial -import serial.tools.list_ports -import fibre -from fibre.utils import TimeoutError - -# TODO: make this customizable -DEFAULT_BAUDRATE = 115200 - -class SerialStreamTransport(fibre.protocol.StreamSource, fibre.protocol.StreamSink): - def __init__(self, port, baud): - self._timeout = 1 - self._dev = serial.Serial(port, baud, timeout=self._timeout) - - def process_bytes(self, bytes): - self._dev.write(bytes) - - def get_bytes(self, n_bytes, deadline): - """ - Returns n bytes unless the deadline is reached, in which case the bytes - that were read up to that point are returned. If deadline is None the - function blocks forever. A deadline before the current time corresponds - to non-blocking mode. - """ - # Only set new timeout value if it is reasonably different from the old one (e.g. 20% as below) - # Otherwise it adds significant overhead (at least under Win10) as the port is reset with every reconfiguration - if deadline is None and self._timeout is not None: - self._timeout = None - self._dev.timeout = None - elif deadline is not None: - new_timeout = max(deadline - time.monotonic(), 0) - if abs(new_timeout - self._timeout) > self._timeout * 0.2: - self._timeout = new_timeout - self._dev.timeout = new_timeout - return self._dev.read(n_bytes) - - def get_bytes_or_fail(self, n_bytes, deadline): - result = self.get_bytes(n_bytes, deadline) - if len(result) < n_bytes: - raise TimeoutError("expected {} bytes but got only {}", n_bytes, len(result)) - return result - - def close(self): - self._dev.close() - - -def find_dev_serial_ports(): - try: - return ['/dev/' + x for x in os.listdir('/dev')] - except FileNotFoundError: - return [] - -def find_pyserial_ports(): - return [x.device for x in serial.tools.list_ports.comports()] - - -def discover_channels(path, serial_number, callback, cancellation_token, channel_termination_token, logger): - """ - Scans for serial ports that match the path spec. - This function blocks until cancellation_token is set. - Channels spawned by this function run until channel_termination_token is set. - """ - if path == None: - # This regex should match all desired port names on macOS, - # Linux and Windows but might match some incorrect port names. - regex = r'^(/dev/tty\.usbmodem.*|/dev/ttyACM.*|COM[0-9]+)$' - else: - regex = "^" + path + "$" - - known_devices = [] - def device_matcher(port_name): - if port_name in known_devices: - return False - return bool(re.match(regex, port_name)) - - def did_disconnect(port_name, device): - device.close() - # TODO: yes there is a race condition here in case you wonder. - known_devices.pop(known_devices.index(port_name)) - - while not cancellation_token.is_set(): - all_ports = find_pyserial_ports() + find_dev_serial_ports() - new_ports = filter(device_matcher, all_ports) - for port_name in new_ports: - try: - serial_device = SerialStreamTransport(port_name, DEFAULT_BAUDRATE) - input_stream = fibre.protocol.PacketFromStreamConverter(serial_device) - output_stream = fibre.protocol.StreamBasedPacketSink(serial_device) - channel = fibre.protocol.Channel( - "serial port {}@{}".format(port_name, DEFAULT_BAUDRATE), - input_stream, output_stream, channel_termination_token, logger) - channel.serial_device = serial_device - except serial.serialutil.SerialException: - logger.debug("Serial device init failed. Ignoring this port. More info: " + traceback.format_exc()) - known_devices.append(port_name) - else: - known_devices.append(port_name) - channel._channel_broken.subscribe(lambda: did_disconnect(port_name, serial_device)) - callback(channel) - time.sleep(1) diff --git a/Firmware/fibre/python/fibre/shell.py b/Firmware/fibre/python/fibre/shell.py deleted file mode 100644 index c5a7257a3..000000000 --- a/Firmware/fibre/python/fibre/shell.py +++ /dev/null @@ -1,128 +0,0 @@ - -import sys -import platform -import threading -import fibre - -def did_discover_device(device, - interactive_variables, discovered_devices, - branding_short, branding_long, - logger, app_shutdown_token): - """ - Handles the discovery of new devices by displaying a - message and making the device available to the interactive - console - """ - serial_number = '{:012X}'.format(device.serial_number) if hasattr(device, 'serial_number') else "[unknown serial number]" - if serial_number in discovered_devices: - verb = "Reconnected" - index = discovered_devices.index(serial_number) - else: - verb = "Connected" - discovered_devices.append(serial_number) - index = len(discovered_devices) - 1 - interactive_name = branding_short + str(index) - - # Publish new device to interactive console - interactive_variables[interactive_name] = device - globals()[interactive_name] = device # Add to globals so tab complete works - logger.notify("{} to {} {} as {}".format(verb, branding_long, serial_number, interactive_name)) - - # Subscribe to disappearance of the device - device.__channel__._channel_broken.subscribe(lambda: did_lose_device(interactive_name, logger, app_shutdown_token)) - -def did_lose_device(interactive_name, logger, app_shutdown_token): - """ - Handles the disappearance of a device by displaying - a message. - """ - if not app_shutdown_token.is_set(): - logger.warn("Oh no {} disappeared".format(interactive_name)) - -def launch_shell(args, - interactive_variables, - print_banner, print_help, - logger, app_shutdown_token, - branding_short="dev", branding_long="device"): - """ - Launches an interactive python or IPython command line - interface. - As devices are connected they are made available as - "dev0", "dev1", ... - The names of the variables can be customized by setting branding_short. - """ - - discovered_devices = [] - globals().update(interactive_variables) - - # Connect to device - logger.debug("Waiting for {}...".format(branding_long)) - fibre.find_all(args.path, args.serial_number, - lambda dev: did_discover_device(dev, interactive_variables, discovered_devices, branding_short, branding_long, logger, app_shutdown_token), - app_shutdown_token, - app_shutdown_token, - logger=logger) - - # Check if IPython is installed - if args.no_ipython: - use_ipython = False - else: - try: - import IPython - use_ipython = True - except: - print("Warning: you don't have IPython installed.") - print("If you want to have an improved interactive console with pretty colors,") - print("you should install IPython\n") - use_ipython = False - - interactive_variables["help"] = lambda: print_help(args, len(discovered_devices) > 0) - - # If IPython is installed, embed IPython shell, otherwise embed regular shell - if use_ipython: - # Override help function # pylint: disable=W0612 - help = lambda: print_help(args, len(discovered_devices) > 0) - # to fix broken "%run -i script.py" - locals()['__name__'] = globals()['__name__'] - console = IPython.terminal.embed.InteractiveShellEmbed(banner1='') - - # hack to make IPython look like the regular console - console.runcode = console.run_cell - interact = console - - # Catch ChannelBrokenException (since disconnect is not always an error) - default_exception_hook = console._showtraceback - def filtered_exception_hook(ex_class, ex, trace): - if(ex_class.__module__+'.'+ex_class.__name__ != 'fibre.protocol.ChannelBrokenException'): - default_exception_hook(ex_class,ex,trace) - - console._showtraceback = filtered_exception_hook - else: - # Enable tab complete if possible - try: - import readline # Works only on Unix - readline.parse_and_bind("tab: complete") - except: - sudo_prefix = "" if platform.system() == "Windows" else "sudo " - print("Warning: could not enable tab-complete. User experience will suffer.\n" - "Run `{}pip install readline` and then restart this script to fix this." - .format(sudo_prefix)) - - import code - console = code.InteractiveConsole(locals=interactive_variables) - interact = lambda: console.interact(banner='') - - # Catch ChannelBrokenException (since disconnect is not alway an error) - console.runcode("import sys") - console.runcode("default_exception_hook = sys.excepthook") - console.runcode("def filtered_exception_hook(ex_class, ex, trace):\n" - " if ex_class.__module__ + '.' + ex_class.__name__ != 'fibre.protocol.ChannelBrokenException':\n" - " default_exception_hook(ex_class,ex,trace)") - console.runcode("sys.excepthook=filtered_exception_hook") - - - # Launch shell - print_banner() - logger._skip_bottom_line = True - interact() - app_shutdown_token.set() diff --git a/Firmware/fibre/python/fibre/tcp_transport.py b/Firmware/fibre/python/fibre/tcp_transport.py deleted file mode 100644 index 5cfb1692c..000000000 --- a/Firmware/fibre/python/fibre/tcp_transport.py +++ /dev/null @@ -1,85 +0,0 @@ - -import sys -import socket -import time -import traceback -import fibre.protocol -from fibre.utils import wait_any, TimeoutError - -def noprint(x): - pass - -class TCPTransport(fibre.protocol.StreamSource, fibre.protocol.StreamSink): - def __init__(self, dest_addr, dest_port, logger): - # TODO: FIXME: use IPv6 - # Problem: getaddrinfo fails if the resolver returns an - # IPv4 address, but we are using AF_INET6 - #family = socket.AF_INET6 if socket.has_ipv6 else socket.AF_INET - family = socket.AF_INET - self.sock = socket.socket(family, socket.SOCK_STREAM) - # TODO: Determine the right address to use from the list - self.target = socket.getaddrinfo(dest_addr, dest_port, family)[0][4] - # TODO: this blocks until a connection is established, or the system cancels it - self.sock.connect(self.target) - - def process_bytes(self, buffer): - self.sock.send(buffer) - - def get_bytes(self, n_bytes, deadline): - """ - Returns n bytes unless the deadline is reached, in which case the bytes - that were read up to that point are returned. If deadline is None the - function blocks forever. A deadline before the current time corresponds - to non-blocking mode. - """ - # convert deadline to seconds (floating point) - deadline = None if deadline is None else max(deadline - time.monotonic(), 0) - self.sock.settimeout(deadline) - try: - data = self.sock.recv(n_bytes, socket.MSG_WAITALL) # receive n_bytes - return data - except socket.timeout: - # if we got a timeout data will still be none, so we call recv again - # this time in non blocking state and see if we can get some data - try: - return self.sock.recv(n_bytes, socket.MSG_DONTWAIT) - except socket.timeout: - raise TimeoutError - - def get_bytes_or_fail(self, n_bytes, deadline): - result = self.get_bytes(n_bytes, deadline) - if len(result) < n_bytes: - raise TimeoutError("expected {} bytes but got only {}".format(n_bytes, len(result))) - return result - - - -def discover_channels(path, serial_number, callback, cancellation_token, channel_termination_token, logger): - """ - Tries to connect to a TCP server based on the path spec. - This function blocks until cancellation_token is set. - Channels spawned by this function run until channel_termination_token is set. - """ - try: - dest_addr = ':'.join(path.split(":")[:-1]) - dest_port = int(path.split(":")[-1]) - except (ValueError, IndexError): - raise Exception('"{}" is not a valid TCP destination. The format should be something like "localhost:1234".' - .format(path)) - - while not cancellation_token.is_set(): - try: - tcp_transport = fibre.tcp_transport.TCPTransport(dest_addr, dest_port, logger) - stream2packet_input = fibre.protocol.PacketFromStreamConverter(tcp_transport) - packet2stream_output = fibre.protocol.StreamBasedPacketSink(tcp_transport) - channel = fibre.protocol.Channel( - "TCP device {}:{}".format(dest_addr, dest_port), - stream2packet_input, packet2stream_output, - channel_termination_token, logger) - except: - #logger.debug("TCP channel init failed. More info: " + traceback.format_exc()) - pass - else: - callback(channel) - wait_any(None, cancellation_token, channel._channel_broken) - time.sleep(1) diff --git a/Firmware/fibre/python/fibre/udp_transport.py b/Firmware/fibre/python/fibre/udp_transport.py deleted file mode 100644 index d0e4c2f04..000000000 --- a/Firmware/fibre/python/fibre/udp_transport.py +++ /dev/null @@ -1,57 +0,0 @@ - -import sys -import socket -import time -import traceback -import fibre.protocol -from fibre.utils import wait_any - -def noprint(x): - pass - -class UDPTransport(fibre.protocol.PacketSource, fibre.protocol.PacketSink): - def __init__(self, dest_addr, dest_port, logger): - # TODO: FIXME: use IPv6 - # Problem: getaddrinfo fails if the resolver returns an - # IPv4 address, but we are using AF_INET6 - #family = socket.AF_INET6 if socket.has_ipv6 else socket.AF_INET - family = socket.AF_INET - self.sock = socket.socket(family, socket.SOCK_DGRAM) - # TODO: Determine the right address to use from the list - self.target = socket.getaddrinfo(dest_addr,dest_port, family)[0][4] - - def process_packet(self, buffer): - self.sock.sendto(buffer, self.target) - - def get_packet(self, deadline): - # TODO: implement deadline - data, _ = self.sock.recvfrom(1024) - return data - -def discover_channels(path, serial_number, callback, cancellation_token, channel_termination_token, logger): - """ - Tries to connect to a UDP server based on the path spec. - This function blocks until cancellation_token is set. - Channels spawned by this function run until channel_termination_token is set. - """ - try: - dest_addr = ':'.join(path.split(":")[:-1]) - dest_port = int(path.split(":")[-1]) - except (ValueError, IndexError): - raise Exception('"{}" is not a valid UDP destination. The format should be something like "localhost:1234".' - .format(path)) - - while not cancellation_token.is_set(): - try: - udp_transport = fibre.udp_transport.UDPTransport(dest_addr, dest_port, logger) - channel = fibre.protocol.Channel( - "UDP device {}:{}".format(dest_addr, dest_port), - udp_transport, udp_transport, - channel_termination_token, logger) - except: - logger.debug("UDP channel init failed. More info: " + traceback.format_exc()) - pass - else: - callback(channel) - wait_any(None, cancellation_token, channel._channel_broken) - time.sleep(1) diff --git a/Firmware/fibre/python/fibre/usbbulk_transport.py b/Firmware/fibre/python/fibre/usbbulk_transport.py deleted file mode 100644 index 6643c8b25..000000000 --- a/Firmware/fibre/python/fibre/usbbulk_transport.py +++ /dev/null @@ -1,216 +0,0 @@ -# requires pyusb -# pip install --pre pyusb - -import usb.core -import usb.util -import sys -import time -import fibre.protocol -import traceback -import platform -from fibre.utils import TimeoutError - -# Currently we identify fibre-enabled devices by VID,PID -# TODO: identify by USB descriptors -WELL_KNOWN_VID_PID_PAIRS = [ - (0x1209, 0x0D31), - (0x1209, 0x0D32), - (0x1209, 0x0D33) -] - -class USBBulkTransport(fibre.protocol.PacketSource, fibre.protocol.PacketSink): - def __init__(self, dev, logger): - self._logger = logger - self.dev = dev - self.intf = None - self._name = "USB device {}:{}".format(dev.idVendor, dev.idProduct) - self._was_damaged = False - - ## - # information about the connected device - ## - def info(self): - # loop through configurations - string = "" - for cfg in self.dev: - string += "ConfigurationValue {0}\n".format(cfg.bConfigurationValue) - for intf in cfg: - string += "\tInterfaceNumber {0},{1}\n".format(intf.bInterfaceNumber, intf.bAlternateSetting) - for ep in intf: - string += "\t\tEndpointAddress {0}\n".format(ep.bEndpointAddress) - return string - - def init(self): - # Under some conditions, the Linux USB/libusb stack ends up in a corrupt - # state where there are a few packets in a receive queue but a call - # to epr.read() does not return these packet until a new packet arrives. - # This undesirable queue can be cleared by resetting the device. - # On windows this would cause file-not-found errors in subsequent dev calls - if platform.system() != 'Windows': - self.dev.reset() - - #self.dev.set_configuration() # no args: set first configuration - - # Find the best interface - self.cfg = self.dev.get_active_configuration() - custom_interfaces = [i for i in self.cfg.interfaces() if i.bInterfaceClass == 0x00 and i.bInterfaceSubClass == 0x01] - cdc_interfaces = [i for i in self.cfg.interfaces() if i.bInterfaceClass == 0x0a and i.bInterfaceSubClass == 0x00] - all_compatible_interfaces = custom_interfaces + cdc_interfaces - if len(all_compatible_interfaces) == 0: - raise Exception("the device has no compatible interfaces") - self.intf = all_compatible_interfaces[0] - - # Try to detach kernel driver from interface - try: - if self.dev.is_kernel_driver_active(self.intf.bInterfaceNumber): - self.dev.detach_kernel_driver(self.intf.bInterfaceNumber) - self._logger.debug("Detached Kernel Driver") - else: - self._logger.debug("Kernel Driver was not attached") - except NotImplementedError: - pass #is_kernel_driver_active not implemented on Windows - - # find write endpoint (first OUT endpoint) - self.epw = usb.util.find_descriptor(self.intf, - custom_match = \ - lambda e: \ - usb.util.endpoint_direction(e.bEndpointAddress) == \ - usb.util.ENDPOINT_OUT - ) - assert self.epw is not None - self._logger.debug("EndpointAddress for writing {}".format(self.epw.bEndpointAddress)) - # find read endpoint (first IN endpoint) - self.epr = usb.util.find_descriptor(self.intf, - custom_match = \ - lambda e: \ - usb.util.endpoint_direction(e.bEndpointAddress) == \ - usb.util.ENDPOINT_IN - ) - assert self.epr is not None - self._logger.debug("EndpointAddress for reading {}".format(self.epr.bEndpointAddress)) - - def deinit(self): - if not self.intf is None: - usb.util.release_interface(self.dev, self.intf) - - def process_packet(self, usbBuffer): - try: - ret = self.epw.write(usbBuffer, 0) - if self._was_damaged: - self._logger.debug("Recovered from USB halt/stall condition") - self._was_damaged = False - return ret - except usb.core.USBError as ex: - if ex.errno == 19 or ex.errno == 32: # "no such device", "pipe error" - raise fibre.protocol.ChannelBrokenException() - elif ex.errno is None or ex.errno == 60 or ex.errno == 110: # timeout - raise TimeoutError() - else: - self._logger.debug("error in usbbulk_transport.py, process_packet") - self._logger.debug(traceback.format_exc()) - self._logger.debug("halt condition: {}".format(ex.errno)) - self._logger.debug(str(ex)) - # Try resetting halt/stall condition - try: - self.deinit() - self.init() - except usb.core.USBError: - raise fibre.protocol.ChannelBrokenException() - # Retry transfer - self._was_damaged = True - raise fibre.protocol.ChannelDamagedException() - - def get_packet(self, deadline): - try: - bufferLen = self.epr.wMaxPacketSize - timeout = max(int((deadline - time.monotonic()) * 1000), 0) - ret = self.epr.read(bufferLen, timeout) - if self._was_damaged: - self._logger.debug("Recovered from USB halt/stall condition") - self._was_damaged = False - return bytearray(ret) - except usb.core.USBError as ex: - if ex.errno == 19 or ex.errno == 32: # "no such device", "pipe error" - raise fibre.protocol.ChannelBrokenException() - elif ex.errno is None or ex.errno == 60 or ex.errno == 110: # timeout - raise TimeoutError() - else: - self._logger.debug("error in usbbulk_transport.py, process_packet") - self._logger.debug(traceback.format_exc()) - self._logger.debug("halt condition: {}".format(ex.errno)) - self._logger.debug(str(ex)) - # Try resetting halt/stall condition - try: - self.deinit() - self.init() - except usb.core.USBError: - raise fibre.protocol.ChannelBrokenException() - # Retry transfer - self._was_damaged = True - raise fibre.protocol.ChannelDamagedException() - - -def discover_channels(path, serial_number, callback, cancellation_token, channel_termination_token, logger): - """ - Scans for USB devices that match the path spec. - This function blocks until cancellation_token is set. - Channels spawned by this function run until channel_termination_token is set. - """ - if path == None or path == "": - bus = None - address = None - else: - try: - bus = int(path.split(":")[0]) - address = int(path.split(":")[1]) - except (ValueError, IndexError): - raise Exception("{} is not a valid USB path specification. " - "Expected a string of the format BUS:DEVICE where BUS " - "and DEVICE are integers.".format(path)) - - known_devices = [] - def device_matcher(device): - #print(" test {:04X}:{:04X}".format(device.idVendor, device.idProduct)) - try: - if (device.bus, device.address) in known_devices: - return False - if bus != None and device.bus != bus: - return False - if address != None and device.address != address: - return False - if serial_number != None and device.serial_number != serial_number: - return False - if (device.idVendor, device.idProduct) not in WELL_KNOWN_VID_PID_PAIRS: - return False - except: - return False - return True - - while not cancellation_token.is_set(): - # logger.debug("USB discover loop") - devices = usb.core.find(find_all=True, custom_match=device_matcher) - for usb_device in devices: - try: - bulk_device = USBBulkTransport(usb_device, logger) - logger.debug(bulk_device.info()) - bulk_device.init() - channel = fibre.protocol.Channel( - "USB device bus {} device {}".format(usb_device.bus, usb_device.address), - bulk_device, bulk_device, channel_termination_token, logger) - channel.usb_device = usb_device # for debugging only - except usb.core.USBError as ex: - if ex.errno == 13: - # TODO: this is an ODrive specific message and should live outside of the fibre library - logger.warn("I found a USB device that looks like an ODrive (bus {}, device {}) but I can't access it. Try running `sudo odrivetool udev-setup`, then unplug and replug the device.".format(usb_device.bus, usb_device.address)) - known_devices.append((usb_device.bus, usb_device.address)) - elif ex.errno == 16: - logger.debug("USB device busy. I'll reset it and try again.") - usb_device.reset() - continue - else: - logger.warn("USB device init failed (bus {}, device {}). Ignoring this device. More info: ".format(usb_device.bus, usb_device.address) + traceback.format_exc()) - known_devices.append((usb_device.bus, usb_device.address)) - else: - known_devices.append((usb_device.bus, usb_device.address)) - callback(channel) - time.sleep(1) diff --git a/Firmware/fibre/test/Tupfile.lua b/Firmware/fibre/test/Tupfile.lua deleted file mode 100644 index 3a4dbcf12..000000000 --- a/Firmware/fibre/test/Tupfile.lua +++ /dev/null @@ -1,27 +0,0 @@ - - -tup.include('../tupfiles/build.lua') -tup.include('../cpp/package.lua') - -test_server = define_package{ - packages={fibre_package}, - sources={'test_server.cpp'} -} - -unit_tests = define_package{ - packages={fibre_package}, - sources={'run_tests.cpp'} -} - - -toolchain=GCCToolchain('', 'build', {'-O3', '-fvisibility=hidden', '-frename-registers', '-funroll-loops'}, {}) -toolchain=GCCToolchain('', 'build', {'-O3', '-g', '-Wall'}, {}) ---toolchain=GCCToolchain('avr-', {'-Ofast', '-fvisibility=hidden', '-frename-registers', '-funroll-loops', '-I/home/samuel/stlport-avr/stlport'}, {}) ---toolchain=LLVMToolchain('x86_64', {'-O3', '-fno-sanitize=safe-stack', '-fno-stack-protector'}, {'-flto', '-Wl,-s'}) ---toolchain=LLVMToolchain('avr', {'-O3', '-std=gnu++11', '--target=avr', '-fno-sanitize=safe-stack', '-fno-stack-protector', '-I/home/samuel/stlport-avr/stlport'}, {'-flto', '-Wl,-s'}) - - -if tup.getconfig("BUILD_FIBRE_TESTS") == "true" then - build_executable('test_server', test_server, toolchain) - --build_executable('run_tests', unit_tests, toolchain) -end diff --git a/Firmware/fibre/test/run_tests.cpp b/Firmware/fibre/test/run_tests.cpp deleted file mode 100644 index 43e24c861..000000000 --- a/Firmware/fibre/test/run_tests.cpp +++ /dev/null @@ -1,140 +0,0 @@ - -#include -#include -#include -#include - -//#define DEBUG_PROTOCOL -void hexdump(const uint8_t* buf, size_t len); - -#include -#include -#include - -void hexdump(const uint8_t* buf, size_t len) { - for (size_t pos = 0; pos < len; ++pos) { - printf(" %02x", buf[pos]); - if ((((pos + 1) % 16) == 0) || ((pos + 1) == len)) - printf("\r\n"); - //osDelay(2); - } -} - - - -bool varint_decoder_test() { - struct test_case_t { - uint8_t encoded[10]; - size_t length; - uint32_t decoded; - }; - const test_case_t test_cases[] = { - // encoded, length, decoded - { { 0x00 }, 1, 0 }, - { { 0x01 }, 1, 1 }, - { { 0xff, 0x01 }, 2, 0xff }, - { { 0xAC, 0x02 }, 2, 300 }, - { { 0xff, 0xff, 0xff, 0xff, 0xf }, 5, 0xffffffff } - }; - - for (size_t i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) { - uint32_t result; - VarintStreamDecoder decoder = make_varint_decoder(result); - size_t processed_bytes = 0; - int status = decoder.process_bytes(test_cases[i].encoded, test_cases[i].length, &processed_bytes); - if (status) { - return false; - } else if (processed_bytes != test_cases[i].length) { - printf("test %zu: expected to process %zu bytes but processed %zu bytes\n", i, test_cases[i].length, processed_bytes); - return false; - } else if (result != test_cases[i].decoded) { - printf("test %zu: expected %u but got %u\n", i, test_cases[i].decoded, result); - return false; - } - - VarintStreamEncoder encoder = make_varint_encoder(test_cases[i].decoded); - uint8_t buffer[10]; - size_t generated_bytes = 0; - status = encoder.get_bytes(buffer, sizeof(buffer), &generated_bytes); - if (status) { - return false; - } else if ((generated_bytes != test_cases[i].length) - || memcmp(buffer, test_cases[i].encoded, test_cases[i].length)) { - printf("test %zu: expected:", i); - hexdump(test_cases[i].encoded, test_cases[i].length); - printf("got: "); - hexdump(buffer, generated_bytes); - return false; - } - } - return true; -} - - - -int main(void) { - /***** Decoder demo (remove or move somewhere else) *****/ - printf("Running decoder... "); - // prepare raw data - uint8_t raw_data[] = { 0xBC, 0x03, 0xAC, 0x5e, 0x02, 0x00, 0x00, 0xd1 }; - //raw_data[3] = calc_crc8(CANONICAL_CRC8_INIT, raw_data, 3); - //raw_data[7] = calc_crc8(raw_data[3], raw_data + 4, 3); - - // instantiate decoder - ReceiverState state; - auto decoder = make_crc8_decoder( - make_decoder_chain( - make_length_decoder(state), - make_endpoint_id_decoder(state) - ) - ); - - // push the raw data through the decoder - size_t processed_bytes = 0; - int status = decoder.process_bytes(raw_data, sizeof(raw_data), &processed_bytes); - - // expected result: "length: 444, endpoint-id: 300, processed 8 bytes" - if (status == 0) - printf("length: %zu, endpoint-id: %zu, processed %zu bytes\n", state.length, state.endpoint_id, processed_bytes); - else - printf("decoder demo failed\n"); - - - /***** Encoder demo (remove or move somewhere else) *****/ - printf("Running encoder... "); - // prepare request - Request request = { - .endpoint_id = 300, - .length = 444, - }; - - // construct encoder for the request - auto e2 = make_crc8_encoder( - make_encoder_chain( - make_length_encoder(request), - make_endpoint_id_encoder(request) - ) - ); - - // pull raw data out of the encoder - uint8_t buffer[20]; - size_t generated_bytes = 0; - status = e2.get_bytes(buffer, sizeof(buffer), &generated_bytes); - if (status == 0) { - printf("generated %zu bytes:\n", generated_bytes); - hexdump(buffer, generated_bytes); - } else { - printf("encoder demo failed\n"); - } - - - /***** run automated test *****/ - bool test_result = varint_decoder_test(); - if (test_result) { - printf("all tests passed\n"); - return 0; - } else { - printf("some tests failed\n"); - return -1; - } -} diff --git a/Firmware/fibre/test/test_server.cpp b/Firmware/fibre/test/test_server.cpp deleted file mode 100644 index 9aa849b8e..000000000 --- a/Firmware/fibre/test/test_server.cpp +++ /dev/null @@ -1,52 +0,0 @@ - -#include -#include -#include -#include - -#include -#include -#include - - -class TestClass { -public: - float property1; - float property2; - - float set_both(float arg1, float arg2) { - property1 = arg1; - property2 = arg2; - return property1 + property2; - } - - FIBRE_EXPORTS(TestClass, - make_protocol_property("property1", &property1), - make_protocol_property("property2", &property2), - make_protocol_function("set_both", *obj, &TestClass::set_both, "arg1", "arg2") - ); -}; - - -int main() { - printf("Starting Fibre server...\n"); - - TestClass test_object = TestClass(); - - // publish the object on Fibre - auto definitions = test_object.fibre_definitions; - fibre_publish(definitions); - - // Expose Fibre objects on TCP and UDP - std::thread server_thread_tcp(serve_on_tcp, 9910); - std::thread server_thread_udp(serve_on_udp, 9910); - printf("Fibre server started.\n"); - - // Dump property1 value - while (1) { - printf("test_object.property1: %f\n", test_object.property1); - usleep(1000000 / 5); // 5 Hz - } - - return 0; -} diff --git a/Firmware/fibre/tupfiles/build.lua b/Firmware/fibre/tupfiles/build.lua deleted file mode 100644 index 8b4a45105..000000000 --- a/Firmware/fibre/tupfiles/build.lua +++ /dev/null @@ -1,211 +0,0 @@ - - -function GCCToolchain(prefix, builddir, compiler_flags, linker_flags) - - -- add some default compiler flags - compiler_flags += '-fstack-usage' - - local gcc_generic_compiler = function(compiler, compiler_flags, gen_su_file, src, flags, includes, outputs) - -- resolve source path - src = tostring(src) - - -- convert include list to flags - inc_flags = {} - for _,inc in pairs(includes) do - inc_flags += "-I"..tostring(inc) - end - - obj_file = builddir.."/"..src:gsub("/","_")..".o" - outputs.object_files += obj_file - if gen_su_file then - su_file = builddir.."/"..src:gsub("/","_")..".su" - extra_outputs = { su_file } - outputs.su_files += su_file - else - extra_outputs = {} - end - tup.frule{ - inputs= { tup.getcwd()..'/'..src }, - command=compiler..' -c %f '.. - tostring(compiler_flags)..' '.. -- CFLAGS for this compiler - tostring(inc_flags)..' '.. -- CFLAGS for this translation unit - tostring(flags).. -- CFLAGS for this translation unit - ' -o %o', - outputs={obj_file,extra_outputs=extra_outputs} - } - end - return { - compile_c = function(src, flags, includes, outputs) gcc_generic_compiler(prefix..'gcc -std=c99', compiler_flags, true, src, flags, includes, outputs) end, - compile_cpp = function(src, flags, includes, outputs) gcc_generic_compiler(prefix..'g++ -std=c++11', compiler_flags, true, src, flags, includes, outputs) end, - compile_asm = function(src, flags, includes, outputs) gcc_generic_compiler(prefix..'gcc -x assembler-with-cpp', compiler_flags, false, src, flags, includes, outputs) end, - link = function(objects, libs, output_name) - -- convert lib list to flags - lib_flags = {} - for _,inc in pairs(libs) do - lib_flags += "-l"..tostring(inc) - end - - output_name = builddir..'/'..output_name - tup.frule{ - inputs=objects, - command=prefix..'g++ %f '.. - tostring(linker_flags)..' '.. - tostring(lib_flags)..' '.. - '-Wl,-Map=%O.map'.. - ' -o %o', - outputs={output_name..'.elf', extra_outputs={output_name..'.map'}} - } - -- display the size - tup.frule{inputs={output_name..'.elf'}, command=prefix..'size %f'} - -- create *.hex and *.bin output formats - tup.frule{inputs={output_name..'.elf'}, command=prefix..'objcopy -O ihex %f %o', outputs={output_name..'.hex'}} - tup.frule{inputs={output_name..'.elf'}, command=prefix..'objcopy -O binary -S %f %o', outputs={output_name..'.bin'}} - end - } -end - - -function LLVMToolchain(arch, compiler_flags, linker_flags) - - -- add some default compiler flags - --compiler_flags += '-march='..arch - compiler_flags += '-std=c++14' - - clang_generic_compiler = function(compiler, compiler_flags, src, flags, includes, outputs) - -- add includes to CFLAGS - for _,inc in pairs(includes) do - flags += "-I"..inc - end - -- todo: vary build directory - obj_file="build/"..src:gsub("/","_")..".o" - tup.frule{ - inputs=src, - command=compiler..' -c %f '.. - tostring(compiler_flags)..' '.. -- CFLAGS for this compiler - tostring(flags).. -- CFLAGS for this translation unit - ' -o %o', - outputs={obj_file} - } - outputs.object_files += obj_file - end - return { - compile_c = function(src, flags, includes, outputs) clang_generic_compiler('clang', compiler_flags, src, flags, includes, outputs) end, - compile_cpp = function(src, flags, includes, outputs) clang_generic_compiler('clang++', compiler_flags, src, flags, includes, outputs) end, - link = function(objects, output_name) - tup.frule{ - inputs=objects, - command='clang++ %f '.. - tostring(linker_flags).. - ' -o %o', - outputs=output_name - } - end - } -end - - -function get_generalized_paths(paths) - if paths == nil then - return {} - else - -- TODO: check for string - generalized_paths = {} - for _,path in pairs(paths) do - table.insert(generalized_paths, tup.nodevariable(path)) - end - return generalized_paths - end -end - - --- A package is a collection of source files and associated --- information required to compile those source files. --- pkg.sources: The source files that shall be compiled as --- part of this package --- pkg.private_headers: The include directories that are required --- to compile the source files in this package --- pkg.headers: The include directories that are _exported_ --- by this package. These directories are included --- when compiling other packages that import --- this package. --- pkg.packages: The packages that are needed to compile and link this --- package. The public include directories of each imported --- package are passed to the compiler when compiling --- the source files of this package. The object files --- emitted by the imported packages are included when linking --- this package. --- pkg.libs: The libraries that are needed to link this --- package -function define_package(pkg) - --print('defined package in '..tup.getcwd()) - pkg.sources = get_generalized_paths(pkg.sources) - pkg.objects = get_generalized_paths(pkg.objects) - pkg.headers = get_generalized_paths(pkg.headers) - pkg.private_headers = get_generalized_paths(private_headers) - if pkg.packages == nil then pkg.packages = {} end - if pkg.libs == nil then pkg.libs = {} end - if pkg.c_flags == nil then pkg.c_flags = {} end - if pkg.cpp_flags == nil then pkg.cpp_flags = {} end - if pkg.asm_flags == nil then pkg.asm_flags = {} end - return pkg -end - - --- Builds object files from the source files in the specified package -function build_objects(pkg, toolchain) - all_headers = {} - tup.append_table(all_headers, pkg.private_headers) - tup.append_table(all_headers, pkg.headers) - - -- add exported header directories of each imported package - for _,imported_pkg in pairs(pkg.packages) do - tup.append_table(all_headers, imported_pkg.headers) - end - - -- compile - outputs = { - object_files = {} - } - tup.append_table(outputs.object_files, pkg.objects) - - for _,src in pairs(pkg.sources) do - --print("compile "..src) - ext = tup.ext(tostring(src)) - if ext == 'c' then - toolchain.compile_c(src, pkg.c_flags, all_headers, outputs) - elseif ext == 'cpp' then - toolchain.compile_cpp(src, pkg.cpp_flags, all_headers, outputs) - elseif ext == 's' or tup.ext(src) == 'asm' then - toolchain.compile_asm(src, pkg.asm_flags, all_headers, outputs) - else - error('unrecognized file ending') - end - end - - return outputs.object_files -end - -function build_executable(name, pkg, toolchain) - all_object_files = {} - all_libs = {} - - -- TODO: flatten the import hierarchy prior to compiling - - -- build current package - tup.append_table(all_object_files, build_objects(pkg, toolchain)) - tup.append_table(all_libs, pkg.libs) - - -- build imported packages - for _,imported_pkg in pairs(pkg.packages) do - objects = build_objects(imported_pkg, toolchain) - tup.append_table(all_object_files, objects) - tup.append_table(all_libs, imported_pkg.libs) - end - - -- link - --tup.append_table(args.linker_objects, outputs.object_files) - print('link objects ') - print(all_object_files) - print(all_libs) - toolchain.link(all_object_files, all_libs, name) -end diff --git a/Firmware/find_programmer.sh b/Firmware/find_programmer.sh deleted file mode 100755 index cea570380..000000000 --- a/Firmware/find_programmer.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -openocd -d3 -f board/stm32f4discovery.cfg -c "hla_serial wrong_serial" 2>&1 | \ - xxd -p | \ - tr -d '\n' | \ - sed -n 's/^.*6e756d6265722027\([0-9a-f]*\)2720646f65736e27.*$/\1/p' | sed -e 's/.\{2\}/\\x&/g'; echo diff --git a/Firmware/Board/v3/Inc/freertos_vars.h b/Firmware/freertos_vars.h similarity index 63% rename from Firmware/Board/v3/Inc/freertos_vars.h rename to Firmware/freertos_vars.h index 7e86c9715..a15e96bf1 100644 --- a/Firmware/Board/v3/Inc/freertos_vars.h +++ b/Firmware/freertos_vars.h @@ -2,16 +2,15 @@ #ifndef __FREERTOS_H #define __FREERTOS_H +// TODO: this header is weird. Move these declarations to somewhere else. + // List of semaphores extern osSemaphoreId sem_usb_irq; -extern osSemaphoreId sem_uart_dma; -extern osSemaphoreId sem_usb_rx; -extern osSemaphoreId sem_usb_tx; +extern osMessageQId uart_event_queue; +extern osMessageQId usb_event_queue; extern osSemaphoreId sem_can; extern osThreadId defaultTaskHandle; -extern osThreadId usb_irq_thread; -extern const uint32_t stack_size_usb_irq_thread; extern const uint32_t stack_size_default_task; #endif /* __FREERTOS_H */ \ No newline at end of file diff --git a/Firmware/interface_generator_stub.py b/Firmware/interface_generator_stub.py index d5b220939..cc5062c23 100644 --- a/Firmware/interface_generator_stub.py +++ b/Firmware/interface_generator_stub.py @@ -4,7 +4,8 @@ import os try: - exec(open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'fibre', 'tools', 'interface_generator.py')).read()) + path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'tools', 'fibre-tools', 'interface_generator.py') + exec(compile(open(path).read(), path, 'exec')) except ImportError as ex: print(str(ex), file=sys.stderr) print("Note that there are new compile-time dependencies since around v0.5.1.", file=sys.stderr) diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 4dfa7ad7f..09094161a 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -5,6 +5,12 @@ summary: ODrive Interface Definitions dictionary: [ODrive] # Prevent the word 'ODrive' from being detected as two words 'O' and 'Drive' +userdata: + c_preamble: | + #include + using float2D = std::pair; + struct Iph_ABC_t { float phA; float phB; float phC; }; + interfaces: ODrive: c_is_class: True @@ -13,6 +19,66 @@ interfaces: The odrv0, odrv1, ... objects that appear in odrivetool implement this toplevel interface. attributes: + error: + nullflag: NONE + flags: + CONTROL_ITERATION_MISSED: + brief: At least one control iteration was missed. + doc: | + The main control loop is supposed to runs at a fixed frequency. + If the device is computationally overloaded (e.g. too many active + components) it's possible that one or more control iterations + are skipped. + DC_BUS_UNDER_VOLTAGE: + brief: The DC voltage fell below the limit configured in `config.dc_bus_undervoltage_trip_level`. + doc: | + Confirm that your power leads are connected securely. For initial + testing a 12V PSU which can supply a couple of amps should be + sufficient while the use of low current ‘wall wart’ plug packs may + lead to inconsistent behaviour and is not recommended. + + You can monitor your PSU voltage using liveplotter in odrivetool + by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If + you see your votlage drop below `config.dc_bus_undervoltage_trip_level` + (default: ~ 8V) then you will trip this error. Even a relatively + small motor can draw multiple kW momentary and so unless you have + a very large PSU or are running of a battery you may encounter + this error when executing high speed movements with a high current + limit. To limit your PSU power draw you can limit your motor + current and/or velocity limit `controller.config.vel_limit` and + `motor.config.current_lim`. + DC_BUS_OVER_VOLTAGE: + brief: The DC voltage exceeded the limit configured in `config.dc_bus_overvoltage_trip_level`. + doc: | + Confirm that you have a brake resistor of the correct value + connected securely and that `config.brake_resistance` is set to + the value of your brake resistor. + + You can monitor your PSU voltage using liveplotter in odrivetool + by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If + during a move you see the voltage rise above your PSU’s nominal + set voltage then you have your brake resistance set too low. This + may happen if you are using long wires or small gauge wires to + connect your brake resistor to your odrive which will added extra + resistance. This extra resistance needs to be accounted for to + prevent this voltage spike. If you have checked all your + connections you can also try increasing your brake resistance by + ~ 0.01 Ohm at a time to a maximum of 0.05 greater than your brake + resistor value. + DC_BUS_OVER_REGEN_CURRENT: + doc: | + Current flowing back into the power supply exceeded `odrv.config.dc_max_negative_current`. + This can happen if your brake resistor is unable to handle the braking current. Check that + `(V_power_supply / Brake_resistance) > (total motor.config.current_lim + total motor.config.current_lim_margin)`. + DC_BUS_OVER_CURRENT: + doc: | + Too much current was pulled from the power supply. `odrv.ibus` exceeded `odrv.config.dc_max_positive_current`. + BRAKE_DEADTIME_VIOLATION: + BRAKE_DUTY_CYCLE_NAN: + INVALID_BRAKE_RESISTANCE: {doc: '`config.brake_resistance` is non-positive or NaN. Make sure that `config.brake_resistance` is a positive number.'} +# BRAKE_RESISTOR_DISARMED: +# doc: The brake resistor was unexpectedly disarmed. + vbus_voltage: type: readonly float32 unit: V @@ -46,27 +112,43 @@ interfaces: doc: 0 for official releases, 1 otherwise brake_resistor_armed: readonly bool brake_resistor_saturated: bool + + # Diagnostics & performance monitoring + n_evt_sampling: {type: readonly uint32, doc: Number of input sampling events since startup (modulo 2^32)} + n_evt_control_loop: {type: readonly uint32, doc: Number of control loop iterations since startup (modulo 2^32)} + task_timers_armed: + type: bool + doc: | + Set by a profiling application to trigger sampling of a single + control iteration. Cleared by the device as soon as the sampling + is complete. + task_times: + c_is_class: False + attributes: + sampling: TaskTimer + control_loop_misc: TaskTimer + control_loop_checks: TaskTimer + dc_calib_wait: TaskTimer system_stats: c_is_class: False attributes: uptime: readonly uint32 min_heap_space: readonly uint32 - min_stack_space_axis0: readonly uint32 - min_stack_space_axis1: readonly uint32 - min_stack_space_comms: readonly uint32 - min_stack_space_usb: readonly uint32 - min_stack_space_uart: readonly uint32 - min_stack_space_can: readonly uint32 - min_stack_space_usb_irq: readonly uint32 - min_stack_space_startup: readonly uint32 - stack_usage_axis0: readonly uint32 - stack_usage_axis1: readonly uint32 - stack_usage_comms: readonly uint32 - stack_usage_usb: readonly uint32 - stack_usage_uart: readonly uint32 - stack_usage_usb_irq: readonly uint32 - stack_usage_startup: readonly uint32 - stack_usage_can: readonly uint32 + max_stack_usage_axis: readonly uint32 + max_stack_usage_usb: readonly uint32 + max_stack_usage_uart: readonly uint32 + max_stack_usage_can: readonly uint32 + max_stack_usage_startup: readonly uint32 + stack_size_axis: readonly uint32 + stack_size_usb: readonly uint32 + stack_size_uart: readonly uint32 + stack_size_startup: readonly uint32 + stack_size_can: readonly uint32 + prio_axis: readonly int32 + prio_usb: readonly int32 + prio_uart: readonly int32 + prio_startup: readonly int32 + prio_can: readonly int32 usb: c_is_class: False attributes: @@ -80,136 +162,233 @@ interfaces: addr_match_cnt: readonly uint32 rx_cnt: readonly uint32 error_cnt: readonly uint32 - config: - c_is_class: False - attributes: - enable_uart: - type: bool - doc: 'TODO: changing this currently requires a reboot - fix this' - uart_baudrate: - type: uint32 - doc: | - Defines the baudrate used on the UART interface. - Some baudrates will have a small timing error due to hardware limitations. - - Here's an (incomplete) list of baudrates for ODrive v3.x: - - Configured | Actual | Error [%] - -------------|---------------|----------- - 1.2 KBps | 1.2 KBps | 0 - 2.4 KBps | 2.4 KBps | 0 - 9.6 KBps | 9.6 KBps | 0 - 19.2 KBps | 19.195 KBps | 0.02 - 38.4 KBps | 38.391 KBps | 0.02 - 57.6 KBps | 57.613 KBps | 0.02 - 115.2 KBps | 115.068 KBps | 0.11 - 230.4 KBps | 230.769 KBps | 0.16 - 460.8 KBps | 461.538 KBps | 0.16 - 921.6 KBps | 913.043 KBps | 0.93 - 1.792 MBps | 1.826 MBps | 1.9 - 1.8432 MBps | 1.826 MBps | 0.93 - - For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the - [STM datasheet](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf). - enable_i2c_instead_of_can: - type: bool - doc: Changing this requires a reboot. - enable_ascii_protocol_on_usb: bool - max_regen_current: float32 - brake_resistance: - type: float32 - unit: Ohm - brief: Value of the brake resistor connected to the ODrive. - doc: Set to 0 to disable. - - dc_bus_undervoltage_trip_level: - type: float32 - unit: V - brief: Minimum voltage below which the motor stops operating. - dc_bus_overvoltage_trip_level: - type: float32 - unit: V - brief: Maximum voltage above which the motor stops operating. - doc: | - This protects against cases in which the power supply fails to dissipate - the brake power if the brake resistor is disabled. - The default is 26V for the 24V board version and 52V for the 48V board version. - - enable_dc_bus_overvoltage_ramp: - type: bool - status: experimental - brief: Enables the DC bus overvoltage ramp feature. - doc: | - If enabled, if the measured DC voltage exceeds `dc_bus_overvoltage_ramp_start`, - the ODrive will sink more power than usual into the the brake resistor - in an attempt to bring the voltage down again. - - The brake duty cycle is increased by the following amount: - - * `vbus_voltage` == `dc_bus_overvoltage_ramp_start` => brake_duty_cycle += 0% - * `vbus_voltage` == `dc_bus_overvoltage_ramp_end` => brake_duty_cycle += 100% - - Remarks: - - This feature is active even when all motors are disarmed. - - This feature is disabled if `brake_resistance` is non-positive. - dc_bus_overvoltage_ramp_start: - type: float32 - status: experimental - brief: See `enable_dc_bus_overvoltage_ramp`. - doc: Do not set this lower than your usual `vbus_voltage`, - unless you like fried brake resistors. - dc_bus_overvoltage_ramp_end: - type: float32 - status: experimental - brief: See `enable_dc_bus_overvoltage_ramp`. - doc: Must be larger than `dc_bus_overvoltage_ramp_start`, - otherwise the ramp feature is disabled. + user_config_loaded: readonly uint32 + misconfigured: + # TODO: make this a system error + type: readonly bool + doc: | + If this property is true, something is bad in the configuration. The + ODrive can still be used in this state but the user should investigate + which setting is problematic. This variable does not cover all + misconfigurations. - dc_max_positive_current: - type: float32 - unit: A - brief: Max current the power supply can source. - dc_max_negative_current: - type: float32 - unit: A - brief: Max current the power supply can sink. - doc: You most likely want a non-positive value here. Set to -INFINITY to disable. - - gpio1_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[0]'} # TODO: disable for ODrive v3.2 and older - gpio2_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[1]'} # TODO: disable for ODrive v3.2 and older - gpio3_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[2]'} # TODO: disable for ODrive v3.2 and older - gpio4_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[3]'} - gpio3_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[2]'} - gpio4_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[3]'} - user_config_loaded: readonly bool + Possible causes: + - A GPIO was set to a mode that it doesn't support + - A GPIO was set to a mode for which the corresponding feature was + not enabled. Example: `GPIO_MODE_UART_A` was used without enabling + `config.enable_uart_a`. + - A feature was enabled which is not supported on this hardware. + Example: `config.enable_uart_c` set to true on ODrive v3.x. + - A GPIO was used as an interrupt input for two internal components + or two GPIOs that are mutually exclusive in their interrupt + capability were both used as interrupt input. + Example: `step_gpio_pin` of both axes were set to the same GPIO. - axis0: {type: Axis, c_name: get_axis(0)} - axis1: {type: Axis, c_name: get_axis(1)} - can: {type: Can, c_name: get_can()} + oscilloscope: {type: Oscilloscope} + can: {type: Can} test_property: uint32 functions: test_function: {in: {delta: int32}, out: {cnt: int32}} - get_oscilloscope_val: {in: {index: uint32}, out: {val: float32}} - get_adc_voltage: {in: {gpio: uint32}, out: {voltage: float32}} - save_configuration: + get_adc_voltage: {in: {gpio: uint32}, out: {voltage: float32}, doc: Reads the ADC voltage of the specified GPIO. The GPIO should be in `GPIO_MODE_ANALOG_IN`.} + save_configuration: {out: {success: bool}} erase_configuration: reboot: enter_dfu_mode: + get_interrupt_status: + in: {irqn: {type: int32, doc: '-12...-1: processor interrupts, 0...239: NVIC interrupts'}} + out: + status: + type: uint32 + doc: | + bit 31: enabled (1) or disabled (0) + bits 30:8: number of times the interrupt fired (modulo 0x800000) + bits 7:0: priority (0 is highest priority) + 0xffffffff if the specified number is not a valid interrupt number. + doc: Returns information about the specified interrupt number. + get_dma_status: + in: {stream_num: {type: uint8, doc: '0...7: DMA1 streams, 8...15: DMA2 streams'}} + out: + status: + type: uint32 + doc: | + bit 31: zero if the stream's configuration is equal to the reset state + bits 4:2: channel + bits 1:0: priority (3 is highest priority) + 0xffffffff if the specified number is not a valid DMA stream number. + doc: Returns information about the specified DMA stream. + get_gpio_states: + out: {status: {type: uint32}} + doc: Returns the logic states of all GPIOs. Bit i represents the state of GPIOi. + get_drv_fault: {out: {drv_fault: uint64}} + clear_errors: + doc: Clear all the errors of this device including all contained submodules. + + ODrive.Config: + c_is_class: False + attributes: + enable_uart_a: + type: bool + brief: Enables/disables UART_A. + doc: | + You also need to set the corresponding GPIOs to GPIO_MODE_UART_A. + Refer to [interfaces](interfaces.md) to see which pins support UART_A. + Changing this requires a reboot. + enable_uart_b: + type: bool + brief: Enables/disables UART_B. + doc: | + You also need to set the corresponding GPIOs to GPIO_MODE_UART_B. + Refer to [interfaces](interfaces.md) to see which pins support UART_B. + Changing this requires a reboot. + enable_uart_c: {type: bool, doc: Not supported on ODrive v3.x.} + uart_a_baudrate: + type: uint32 + unit: baud/s + brief: Defines the baudrate used on the UART interface. + doc: | + Some baudrates will have a small timing error due to hardware limitations. + + Here's an (incomplete) list of baudrates for ODrive v3.x: + + Configured | Actual | Error [%] + -------------|---------------|----------- + 1.2 KBps | 1.2 KBps | 0 + 2.4 KBps | 2.4 KBps | 0 + 9.6 KBps | 9.6 KBps | 0 + 19.2 KBps | 19.195 KBps | 0.02 + 38.4 KBps | 38.391 KBps | 0.02 + 57.6 KBps | 57.613 KBps | 0.02 + 115.2 KBps | 115.068 KBps | 0.11 + 230.4 KBps | 230.769 KBps | 0.16 + 460.8 KBps | 461.538 KBps | 0.16 + 921.6 KBps | 913.043 KBps | 0.93 + 1.792 MBps | 1.826 MBps | 1.9 + 1.8432 MBps | 1.826 MBps | 0.93 + + For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the + [STM datasheet](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf). + uart_b_baudrate: + type: uint32 + unit: baud/s + brief: Defines the baudrate used on the UART interface. + doc: See `uart_a_baudrate` for details. + uart_c_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} + enable_can_a: + type: bool + doc: | + Enables CAN. Changing this setting requires a reboot. + enable_i2c_a: + type: bool + doc: | + Enables I2C. The I2C pins on ODrive v3.x are in conflict with CAN. + This setting has no effect if `enable_can_a` is also true. + This setting has no effect on ODrive v3.2 or earlier. + Changing this setting requires a reboot. + usb_cdc_protocol: + type: StreamProtocolType + doc: | + The protocol that's being run on the device's virtual COM port on + USB. + Note that the ODrive has two independent interfaces on USB: One + is the virtual COM port (affected by this option) and the other + one is a vendor specific interface which always runs Fibre. + So changing this option does not affect the working of odrivetool. + uart0_protocol: StreamProtocolType + uart1_protocol: StreamProtocolType + uart2_protocol: StreamProtocolType + max_regen_current: float32 + brake_resistance: + type: float32 + unit: Ohm + brief: Value of the brake resistor connected to the ODrive. + doc: | + If you set this to a lower value than the true brake resistance + then the ODrive will not meed the `max_regen_current` constraint + during braking, that is it will sink more than `max_regen_current` + into the power supply. Some power supplies don't like this. + + If you set this to a higher value than the true brake resistance + then the ODrive will unnecessarily burn more power than required + during braking. + enable_brake_resistor: + type: bool + brief: Enable/disable the use of a brake resistor. + doc: | + Setting this to False even though a brake resistor is connected is + harmless. Setting this to True even though no brake resistor is + connected can break the power supply. + Changes to this value require a reboot to take effect. + + dc_bus_undervoltage_trip_level: + type: float32 + unit: V + brief: Minimum voltage below which the motor stops operating. + dc_bus_overvoltage_trip_level: + type: float32 + unit: V + brief: Maximum voltage above which the motor stops operating. + doc: | + This protects against cases in which the power supply fails to dissipate + the brake power if the brake resistor is disabled. + The default is 26V for the 24V board version and 52V for the 48V board version. + + enable_dc_bus_overvoltage_ramp: + type: bool + status: experimental + brief: Enables the DC bus overvoltage ramp feature. + doc: | + If enabled, if the measured DC voltage exceeds `dc_bus_overvoltage_ramp_start`, + the ODrive will sink more power than usual into the the brake resistor + in an attempt to bring the voltage down again. + + The brake duty cycle is increased by the following amount: + + * `vbus_voltage` == `dc_bus_overvoltage_ramp_start` => brake_duty_cycle += 0% + * `vbus_voltage` == `dc_bus_overvoltage_ramp_end` => brake_duty_cycle += 100% + + Remarks: + - This feature is active even when all motors are disarmed. + - This feature is disabled if `brake_resistance` is non-positive. + dc_bus_overvoltage_ramp_start: + type: float32 + status: experimental + brief: See `enable_dc_bus_overvoltage_ramp`. + doc: Do not set this lower than your usual `vbus_voltage`, + unless you like fried brake resistors. + dc_bus_overvoltage_ramp_end: + type: float32 + status: experimental + brief: See `enable_dc_bus_overvoltage_ramp`. + doc: Must be larger than `dc_bus_overvoltage_ramp_start`, + otherwise the ramp feature is disabled. + + dc_max_positive_current: + type: float32 + unit: A + brief: Max current the power supply can source. + dc_max_negative_current: + type: float32 + unit: A + brief: Max current the power supply can sink. + doc: You most likely want a non-positive value here. Set to -INFINITY to disable. + + error_gpio_pin: {type: uint32} + + gpio3_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} + gpio4_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[4]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} ODrive.Can: c_is_class: True attributes: error: - nullflag: None - flags: {DuplicateCanIds: } + nullflag: NONE + flags: {DUPLICATE_CAN_IDS: } config: c_is_class: False attributes: - baud_rate: readonly uint32 + baud_rate: {type: uint32, c_setter: 'set_baud_rate'} protocol: Protocol - functions: - set_baud_rate: {in: {baudRate: uint32}} ODrive.Endpoint: c_is_class: False @@ -222,84 +401,40 @@ interfaces: c_is_class: True attributes: error: - nullflag: 'None' + nullflag: NONE flags: - InvalidState: + INVALID_STATE: brief: An invalid state was requested. doc: | You tried to run a state before you are allowed to. Typically you tried to run encoder calibration or closed loop control before the motor was calibrated, or you tried to run closed loop control before the encoder was calibrated. - DcBusUnderVoltage: - brief: The DC voltage fell below the limit configured in `config.dc_bus_undervoltage_trip_level`. - doc: | - Confirm that your power leads are connected securely. For initial - testing a 12V PSU which can supply a couple of amps should be - sufficient while the use of low current ‘wall wart’ plug packs may - lead to inconsistent behaviour and is not recommended. - - You can monitor your PSU voltage using liveplotter in odrivetool - by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If - you see your votlage drop below `config.dc_bus_undervoltage_trip_level` - (default: ~ 8V) then you will trip this error. Even a relatively - small motor can draw multiple kW momentary and so unless you have - a very large PSU or are running of a battery you may encounter - this error when executing high speed movements with a high current - limit. To limit your PSU power draw you can limit your motor - current and/or velocity limit `controller.config.vel_limit` and - `motor.config.current_lim`. - DcBusOverVoltage: - brief: The DC voltage exceeded the limit configured in `config.dc_bus_overvoltage_trip_level`. + WATCHDOG_TIMER_EXPIRED: + bit: 11 + brief: The axis watchdog timer expired. doc: | - Confirm that you have a brake resistor of the correct value - connected securely and that `config.brake_resistance` is set to - the value of your brake resistor. - - You can monitor your PSU voltage using liveplotter in odrivetool - by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If - during a move you see the voltage rise above your PSU’s nominal - set voltage then you have your brake resistance set too low. This - may happen if you are using long wires or small gauge wires to - connect your brake resistor to your odrive which will added extra - resistance. This extra resistance needs to be accounted for to - prevent this voltage spike. If you have checked all your - connections you can also try increasing your brake resistance by - ~ 0.01 Ohm at a time to a maximum of 0.05 greater than your brake - resistor value. - CurrentMeasurementTimeout: - BrakeResistorDisarmed: - doc: The brake resistor was unexpectedly disarmed. - MotorDisarmed: - doc: The motor was unexpectedly disarmed. - MotorFailed: - doc: Check `motor.error` for more information. - SensorlessEstimatorFailed: - EncoderFailed: - doc: Check `encoder.error` for more information. - ControllerFailed: - PosCtrlDuringSensorless: - status: deprecated - WatchdogTimerExpired: - MinEndstopPressed: - MaxEndstopPressed: - EstopRequested: - HomingWithoutEndstop: + An amount of time greater than `axis.config.watchdog_timeout` passed + without the watchdog being fed. + MIN_ENDSTOP_PRESSED: + brief: The min endstop was pressed + MAX_ENDSTOP_PRESSED: + brief: The max endstop was pressed + ESTOP_REQUESTED: + brief: The estop message was sent over CAN + HOMING_WITHOUT_ENDSTOP: bit: 17 doc: the min endstop was not enabled during homing - OverTemp: - doc: Check `fet_thermistor.error` and `motor_thermistor.error` for more information. + OVER_TEMP: + # unused + doc: Check `motor.error` for more details. + UNKNOWN_POSITION: + doc: There isn't a valid position estimate available. step_dir_active: readonly bool + last_drv_fault: readonly uint32 + steps: readonly int64 current_state: readonly AxisState requested_state: AxisState - loop_counter: readonly uint32 - lockin_state: - typeargs: {fibre.Property.mode: readonly} - values: - Inactive: - Ramp: - Accelerate: - ConstVel: is_homed: {type: bool, c_name: homing_.is_homed} config: c_is_class: False @@ -316,23 +451,20 @@ interfaces: startup_closed_loop_control: type: bool doc: enable closed loop control after calibration/startup - startup_sensorless_control: - type: bool - doc: enable sensorless control after calibration/startup startup_homing: type: bool doc: enable homing after calibration/startup enable_step_dir: type: bool doc: Enable step/dir input after calibration. - For M0 this has no effect if `config.enable_uart` is true. + Make sure to set the corresponding GPIO's mode to `GPIO_MODE_DIGITAL`. step_dir_always_on: type: bool doc: Keep step/dir enabled while the motor is disabled. This is ignored if enable_step_dir is false. This setting only takes effect on a state transition into idle or out of closed loop control. - turns_per_step: float32 + enable_sensorless_mode: bool watchdog_timeout: type: float32 unit: s @@ -349,25 +481,35 @@ interfaces: vel: float32 sensorless_ramp: LockinConfig general_lockin: LockinConfig - can_node_id: - type: uint32 - doc: Both axes will have the same id to start - can_node_id_extended: bool - can_heartbeat_rate_ms: uint32 - fet_thermistor: OnboardThermistorCurrentLimiter - motor_thermistor: OffboardThermistorCurrentLimiter + can: CanConfig motor: Motor controller: Controller encoder: Encoder + acim_estimator: AcimEstimator sensorless_estimator: SensorlessEstimator trap_traj: TrapezoidalTrajectory min_endstop: Endstop max_endstop: Endstop + mechanical_brake: MechanicalBrake + task_times: + c_is_class: False + attributes: + thermistor_update: TaskTimer + encoder_update: TaskTimer + sensorless_estimator_update: TaskTimer + endstop_update: TaskTimer + can_heartbeat: TaskTimer + controller_update: TaskTimer + open_loop_controller_update: TaskTimer + acim_estimator_update: TaskTimer + motor_update: TaskTimer + current_controller_update: TaskTimer + dc_calib: TaskTimer + current_sense: TaskTimer + pwm_update: TaskTimer functions: watchdog_feed: doc: Feed the watchdog to prevent watchdog timeouts. - clear_errors: - doc: Check the watchdog timer for expiration. Also sets the watchdog error bit if expired. ODrive.Axis.LockinConfig: c_is_class: False @@ -394,14 +536,23 @@ interfaces: finish_on_distance: bool finish_on_enc_idx: bool + ODrive.Axis.CanConfig: + c_is_class: False + attributes: + node_id: uint32 + is_extended: bool + heartbeat_rate_ms: uint32 + encoder_rate_ms: uint32 + ODrive.ThermistorCurrentLimiter: c_is_class: False ODrive.OnboardThermistorCurrentLimiter: c_is_class: True attributes: - error: ThermistorCurrentLimiter.Error - temperature: readonly float32 + temperature: + type: readonly float32 + unit: °C config: c_is_class: False attributes: @@ -416,8 +567,9 @@ interfaces: ODrive.OffboardThermistorCurrentLimiter: c_is_class: True attributes: - error: ThermistorCurrentLimiter.Error - temperature: readonly float32 + temperature: + type: readonly float32 + unit: °C config: c_is_class: False attributes: @@ -437,10 +589,11 @@ interfaces: ODrive.Motor: c_is_class: True attributes: + last_error_time: float32 error: - nullflag: None + nullflag: NONE flags: - PhaseResistanceOutOfRange: + PHASE_RESISTANCE_OUT_OF_RANGE: brief: The measured motor phase resistance is outside of the plausible range. doc: | During calibration the motor resistance and @@ -471,12 +624,12 @@ interfaces: resistance_calib_max_voltage > calibration_current * phase_resistance resistance_calib_max_voltage < 0.5 * vbus_voltage ``` - PhaseInductanceOutOfRange: + PHASE_INDUCTANCE_OUT_OF_RANGE: brief: The measured motor phase inductance is outside of the plausible range. doc: | - See `PhaseResistanceOutOfRange` for details. - AdcFailed: - DrvFault: + See `PHASE_RESISTANCE_OUT_OF_RANGE` for details. + DRV_FAULT: + bit: 3 brief: The gate driver chip reported an error. doc: | The ODrive v3.4 is known to have a hardware issue whereby the @@ -492,10 +645,9 @@ interfaces: test motor and 50A on another test motor. Refer to [this post](https://discourse.odriverobotics.com/t/drv-fault-on-odrive-v3-4/558) for instructions for a hardware fix. - ControlDeadlineMissed: - NotImplementedMotorType: - BrakeCurrentOutOfRange: - ModulationMagnitude: + CONTROL_DEADLINE_MISSED: + MODULATION_MAGNITUDE: + bit: 7 doc: | The bus voltage was insufficent to push the requested current through the motor. @@ -506,99 +658,98 @@ interfaces: For gimbal motors, it is recommended to set the `config.calibration_current` and `config.current_lim` to half your bus voltage, or less. - BrakeDeadtimeViolation: - UnexpectedTimerCallback: - CurrentSenseSaturation: - brief: The phase current was outside the measurable range. + CURRENT_SENSE_SATURATION: + bit: 10 doc: | - There is a default 10% margin between the max allowed current and the measurable current range. - This is usually sufficent, but in some situations you may need some more margin. You can simply - decrease the `current_lim` a bit, or you can increase the `requested_current_range`. If you - still have issues after `requested_current_range` is 30% higher than `current_lim` then - you may have instability issues in the current controller. - CurrentLimitViolation: + The current sense circuit saturated the current sense amplifier. + This can be caused by setting `motor.config.current_lim` higher than + `motor.config.requested_current_range`. If this happens, increase the + requested current range, save the configuration, and reboot the controller. + CURRENT_LIMIT_VIOLATION: bit: 12 - brief: Motor current magnitude exceeded the limit and margin. doc: | - There is a default of 8A margin between the `current_lim` and this error. In some - situations the current controller may overshoot a bit more than this, so you can try to - turn this up a bit. If you still have issues after the margin is more than 30% of your - `current_lim` you may have stability issues in the current controller. - BrakeDutyCycleNan: - DcBusOverRegenCurrent: {doc: too much current pushed into the power supply} - DcBusOverCurrent: {doc: too much current pulled out of the power supply} - armed_state: - typeargs: {fibre.Property.mode: readonly} - values: - Disarmed: - WaitingForTimings: - WaitingForUpdate: - Armed: + The motor current exceeded `motor.config.current_lim + motor.config.current_lim_margin`. + The current controller is a PI controller, so it can experience overshoot. The PI gains + are automatically calculated based on `motor.config.current_control_bandwidth` and the + motor resistance and inductance (pole placement). Some overshoot is normal, so a sensible + solution is to increase the current limit margin if your current limit is large. + MODULATION_IS_NAN: {bit: 16} + MOTOR_THERMISTOR_OVER_TEMP: {doc: The motor thermistor measured a temperature above motor.motor_thermistor.config.temp_limit_upper} + FET_THERMISTOR_OVER_TEMP: {doc: The inverter thermistor measured a temperature above motor.fet_thermistor.config.temp_limit_upper} + TIMER_UPDATE_MISSED: {doc: A timer update event was missed. Perhaps the previous timer update took too much time. This is not expected in official release firmware.} + CURRENT_MEASUREMENT_UNAVAILABLE: {doc: The phase current measurement is not available. The ADC failed to sample the current sensor in time. This is not expected in official release firmware.} + CONTROLLER_FAILED: {doc: The motor was disarmed because the underlying controller failed. Usually this is the FOC controller.} + I_BUS_OUT_OF_RANGE: + doc: | + The DC current sourced/sunk by this motor exceeded the configured + hard limits. More specifically `i_bus` fell outside of the range + `config.i_bus_hard_min` ... `config.i_bus_hard_max`. + BRAKE_RESISTOR_DISARMED: + doc: | + An attempt was made to run the motor PWM while the brake resistor was enabled but disarmed. + The brake resistor can be disarmed for many reasons, but this usually happens if an error + is thrown that disables the motor. Check for other errors, then run `odrvX.clear_errors()` + to clear the errors and rearm the brake resistor. + SYSTEM_LEVEL: + doc: | + The motor had to be disarmed because of a system level error. + See `ODrive.Error` for more details. + BAD_TIMING: {doc: The main control loop got out of sync with the motor control loop. This could indicate that the main control loop got stuck.} + UNKNOWN_PHASE_ESTIMATE: {doc: The current controller did not get a valid angle input. Maybe you didn't calibrate the encoder.} + UNKNOWN_PHASE_VEL: {doc: The motor controller did not get a valid phase velocity input.} + UNKNOWN_TORQUE: {doc: The motor controller did not get a valid torque input.} + UNKNOWN_CURRENT_COMMAND: {doc: The current controller did not get a valid current setpoint. Maybe you didn't configure the controller correctly.} + UNKNOWN_CURRENT_MEASUREMENT: {doc: The current controller did not get a valid current measurement.} + UNKNOWN_VBUS_VOLTAGE: {doc: The current controller did not get a valid `vbus_voltage` measurement.} + UNKNOWN_VOLTAGE_COMMAND: {doc: The current controller did not get a valid feedforward voltage setpoint.} + UNKNOWN_GAINS: {doc: The current controller gains were not configured. Run motor calibration or set `config.phase_resistance` and `config.phase_inductance` manually.} + CONTROLLER_INITIALIZING: {doc: Internal value used while the controller is not yet ready to generate PWM timings.} + UNBALANCED_PHASES: {doc: The motor phases are not balanced.} + is_armed: readonly bool is_calibrated: readonly bool - current_meas_phB: {type: readonly float32, c_name: current_meas_.phB} - current_meas_phC: {type: readonly float32, c_name: current_meas_.phC} + current_meas_phA: {type: readonly float32, c_getter: 'current_meas_.value_or(Iph_ABC_t{0.0f, 0.0f, 0.0f}).phA'} + current_meas_phB: {type: readonly float32, c_getter: 'current_meas_.value_or(Iph_ABC_t{0.0f, 0.0f, 0.0f}).phB'} + current_meas_phC: {type: readonly float32, c_getter: 'current_meas_.value_or(Iph_ABC_t{0.0f, 0.0f, 0.0f}).phC'} + DC_calib_phA: {type: float32, c_name: DC_calib_.phA} DC_calib_phB: {type: float32, c_name: DC_calib_.phB} DC_calib_phC: {type: float32, c_name: DC_calib_.phC} + I_bus: {type: readonly float32, unit: A} phase_current_rev_gain: float32 effective_current_lim: readonly float32 + max_allowed_current: + type: readonly float32 + unit: A + doc: | + Indicates the maximum current that can be measured by the current + sensors in the current hardware configuration. This value depends on + `config.requested_current_range`. + max_dc_calib: {type: readonly float32, unit: A} + fet_thermistor: OnboardThermistorCurrentLimiter + motor_thermistor: OffboardThermistorCurrentLimiter current_control: - c_is_class: False + c_is_class: True attributes: - p_gain: float32 - i_gain: float32 + p_gain: {type: readonly float32, c_getter: 'pi_gains_.value_or(float2D{0.0f, 0.0f}).first'} + i_gain: {type: readonly float32, c_getter: 'pi_gains_.value_or(float2D{0.0f, 0.0f}).second'} + I_measured_report_filter_k: float32 + Id_setpoint: {type: readonly float32, c_getter: 'Idq_setpoint_.value_or(float2D{0.0f, 0.0f}).first'} + Iq_setpoint: {type: readonly float32, c_getter: 'Idq_setpoint_.value_or(float2D{0.0f, 0.0f}).second'} + Vd_setpoint: {type: readonly float32, c_getter: 'Vdq_setpoint_.value_or(float2D{0.0f, 0.0f}).first'} + Vq_setpoint: {type: readonly float32, c_getter: 'Vdq_setpoint_.value_or(float2D{0.0f, 0.0f}).second'} + phase: {type: readonly float32, c_getter: 'phase_.value_or(0.0f)'} + phase_vel: {type: readonly float32, c_getter: 'phase_vel_.value_or(0.0f)'} + Ialpha_measured: {type: readonly float32, c_getter: 'Ialpha_beta_measured_.value_or(float2D{0.0f, 0.0f}).first'} + Ibeta_measured: {type: readonly float32, c_getter: 'Ialpha_beta_measured_.value_or(float2D{0.0f, 0.0f}).second'} + Id_measured: readonly float32 + Iq_measured: readonly float32 + power: readonly float32 v_current_control_integral_d: float32 v_current_control_integral_q: float32 - Ibus: float32 - final_v_alpha: float32 - final_v_beta: float32 - Id_setpoint: float32 - Iq_setpoint: readonly float32 - Iq_measured: float32 - Id_measured: float32 - I_measured_report_filter_k: float32 - max_allowed_current: readonly float32 - overcurrent_trip_level: readonly float32 - acim_rotor_flux: float32 - async_phase_vel: readonly float32 - async_phase_offset: float32 - gate_driver: - c_name: gate_driver_exported_ - c_is_class: False - attributes: - drv_fault: - typeargs: {fibre.Property.mode: readonly} - nullflag: NoFault - flags: - FetLowCOvercurrent: {bit: 0, doc: FET Low side, Phase C Over Current fault} - FetHighCOvercurrent: {bit: 1, doc: FET High side, Phase C Over Current fault} - FetLowBOvercurrent: {bit: 2, doc: FET Low side, Phase B Over Current fault} - FetHighBOvercurrent: {bit: 3, doc: FET High side, Phase B Over Current fault} - FetLowAOvercurrent: {bit: 4, doc: FET Low side, Phase A Over Current fault} - FetHighAOvercurrent: {bit: 5, doc: FET High side, Phase A Over Current fault} - OvertemperatureWarning: {bit: 6, doc: Over Temperature Warning fault} - OvertemperatureShutdown: {bit: 7, doc: Over Temperature Shut Down fault} - PVddUndervoltage: {bit: 8, doc: Power supply Vdd Under Voltage fault} - GVddUndervoltage: {bit: 9, doc: DRV8301 Vdd Under Voltage fault} - GVddOvervoltage: {bit: 10, doc: DRV8301 Vdd Over Voltage fault} - # status_reg_1: readonly uint32 - # status_reg_2: readonly uint32 - # ctrl_reg_1: readonly uint32 - # ctrl_reg_2: readonly uint32 - timing_log: - c_is_class: False - attributes: - general: {type: readonly uint16, c_name: 'get(TIMING_LOG_GENERAL)'} - adc_cb_i: {type: readonly uint16, c_name: 'get(TIMING_LOG_ADC_CB_I)'} - adc_cb_dc: {type: readonly uint16, c_name: 'get(TIMING_LOG_ADC_CB_DC)'} - meas_r: {type: readonly uint16, c_name: 'get(TIMING_LOG_MEAS_R)'} - meas_l: {type: readonly uint16, c_name: 'get(TIMING_LOG_MEAS_L)'} - enc_calib: {type: readonly uint16, c_name: 'get(TIMING_LOG_ENC_CALIB)'} - idx_search: {type: readonly uint16, c_name: 'get(TIMING_LOG_IDX_SEARCH)'} - foc_voltage: {type: readonly uint16, c_name: 'get(TIMING_LOG_FOC_VOLTAGE)'} - foc_current: {type: readonly uint16, c_name: 'get(TIMING_LOG_FOC_CURRENT)'} - spi_start: {type: readonly uint16, c_name: 'get(TIMING_LOG_SPI_START)'} - sample_now: {type: readonly uint16, c_name: 'get(TIMING_LOG_SAMPLE_NOW)'} - spi_end: {type: readonly uint16, c_name: 'get(TIMING_LOG_SPI_END)'} + final_v_alpha: readonly float32 + final_v_beta: readonly float32 + n_evt_current_measurement: {type: readonly uint32, doc: Number of current measurement events since startup (modulo 2^32)} + n_evt_pwm_update: {type: readonly uint32, doc: Number of PWM update events since startup (modulo 2^32)} + config: c_is_class: False attributes: @@ -609,7 +760,6 @@ interfaces: phase_inductance: {type: float32, c_setter: set_phase_inductance} phase_resistance: {type: float32, c_setter: set_phase_resistance} torque_constant: float32 - direction: int32 motor_type: MotorType current_lim: float32 current_lim_margin: float32 @@ -618,37 +768,116 @@ interfaces: inverter_temp_limit_upper: float32 requested_current_range: float32 current_control_bandwidth: {type: float32, c_setter: set_current_control_bandwidth} - acim_slip_velocity: float32 acim_gain_min_flux: float32 acim_autoflux_min_Id: float32 acim_autoflux_enable: bool acim_autoflux_attack_gain: float32 acim_autoflux_decay_gain: float32 + R_wL_FF_enable: bool + bEMF_FF_enable: bool + I_bus_hard_min: + type: float32 + unit: A + doc: | + If the controller fails to keep this motor's DC current (`I_bus`) + above this value the motor gets disarmed immediately. Most likely + you want a negative value here. Set to -inf to disable. Take noise + into account when chosing a value. + I_bus_hard_max: + type: float32 + unit: A + doc: | + If the controller fails to keep this motor's DC current (`I_bus`) + below this value the motor gets disarmed immediately. Usually this + is set in conjunction with `I_bus_hard_min`. Set to inf to disable. + Take noise into account when chosing a value. + I_leak_max: + type: float32 + unit: A + doc: | + In almost all scenarios, the currents on phase A, B and C should + add up to zero. A small amount of measurement noise is expected. + However if the sum of A, B, C currents exceeds this configuration + value, the motor gets disarmed immediately. + Note that this feature is only works on devices with three current + sensors (e.g. ODrive v4). + dc_calib_tau: float32 + + ODrive.Oscilloscope: + c_is_class: True + attributes: + size: readonly uint32 + functions: + get_val: {in: {index: uint32}, out: {val: float32}} + + ODrive.AcimEstimator: + c_is_class: True + attributes: + rotor_flux: {type: readonly float32, unit: A, doc: estimated magnitude of the rotor flux} + slip_vel: + type: readonly float32 + unit: rad/s + doc: estimated slip between physical and electrical angular velocity} + c_getter: slip_vel_.any().value_or(0.0f) + phase_offset: + type: readonly float32 + unit: rad + doc: estimate offset between physical and electrical angular position} + stator_phase_vel: + type: readonly float32 + unit: rad/s + doc: calculated setpoint for the electrical velocity} + c_getter: stator_phase_vel_.any().value_or(0.0f) + stator_phase: + type: readonly float32 + unit: rad + doc: calculated setpoint for the electrical phase} + c_getter: stator_phase_.any().value_or(0.0f) + config: + c_is_class: False + attributes: + slip_velocity: float32 ODrive.Controller: c_is_class: True attributes: error: - nullflag: None + nullflag: NONE flags: - Overspeed: + OVERSPEED: + brief: Motor speed exceeded `config.vel_limit * config.vel_limit_tolerance` and `config.enable_overspeed_error` was enabled. doc: | Try increasing `config.vel_limit`. The default of 2 turns per second gives a motor speed of only 120 RPM. Note: Even if - you do not commanded your motor to exceed `config.vel_limit` + you do not command your motor to exceed `config.vel_limit`, sudden changes in the load placed on a motor may cause this speed to be temporarily exceeded, resulting in this error. You can also try increasing `config.vel_limit_tolerance`. The default value of 1.2 means it will only allow a 20% violation of - the speed limit. You can set the `config.vel_limit_tolerance` to 0 - to disable the check altogether. - InvalidInputMode: - UnstableGain: - InvalidMirrorAxis: - InvalidLoadEncoder: - InvalidEstimate: + the speed limit. You can set `config.enable_overspeed_error` to False + to disable this error. + INVALID_INPUT_MODE: + brief: The `controller.config.input_mode` setting was set to an invalid value. See InputMode for available values + doc: | + Input modes and control modes are separate concepts. A control mode sets the type of control to be used, + like position, velocity, or torque control. Input modes modify the input given (`input_pos`, etc) to + give desired behavior. For example, in position *control mode*, the position filter *input mode* will + smooth out `input_pos` commands to give smoother motion. + UNSTABLE_GAIN: + INVALID_MIRROR_AXIS: + INVALID_LOAD_ENCODER: + INVALID_ESTIMATE: + INVALID_CIRCULAR_RANGE: + SPINOUT_DETECTED: + doc: | + The motor mechanical power and electrical power do not agree. This is usually + caused by a slipping encoder or incorrect encoder offset calibration. + Check that your encoder is not slipping on the motor. If using an Index pin, check + that you are not getting false index pulses caused by noise. This can happen if you + are using unshielded cable for the encoder signals. + last_error_time: float32 input_pos: type: float32 unit: turn @@ -705,6 +934,10 @@ interfaces: circular_setpoint_range: type: float32 doc: circular range in [turns] for position setpoints when circular_setpoints is True + steps_per_circular_range: + type: int32 + doc: Number of steps within the circular setpoint range. Set this and the circular setpoint range to powers of 2 for the best results. + c_setter: set_steps_per_circular_range homing_speed: type: float32 unit: turns/s @@ -713,6 +946,7 @@ interfaces: unit: Nm/(turn/s^2) axis_to_mirror: uint8 mirror_ratio: float32 + torque_mirror_ratio: float32 load_encoder_axis: type: uint8 # TODO: this is meaningless for a user. Should there be a separate developer note? @@ -731,6 +965,40 @@ interfaces: calib_vel_threshold: float32 cogging_ratio: readonly float32 anticogging_enabled: bool + mechanical_power_bandwidth: + type: float32 + doc: "Bandwidth for mechanical power estimate. Used for spinout detection" + unit: rad/s + electrical_power_bandwidth: + type: float32 + doc: "Bandwidth for electrical power estimate. Used for spinout detection. Dot product of Vdq and Idq" + unit: rad/s + spinout_mechanical_power_threshold: + type: float32 + doc: "Mechanical power threshold for spinout detection. This should be a negative value" + unit: Watt + spinout_electrical_power_threshold: + type: float32 + doc: "Electrical power threshold for spinout detection. This should be a positive value" + unit: Watt + autotuning: + c_is_class: False + attributes: + frequency: float32 + pos_amplitude: float32 + pos_phase: float32 + vel_amplitude: float32 + vel_phase: float32 + torque_amplitude: float32 + torque_phase: float32 + mechanical_power: + type: readonly float32 + unit: Watt + doc: "Mechanical power estimate. Torque * velocity" + electrical_power: + type: readonly float32 + unit: Watt + doc: "Electrical power estimate. Vdq dot Idq" functions: move_incremental: doc: Moves the axes' goal point by a specified increment. @@ -748,10 +1016,10 @@ interfaces: c_is_class: True attributes: error: - nullflag: None + nullflag: NONE flags: - UnstableGain: - CprPolepairsMismatch: + UNSTABLE_GAIN: + CPR_POLEPAIRS_MISMATCH: doc: | Confirm you have entered the correct count per rotation (CPR) for [your encoder](https://docs.odriverobotics.com/encoders). The @@ -761,37 +1029,48 @@ interfaces: If you are still having issues, you can try to increase `encoder.config.calib_scan_distance` up to a factor of 4 above the default. + If your encoder cpr and motor pole pair settings are correct, + this error can be caused because motor cogging makes the motor + move less or more than commanded. You can fix this by increasing + `encoder.config.calib_scan_distance`. + Note that the AMT encoders are configurable using the micro- switches on the encoder PCB and so you may need to check that these are in the right positions. If your encoder lists its pulse per rotation (PPR) multiply that number by four to get CPR. - NoResponse: + NO_RESPONSE: doc: | Confirm that your encoder is plugged into the right pins on the ODrive board. - UnsupportedEncoderMode: - IllegalHallState: - IndexNotFoundYet: + UNSUPPORTED_ENCODER_MODE: + ILLEGAL_HALL_STATE: + doc: | + Hall effect encoder only have 6 valid states out of 8 (2^3) possible states. + An invalid state can be caused by noise or a hardware fault. If you get this + error and you are sure that your electrical connections are correct, add + 22nF capacitors between the encoder A,B,Z pins and ground to filter out noise. + INDEX_NOT_FOUND_YET: doc: | Check that your encoder is a model that has an index pulse. If your encoder does not have a wire connected to pin Z on your ODrive then it does not output an index pulse. - AbsSpiTimeout: - AbsSpiComFail: - AbsSpiNotReady: + ABS_SPI_TIMEOUT: + ABS_SPI_COM_FAIL: + ABS_SPI_NOT_READY: + HALL_NOT_CALIBRATED_YET: is_ready: readonly bool index_found: readonly bool shadow_count: readonly int32 count_in_cpr: readonly int32 interpolation: readonly float32 - phase: readonly float32 - pos_estimate: readonly float32 + phase: {type: readonly float32, c_getter: phase_.any().value_or(0.0f)} + pos_estimate: {type: readonly float32, c_getter: pos_estimate_.any().value_or(0.0f)} pos_estimate_counts: readonly float32 - pos_cpr: readonly float32 pos_cpr_counts: readonly float32 - pos_circular: readonly float32 + delta_pos_cpr_counts: readonly float32 + pos_circular: {type: readonly float32, c_getter: pos_circular_.any().value_or(0.0f)} hall_state: readonly uint8 - vel_estimate: readonly float32 + vel_estimate: {type: readonly float32, c_getter: vel_estimate_.any().value_or(0.0f)} vel_estimate_counts: readonly float32 calib_scan_response: readonly float32 pos_abs: int32 @@ -801,22 +1080,29 @@ interfaces: attributes: mode: Mode use_index: {type: bool, c_setter: set_use_index} + index_offset: float32 + use_index_offset: bool find_idx_on_lockin_only: {type: bool, c_setter: set_find_idx_on_lockin_only} - abs_spi_cs_gpio_pin: {type: uint16, c_setter: set_abs_spi_cs_gpio_pin} - zero_count_on_find_idx: bool + abs_spi_cs_gpio_pin: {type: uint16, c_setter: set_abs_spi_cs_gpio_pin, doc: Make sure that the GPIO is in `GPIO_MODE_DIGITAL`.} cpr: int32 - offset: int32 + phase_offset: int32 + phase_offset_float: float32 + direction: int32 pre_calibrated: {type: bool, c_setter: set_pre_calibrated} - offset_float: float32 enable_phase_interpolation: bool bandwidth: {type: float32, c_setter: set_bandwidth} calib_range: float32 calib_scan_distance: float32 calib_scan_omega: float32 - idx_search_unidirectional: bool ignore_illegal_hall_state: bool - sincos_gpio_pin_sin: uint16 - sincos_gpio_pin_cos: uint16 + hall_polarity: uint8 + hall_polarity_calibrated: bool + sincos_gpio_pin_sin: + type: uint16 + doc: Analog sine signal of a sin/cos encoder. The corresponding GPIO must be in `GPIO_MODE_ANALOG_IN`. + sincos_gpio_pin_cos: + type: uint16 + doc: Analog cosine signal of a sin/cos encoder. The corresponding GPIO must be in `GPIO_MODE_ANALOG_IN`. functions: set_linear_count: {in: {count: int32}} @@ -825,12 +1111,14 @@ interfaces: c_is_class: True attributes: error: - nullflag: None + nullflag: NONE flags: - UnstableGain: - phase: float32 - pll_pos: float32 - vel_estimate: float32 + UNSTABLE_GAIN: + UNKNOWN_CURRENT_MEASUREMENT: + phase: {type: readonly float32, unit: rad, c_getter: phase_.any().value_or(0.0f)} + pll_pos: {type: readonly float32, unit: rad} + phase_vel: {type: readonly float32, unit: rad/s, c_getter: phase_vel_.any().value_or(0.0f)} + vel_estimate: {type: readonly float32, unit: turns/s, c_getter: vel_estimate_.any().value_or(0.0f)} # pll_kp: float32 # pll_ki: float32 config: @@ -851,7 +1139,6 @@ interfaces: accel_limit: float32 decel_limit: float32 - ODrive.Endstop: c_is_class: True attributes: @@ -859,31 +1146,132 @@ interfaces: config: c_is_class: False attributes: - gpio_num: {type: uint16, c_setter: set_gpio_num} + gpio_num: {type: uint16, c_setter: set_gpio_num, doc: Make sure the corresponding GPIO is in `GPIO_MODE_DIGITAL`.} enabled: {type: bool, c_setter: set_enabled} offset: float32 is_active_high: bool - pullup: bool debounce_ms: {type: uint32, c_setter: set_debounce_ms} + ODrive.MechanicalBrake: + c_is_class: True + attributes: + config: + c_is_class: False + attributes: + gpio_num: {type: uint16, c_setter: set_gpio_num} + is_active_low: bool + functions: + engage: + doc: | + This function engages the mechanical brake if one is present and enabled. + release: + doc: | + This function releases the mecahncal brake if one is present and enabled. + + ODrive.TaskTimer: + c_is_class: True + attributes: + start_time: readonly uint32 + end_time: readonly uint32 + length: readonly uint32 + max_length: uint32 + + ODrive3: + c_is_class: True + implements: ODrive + attributes: + config: + c_is_class: False + implements: ODrive.Config + attributes: + # TODO: add support for arrays + gpio1_mode: {type: ODrive.GpioMode, doc: Mode of GPIO1 (changes take effect after reboot), c_name: 'gpio_modes[1]'} + gpio2_mode: {type: ODrive.GpioMode, doc: Mode of GPIO2 (changes take effect after reboot), c_name: 'gpio_modes[2]'} + gpio3_mode: {type: ODrive.GpioMode, doc: Mode of GPIO3 (changes take effect after reboot), c_name: 'gpio_modes[3]'} + gpio4_mode: {type: ODrive.GpioMode, doc: Mode of GPIO4 (changes take effect after reboot), c_name: 'gpio_modes[4]'} + gpio5_mode: {type: ODrive.GpioMode, doc: Mode of GPIO5 (changes take effect after reboot), c_name: 'gpio_modes[5]'} + gpio6_mode: {type: ODrive.GpioMode, doc: Mode of GPIO6 (changes take effect after reboot), c_name: 'gpio_modes[6]'} + gpio7_mode: {type: ODrive.GpioMode, doc: Mode of GPIO7 (changes take effect after reboot), c_name: 'gpio_modes[7]'} + gpio8_mode: {type: ODrive.GpioMode, doc: Mode of GPIO8 (changes take effect after reboot), c_name: 'gpio_modes[8]'} + gpio9_mode: {type: ODrive.GpioMode, doc: Mode of GPIO9 (changes take effect after reboot), c_name: 'gpio_modes[9]'} + gpio10_mode: {type: ODrive.GpioMode, doc: Mode of GPIO10 (changes take effect after reboot), c_name: 'gpio_modes[10]'} + gpio11_mode: {type: ODrive.GpioMode, doc: Mode of GPIO11 (changes take effect after reboot), c_name: 'gpio_modes[11]'} + gpio12_mode: {type: ODrive.GpioMode, doc: Mode of GPIO12 (changes take effect after reboot), c_name: 'gpio_modes[12]'} + gpio13_mode: {type: ODrive.GpioMode, doc: Mode of GPIO13 (changes take effect after reboot), c_name: 'gpio_modes[13]'} + gpio14_mode: {type: ODrive.GpioMode, doc: Mode of GPIO14 (changes take effect after reboot), c_name: 'gpio_modes[14]'} + gpio15_mode: {type: ODrive.GpioMode, doc: Mode of GPIO15 (changes take effect after reboot), c_name: 'gpio_modes[15]'} + gpio16_mode: {type: ODrive.GpioMode, doc: Mode of GPIO16 (changes take effect after reboot), c_name: 'gpio_modes[16]'} + + gpio1_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[0]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio2_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[1]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio3_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[2]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio4_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + axis0: {type: ODrive.Axis, c_name: get_axis(0)} + axis1: {type: ODrive.Axis, c_name: get_axis(1)} valuetypes: + ODrive.GpioMode: + values: + DIGITAL: + doc: | + The pin can be used for one or more of these functions: + Step, dir, enable, encoder index, hall effect encoder, SPI encoder nCS (this one is exclusive). + DIGITAL_PULL_UP: + doc: Same as `DIGITAL` but with the internal pull-up resistor enabled. + DIGITAL_PULL_DOWN: + doc: Same as `DIGITAL` but with the internal pull-down resistor enabled. + ANALOG_IN: + doc: | + The pin can be used for one or more of these functions: + Sin/cos encoders, analog input, `get_adc_voltage`. + UART_A: {doc: See `config.enable_uart_a`.} + UART_B: {doc: This mode is not supported on ODrive v3.x.} + UART_C: {doc: This mode is not supported on ODrive v3.x.} + CAN_A: {doc: See `config.enable_can_a`.} + I2C_A: {doc: See `config.enable_i2c_a`.} + SPI_A: {doc: Note that the SPI pins on ODrive v3.x are hardwired so they + cannot be configured through software. Consequently, even though SPI_A + is exposed, this mode is of no use on ODrive v3.x.} + PWM: {doc: See `config.gpio0_pwm_mapping`.} + ENC0: {doc: The pin is used by quadrature encoder 0.} + ENC1: {doc: The pin is used by quadrature encoder 1.} + ENC2: {doc: This mode is not supported on ODrive v3.x.} + MECH_BRAKE: {doc: This is to support external mechanical brakes.} + STATUS: {doc: The pin is used for status output (see `config.error_gpio_pin`)} + + ODrive.StreamProtocolType: + values: + Fibre: + doc: | + Machine-to-machine protocol which gives access to all features of the + ODrive. This protocol is used by the official odrivetool and GUI. + Developers who wish to interact with this protocol are advised to do + so through libfibre. + Ascii: + doc: | + Human readable protocol designed for easy implementation for cases + where the use of libfibre is not desired or feasible. + Refer to [this page](ascii-protocol.md) for details. + Stdout: {doc: Output of printf(). Only intended for developers who modify + ODrive firmware.} + AsciiAndStdout: {doc: Combination of `Ascii` and `Stdout`.} + ODrive.Can.Protocol: - values: {Simple: } + flags: {SIMPLE: } ODrive.Axis.AxisState: # TODO: remove redundant "Axis" in name values: - Undefined: + UNDEFINED: doc: will fall through to idle - Idle: + IDLE: brief: Disable motor PWM and do nothing. - StartupSequence: + STARTUP_SEQUENCE: brief: Run the startup procedure. doc: the actual sequence is defined by the `config`.startup... flags - FullCalibrationSequence: + FULL_CALIBRATION_SEQUENCE: doc: Run motor calibration and then encoder offset calibration (or encoder index search if `.encoder.config.use_index` is `True`). - MotorCalibration: + MOTOR_CALIBRATION: brief: Measure phase resistance and phase inductance of the motor. doc: | * To store the results set `motor.config.pre_calibrated` to `True` @@ -891,79 +1279,81 @@ valuetypes: don't have to run the motor calibration on the next start up. * This modifies the variables `motor.config.phase_resistance` and `motor.config.phase_inductance`. - SensorlessControl: - brief: Run sensorless control. - doc: | - * The motor must be calibrated (`motor.is_calibrated`) - * `controller.config.control_mode` must be `True`. - EncoderIndexSearch: + ENCODER_INDEX_SEARCH: brief: Turn the motor in one direction until the encoder index is traversed. doc: This state can only be entered if `encoder.config.use_index` is `True`. - EncoderOffsetCalibration: + value: 6 + ENCODER_OFFSET_CALIBRATION: brief: Turn the motor in one direction for a few seconds and then back to measure the offset between the encoder position and the electrical phase. doc: | * Can only be entered if the motor is calibrated (`motor.is_calibrated`). * A successful encoder calibration will make the `encoder.is_ready` go to true. - ClosedLoopControl: + CLOSED_LOOP_CONTROL: brief: Run closed loop control. doc: | * The action depends on the `controller.config.control_mode`. * Can only be entered if the motor is calibrated (`motor.is_calibrated`) and the encoder is ready (`encoder.is_ready`). - LockinSpin: + LOCKIN_SPIN: brief: Run lockin spin. doc: | Can only be entered if the motor is calibrated (`motor.is_calibrated`) or the motor direction is unspecified (`motor.config.direction` == 1) - EncoderDirFind: + ENCODER_DIR_FIND: brief: Run encoder direction search. doc: | Can only be entered if the motor is calibrated (`motor.is_calibrated`). - Homing: + HOMING: brief: Run axis homing function. doc: Endstops must be enabled to use this feature. - - ODrive.ThermistorCurrentLimiter.Error: - nullflag: None - flags: - OverTemp: - doc: The thermistor temperature upper limit was exceeded. + ENCODER_HALL_POLARITY_CALIBRATION: + brief: Rotate the motor in lockin and calibrate hall polarity + doc: + ODrive assumes 120 degree electrical hall spacing. This routine determines if that + is the case and sets the polarity if the halls are on 60 degree electrical spacing + ENCODER_HALL_PHASE_CALIBRATION: + brief: Rotate the motor for 30s to calibrate hall sensor edge offsets + doc: + The phase offset is not calibrated at this time, so the map is only relative ODrive.Encoder.Mode: values: - Incremental: - Hall: - Sincos: - SpiAbsCui: + INCREMENTAL: + HALL: + SINCOS: + SPI_ABS_CUI: value: 0x100 doc: compatible with CUI AMT23xx - SpiAbsAms: + SPI_ABS_AMS: value: 0x101 doc: compatible with AMS AS5047P, AS5048A/AS5048B (no daisy chain support) - SpiAbsAeat: + SPI_ABS_AEAT: value: 0x102 doc: not yet implemented - SpiAbsRls: + SPI_ABS_RLS: value: 0x103 doc: RLS Encoders + SPI_ABS_MA732: + value: 0x104 + doc: MagAlpha MA732 magnetic encoder ODrive.Controller.ControlMode: values: # Note: these should be sorted from lowest level of control to # highest level of control, to allow "<" style comparisons. - VoltageControl: + VOLTAGE_CONTROL: doc: this one is not normally used - TorqueControl: - VelocityControl: - PositionControl: + TORQUE_CONTROL: + VELOCITY_CONTROL: + POSITION_CONTROL: ODrive.Controller.InputMode: values: - Inactive: + INACTIVE: brief: Disable inputs. Setpoints retain their last value. - Passthrough: + PASSTHROUGH: brief: Pass `input_xxx` through to `xxx_setpoint` directly. doc: | ### Valid Inputs: @@ -976,7 +1366,7 @@ valuetypes: * `CONTROL_MODE_TORQUE_CONTROL` * `CONTROL_MODE_VELOCITY_CONTROL` * `CONTROL_MODE_POSITION_CONTROL` - VelRamp: + VEL_RAMP: brief: Ramps a velocity command from the current value to the target value. doc: | ### Configuration Values: @@ -988,7 +1378,7 @@ valuetypes: ### Valid Control Modes: * `CONTROL_MODE_VELOCITY_CONTROL` - PosFilter: + POS_FILTER: brief: Implements a 2nd order position tracking filter. doc: | Intended for use with step/dir interface, but can also be used with @@ -1006,9 +1396,9 @@ valuetypes: ### Valid Control modes: * `CONTROL_MODE_POSITION_CONTROL` - MixChannels: + MIX_CHANNELS: brief: Not Implemented. - TrapTraj: + TRAP_TRAJ: brief: Implementes an online trapezoidal trajectory planner. doc: | ![Trapezoidal Planner Response](../TrapTrajPosVel.PNG) @@ -1024,7 +1414,7 @@ valuetypes: ### Valid Control Modes: * `CONTROL_MODE_POSITION_CONTROL` - TorqueRamp: + TORQUE_RAMP: brief: Ramp a torque command from the current value to the target value. doc: | ### Configuration Values: @@ -1035,7 +1425,7 @@ valuetypes: ### Valid Control Modes: * `CONTROL_MODE_TORQUE_CONTROL` - Mirror: + MIRROR: brief: Implements "electronic mirroring". doc: | This is like electronic camming, but you can only mirror exactly the @@ -1052,10 +1442,16 @@ valuetypes: ### Valid Control modes * `CONTROL_MODE_POSITION_CONTROL` + Tuning: + brief: Implements a tuning mode + doc: | + Used for tuning your odrive, this mode allows the user to set different frequencies. + Set control_mode for the loop you want to tune, then set the frequency desired. + The ODrive will send a 1 turn amplitude sine wave to the controller with the given frequency and phase. ODrive.Motor.MotorType: values: - HighCurrent: + HIGH_CURRENT: #LowCurrent: # not implemented - Gimbal: {value: 2} - Acim: + GIMBAL: {value: 2} + ACIM: diff --git a/Firmware/openocd.gdbinit b/Firmware/openocd.gdbinit deleted file mode 100644 index 08166929e..000000000 --- a/Firmware/openocd.gdbinit +++ /dev/null @@ -1,2 +0,0 @@ -target remote | openocd -f "interface/stlink-v2.cfg" -f "target/stm32f4x_stlink.cfg" -c "gdb_port pipe; log_output openocd.log" -monitor reset halt diff --git a/Firmware/sampler.py b/Firmware/sampler.py index 75ebbe55e..a2da9ecf5 100644 --- a/Firmware/sampler.py +++ b/Firmware/sampler.py @@ -3,7 +3,7 @@ # run openocd (0.9.0) with : # $ openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg &> /dev/null & # then run -# $ python2 sampler.py path_to_myelf_with_symbols +# $ python sampler.py path_to_myelf_with_symbols # ctrl-c to stop sampling. # To terminate the openocd session, enter command "fg" then do ctrl-c. @@ -45,17 +45,22 @@ def getpc(self): return 0 - def initSymbols(self, elf, readelf='arm-none-eabi-readelf'): - proc = subprocess.Popen([readelf, '-s', elf], stdout=subprocess.PIPE) + def initSymbols(self, elf, symbol_dump_cmd='arm-none-eabi-nm'): + proc = subprocess.Popen([symbol_dump_cmd, '-CS', '--size-sort', elf], stdout=subprocess.PIPE) for line in proc.stdout.readlines(): field = line.split() - # for i,txt in enumerate(field): - # print("{}, {}".format(i, txt)) + try: - if field[3] == b'FUNC': - addr = int(field[1], 16) - 1 # For some reason readelf dumps the func addr off by 1 - func = field[7] - size = int(field[2]) + # For using nm -CS + if field[2] in (b't', b'T', b'w', b'W'): + addr = int(field[0], 16) + func = b' '.join(field[3:]) + size = int(field[1], 16) + # # For using readelf -s + # if field[3] == b'FUNC': + # addr = int(field[1], 16) - 1 # For some reason readelf dumps the func addr off by 1 + # func = field[7] + # size = int(field[2]) if addr not in self.indexes: self.table.append((addr, func, size)) self.indexes.add(addr) @@ -88,6 +93,7 @@ def func(self, pc): total = 0 countmap = { } pcmap = { } + funcmap = { } start = time.time() try: @@ -101,6 +107,9 @@ def func(self, pc): func, addr = sampler.func(pc) + if(func == 'ADC_IRQ_Dispatch'): + funcmap[pc] = 1 + if not addr: continue @@ -112,14 +121,22 @@ def func(self, pc): total += 1 cur = time.time() - if cur - start > 1.0: + if cur - start > 5.0: + + # tmp = sorted(funcmap) + # for k in tmp: + # print(hex(k)) + tmp = sorted(countmap.items(), key=operator.itemgetter(1)) #, reverse=True) for k, v in tmp: - print('{:05.2f}% {}'.format((v * 100.) / total, k)) + print('{:05.2f}% {}'.format((v * 100.) / total, k.decode('UTF-8'))) # print('{:06.2f} clocks : {}'.format((v * 10500) / total, k)) start = cur print('{} Samples'.format(total)) print('') + # total = 0 + # countmap = { } + # pcmap = { } except KeyboardInterrupt: pcmap = sorted(pcmap.items(), key=operator.itemgetter(1), reverse=True) diff --git a/Firmware/Board/v3/Src/syscalls.c b/Firmware/syscalls.c similarity index 92% rename from Firmware/Board/v3/Src/syscalls.c rename to Firmware/syscalls.c index eff893dc6..1c21082c2 100644 --- a/Firmware/Board/v3/Src/syscalls.c +++ b/Firmware/syscalls.c @@ -5,11 +5,8 @@ ****************************************************************************** */ -#include -#include #include -#include -#include +#include //int _read(int file, char *data, int len) {} @@ -41,8 +38,8 @@ void* heap_end_ptr = 0; */ intptr_t _sbrk(size_t size) { intptr_t ptr; - vTaskSuspendAll(); { + uint32_t mask = cpu_enter_critical(); if (!heap_end_ptr) heap_end_ptr = _end_ptr; if (heap_end_ptr + size > _heap_end_max_ptr) { @@ -51,8 +48,8 @@ intptr_t _sbrk(size_t size) { ptr = (intptr_t)heap_end_ptr; heap_end_ptr += size; } + cpu_exit_critical(mask); } - (void)xTaskResumeAll(); return ptr; } diff --git a/Firmware/tup.config.default b/Firmware/tup.config.default index b2d49106d..a4deb7804 100644 --- a/Firmware/tup.config.default +++ b/Firmware/tup.config.default @@ -1,10 +1,9 @@ # Copy this file to tup.config and adapt it to your needs # make sure this fits your board #CONFIG_BOARD_VERSION=v3.5-24V -CONFIG_USB_PROTOCOL=native -CONFIG_UART_PROTOCOL=ascii CONFIG_DEBUG=false CONFIG_DOCTEST=false +CONFIG_USE_LTO=false # Uncomment this to error on compilation warnings #CONFIG_STRICT=true diff --git a/GUI/.gitignore b/GUI/.gitignore new file mode 100644 index 000000000..3b33109f1 --- /dev/null +++ b/GUI/.gitignore @@ -0,0 +1,28 @@ +.DS_Store +node_modules +/dist + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +#Electron-builder output +/dist_electron + +#Electron icon generator output +/build \ No newline at end of file diff --git a/GUI/LICENSE.md b/GUI/LICENSE.md new file mode 100644 index 000000000..0a7411b9a --- /dev/null +++ b/GUI/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 ODrive Robotics + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/GUI/README.md b/GUI/README.md new file mode 100644 index 000000000..c4cba806a --- /dev/null +++ b/GUI/README.md @@ -0,0 +1,56 @@ +# odrive_gui + +Python requirements: `pip install flask flask_socketio flask_cors odrive` + +If the default odrive python package is not desired, the path to the modules can be passed as command line arguments. + +example on windows 10: +``` +./odrive_gui_win.exe C:/Users//ODrive/tools C:/Users//ODrive/Firmware +``` + +The first argument is for your local version of odrivetool, the second is for fibre. + +## Development and testing instructions + +### Project setup +``` +npm install +``` + +### Compiles and hot-reloads for development +``` +npm run serve +``` + +### Lints and fixes files +``` +npm run lint +``` + +### Serve electron version of GUI +``` +npm run electron:serve +``` + +### Package electron app into executable +``` +npm run electron:build +``` + +### Build electron app for all platforms +``` +npm run electron:build -- -mwl +``` + +### Building for rpi and potentially other ARM platform devices + +PhantomJS is required as a dependency, so it must be installed first: +``` +sudo apt install phantomjs +``` + +After it is installed, `npm run electron:build` can be used to build the AppImage for ARM + +### Customize configuration +See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/GUI/babel.config.js b/GUI/babel.config.js new file mode 100644 index 000000000..e9558405f --- /dev/null +++ b/GUI/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/cli-plugin-babel/preset' + ] +} diff --git a/GUI/fibre-js/README.md b/GUI/fibre-js/README.md new file mode 100644 index 000000000..7d08910d4 --- /dev/null +++ b/GUI/fibre-js/README.md @@ -0,0 +1,83 @@ +# fibre-js + +This directory provides JavaScript bindings for [Fibre](https://github.com/samuelsadok/fibre). Its home is located [here](https://github.com/samuelsadok/fibre/tree/master/js). There's also a standalone repository for this directory [here](https://github.com/samuelsadok/fibre-js). + +## Current Status + +So far fibre-js was only tested to run in Chrome but it should work in any browser that [supports WebAssembly](https://caniuse.com/wasm). + +fibre-js currently supports the following transport providers: + + - WebUSB ([Chrome and Edge only](https://caniuse.com/webusb)) + +## How to use + +So you want your website or Electron App to connect to some resource(s) using Fibre. Let's start with a simple HTML scaffolding. + +```HTML + + + + + fibre-js Test + + +

not connected

+ + + +``` + +Now, right before the closing ``, insert a script tag to load and open fibre-js: + +```HTML + +``` + +This will load the files `fibre.js`, `libfibre-wasm.js` and `libfibre-wasm.wasm` from the same directory the HTML file comes from so make sure those files exist. + +Now that fibre-js is ready to use we can start discovering remote objects. For this we need to specify a filter string which tells Fibre where to look for objects. In this example we want to talk to a USB device that runs Fibre (more specfically an [ODrive](https://odriverobotics.com/)). The parameters we use here are something you can find out from reading the docs of your USB device or inspecting the device by using `lsusb` on a Linux or macOS computer. + +```JS +const filter = 'usb:idVendor=0x1209,idProduct=0x0D32,bInterfaceClass=0,bInterfaceSubClass=1,bInterfaceProtocol=0'; +const onFoundObject = async (obj) => { + console.log("found an object!", obj); + // TODO: do something with obj +} +libfibre.startDiscovery(filter, onFoundObject); +``` + +Now that we have the object we can start calling functions on it. Let's assume our object has the property `vbus_voltage`. In the `onFoundObject` handle, add: + +```JS +while (true) { + document.getElementById("statusText").innerHTML = "vbus_voltage: " + (await obj.vbus_voltage.read()); + await new Promise((resolve) => setTimeout(resolve, 100)); +} +``` + +This will read the `vbus_voltage` property every 100ms and display the value on the web page. + +Optionally you could add a callback to the `obj._onLost` promise in order to gracefully stop polling when the device disconnects. + +Since we're using WebUSB in this example, there's one more thing left to do: The user needs to authorize the website (only once) to access the USB device. This must happen on a user gesture, which is why earlier we put a Connect-button into our HTML. + +So directly after the call to `libfibre.startDiscovery()`, add: + +```JS +document.getElementById("connectBtn").onclick = libfibre.usbDiscoverer.showDialog; +document.getElementById("connectBtn").removeAttribute('disabled'); +``` + +That's it! You can now open the HTML page in the browser and should see something like this (using Chrome 86 on Linux): + +![Example](example.gif) + +Note how the user doesn't need to manually reselect the device after the website is reloaded. The website is now already authorized to access the device so Fibre immediately connects to it. The same is true when the device is unpluggend and replugged to the computer. + +You can find the complete example code [here](example.html). A more elaborate example which can connect to multiple objects can be found here [here](multidevice_example.html). diff --git a/GUI/fibre-js/example.gif b/GUI/fibre-js/example.gif new file mode 100644 index 000000000..30bae3854 Binary files /dev/null and b/GUI/fibre-js/example.gif differ diff --git a/GUI/fibre-js/example.html b/GUI/fibre-js/example.html new file mode 100644 index 000000000..09f590b73 --- /dev/null +++ b/GUI/fibre-js/example.html @@ -0,0 +1,28 @@ + + + + + fibre-js Test + + +

not connected

+ + + + \ No newline at end of file diff --git a/GUI/fibre-js/fibre.js b/GUI/fibre-js/fibre.js new file mode 100644 index 000000000..b8d4ce800 --- /dev/null +++ b/GUI/fibre-js/fibre.js @@ -0,0 +1,724 @@ + +import wasm from './libfibre-wasm.js'; + +function assert(expression) { + if (!expression) { + throw Error("assert failed"); + } +} + +const FIBRE_STATUS_OK = 0; +const FIBRE_STATUS_CANCELLED = 1; +const FIBRE_STATUS_CLOSED = 2; +const FIBRE_STATUS_INVALID_ARGUMENT = 3; +const FIBRE_STATUS_INTERNAL_ERROR = 4; + +const ptrSize = 4; + +function _getError(code) { + if (code == FIBRE_CANCELLED) { + return Error("libfibre: Operation Cancelled"); + } else if (code == FIBRE_STATUS_CLOSED) { + return Error("libfibre: Closed"); + } else if (code == FIBRE_STATUS_INVALID_ARGUMENT) { + return Error("libfibre: Invalid Argument"); + } else if (code == FIBRE_STATUS_INTERNAL_ERROR) { + return Error("libfibre: Internal Error"); + } else { + return Error("libfibre: Unknown Error " + code); + } +} + +class BasicCodec { + constructor(typeName, byteLength, littleEndian) { + this.getSize = () => byteLength; + this.serialize = (libfibre, val) => { + var myBuf = new ArrayBuffer(byteLength); + new DataView(myBuf)['set' + typeName](0, val, littleEndian) + return Array.from(new Uint8Array(myBuf)); + } + this.deserialize = (libfibre, buf) => { + var myBuf = new ArrayBuffer(buf.length); + new Uint8Array(myBuf).set(buf); + return new DataView(myBuf)['get' + typeName](0, littleEndian); + } + } +} + +const codecs = { + 'int8': new BasicCodec('Int8', 1, true), + 'uint8': new BasicCodec('Uint8', 1, true), + 'int16': new BasicCodec('Int16', 2, true), + 'uint16': new BasicCodec('Uint16', 2, true), + 'int32': new BasicCodec('Int32', 4, true), + 'uint32': new BasicCodec('Uint32', 4, true), + 'int64': new BasicCodec('BigInt64', 8, true), + 'uint64': new BasicCodec('BigUint64', 8, true), + 'float': new BasicCodec('Float32', 4, true), +}; + +class EofError extends Error { + constructor(msg) { + super(msg); + } +} + +class TxStream { + constructor(libfibre, handle) { + this._libfibre = libfibre; + this._handle = handle; + this.isClosed = false; + this._libfibre._txStreamMap[this._handle] = this; + } + + write(data) { + assert(this._handle); + return new Promise((resolve, reject) => { + let byteLength = Array.isArray(data) ? data.length : data.byteLength; + if (!Array.isArray(data)) { + data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength) + } + this._buf = [this._libfibre.wasm.malloc(byteLength), byteLength]; + this._libfibre.wasm.Module.HEAPU8.set(data, this._buf[0]); + this._writeResolve = resolve; + this._writeReject = reject; + this._libfibre.wasm.Module._libfibre_start_tx(this._handle, this._buf[0], this._buf[1], this._libfibre._onTxCompleted, this._handle); + }); + } + + async writeAll(data) { + while (true) { + const nWritten = await this.write(data); + data = data.slice(nWritten); + if (data.length == 0) { + break; + } + if (this.isClosed) { + throw EofError(`the TX stream was closed but there are still ${data.length} bytes left to send`); + } + assert(nWritten > 0); // ensure progress + } + } + + close(status) { + assert(this._handle); + this._libfibre.wasm.Module._libfibre_close_tx(this._handle, status); + delete this._handle; + } + + _complete(status, txEnd) { + const len = txEnd - this._buf[0]; + assert(len <= this._buf[1]); + this._libfibre.wasm.free(this._buf[0]); + delete this._buf; + const resolve = this._writeResolve; + const reject = this._writeReject; + delete this._writeResolve; + delete this._writeReject; + + this.isClosed = this.isClosed || (status == FIBRE_STATUS_CLOSED); + + if (status == FIBRE_STATUS_OK || status == FIBRE_STATUS_CLOSED) { + resolve(len); + } else { + reject(_getError(status)); + } + } +} + +class RxStream { + constructor(libfibre, handle) { + this._libfibre = libfibre; + this._handle = handle; + this.isClosed = false; + this._libfibre._rxStreamMap[this._handle] = this; + } + + read(len) { + assert(this._handle); + return new Promise((resolve, reject) => { + this._buf = [this._libfibre.wasm.malloc(len), len]; + this._readResolve = resolve; + this._readReject = reject; + this._libfibre.wasm.Module._libfibre_start_rx(this._handle, this._buf[0], this._buf[1], this._libfibre._onRxCompleted, this._handle); + }); + } + + async readAll(len) { + let data = []; + while (true) { + let chunk = await this.read(len - data.length); + data.push(...chunk); + if (data.length >= len) { + break; + } + if (this.isClosed) { + throw EofError(`the RX stream was closed but there are still ${len - data.length} bytes left to receive`); + } + assert(chunk.length > 0); // ensure progress + } + return data; + } + + close(status) { + assert(this._handle); + this._libfibre.wasm.Module._libfibre_close_rx(this._handle, status); + delete this._handle; + } + + _complete(status, rxEnd) { + const len = rxEnd - this._buf[0]; + assert(len <= this._buf[1]); + const result = new Uint8Array(this._libfibre.wasm.Module.HEAPU8.buffer, this._buf[0], len).slice(0); + this._libfibre.wasm.free(this._buf[0]); + delete this._buf; + const resolve = this._readResolve; + const reject = this._readReject; + delete this._readResolve; + delete this._readReject; + + this.isClosed = this.isClosed || (status == FIBRE_STATUS_CLOSED); + + if (status == FIBRE_STATUS_OK || status == FIBRE_STATUS_CLOSED) { + resolve(result); + } else { + reject(_getError(status)); + } + } +} + +class RemoteInterface { + constructor(libfibre, intfName) { + this._libfibre = libfibre; + this.intfName = intfName; + this._onLost = new Promise((resolve) => this._onLostResolve = resolve); + } +} + +const _staleObject = {}; + +class RemoteAttribute { + constructor(libfibre, handle, subintf, subintfName) { + this._libfibre = libfibre; + this._handle = handle; + this._subintf = subintf; + this._subintfName = subintfName; + } + + get(obj) { + let objHandlePtr = this._libfibre.wasm.malloc(ptrSize); + try { + this._libfibre.wasm.Module._libfibre_get_attribute(obj._handle, this._handle, objHandlePtr); + return this._libfibre._objMap[this._libfibre._derefIntPtr(objHandlePtr)]; + } finally { + this._libfibre.wasm.free(objHandlePtr); + } + } +} + +class RemoteFunction extends Function { + constructor(libfibre, handle, inputArgs, outputArgs) { + let closure = function(...args) { + return closure._call(this, ...args); + } + closure._libfibre = libfibre; + closure._handle = handle; + closure._inputArgs = inputArgs; + closure._outputArgs = outputArgs; + // Return closure instead of the original "this" object that was + // associated with the constructor call. + return Object.setPrototypeOf(closure, new.target.prototype); + } + + /** + * @brief Starts a function call on this function. + * @return {TxStream, RxStream, Promise} A TX stream that can be used to + * send data on this function call, an RX stream that can be used to receive + * data on this function call and a promise that will be completed once the + * function call completes. + */ + startCall(obj) { + let _callHandle, _txStreamHandle, _rxStreamHandle; + let completionPromise = new Promise((resolve, reject) => { + let completer = (status) => { + if (status == FIBRE_STATUS_OK) { + resolve(); + } else { + reject(_getError(status)); + } + }; + [_callHandle, _txStreamHandle, _rxStreamHandle] = this._libfibre._withOutputArg( + (_callHandlePtr, _txStreamHandlePtr, _rxStreamHandlePtr) => + this._libfibre.wasm.Module._libfibre_start_call(obj._handle, this._handle, + _callHandlePtr, + _txStreamHandlePtr, _rxStreamHandlePtr, this._libfibre._onCallCompleted, + this._libfibre._allocRef(completer))); + }); + return [ + new TxStream(this._libfibre, _txStreamHandle), + new RxStream(this._libfibre, _rxStreamHandle), + completionPromise]; + } + + async _call(obj, ...args) { + assert(args.length == this._inputArgs.length); + + let txBuf = []; + for (let argSpec of this._inputArgs) { + txBuf.push(...argSpec.codec.serialize()); + } + + const rxLen = this._outputArgs.reduce((a, b) => a + b.codec.getSize(), 0); + assert(Number.isInteger(rxLen)); + + const [txStream, rxStream, completionPromise] = this.startCall(obj); + + // RX must be started before TX + const rxPromise = rxStream.readAll(rxLen); + await txStream.writeAll(txBuf); + let rxBuf = await rxPromise; + await completionPromise; + + let outputs = []; + for (let argSpec of this._outputArgs) { + let argLength = argSpec.codec.getSize() + outputs.push(argSpec.codec.deserialize(self._libfibre, rxBuf.slice(0, argLength))) + rxBuf = rxBuf.slice(argLength); + } + + if (outputs.length == 1) { + return outputs[0]; + } else if (outputs.length > 1) { + return outputs; + } + } +} + +class LibFibre { + constructor(wasm) { + this.wasm = wasm; + + // The following functions are JavaScript callbacks that get invoked from C + // code (WebAssembly). We convert each function to a pointer which can then + // be passed to C code. + // Function signature codes are documented here: + // https://github.com/aheejin/emscripten/blob/master/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst#calling-javascript-functions-as-function-pointers-from-c + + this._onPost = wasm.addFunction((cb, cbCtx) => { + // TODO + console.log("event loop post"); + return new Promise((resolve, reject) => { + cb(cbCtx); + resolve(); + }); + }, 'iii'); + + this._onCallLater = wasm.addFunction((delay, cb, cbCtx) => { + console.log("event loop call later"); + return setTimeout(() => cb(cbCtx), delay * 1000); + }, 'ifii') + + this._onCancelTimer = wasm.addFunction((timer) => { + return cancelTimeout(timer); + }, 'ii') + + this._onConstructObject = wasm.addFunction((ctx, obj, intf, intfName, intfNameLength) => { + let jsIntf = this._loadJsIntf(intf, this.wasm.UTF8ArrayToString(this.wasm.Module.HEAPU8, intfName, intfNameLength)); + this._objMap[obj] = new jsIntf(this, obj); + }, 'viiiii') + + this._onDestroyObject = wasm.addFunction((ctx, obj) => { + let jsObj = this._objMap[obj]; + delete this._objMap[obj]; + delete jsObj._handle; + Object.setPrototypeOf(jsObj, _staleObject); + console.log("destroyed remote object"); + jsObj._oldHandle = obj; + jsObj._onLostResolve(); + }, 'vii') + + this._onStartDiscovery = wasm.addFunction((ctx, discCtx, specs, specsLength) => { + console.log("start discovery"); + let discoverer = this._deref(ctx); + discoverer.startChannelDiscovery( + this.wasm.UTF8ArrayToString(this.wasm.Module.HEAPU8, specs, specsLength), + discCtx); + }, 'viiii') + + this._onStopDiscovery = wasm.addFunction((ctx, discCtx) => { + console.log("stop discovery"); + this._freeRef(ctx); + }, 'viiii') + + this._onFoundObject = wasm.addFunction((ctx, obj) => { + let onFoundObject = this._deref(ctx); + onFoundObject(this._objMap[obj]); + }, 'vii') + + this._onAttributeAdded = wasm.addFunction((ctx, attr, name, nameNength, subintf, subintfName, subintfNameLength) => { + let jsIntf = this._intfMap[ctx]; + let jsAttr = new RemoteAttribute(this, attr, subintf, this.wasm.UTF8ArrayToString(this.wasm.Module.HEAPU8, subintfName, subintfNameLength)); + Object.defineProperty(jsIntf.prototype, this.wasm.UTF8ArrayToString(this.wasm.Module.HEAPU8, name, nameNength), { + get: function () { return jsAttr.get(this); } + }); + }, 'viiiiiii') + + this._onAttributeRemoved = wasm.addFunction((ctx, attr) => { + console.log("attribute removed"); // TODO + }, 'vii') + + this._onFunctionAdded = wasm.addFunction((ctx, func, name, nameLength, inputNames, inputCodecs, outputNames, outputCodecs) => { + const jsIntf = this._intfMap[ctx]; + const jsInputParams = [...this._decodeArgList(inputNames, inputCodecs)]; + const jsOutputParams = [...this._decodeArgList(outputNames, outputCodecs)]; + let jsFunc = new RemoteFunction(this, func, jsInputParams, jsOutputParams); + jsIntf.prototype[this.wasm.UTF8ArrayToString(this.wasm.Module.HEAPU8, name, nameLength)] = jsFunc; + }, 'viiiiiiii') + + this._onFunctionRemoved = wasm.addFunction((ctx, func) => { + console.log("function removed"); // TODO + }, 'vii') + + this._onCallCompleted = wasm.addFunction((ctx, status) => { + let completer = this._freeRef(ctx); + completer(status); + }, 'vii'); + + this._onTxCompleted = wasm.addFunction((ctx, txStream, status, txEnd) => { + this._txStreamMap[txStream]._complete(status, txEnd); + }, 'viiii') + + this._onRxCompleted = wasm.addFunction((ctx, rxStream, status, rxEnd) => { + this._rxStreamMap[rxStream]._complete(status, rxEnd); + }, 'viiii') + + const ptr = this.wasm.Module._libfibre_get_version(); + const version_array = (new Uint16Array(wasm.Module.HEAPU8.buffer, ptr, 3)) + this.version = { + major: version_array[0], + minor: version_array[1], + patch: version_array[2] + }; + + this._intfMap = {}; + this._refMap = {}; + this._objMap = {}; + this._txStreamMap = {}; + this._rxStreamMap = {}; + + this._handle = this.wasm.Module._libfibre_open( + this._onPost, + 0, + 0, + this._onCallLater, + this._onCancelTimer, + this._onConstructObject, + this._onDestroyObject + ); + + this.usbDiscoverer = new WebUsbDiscoverer(this); + + let buf = [this.wasm.malloc(4), 4]; + try { + let len = this.wasm.stringToUTF8Array("usb", this.wasm.Module.HEAPU8, buf[0], buf[1]); + this.wasm.Module._libfibre_register_discoverer(this._handle, buf[0], len, + this._onStartDiscovery, this._onStopDiscovery, this._allocRef(this.usbDiscoverer)); + } finally { + this.wasm.free(buf[0]); + } + } + + addChannels(mtu, channelDiscoveryHandle) { + console.log("add channel with mtu " + mtu); + const [txChannelId, rxChannelId] = this._withOutputArg((txChannelIdPtr, rxChannelIdPtr) => + this.wasm.Module._libfibre_add_channels(this._handle, channelDiscoveryHandle, txChannelIdPtr, rxChannelIdPtr, mtu) + ); + return [ + new RxStream(this, txChannelId), + new TxStream(this, rxChannelId) + ]; + } + + startDiscovery(filter, onFoundObject) { + let buf = [this.wasm.malloc(filter.length + 1), filter.length + 1]; + try { + let len = this.wasm.stringToUTF8Array(filter, this.wasm.Module.HEAPU8, buf[0], buf[1]); + this._withOutputArg((discoveryHandlePtr) => + this.wasm.Module._libfibre_start_discovery(this._handle, buf[0], len, + discoveryHandlePtr, + this._onFoundObject, this._onStopped, this._allocRef(onFoundObject))); + } finally { + this.wasm.free(buf[0]); + } + } + + /** + * @brief Allocates a unique integer key for the specified JavaScript object. + * + * The unique key can later be used to look up the JavaScript object using + * _freeRef(). + */ + _allocRef(obj) { + let id = 0; + while (++id in this._refMap) {} + console.log("allocating id " + id, obj); + this._refMap[id] = obj; + return id; + } + + _deref(id) { + return this._refMap[id]; + } + + _derefIntPtr(ptr) { + return new Uint32Array(this.wasm.Module.HEAPU8.buffer, ptr, 1)[0] + } + + * _getStringList(ptr) { + for (let i = 0; this._derefIntPtr(ptr + i * ptrSize); ++i) { + yield this.wasm.UTF8ArrayToString(this.wasm.Module.HEAPU8, this._derefIntPtr(ptr + i * ptrSize)); + } + } + + /** + * @bried Deallocates a unique integer key that was previously obtained with + * _allocRef. + * + * @return The JavaScript object that was passed to the corresponding + * _allocRef call. + */ + _freeRef(id) { + let obj = this._refMap[id]; + delete this._refMap[id]; + console.log("freeing id " + id, obj); + return obj; + } + + _withOutputArg(func) { + let ptrs = []; + try { + for (let i = 0; i < func.length; ++i) { + ptrs.push(this.wasm.malloc(ptrSize)); + } + func(...ptrs); + return ptrs.map((ptr) => this._derefIntPtr(ptr)); + } finally { + for (let ptr of ptrs) { + this.wasm.free(ptr); + } + } + } + + * _decodeArgList(argNames, codecNames) { + argNames = [...this._getStringList(argNames)]; + codecNames = [...this._getStringList(codecNames)]; + assert(argNames.length == codecNames.length); + for (let i in argNames) { + yield { + name: argNames[i], + codecName: codecNames[i], + codec: codecs[codecNames[i]] + }; + } + } + + _loadJsIntf(intfHandle, intfName) { + if (intfHandle in this._intfMap) { + return this._intfMap[intfHandle]; + } else { + class RemoteObject extends RemoteInterface { + constructor(libfibre, handle) { + super(libfibre, intfName); + this._handle = handle; + } + }; + let jsIntf = RemoteObject; + this._intfMap[intfHandle] = jsIntf; + this.wasm.Module._libfibre_subscribe_to_interface(intfHandle, + this._onAttributeAdded, + this._onAttributeRemoved, + this._onFunctionAdded, + this._onFunctionRemoved, + intfHandle); + return jsIntf; + } + } + +} + +export function fibreOpen() { + return new Promise(async (resolve) => { + let Module = { + preRun: [function() {Module.ENV.FIBRE_LOG = "4"}], // enable logging + instantiateWasm: async (info, receiveInstance) => { + const isWebpack = typeof __webpack_require__ === 'function'; + const wasmPath = isWebpack ? (await import("!!file-loader!./libfibre-wasm.wasm")).default + : "./libfibre-wasm.wasm"; + const response = fetch(wasmPath, { credentials: 'same-origin' }); + const result = await WebAssembly.instantiateStreaming(response, info); + receiveInstance(result['instance']); + return {}; + } + }; + + Module = await wasm(Module); + await Module.ready; + wasm.addFunction = Module.addFunction; + wasm.stringToUTF8Array = Module.stringToUTF8Array; + wasm.UTF8ArrayToString = Module.UTF8ArrayToString; + wasm.malloc = Module._malloc; + wasm.free = Module._free; + wasm.Module = Module; + + resolve(new LibFibre(wasm)); + }); +} + +class WebUsbDiscoverer { + constructor(libfibre) { + this._libfibre = libfibre; + this._discoveryProcesses = []; + + if (navigator.usb.onconnect != null) { + console.warn("There was already a subscriber for usb.onconnect."); + } + navigator.usb.onconnect = (event) => { + for (let discovery of this._discoveryProcesses) { + this._consider(event.device, discovery.filter, discovery.handle); + } + } + navigator.usb.ondisconnect = () => console.log("disconnect"); + + this._filterKeys = { + 'idVendor': 'vendorId', + 'idProduct': 'productId', + 'bInterfaceClass': 'classCode', + 'bInterfaceSubClass': 'subclassCode', + 'bInterfaceProtocol': 'protocolCode', + }; + + this.showDialog = async () => { + let filters = this._discoveryProcesses.map((d) => d.filter); + let dev = await navigator.usb.requestDevice({filters: filters}); + for (let discovery of this._discoveryProcesses) { + this._consider(dev, discovery.filter, discovery.handle); + } + }; + } + + async startChannelDiscovery(specs, discoveryHandle) { + let filter = {} + for (let item of specs.split(',')) { + filter[this._filterKeys[item.split('=')[0]]] = item.split('=')[1] + } + + this._discoveryProcesses.push({ + filter: filter, + handle: discoveryHandle + }); + + for (let dev of await navigator.usb.getDevices({filters: [filter]})) { + this._consider(dev, filter, discoveryHandle); + } + } + + async _consider(device, filter, channelDiscoveryHandle) { + if ((filter.vendorId != undefined) && (filter.vendorId != device.vendorId)) { + return; + } + if ((filter.productId != undefined) && (filter.productId != device.productId)) { + return; + } + + for (let config of device.configurations) { + if (device.configuration !== null && device.configuration.configurationValue != config.configurationValue) { + continue; // A configuration was already set and it's different from this one + } + + for (let intf of config.interfaces) { + for (let alternate of intf.alternates) { + const mismatch = ((filter.classCode != undefined) && (filter.classCode != alternate.interfaceClass)) + || ((filter.subclassCode != undefined) && (filter.subclassCode != alternate.interfaceSubclass)) + || ((filter.protocolCode != undefined) && (filter.protocolCode != alternate.interfaceProtocol)); + if (mismatch) { + continue; + } + + await device.open(); + await device.selectConfiguration(config.configurationValue); + await device.claimInterface(intf.interfaceNumber); + + let epOut = null, epIn = null; + for (let ep of alternate.endpoints) { + if (ep.type == "bulk" && ep.direction == "in") + epIn = ep; + else if (ep.type == "bulk" && ep.direction == "out") + epOut = ep; + } + + device.knownInEndpoints = device.knownInEndpoints || []; + device.knownOutEndpoints = device.knownOutEndpoints || []; + console.log(device.knownOutEndpoints); + if (device.knownInEndpoints.indexOf(epIn.endpointNumber) >= 0) { + continue; + } + if (device.knownOutEndpoints.indexOf(epOut.endpointNumber) >= 0) { + continue; + } + device.knownInEndpoints.push(epIn.endpointNumber); + device.knownOutEndpoints.push(epOut.endpointNumber); + + let mtu = Math.min(epIn.packetSize, epOut.packetSize); + const [txChannel, rxChannel] = this._libfibre.addChannels(mtu, channelDiscoveryHandle); + this._connectBulkInEp(device, epIn, rxChannel); + this._connectBulkOutEp(device, epOut, txChannel); + } + } + } + } + + async _connectBulkOutEp(dev, ep, stream) { + while (true) { + //console.log("waiting for data from libfibre..."); + const data = await stream.read(ep.packetSize); + //console.log("forwarding " + data.length + " bytes to USB..."); + //console.log(data); + let result; + try { + result = await dev.transferOut(ep.endpointNumber, data); + } catch (e) { // TODO: propagate actual transfer errors + result = {status: "stall"}; + } + assert(result.bytesWritten == data.length); + console.log(dev.opened); + if (!dev.opened || result.status == "stall") { + stream.close(FIBRE_STATUS_CLOSED); + break; + } + assert(result.status == "ok") + }; + } + + async _connectBulkInEp(dev, ep, stream) { + while (true) { + //console.log("waiting for up to " + epIn.packetSize + " bytes from USB..."); + let result; + try { + result = await dev.transferIn(ep.endpointNumber, ep.packetSize); + } catch (e) { // TODO: propagate actual transfer errors + result = {status: "stall"}; + } + //console.log("got for data from USB..."); + console.log(dev.opened); + console.log(dev); + if (!dev.opened || result.status == "stall") { + stream.close(FIBRE_STATUS_CLOSED); + break; + } + assert(result.status == "ok"); + //console.log("forwarding " + result.data.byteLength + " bytes to libfibre..."); + const len = await stream.write(result.data); + assert(len == result.data.byteLength); + } + } +} diff --git a/GUI/fibre-js/libfibre-wasm.js b/GUI/fibre-js/libfibre-wasm.js new file mode 100644 index 000000000..5ee8284a4 --- /dev/null +++ b/GUI/fibre-js/libfibre-wasm.js @@ -0,0 +1,5104 @@ + +var Module = (function() { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; + return ( +function(Module) { + Module = Module || {}; + + + +// The Module object: Our interface to the outside world. We import +// and export values on it. There are various ways Module can be used: +// 1. Not defined. We create it here +// 2. A function parameter, function(Module) { ..generated code.. } +// 3. pre-run appended it, var Module = {}; ..generated code.. +// 4. External script tag defines var Module. +// We need to check if Module already exists (e.g. case 3 above). +// Substitution will be replaced with actual code on later stage of the build, +// this way Closure Compiler will not mangle it (e.g. case 4. above). +// Note that if you want to run closure, and also to use Module +// after the generated code, you will need to define var Module = {}; +// before the code. Then that object will be used in the code, and you +// can continue to use Module afterwards as well. +var Module = typeof Module !== 'undefined' ? Module : {}; + + +// Set up the promise that indicates the Module is initialized +var readyPromiseResolve, readyPromiseReject; +Module['ready'] = new Promise(function(resolve, reject) { + readyPromiseResolve = resolve; + readyPromiseReject = reject; +}); + +// --pre-jses are emitted after the Module integration code, so that they can +// refer to Module (if they choose; they can also define Module) +// {{PRE_JSES}} + +// Sometimes an existing Module object exists with properties +// meant to overwrite the default module functionality. Here +// we collect those properties and reapply _after_ we configure +// the current environment's defaults to avoid having to be so +// defensive during initialization. +var moduleOverrides = {}; +var key; +for (key in Module) { + if (Module.hasOwnProperty(key)) { + moduleOverrides[key] = Module[key]; + } +} + +var arguments_ = []; +var thisProgram = './this.program'; +var quit_ = function(status, toThrow) { + throw toThrow; +}; + +// Determine the runtime environment we are in. You can customize this by +// setting the ENVIRONMENT setting at compile time (see settings.js). + +var ENVIRONMENT_IS_WEB = false; +var ENVIRONMENT_IS_WORKER = false; +var ENVIRONMENT_IS_NODE = false; +var ENVIRONMENT_IS_SHELL = false; +ENVIRONMENT_IS_WEB = typeof window === 'object'; +ENVIRONMENT_IS_WORKER = typeof importScripts === 'function'; +// N.b. Electron.js environment is simultaneously a NODE-environment, but +// also a web environment. +ENVIRONMENT_IS_NODE = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string'; +ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; + + + + +// `/` should be present at the end if `scriptDirectory` is not empty +var scriptDirectory = ''; +function locateFile(path) { + if (Module['locateFile']) { + return Module['locateFile'](path, scriptDirectory); + } + return scriptDirectory + path; +} + +// Hooks that are implemented differently in different runtime environments. +var read_, + readAsync, + readBinary, + setWindowTitle; + +var nodeFS; +var nodePath; + +if (ENVIRONMENT_IS_NODE) { + if (ENVIRONMENT_IS_WORKER) { + scriptDirectory = require('path').dirname(scriptDirectory) + '/'; + } else { + scriptDirectory = __dirname + '/'; + } + + + + +read_ = function shell_read(filename, binary) { + if (!nodeFS) nodeFS = require('fs'); + if (!nodePath) nodePath = require('path'); + filename = nodePath['normalize'](filename); + return nodeFS['readFileSync'](filename, binary ? null : 'utf8'); +}; + +readBinary = function readBinary(filename) { + var ret = read_(filename, true); + if (!ret.buffer) { + ret = new Uint8Array(ret); + } + assert(ret.buffer); + return ret; +}; + + + + if (process['argv'].length > 1) { + thisProgram = process['argv'][1].replace(/\\/g, '/'); + } + + arguments_ = process['argv'].slice(2); + + // MODULARIZE will export the module in the proper place outside, we don't need to export here + + process['on']('uncaughtException', function(ex) { + // suppress ExitStatus exceptions from showing an error + if (!(ex instanceof ExitStatus)) { + throw ex; + } + }); + + process['on']('unhandledRejection', abort); + + quit_ = function(status) { + process['exit'](status); + }; + + Module['inspect'] = function () { return '[Emscripten Module object]'; }; + + + +} else +if (ENVIRONMENT_IS_SHELL) { + + + if (typeof read != 'undefined') { + read_ = function shell_read(f) { + return read(f); + }; + } + + readBinary = function readBinary(f) { + var data; + if (typeof readbuffer === 'function') { + return new Uint8Array(readbuffer(f)); + } + data = read(f, 'binary'); + assert(typeof data === 'object'); + return data; + }; + + if (typeof scriptArgs != 'undefined') { + arguments_ = scriptArgs; + } else if (typeof arguments != 'undefined') { + arguments_ = arguments; + } + + if (typeof quit === 'function') { + quit_ = function(status) { + quit(status); + }; + } + + if (typeof print !== 'undefined') { + // Prefer to use print/printErr where they exist, as they usually work better. + if (typeof console === 'undefined') console = /** @type{!Console} */({}); + console.log = /** @type{!function(this:Console, ...*): undefined} */ (print); + console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr !== 'undefined' ? printErr : print); + } + + +} else + +// Note that this includes Node.js workers when relevant (pthreads is enabled). +// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and +// ENVIRONMENT_IS_NODE. +if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled + scriptDirectory = self.location.href; + } else if (document.currentScript) { // web + scriptDirectory = document.currentScript.src; + } + // When MODULARIZE, this JS may be executed later, after document.currentScript + // is gone, so we saved it, and we use it here instead of any other info. + if (_scriptDir) { + scriptDirectory = _scriptDir; + } + // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. + // otherwise, slice off the final part of the url to find the script directory. + // if scriptDirectory does not contain a slash, lastIndexOf will return -1, + // and scriptDirectory will correctly be replaced with an empty string. + if (scriptDirectory.indexOf('blob:') !== 0) { + scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf('/')+1); + } else { + scriptDirectory = ''; + } + + + // Differentiate the Web Worker from the Node Worker case, as reading must + // be done differently. + { + + + + + read_ = function(url) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.send(null); + return xhr.responseText; + }; + + if (ENVIRONMENT_IS_WORKER) { + readBinary = function(url) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.responseType = 'arraybuffer'; + xhr.send(null); + return new Uint8Array(/** @type{!ArrayBuffer} */(xhr.response)); + }; + } + + readAsync = function(url, onload, onerror) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.onload = function() { + if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 + onload(xhr.response); + return; + } + onerror(); + }; + xhr.onerror = onerror; + xhr.send(null); + }; + + + + + } + + setWindowTitle = function(title) { document.title = title }; +} else +{ +} + + +// Set up the out() and err() hooks, which are how we can print to stdout or +// stderr, respectively. +var out = Module['print'] || console.log.bind(console); +var err = Module['printErr'] || console.warn.bind(console); + +// Merge back in the overrides +for (key in moduleOverrides) { + if (moduleOverrides.hasOwnProperty(key)) { + Module[key] = moduleOverrides[key]; + } +} +// Free the object hierarchy contained in the overrides, this lets the GC +// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. +moduleOverrides = null; + +// Emit code to handle expected values on the Module object. This applies Module.x +// to the proper local x. This has two benefits: first, we only emit it if it is +// expected to arrive, and second, by using a local everywhere else that can be +// minified. + +if (Module['arguments']) arguments_ = Module['arguments']; + +if (Module['thisProgram']) thisProgram = Module['thisProgram']; + +if (Module['quit']) quit_ = Module['quit']; + +// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message + + + + + +var STACK_ALIGN = 16; + +function alignMemory(size, factor) { + if (!factor) factor = STACK_ALIGN; // stack alignment (16-byte) by default + return Math.ceil(size / factor) * factor; +} + +function getNativeTypeSize(type) { + switch (type) { + case 'i1': case 'i8': return 1; + case 'i16': return 2; + case 'i32': return 4; + case 'i64': return 8; + case 'float': return 4; + case 'double': return 8; + default: { + if (type[type.length-1] === '*') { + return 4; // A pointer + } else if (type[0] === 'i') { + var bits = Number(type.substr(1)); + assert(bits % 8 === 0, 'getNativeTypeSize invalid bits ' + bits + ', type ' + type); + return bits / 8; + } else { + return 0; + } + } + } +} + +function warnOnce(text) { + if (!warnOnce.shown) warnOnce.shown = {}; + if (!warnOnce.shown[text]) { + warnOnce.shown[text] = 1; + err(text); + } +} + + + + +// Wraps a JS function as a wasm function with a given signature. +function convertJsFunctionToWasm(func, sig) { + + // If the type reflection proposal is available, use the new + // "WebAssembly.Function" constructor. + // Otherwise, construct a minimal wasm module importing the JS function and + // re-exporting it. + if (typeof WebAssembly.Function === "function") { + var typeNames = { + 'i': 'i32', + 'j': 'i64', + 'f': 'f32', + 'd': 'f64' + }; + var type = { + parameters: [], + results: sig[0] == 'v' ? [] : [typeNames[sig[0]]] + }; + for (var i = 1; i < sig.length; ++i) { + type.parameters.push(typeNames[sig[i]]); + } + return new WebAssembly.Function(type, func); + } + + // The module is static, with the exception of the type section, which is + // generated based on the signature passed in. + var typeSection = [ + 0x01, // id: section, + 0x00, // length: 0 (placeholder) + 0x01, // count: 1 + 0x60, // form: func + ]; + var sigRet = sig.slice(0, 1); + var sigParam = sig.slice(1); + var typeCodes = { + 'i': 0x7f, // i32 + 'j': 0x7e, // i64 + 'f': 0x7d, // f32 + 'd': 0x7c, // f64 + }; + + // Parameters, length + signatures + typeSection.push(sigParam.length); + for (var i = 0; i < sigParam.length; ++i) { + typeSection.push(typeCodes[sigParam[i]]); + } + + // Return values, length + signatures + // With no multi-return in MVP, either 0 (void) or 1 (anything else) + if (sigRet == 'v') { + typeSection.push(0x00); + } else { + typeSection = typeSection.concat([0x01, typeCodes[sigRet]]); + } + + // Write the overall length of the type section back into the section header + // (excepting the 2 bytes for the section id and length) + typeSection[1] = typeSection.length - 2; + + // Rest of the module is static + var bytes = new Uint8Array([ + 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm") + 0x01, 0x00, 0x00, 0x00, // version: 1 + ].concat(typeSection, [ + 0x02, 0x07, // import section + // (import "e" "f" (func 0 (type 0))) + 0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00, + 0x07, 0x05, // export section + // (export "f" (func 0 (type 0))) + 0x01, 0x01, 0x66, 0x00, 0x00, + ])); + + // We can compile this wasm module synchronously because it is very small. + // This accepts an import (at "e.f"), that it reroutes to an export (at "f") + var module = new WebAssembly.Module(bytes); + var instance = new WebAssembly.Instance(module, { + 'e': { + 'f': func + } + }); + var wrappedFunc = instance.exports['f']; + return wrappedFunc; +} + +var freeTableIndexes = []; + +// Weak map of functions in the table to their indexes, created on first use. +var functionsInTableMap; + +// Add a wasm function to the table. +function addFunctionWasm(func, sig) { + var table = wasmTable; + + // Check if the function is already in the table, to ensure each function + // gets a unique index. First, create the map if this is the first use. + if (!functionsInTableMap) { + functionsInTableMap = new WeakMap(); + for (var i = 0; i < table.length; i++) { + var item = table.get(i); + // Ignore null values. + if (item) { + functionsInTableMap.set(item, i); + } + } + } + if (functionsInTableMap.has(func)) { + return functionsInTableMap.get(func); + } + + // It's not in the table, add it now. + + + var ret; + // Reuse a free index if there is one, otherwise grow. + if (freeTableIndexes.length) { + ret = freeTableIndexes.pop(); + } else { + ret = table.length; + // Grow the table + try { + table.grow(1); + } catch (err) { + if (!(err instanceof RangeError)) { + throw err; + } + throw 'Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.'; + } + } + + // Set the new value. + try { + // Attempting to call this with JS function will cause of table.set() to fail + table.set(ret, func); + } catch (err) { + if (!(err instanceof TypeError)) { + throw err; + } + var wrapped = convertJsFunctionToWasm(func, sig); + table.set(ret, wrapped); + } + + functionsInTableMap.set(func, ret); + + return ret; +} + +function removeFunctionWasm(index) { + functionsInTableMap.delete(wasmTable.get(index)); + freeTableIndexes.push(index); +} + +// 'sig' parameter is required for the llvm backend but only when func is not +// already a WebAssembly function. +function addFunction(func, sig) { + + return addFunctionWasm(func, sig); +} + +function removeFunction(index) { + removeFunctionWasm(index); +} + + + + + + + + + +function makeBigInt(low, high, unsigned) { + return unsigned ? ((+((low>>>0)))+((+((high>>>0)))*4294967296.0)) : ((+((low>>>0)))+((+((high|0)))*4294967296.0)); +} + +var tempRet0 = 0; + +var setTempRet0 = function(value) { + tempRet0 = value; +}; + +var getTempRet0 = function() { + return tempRet0; +}; + + + + + +// === Preamble library stuff === + +// Documentation for the public APIs defined in this file must be updated in: +// site/source/docs/api_reference/preamble.js.rst +// A prebuilt local version of the documentation is available at: +// site/build/text/docs/api_reference/preamble.js.txt +// You can also build docs locally as HTML or other formats in site/ +// An online HTML version (which may be of a different version of Emscripten) +// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html + +var wasmBinary; +if (Module['wasmBinary']) wasmBinary = Module['wasmBinary']; +var noExitRuntime = Module['noExitRuntime'] || true; + + +if (typeof WebAssembly !== 'object') { + abort('no native wasm support detected'); +} + + + + +// In MINIMAL_RUNTIME, setValue() and getValue() are only available when building with safe heap enabled, for heap safety checking. +// In traditional runtime, setValue() and getValue() are always available (although their use is highly discouraged due to perf penalties) + +/** @param {number} ptr + @param {number} value + @param {string} type + @param {number|boolean=} noSafe */ +function setValue(ptr, value, type, noSafe) { + type = type || 'i8'; + if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit + switch(type) { + case 'i1': HEAP8[((ptr)>>0)] = value; break; + case 'i8': HEAP8[((ptr)>>0)] = value; break; + case 'i16': HEAP16[((ptr)>>1)] = value; break; + case 'i32': HEAP32[((ptr)>>2)] = value; break; + case 'i64': (tempI64 = [value>>>0,(tempDouble=value,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((ptr)>>2)] = tempI64[0],HEAP32[(((ptr)+(4))>>2)] = tempI64[1]); break; + case 'float': HEAPF32[((ptr)>>2)] = value; break; + case 'double': HEAPF64[((ptr)>>3)] = value; break; + default: abort('invalid type for setValue: ' + type); + } +} + +/** @param {number} ptr + @param {string} type + @param {number|boolean=} noSafe */ +function getValue(ptr, type, noSafe) { + type = type || 'i8'; + if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit + switch(type) { + case 'i1': return HEAP8[((ptr)>>0)]; + case 'i8': return HEAP8[((ptr)>>0)]; + case 'i16': return HEAP16[((ptr)>>1)]; + case 'i32': return HEAP32[((ptr)>>2)]; + case 'i64': return HEAP32[((ptr)>>2)]; + case 'float': return HEAPF32[((ptr)>>2)]; + case 'double': return HEAPF64[((ptr)>>3)]; + default: abort('invalid type for getValue: ' + type); + } + return null; +} + + + + + + +// Wasm globals + +var wasmMemory; +var wasmTable; + + +//======================================== +// Runtime essentials +//======================================== + +// whether we are quitting the application. no code should run after this. +// set in exit() and abort() +var ABORT = false; + +// set by exit() and abort(). Passed to 'onExit' handler. +// NOTE: This is also used as the process return code code in shell environments +// but only when noExitRuntime is false. +var EXITSTATUS = 0; + +/** @type {function(*, string=)} */ +function assert(condition, text) { + if (!condition) { + abort('Assertion failed: ' + text); + } +} + +// Returns the C function with a specified identifier (for C++, you need to do manual name mangling) +function getCFunc(ident) { + var func = Module['_' + ident]; // closure exported function + assert(func, 'Cannot call unknown function ' + ident + ', make sure it is exported'); + return func; +} + +// C calling interface. +/** @param {string|null=} returnType + @param {Array=} argTypes + @param {Arguments|Array=} args + @param {Object=} opts */ +function ccall(ident, returnType, argTypes, args, opts) { + // For fast lookup of conversion functions + var toC = { + 'string': function(str) { + var ret = 0; + if (str !== null && str !== undefined && str !== 0) { // null string + // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0' + var len = (str.length << 2) + 1; + ret = stackAlloc(len); + stringToUTF8(str, ret, len); + } + return ret; + }, + 'array': function(arr) { + var ret = stackAlloc(arr.length); + writeArrayToMemory(arr, ret); + return ret; + } + }; + + function convertReturnValue(ret) { + if (returnType === 'string') return UTF8ToString(ret); + if (returnType === 'boolean') return Boolean(ret); + return ret; + } + + var func = getCFunc(ident); + var cArgs = []; + var stack = 0; + if (args) { + for (var i = 0; i < args.length; i++) { + var converter = toC[argTypes[i]]; + if (converter) { + if (stack === 0) stack = stackSave(); + cArgs[i] = converter(args[i]); + } else { + cArgs[i] = args[i]; + } + } + } + var ret = func.apply(null, cArgs); + + ret = convertReturnValue(ret); + if (stack !== 0) stackRestore(stack); + return ret; +} + +/** @param {string=} returnType + @param {Array=} argTypes + @param {Object=} opts */ +function cwrap(ident, returnType, argTypes, opts) { + argTypes = argTypes || []; + // When the function takes numbers and returns a number, we can just return + // the original function + var numericArgs = argTypes.every(function(type){ return type === 'number'}); + var numericRet = returnType !== 'string'; + if (numericRet && numericArgs && !opts) { + return getCFunc(ident); + } + return function() { + return ccall(ident, returnType, argTypes, arguments, opts); + } +} + + +var ALLOC_NORMAL = 0; // Tries to use _malloc() +var ALLOC_STACK = 1; // Lives for the duration of the current function call + +// allocate(): This is for internal use. You can use it yourself as well, but the interface +// is a little tricky (see docs right below). The reason is that it is optimized +// for multiple syntaxes to save space in generated code. So you should +// normally not use allocate(), and instead allocate memory using _malloc(), +// initialize it with setValue(), and so forth. +// @slab: An array of data. +// @allocator: How to allocate memory, see ALLOC_* +/** @type {function((Uint8Array|Array), number)} */ +function allocate(slab, allocator) { + var ret; + + if (allocator == ALLOC_STACK) { + ret = stackAlloc(slab.length); + } else { + ret = _malloc(slab.length); + } + + if (slab.subarray || slab.slice) { + HEAPU8.set(/** @type {!Uint8Array} */(slab), ret); + } else { + HEAPU8.set(new Uint8Array(slab), ret); + } + return ret; +} + + + + +// runtime_strings.js: Strings related runtime functions that are part of both MINIMAL_RUNTIME and regular runtime. + +// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the given array that contains uint8 values, returns +// a copy of that string as a Javascript String object. + +var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined; + +/** + * @param {number} idx + * @param {number=} maxBytesToRead + * @return {string} + */ +function UTF8ArrayToString(heap, idx, maxBytesToRead) { + var endIdx = idx + maxBytesToRead; + var endPtr = idx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. + // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. + // (As a tiny code save trick, compare endPtr against endIdx using a negation, so that undefined means Infinity) + while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr; + + if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) { + return UTF8Decoder.decode(heap.subarray(idx, endPtr)); + } else { + var str = ''; + // If building with TextDecoder, we have already computed the string length above, so test loop end condition against that + while (idx < endPtr) { + // For UTF8 byte structure, see: + // http://en.wikipedia.org/wiki/UTF-8#Description + // https://www.ietf.org/rfc/rfc2279.txt + // https://tools.ietf.org/html/rfc3629 + var u0 = heap[idx++]; + if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } + var u1 = heap[idx++] & 63; + if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } + var u2 = heap[idx++] & 63; + if ((u0 & 0xF0) == 0xE0) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heap[idx++] & 63); + } + + if (u0 < 0x10000) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } + } + } + return str; +} + +// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the emscripten HEAP, returns a +// copy of that string as a Javascript String object. +// maxBytesToRead: an optional length that specifies the maximum number of bytes to read. You can omit +// this parameter to scan the string until the first \0 byte. If maxBytesToRead is +// passed, and the string at [ptr, ptr+maxBytesToReadr[ contains a null byte in the +// middle, then the string will cut short at that byte index (i.e. maxBytesToRead will +// not produce a string of exact length [ptr, ptr+maxBytesToRead[) +// N.B. mixing frequent uses of UTF8ToString() with and without maxBytesToRead may +// throw JS JIT optimizations off, so it is worth to consider consistently using one +// style or the other. +/** + * @param {number} ptr + * @param {number=} maxBytesToRead + * @return {string} + */ +function UTF8ToString(ptr, maxBytesToRead) { + return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; +} + +// Copies the given Javascript String object 'str' to the given byte array at address 'outIdx', +// encoded in UTF8 form and null-terminated. The copy will require at most str.length*4+1 bytes of space in the HEAP. +// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// heap: the array to copy to. Each index in this array is assumed to be one 8-byte element. +// outIdx: The starting offset in the array to begin the copying. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. +// This count should include the null terminator, +// i.e. if maxBytesToWrite=1, only the null terminator will be written and nothing else. +// maxBytesToWrite=0 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { + if (!(maxBytesToWrite > 0)) // Parameter maxBytesToWrite is not optional. Negative values, 0, null, undefined and false each don't write out any bytes. + return 0; + + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) { + var u1 = str.charCodeAt(++i); + u = 0x10000 + ((u & 0x3FF) << 10) | (u1 & 0x3FF); + } + if (u <= 0x7F) { + if (outIdx >= endIdx) break; + heap[outIdx++] = u; + } else if (u <= 0x7FF) { + if (outIdx + 1 >= endIdx) break; + heap[outIdx++] = 0xC0 | (u >> 6); + heap[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0xFFFF) { + if (outIdx + 2 >= endIdx) break; + heap[outIdx++] = 0xE0 | (u >> 12); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + } else { + if (outIdx + 3 >= endIdx) break; + heap[outIdx++] = 0xF0 | (u >> 18); + heap[outIdx++] = 0x80 | ((u >> 12) & 63); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + } + } + // Null-terminate the pointer to the buffer. + heap[outIdx] = 0; + return outIdx - startIdx; +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF8 form. The copy will require at most str.length*4+1 bytes of space in the HEAP. +// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF8(str, outPtr, maxBytesToWrite) { + return stringToUTF8Array(str, HEAPU8,outPtr, maxBytesToWrite); +} + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte. +function lengthBytesUTF8(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); + if (u <= 0x7F) ++len; + else if (u <= 0x7FF) len += 2; + else if (u <= 0xFFFF) len += 3; + else len += 4; + } + return len; +} + + + + + +// runtime_strings_extra.js: Strings related runtime functions that are available only in regular runtime. + +// Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the emscripten HEAP, returns +// a copy of that string as a Javascript String object. + +function AsciiToString(ptr) { + var str = ''; + while (1) { + var ch = HEAPU8[((ptr++)>>0)]; + if (!ch) return str; + str += String.fromCharCode(ch); + } +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in ASCII form. The copy will require at most str.length+1 bytes of space in the HEAP. + +function stringToAscii(str, outPtr) { + return writeAsciiToMemory(str, outPtr, false); +} + +// Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns +// a copy of that string as a Javascript String object. + +var UTF16Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : undefined; + +function UTF16ToString(ptr, maxBytesToRead) { + var endPtr = ptr; + // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. + // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. + var idx = endPtr >> 1; + var maxIdx = idx + maxBytesToRead / 2; + // If maxBytesToRead is not passed explicitly, it will be undefined, and this + // will always evaluate to true. This saves on code size. + while (!(idx >= maxIdx) && HEAPU16[idx]) ++idx; + endPtr = idx << 1; + + if (endPtr - ptr > 32 && UTF16Decoder) { + return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr)); + } else { + var i = 0; + + var str = ''; + while (1) { + var codeUnit = HEAP16[(((ptr)+(i*2))>>1)]; + if (codeUnit == 0 || i == maxBytesToRead / 2) return str; + ++i; + // fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through. + str += String.fromCharCode(codeUnit); + } + } +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF16 form. The copy will require at most str.length*4+2 bytes of space in the HEAP. +// Use the function lengthBytesUTF16() to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// outPtr: Byte address in Emscripten HEAP where to write the string to. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null +// terminator, i.e. if maxBytesToWrite=2, only the null terminator will be written and nothing else. +// maxBytesToWrite<2 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF16(str, outPtr, maxBytesToWrite) { + // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. + if (maxBytesToWrite === undefined) { + maxBytesToWrite = 0x7FFFFFFF; + } + if (maxBytesToWrite < 2) return 0; + maxBytesToWrite -= 2; // Null terminator. + var startPtr = outPtr; + var numCharsToWrite = (maxBytesToWrite < str.length*2) ? (maxBytesToWrite / 2) : str.length; + for (var i = 0; i < numCharsToWrite; ++i) { + // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP. + var codeUnit = str.charCodeAt(i); // possibly a lead surrogate + HEAP16[((outPtr)>>1)] = codeUnit; + outPtr += 2; + } + // Null-terminate the pointer to the HEAP. + HEAP16[((outPtr)>>1)] = 0; + return outPtr - startPtr; +} + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. + +function lengthBytesUTF16(str) { + return str.length*2; +} + +function UTF32ToString(ptr, maxBytesToRead) { + var i = 0; + + var str = ''; + // If maxBytesToRead is not passed explicitly, it will be undefined, and this + // will always evaluate to true. This saves on code size. + while (!(i >= maxBytesToRead / 4)) { + var utf32 = HEAP32[(((ptr)+(i*4))>>2)]; + if (utf32 == 0) break; + ++i; + // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + if (utf32 >= 0x10000) { + var ch = utf32 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } else { + str += String.fromCharCode(utf32); + } + } + return str; +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF32 form. The copy will require at most str.length*4+4 bytes of space in the HEAP. +// Use the function lengthBytesUTF32() to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// outPtr: Byte address in Emscripten HEAP where to write the string to. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null +// terminator, i.e. if maxBytesToWrite=4, only the null terminator will be written and nothing else. +// maxBytesToWrite<4 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF32(str, outPtr, maxBytesToWrite) { + // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. + if (maxBytesToWrite === undefined) { + maxBytesToWrite = 0x7FFFFFFF; + } + if (maxBytesToWrite < 4) return 0; + var startPtr = outPtr; + var endPtr = startPtr + maxBytesToWrite - 4; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var codeUnit = str.charCodeAt(i); // possibly a lead surrogate + if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) { + var trailSurrogate = str.charCodeAt(++i); + codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF); + } + HEAP32[((outPtr)>>2)] = codeUnit; + outPtr += 4; + if (outPtr + 4 > endPtr) break; + } + // Null-terminate the pointer to the HEAP. + HEAP32[((outPtr)>>2)] = 0; + return outPtr - startPtr; +} + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. + +function lengthBytesUTF32(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var codeUnit = str.charCodeAt(i); + if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) ++i; // possibly a lead surrogate, so skip over the tail surrogate. + len += 4; + } + + return len; +} + +// Allocate heap space for a JS string, and write it there. +// It is the responsibility of the caller to free() that memory. +function allocateUTF8(str) { + var size = lengthBytesUTF8(str) + 1; + var ret = _malloc(size); + if (ret) stringToUTF8Array(str, HEAP8, ret, size); + return ret; +} + +// Allocate stack space for a JS string, and write it there. +function allocateUTF8OnStack(str) { + var size = lengthBytesUTF8(str) + 1; + var ret = stackAlloc(size); + stringToUTF8Array(str, HEAP8, ret, size); + return ret; +} + +// Deprecated: This function should not be called because it is unsafe and does not provide +// a maximum length limit of how many bytes it is allowed to write. Prefer calling the +// function stringToUTF8Array() instead, which takes in a maximum length that can be used +// to be secure from out of bounds writes. +/** @deprecated + @param {boolean=} dontAddNull */ +function writeStringToMemory(string, buffer, dontAddNull) { + warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); + + var /** @type {number} */ lastChar, /** @type {number} */ end; + if (dontAddNull) { + // stringToUTF8Array always appends null. If we don't want to do that, remember the + // character that existed at the location where the null will be placed, and restore + // that after the write (below). + end = buffer + lengthBytesUTF8(string); + lastChar = HEAP8[end]; + } + stringToUTF8(string, buffer, Infinity); + if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. +} + +function writeArrayToMemory(array, buffer) { + HEAP8.set(array, buffer); +} + +/** @param {boolean=} dontAddNull */ +function writeAsciiToMemory(str, buffer, dontAddNull) { + for (var i = 0; i < str.length; ++i) { + HEAP8[((buffer++)>>0)] = str.charCodeAt(i); + } + // Null-terminate the pointer to the HEAP. + if (!dontAddNull) HEAP8[((buffer)>>0)] = 0; +} + + + +// Memory management + +var PAGE_SIZE = 16384; +var WASM_PAGE_SIZE = 65536; + +function alignUp(x, multiple) { + if (x % multiple > 0) { + x += multiple - (x % multiple); + } + return x; +} + +var HEAP, +/** @type {ArrayBuffer} */ + buffer, +/** @type {Int8Array} */ + HEAP8, +/** @type {Uint8Array} */ + HEAPU8, +/** @type {Int16Array} */ + HEAP16, +/** @type {Uint16Array} */ + HEAPU16, +/** @type {Int32Array} */ + HEAP32, +/** @type {Uint32Array} */ + HEAPU32, +/** @type {Float32Array} */ + HEAPF32, +/** @type {Float64Array} */ + HEAPF64; + +function updateGlobalBufferAndViews(buf) { + buffer = buf; + Module['HEAP8'] = HEAP8 = new Int8Array(buf); + Module['HEAP16'] = HEAP16 = new Int16Array(buf); + Module['HEAP32'] = HEAP32 = new Int32Array(buf); + Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf); + Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf); + Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf); + Module['HEAPF32'] = HEAPF32 = new Float32Array(buf); + Module['HEAPF64'] = HEAPF64 = new Float64Array(buf); +} + +var STACK_BASE = 5270384, + STACKTOP = STACK_BASE, + STACK_MAX = 27504; + + + + +var TOTAL_STACK = 5242880; + +var INITIAL_INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216; + + + +// In non-standalone/normal mode, we create the memory here. + + + +// Create the main memory. (Note: this isn't used in STANDALONE_WASM mode since the wasm +// memory is created in the wasm, not in JS.) + + if (Module['wasmMemory']) { + wasmMemory = Module['wasmMemory']; + } else + { + wasmMemory = new WebAssembly.Memory({ + 'initial': INITIAL_INITIAL_MEMORY / WASM_PAGE_SIZE + , + 'maximum': INITIAL_INITIAL_MEMORY / WASM_PAGE_SIZE + }); + } + + +if (wasmMemory) { + buffer = wasmMemory.buffer; +} + +// If the user provides an incorrect length, just use that length instead rather than providing the user to +// specifically provide the memory length with Module['INITIAL_MEMORY']. +INITIAL_INITIAL_MEMORY = buffer.byteLength; +updateGlobalBufferAndViews(buffer); + + + + + + + + + + + + + + + + + + +var __ATPRERUN__ = []; // functions called before the runtime is initialized +var __ATINIT__ = []; // functions called during startup +var __ATMAIN__ = []; // functions called when main() is to be run +var __ATEXIT__ = []; // functions called during shutdown +var __ATPOSTRUN__ = []; // functions called after the main() is called + +var runtimeInitialized = false; +var runtimeExited = false; + +__ATINIT__.push({ func: function() { ___wasm_call_ctors() } }); + +function preRun() { + + if (Module['preRun']) { + if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; + while (Module['preRun'].length) { + addOnPreRun(Module['preRun'].shift()); + } + } + + callRuntimeCallbacks(__ATPRERUN__); +} + +function initRuntime() { + runtimeInitialized = true; + if (!Module["noFSInit"] && !FS.init.initialized) FS.init(); +TTY.init(); + callRuntimeCallbacks(__ATINIT__); +} + +function preMain() { + FS.ignorePermissions = false; + callRuntimeCallbacks(__ATMAIN__); +} + +function exitRuntime() { + runtimeExited = true; +} + +function postRun() { + + if (Module['postRun']) { + if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; + while (Module['postRun'].length) { + addOnPostRun(Module['postRun'].shift()); + } + } + + callRuntimeCallbacks(__ATPOSTRUN__); +} + +function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); +} + +function addOnInit(cb) { + __ATINIT__.unshift(cb); +} + +function addOnPreMain(cb) { + __ATMAIN__.unshift(cb); +} + +function addOnExit(cb) { +} + +function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); +} + + + + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc + + + + +// A counter of dependencies for calling run(). If we need to +// do asynchronous work before running, increment this and +// decrement it. Incrementing must happen in a place like +// Module.preRun (used by emcc to add file preloading). +// Note that you can add dependencies in preRun, even though +// it happens right before run - run will be postponed until +// the dependencies are met. +var runDependencies = 0; +var runDependencyWatcher = null; +var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled + +function getUniqueRunDependency(id) { + return id; +} + +function addRunDependency(id) { + runDependencies++; + + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } + +} + +function removeRunDependency(id) { + runDependencies--; + + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } + + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); // can add another dependenciesFulfilled + } + } +} + +Module["preloadedImages"] = {}; // maps url to image data +Module["preloadedAudios"] = {}; // maps url to audio data + +/** @param {string|number=} what */ +function abort(what) { + if (Module['onAbort']) { + Module['onAbort'](what); + } + + what += ''; + err(what); + + ABORT = true; + EXITSTATUS = 1; + + what = 'abort(' + what + '). Build with -s ASSERTIONS=1 for more info.'; + + // Use a wasm runtime error, because a JS error might be seen as a foreign + // exception, which means we'd run destructors on it. We need the error to + // simply make the program stop. + var e = new WebAssembly.RuntimeError(what); + + readyPromiseReject(e); + // Throw the error whether or not MODULARIZE is set because abort is used + // in code paths apart from instantiation where an exception is expected + // to be thrown when abort is called. + throw e; +} + +// {{MEM_INITIALIZER}} + + + + + + + + + + + +function hasPrefix(str, prefix) { + return String.prototype.startsWith ? + str.startsWith(prefix) : + str.indexOf(prefix) === 0; +} + +// Prefix of data URIs emitted by SINGLE_FILE and related options. +var dataURIPrefix = 'data:application/octet-stream;base64,'; + +// Indicates whether filename is a base64 data URI. +function isDataURI(filename) { + return hasPrefix(filename, dataURIPrefix); +} + +var fileURIPrefix = "file://"; + +// Indicates whether filename is delivered via file protocol (as opposed to http/https) +function isFileURI(filename) { + return hasPrefix(filename, fileURIPrefix); +} + + + + + +var wasmBinaryFile = 'libfibre-wasm.wasm'; +if (!isDataURI(wasmBinaryFile)) { + wasmBinaryFile = locateFile(wasmBinaryFile); +} + +function getBinary(file) { + try { + if (file == wasmBinaryFile && wasmBinary) { + return new Uint8Array(wasmBinary); + } + if (readBinary) { + return readBinary(file); + } else { + throw "both async and sync fetching of the wasm failed"; + } + } + catch (err) { + abort(err); + } +} + +function getBinaryPromise() { + // If we don't have the binary yet, try to to load it asynchronously. + // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url. + // See https://github.com/github/fetch/pull/92#issuecomment-140665932 + // Cordova or Electron apps are typically loaded from a file:// url. + // So use fetch if it is available and the url is not a file, otherwise fall back to XHR. + if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { + if (typeof fetch === 'function' + && !isFileURI(wasmBinaryFile) + ) { + return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { + if (!response['ok']) { + throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; + } + return response['arrayBuffer'](); + }).catch(function () { + return getBinary(wasmBinaryFile); + }); + } + else { + if (readAsync) { + // fetch is not available or url is file => try XHR (readAsync uses XHR internally) + return new Promise(function(resolve, reject) { + readAsync(wasmBinaryFile, function(response) { resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))) }, reject) + }); + } + } + } + + // Otherwise, getBinary should be able to get it synchronously + return Promise.resolve().then(function() { return getBinary(wasmBinaryFile); }); +} + + + +// Create the wasm instance. +// Receives the wasm imports, returns the exports. +function createWasm() { + // prepare imports + var info = { + 'env': asmLibraryArg, + 'wasi_snapshot_preview1': asmLibraryArg + }; + // Load the wasm module and create an instance of using native support in the JS engine. + // handle a generated wasm instance, receiving its exports and + // performing other necessary setup + /** @param {WebAssembly.Module=} module*/ + function receiveInstance(instance, module) { + var exports = instance.exports; + + + + + Module['asm'] = exports; + + wasmTable = Module['asm']['__indirect_function_table']; + + + removeRunDependency('wasm-instantiate'); + } + // we can't run yet (except in a pthread, where we have a custom sync instantiator) + addRunDependency('wasm-instantiate'); + + + function receiveInstantiatedSource(output) { + // 'output' is a WebAssemblyInstantiatedSource object which has both the module and instance. + // receiveInstance() will swap in the exports (to Module.asm) so they can be called + // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. + // When the regression is fixed, can restore the above USE_PTHREADS-enabled path. + receiveInstance(output['instance']); + } + + + function instantiateArrayBuffer(receiver) { + return getBinaryPromise().then(function(binary) { + return WebAssembly.instantiate(binary, info); + }).then(receiver, function(reason) { + err('failed to asynchronously prepare wasm: ' + reason); + + + abort(reason); + }); + } + + // Prefer streaming instantiation if available. + function instantiateAsync() { + if (!wasmBinary && + typeof WebAssembly.instantiateStreaming === 'function' && + !isDataURI(wasmBinaryFile) && + // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. + !isFileURI(wasmBinaryFile) && + typeof fetch === 'function') { + fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) { + var result = WebAssembly.instantiateStreaming(response, info); + return result.then(receiveInstantiatedSource, function(reason) { + // We expect the most common failure cause to be a bad MIME type for the binary, + // in which case falling back to ArrayBuffer instantiation should work. + err('wasm streaming compile failed: ' + reason); + err('falling back to ArrayBuffer instantiation'); + return instantiateArrayBuffer(receiveInstantiatedSource); + }); + }); + } else { + return instantiateArrayBuffer(receiveInstantiatedSource); + } + } + + // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback + // to manually instantiate the Wasm module themselves. This allows pages to run the instantiation parallel + // to any other async startup actions they are performing. + if (Module['instantiateWasm']) { + try { + var exports = Module['instantiateWasm'](info, receiveInstance); + return exports; + } catch(e) { + err('Module.instantiateWasm callback failed with error: ' + e); + return false; + } + } + + instantiateAsync(); + return {}; // no exports yet; we'll fill them in later +} + +// Globals used by JS i64 conversions (see makeSetValue) +var tempDouble; +var tempI64; + +// === Body === + +var ASM_CONSTS = { + +}; + + + + + + + function callRuntimeCallbacks(callbacks) { + while(callbacks.length > 0) { + var callback = callbacks.shift(); + if (typeof callback == 'function') { + callback(Module); // Pass the module as the first argument. + continue; + } + var func = callback.func; + if (typeof func === 'number') { + if (callback.arg === undefined) { + wasmTable.get(func)(); + } else { + wasmTable.get(func)(callback.arg); + } + } else { + func(callback.arg === undefined ? null : callback.arg); + } + } + } + + function demangle(func) { + return func; + } + + function demangleAll(text) { + var regex = + /\b_Z[\w\d_]+/g; + return text.replace(regex, + function(x) { + var y = demangle(x); + return x === y ? x : (y + ' [' + x + ']'); + }); + } + + function dynCallLegacy(sig, ptr, args) { + if (args && args.length) { + return Module['dynCall_' + sig].apply(null, [ptr].concat(args)); + } + return Module['dynCall_' + sig].call(null, ptr); + } + function dynCall(sig, ptr, args) { + // Without WASM_BIGINT support we cannot directly call function with i64 as + // part of thier signature, so we rely the dynCall functions generated by + // wasm-emscripten-finalize + if (sig.indexOf('j') != -1) { + return dynCallLegacy(sig, ptr, args); + } + + return wasmTable.get(ptr).apply(null, args) + } + + function jsStackTrace() { + var error = new Error(); + if (!error.stack) { + // IE10+ special cases: It does have callstack info, but it is only populated if an Error object is thrown, + // so try that as a special-case. + try { + throw new Error(); + } catch(e) { + error = e; + } + if (!error.stack) { + return '(no stack trace available)'; + } + } + return error.stack.toString(); + } + + function stackTrace() { + var js = jsStackTrace(); + if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace'](); + return demangleAll(js); + } + + var ExceptionInfoAttrs={DESTRUCTOR_OFFSET:0,REFCOUNT_OFFSET:4,TYPE_OFFSET:8,CAUGHT_OFFSET:12,RETHROWN_OFFSET:13,SIZE:16}; + function ___cxa_allocate_exception(size) { + // Thrown object is prepended by exception metadata block + return _malloc(size + ExceptionInfoAttrs.SIZE) + ExceptionInfoAttrs.SIZE; + } + + function _atexit(func, arg) { + } + function ___cxa_atexit(a0,a1 + ) { + return _atexit(a0,a1); + } + + var exceptionCaught= []; + + var exceptionLast=0; + function ___cxa_rethrow() { + var catchInfo = exceptionCaught.pop(); + if (!catchInfo) { + abort('no exception to throw'); + } + var info = catchInfo.get_exception_info(); + var ptr = catchInfo.get_base_ptr(); + if (!info.get_rethrown()) { + // Only pop if the corresponding push was through rethrow_primary_exception + exceptionCaught.push(catchInfo); + info.set_rethrown(true); + } else { + catchInfo.free(); + } + exceptionLast = ptr; + throw ptr; + } + + function ExceptionInfo(excPtr) { + this.excPtr = excPtr; + this.ptr = excPtr - ExceptionInfoAttrs.SIZE; + + this.set_type = function(type) { + HEAP32[(((this.ptr)+(ExceptionInfoAttrs.TYPE_OFFSET))>>2)] = type; + }; + + this.get_type = function() { + return HEAP32[(((this.ptr)+(ExceptionInfoAttrs.TYPE_OFFSET))>>2)]; + }; + + this.set_destructor = function(destructor) { + HEAP32[(((this.ptr)+(ExceptionInfoAttrs.DESTRUCTOR_OFFSET))>>2)] = destructor; + }; + + this.get_destructor = function() { + return HEAP32[(((this.ptr)+(ExceptionInfoAttrs.DESTRUCTOR_OFFSET))>>2)]; + }; + + this.set_refcount = function(refcount) { + HEAP32[(((this.ptr)+(ExceptionInfoAttrs.REFCOUNT_OFFSET))>>2)] = refcount; + }; + + this.set_caught = function (caught) { + caught = caught ? 1 : 0; + HEAP8[(((this.ptr)+(ExceptionInfoAttrs.CAUGHT_OFFSET))>>0)] = caught; + }; + + this.get_caught = function () { + return HEAP8[(((this.ptr)+(ExceptionInfoAttrs.CAUGHT_OFFSET))>>0)] != 0; + }; + + this.set_rethrown = function (rethrown) { + rethrown = rethrown ? 1 : 0; + HEAP8[(((this.ptr)+(ExceptionInfoAttrs.RETHROWN_OFFSET))>>0)] = rethrown; + }; + + this.get_rethrown = function () { + return HEAP8[(((this.ptr)+(ExceptionInfoAttrs.RETHROWN_OFFSET))>>0)] != 0; + }; + + // Initialize native structure fields. Should be called once after allocated. + this.init = function(type, destructor) { + this.set_type(type); + this.set_destructor(destructor); + this.set_refcount(0); + this.set_caught(false); + this.set_rethrown(false); + } + + this.add_ref = function() { + var value = HEAP32[(((this.ptr)+(ExceptionInfoAttrs.REFCOUNT_OFFSET))>>2)]; + HEAP32[(((this.ptr)+(ExceptionInfoAttrs.REFCOUNT_OFFSET))>>2)] = value + 1; + }; + + // Returns true if last reference released. + this.release_ref = function() { + var prev = HEAP32[(((this.ptr)+(ExceptionInfoAttrs.REFCOUNT_OFFSET))>>2)]; + HEAP32[(((this.ptr)+(ExceptionInfoAttrs.REFCOUNT_OFFSET))>>2)] = prev - 1; + return prev === 1; + }; + } + + function __ZSt18uncaught_exceptionv() { // std::uncaught_exception() + return __ZSt18uncaught_exceptionv.uncaught_exceptions > 0; + } + function ___cxa_throw(ptr, type, destructor) { + var info = new ExceptionInfo(ptr); + // Initialize ExceptionInfo content after it was allocated in __cxa_allocate_exception. + info.init(type, destructor); + exceptionLast = ptr; + if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) { + __ZSt18uncaught_exceptionv.uncaught_exceptions = 1; + } else { + __ZSt18uncaught_exceptionv.uncaught_exceptions++; + } + throw ptr; + } + + function _tzset() { + // TODO: Use (malleable) environment variables instead of system settings. + if (_tzset.called) return; + _tzset.called = true; + + var currentYear = new Date().getFullYear(); + var winter = new Date(currentYear, 0, 1); + var summer = new Date(currentYear, 6, 1); + var winterOffset = winter.getTimezoneOffset(); + var summerOffset = summer.getTimezoneOffset(); + + // Local standard timezone offset. Local standard time is not adjusted for daylight savings. + // This code uses the fact that getTimezoneOffset returns a greater value during Standard Time versus Daylight Saving Time (DST). + // Thus it determines the expected output during Standard Time, and it compares whether the output of the given date the same (Standard) or less (DST). + var stdTimezoneOffset = Math.max(winterOffset, summerOffset); + + // timezone is specified as seconds west of UTC ("The external variable + // `timezone` shall be set to the difference, in seconds, between + // Coordinated Universal Time (UTC) and local standard time."), the same + // as returned by stdTimezoneOffset. + // See http://pubs.opengroup.org/onlinepubs/009695399/functions/tzset.html + HEAP32[((__get_timezone())>>2)] = stdTimezoneOffset * 60; + + HEAP32[((__get_daylight())>>2)] = Number(winterOffset != summerOffset); + + function extractZone(date) { + var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/); + return match ? match[1] : "GMT"; + }; + var winterName = extractZone(winter); + var summerName = extractZone(summer); + var winterNamePtr = allocateUTF8(winterName); + var summerNamePtr = allocateUTF8(summerName); + if (summerOffset < winterOffset) { + // Northern hemisphere + HEAP32[((__get_tzname())>>2)] = winterNamePtr; + HEAP32[(((__get_tzname())+(4))>>2)] = summerNamePtr; + } else { + HEAP32[((__get_tzname())>>2)] = summerNamePtr; + HEAP32[(((__get_tzname())+(4))>>2)] = winterNamePtr; + } + } + function _localtime_r(time, tmPtr) { + _tzset(); + var date = new Date(HEAP32[((time)>>2)]*1000); + HEAP32[((tmPtr)>>2)] = date.getSeconds(); + HEAP32[(((tmPtr)+(4))>>2)] = date.getMinutes(); + HEAP32[(((tmPtr)+(8))>>2)] = date.getHours(); + HEAP32[(((tmPtr)+(12))>>2)] = date.getDate(); + HEAP32[(((tmPtr)+(16))>>2)] = date.getMonth(); + HEAP32[(((tmPtr)+(20))>>2)] = date.getFullYear()-1900; + HEAP32[(((tmPtr)+(24))>>2)] = date.getDay(); + + var start = new Date(date.getFullYear(), 0, 1); + var yday = ((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))|0; + HEAP32[(((tmPtr)+(28))>>2)] = yday; + HEAP32[(((tmPtr)+(36))>>2)] = -(date.getTimezoneOffset() * 60); + + // Attention: DST is in December in South, and some regions don't have DST at all. + var summerOffset = new Date(date.getFullYear(), 6, 1).getTimezoneOffset(); + var winterOffset = start.getTimezoneOffset(); + var dst = (summerOffset != winterOffset && date.getTimezoneOffset() == Math.min(winterOffset, summerOffset))|0; + HEAP32[(((tmPtr)+(32))>>2)] = dst; + + var zonePtr = HEAP32[(((__get_tzname())+(dst ? 4 : 0))>>2)]; + HEAP32[(((tmPtr)+(40))>>2)] = zonePtr; + + return tmPtr; + } + function ___localtime_r(a0,a1 + ) { + return _localtime_r(a0,a1); + } + + function _abort() { + abort(); + } + + var _emscripten_get_now;if (ENVIRONMENT_IS_NODE) { + _emscripten_get_now = function() { + var t = process['hrtime'](); + return t[0] * 1e3 + t[1] / 1e6; + }; + } else if (typeof dateNow !== 'undefined') { + _emscripten_get_now = dateNow; + } else _emscripten_get_now = function() { return performance.now(); } + ; + + var _emscripten_get_now_is_monotonic=true;; + + function setErrNo(value) { + HEAP32[((___errno_location())>>2)] = value; + return value; + } + function _clock_gettime(clk_id, tp) { + // int clock_gettime(clockid_t clk_id, struct timespec *tp); + var now; + if (clk_id === 0) { + now = Date.now(); + } else if ((clk_id === 1 || clk_id === 4) && _emscripten_get_now_is_monotonic) { + now = _emscripten_get_now(); + } else { + setErrNo(28); + return -1; + } + HEAP32[((tp)>>2)] = (now/1000)|0; // seconds + HEAP32[(((tp)+(4))>>2)] = ((now % 1000)*1000*1000)|0; // nanoseconds + return 0; + } + + function _emscripten_memcpy_big(dest, src, num) { + HEAPU8.copyWithin(dest, src, src + num); + } + + function _emscripten_get_heap_size() { + return HEAPU8.length; + } + + function abortOnCannotGrowMemory(requestedSize) { + abort('OOM'); + } + function _emscripten_resize_heap(requestedSize) { + abortOnCannotGrowMemory(requestedSize); + } + + var ENV={}; + + function getExecutableName() { + return thisProgram || './this.program'; + } + function getEnvStrings() { + if (!getEnvStrings.strings) { + // Default values. + // Browser language detection #8751 + var lang = ((typeof navigator === 'object' && navigator.languages && navigator.languages[0]) || 'C').replace('-', '_') + '.UTF-8'; + var env = { + 'USER': 'web_user', + 'LOGNAME': 'web_user', + 'PATH': '/', + 'PWD': '/', + 'HOME': '/home/web_user', + 'LANG': lang, + '_': getExecutableName() + }; + // Apply the user-provided values, if any. + for (var x in ENV) { + env[x] = ENV[x]; + } + var strings = []; + for (var x in env) { + strings.push(x + '=' + env[x]); + } + getEnvStrings.strings = strings; + } + return getEnvStrings.strings; + } + function _environ_get(__environ, environ_buf) { + var bufSize = 0; + getEnvStrings().forEach(function(string, i) { + var ptr = environ_buf + bufSize; + HEAP32[(((__environ)+(i * 4))>>2)]=ptr; + writeAsciiToMemory(string, ptr); + bufSize += string.length + 1; + }); + return 0; + } + + function _environ_sizes_get(penviron_count, penviron_buf_size) { + var strings = getEnvStrings(); + HEAP32[((penviron_count)>>2)]=strings.length; + var bufSize = 0; + strings.forEach(function(string) { + bufSize += string.length + 1; + }); + HEAP32[((penviron_buf_size)>>2)]=bufSize; + return 0; + } + + var PATH={splitPath:function(filename) { + var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; + return splitPathRe.exec(filename).slice(1); + },normalizeArray:function(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up; up--) { + parts.unshift('..'); + } + } + return parts; + },normalize:function(path) { + var isAbsolute = path.charAt(0) === '/', + trailingSlash = path.substr(-1) === '/'; + // Normalize the path + path = PATH.normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), !isAbsolute).join('/'); + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + return (isAbsolute ? '/' : '') + path; + },dirname:function(path) { + var result = PATH.splitPath(path), + root = result[0], + dir = result[1]; + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + return root + dir; + },basename:function(path) { + // EMSCRIPTEN return '/'' for '/', not an empty string + if (path === '/') return '/'; + path = PATH.normalize(path); + path = path.replace(/\/$/, ""); + var lastSlash = path.lastIndexOf('/'); + if (lastSlash === -1) return path; + return path.substr(lastSlash+1); + },extname:function(path) { + return PATH.splitPath(path)[3]; + },join:function() { + var paths = Array.prototype.slice.call(arguments, 0); + return PATH.normalize(paths.join('/')); + },join2:function(l, r) { + return PATH.normalize(l + '/' + r); + }}; + + function getRandomDevice() { + if (typeof crypto === 'object' && typeof crypto['getRandomValues'] === 'function') { + // for modern web browsers + var randomBuffer = new Uint8Array(1); + return function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; }; + } else + if (ENVIRONMENT_IS_NODE) { + // for nodejs with or without crypto support included + try { + var crypto_module = require('crypto'); + // nodejs has crypto support + return function() { return crypto_module['randomBytes'](1)[0]; }; + } catch (e) { + // nodejs doesn't have crypto support + } + } + // we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096 + return function() { abort("randomDevice"); }; + } + + var PATH_FS={resolve:function() { + var resolvedPath = '', + resolvedAbsolute = false; + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : FS.cwd(); + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + return ''; // an invalid portion invalidates the whole thing + } + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + resolvedPath = PATH.normalizeArray(resolvedPath.split('/').filter(function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; + },relative:function(from, to) { + from = PATH_FS.resolve(from).substr(1); + to = PATH_FS.resolve(to).substr(1); + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + return outputParts.join('/'); + }}; + + var TTY={ttys:[],init:function () { + // https://github.com/emscripten-core/emscripten/pull/1555 + // if (ENVIRONMENT_IS_NODE) { + // // currently, FS.init does not distinguish if process.stdin is a file or TTY + // // device, it always assumes it's a TTY device. because of this, we're forcing + // // process.stdin to UTF8 encoding to at least make stdin reading compatible + // // with text files until FS.init can be refactored. + // process['stdin']['setEncoding']('utf8'); + // } + },shutdown:function() { + // https://github.com/emscripten-core/emscripten/pull/1555 + // if (ENVIRONMENT_IS_NODE) { + // // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)? + // // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation + // // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists? + // // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle + // // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call + // process['stdin']['pause'](); + // } + },register:function(dev, ops) { + TTY.ttys[dev] = { input: [], output: [], ops: ops }; + FS.registerDevice(dev, TTY.stream_ops); + },stream_ops:{open:function(stream) { + var tty = TTY.ttys[stream.node.rdev]; + if (!tty) { + throw new FS.ErrnoError(43); + } + stream.tty = tty; + stream.seekable = false; + },close:function(stream) { + // flush any pending line data + stream.tty.ops.flush(stream.tty); + },flush:function(stream) { + stream.tty.ops.flush(stream.tty); + },read:function(stream, buffer, offset, length, pos /* ignored */) { + if (!stream.tty || !stream.tty.ops.get_char) { + throw new FS.ErrnoError(60); + } + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = stream.tty.ops.get_char(stream.tty); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(6); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset+i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + },write:function(stream, buffer, offset, length, pos) { + if (!stream.tty || !stream.tty.ops.put_char) { + throw new FS.ErrnoError(60); + } + try { + for (var i = 0; i < length; i++) { + stream.tty.ops.put_char(stream.tty, buffer[offset+i]); + } + } catch (e) { + throw new FS.ErrnoError(29); + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + }},default_tty_ops:{get_char:function(tty) { + if (!tty.input.length) { + var result = null; + if (ENVIRONMENT_IS_NODE) { + // we will read data by chunks of BUFSIZE + var BUFSIZE = 256; + var buf = Buffer.alloc ? Buffer.alloc(BUFSIZE) : new Buffer(BUFSIZE); + var bytesRead = 0; + + try { + bytesRead = nodeFS.readSync(process.stdin.fd, buf, 0, BUFSIZE, null); + } catch(e) { + // Cross-platform differences: on Windows, reading EOF throws an exception, but on other OSes, + // reading EOF returns 0. Uniformize behavior by treating the EOF exception to return 0. + if (e.toString().indexOf('EOF') != -1) bytesRead = 0; + else throw e; + } + + if (bytesRead > 0) { + result = buf.slice(0, bytesRead).toString('utf-8'); + } else { + result = null; + } + } else + if (typeof window != 'undefined' && + typeof window.prompt == 'function') { + // Browser. + result = window.prompt('Input: '); // returns null on cancel + if (result !== null) { + result += '\n'; + } + } else if (typeof readline == 'function') { + // Command line. + result = readline(); + if (result !== null) { + result += '\n'; + } + } + if (!result) { + return null; + } + tty.input = intArrayFromString(result, true); + } + return tty.input.shift(); + },put_char:function(tty, val) { + if (val === null || val === 10) { + out(UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } else { + if (val != 0) tty.output.push(val); // val == 0 would cut text output off in the middle. + } + },flush:function(tty) { + if (tty.output && tty.output.length > 0) { + out(UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } + }},default_tty1_ops:{put_char:function(tty, val) { + if (val === null || val === 10) { + err(UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } else { + if (val != 0) tty.output.push(val); + } + },flush:function(tty) { + if (tty.output && tty.output.length > 0) { + err(UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } + }}}; + + function mmapAlloc(size) { + var alignedSize = alignMemory(size, 16384); + var ptr = _malloc(alignedSize); + while (size < alignedSize) HEAP8[ptr + size++] = 0; + return ptr; + } + var MEMFS={ops_table:null,mount:function(mount) { + return MEMFS.createNode(null, '/', 16384 | 511 /* 0777 */, 0); + },createNode:function(parent, name, mode, dev) { + if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { + // no supported + throw new FS.ErrnoError(63); + } + if (!MEMFS.ops_table) { + MEMFS.ops_table = { + dir: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + lookup: MEMFS.node_ops.lookup, + mknod: MEMFS.node_ops.mknod, + rename: MEMFS.node_ops.rename, + unlink: MEMFS.node_ops.unlink, + rmdir: MEMFS.node_ops.rmdir, + readdir: MEMFS.node_ops.readdir, + symlink: MEMFS.node_ops.symlink + }, + stream: { + llseek: MEMFS.stream_ops.llseek + } + }, + file: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }, + stream: { + llseek: MEMFS.stream_ops.llseek, + read: MEMFS.stream_ops.read, + write: MEMFS.stream_ops.write, + allocate: MEMFS.stream_ops.allocate, + mmap: MEMFS.stream_ops.mmap, + msync: MEMFS.stream_ops.msync + } + }, + link: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + readlink: MEMFS.node_ops.readlink + }, + stream: {} + }, + chrdev: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }, + stream: FS.chrdev_stream_ops + } + }; + } + var node = FS.createNode(parent, name, mode, dev); + if (FS.isDir(node.mode)) { + node.node_ops = MEMFS.ops_table.dir.node; + node.stream_ops = MEMFS.ops_table.dir.stream; + node.contents = {}; + } else if (FS.isFile(node.mode)) { + node.node_ops = MEMFS.ops_table.file.node; + node.stream_ops = MEMFS.ops_table.file.stream; + node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity. + // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred + // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size + // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme. + node.contents = null; + } else if (FS.isLink(node.mode)) { + node.node_ops = MEMFS.ops_table.link.node; + node.stream_ops = MEMFS.ops_table.link.stream; + } else if (FS.isChrdev(node.mode)) { + node.node_ops = MEMFS.ops_table.chrdev.node; + node.stream_ops = MEMFS.ops_table.chrdev.stream; + } + node.timestamp = Date.now(); + // add the new node to the parent + if (parent) { + parent.contents[name] = node; + parent.timestamp = node.timestamp; + } + return node; + },getFileDataAsTypedArray:function(node) { + if (!node.contents) return new Uint8Array(0); + if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. + return new Uint8Array(node.contents); + },expandFileStorage:function(node, newCapacity) { + var prevCapacity = node.contents ? node.contents.length : 0; + if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough. + // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity. + // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to + // avoid overshooting the allocation cap by a very large margin. + var CAPACITY_DOUBLING_MAX = 1024 * 1024; + newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0); + if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. + var oldContents = node.contents; + node.contents = new Uint8Array(newCapacity); // Allocate new storage. + if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage. + },resizeFileStorage:function(node, newSize) { + if (node.usedBytes == newSize) return; + if (newSize == 0) { + node.contents = null; // Fully decommit when requesting a resize to zero. + node.usedBytes = 0; + } else { + var oldContents = node.contents; + node.contents = new Uint8Array(newSize); // Allocate new storage. + if (oldContents) { + node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage. + } + node.usedBytes = newSize; + } + },node_ops:{getattr:function(node) { + var attr = {}; + // device numbers reuse inode numbers. + attr.dev = FS.isChrdev(node.mode) ? node.id : 1; + attr.ino = node.id; + attr.mode = node.mode; + attr.nlink = 1; + attr.uid = 0; + attr.gid = 0; + attr.rdev = node.rdev; + if (FS.isDir(node.mode)) { + attr.size = 4096; + } else if (FS.isFile(node.mode)) { + attr.size = node.usedBytes; + } else if (FS.isLink(node.mode)) { + attr.size = node.link.length; + } else { + attr.size = 0; + } + attr.atime = new Date(node.timestamp); + attr.mtime = new Date(node.timestamp); + attr.ctime = new Date(node.timestamp); + // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), + // but this is not required by the standard. + attr.blksize = 4096; + attr.blocks = Math.ceil(attr.size / attr.blksize); + return attr; + },setattr:function(node, attr) { + if (attr.mode !== undefined) { + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + node.timestamp = attr.timestamp; + } + if (attr.size !== undefined) { + MEMFS.resizeFileStorage(node, attr.size); + } + },lookup:function(parent, name) { + throw FS.genericErrors[44]; + },mknod:function(parent, name, mode, dev) { + return MEMFS.createNode(parent, name, mode, dev); + },rename:function(old_node, new_dir, new_name) { + // if we're overwriting a directory at new_name, make sure it's empty. + if (FS.isDir(old_node.mode)) { + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) { + } + if (new_node) { + for (var i in new_node.contents) { + throw new FS.ErrnoError(55); + } + } + } + // do the internal rewiring + delete old_node.parent.contents[old_node.name]; + old_node.parent.timestamp = Date.now() + old_node.name = new_name; + new_dir.contents[new_name] = old_node; + new_dir.timestamp = old_node.parent.timestamp; + old_node.parent = new_dir; + },unlink:function(parent, name) { + delete parent.contents[name]; + parent.timestamp = Date.now(); + },rmdir:function(parent, name) { + var node = FS.lookupNode(parent, name); + for (var i in node.contents) { + throw new FS.ErrnoError(55); + } + delete parent.contents[name]; + parent.timestamp = Date.now(); + },readdir:function(node) { + var entries = ['.', '..']; + for (var key in node.contents) { + if (!node.contents.hasOwnProperty(key)) { + continue; + } + entries.push(key); + } + return entries; + },symlink:function(parent, newname, oldpath) { + var node = MEMFS.createNode(parent, newname, 511 /* 0777 */ | 40960, 0); + node.link = oldpath; + return node; + },readlink:function(node) { + if (!FS.isLink(node.mode)) { + throw new FS.ErrnoError(28); + } + return node.link; + }},stream_ops:{read:function(stream, buffer, offset, length, position) { + var contents = stream.node.contents; + if (position >= stream.node.usedBytes) return 0; + var size = Math.min(stream.node.usedBytes - position, length); + if (size > 8 && contents.subarray) { // non-trivial, and typed array + buffer.set(contents.subarray(position, position + size), offset); + } else { + for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i]; + } + return size; + },write:function(stream, buffer, offset, length, position, canOwn) { + + if (!length) return 0; + var node = stream.node; + node.timestamp = Date.now(); + + if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array? + if (canOwn) { + node.contents = buffer.subarray(offset, offset + length); + node.usedBytes = length; + return length; + } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. + node.contents = buffer.slice(offset, offset + length); + node.usedBytes = length; + return length; + } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file? + node.contents.set(buffer.subarray(offset, offset + length), position); + return length; + } + } + + // Appending to an existing file and we need to reallocate, or source data did not come as a typed array. + MEMFS.expandFileStorage(node, position+length); + if (node.contents.subarray && buffer.subarray) { + // Use typed array write which is available. + node.contents.set(buffer.subarray(offset, offset + length), position); + } else { + for (var i = 0; i < length; i++) { + node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not. + } + } + node.usedBytes = Math.max(node.usedBytes, position + length); + return length; + },llseek:function(stream, offset, whence) { + var position = offset; + if (whence === 1) { + position += stream.position; + } else if (whence === 2) { + if (FS.isFile(stream.node.mode)) { + position += stream.node.usedBytes; + } + } + if (position < 0) { + throw new FS.ErrnoError(28); + } + return position; + },allocate:function(stream, offset, length) { + MEMFS.expandFileStorage(stream.node, offset + length); + stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length); + },mmap:function(stream, address, length, position, prot, flags) { + if (address !== 0) { + // We don't currently support location hints for the address of the mapping + throw new FS.ErrnoError(28); + } + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + var ptr; + var allocated; + var contents = stream.node.contents; + // Only make a new copy when MAP_PRIVATE is specified. + if (!(flags & 2) && contents.buffer === buffer) { + // We can't emulate MAP_SHARED when the file is not backed by the buffer + // we're mapping to (e.g. the HEAP buffer). + allocated = false; + ptr = contents.byteOffset; + } else { + // Try to avoid unnecessary slices. + if (position > 0 || position + length < contents.length) { + if (contents.subarray) { + contents = contents.subarray(position, position + length); + } else { + contents = Array.prototype.slice.call(contents, position, position + length); + } + } + allocated = true; + ptr = mmapAlloc(length); + if (!ptr) { + throw new FS.ErrnoError(48); + } + HEAP8.set(contents, ptr); + } + return { ptr: ptr, allocated: allocated }; + },msync:function(stream, buffer, offset, length, mmapFlags) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + if (mmapFlags & 2) { + // MAP_PRIVATE calls need not to be synced back to underlying fs + return 0; + } + + var bytesWritten = MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); + // should we check if bytesWritten and length are the same? + return 0; + }}}; + var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,trackingDelegate:{},tracking:{openFlags:{READ:1,WRITE:2}},ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,handleFSError:function(e) { + if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + stackTrace(); + return setErrNo(e.errno); + },lookupPath:function(path, opts) { + path = PATH_FS.resolve(FS.cwd(), path); + opts = opts || {}; + + if (!path) return { path: '', node: null }; + + var defaults = { + follow_mount: true, + recurse_count: 0 + }; + for (var key in defaults) { + if (opts[key] === undefined) { + opts[key] = defaults[key]; + } + } + + if (opts.recurse_count > 8) { // max recursive lookup of 8 + throw new FS.ErrnoError(32); + } + + // split the path + var parts = PATH.normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), false); + + // start at the root + var current = FS.root; + var current_path = '/'; + + for (var i = 0; i < parts.length; i++) { + var islast = (i === parts.length-1); + if (islast && opts.parent) { + // stop resolving + break; + } + + current = FS.lookupNode(current, parts[i]); + current_path = PATH.join2(current_path, parts[i]); + + // jump to the mount's root node if this is a mountpoint + if (FS.isMountpoint(current)) { + if (!islast || (islast && opts.follow_mount)) { + current = current.mounted.root; + } + } + + // by default, lookupPath will not follow a symlink if it is the final path component. + // setting opts.follow = true will override this behavior. + if (!islast || opts.follow) { + var count = 0; + while (FS.isLink(current.mode)) { + var link = FS.readlink(current_path); + current_path = PATH_FS.resolve(PATH.dirname(current_path), link); + + var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count }); + current = lookup.node; + + if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX). + throw new FS.ErrnoError(32); + } + } + } + } + + return { path: current_path, node: current }; + },getPath:function(node) { + var path; + while (true) { + if (FS.isRoot(node)) { + var mount = node.mount.mountpoint; + if (!path) return mount; + return mount[mount.length-1] !== '/' ? mount + '/' + path : mount + path; + } + path = path ? node.name + '/' + path : node.name; + node = node.parent; + } + },hashName:function(parentid, name) { + var hash = 0; + + + for (var i = 0; i < name.length; i++) { + hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; + } + return ((parentid + hash) >>> 0) % FS.nameTable.length; + },hashAddNode:function(node) { + var hash = FS.hashName(node.parent.id, node.name); + node.name_next = FS.nameTable[hash]; + FS.nameTable[hash] = node; + },hashRemoveNode:function(node) { + var hash = FS.hashName(node.parent.id, node.name); + if (FS.nameTable[hash] === node) { + FS.nameTable[hash] = node.name_next; + } else { + var current = FS.nameTable[hash]; + while (current) { + if (current.name_next === node) { + current.name_next = node.name_next; + break; + } + current = current.name_next; + } + } + },lookupNode:function(parent, name) { + var errCode = FS.mayLookup(parent); + if (errCode) { + throw new FS.ErrnoError(errCode, parent); + } + var hash = FS.hashName(parent.id, name); + for (var node = FS.nameTable[hash]; node; node = node.name_next) { + var nodeName = node.name; + if (node.parent.id === parent.id && nodeName === name) { + return node; + } + } + // if we failed to find it in the cache, call into the VFS + return FS.lookup(parent, name); + },createNode:function(parent, name, mode, rdev) { + var node = new FS.FSNode(parent, name, mode, rdev); + + FS.hashAddNode(node); + + return node; + },destroyNode:function(node) { + FS.hashRemoveNode(node); + },isRoot:function(node) { + return node === node.parent; + },isMountpoint:function(node) { + return !!node.mounted; + },isFile:function(mode) { + return (mode & 61440) === 32768; + },isDir:function(mode) { + return (mode & 61440) === 16384; + },isLink:function(mode) { + return (mode & 61440) === 40960; + },isChrdev:function(mode) { + return (mode & 61440) === 8192; + },isBlkdev:function(mode) { + return (mode & 61440) === 24576; + },isFIFO:function(mode) { + return (mode & 61440) === 4096; + },isSocket:function(mode) { + return (mode & 49152) === 49152; + },flagModes:{"r":0,"rs":1052672,"r+":2,"w":577,"wx":705,"xw":705,"w+":578,"wx+":706,"xw+":706,"a":1089,"ax":1217,"xa":1217,"a+":1090,"ax+":1218,"xa+":1218},modeStringToFlags:function(str) { + var flags = FS.flagModes[str]; + if (typeof flags === 'undefined') { + throw new Error('Unknown file open mode: ' + str); + } + return flags; + },flagsToPermissionString:function(flag) { + var perms = ['r', 'w', 'rw'][flag & 3]; + if ((flag & 512)) { + perms += 'w'; + } + return perms; + },nodePermissions:function(node, perms) { + if (FS.ignorePermissions) { + return 0; + } + // return 0 if any user, group or owner bits are set. + if (perms.indexOf('r') !== -1 && !(node.mode & 292)) { + return 2; + } else if (perms.indexOf('w') !== -1 && !(node.mode & 146)) { + return 2; + } else if (perms.indexOf('x') !== -1 && !(node.mode & 73)) { + return 2; + } + return 0; + },mayLookup:function(dir) { + var errCode = FS.nodePermissions(dir, 'x'); + if (errCode) return errCode; + if (!dir.node_ops.lookup) return 2; + return 0; + },mayCreate:function(dir, name) { + try { + var node = FS.lookupNode(dir, name); + return 20; + } catch (e) { + } + return FS.nodePermissions(dir, 'wx'); + },mayDelete:function(dir, name, isdir) { + var node; + try { + node = FS.lookupNode(dir, name); + } catch (e) { + return e.errno; + } + var errCode = FS.nodePermissions(dir, 'wx'); + if (errCode) { + return errCode; + } + if (isdir) { + if (!FS.isDir(node.mode)) { + return 54; + } + if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { + return 10; + } + } else { + if (FS.isDir(node.mode)) { + return 31; + } + } + return 0; + },mayOpen:function(node, flags) { + if (!node) { + return 44; + } + if (FS.isLink(node.mode)) { + return 32; + } else if (FS.isDir(node.mode)) { + if (FS.flagsToPermissionString(flags) !== 'r' || // opening for write + (flags & 512)) { // TODO: check for O_SEARCH? (== search for dir only) + return 31; + } + } + return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + },MAX_OPEN_FDS:4096,nextfd:function(fd_start, fd_end) { + fd_start = fd_start || 0; + fd_end = fd_end || FS.MAX_OPEN_FDS; + for (var fd = fd_start; fd <= fd_end; fd++) { + if (!FS.streams[fd]) { + return fd; + } + } + throw new FS.ErrnoError(33); + },getStream:function(fd) { + return FS.streams[fd]; + },createStream:function(stream, fd_start, fd_end) { + if (!FS.FSStream) { + FS.FSStream = /** @constructor */ function(){}; + FS.FSStream.prototype = { + object: { + get: function() { return this.node; }, + set: function(val) { this.node = val; } + }, + isRead: { + get: function() { return (this.flags & 2097155) !== 1; } + }, + isWrite: { + get: function() { return (this.flags & 2097155) !== 0; } + }, + isAppend: { + get: function() { return (this.flags & 1024); } + } + }; + } + // clone it, so we can return an instance of FSStream + var newStream = new FS.FSStream(); + for (var p in stream) { + newStream[p] = stream[p]; + } + stream = newStream; + var fd = FS.nextfd(fd_start, fd_end); + stream.fd = fd; + FS.streams[fd] = stream; + return stream; + },closeStream:function(fd) { + FS.streams[fd] = null; + },chrdev_stream_ops:{open:function(stream) { + var device = FS.getDevice(stream.node.rdev); + // override node's stream ops with the device's + stream.stream_ops = device.stream_ops; + // forward the open call + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); + } + },llseek:function() { + throw new FS.ErrnoError(70); + }},major:function(dev) { + return ((dev) >> 8); + },minor:function(dev) { + return ((dev) & 0xff); + },makedev:function(ma, mi) { + return ((ma) << 8 | (mi)); + },registerDevice:function(dev, ops) { + FS.devices[dev] = { stream_ops: ops }; + },getDevice:function(dev) { + return FS.devices[dev]; + },getMounts:function(mount) { + var mounts = []; + var check = [mount]; + + while (check.length) { + var m = check.pop(); + + mounts.push(m); + + check.push.apply(check, m.mounts); + } + + return mounts; + },syncfs:function(populate, callback) { + if (typeof(populate) === 'function') { + callback = populate; + populate = false; + } + + FS.syncFSRequests++; + + if (FS.syncFSRequests > 1) { + err('warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work'); + } + + var mounts = FS.getMounts(FS.root.mount); + var completed = 0; + + function doCallback(errCode) { + FS.syncFSRequests--; + return callback(errCode); + } + + function done(errCode) { + if (errCode) { + if (!done.errored) { + done.errored = true; + return doCallback(errCode); + } + return; + } + if (++completed >= mounts.length) { + doCallback(null); + } + }; + + // sync all mounts + mounts.forEach(function (mount) { + if (!mount.type.syncfs) { + return done(null); + } + mount.type.syncfs(mount, populate, done); + }); + },mount:function(type, opts, mountpoint) { + var root = mountpoint === '/'; + var pseudo = !mountpoint; + var node; + + if (root && FS.root) { + throw new FS.ErrnoError(10); + } else if (!root && !pseudo) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + + mountpoint = lookup.path; // use the absolute path + node = lookup.node; + + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(10); + } + + if (!FS.isDir(node.mode)) { + throw new FS.ErrnoError(54); + } + } + + var mount = { + type: type, + opts: opts, + mountpoint: mountpoint, + mounts: [] + }; + + // create a root node for the fs + var mountRoot = type.mount(mount); + mountRoot.mount = mount; + mount.root = mountRoot; + + if (root) { + FS.root = mountRoot; + } else if (node) { + // set as a mountpoint + node.mounted = mount; + + // add the new mount to the current mount's children + if (node.mount) { + node.mount.mounts.push(mount); + } + } + + return mountRoot; + },unmount:function (mountpoint) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + + if (!FS.isMountpoint(lookup.node)) { + throw new FS.ErrnoError(28); + } + + // destroy the nodes for this mount, and all its child mounts + var node = lookup.node; + var mount = node.mounted; + var mounts = FS.getMounts(mount); + + Object.keys(FS.nameTable).forEach(function (hash) { + var current = FS.nameTable[hash]; + + while (current) { + var next = current.name_next; + + if (mounts.indexOf(current.mount) !== -1) { + FS.destroyNode(current); + } + + current = next; + } + }); + + // no longer a mountpoint + node.mounted = null; + + // remove this mount from the child mounts + var idx = node.mount.mounts.indexOf(mount); + node.mount.mounts.splice(idx, 1); + },lookup:function(parent, name) { + return parent.node_ops.lookup(parent, name); + },mknod:function(path, mode, dev) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + if (!name || name === '.' || name === '..') { + throw new FS.ErrnoError(28); + } + var errCode = FS.mayCreate(parent, name); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.mknod) { + throw new FS.ErrnoError(63); + } + return parent.node_ops.mknod(parent, name, mode, dev); + },create:function(path, mode) { + mode = mode !== undefined ? mode : 438 /* 0666 */; + mode &= 4095; + mode |= 32768; + return FS.mknod(path, mode, 0); + },mkdir:function(path, mode) { + mode = mode !== undefined ? mode : 511 /* 0777 */; + mode &= 511 | 512; + mode |= 16384; + return FS.mknod(path, mode, 0); + },mkdirTree:function(path, mode) { + var dirs = path.split('/'); + var d = ''; + for (var i = 0; i < dirs.length; ++i) { + if (!dirs[i]) continue; + d += '/' + dirs[i]; + try { + FS.mkdir(d, mode); + } catch(e) { + if (e.errno != 20) throw e; + } + } + },mkdev:function(path, mode, dev) { + if (typeof(dev) === 'undefined') { + dev = mode; + mode = 438 /* 0666 */; + } + mode |= 8192; + return FS.mknod(path, mode, dev); + },symlink:function(oldpath, newpath) { + if (!PATH_FS.resolve(oldpath)) { + throw new FS.ErrnoError(44); + } + var lookup = FS.lookupPath(newpath, { parent: true }); + var parent = lookup.node; + if (!parent) { + throw new FS.ErrnoError(44); + } + var newname = PATH.basename(newpath); + var errCode = FS.mayCreate(parent, newname); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.symlink) { + throw new FS.ErrnoError(63); + } + return parent.node_ops.symlink(parent, newname, oldpath); + },rename:function(old_path, new_path) { + var old_dirname = PATH.dirname(old_path); + var new_dirname = PATH.dirname(new_path); + var old_name = PATH.basename(old_path); + var new_name = PATH.basename(new_path); + // parents must exist + var lookup, old_dir, new_dir; + + // let the errors from non existant directories percolate up + lookup = FS.lookupPath(old_path, { parent: true }); + old_dir = lookup.node; + lookup = FS.lookupPath(new_path, { parent: true }); + new_dir = lookup.node; + + if (!old_dir || !new_dir) throw new FS.ErrnoError(44); + // need to be part of the same mount + if (old_dir.mount !== new_dir.mount) { + throw new FS.ErrnoError(75); + } + // source must exist + var old_node = FS.lookupNode(old_dir, old_name); + // old path should not be an ancestor of the new path + var relative = PATH_FS.relative(old_path, new_dirname); + if (relative.charAt(0) !== '.') { + throw new FS.ErrnoError(28); + } + // new path should not be an ancestor of the old path + relative = PATH_FS.relative(new_path, old_dirname); + if (relative.charAt(0) !== '.') { + throw new FS.ErrnoError(55); + } + // see if the new path already exists + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) { + // not fatal + } + // early out if nothing needs to change + if (old_node === new_node) { + return; + } + // we'll need to delete the old entry + var isdir = FS.isDir(old_node.mode); + var errCode = FS.mayDelete(old_dir, old_name, isdir); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + // need delete permissions if we'll be overwriting. + // need create permissions if new doesn't already exist. + errCode = new_node ? + FS.mayDelete(new_dir, new_name, isdir) : + FS.mayCreate(new_dir, new_name); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!old_dir.node_ops.rename) { + throw new FS.ErrnoError(63); + } + if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { + throw new FS.ErrnoError(10); + } + // if we are going to change the parent, check write permissions + if (new_dir !== old_dir) { + errCode = FS.nodePermissions(old_dir, 'w'); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + } + try { + if (FS.trackingDelegate['willMovePath']) { + FS.trackingDelegate['willMovePath'](old_path, new_path); + } + } catch(e) { + err("FS.trackingDelegate['willMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message); + } + // remove the node from the lookup hash + FS.hashRemoveNode(old_node); + // do the underlying fs rename + try { + old_dir.node_ops.rename(old_node, new_dir, new_name); + } catch (e) { + throw e; + } finally { + // add the node back to the hash (in case node_ops.rename + // changed its name) + FS.hashAddNode(old_node); + } + try { + if (FS.trackingDelegate['onMovePath']) FS.trackingDelegate['onMovePath'](old_path, new_path); + } catch(e) { + err("FS.trackingDelegate['onMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message); + } + },rmdir:function(path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var errCode = FS.mayDelete(parent, name, true); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.rmdir) { + throw new FS.ErrnoError(63); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(10); + } + try { + if (FS.trackingDelegate['willDeletePath']) { + FS.trackingDelegate['willDeletePath'](path); + } + } catch(e) { + err("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message); + } + parent.node_ops.rmdir(parent, name); + FS.destroyNode(node); + try { + if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path); + } catch(e) { + err("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message); + } + },readdir:function(path) { + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + if (!node.node_ops.readdir) { + throw new FS.ErrnoError(54); + } + return node.node_ops.readdir(node); + },unlink:function(path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var errCode = FS.mayDelete(parent, name, false); + if (errCode) { + // According to POSIX, we should map EISDIR to EPERM, but + // we instead do what Linux does (and we must, as we use + // the musl linux libc). + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.unlink) { + throw new FS.ErrnoError(63); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(10); + } + try { + if (FS.trackingDelegate['willDeletePath']) { + FS.trackingDelegate['willDeletePath'](path); + } + } catch(e) { + err("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message); + } + parent.node_ops.unlink(parent, name); + FS.destroyNode(node); + try { + if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path); + } catch(e) { + err("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message); + } + },readlink:function(path) { + var lookup = FS.lookupPath(path); + var link = lookup.node; + if (!link) { + throw new FS.ErrnoError(44); + } + if (!link.node_ops.readlink) { + throw new FS.ErrnoError(28); + } + return PATH_FS.resolve(FS.getPath(link.parent), link.node_ops.readlink(link)); + },stat:function(path, dontFollow) { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + var node = lookup.node; + if (!node) { + throw new FS.ErrnoError(44); + } + if (!node.node_ops.getattr) { + throw new FS.ErrnoError(63); + } + return node.node_ops.getattr(node); + },lstat:function(path) { + return FS.stat(path, true); + },chmod:function(path, mode, dontFollow) { + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(63); + } + node.node_ops.setattr(node, { + mode: (mode & 4095) | (node.mode & ~4095), + timestamp: Date.now() + }); + },lchmod:function(path, mode) { + FS.chmod(path, mode, true); + },fchmod:function(fd, mode) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(8); + } + FS.chmod(stream.node, mode); + },chown:function(path, uid, gid, dontFollow) { + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(63); + } + node.node_ops.setattr(node, { + timestamp: Date.now() + // we ignore the uid / gid for now + }); + },lchown:function(path, uid, gid) { + FS.chown(path, uid, gid, true); + },fchown:function(fd, uid, gid) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(8); + } + FS.chown(stream.node, uid, gid); + },truncate:function(path, len) { + if (len < 0) { + throw new FS.ErrnoError(28); + } + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: true }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(63); + } + if (FS.isDir(node.mode)) { + throw new FS.ErrnoError(31); + } + if (!FS.isFile(node.mode)) { + throw new FS.ErrnoError(28); + } + var errCode = FS.nodePermissions(node, 'w'); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + node.node_ops.setattr(node, { + size: len, + timestamp: Date.now() + }); + },ftruncate:function(fd, len) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(8); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(28); + } + FS.truncate(stream.node, len); + },utime:function(path, atime, mtime) { + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + node.node_ops.setattr(node, { + timestamp: Math.max(atime, mtime) + }); + },open:function(path, flags, mode, fd_start, fd_end) { + if (path === "") { + throw new FS.ErrnoError(44); + } + flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; + mode = typeof mode === 'undefined' ? 438 /* 0666 */ : mode; + if ((flags & 64)) { + mode = (mode & 4095) | 32768; + } else { + mode = 0; + } + var node; + if (typeof path === 'object') { + node = path; + } else { + path = PATH.normalize(path); + try { + var lookup = FS.lookupPath(path, { + follow: !(flags & 131072) + }); + node = lookup.node; + } catch (e) { + // ignore + } + } + // perhaps we need to create the node + var created = false; + if ((flags & 64)) { + if (node) { + // if O_CREAT and O_EXCL are set, error out if the node already exists + if ((flags & 128)) { + throw new FS.ErrnoError(20); + } + } else { + // node doesn't exist, try to create it + node = FS.mknod(path, mode, 0); + created = true; + } + } + if (!node) { + throw new FS.ErrnoError(44); + } + // can't truncate a device + if (FS.isChrdev(node.mode)) { + flags &= ~512; + } + // if asked only for a directory, then this must be one + if ((flags & 65536) && !FS.isDir(node.mode)) { + throw new FS.ErrnoError(54); + } + // check permissions, if this is not a file we just created now (it is ok to + // create and write to a file with read-only permissions; it is read-only + // for later use) + if (!created) { + var errCode = FS.mayOpen(node, flags); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + } + // do truncation if necessary + if ((flags & 512)) { + FS.truncate(node, 0); + } + // we've already handled these, don't pass down to the underlying vfs + flags &= ~(128 | 512 | 131072); + + // register the stream with the filesystem + var stream = FS.createStream({ + node: node, + path: FS.getPath(node), // we want the absolute path to the node + flags: flags, + seekable: true, + position: 0, + stream_ops: node.stream_ops, + // used by the file family libc calls (fopen, fwrite, ferror, etc.) + ungotten: [], + error: false + }, fd_start, fd_end); + // call the new stream's open function + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); + } + if (Module['logReadFiles'] && !(flags & 1)) { + if (!FS.readFiles) FS.readFiles = {}; + if (!(path in FS.readFiles)) { + FS.readFiles[path] = 1; + err("FS.trackingDelegate error on read file: " + path); + } + } + try { + if (FS.trackingDelegate['onOpenFile']) { + var trackingFlags = 0; + if ((flags & 2097155) !== 1) { + trackingFlags |= FS.tracking.openFlags.READ; + } + if ((flags & 2097155) !== 0) { + trackingFlags |= FS.tracking.openFlags.WRITE; + } + FS.trackingDelegate['onOpenFile'](path, trackingFlags); + } + } catch(e) { + err("FS.trackingDelegate['onOpenFile']('"+path+"', flags) threw an exception: " + e.message); + } + return stream; + },close:function(stream) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if (stream.getdents) stream.getdents = null; // free readdir state + try { + if (stream.stream_ops.close) { + stream.stream_ops.close(stream); + } + } catch (e) { + throw e; + } finally { + FS.closeStream(stream.fd); + } + stream.fd = null; + },isClosed:function(stream) { + return stream.fd === null; + },llseek:function(stream, offset, whence) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if (!stream.seekable || !stream.stream_ops.llseek) { + throw new FS.ErrnoError(70); + } + if (whence != 0 && whence != 1 && whence != 2) { + throw new FS.ErrnoError(28); + } + stream.position = stream.stream_ops.llseek(stream, offset, whence); + stream.ungotten = []; + return stream.position; + },read:function(stream, buffer, offset, length, position) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(28); + } + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if ((stream.flags & 2097155) === 1) { + throw new FS.ErrnoError(8); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(31); + } + if (!stream.stream_ops.read) { + throw new FS.ErrnoError(28); + } + var seeking = typeof position !== 'undefined'; + if (!seeking) { + position = stream.position; + } else if (!stream.seekable) { + throw new FS.ErrnoError(70); + } + var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); + if (!seeking) stream.position += bytesRead; + return bytesRead; + },write:function(stream, buffer, offset, length, position, canOwn) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(28); + } + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(8); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(31); + } + if (!stream.stream_ops.write) { + throw new FS.ErrnoError(28); + } + if (stream.seekable && stream.flags & 1024) { + // seek to the end before writing in append mode + FS.llseek(stream, 0, 2); + } + var seeking = typeof position !== 'undefined'; + if (!seeking) { + position = stream.position; + } else if (!stream.seekable) { + throw new FS.ErrnoError(70); + } + var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); + if (!seeking) stream.position += bytesWritten; + try { + if (stream.path && FS.trackingDelegate['onWriteToFile']) FS.trackingDelegate['onWriteToFile'](stream.path); + } catch(e) { + err("FS.trackingDelegate['onWriteToFile']('"+stream.path+"') threw an exception: " + e.message); + } + return bytesWritten; + },allocate:function(stream, offset, length) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError(8); + } + if (offset < 0 || length <= 0) { + throw new FS.ErrnoError(28); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(8); + } + if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(43); + } + if (!stream.stream_ops.allocate) { + throw new FS.ErrnoError(138); + } + stream.stream_ops.allocate(stream, offset, length); + },mmap:function(stream, address, length, position, prot, flags) { + // User requests writing to file (prot & PROT_WRITE != 0). + // Checking if we have permissions to write to the file unless + // MAP_PRIVATE flag is set. According to POSIX spec it is possible + // to write to file opened in read-only mode with MAP_PRIVATE flag, + // as all modifications will be visible only in the memory of + // the current process. + if ((prot & 2) !== 0 + && (flags & 2) === 0 + && (stream.flags & 2097155) !== 2) { + throw new FS.ErrnoError(2); + } + if ((stream.flags & 2097155) === 1) { + throw new FS.ErrnoError(2); + } + if (!stream.stream_ops.mmap) { + throw new FS.ErrnoError(43); + } + return stream.stream_ops.mmap(stream, address, length, position, prot, flags); + },msync:function(stream, buffer, offset, length, mmapFlags) { + if (!stream || !stream.stream_ops.msync) { + return 0; + } + return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); + },munmap:function(stream) { + return 0; + },ioctl:function(stream, cmd, arg) { + if (!stream.stream_ops.ioctl) { + throw new FS.ErrnoError(59); + } + return stream.stream_ops.ioctl(stream, cmd, arg); + },readFile:function(path, opts) { + opts = opts || {}; + opts.flags = opts.flags || 'r'; + opts.encoding = opts.encoding || 'binary'; + if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { + throw new Error('Invalid encoding type "' + opts.encoding + '"'); + } + var ret; + var stream = FS.open(path, opts.flags); + var stat = FS.stat(path); + var length = stat.size; + var buf = new Uint8Array(length); + FS.read(stream, buf, 0, length, 0); + if (opts.encoding === 'utf8') { + ret = UTF8ArrayToString(buf, 0); + } else if (opts.encoding === 'binary') { + ret = buf; + } + FS.close(stream); + return ret; + },writeFile:function(path, data, opts) { + opts = opts || {}; + opts.flags = opts.flags || 'w'; + var stream = FS.open(path, opts.flags, opts.mode); + if (typeof data === 'string') { + var buf = new Uint8Array(lengthBytesUTF8(data)+1); + var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length); + FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn); + } else if (ArrayBuffer.isView(data)) { + FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); + } else { + throw new Error('Unsupported data type'); + } + FS.close(stream); + },cwd:function() { + return FS.currentPath; + },chdir:function(path) { + var lookup = FS.lookupPath(path, { follow: true }); + if (lookup.node === null) { + throw new FS.ErrnoError(44); + } + if (!FS.isDir(lookup.node.mode)) { + throw new FS.ErrnoError(54); + } + var errCode = FS.nodePermissions(lookup.node, 'x'); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + FS.currentPath = lookup.path; + },createDefaultDirectories:function() { + FS.mkdir('/tmp'); + FS.mkdir('/home'); + FS.mkdir('/home/web_user'); + },createDefaultDevices:function() { + // create /dev + FS.mkdir('/dev'); + // setup /dev/null + FS.registerDevice(FS.makedev(1, 3), { + read: function() { return 0; }, + write: function(stream, buffer, offset, length, pos) { return length; } + }); + FS.mkdev('/dev/null', FS.makedev(1, 3)); + // setup /dev/tty and /dev/tty1 + // stderr needs to print output using Module['printErr'] + // so we register a second tty just for it. + TTY.register(FS.makedev(5, 0), TTY.default_tty_ops); + TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops); + FS.mkdev('/dev/tty', FS.makedev(5, 0)); + FS.mkdev('/dev/tty1', FS.makedev(6, 0)); + // setup /dev/[u]random + var random_device = getRandomDevice(); + FS.createDevice('/dev', 'random', random_device); + FS.createDevice('/dev', 'urandom', random_device); + // we're not going to emulate the actual shm device, + // just create the tmp dirs that reside in it commonly + FS.mkdir('/dev/shm'); + FS.mkdir('/dev/shm/tmp'); + },createSpecialDirectories:function() { + // create /proc/self/fd which allows /proc/self/fd/6 => readlink gives the + // name of the stream for fd 6 (see test_unistd_ttyname) + FS.mkdir('/proc'); + var proc_self = FS.mkdir('/proc/self'); + FS.mkdir('/proc/self/fd'); + FS.mount({ + mount: function() { + var node = FS.createNode(proc_self, 'fd', 16384 | 511 /* 0777 */, 73); + node.node_ops = { + lookup: function(parent, name) { + var fd = +name; + var stream = FS.getStream(fd); + if (!stream) throw new FS.ErrnoError(8); + var ret = { + parent: null, + mount: { mountpoint: 'fake' }, + node_ops: { readlink: function() { return stream.path } } + }; + ret.parent = ret; // make it look like a simple root node + return ret; + } + }; + return node; + } + }, {}, '/proc/self/fd'); + },createStandardStreams:function() { + // TODO deprecate the old functionality of a single + // input / output callback and that utilizes FS.createDevice + // and instead require a unique set of stream ops + + // by default, we symlink the standard streams to the + // default tty devices. however, if the standard streams + // have been overwritten we create a unique device for + // them instead. + if (Module['stdin']) { + FS.createDevice('/dev', 'stdin', Module['stdin']); + } else { + FS.symlink('/dev/tty', '/dev/stdin'); + } + if (Module['stdout']) { + FS.createDevice('/dev', 'stdout', null, Module['stdout']); + } else { + FS.symlink('/dev/tty', '/dev/stdout'); + } + if (Module['stderr']) { + FS.createDevice('/dev', 'stderr', null, Module['stderr']); + } else { + FS.symlink('/dev/tty1', '/dev/stderr'); + } + + // open default streams for the stdin, stdout and stderr devices + var stdin = FS.open('/dev/stdin', 'r'); + var stdout = FS.open('/dev/stdout', 'w'); + var stderr = FS.open('/dev/stderr', 'w'); + },ensureErrnoError:function() { + if (FS.ErrnoError) return; + FS.ErrnoError = /** @this{Object} */ function ErrnoError(errno, node) { + this.node = node; + this.setErrno = /** @this{Object} */ function(errno) { + this.errno = errno; + }; + this.setErrno(errno); + this.message = 'FS error'; + + }; + FS.ErrnoError.prototype = new Error(); + FS.ErrnoError.prototype.constructor = FS.ErrnoError; + // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info) + [44].forEach(function(code) { + FS.genericErrors[code] = new FS.ErrnoError(code); + FS.genericErrors[code].stack = ''; + }); + },staticInit:function() { + FS.ensureErrnoError(); + + FS.nameTable = new Array(4096); + + FS.mount(MEMFS, {}, '/'); + + FS.createDefaultDirectories(); + FS.createDefaultDevices(); + FS.createSpecialDirectories(); + + FS.filesystems = { + 'MEMFS': MEMFS, + }; + },init:function(input, output, error) { + FS.init.initialized = true; + + FS.ensureErrnoError(); + + // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here + Module['stdin'] = input || Module['stdin']; + Module['stdout'] = output || Module['stdout']; + Module['stderr'] = error || Module['stderr']; + + FS.createStandardStreams(); + },quit:function() { + FS.init.initialized = false; + // force-flush all streams, so we get musl std streams printed out + var fflush = Module['_fflush']; + if (fflush) fflush(0); + // close all of our streams + for (var i = 0; i < FS.streams.length; i++) { + var stream = FS.streams[i]; + if (!stream) { + continue; + } + FS.close(stream); + } + },getMode:function(canRead, canWrite) { + var mode = 0; + if (canRead) mode |= 292 | 73; + if (canWrite) mode |= 146; + return mode; + },findObject:function(path, dontResolveLastLink) { + var ret = FS.analyzePath(path, dontResolveLastLink); + if (ret.exists) { + return ret.object; + } else { + setErrNo(ret.error); + return null; + } + },analyzePath:function(path, dontResolveLastLink) { + // operate from within the context of the symlink's target + try { + var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + path = lookup.path; + } catch (e) { + } + var ret = { + isRoot: false, exists: false, error: 0, name: null, path: null, object: null, + parentExists: false, parentPath: null, parentObject: null + }; + try { + var lookup = FS.lookupPath(path, { parent: true }); + ret.parentExists = true; + ret.parentPath = lookup.path; + ret.parentObject = lookup.node; + ret.name = PATH.basename(path); + lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + ret.exists = true; + ret.path = lookup.path; + ret.object = lookup.node; + ret.name = lookup.node.name; + ret.isRoot = lookup.path === '/'; + } catch (e) { + ret.error = e.errno; + }; + return ret; + },createPath:function(parent, path, canRead, canWrite) { + parent = typeof parent === 'string' ? parent : FS.getPath(parent); + var parts = path.split('/').reverse(); + while (parts.length) { + var part = parts.pop(); + if (!part) continue; + var current = PATH.join2(parent, part); + try { + FS.mkdir(current); + } catch (e) { + // ignore EEXIST + } + parent = current; + } + return current; + },createFile:function(parent, name, properties, canRead, canWrite) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(canRead, canWrite); + return FS.create(path, mode); + },createDataFile:function(parent, name, data, canRead, canWrite, canOwn) { + var path = name ? PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent; + var mode = FS.getMode(canRead, canWrite); + var node = FS.create(path, mode); + if (data) { + if (typeof data === 'string') { + var arr = new Array(data.length); + for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); + data = arr; + } + // make sure we can write to the file + FS.chmod(node, mode | 146); + var stream = FS.open(node, 'w'); + FS.write(stream, data, 0, data.length, 0, canOwn); + FS.close(stream); + FS.chmod(node, mode); + } + return node; + },createDevice:function(parent, name, input, output) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(!!input, !!output); + if (!FS.createDevice.major) FS.createDevice.major = 64; + var dev = FS.makedev(FS.createDevice.major++, 0); + // Create a fake device that a set of stream ops to emulate + // the old behavior. + FS.registerDevice(dev, { + open: function(stream) { + stream.seekable = false; + }, + close: function(stream) { + // flush any pending line data + if (output && output.buffer && output.buffer.length) { + output(10); + } + }, + read: function(stream, buffer, offset, length, pos /* ignored */) { + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = input(); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(6); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset+i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + }, + write: function(stream, buffer, offset, length, pos) { + for (var i = 0; i < length; i++) { + try { + output(buffer[offset+i]); + } catch (e) { + throw new FS.ErrnoError(29); + } + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + } + }); + return FS.mkdev(path, mode, dev); + },forceLoadFile:function(obj) { + if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; + var success = true; + if (typeof XMLHttpRequest !== 'undefined') { + throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + } else if (read_) { + // Command-line. + try { + // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as + // read() will try to parse UTF8. + obj.contents = intArrayFromString(read_(obj.url), true); + obj.usedBytes = obj.contents.length; + } catch (e) { + success = false; + } + } else { + throw new Error('Cannot load without read() or XMLHttpRequest.'); + } + if (!success) setErrNo(29); + return success; + },createLazyFile:function(parent, name, url, canRead, canWrite) { + // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. + /** @constructor */ + function LazyUint8Array() { + this.lengthKnown = false; + this.chunks = []; // Loaded chunks. Index is the chunk number + } + LazyUint8Array.prototype.get = /** @this{Object} */ function LazyUint8Array_get(idx) { + if (idx > this.length-1 || idx < 0) { + return undefined; + } + var chunkOffset = idx % this.chunkSize; + var chunkNum = (idx / this.chunkSize)|0; + return this.getter(chunkNum)[chunkOffset]; + }; + LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter(getter) { + this.getter = getter; + }; + LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { + // Find length + var xhr = new XMLHttpRequest(); + xhr.open('HEAD', url, false); + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + var datalength = Number(xhr.getResponseHeader("Content-length")); + var header; + var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; + var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip"; + + var chunkSize = 1024*1024; // Chunk size in bytes + + if (!hasByteServing) chunkSize = datalength; + + // Function to get a range from the remote URL. + var doXHR = (function(from, to) { + if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); + if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); + + // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); + + // Some hints to the browser that we want binary data. + if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; + if (xhr.overrideMimeType) { + xhr.overrideMimeType('text/plain; charset=x-user-defined'); + } + + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + if (xhr.response !== undefined) { + return new Uint8Array(/** @type{Array} */(xhr.response || [])); + } else { + return intArrayFromString(xhr.responseText || '', true); + } + }); + var lazyArray = this; + lazyArray.setDataGetter(function(chunkNum) { + var start = chunkNum * chunkSize; + var end = (chunkNum+1) * chunkSize - 1; // including this byte + end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { + lazyArray.chunks[chunkNum] = doXHR(start, end); + } + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); + return lazyArray.chunks[chunkNum]; + }); + + if (usesGzip || !datalength) { + // if the server uses gzip or doesn't supply the length, we have to download the whole file to get the (uncompressed) length + chunkSize = datalength = 1; // this will force getter(0)/doXHR do download the whole file + datalength = this.getter(0).length; + chunkSize = datalength; + out("LazyFiles on gzip forces download of the whole file when length is accessed"); + } + + this._length = datalength; + this._chunkSize = chunkSize; + this.lengthKnown = true; + }; + if (typeof XMLHttpRequest !== 'undefined') { + if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; + var lazyArray = new LazyUint8Array(); + Object.defineProperties(lazyArray, { + length: { + get: /** @this{Object} */ function() { + if(!this.lengthKnown) { + this.cacheLength(); + } + return this._length; + } + }, + chunkSize: { + get: /** @this{Object} */ function() { + if(!this.lengthKnown) { + this.cacheLength(); + } + return this._chunkSize; + } + } + }); + + var properties = { isDevice: false, contents: lazyArray }; + } else { + var properties = { isDevice: false, url: url }; + } + + var node = FS.createFile(parent, name, properties, canRead, canWrite); + // This is a total hack, but I want to get this lazy file code out of the + // core of MEMFS. If we want to keep this lazy file concept I feel it should + // be its own thin LAZYFS proxying calls to MEMFS. + if (properties.contents) { + node.contents = properties.contents; + } else if (properties.url) { + node.contents = null; + node.url = properties.url; + } + // Add a function that defers querying the file size until it is asked the first time. + Object.defineProperties(node, { + usedBytes: { + get: /** @this {FSNode} */ function() { return this.contents.length; } + } + }); + // override each stream op with one that tries to force load the lazy file first + var stream_ops = {}; + var keys = Object.keys(node.stream_ops); + keys.forEach(function(key) { + var fn = node.stream_ops[key]; + stream_ops[key] = function forceLoadLazyFile() { + if (!FS.forceLoadFile(node)) { + throw new FS.ErrnoError(29); + } + return fn.apply(null, arguments); + }; + }); + // use a custom read function + stream_ops.read = function stream_ops_read(stream, buffer, offset, length, position) { + if (!FS.forceLoadFile(node)) { + throw new FS.ErrnoError(29); + } + var contents = stream.node.contents; + if (position >= contents.length) + return 0; + var size = Math.min(contents.length - position, length); + if (contents.slice) { // normal array + for (var i = 0; i < size; i++) { + buffer[offset + i] = contents[position + i]; + } + } else { + for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR + buffer[offset + i] = contents.get(position + i); + } + } + return size; + }; + node.stream_ops = stream_ops; + return node; + },createPreloadedFile:function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) { + Browser.init(); // XXX perhaps this method should move onto Browser? + // TODO we should allow people to just pass in a complete filename instead + // of parent and name being that we just join them anyways + var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; + var dep = getUniqueRunDependency('cp ' + fullname); // might have several active requests for the same fullname + function processData(byteArray) { + function finish(byteArray) { + if (preFinish) preFinish(); + if (!dontCreateFile) { + FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); + } + if (onload) onload(); + removeRunDependency(dep); + } + var handled = false; + Module['preloadPlugins'].forEach(function(plugin) { + if (handled) return; + if (plugin['canHandle'](fullname)) { + plugin['handle'](byteArray, fullname, finish, function() { + if (onerror) onerror(); + removeRunDependency(dep); + }); + handled = true; + } + }); + if (!handled) finish(byteArray); + } + addRunDependency(dep); + if (typeof url == 'string') { + Browser.asyncLoad(url, function(byteArray) { + processData(byteArray); + }, onerror); + } else { + processData(url); + } + },indexedDB:function() { + return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + },DB_NAME:function() { + return 'EM_FS_' + window.location.pathname; + },DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:function(paths, onload, onerror) { + onload = onload || function(){}; + onerror = onerror || function(){}; + var indexedDB = FS.indexedDB(); + try { + var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); + } catch (e) { + return onerror(e); + } + openRequest.onupgradeneeded = function openRequest_onupgradeneeded() { + out('creating db'); + var db = openRequest.result; + db.createObjectStore(FS.DB_STORE_NAME); + }; + openRequest.onsuccess = function openRequest_onsuccess() { + var db = openRequest.result; + var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite'); + var files = transaction.objectStore(FS.DB_STORE_NAME); + var ok = 0, fail = 0, total = paths.length; + function finish() { + if (fail == 0) onload(); else onerror(); + } + paths.forEach(function(path) { + var putRequest = files.put(FS.analyzePath(path).object.contents, path); + putRequest.onsuccess = function putRequest_onsuccess() { ok++; if (ok + fail == total) finish() }; + putRequest.onerror = function putRequest_onerror() { fail++; if (ok + fail == total) finish() }; + }); + transaction.onerror = onerror; + }; + openRequest.onerror = onerror; + },loadFilesFromDB:function(paths, onload, onerror) { + onload = onload || function(){}; + onerror = onerror || function(){}; + var indexedDB = FS.indexedDB(); + try { + var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); + } catch (e) { + return onerror(e); + } + openRequest.onupgradeneeded = onerror; // no database to load from + openRequest.onsuccess = function openRequest_onsuccess() { + var db = openRequest.result; + try { + var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly'); + } catch(e) { + onerror(e); + return; + } + var files = transaction.objectStore(FS.DB_STORE_NAME); + var ok = 0, fail = 0, total = paths.length; + function finish() { + if (fail == 0) onload(); else onerror(); + } + paths.forEach(function(path) { + var getRequest = files.get(path); + getRequest.onsuccess = function getRequest_onsuccess() { + if (FS.analyzePath(path).exists) { + FS.unlink(path); + } + FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true); + ok++; + if (ok + fail == total) finish(); + }; + getRequest.onerror = function getRequest_onerror() { fail++; if (ok + fail == total) finish() }; + }); + transaction.onerror = onerror; + }; + openRequest.onerror = onerror; + }}; + var SYSCALLS={mappings:{},DEFAULT_POLLMASK:5,umask:511,calculateAt:function(dirfd, path, allowEmpty) { + if (path[0] === '/') { + return path; + } + // relative path + var dir; + if (dirfd === -100) { + dir = FS.cwd(); + } else { + var dirstream = FS.getStream(dirfd); + if (!dirstream) throw new FS.ErrnoError(8); + dir = dirstream.path; + } + if (path.length == 0) { + if (!allowEmpty) { + throw new FS.ErrnoError(44);; + } + return dir; + } + return PATH.join2(dir, path); + },doStat:function(func, path, buf) { + try { + var stat = func(path); + } catch (e) { + if (e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node))) { + // an error occurred while trying to look up the path; we should just report ENOTDIR + return -54; + } + throw e; + } + HEAP32[((buf)>>2)] = stat.dev; + HEAP32[(((buf)+(4))>>2)] = 0; + HEAP32[(((buf)+(8))>>2)] = stat.ino; + HEAP32[(((buf)+(12))>>2)] = stat.mode; + HEAP32[(((buf)+(16))>>2)] = stat.nlink; + HEAP32[(((buf)+(20))>>2)] = stat.uid; + HEAP32[(((buf)+(24))>>2)] = stat.gid; + HEAP32[(((buf)+(28))>>2)] = stat.rdev; + HEAP32[(((buf)+(32))>>2)] = 0; + (tempI64 = [stat.size>>>0,(tempDouble=stat.size,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[(((buf)+(40))>>2)] = tempI64[0],HEAP32[(((buf)+(44))>>2)] = tempI64[1]); + HEAP32[(((buf)+(48))>>2)] = 4096; + HEAP32[(((buf)+(52))>>2)] = stat.blocks; + HEAP32[(((buf)+(56))>>2)] = (stat.atime.getTime() / 1000)|0; + HEAP32[(((buf)+(60))>>2)] = 0; + HEAP32[(((buf)+(64))>>2)] = (stat.mtime.getTime() / 1000)|0; + HEAP32[(((buf)+(68))>>2)] = 0; + HEAP32[(((buf)+(72))>>2)] = (stat.ctime.getTime() / 1000)|0; + HEAP32[(((buf)+(76))>>2)] = 0; + (tempI64 = [stat.ino>>>0,(tempDouble=stat.ino,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[(((buf)+(80))>>2)] = tempI64[0],HEAP32[(((buf)+(84))>>2)] = tempI64[1]); + return 0; + },doMsync:function(addr, stream, len, flags, offset) { + var buffer = HEAPU8.slice(addr, addr + len); + FS.msync(stream, buffer, offset, len, flags); + },doMkdir:function(path, mode) { + // remove a trailing slash, if one - /a/b/ has basename of '', but + // we want to create b in the context of this function + path = PATH.normalize(path); + if (path[path.length-1] === '/') path = path.substr(0, path.length-1); + FS.mkdir(path, mode, 0); + return 0; + },doMknod:function(path, mode, dev) { + // we don't want this in the JS API as it uses mknod to create all nodes. + switch (mode & 61440) { + case 32768: + case 8192: + case 24576: + case 4096: + case 49152: + break; + default: return -28; + } + FS.mknod(path, mode, dev); + return 0; + },doReadlink:function(path, buf, bufsize) { + if (bufsize <= 0) return -28; + var ret = FS.readlink(path); + + var len = Math.min(bufsize, lengthBytesUTF8(ret)); + var endChar = HEAP8[buf+len]; + stringToUTF8(ret, buf, bufsize+1); + // readlink is one of the rare functions that write out a C string, but does never append a null to the output buffer(!) + // stringToUTF8() always appends a null byte, so restore the character under the null byte after the write. + HEAP8[buf+len] = endChar; + + return len; + },doAccess:function(path, amode) { + if (amode & ~7) { + // need a valid mode + return -28; + } + var node; + var lookup = FS.lookupPath(path, { follow: true }); + node = lookup.node; + if (!node) { + return -44; + } + var perms = ''; + if (amode & 4) perms += 'r'; + if (amode & 2) perms += 'w'; + if (amode & 1) perms += 'x'; + if (perms /* otherwise, they've just passed F_OK */ && FS.nodePermissions(node, perms)) { + return -2; + } + return 0; + },doDup:function(path, flags, suggestFD) { + var suggest = FS.getStream(suggestFD); + if (suggest) FS.close(suggest); + return FS.open(path, flags, 0, suggestFD, suggestFD).fd; + },doReadv:function(stream, iov, iovcnt, offset) { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAP32[(((iov)+(i*8))>>2)]; + var len = HEAP32[(((iov)+(i*8 + 4))>>2)]; + var curr = FS.read(stream, HEAP8,ptr, len, offset); + if (curr < 0) return -1; + ret += curr; + if (curr < len) break; // nothing more to read + } + return ret; + },doWritev:function(stream, iov, iovcnt, offset) { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAP32[(((iov)+(i*8))>>2)]; + var len = HEAP32[(((iov)+(i*8 + 4))>>2)]; + var curr = FS.write(stream, HEAP8,ptr, len, offset); + if (curr < 0) return -1; + ret += curr; + } + return ret; + },varargs:undefined,get:function() { + SYSCALLS.varargs += 4; + var ret = HEAP32[(((SYSCALLS.varargs)-(4))>>2)]; + return ret; + },getStr:function(ptr) { + var ret = UTF8ToString(ptr); + return ret; + },getStreamFromFD:function(fd) { + var stream = FS.getStream(fd); + if (!stream) throw new FS.ErrnoError(8); + return stream; + },get64:function(low, high) { + return low; + }}; + function _environ_get(__environ, environ_buf) {try { + + var bufSize = 0; + getEnvStrings().forEach(function(string, i) { + var ptr = environ_buf + bufSize; + HEAP32[(((__environ)+(i * 4))>>2)] = ptr; + writeAsciiToMemory(string, ptr); + bufSize += string.length + 1; + }); + return 0; + } catch (e) { + if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } + } + + function _environ_sizes_get(penviron_count, penviron_buf_size) {try { + + var strings = getEnvStrings(); + HEAP32[((penviron_count)>>2)] = strings.length; + var bufSize = 0; + strings.forEach(function(string) { + bufSize += string.length + 1; + }); + HEAP32[((penviron_buf_size)>>2)] = bufSize; + return 0; + } catch (e) { + if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } + } + + function _fd_close(fd) {try { + + var stream = SYSCALLS.getStreamFromFD(fd); + FS.close(stream); + return 0; + } catch (e) { + if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } + } + + function _fd_read(fd, iov, iovcnt, pnum) {try { + + var stream = SYSCALLS.getStreamFromFD(fd); + var num = SYSCALLS.doReadv(stream, iov, iovcnt); + HEAP32[((pnum)>>2)] = num + return 0; + } catch (e) { + if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } + } + + function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {try { + + + var stream = SYSCALLS.getStreamFromFD(fd); + var HIGH_OFFSET = 0x100000000; // 2^32 + // use an unsigned operator on low and shift high by 32-bits + var offset = offset_high * HIGH_OFFSET + (offset_low >>> 0); + + var DOUBLE_LIMIT = 0x20000000000000; // 2^53 + // we also check for equality since DOUBLE_LIMIT + 1 == DOUBLE_LIMIT + if (offset <= -DOUBLE_LIMIT || offset >= DOUBLE_LIMIT) { + return -61; + } + + FS.llseek(stream, offset, whence); + (tempI64 = [stream.position>>>0,(tempDouble=stream.position,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((newOffset)>>2)] = tempI64[0],HEAP32[(((newOffset)+(4))>>2)] = tempI64[1]); + if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null; // reset readdir state + return 0; + } catch (e) { + if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } + } + + function _fd_write(fd, iov, iovcnt, pnum) {try { + + var stream = SYSCALLS.getStreamFromFD(fd); + var num = SYSCALLS.doWritev(stream, iov, iovcnt); + HEAP32[((pnum)>>2)] = num + return 0; + } catch (e) { + if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); + return e.errno; + } + } + + function _setTempRet0($i) { + setTempRet0(($i) | 0); + } + + function __isLeapYear(year) { + return year%4 === 0 && (year%100 !== 0 || year%400 === 0); + } + + function __arraySum(array, index) { + var sum = 0; + for (var i = 0; i <= index; sum += array[i++]) { + // no-op + } + return sum; + } + + var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31]; + + var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31]; + function __addDays(date, days) { + var newDate = new Date(date.getTime()); + while(days > 0) { + var leap = __isLeapYear(newDate.getFullYear()); + var currentMonth = newDate.getMonth(); + var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth]; + + if (days > daysInCurrentMonth-newDate.getDate()) { + // we spill over to next month + days -= (daysInCurrentMonth-newDate.getDate()+1); + newDate.setDate(1); + if (currentMonth < 11) { + newDate.setMonth(currentMonth+1) + } else { + newDate.setMonth(0); + newDate.setFullYear(newDate.getFullYear()+1); + } + } else { + // we stay in current month + newDate.setDate(newDate.getDate()+days); + return newDate; + } + } + + return newDate; + } + function _strftime(s, maxsize, format, tm) { + // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html + + var tm_zone = HEAP32[(((tm)+(40))>>2)]; + + var date = { + tm_sec: HEAP32[((tm)>>2)], + tm_min: HEAP32[(((tm)+(4))>>2)], + tm_hour: HEAP32[(((tm)+(8))>>2)], + tm_mday: HEAP32[(((tm)+(12))>>2)], + tm_mon: HEAP32[(((tm)+(16))>>2)], + tm_year: HEAP32[(((tm)+(20))>>2)], + tm_wday: HEAP32[(((tm)+(24))>>2)], + tm_yday: HEAP32[(((tm)+(28))>>2)], + tm_isdst: HEAP32[(((tm)+(32))>>2)], + tm_gmtoff: HEAP32[(((tm)+(36))>>2)], + tm_zone: tm_zone ? UTF8ToString(tm_zone) : '' + }; + + var pattern = UTF8ToString(format); + + // expand format + var EXPANSION_RULES_1 = { + '%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013 + '%D': '%m/%d/%y', // Equivalent to %m / %d / %y + '%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d + '%h': '%b', // Equivalent to %b + '%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation + '%R': '%H:%M', // Replaced by the time in 24-hour notation + '%T': '%H:%M:%S', // Replaced by the time + '%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation + '%X': '%H:%M:%S', // Replaced by the locale's appropriate time representation + // Modified Conversion Specifiers + '%Ec': '%c', // Replaced by the locale's alternative appropriate date and time representation. + '%EC': '%C', // Replaced by the name of the base year (period) in the locale's alternative representation. + '%Ex': '%m/%d/%y', // Replaced by the locale's alternative date representation. + '%EX': '%H:%M:%S', // Replaced by the locale's alternative time representation. + '%Ey': '%y', // Replaced by the offset from %EC (year only) in the locale's alternative representation. + '%EY': '%Y', // Replaced by the full alternative year representation. + '%Od': '%d', // Replaced by the day of the month, using the locale's alternative numeric symbols, filled as needed with leading zeros if there is any alternative symbol for zero; otherwise, with leading characters. + '%Oe': '%e', // Replaced by the day of the month, using the locale's alternative numeric symbols, filled as needed with leading characters. + '%OH': '%H', // Replaced by the hour (24-hour clock) using the locale's alternative numeric symbols. + '%OI': '%I', // Replaced by the hour (12-hour clock) using the locale's alternative numeric symbols. + '%Om': '%m', // Replaced by the month using the locale's alternative numeric symbols. + '%OM': '%M', // Replaced by the minutes using the locale's alternative numeric symbols. + '%OS': '%S', // Replaced by the seconds using the locale's alternative numeric symbols. + '%Ou': '%u', // Replaced by the weekday as a number in the locale's alternative representation (Monday=1). + '%OU': '%U', // Replaced by the week number of the year (Sunday as the first day of the week, rules corresponding to %U ) using the locale's alternative numeric symbols. + '%OV': '%V', // Replaced by the week number of the year (Monday as the first day of the week, rules corresponding to %V ) using the locale's alternative numeric symbols. + '%Ow': '%w', // Replaced by the number of the weekday (Sunday=0) using the locale's alternative numeric symbols. + '%OW': '%W', // Replaced by the week number of the year (Monday as the first day of the week) using the locale's alternative numeric symbols. + '%Oy': '%y', // Replaced by the year (offset from %C ) using the locale's alternative numeric symbols. + }; + for (var rule in EXPANSION_RULES_1) { + pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]); + } + + var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + + function leadingSomething(value, digits, character) { + var str = typeof value === 'number' ? value.toString() : (value || ''); + while (str.length < digits) { + str = character[0]+str; + } + return str; + } + + function leadingNulls(value, digits) { + return leadingSomething(value, digits, '0'); + } + + function compareByDay(date1, date2) { + function sgn(value) { + return value < 0 ? -1 : (value > 0 ? 1 : 0); + } + + var compare; + if ((compare = sgn(date1.getFullYear()-date2.getFullYear())) === 0) { + if ((compare = sgn(date1.getMonth()-date2.getMonth())) === 0) { + compare = sgn(date1.getDate()-date2.getDate()); + } + } + return compare; + } + + function getFirstWeekStartDate(janFourth) { + switch (janFourth.getDay()) { + case 0: // Sunday + return new Date(janFourth.getFullYear()-1, 11, 29); + case 1: // Monday + return janFourth; + case 2: // Tuesday + return new Date(janFourth.getFullYear(), 0, 3); + case 3: // Wednesday + return new Date(janFourth.getFullYear(), 0, 2); + case 4: // Thursday + return new Date(janFourth.getFullYear(), 0, 1); + case 5: // Friday + return new Date(janFourth.getFullYear()-1, 11, 31); + case 6: // Saturday + return new Date(janFourth.getFullYear()-1, 11, 30); + } + } + + function getWeekBasedYear(date) { + var thisDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); + + var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4); + var janFourthNextYear = new Date(thisDate.getFullYear()+1, 0, 4); + + var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); + var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); + + if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { + // this date is after the start of the first week of this year + if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { + return thisDate.getFullYear()+1; + } else { + return thisDate.getFullYear(); + } + } else { + return thisDate.getFullYear()-1; + } + } + + var EXPANSION_RULES_2 = { + '%a': function(date) { + return WEEKDAYS[date.tm_wday].substring(0,3); + }, + '%A': function(date) { + return WEEKDAYS[date.tm_wday]; + }, + '%b': function(date) { + return MONTHS[date.tm_mon].substring(0,3); + }, + '%B': function(date) { + return MONTHS[date.tm_mon]; + }, + '%C': function(date) { + var year = date.tm_year+1900; + return leadingNulls((year/100)|0,2); + }, + '%d': function(date) { + return leadingNulls(date.tm_mday, 2); + }, + '%e': function(date) { + return leadingSomething(date.tm_mday, 2, ' '); + }, + '%g': function(date) { + // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year. + // In this system, weeks begin on a Monday and week 1 of the year is the week that includes + // January 4th, which is also the week that includes the first Thursday of the year, and + // is also the first week that contains at least four days in the year. + // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of + // the last week of the preceding year; thus, for Saturday 2nd January 1999, + // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th, + // or 31st is a Monday, it and any following days are part of week 1 of the following year. + // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01. + + return getWeekBasedYear(date).toString().substring(2); + }, + '%G': function(date) { + return getWeekBasedYear(date); + }, + '%H': function(date) { + return leadingNulls(date.tm_hour, 2); + }, + '%I': function(date) { + var twelveHour = date.tm_hour; + if (twelveHour == 0) twelveHour = 12; + else if (twelveHour > 12) twelveHour -= 12; + return leadingNulls(twelveHour, 2); + }, + '%j': function(date) { + // Day of the year (001-366) + return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon-1), 3); + }, + '%m': function(date) { + return leadingNulls(date.tm_mon+1, 2); + }, + '%M': function(date) { + return leadingNulls(date.tm_min, 2); + }, + '%n': function() { + return '\n'; + }, + '%p': function(date) { + if (date.tm_hour >= 0 && date.tm_hour < 12) { + return 'AM'; + } else { + return 'PM'; + } + }, + '%S': function(date) { + return leadingNulls(date.tm_sec, 2); + }, + '%t': function() { + return '\t'; + }, + '%u': function(date) { + return date.tm_wday || 7; + }, + '%U': function(date) { + // Replaced by the week number of the year as a decimal number [00,53]. + // The first Sunday of January is the first day of week 1; + // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + var janFirst = new Date(date.tm_year+1900, 0, 1); + var firstSunday = janFirst.getDay() === 0 ? janFirst : __addDays(janFirst, 7-janFirst.getDay()); + var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); + + // is target date after the first Sunday? + if (compareByDay(firstSunday, endDate) < 0) { + // calculate difference in days between first Sunday and endDate + var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; + var firstSundayUntilEndJanuary = 31-firstSunday.getDate(); + var days = firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); + return leadingNulls(Math.ceil(days/7), 2); + } + + return compareByDay(firstSunday, janFirst) === 0 ? '01': '00'; + }, + '%V': function(date) { + // Replaced by the week number of the year (Monday as the first day of the week) + // as a decimal number [01,53]. If the week containing 1 January has four + // or more days in the new year, then it is considered week 1. + // Otherwise, it is the last week of the previous year, and the next week is week 1. + // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] + var janFourthThisYear = new Date(date.tm_year+1900, 0, 4); + var janFourthNextYear = new Date(date.tm_year+1901, 0, 4); + + var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); + var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); + + var endDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); + + if (compareByDay(endDate, firstWeekStartThisYear) < 0) { + // if given date is before this years first week, then it belongs to the 53rd week of last year + return '53'; + } + + if (compareByDay(firstWeekStartNextYear, endDate) <= 0) { + // if given date is after next years first week, then it belongs to the 01th week of next year + return '01'; + } + + // given date is in between CW 01..53 of this calendar year + var daysDifference; + if (firstWeekStartThisYear.getFullYear() < date.tm_year+1900) { + // first CW of this year starts last year + daysDifference = date.tm_yday+32-firstWeekStartThisYear.getDate() + } else { + // first CW of this year starts this year + daysDifference = date.tm_yday+1-firstWeekStartThisYear.getDate(); + } + return leadingNulls(Math.ceil(daysDifference/7), 2); + }, + '%w': function(date) { + return date.tm_wday; + }, + '%W': function(date) { + // Replaced by the week number of the year as a decimal number [00,53]. + // The first Monday of January is the first day of week 1; + // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + var janFirst = new Date(date.tm_year, 0, 1); + var firstMonday = janFirst.getDay() === 1 ? janFirst : __addDays(janFirst, janFirst.getDay() === 0 ? 1 : 7-janFirst.getDay()+1); + var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); + + // is target date after the first Monday? + if (compareByDay(firstMonday, endDate) < 0) { + var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; + var firstMondayUntilEndJanuary = 31-firstMonday.getDate(); + var days = firstMondayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); + return leadingNulls(Math.ceil(days/7), 2); + } + return compareByDay(firstMonday, janFirst) === 0 ? '01': '00'; + }, + '%y': function(date) { + // Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year] + return (date.tm_year+1900).toString().substring(2); + }, + '%Y': function(date) { + // Replaced by the year as a decimal number (for example, 1997). [ tm_year] + return date.tm_year+1900; + }, + '%z': function(date) { + // Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ). + // For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). + var off = date.tm_gmtoff; + var ahead = off >= 0; + off = Math.abs(off) / 60; + // convert from minutes into hhmm format (which means 60 minutes = 100 units) + off = (off / 60)*100 + (off % 60); + return (ahead ? '+' : '-') + String("0000" + off).slice(-4); + }, + '%Z': function(date) { + return date.tm_zone; + }, + '%%': function() { + return '%'; + } + }; + for (var rule in EXPANSION_RULES_2) { + if (pattern.indexOf(rule) >= 0) { + pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date)); + } + } + + var bytes = intArrayFromString(pattern, false); + if (bytes.length > maxsize) { + return 0; + } + + writeArrayToMemory(bytes, s); + return bytes.length-1; + } + + function _strftime_l(s, maxsize, format, tm) { + return _strftime(s, maxsize, format, tm); // no locale support yet + } +var FSNode = /** @constructor */ function(parent, name, mode, rdev) { + if (!parent) { + parent = this; // root node sets parent to itself + } + this.parent = parent; + this.mount = parent.mount; + this.mounted = null; + this.id = FS.nextInode++; + this.name = name; + this.mode = mode; + this.node_ops = {}; + this.stream_ops = {}; + this.rdev = rdev; + }; + var readMode = 292/*292*/ | 73/*73*/; + var writeMode = 146/*146*/; + Object.defineProperties(FSNode.prototype, { + read: { + get: /** @this{FSNode} */function() { + return (this.mode & readMode) === readMode; + }, + set: /** @this{FSNode} */function(val) { + val ? this.mode |= readMode : this.mode &= ~readMode; + } + }, + write: { + get: /** @this{FSNode} */function() { + return (this.mode & writeMode) === writeMode; + }, + set: /** @this{FSNode} */function(val) { + val ? this.mode |= writeMode : this.mode &= ~writeMode; + } + }, + isFolder: { + get: /** @this{FSNode} */function() { + return FS.isDir(this.mode); + } + }, + isDevice: { + get: /** @this{FSNode} */function() { + return FS.isChrdev(this.mode); + } + } + }); + FS.FSNode = FSNode; + FS.staticInit();; +var ASSERTIONS = false; + + + +/** @type {function(string, boolean=, number=)} */ +function intArrayFromString(stringy, dontAddNull, length) { + var len = length > 0 ? length : lengthBytesUTF8(stringy)+1; + var u8array = new Array(len); + var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); + if (dontAddNull) u8array.length = numBytesWritten; + return u8array; +} + +function intArrayToString(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + var chr = array[i]; + if (chr > 0xFF) { + if (ASSERTIONS) { + assert(false, 'Character code ' + chr + ' (' + String.fromCharCode(chr) + ') at offset ' + i + ' not in 0x00-0xFF.'); + } + chr &= 0xFF; + } + ret.push(String.fromCharCode(chr)); + } + return ret.join(''); +} + + +var asmLibraryArg = { + "__cxa_allocate_exception": ___cxa_allocate_exception, + "__cxa_atexit": ___cxa_atexit, + "__cxa_rethrow": ___cxa_rethrow, + "__cxa_throw": ___cxa_throw, + "__localtime_r": ___localtime_r, + "abort": _abort, + "clock_gettime": _clock_gettime, + "emscripten_memcpy_big": _emscripten_memcpy_big, + "emscripten_resize_heap": _emscripten_resize_heap, + "environ_get": _environ_get, + "environ_sizes_get": _environ_sizes_get, + "fd_close": _fd_close, + "fd_read": _fd_read, + "fd_seek": _fd_seek, + "fd_write": _fd_write, + "memory": wasmMemory, + "setTempRet0": _setTempRet0, + "strftime": _strftime, + "strftime_l": _strftime_l +}; +var asm = createWasm(); +/** @type {function(...*):?} */ +var ___wasm_call_ctors = Module["___wasm_call_ctors"] = function() { + return (___wasm_call_ctors = Module["___wasm_call_ctors"] = Module["asm"]["__wasm_call_ctors"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_get_version = Module["_libfibre_get_version"] = function() { + return (_libfibre_get_version = Module["_libfibre_get_version"] = Module["asm"]["libfibre_get_version"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_open = Module["_libfibre_open"] = function() { + return (_libfibre_open = Module["_libfibre_open"] = Module["asm"]["libfibre_open"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_close = Module["_libfibre_close"] = function() { + return (_libfibre_close = Module["_libfibre_close"] = Module["asm"]["libfibre_close"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_register_discoverer = Module["_libfibre_register_discoverer"] = function() { + return (_libfibre_register_discoverer = Module["_libfibre_register_discoverer"] = Module["asm"]["libfibre_register_discoverer"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_add_channels = Module["_libfibre_add_channels"] = function() { + return (_libfibre_add_channels = Module["_libfibre_add_channels"] = Module["asm"]["libfibre_add_channels"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_start_discovery = Module["_libfibre_start_discovery"] = function() { + return (_libfibre_start_discovery = Module["_libfibre_start_discovery"] = Module["asm"]["libfibre_start_discovery"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_stop_discovery = Module["_libfibre_stop_discovery"] = function() { + return (_libfibre_stop_discovery = Module["_libfibre_stop_discovery"] = Module["asm"]["libfibre_stop_discovery"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_subscribe_to_interface = Module["_libfibre_subscribe_to_interface"] = function() { + return (_libfibre_subscribe_to_interface = Module["_libfibre_subscribe_to_interface"] = Module["asm"]["libfibre_subscribe_to_interface"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_get_attribute = Module["_libfibre_get_attribute"] = function() { + return (_libfibre_get_attribute = Module["_libfibre_get_attribute"] = Module["asm"]["libfibre_get_attribute"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_start_call = Module["_libfibre_start_call"] = function() { + return (_libfibre_start_call = Module["_libfibre_start_call"] = Module["asm"]["libfibre_start_call"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_end_call = Module["_libfibre_end_call"] = function() { + return (_libfibre_end_call = Module["_libfibre_end_call"] = Module["asm"]["libfibre_end_call"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_start_tx = Module["_libfibre_start_tx"] = function() { + return (_libfibre_start_tx = Module["_libfibre_start_tx"] = Module["asm"]["libfibre_start_tx"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_cancel_tx = Module["_libfibre_cancel_tx"] = function() { + return (_libfibre_cancel_tx = Module["_libfibre_cancel_tx"] = Module["asm"]["libfibre_cancel_tx"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_close_tx = Module["_libfibre_close_tx"] = function() { + return (_libfibre_close_tx = Module["_libfibre_close_tx"] = Module["asm"]["libfibre_close_tx"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_start_rx = Module["_libfibre_start_rx"] = function() { + return (_libfibre_start_rx = Module["_libfibre_start_rx"] = Module["asm"]["libfibre_start_rx"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_cancel_rx = Module["_libfibre_cancel_rx"] = function() { + return (_libfibre_cancel_rx = Module["_libfibre_cancel_rx"] = Module["asm"]["libfibre_cancel_rx"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _libfibre_close_rx = Module["_libfibre_close_rx"] = function() { + return (_libfibre_close_rx = Module["_libfibre_close_rx"] = Module["asm"]["libfibre_close_rx"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var ___errno_location = Module["___errno_location"] = function() { + return (___errno_location = Module["___errno_location"] = Module["asm"]["__errno_location"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _malloc = Module["_malloc"] = function() { + return (_malloc = Module["_malloc"] = Module["asm"]["malloc"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var __get_tzname = Module["__get_tzname"] = function() { + return (__get_tzname = Module["__get_tzname"] = Module["asm"]["_get_tzname"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var __get_daylight = Module["__get_daylight"] = function() { + return (__get_daylight = Module["__get_daylight"] = Module["asm"]["_get_daylight"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var __get_timezone = Module["__get_timezone"] = function() { + return (__get_timezone = Module["__get_timezone"] = Module["asm"]["_get_timezone"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var stackSave = Module["stackSave"] = function() { + return (stackSave = Module["stackSave"] = Module["asm"]["stackSave"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var stackRestore = Module["stackRestore"] = function() { + return (stackRestore = Module["stackRestore"] = Module["asm"]["stackRestore"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var stackAlloc = Module["stackAlloc"] = function() { + return (stackAlloc = Module["stackAlloc"] = Module["asm"]["stackAlloc"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _setThrew = Module["_setThrew"] = function() { + return (_setThrew = Module["_setThrew"] = Module["asm"]["setThrew"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _free = Module["_free"] = function() { + return (_free = Module["_free"] = Module["asm"]["free"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var dynCall_viijii = Module["dynCall_viijii"] = function() { + return (dynCall_viijii = Module["dynCall_viijii"] = Module["asm"]["dynCall_viijii"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var dynCall_jiji = Module["dynCall_jiji"] = function() { + return (dynCall_jiji = Module["dynCall_jiji"] = Module["asm"]["dynCall_jiji"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var dynCall_iiiiij = Module["dynCall_iiiiij"] = function() { + return (dynCall_iiiiij = Module["dynCall_iiiiij"] = Module["asm"]["dynCall_iiiiij"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var dynCall_iiiiijj = Module["dynCall_iiiiijj"] = function() { + return (dynCall_iiiiijj = Module["dynCall_iiiiijj"] = Module["asm"]["dynCall_iiiiijj"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var dynCall_iiiiiijj = Module["dynCall_iiiiiijj"] = function() { + return (dynCall_iiiiiijj = Module["dynCall_iiiiiijj"] = Module["asm"]["dynCall_iiiiiijj"]).apply(null, arguments); +}; + + + + + +// === Auto-generated postamble setup entry stuff === + +Module["UTF8ArrayToString"] = UTF8ArrayToString; +Module["stringToUTF8Array"] = stringToUTF8Array; +Module["addFunction"] = addFunction; +Module["ENV"] = ENV; + + +var calledRun; + +/** + * @constructor + * @this {ExitStatus} + */ +function ExitStatus(status) { + this.name = "ExitStatus"; + this.message = "Program terminated with exit(" + status + ")"; + this.status = status; +} + +var calledMain = false; + + +dependenciesFulfilled = function runCaller() { + // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) + if (!calledRun) run(); + if (!calledRun) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled +}; + + +/** @type {function(Array=)} */ +function run(args) { + args = args || arguments_; + + if (runDependencies > 0) { + return; + } + + + preRun(); + + // a preRun added a dependency, run will be called later + if (runDependencies > 0) { + return; + } + + function doRun() { + // run may have just been called through dependencies being fulfilled just in this very frame, + // or while the async setStatus time below was happening + if (calledRun) return; + calledRun = true; + Module['calledRun'] = true; + + if (ABORT) return; + + initRuntime(); + + preMain(); + + readyPromiseResolve(Module); + if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized'](); + + + postRun(); + } + + if (Module['setStatus']) { + Module['setStatus']('Running...'); + setTimeout(function() { + setTimeout(function() { + Module['setStatus'](''); + }, 1); + doRun(); + }, 1); + } else + { + doRun(); + } +} +Module['run'] = run; + + +/** @param {boolean|number=} implicit */ +function exit(status, implicit) { + + // if this is just main exit-ing implicitly, and the status is 0, then we + // don't need to do anything here and can just leave. if the status is + // non-zero, though, then we need to report it. + // (we may have warned about this earlier, if a situation justifies doing so) + if (implicit && noExitRuntime && status === 0) { + return; + } + + if (noExitRuntime) { + } else { + + EXITSTATUS = status; + + exitRuntime(); + + if (Module['onExit']) Module['onExit'](status); + + ABORT = true; + } + + quit_(status, new ExitStatus(status)); +} + +if (Module['preInit']) { + if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; + while (Module['preInit'].length > 0) { + Module['preInit'].pop()(); + } +} + +run(); + + + + + + + + + + + return Module.ready +} +); +})(); +export default Module; \ No newline at end of file diff --git a/GUI/fibre-js/libfibre-wasm.wasm b/GUI/fibre-js/libfibre-wasm.wasm new file mode 100755 index 000000000..9e275771a Binary files /dev/null and b/GUI/fibre-js/libfibre-wasm.wasm differ diff --git a/GUI/fibre-js/multidevice_example.html b/GUI/fibre-js/multidevice_example.html new file mode 100644 index 000000000..60701b624 --- /dev/null +++ b/GUI/fibre-js/multidevice_example.html @@ -0,0 +1,47 @@ + + + + + WebAssembly Test + + + +
loading...
+ + + + + diff --git a/GUI/fibre-js/package.json b/GUI/fibre-js/package.json new file mode 100644 index 000000000..1ca9ca469 --- /dev/null +++ b/GUI/fibre-js/package.json @@ -0,0 +1,19 @@ +{ + "name": "fibre-js", + "version": "0.1.0", + "eslintConfig": { + "rules": { + "no-empty": "off", + "no-unused-vars": "off", + "no-undef": "off", + "no-constant-condition": "off", + "no-extra-semi": "off", + "no-async-promise-executor": "off", + "no-redeclare": "off", + "no-prototype-builtins": "off", + "no-global-assign": "off", + "no-useless-escape": "off", + "no-useless-catch": "off" + } + } +} diff --git a/GUI/package-lock.json b/GUI/package-lock.json new file mode 100644 index 000000000..a2b44c5a2 --- /dev/null +++ b/GUI/package-lock.json @@ -0,0 +1,15195 @@ +{ + "name": "ODriveGUI", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "7zip-bin": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.0.3.tgz", + "integrity": "sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA==", + "dev": true + }, + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/compat-data": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.10.4.tgz", + "integrity": "sha512-t+rjExOrSVvjQQXNp5zAIYDp00KjdvGl/TpDX5REPr0S9IAIPQMTilcfG6q8c0QFmj9lSTVySV2VTsyggvtNIw==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "invariant": "^2.2.4", + "semver": "^5.5.0" + } + }, + "@babel/core": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.10.4.tgz", + "integrity": "sha512-3A0tS0HWpy4XujGc7QtOIHTeNwUgWaZc/WuS5YQrfhU67jnVmsD6OGPc1AKHH0LJHQICGncy3+YUjIhVlfDdcA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.10.4", + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helpers": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + } + }, + "@babel/generator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.4.tgz", + "integrity": "sha512-toLIHUIAgcQygFZRAQcsLQV3CBuX6yOIru1kJk/qqqvcRmZrYe6WavZTSG+bB8MxhnL9YPf+pKQfuiP161q7ng==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", + "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", + "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.10.4", + "browserslist": "^4.12.0", + "invariant": "^2.2.4", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.4.tgz", + "integrity": "sha512-9raUiOsXPxzzLjCXeosApJItoMnX3uyT4QdM2UldffuGApNrF8e938MwNpDCK9CPoyxrEoCgT+hObJc3mZa6lQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", + "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-regex": "^7.10.4", + "regexpu-core": "^4.7.0" + } + }, + "@babel/helper-define-map": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.4.tgz", + "integrity": "sha512-nIij0oKErfCnLUCWaCaHW0Bmtl2RO9cN7+u2QT8yqTywgALKlyUVOvHDElh+b5DwVC6YB1FOYFOTWcN/+41EDA==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.4", + "lodash": "^4.17.13" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", + "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", + "dev": true, + "requires": { + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", + "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.4.tgz", + "integrity": "sha512-m5j85pK/KZhuSdM/8cHUABQTAslV47OjfIB9Cc7P+PvlAoBzdb79BGNfw8RhT5Mq3p+xGd0ZfAKixbrUZx0C7A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-module-imports": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-module-transforms": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.4.tgz", + "integrity": "sha512-Er2FQX0oa3nV7eM1o0tNCTx7izmQtwAQsIiaLRWtavAAEcskb0XJ5OjJbVrYXWOTr8om921Scabn4/tzlx7j1Q==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4", + "lodash": "^4.17.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.4.tgz", + "integrity": "sha512-inWpnHGgtg5NOF0eyHlC0/74/VkdRITY9dtTpB2PrxKKn+AkVMRiZz/Adrx+Ssg+MLDesi2zohBW6MVq6b4pOQ==", + "dev": true, + "requires": { + "lodash": "^4.17.13" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", + "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-replace-supers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", + "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-simple-access": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", + "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", + "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", + "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helpers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", + "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.4.tgz", + "integrity": "sha512-8jHII4hf+YVDsskTF6WuMB3X4Eh+PsUkC2ljq22so5rHvH+T8BzyL94VOdyFLNR8tBSVXOTbNHOKpR4TfRxVtA==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.4.tgz", + "integrity": "sha512-MJbxGSmejEFVOANAezdO39SObkURO5o/8b6fSH6D1pi9RZQt+ldppKPXfqgUWpSQ9asM6xaSaSJIaeWMDRP0Zg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", + "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.4.tgz", + "integrity": "sha512-JHTWjQngOPv+ZQQqOGv2x6sCCr4IYWy7S1/VH6BE9ZfkoLrdQ2GpEP3tfb5M++G9PwvqjhY8VC/C3tXm+/eHvA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-decorators": "^7.10.4" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", + "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", + "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", + "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", + "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.4.tgz", + "integrity": "sha512-6vh4SqRuLLarjgeOf4EaROJAHjvu9Gl+/346PbDH9yWbJyfnJ/ah3jmYKYtswEyCoWZiidvVHjHshd4WgjB9BA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.10.4" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", + "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.4.tgz", + "integrity": "sha512-ZIhQIEeavTgouyMSdZRap4VPPHqJJ3NEs2cuHs5p0erH+iz6khB0qfgU8g7UuJkG88+fBMy23ZiU+nuHvekJeQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", + "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", + "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", + "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.4.tgz", + "integrity": "sha512-2NaoC6fAk2VMdhY1eerkfHV+lVYC1u8b+jmRJISqANCJlTxYy19HGdIkkQtix2UtkcPuPu+IlDgrVseZnU03bw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz", + "integrity": "sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", + "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", + "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", + "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", + "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.4.tgz", + "integrity": "sha512-J3b5CluMg3hPUii2onJDRiaVbPtKFPLEaV5dOPY5OeAbDi1iU/UbbFFTgwb7WnanaDy7bjU35kc26W3eM5Qa0A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "lodash": "^4.17.13" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", + "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-define-map": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", + "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", + "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", + "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", + "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", + "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", + "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", + "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", + "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", + "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.4.tgz", + "integrity": "sha512-3Fw+H3WLUrTlzi3zMiZWp3AR4xadAEMv6XRCYnd5jAlLM61Rn+CRJaZMaNvIpcJpQ3vs1kyifYvEVPFfoSkKOA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", + "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.4.tgz", + "integrity": "sha512-Tb28LlfxrTiOTGtZFsvkjpyjCl9IoaRI52AEU/VIwOwvDQWtbNJsAqTXzh+5R7i74e/OZHH2c2w2fsOqAfnQYQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.10.4", + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", + "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", + "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", + "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", + "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.4.tgz", + "integrity": "sha512-RurVtZ/D5nYfEg0iVERXYKEgDFeesHrHfx8RT05Sq57ucj2eOYAP6eu5fynL4Adju4I/mP/I6SO0DqNWAXjfLQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", + "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", + "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", + "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.10.4.tgz", + "integrity": "sha512-8ULlGv8p+Vuxu+kz2Y1dk6MYS2b/Dki+NO6/0ZlfSj5tMalfDL7jI/o/2a+rrWLqSXvnadEqc2WguB4gdQIxZw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "resolve": "^1.8.1", + "semver": "^5.5.1" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", + "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.4.tgz", + "integrity": "sha512-1e/51G/Ni+7uH5gktbWv+eCED9pP8ZpRhZB3jOaI3mmzfvJTWHkuyYTv0Z5PYtyM+Tr2Ccr9kUdQxn60fI5WuQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", + "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-regex": "^7.10.4" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.4.tgz", + "integrity": "sha512-4NErciJkAYe+xI5cqfS8pV/0ntlY5N5Ske/4ImxAVX7mk9Rxt2bwDTGv1Msc2BRJvWQcmYEC+yoMLdX22aE4VQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", + "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", + "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", + "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/preset-env": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.10.4.tgz", + "integrity": "sha512-tcmuQ6vupfMZPrLrc38d0sF2OjLT3/bZ0dry5HchNCQbrokoQi4reXqclvkkAT5b+gWc23meVWpve5P/7+w/zw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.10.4", + "@babel/helper-compilation-targets": "^7.10.4", + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-proposal-async-generator-functions": "^7.10.4", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-dynamic-import": "^7.10.4", + "@babel/plugin-proposal-json-strings": "^7.10.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", + "@babel/plugin-proposal-numeric-separator": "^7.10.4", + "@babel/plugin-proposal-object-rest-spread": "^7.10.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", + "@babel/plugin-proposal-optional-chaining": "^7.10.4", + "@babel/plugin-proposal-private-methods": "^7.10.4", + "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-class-properties": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.10.4", + "@babel/plugin-transform-arrow-functions": "^7.10.4", + "@babel/plugin-transform-async-to-generator": "^7.10.4", + "@babel/plugin-transform-block-scoped-functions": "^7.10.4", + "@babel/plugin-transform-block-scoping": "^7.10.4", + "@babel/plugin-transform-classes": "^7.10.4", + "@babel/plugin-transform-computed-properties": "^7.10.4", + "@babel/plugin-transform-destructuring": "^7.10.4", + "@babel/plugin-transform-dotall-regex": "^7.10.4", + "@babel/plugin-transform-duplicate-keys": "^7.10.4", + "@babel/plugin-transform-exponentiation-operator": "^7.10.4", + "@babel/plugin-transform-for-of": "^7.10.4", + "@babel/plugin-transform-function-name": "^7.10.4", + "@babel/plugin-transform-literals": "^7.10.4", + "@babel/plugin-transform-member-expression-literals": "^7.10.4", + "@babel/plugin-transform-modules-amd": "^7.10.4", + "@babel/plugin-transform-modules-commonjs": "^7.10.4", + "@babel/plugin-transform-modules-systemjs": "^7.10.4", + "@babel/plugin-transform-modules-umd": "^7.10.4", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", + "@babel/plugin-transform-new-target": "^7.10.4", + "@babel/plugin-transform-object-super": "^7.10.4", + "@babel/plugin-transform-parameters": "^7.10.4", + "@babel/plugin-transform-property-literals": "^7.10.4", + "@babel/plugin-transform-regenerator": "^7.10.4", + "@babel/plugin-transform-reserved-words": "^7.10.4", + "@babel/plugin-transform-shorthand-properties": "^7.10.4", + "@babel/plugin-transform-spread": "^7.10.4", + "@babel/plugin-transform-sticky-regex": "^7.10.4", + "@babel/plugin-transform-template-literals": "^7.10.4", + "@babel/plugin-transform-typeof-symbol": "^7.10.4", + "@babel/plugin-transform-unicode-escapes": "^7.10.4", + "@babel/plugin-transform-unicode-regex": "^7.10.4", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.10.4", + "browserslist": "^4.12.0", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/preset-modules": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", + "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.4.tgz", + "integrity": "sha512-UpTN5yUJr9b4EX2CnGNWIvER7Ab83ibv0pcvvHc4UOdrBI5jb8bj+32cCwPX6xu0mt2daFNjYhoi+X7beH0RSw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.4.tgz", + "integrity": "sha512-BFlgP2SoLO9HJX9WBwN67gHWMBhDX/eDz64Jajd6mR/UAUzqrNMm99d4qHnVaKscAElZoFiPv+JpR/Siud5lXw==", + "dev": true, + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.4.tgz", + "integrity": "sha512-aSy7p5THgSYm4YyxNGz6jZpXf+Ok40QF3aA2LyIONkDHpAcJzDUqlCKXv6peqYUs2gmic849C/t2HKw2a2K20Q==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", + "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "dev": true, + "requires": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + } + }, + "@electron/get": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.12.2.tgz", + "integrity": "sha512-vAuHUbfvBQpYTJ5wB7uVIDq5c/Ry0fiTBMs7lnEYAo/qXXppIVcWdfBr57u6eRnKdVso7KSiH6p/LbQAG6Izrg==", + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^2.0.2", + "global-tunnel-ng": "^2.7.1", + "got": "^9.6.0", + "progress": "^2.0.3", + "sanitize-filename": "^1.6.2", + "sumchecker": "^3.0.1" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==", + "dev": true + }, + "@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==", + "dev": true + }, + "@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==", + "dev": true + }, + "@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "dev": true, + "requires": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "dev": true, + "requires": { + "@hapi/hoek": "^8.3.0" + } + }, + "@intervolga/optimize-cssnano-plugin": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz", + "integrity": "sha512-zN69TnSr0viRSU6cEDIcuPcP67QcpQ6uHACg58FiN9PDrU6SLyGW3MR4tiISbYxy1kDWAVPwD+XwQTWE5cigAA==", + "dev": true, + "requires": { + "cssnano": "^4.0.0", + "cssnano-preset-default": "^4.0.0", + "postcss": "^7.0.0" + } + }, + "@jimp/bmp": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.9.8.tgz", + "integrity": "sha512-CZYQPEC3iUBMuaGWrtIG+GKNl93q/PkdudrCKJR/B96dfNngsmoosEm3LuFgJHEcJIfvnJkNqKw74l+zEiqCbg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "bmp-js": "^0.1.0", + "core-js": "^3.4.1" + } + }, + "@jimp/core": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.9.8.tgz", + "integrity": "sha512-N4GCjcXb0QwR5GBABDK2xQ3cKyaF7LlCYeJEG9mV7G/ynBoRqJe4JA6YKU9Ww9imGkci/4A594nQo8tUIqdcBw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "any-base": "^1.1.0", + "buffer": "^5.2.0", + "core-js": "^3.4.1", + "exif-parser": "^0.1.12", + "file-type": "^9.0.0", + "load-bmfont": "^1.3.1", + "mkdirp": "^0.5.1", + "phin": "^2.9.1", + "pixelmatch": "^4.0.2", + "tinycolor2": "^1.4.1" + }, + "dependencies": { + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + } + } + }, + "@jimp/custom": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.9.8.tgz", + "integrity": "sha512-1UpJjI7fhX02BWLJ/KEqPwkHH60eNkCNeD6hEd+IZdTwLXfZCfFiM5BVlpgiZYZJSsVoRiAL4ne2Q5mCiKPKyw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/core": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/gif": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.9.8.tgz", + "integrity": "sha512-LEbfpcO1sBJIQCJHchZjNlyNxzPjZQQ4X32klpQHZJG58n9FvL7Uuh1rpkrJRbqv3cU3P0ENNtTrsBDxsYwcfA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1", + "omggif": "^1.0.9" + } + }, + "@jimp/jpeg": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.9.8.tgz", + "integrity": "sha512-5u29SUzbZ32ZMmOaz3gO0hXatwSCnsvEAXRCKZoPPgbsPoyFAiZKVxjfLzjkeQF6awkvJ8hZni5chM15SNMg+g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1", + "jpeg-js": "^0.3.4" + } + }, + "@jimp/plugin-blit": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.9.8.tgz", + "integrity": "sha512-6xTDomxJybhBcby1IUVaPydZFhxf+V0DRgfDlVK81kR9kSCoshJpzWqDuWrMqjNEPspPE7jRQwHMs0FdU7mVwQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-blur": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.9.8.tgz", + "integrity": "sha512-dqbxuNFBRbmt35iIRacdgma7nlXklmPThsKcGWNTDmqb/hniK5IC+0xSPzBV4qMI2fLGP39LWHqqDZ0xDz14dA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-circle": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.9.8.tgz", + "integrity": "sha512-+UStXUPCzPqzTixLC8eVqcFcEa6TS+BEM/6/hyM11TDb9sbiMGeUtgpwZP/euR5H5gfpAQDA1Ppzqhh5fuMDlw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-color": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.9.8.tgz", + "integrity": "sha512-SDHxOQsJHpt75hk6+sSlCPc2B3UJlXosFW+iLZ11xX1Qr0IdDtbfYlIoPmjKQFIDUNzqLSue/z7sKQ1OMZr/QA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1", + "tinycolor2": "^1.4.1" + } + }, + "@jimp/plugin-contain": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.9.8.tgz", + "integrity": "sha512-oK52CPt7efozuLYCML7qOmpFeDt3zpU8qq8UZlnjsDs15reU6L8EiUbwYpJvzoEnEOh1ZqamB8F/gymViEO5og==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-cover": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.9.8.tgz", + "integrity": "sha512-nnamtHzMrNd5j5HRSPd1VzpZ8v9YYtUJPtvCdHOOiIjqG72jxJ2kTBlsS3oG5XS64h/2MJwpl/fmmMs1Tj1CmQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-crop": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.9.8.tgz", + "integrity": "sha512-Nv/6AIp4aJmbSIH2uiIqm+kSoShKM8eaX2fyrUTj811kio0hwD3f/vIxrWebvAqwDZjAFIAmMufFoFCVg6caoQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-displace": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.9.8.tgz", + "integrity": "sha512-0OgPjkOVa2xdbqI8P6gBKX/UK36RbaYVrFyXL8Jy9oNF69+LYWyTskuCu9YbGxzlCVjY/JFqQOvrKDbxgMYAKA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-dither": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.9.8.tgz", + "integrity": "sha512-jGM/4ByniZJnmV2fv8hKwyyydXZe/YzvgBcnB8XxzCq8kVR3Imcn+qnd2PEPZzIPKOTH4Cig/zo9Vk9Bs+m5FQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-fisheye": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.9.8.tgz", + "integrity": "sha512-VnsalrD05f4pxG1msjnkwIFi5QveOqRm4y7VkoZKNX+iqs4TvRnH5+HpBnfdMzX/RXBi+Lf/kpTtuZgbOu/QWw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-flip": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.9.8.tgz", + "integrity": "sha512-XbiZ4OfHD6woc0f6Sk7XxB6a7IyMjTRQ4pNU7APjaNxsl3L6qZC8qfCQphWVe3DHx7f3y7jEiPMvNnqRDP1xgA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-gaussian": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.9.8.tgz", + "integrity": "sha512-ZBl5RA6+4XAD+mtqLfiG7u+qd8W5yqq3RBNca8eFqUSVo1v+eB2tzeLel0CWfVC/z6cw93Awm/nVnm6/CL2Oew==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-invert": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.9.8.tgz", + "integrity": "sha512-ESploqCoF6qUv5IWhVLaO5fEcrYZEsAWPFflh6ROiD2mmFKQxfeK+vHnk3IDLHtUwWTkAZQNbk89BVq7xvaNpQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-mask": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.9.8.tgz", + "integrity": "sha512-zSvEisTV4iGsBReitEdnQuGJq9/1xB5mPATadYZmIlp8r5HpD72HQb0WdEtb51/pu9Odt8KAxUf0ASg/PRVUiQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-normalize": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.9.8.tgz", + "integrity": "sha512-dPFBfwTa67K1tRw1leCidQT25R3ozrTUUOpO4jcGFHqXvBTWaR8sML1qxdfOBWs164mE5YpfdTvu6MM/junvCg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-print": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.9.8.tgz", + "integrity": "sha512-nLLPv1/faehRsOjecXXUb6kzhRcZzImO55XuFZ0c90ZyoiHm4UFREwO5sKxHGvpLXS6RnkhvSav4+IWD2qGbEQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1", + "load-bmfont": "^1.4.0" + } + }, + "@jimp/plugin-resize": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.9.8.tgz", + "integrity": "sha512-L80NZ+HKsiKFyeDc6AfneC4+5XACrdL2vnyAVfAAsb3pmamgT/jDInWvvGhyI0Y76vx2w6XikplzEznW/QQvWg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-rotate": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.9.8.tgz", + "integrity": "sha512-bpqzQheISYnBXKyU1lIj46uR7mRs0UhgEREWK70HnvFJSlRshdcoNMIrKamyrJeFdJrkYPSfR/a6D0d5zsWf1Q==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-scale": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.9.8.tgz", + "integrity": "sha512-QU3ZS4Lre8nN66U9dKCOC4FNfaOh/QJFYUmQPKpPS924oYbtnm4OlmsdfpK2hVMSVVyVOis8M+xpA1rDBnIp7w==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-shadow": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.9.8.tgz", + "integrity": "sha512-t/pE+QS3r1ZUxGIQNmwWDI3c5+/hLU+gxXD+C3EEC47/qk3gTBHpj/xDdGQBoObdT/HRjR048vC2BgBfzjj2hg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-threshold": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.9.8.tgz", + "integrity": "sha512-WWmC3lnIwOTPvkKu55w4DUY8Ehlzf3nU98bY0QtIzkqxkAOZU5m+lvgC/JxO5FyGiA57j9FLMIf0LsWkjARj7g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1" + } + }, + "@jimp/plugins": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.9.8.tgz", + "integrity": "sha512-tD+cxS9SuEZaQ1hhAkNKw9TkUAqfoBAhdWPBrEZDr/GvGPrvJR4pYmmpSYhc5IZmMbXfQayHTTGqjj8D18bToA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/plugin-blit": "^0.9.8", + "@jimp/plugin-blur": "^0.9.8", + "@jimp/plugin-circle": "^0.9.8", + "@jimp/plugin-color": "^0.9.8", + "@jimp/plugin-contain": "^0.9.8", + "@jimp/plugin-cover": "^0.9.8", + "@jimp/plugin-crop": "^0.9.8", + "@jimp/plugin-displace": "^0.9.8", + "@jimp/plugin-dither": "^0.9.8", + "@jimp/plugin-fisheye": "^0.9.8", + "@jimp/plugin-flip": "^0.9.8", + "@jimp/plugin-gaussian": "^0.9.8", + "@jimp/plugin-invert": "^0.9.8", + "@jimp/plugin-mask": "^0.9.8", + "@jimp/plugin-normalize": "^0.9.8", + "@jimp/plugin-print": "^0.9.8", + "@jimp/plugin-resize": "^0.9.8", + "@jimp/plugin-rotate": "^0.9.8", + "@jimp/plugin-scale": "^0.9.8", + "@jimp/plugin-shadow": "^0.9.8", + "@jimp/plugin-threshold": "^0.9.8", + "core-js": "^3.4.1", + "timm": "^1.6.1" + } + }, + "@jimp/png": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.9.8.tgz", + "integrity": "sha512-9CqR8d40zQCDhbnXHqcwkAMnvlV0vk9xSyE6LHjkYHS7x18Unsz5txQdsaEkEcXxCrOQSoWyITfLezlrWXRJAA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.8", + "core-js": "^3.4.1", + "pngjs": "^3.3.3" + } + }, + "@jimp/tiff": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.9.8.tgz", + "integrity": "sha512-eMxcpJivJqMByn2dZxUHLeh6qvVs5J/52kBF3TFa3C922OJ97D9l1C1h0WKUCBqFMWzMYapQQ4vwnLgpJ5tkow==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "core-js": "^3.4.1", + "utif": "^2.0.1" + } + }, + "@jimp/types": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.9.8.tgz", + "integrity": "sha512-H5y/uqt0lqJ/ZN8pWqFG+pv8jPAppMKkTMByuC8YBIjWSsornwv44hjiWl93sbYhduLZY8ubz/CbX9jH2X6EwA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/bmp": "^0.9.8", + "@jimp/gif": "^0.9.8", + "@jimp/jpeg": "^0.9.8", + "@jimp/png": "^0.9.8", + "@jimp/tiff": "^0.9.8", + "core-js": "^3.4.1", + "timm": "^1.6.1" + } + }, + "@jimp/utils": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.9.8.tgz", + "integrity": "sha512-UK0Fu0eevQlpRXq5ff4o/71HJlpX9wJMddJjMYg9vUqCCl8ZnumRAljfShHFhGyO+Vc9IzN6dd8Y5JZZTp1KOw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "core-js": "^3.4.1" + } + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "@npmcli/move-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", + "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@soda/friendly-errors-webpack-plugin": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz", + "integrity": "sha512-cWKrGaFX+rfbMrAxVv56DzhPNqOJPZuNIS2HGMELtgGzb+vsMzyig9mml5gZ/hr2BGtSLV+dP2LUEuAL8aG2mQ==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "error-stack-parser": "^2.0.0", + "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "@soda/get-current-script": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@soda/get-current-script/-/get-current-script-1.0.2.tgz", + "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/component-emitter": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", + "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" + }, + "@types/debug": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", + "dev": true + }, + "@types/fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-B42Sxuaz09MhC3DDeW5kubRcQ5by4iuVQ0cRRWM2lggLzAa/KVom0Aft/208NgMvNQQZ86s5rVcqDdn/SH0/mg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/json-schema": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "14.0.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.18.tgz", + "integrity": "sha512-0Z3nS5acM0cIV4JPzrj9g/GH0Et5vmADWtip3YOXOp1NpOLU8V3KoZDc8ny9c1pe/YSYYzQkAWob6dyV/EWg4g==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", + "dev": true + }, + "@types/yargs": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", + "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, + "@vue/babel-helper-vue-jsx-merge-props": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz", + "integrity": "sha512-6tyf5Cqm4m6v7buITuwS+jHzPlIPxbFzEhXR5JGZpbrvOcp1hiQKckd305/3C7C36wFekNTQSxAtgeM0j0yoUw==", + "dev": true + }, + "@vue/babel-plugin-transform-vue-jsx": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.1.2.tgz", + "integrity": "sha512-YfdaoSMvD1nj7+DsrwfTvTnhDXI7bsuh+Y5qWwvQXlD24uLgnsoww3qbiZvWf/EoviZMrvqkqN4CBw0W3BWUTQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", + "html-tags": "^2.0.0", + "lodash.kebabcase": "^4.1.1", + "svg-tags": "^1.0.0" + } + }, + "@vue/babel-preset-app": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-4.4.6.tgz", + "integrity": "sha512-urIa6Qk3lKacLvscrzxMNyYlTqKFcPAUo5MohOjv1ISZ9PssHw693WTOrqSC0XksdMLtp/rnLvc6l5G8Muk0lw==", + "dev": true, + "requires": { + "@babel/core": "^7.9.6", + "@babel/helper-compilation-targets": "^7.9.6", + "@babel/helper-module-imports": "^7.8.3", + "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/plugin-proposal-decorators": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.9.6", + "@babel/preset-env": "^7.9.6", + "@babel/runtime": "^7.9.6", + "@vue/babel-preset-jsx": "^1.1.2", + "babel-plugin-dynamic-import-node": "^2.3.3", + "core-js": "^3.6.5", + "core-js-compat": "^3.6.5", + "semver": "^6.1.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@vue/babel-preset-jsx": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-jsx/-/babel-preset-jsx-1.1.2.tgz", + "integrity": "sha512-zDpVnFpeC9YXmvGIDSsKNdL7qCG2rA3gjywLYHPCKDT10erjxF4U+6ay9X6TW5fl4GsDlJp9bVfAVQAAVzxxvQ==", + "dev": true, + "requires": { + "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", + "@vue/babel-sugar-functional-vue": "^1.1.2", + "@vue/babel-sugar-inject-h": "^1.1.2", + "@vue/babel-sugar-v-model": "^1.1.2", + "@vue/babel-sugar-v-on": "^1.1.2" + } + }, + "@vue/babel-sugar-functional-vue": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.1.2.tgz", + "integrity": "sha512-YhmdJQSVEFF5ETJXzrMpj0nkCXEa39TvVxJTuVjzvP2rgKhdMmQzlJuMv/HpadhZaRVMCCF3AEjjJcK5q/cYzQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@vue/babel-sugar-inject-h": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.1.2.tgz", + "integrity": "sha512-VRSENdTvD5htpnVp7i7DNuChR5rVMcORdXjvv5HVvpdKHzDZAYiLSD+GhnhxLm3/dMuk8pSzV+k28ECkiN5m8w==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@vue/babel-sugar-v-model": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.1.2.tgz", + "integrity": "sha512-vLXPvNq8vDtt0u9LqFdpGM9W9IWDmCmCyJXuozlq4F4UYVleXJ2Fa+3JsnTZNJcG+pLjjfnEGHci2339Kj5sGg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", + "camelcase": "^5.0.0", + "html-tags": "^2.0.0", + "svg-tags": "^1.0.0" + } + }, + "@vue/babel-sugar-v-on": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.1.2.tgz", + "integrity": "sha512-T8ZCwC8Jp2uRtcZ88YwZtZXe7eQrJcfRq0uTFy6ShbwYJyz5qWskRFoVsdTi9o0WEhmQXxhQUewodOSCUPVmsQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", + "camelcase": "^5.0.0" + } + }, + "@vue/cli-overlay": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-4.4.6.tgz", + "integrity": "sha512-fzjg2gWQt+jw5fyLsD9HZNxGNQgZjLDI2s9bLWJwRucdfmncSi9neqA0TZyszGrgcJA4Qu4V5KgV0qwVSBYCaw==", + "dev": true + }, + "@vue/cli-plugin-babel": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-babel/-/cli-plugin-babel-4.4.6.tgz", + "integrity": "sha512-9cX9mN+4DIbcqw3rV6UBOA0t5zikIkrBLQloUzsOBOu5Xb7/UoD7inInFj7bnyHUflr5LqbdWJ+etCQcWAIIXA==", + "dev": true, + "requires": { + "@babel/core": "^7.9.6", + "@vue/babel-preset-app": "^4.4.6", + "@vue/cli-shared-utils": "^4.4.6", + "babel-loader": "^8.1.0", + "cache-loader": "^4.1.0", + "thread-loader": "^2.1.3", + "webpack": "^4.0.0" + } + }, + "@vue/cli-plugin-eslint": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.4.6.tgz", + "integrity": "sha512-3a9rVpOKPQsDgAlRkhmBMHboGobivG/47BbQGE66Z8YJxrgF/AWikP3Jy67SmxtszRkyiWfw4aJFRV9r3MzffQ==", + "dev": true, + "requires": { + "@vue/cli-shared-utils": "^4.4.6", + "eslint-loader": "^2.2.1", + "globby": "^9.2.0", + "inquirer": "^7.1.0", + "webpack": "^4.0.0", + "yorkie": "^2.0.0" + } + }, + "@vue/cli-plugin-router": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.4.6.tgz", + "integrity": "sha512-TkLdn0ZYo3zgn78Rk8doPlR+4UkGjGW2R1eGEaZEkue/mw2VhUWtTk9cKLZaYrw0eY8Ro/j+OV6mD+scyrairg==", + "dev": true, + "requires": { + "@vue/cli-shared-utils": "^4.4.6" + } + }, + "@vue/cli-plugin-vuex": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.4.6.tgz", + "integrity": "sha512-Ho0YzUivn8BLPqFoFypntR8CMTEXYYHVr0GdnZW99XL+DbGw75f+tJfnrV9UFHDTfvZt7uewKiXDMlrzQ0l3Ug==", + "dev": true + }, + "@vue/cli-service": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-4.4.6.tgz", + "integrity": "sha512-k5OFGh2NnvRymCyq9DfBiNJvECUuun3pl5KMm3557IZyA5E5csv+RHoSW3dX8HHe0zXq18g52VswP1llvR9POw==", + "dev": true, + "requires": { + "@intervolga/optimize-cssnano-plugin": "^1.0.5", + "@soda/friendly-errors-webpack-plugin": "^1.7.1", + "@soda/get-current-script": "^1.0.0", + "@vue/cli-overlay": "^4.4.6", + "@vue/cli-plugin-router": "^4.4.6", + "@vue/cli-plugin-vuex": "^4.4.6", + "@vue/cli-shared-utils": "^4.4.6", + "@vue/component-compiler-utils": "^3.1.2", + "@vue/preload-webpack-plugin": "^1.1.0", + "@vue/web-component-wrapper": "^1.2.0", + "acorn": "^7.2.0", + "acorn-walk": "^7.1.1", + "address": "^1.1.2", + "autoprefixer": "^9.8.0", + "browserslist": "^4.12.0", + "cache-loader": "^4.1.0", + "case-sensitive-paths-webpack-plugin": "^2.3.0", + "cli-highlight": "^2.1.4", + "clipboardy": "^2.3.0", + "cliui": "^6.0.0", + "copy-webpack-plugin": "^5.1.1", + "css-loader": "^3.5.3", + "cssnano": "^4.1.10", + "debug": "^4.1.1", + "default-gateway": "^5.0.5", + "dotenv": "^8.2.0", + "dotenv-expand": "^5.1.0", + "file-loader": "^4.2.0", + "fs-extra": "^7.0.1", + "globby": "^9.2.0", + "hash-sum": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "launch-editor-middleware": "^2.2.1", + "lodash.defaultsdeep": "^4.6.1", + "lodash.mapvalues": "^4.6.0", + "lodash.transform": "^4.6.0", + "mini-css-extract-plugin": "^0.9.0", + "minimist": "^1.2.5", + "pnp-webpack-plugin": "^1.6.4", + "portfinder": "^1.0.26", + "postcss-loader": "^3.0.0", + "ssri": "^7.1.0", + "terser-webpack-plugin": "^2.3.6", + "thread-loader": "^2.1.3", + "url-loader": "^2.2.0", + "vue-loader": "^15.9.2", + "vue-style-loader": "^4.1.2", + "webpack": "^4.0.0", + "webpack-bundle-analyzer": "^3.8.0", + "webpack-chain": "^6.4.0", + "webpack-dev-server": "^3.11.0", + "webpack-merge": "^4.2.2" + }, + "dependencies": { + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + }, + "cacache": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", + "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", + "dev": true, + "requires": { + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "p-map": "^3.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^2.7.1", + "ssri": "^7.0.0", + "unique-filename": "^1.1.1" + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "ssri": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", + "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "minipass": "^3.1.1" + } + }, + "terser-webpack-plugin": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.7.tgz", + "integrity": "sha512-xzYyaHUNhzgaAdBsXxk2Yvo/x1NJdslUaussK3fdpBbvttm1iIwU+c26dj9UxJcwk2c5UWt5F55MUTIA8BE7Dg==", + "dev": true, + "requires": { + "cacache": "^13.0.1", + "find-cache-dir": "^3.3.1", + "jest-worker": "^25.4.0", + "p-limit": "^2.3.0", + "schema-utils": "^2.6.6", + "serialize-javascript": "^3.1.0", + "source-map": "^0.6.1", + "terser": "^4.6.12", + "webpack-sources": "^1.4.3" + } + } + } + }, + "@vue/cli-shared-utils": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.4.6.tgz", + "integrity": "sha512-ba+FZZCjiTSu2otnLjY4qXqASe7ZIQ/QBljk5oRPgqrR0p1NUkDPUcZhqa041aOaSW1yAfSfhOD7Q84nMnWhzQ==", + "dev": true, + "requires": { + "@hapi/joi": "^15.0.1", + "chalk": "^2.4.2", + "execa": "^1.0.0", + "launch-editor": "^2.2.1", + "lru-cache": "^5.1.1", + "node-ipc": "^9.1.1", + "open": "^6.3.0", + "ora": "^3.4.0", + "read-pkg": "^5.1.1", + "request": "^2.88.2", + "semver": "^6.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@vue/component-compiler-utils": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.1.2.tgz", + "integrity": "sha512-QLq9z8m79mCinpaEeSURhnNCN6djxpHw0lpP/bodMlt5kALfONpryMthvnrQOlTcIKoF+VoPi+lPHUYeDFPXug==", + "dev": true, + "requires": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.14", + "postcss-selector-parser": "^6.0.2", + "prettier": "^1.18.2", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "@vue/preload-webpack-plugin": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.1.tgz", + "integrity": "sha512-8VCoJeeH8tCkzhkpfOkt+abALQkS11OIHhte5MBzYaKMTqK0A3ZAKEUVAffsOklhEv7t0yrQt696Opnu9oAx+w==", + "dev": true + }, + "@vue/web-component-wrapper": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vue/web-component-wrapper/-/web-component-wrapper-1.2.0.tgz", + "integrity": "sha512-Xn/+vdm9CjuC9p3Ae+lTClNutrVhsXpzxvoTXXtoys6kVRX9FkueSUAqSWAyZntmVLlR4DosBV4pH8y5Z/HbUw==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "address": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", + "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", + "dev": true + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.0.tgz", + "integrity": "sha512-eyoaac3btgU8eJlvh01En8OCKzRqlLe2G5jDsCr3RiE2uLGMEEB1aaGwVVpwR8M95956tGH6R+9edC++OvzaVw==", + "dev": true + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", + "dev": true + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "app-builder-bin": { + "version": "3.5.9", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.5.9.tgz", + "integrity": "sha512-NSjtqZ3x2kYiDp3Qezsgukx/AUzKPr3Xgf9by4cYt05ILWGAptepeeu0Uv+7MO+41o6ujhLixTou8979JGg2Kg==", + "dev": true + }, + "app-builder-lib": { + "version": "22.7.0", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.7.0.tgz", + "integrity": "sha512-blRKwV8h0ztualXS50ciCTo39tbuDGNS+ldcy8+KLvKXuT6OpYnSJ7M6MSfPT+xWatshMHJV1rJx3Tl+k/Sn/g==", + "dev": true, + "requires": { + "7zip-bin": "~5.0.3", + "@develar/schema-utils": "~2.6.5", + "async-exit-hook": "^2.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "22.7.0", + "builder-util-runtime": "8.7.1", + "chromium-pickle-js": "^0.2.0", + "debug": "^4.2.0", + "ejs": "^3.1.3", + "electron-publish": "22.7.0", + "fs-extra": "^9.0.0", + "hosted-git-info": "^3.0.4", + "is-ci": "^2.0.0", + "isbinaryfile": "^4.0.6", + "js-yaml": "^3.14.0", + "lazy-val": "^1.0.4", + "minimatch": "^3.0.4", + "normalize-package-data": "^2.5.0", + "read-config-file": "6.0.0", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.2", + "temp-file": "^3.3.7" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ejs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz", + "integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==", + "dev": true, + "requires": { + "jake": "^10.6.1" + } + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.5.tgz", + "integrity": "sha512-i4dpK6xj9BIpVOTboXIlKG9+8HMKggcrMX7WA24xZtKwX0TPelq/rbaS5rCKeNX8sJXZJGdSxpnEGtta+wismQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "arch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz", + "integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "args": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz", + "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==", + "dev": true, + "requires": { + "camelcase": "5.0.0", + "chalk": "2.4.2", + "leven": "2.1.0", + "mri": "1.1.4" + }, + "dependencies": { + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "dev": true + } + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "9.8.4", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.4.tgz", + "integrity": "sha512-84aYfXlpUe45lvmS+HoAWKCkirI/sw4JK0/bTeeqgHYco3dcsOn0NqdejISjptsYwNji/21dnkDri9PsYKk89A==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001087", + "colorette": "^1.2.0", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", + "dev": true + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + } + }, + "babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "dev": true, + "requires": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bfj": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bluebird-lst": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", + "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", + "dev": true, + "requires": { + "bluebird": "^3.5.5" + } + }, + "bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM=", + "dev": true + }, + "bn.js": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + }, + "dependencies": { + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "boolean": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", + "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==", + "optional": true + }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "browserify-sign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz", + "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.2", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz", + "integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001093", + "electron-to-chromium": "^1.3.488", + "escalade": "^3.0.1", + "node-releases": "^1.1.58" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-json/-/buffer-json-2.0.0.tgz", + "integrity": "sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builder-util": { + "version": "22.7.0", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.7.0.tgz", + "integrity": "sha512-UV3MKL0mwjMq2y9JlBf28Cegpj0CrIXcjGkO0TXn+QZ6Yy9rY6lHOuUvpQ19ct2Qh1o+QSwH3Q1nKUf5viJBBg==", + "dev": true, + "requires": { + "7zip-bin": "~5.0.3", + "@types/debug": "^4.1.5", + "@types/fs-extra": "^9.0.1", + "app-builder-bin": "3.5.9", + "bluebird-lst": "^1.0.9", + "builder-util-runtime": "8.7.1", + "chalk": "^4.0.0", + "debug": "^4.2.0", + "fs-extra": "^9.0.0", + "is-ci": "^2.0.0", + "js-yaml": "^3.14.0", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.3.7" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + } + } + }, + "builder-util-runtime": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.1.tgz", + "integrity": "sha512-uEBH1nAnTvzjcsrh2XI3qOzJ39h0+9kuIuwj+kCc3a07TZNGShfJcai8fFzL3mNgGjEFxoq+XMssR11r+FOFSg==", + "dev": true, + "requires": { + "debug": "^4.2.0", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + } + } + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cache-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-4.1.0.tgz", + "integrity": "sha512-ftOayxve0PwKzBF/GLsZNC9fJBXl8lkZE3TOsjkboHfVHVkL39iUEs1FO07A33mizmci5Dudt38UZrrYXDtbhw==", + "dev": true, + "requires": { + "buffer-json": "^2.0.0", + "find-cache-dir": "^3.0.0", + "loader-utils": "^1.2.3", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "schema-utils": "^2.0.0" + }, + "dependencies": { + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + } + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001178", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001178.tgz", + "integrity": "sha512-VtdZLC0vsXykKni8Uztx45xynytOi71Ufx9T8kHptSw9AL4dpqailUJJHavttuzUe1KYuBYtChiWv+BAb7mPmQ==", + "dev": true + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chart.js": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.3.tgz", + "integrity": "sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==", + "requires": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "chartjs-color": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", + "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", + "requires": { + "chartjs-color-string": "^0.6.0", + "color-convert": "^1.9.3" + } + }, + "chartjs-color-string": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", + "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "requires": { + "color-name": "^1.0.0" + } + }, + "chartjs-plugin-streaming": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/chartjs-plugin-streaming/-/chartjs-plugin-streaming-1.8.0.tgz", + "integrity": "sha512-r7kHyNvSAz12J+W5FBmI/K400z4MXqfNYhA5xaTKJ6PA3ZA6Vq+2d5/OCGyVZF/7UsUDWDRK5tHHYj8jqe+nOg==" + }, + "check-types": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", + "dev": true + }, + "chokidar": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", + "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=", + "dev": true + }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-boxes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-highlight": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.4.tgz", + "integrity": "sha512-s7Zofobm20qriqDoU9sXptQx0t2R9PEgac92mENNm7xaEe1hn71IIMsXMK+6encA6WRCWWxIGQbipr3q998tlQ==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "highlight.js": "^9.6.0", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^5.1.1", + "yargs": "^15.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cli-spinners": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.3.0.tgz", + "integrity": "sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==", + "dev": true + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "clipboardy": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz", + "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", + "dev": true, + "requires": { + "arch": "^2.1.1", + "execa": "^1.0.0", + "is-wsl": "^2.1.1" + }, + "dependencies": { + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + } + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", + "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "optional": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "dev": true, + "requires": { + "bluebird": "^3.1.1" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz", + "integrity": "sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ==", + "dev": true, + "requires": { + "cacache": "^12.0.3", + "find-cache-dir": "^2.1.0", + "glob-parent": "^3.1.0", + "globby": "^7.1.1", + "is-glob": "^4.0.1", + "loader-utils": "^1.2.3", + "minimatch": "^3.0.4", + "normalize-path": "^3.0.0", + "p-limit": "^2.2.1", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + } + } + }, + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" + }, + "core-js-compat": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", + "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "dev": true, + "requires": { + "browserslist": "^4.8.5", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-js-pure": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", + "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + } + }, + "css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-what": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.3.0.tgz", + "integrity": "sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.7", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "cssnano-preset-default": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", + "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "dev": true, + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.2", + "postcss-unique-selectors": "^4.0.1" + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "dev": true + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "dev": true + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true + }, + "csso": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz", + "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==", + "dev": true, + "requires": { + "css-tree": "1.0.0-alpha.39" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz", + "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==", + "dev": true, + "requires": { + "mdn-data": "2.0.6", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz", + "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-3.2.0.tgz", + "integrity": "sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==", + "dev": true, + "requires": { + "xregexp": "^4.2.4" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", + "dev": true + }, + "default-gateway": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-5.0.5.tgz", + "integrity": "sha512-z2RnruVmj8hVMmAnEJMTIJNijhKCDiGjbLP+BHJFOT7ld3Bo5qcIBpVYDniqhbMIIf+jZDlkP2MkPXiQy/DBLA==", + "dev": true, + "requires": { + "execa": "^3.3.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "dmg-builder": { + "version": "22.7.0", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.7.0.tgz", + "integrity": "sha512-5Ea2YEz6zSNbyGzZD+O9/MzmaXb6oa15cSKWo4JQ1xP4rorOpte7IOj2jcwYjtc+Los2gu1lvT314OC1OZIWgg==", + "dev": true, + "requires": { + "app-builder-lib": "22.7.0", + "builder-util": "22.7.0", + "fs-extra": "^9.0.0", + "iconv-lite": "^0.5.1", + "js-yaml": "^3.14.0", + "sanitize-filename": "^1.6.3" + }, + "dependencies": { + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "iconv-lite": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + } + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "dev": true + } + } + }, + "dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "dev": true + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "dev": true + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "easy-stack": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", + "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "dev": true + }, + "electron": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-9.1.1.tgz", + "integrity": "sha512-BYvroBLV9x7G4iN33P/IxeZqwjl62/9VuBAF1CoM0m6OeheaiLog1ZMKLlCqVXycJvvrAvLHc454DDEmwnqqhA==", + "requires": { + "@electron/get": "^1.0.1", + "@types/node": "^12.0.12", + "extract-zip": "^1.0.3" + }, + "dependencies": { + "@types/node": { + "version": "12.12.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.52.tgz", + "integrity": "sha512-oNJSI5bzuAH4f4I5JatPf4jytM6vQPWMw8JDOH68mNMyOmSqBkctHBfsyaBU3Su+dhYd8E+tDtPJWUXsyO5Msg==" + } + } + }, + "electron-builder": { + "version": "22.7.0", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.7.0.tgz", + "integrity": "sha512-t6E3oMutpST64YWbZCg7HodEwJOsnjUF1vnDIHm2MW6CFZPX8tlCK6efqaV66LU0E0Nkp/JH6TE5bCqQ1+VdPQ==", + "dev": true, + "requires": { + "@types/yargs": "^15.0.5", + "app-builder-lib": "22.7.0", + "bluebird-lst": "^1.0.9", + "builder-util": "22.7.0", + "builder-util-runtime": "8.7.1", + "chalk": "^4.0.0", + "dmg-builder": "22.7.0", + "fs-extra": "^9.0.0", + "is-ci": "^2.0.0", + "lazy-val": "^1.0.4", + "read-config-file": "6.0.0", + "sanitize-filename": "^1.6.3", + "update-notifier": "^4.1.0", + "yargs": "^15.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + } + } + }, + "electron-devtools-installer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/electron-devtools-installer/-/electron-devtools-installer-3.1.1.tgz", + "integrity": "sha512-g2D4J6APbpsiIcnLkFMyKZ6bOpEJ0Ltcc2m66F7oKUymyGAt628OWeU9nRZoh1cNmUs/a6Cls2UfOmsZtE496Q==", + "dev": true, + "requires": { + "rimraf": "^3.0.2", + "semver": "^7.2.1", + "unzip-crx-3": "^0.2.0" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "electron-icon-builder": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/electron-icon-builder/-/electron-icon-builder-1.0.2.tgz", + "integrity": "sha512-ti7Xp6UmOmq+AThfrnmPVaCiMYLWaVcyTVkPommmFHb4GkQhtOeTJtwtko+FnnC7RFBLo4MogDrz611lNdSVPw==", + "dev": true, + "requires": { + "args": "^5.0.0", + "icon-gen": "^2.0.0", + "jimp": "^0.9.3" + } + }, + "electron-publish": { + "version": "22.7.0", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.7.0.tgz", + "integrity": "sha512-hmU69xlb6vvAV3QfpHYDlkdZMFdBAgDbptoxbLFrnTq5bOkcL8AaDbvxeoZ4+lvqgs29NwqGpkHo2oN+p/hCfg==", + "dev": true, + "requires": { + "@types/fs-extra": "^9.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "22.7.0", + "builder-util-runtime": "8.7.1", + "chalk": "^4.0.0", + "fs-extra": "^9.0.0", + "lazy-val": "^1.0.4", + "mime": "^2.4.5" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + } + } + }, + "electron-to-chromium": { + "version": "1.3.490", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.490.tgz", + "integrity": "sha512-jKJF1mKXrQkT0ZiuJ/oV63Q02hAeWz0GGt/z6ryc518uCHtMyS9+wYAysZtBQ8rsjqFPAYXV4TIz5GQ8xyubPA==", + "dev": true + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz", + "integrity": "sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "dev": true + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==" + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", + "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", + "dev": true, + "requires": { + "stackframe": "^1.1.1" + } + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "escalade": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.1.tgz", + "integrity": "sha512-DR6NO3h9niOT+MZs7bjxlj2a1k+POu5RN8CLTPX2+i78bRi9eLe7+0zXgUHMnGXWybYcL61E9hGhPKqedy8tQA==", + "dev": true + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "eslint-loader": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.2.1.tgz", + "integrity": "sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg==", + "dev": true, + "requires": { + "loader-fs-cache": "^1.0.0", + "loader-utils": "^1.0.2", + "object-assign": "^4.0.1", + "object-hash": "^1.1.4", + "rimraf": "^2.6.1" + } + }, + "eslint-plugin-vue": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-6.2.2.tgz", + "integrity": "sha512-Nhc+oVAHm0uz/PkJAWscwIT4ijTrK5fqNqz9QB1D35SbbuMG1uB6Yr5AJpvPSWg+WOw7nYNswerYh0kOk64gqQ==", + "dev": true, + "requires": { + "natural-compare": "^1.4.0", + "semver": "^5.6.0", + "vue-eslint-parser": "^7.0.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", + "dev": true + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha1-WKnS1ywCwfbwKg70qRZicrd2CSI=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "requires": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + } + }, + "file-saver": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz", + "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==" + }, + "file-type": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz", + "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==", + "dev": true + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "file-url": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-2.0.2.tgz", + "integrity": "sha1-6VF4TXkJUSfTcTApqwY/QIGMoq4=", + "dev": true + }, + "filelist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", + "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", + "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "friendly-errors-webpack-plugin": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.0.tgz", + "integrity": "sha512-K27M3VK30wVoOarP651zDmb93R9zF28usW4ocaK3mfQeIEI5BPht/EzZs5E8QLLwbLRJQMwscAjDxYPb1FuNiw==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "error-stack-parser": "^2.0.0", + "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "dev": true, + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + }, + "dependencies": { + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", + "dev": true + } + } + }, + "global-agent": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.12.tgz", + "integrity": "sha512-caAljRMS/qcDo69X9BfkgrihGUgGx44Fb4QQToNQjsiWh+YlQ66uqYVAdA8Olqit+5Ng0nkz09je3ZzANMZcjg==", + "optional": true, + "requires": { + "boolean": "^3.0.1", + "core-js": "^3.6.5", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "optional": true + } + } + }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "dev": true, + "requires": { + "ini": "^1.3.5" + } + }, + "global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "optional": true, + "requires": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globalthis": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.1.tgz", + "integrity": "sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw==", + "optional": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hasha": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", + "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", + "dev": true, + "requires": { + "is-stream": "^1.0.1", + "pinkie-promise": "^2.0.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "highlight.js": { + "version": "9.18.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", + "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "dev": true + }, + "html-entities": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", + "dev": true + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + } + } + }, + "html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", + "dev": true + }, + "html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "dev": true, + "requires": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + } + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "icon-gen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/icon-gen/-/icon-gen-2.0.0.tgz", + "integrity": "sha512-Asj0rWMoFDY3AHLsZdx8UgHX7AIh/9u5ZTXYWJYH+2n8HqHQr655ATdoa1yQLidmm2fnTYlob+Rm4zzrjWr5Bw==", + "dev": true, + "requires": { + "del": "^3.0.0", + "mkdirp": "^0.5.1", + "pngjs-nozlib": "^1.0.0", + "svg2png": "4.1.1", + "uuid": "^3.3.2" + }, + "dependencies": { + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "inquirer": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.0.tgz", + "integrity": "sha512-K+LZp6L/6eE5swqIcVXrxl21aGDU4S50gKH0/d96OMQnSBCyGyZl/oZhbkVmdp5sBoINHd4xZvFSARh2dk6DWA==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "dependencies": { + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + } + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-docker": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", + "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dev": true, + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + }, + "dependencies": { + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + } + } + }, + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "dev": true, + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isbinaryfile": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.6.tgz", + "integrity": "sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "jake": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", + "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", + "dev": true, + "requires": { + "async": "0.9.x", + "chalk": "^2.4.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + } + } + }, + "javascript-stringify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.0.1.tgz", + "integrity": "sha512-yV+gqbd5vaOYjqlbk16EG89xB5udgjqQF3C5FAORDg4f/IS1Yc5ERCv5e/57yBcfJYw05V5JyIXabhwb75Xxow==", + "dev": true + }, + "jest-worker": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", + "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jimp": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.9.8.tgz", + "integrity": "sha512-DHN4apKMwLIvD/TKO9tFfPuankNuVK98vCwHm/Jv9z5cJnrd38xhi+4I7IAGmDU3jIDlrEVhzTkFH1Ymv5yTQQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/custom": "^0.9.8", + "@jimp/plugins": "^0.9.8", + "@jimp/types": "^0.9.8", + "core-js": "^3.4.1", + "regenerator-runtime": "^0.13.3" + } + }, + "jpeg-js": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz", + "integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==", + "dev": true + }, + "js-message": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", + "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=", + "dev": true + }, + "js-queue": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", + "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "dev": true, + "requires": { + "easy-stack": "^1.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jszip": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", + "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "kew": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, + "launch-editor": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.2.1.tgz", + "integrity": "sha512-On+V7K2uZK6wK7x691ycSUbLD/FyKKelArkbaAMSSJU8JmqmhwN2+mnJDNINuJWSrh2L0kDk+ZQtbC/gOWUwLw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "shell-quote": "^1.6.1" + } + }, + "launch-editor-middleware": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.2.1.tgz", + "integrity": "sha512-s0UO2/gEGiCgei3/2UN3SMuUj1phjQN8lcpnvgLSz26fAzNWPQ6Nf/kF5IFClnfU2ehp6LrmKdMU/beveO+2jg==", + "dev": true, + "requires": { + "launch-editor": "^2.2.1" + } + }, + "lazy-val": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.4.tgz", + "integrity": "sha512-u93kb2fPbIrfzBuLjZE+w+fJbUUMhNDXxNmMfaqNgpfQf1CO5ZSe2LfsnBqVAk7i/2NF48OSoRj+Xe2VT+lE8Q==", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "dev": true, + "requires": { + "leven": "^3.1.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "load-bmfont": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz", + "integrity": "sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==", + "dev": true, + "requires": { + "buffer-equal": "0.0.1", + "mime": "^1.3.4", + "parse-bmfont-ascii": "^1.0.3", + "parse-bmfont-binary": "^1.0.5", + "parse-bmfont-xml": "^1.1.4", + "phin": "^2.9.1", + "xhr": "^2.0.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + } + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "loader-fs-cache": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz", + "integrity": "sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA==", + "dev": true, + "requires": { + "find-cache-dir": "^0.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "find-cache-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "^1.0.0" + } + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", + "dev": true + }, + "lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", + "dev": true + }, + "lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.transform": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.transform/-/lodash.transform-4.6.0.tgz", + "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "loglevel": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz", + "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "optional": true, + "requires": { + "escape-string-regexp": "^4.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "optional": true + } + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "dev": true, + "requires": { + "dom-walk": "^0.1.0" + } + }, + "mini-css-extract-plugin": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", + "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.3.tgz", + "integrity": "sha512-cFOknTvng5vqnwOpDsZTWhNll6Jf8o2x+/diplafmxpuIymAjzoOolZG0VvQf3V2HgqzJNhnuKHYp2BqDgz8IQ==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz", + "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", + "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "mri": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", + "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + }, + "node-ipc": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", + "integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "dev": true, + "requires": { + "event-pubsub": "4.3.0", + "js-message": "1.0.5", + "js-queue": "2.0.0" + } + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-releases": { + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "optional": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "optional": true + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-hash": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", + "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", + "dev": true + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, + "object-is": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", + "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "opener": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "dev": true + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "ora": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU=", + "dev": true + }, + "parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY=", + "dev": true + }, + "parse-bmfont-xml": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", + "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", + "dev": true, + "requires": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.4.5" + } + }, + "parse-headers": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", + "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==", + "dev": true + }, + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "parse5-htmlparser2-tree-adapter": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-5.1.1.tgz", + "integrity": "sha512-CF+TKjXqoqyDwHqBhFQ+3l5t83xYi6fVT1tQNg+Ye0JRLnTxWvIroCjEp1A0k4lneHNBGnICUf0cfYVYGEazqw==", + "dev": true, + "requires": { + "parse5": "^5.1.1" + } + }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "phantomjs-prebuilt": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz", + "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3", + "extract-zip": "^1.6.5", + "fs-extra": "^1.0.0", + "hasha": "^2.2.0", + "kew": "^0.7.0", + "progress": "^1.1.8", + "request": "^2.81.0", + "request-progress": "^2.0.1", + "which": "^1.2.10" + }, + "dependencies": { + "fs-extra": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + } + } + }, + "phin": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", + "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pixelmatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", + "integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=", + "dev": true, + "requires": { + "pngjs": "^3.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true + }, + "pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true + }, + "pngjs-nozlib": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pngjs-nozlib/-/pngjs-nozlib-1.0.0.tgz", + "integrity": "sha1-nmTWAs/pzOTZ1Zl9BodCmnPwt9c=", + "dev": true + }, + "pnp-webpack-plugin": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", + "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==", + "dev": true, + "requires": { + "ts-pnp": "^1.1.6" + } + }, + "popper.js": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz", + "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==" + }, + "portfinder": { + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz", + "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-calc": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz", + "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==", + "dev": true, + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-load-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", + "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", + "dev": true, + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.16", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.0" + } + }, + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", + "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "dev": true, + "requires": { + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true, + "optional": true + }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "optional": true + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "pupa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", + "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "read-config-file": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.0.0.tgz", + "integrity": "sha512-PHjROSdpceKUmqS06wqwP92VrM46PZSTubmNIMJ5DrMwg1OgenSTSEHIkCa6TiOJ+y/J0xnG1fFwG3M+Oi1aNA==", + "dev": true, + "requires": { + "dotenv": "^8.2.0", + "dotenv-expand": "^5.1.0", + "js-yaml": "^3.13.1", + "json5": "^2.1.2", + "lazy-val": "^1.0.4" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "regenerate": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", + "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "regexpu-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "registry-auth-token": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.0.tgz", + "integrity": "sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "renderkid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", + "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "dev": true, + "requires": { + "css-select": "^1.1.0", + "dom-converter": "^0.2", + "htmlparser2": "^3.3.0", + "strip-ansi": "^3.0.0", + "utila": "^0.4.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "request-progress": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", + "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", + "dev": true, + "requires": { + "throttleit": "^1.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "roarr": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.3.tgz", + "integrity": "sha512-AEjYvmAhlyxOeB9OqPUzQCo3kuAkNfuDk/HqWbZdFsqDFpapkTjiw+p4svNEoRLvuqNTxqfL+s+gtD4eDgZ+CA==", + "optional": true, + "requires": { + "boolean": "^3.0.0", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "optional": true + } + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", + "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", + "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", + "dev": true, + "requires": { + "node-forge": "^0.10.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "optional": true, + "requires": { + "type-fest": "^0.13.1" + }, + "dependencies": { + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true + } + } + }, + "serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-loader": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shebang-loader/-/shebang-loader-0.0.1.tgz", + "integrity": "sha1-pAAEldRMzu++xjQ157FphWn6Uuw=", + "dev": true + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "socket.io-client": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-3.0.4.tgz", + "integrity": "sha512-qMvBuS+W9JIN2mkfAWDCxuIt+jpIKDf8C0604zEqx1JrPaPSS6cN0F3B2GYWC83TqBeVJXW66GFxWV3KD88n0Q==", + "requires": { + "@types/component-emitter": "^1.2.10", + "backo2": "1.0.2", + "component-bind": "1.0.0", + "component-emitter": "~1.3.0", + "debug": "~4.1.0", + "engine.io-client": "~4.0.0", + "parseuri": "0.0.6", + "socket.io-parser": "~4.0.1" + }, + "dependencies": { + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "engine.io-client": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.5.tgz", + "integrity": "sha512-1lkn0QdekHQPMTcxUh8LqIuxQHNtKV5GvqkQzmZ1rYKAvB6puMm13U7K1ps3OQZ4joE46asQiAKrcdL9weNEVw==", + "requires": { + "base64-arraybuffer": "0.1.4", + "component-emitter": "~1.3.0", + "debug": "~4.1.0", + "engine.io-parser": "~4.0.1", + "has-cors": "1.1.0", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.2.1", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + } + }, + "engine.io-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", + "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", + "requires": { + "base64-arraybuffer": "0.1.4" + } + }, + "socket.io-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.2.tgz", + "integrity": "sha512-Bs3IYHDivwf+bAAuW/8xwJgIiBNtlvnjYRc4PbXgniLmcP1BrakBoq/QhO24rgtgW7VZ7uAaswRGxutUnlAK7g==", + "requires": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.1.0" + } + }, + "ws": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", + "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==" + } + } + }, + "sockjs": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", + "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.4.0", + "websocket-driver": "0.6.5" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "sockjs-client": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "split2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.1.1.tgz", + "integrity": "sha512-emNzr1s7ruq4N+1993yht631/JH+jaj0NYBosuKmLcq+JkGQ9MmTw1RB1fGaTCzUuseRIClrlSLHRNYGwWQ58Q==", + "dev": true, + "requires": { + "readable-stream": "^3.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stackframe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", + "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", + "dev": true + }, + "stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + } + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "requires": { + "debug": "^4.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "svg2png": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/svg2png/-/svg2png-4.1.1.tgz", + "integrity": "sha1-a54DmKpBh3i2Q24Sei+38A1JnCg=", + "dev": true, + "requires": { + "file-url": "^2.0.0", + "phantomjs-prebuilt": "^2.1.14", + "pn": "^1.0.0", + "yargs": "^6.5.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^4.2.0" + } + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } + } + } + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "tar": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.2.tgz", + "integrity": "sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.0", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "temp-file": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.3.7.tgz", + "integrity": "sha512-9tBJKt7GZAQt/Rg0QzVWA8Am8c1EFl+CAv04/aBVqlx5oyfQ508sFIABshQ0xbZu6mBrFLWIUXO/bbLYghW70g==", + "dev": true, + "requires": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "term-size": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", + "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", + "dev": true + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz", + "integrity": "sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^3.1.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "thread-loader": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/thread-loader/-/thread-loader-2.1.3.tgz", + "integrity": "sha512-wNrVKH2Lcf8ZrWxDF/khdlLlsTMczdcwPA9VEK4c2exlEPynYWxi9op3nPTo5lAnDIkE0rQEB3VBP+4Zncc9Hg==", + "dev": true, + "requires": { + "loader-runner": "^2.3.1", + "loader-utils": "^1.1.0", + "neo-async": "^2.6.0" + } + }, + "throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "through2-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-map/-/through2-map-3.0.0.tgz", + "integrity": "sha1-psMCbOY7SJipl9VAUGtm/9lw8nE=", + "dev": true, + "requires": { + "through2": "~2.0.0", + "xtend": "^4.0.0" + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timm": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/timm/-/timm-1.7.1.tgz", + "integrity": "sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==", + "dev": true + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "tinycolor2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", + "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true + }, + "ts-pnp": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", + "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==", + "dev": true + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typeface-roboto": { + "version": "0.0.75", + "resolved": "https://registry.npmjs.org/typeface-roboto/-/typeface-roboto-0.0.75.tgz", + "integrity": "sha512-VrR/IiH00Z1tFP4vDGfwZ1esNqTiDMchBEXYY9kilT6wRGgFoCAlgkEUMHb1E3mB0FsfZhv756IF0+R+SFPfdg==" + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "unzip-crx": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/unzip-crx/-/unzip-crx-0.2.0.tgz", + "integrity": "sha1-TAuqi9rHViVnVL7KeEPBPXuFjBg=", + "dev": true, + "requires": { + "jszip": "^3.1.0", + "mkdirp": "^0.5.1", + "yaku": "^0.16.6" + } + }, + "unzip-crx-3": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz", + "integrity": "sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==", + "dev": true, + "requires": { + "jszip": "^3.1.0", + "mkdirp": "^0.5.1", + "yaku": "^0.16.6" + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "update-notifier": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", + "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", + "dev": true, + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz", + "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.5.0" + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + }, + "dependencies": { + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=" + }, + "utif": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz", + "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==", + "dev": true, + "requires": { + "pako": "^1.0.5" + } + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz", + "integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==" + }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "vue": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz", + "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==" + }, + "vue-chartjs": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-3.5.0.tgz", + "integrity": "sha512-yWNhG3B6g6lvYqNInP0WaDWNZG/SNb6XnltkjR0wYC5pmLm6jvdiotj8er7Mui8qkJGfLZe6ULjrZdHWjegAUg==" + }, + "vue-class-component": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.6.tgz", + "integrity": "sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==" + }, + "vue-cli-plugin-electron-builder": { + "version": "2.0.0-rc.4", + "resolved": "https://registry.npmjs.org/vue-cli-plugin-electron-builder/-/vue-cli-plugin-electron-builder-2.0.0-rc.4.tgz", + "integrity": "sha512-yA/MUY8t/OiCnNea0zyVhFuewd+g/mGTc7MNbE/e/sb9XjBsEW517CD5KhLIu5/uItmvmMOpjLSTDILqE2RJFA==", + "dev": true, + "requires": { + "chokidar": "^3.0.2", + "electron-builder": "^22.2.0", + "execa": "^4.0.0", + "friendly-errors-webpack-plugin": "^1.7.0", + "fs-extra": "^9.0.1", + "lodash.merge": "^4.6.1", + "portfinder": "^1.0.16", + "pumpify": "^2.0.1", + "semver": "^7.3.2", + "shebang-loader": "^0.0.1", + "split2": "^3.0.0", + "terser-webpack-plugin": "^3.0.3", + "through2-filter": "^3.0.0", + "through2-map": "^3.0.0", + "unzip-crx": "^0.2.0", + "webpack": "^4.18.0", + "webpack-chain": "^6.0.0", + "webpack-merge": "^4.2.2", + "yargs": "^15.3.1" + }, + "dependencies": { + "cacache": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz", + "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==", + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "dev": true, + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "execa": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "jest-worker": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.1.0.tgz", + "integrity": "sha512-Z9P5pZ6UC+kakMbNJn+tA2RdVdNX5WH1x+5UCBZ9MxIK24pjYtFt96fK+UwBTrjLYm232g1xz0L3eTh51OW+yQ==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + }, + "dependencies": { + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + } + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "pumpify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "dev": true, + "requires": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "ssri": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", + "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "terser-webpack-plugin": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-3.0.7.tgz", + "integrity": "sha512-5JqibUOctE6Ou4T00IVGYTQJBOhu24jz0PpqYeitQJJ3hlZY2ZKSwzzuqjmBH8MzbdWMgIefpmHwTkvwm6Q4CQ==", + "dev": true, + "requires": { + "cacache": "^15.0.5", + "find-cache-dir": "^3.3.1", + "jest-worker": "^26.1.0", + "p-limit": "^3.0.2", + "schema-utils": "^2.6.6", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.8.0", + "webpack-sources": "^1.4.3" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "vue-cli-plugin-yaml": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/vue-cli-plugin-yaml/-/vue-cli-plugin-yaml-1.0.2.tgz", + "integrity": "sha512-gdPdHWjLyynzy97krmeSBDarkoYO8MVZUYu38CthNvLDBL+xJoM1jclBneI85x2nLtnqfaf753nMnsV6iIDl3A==", + "dev": true, + "requires": { + "json-loader": "^0.5.7", + "yaml-loader": "^0.6.0" + } + }, + "vue-clickaway": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/vue-clickaway/-/vue-clickaway-2.2.2.tgz", + "integrity": "sha512-25SpjXKetL06GLYoLoC8pqAV6Cur9cQ//2g35GRFBV4FgoljbZZjTINR8g2NuVXXDMLSUXaKx5dutgO4PaDE7A==", + "requires": { + "loose-envify": "^1.2.0" + } + }, + "vue-context": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vue-context/-/vue-context-5.2.0.tgz", + "integrity": "sha512-XH3SwDanAcE7ppzVEkXqpMyzkFKUDp8TDh4vBE9UPbT6OHwLIwtANH6ZAakq8q2iV+hGtDDfwYgX12IbZjyNnw==", + "requires": { + "vue-clickaway": "^2.2.2" + } + }, + "vue-directive-tooltip": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/vue-directive-tooltip/-/vue-directive-tooltip-1.6.3.tgz", + "integrity": "sha512-aSdlBIdibctL+Tw+2j+IVUtz/fOZPLMQz0xxSoIGOA223WsPahiybSykJHx6QxtPzWq2phE/OZaCTmvMOVPyeA==", + "requires": { + "popper.js": "1.15.0" + } + }, + "vue-eslint-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.0.tgz", + "integrity": "sha512-Kr21uPfthDc63nDl27AGQEhtt9VrZ9nkYk/NTftJ2ws9XiJwzJJCnCr3AITQ2jpRMA0XPGDECxYH8E027qMK9Q==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-scope": "^5.0.0", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.2.1", + "esquery": "^1.0.1", + "lodash": "^4.17.15" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } + } + }, + "vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "vue-json-component": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/vue-json-component/-/vue-json-component-0.4.1.tgz", + "integrity": "sha512-8Tw4C5LJOT09BRKjQx8F5itheP7UIeZtUUJvWnSltb0UFQZSrXw/4B32z3pelWo38MtoglaSmFTYpyxFIWWNRg==" + }, + "vue-loader": { + "version": "15.9.3", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.3.tgz", + "integrity": "sha512-Y67VnGGgVLH5Voostx8JBZgPQTlDQeOVBLOEsjc2cXbCYBKexSKEpOA56x0YZofoDOTszrLnIShyOX1p9uCEHA==", + "dev": true, + "requires": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + } + } + }, + "vue-property-decorator": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-8.5.1.tgz", + "integrity": "sha512-O6OUN2OMsYTGPvgFtXeBU3jPnX5ffQ9V4I1WfxFQ6dqz6cOUbR3Usou7kgFpfiXDvV7dJQSFcJ5yUPgOtPPm1Q==", + "requires": { + "vue-class-component": "^7.1.0" + } + }, + "vue-slider-component": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-3.2.11.tgz", + "integrity": "sha512-2YyJW6TFnYk5FUvqQLvZcCJ+hthBXB819qNHtwnEUyDbOcTXV0n3Ou1ZphOi5FX9phlQIiC2NvjLuRAVmNq+Zw==", + "requires": { + "core-js": "^3.6.5", + "vue-property-decorator": "^8.0.0" + } + }, + "vue-style-loader": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz", + "integrity": "sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ==", + "dev": true, + "requires": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + } + } + }, + "vue-template-compiler": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz", + "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==", + "dev": true, + "requires": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "vuex": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz", + "integrity": "sha512-w7oJzmHQs0FM9LXodfskhw9wgKBiaB+totOdb8sNzbTB2KDCEEwEs29NzBZFh/lmEK1t5tDmM1vtsO7ubG1DFw==" + }, + "watchpack": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz", + "integrity": "sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g==", + "dev": true, + "requires": { + "chokidar": "^3.4.0", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.0" + } + }, + "watchpack-chokidar2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", + "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", + "dev": true, + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webpack": { + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.43.0.tgz", + "integrity": "sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.1", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-bundle-analyzer": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.8.0.tgz", + "integrity": "sha512-PODQhAYVEourCcOuU+NiYI7WdR8QyELZGgPvB1y2tjbUpbmcQOt5Q7jEK+ttd5se0KSBKD9SXHCEozS++Wllmw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.15", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "dependencies": { + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + } + } + }, + "webpack-chain": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.5.0.tgz", + "integrity": "sha512-K4EHiEg4WlP4w1rKXKpYWvX9cfGBERHCGP06ETSNV62XUIfOUg1DDRQpxyBsFYxZLKc4YUAI3iiCIvWoliheGA==", + "dev": true, + "requires": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^2.0.1" + } + }, + "webpack-dev-middleware": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "dev": true, + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + } + }, + "webpack-dev-server": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", + "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.7", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.20", + "sockjs-client": "1.4.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "websocket-driver": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "dev": true, + "requires": { + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, + "xhr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", + "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", + "dev": true, + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha1-qQKekp09vN7RafPG4oI42VpdWig=", + "dev": true + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, + "xregexp": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz", + "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==", + "dev": true, + "requires": { + "@babel/runtime-corejs3": "^7.8.3" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yaku": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/yaku/-/yaku-0.16.7.tgz", + "integrity": "sha1-HRlceKqbW/hHnIlblQT9TwhHmE4=", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true + }, + "yaml-loader": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/yaml-loader/-/yaml-loader-0.6.0.tgz", + "integrity": "sha512-1bNiLelumURyj+zvVHOv8Y3dpCri0F2S+DCcmps0pA1zWRLjS+FhZQg4o3aUUDYESh73+pKZNI18bj7stpReow==", + "dev": true, + "requires": { + "loader-utils": "^1.4.0", + "yaml": "^1.8.3" + } + }, + "yargs": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.0.tgz", + "integrity": "sha512-D3fRFnZwLWp8jVAAhPZBsmeIHY8tTsb8ItV9KaAaopmC6wde2u6Yw29JBIZHXw14kgkRnYmDgmQU4FVMDlIsWw==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^3.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + } + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + }, + "yorkie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yorkie/-/yorkie-2.0.0.tgz", + "integrity": "sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==", + "dev": true, + "requires": { + "execa": "^0.8.0", + "is-ci": "^1.0.10", + "normalize-path": "^1.0.0", + "strip-indent": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", + "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "normalize-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz", + "integrity": "sha1-MtDkcvkf80VwHBWoMRAY07CpA3k=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + } + } +} diff --git a/GUI/package.json b/GUI/package.json new file mode 100644 index 000000000..6582aa45f --- /dev/null +++ b/GUI/package.json @@ -0,0 +1,70 @@ +{ + "name": "ODriveGUI", + "version": "0.1.0", + "private": true, + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build", + "lint": "vue-cli-service lint", + "enumGenerate": "node scripts/enumGenerate.js", + "preelectron:build": "electron-icon-builder --input=./public/icon.png --output=build --flatten && npm run enumGenerate", + "electron:build": "vue-cli-service electron:build", + "preelectron:serve": "npm run enumGenerate", + "electron:serve": "vue-cli-service electron:serve", + "postinstall": "electron-builder install-app-deps", + "postuninstall": "electron-builder install-app-deps", + "electron:generate-icons": "electron-icon-builder --input=./public/icon.png --output=build --flatten" + }, + "main": "background.js", + "dependencies": { + "chart.js": "^2.9.3", + "chartjs-plugin-streaming": "^1.8.0", + "core-js": "^3.6.5", + "electron": "^9.1.1", + "file-saver": "^2.0.2", + "socket.io-client": "^3.0.4", + "typeface-roboto": "0.0.75", + "uuid": "^8.2.0", + "vue": "^2.6.11", + "vue-chartjs": "^3.5.0", + "vue-context": "^5.2.0", + "vue-directive-tooltip": "^1.6.3", + "vue-json-component": "^0.4.1", + "vue-slider-component": "^3.2.11", + "vuex": "^3.5.1" + }, + "devDependencies": { + "@vue/cli-plugin-babel": "~4.4.0", + "@vue/cli-plugin-eslint": "~4.4.0", + "@vue/cli-plugin-router": "^4.4.6", + "@vue/cli-service": "~4.4.0", + "babel-eslint": "^10.1.0", + "electron": "^9.0.0", + "electron-devtools-installer": "^3.1.0", + "electron-icon-builder": "^1.0.2", + "eslint": "^6.7.2", + "eslint-plugin-vue": "^6.2.2", + "vue-cli-plugin-electron-builder": "~2.0.0-rc.4", + "vue-cli-plugin-yaml": "^1.0.2", + "vue-template-compiler": "^2.6.11" + }, + "eslintConfig": { + "root": true, + "env": { + "node": true + }, + "extends": [ + "plugin:vue/essential", + "eslint:recommended" + ], + "parserOptions": { + "parser": "babel-eslint" + }, + "rules": {} + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not dead" + ] +} diff --git a/GUI/public/favicon.ico b/GUI/public/favicon.ico new file mode 100644 index 000000000..df36fcfb7 Binary files /dev/null and b/GUI/public/favicon.ico differ diff --git a/GUI/public/icon.png b/GUI/public/icon.png new file mode 100644 index 000000000..fa8c12b46 Binary files /dev/null and b/GUI/public/icon.png differ diff --git a/GUI/public/index.html b/GUI/public/index.html new file mode 100644 index 000000000..412352865 --- /dev/null +++ b/GUI/public/index.html @@ -0,0 +1,17 @@ + + + + + + + + <%= htmlWebpackPlugin.options.title %> + + + +
+ + + diff --git a/GUI/scripts/enumGenerate.js b/GUI/scripts/enumGenerate.js new file mode 100644 index 000000000..3aed19161 --- /dev/null +++ b/GUI/scripts/enumGenerate.js @@ -0,0 +1,23 @@ +const fs = require('fs'); +const path = require('path'); + + +fs.readFile(path.resolve(__dirname,'..','..','tools','odrive','enums.py'), 'utf8', function(err, data) { + if (err) throw err; + let enumsString = data; + let lines = enumsString.split(process.platform == 'win32' ? '\r\n' : '\n'); + let enums = {}; + for (const line of lines) { + if (line != '' && line[0] != '#') { + let name; + let value; + name = line.split('=')[0]; + value = line.split('=')[1]; + enums[name.trim()] = parseInt(value.trim()); + } + } + fs.writeFile(path.resolve(__dirname, '../src/assets/odriveEnums.json'), JSON.stringify(enums, null, 4), function(err) { + if (err) throw err; + }); + console.log('Wrote ODrive enums to GUI/src/assets/odriveEnums.json'); +}); diff --git a/GUI/server/odrive_server.py b/GUI/server/odrive_server.py new file mode 100644 index 000000000..528bff329 --- /dev/null +++ b/GUI/server/odrive_server.py @@ -0,0 +1,298 @@ +import sys +import flask +import os +from flask import make_response, request, jsonify, session +from flask_socketio import SocketIO, send, emit +from flask_cors import CORS +from engineio.payload import Payload +import json +import time +import argparse +import logging +import math +import traceback + +# interface for odrive GUI to get data from odrivetool + +# Flush stdout by default +# Source: +# https://stackoverflow.com/questions/230751/how-to-flush-output-of-python-print +old_print = print +def print(*args, **kwargs): + kwargs.pop('flush', False) + old_print(*args, **kwargs) + file = kwargs.get('file', sys.stdout) + file.flush() if file is not None else sys.stdout.flush() + +app = flask.Flask(__name__) +# disable logging, very noisy! +log = logging.getLogger('werkzeug') +log.disabled = True +app.config['SECRET_KEY'] = 'secret' +app.config.update( + SESSION_COOKIE_SECURE=True, + SESSION_COOKIE_HTTPONLY=True, + SESSION_COOKIE_SAMESITE='None' +) +CORS(app, support_credentials=True) +Payload.max_decode_packets = 500 +socketio = SocketIO(app, cors_allowed_origins="*", async_mode = "threading") + +#def get_odrive(): +# globals()['odrives'] = [] +# globals()['odrives'].append(odrive.find_any()) +# globals()['odrives'][0].__channel__._channel_broken.subscribe(lambda: handle_disconnect()) +# print("odrives found") +# socketio.emit('odrive-found') + +async def discovered_device(device): + # when device is discovered, add it to list of serial numbers and global odrive list + # shamelessly lifted from odrive python package + serial_number = await odrive.get_serial_number_str(device) + if serial_number in globals()['discovered_devices']: + index = globals()['discovered_devices'].index(serial_number) + else: + globals()['discovered_devices'].append(serial_number) + index = len(globals()['discovered_devices']) - 1 + odrive_name = "odrive" + str(index) + + # add to list of odrives + while globals()['inUse']: + time.sleep(0.1) + globals()['odrives'][odrive_name] = device + globals()['odrives_status'][odrive_name] = True + print("Found " + str(serial_number)) + print("odrive list: " + str([key for key in globals()['odrives'].keys()])) + # tell GUI the status of known ODrives (previously connected and then disconnected ODrives will be "False") + socketio.emit('odrives-status', json.dumps(globals()['odrives_status'])) + # triggers a getODrives socketio message + socketio.emit('odrive-found') + +def start_discovery(): + print("starting disco loop...") + log = fibre.Logger(verbose = False) + + domain = fibre.Domain("usb:idVendor=0x1209,idProduct=0x0D32,bInterfaceClass=0,bInterfaceSubClass=1,bInterfaceProtocol=0") + domain = domain.__enter__() + discovery = domain.run_discovery(discovered_device) + +def handle_disconnect(odrive_name): + print("lost odrive") + globals()['odrives_status'][odrive_name] = False + # emit the whole list of odrive statuses + # in the GUI, mark and use status as ODrive state. + socketio.emit('odrives-status', json.dumps(globals()['odrives_status'])) + +@socketio.on('findODrives') +def getODrives(message): + print("looking for odrive") + start_discovery() + +@socketio.on('enableSampling') +def enableSampling(message): + print("sampling enabled") + session['samplingEnabled'] = True + emit('samplingEnabled') + +@socketio.on('stopSampling') +def stopSampling(message): + session['samplingEnabled'] = False + emit('samplingDisabled') + +@socketio.on('sampledVarNames') +def sampledVarNames(message): + session['sampledVars'] = message + print(session['sampledVars']) + +@socketio.on('startSampling') +def sendSamples(message): + print(session['samplingEnabled']) + while session['samplingEnabled']: + emit('sampledData', json.dumps(getSampledData(session['sampledVars']))) + time.sleep(0.02) + +@socketio.on('message') +def handle_message(message): + print(message) + emit('response', 'hello from server!') + +@socketio.on('getODrives') +def get_odrives(data): + # spinlock + while globals()['inUse']: + time.sleep(0.1) + + globals()['inUse'] = True + odriveDict = {} + #for (index, odrv) in enumerate(globals()['odrives']): + # odriveDict["odrive" + str(index)] = dictFromRO(odrv) + for key in globals()['odrives_status'].keys(): + if globals()['odrives_status'][key] == True: + odriveDict[key] = dictFromRO(globals()['odrives'][key]) + globals()['inUse'] = False + emit('odrives', json.dumps(odriveDict)) + +@socketio.on('getProperty') +def get_property(message): + # message is dict natively + # will be {"path": "odriveX.axisY.blah.blah"} + while globals()['inUse']: + time.sleep(0.1) + if globals()['odrives_status'][message["path"].split('.')[0]]: + globals()['inUse'] = True + val = getVal(globals()['odrives'], message["path"].split('.')) + globals()['inUse'] = False + emit('ODriveProperty', json.dumps({"path": message["path"], "val": val})) + +@socketio.on('setProperty') +def set_property(message): + # message is {"path":, "val":, "type":} + while globals()['inUse']: + time.sleep(0.1) + globals()['inUse'] = True + print("From setProperty event handler: " + str(message)) + postVal(globals()['odrives'], message["path"].split('.'), message["val"], message["type"]) + val = getVal(globals()['odrives'], message["path"].split('.')) + globals()['inUse'] = False + emit('ODriveProperty', json.dumps({"path": message["path"], "val": val})) + +@socketio.on('callFunction') +def call_function(message): + # message is {"path"}, no args yet (do we know which functions accept arguments from the odrive tree directly?) + while globals()['inUse']: + time.sleep(0.1) + print("From callFunction event handler: " + str(message)) + globals()['inUse'] = True + callFunc(globals()['odrives'], message["path"].split('.')) + globals()['inUse'] = False + +@app.route('/', methods=['GET']) +def home(): + return "

ODrive GUI Server

" + +def dictFromRO(RO): + # create dict from an odrive RemoteObject that's suitable for sending as JSON + returnDict = {} + + for key in dir(RO): + v = getattr(RO, key) + if not key.startswith('_') and isinstance(v, fibre.libfibre.RemoteObject): + # recurse + returnDict[key] = dictFromRO(v) + + elif key.startswith('_') and key.endswith('_property'): + # grab value of that property + # indicate if this property can be written or not + val = str(v.read()) + _type = str(v.read._outputs[0][1]) + if val == "inf": + val = "Infinity" + _type = "str" + elif val == "-inf": + val = "-Infinity" + _type = "str" + returnDict[key[1:-9]] = {"val": val, + "readonly": not hasattr(v, 'exchange'), + "type": _type} + elif not key.startswith('_') and hasattr(v, '__call__'): + # this is a function - do nothing for now. + print('found a function!',key) + returnDict[key] = "function" + + return returnDict + +def postVal(odrives, keyList, value, argType): + # expect a list of keys in the form of ["key1", "key2", "keyN"] + # "key1" will be "odriveN" + # like this: postVal(odrives, ["odrive0","axis0","config","calibration_lockin","accel"], 17.0) + try: + odrv = keyList.pop(0) + RO = odrives[odrv] + keyList[-1] = '_' + keyList[-1] + '_property' + for key in keyList: + RO = getattr(RO, key) + if argType == "number": + RO.exchange(float(value)) + elif argType == "boolean": + RO.exchange(value) + elif argType == "string": + if value == "Infinity": + RO.exchange(math.inf) + elif value == "-Infinity": + RO.exchange(-math.inf) + else: + pass # dont support that type yet + except fibre.ObjectLostError: + handle_disconnect(odrv) + except Exception as ex: + print("exception in postVal: ", traceback.format_exc()) + +def getVal(odrives, keyList): + try: + odrv = keyList.pop(0) + RO = odrives[odrv] + keyList[-1] = '_' + keyList[-1] + '_property' + for key in keyList: + RO = getattr(RO, key) + retVal = RO.read() + if retVal == math.inf: + retVal = "Infinity" + elif retVal == -math.inf: + retVal = "-Infinity" + return retVal + except fibre.ObjectLostError: + handle_disconnect(odrv) + except Exception as ex: + print("exception in getVal: ", traceback.format_exc()) + return 0 + +def getSampledData(vars): + #use getVal to populate a dict + #return a dict {path:value} + samples = {} + for path in vars["paths"]: + keys = path.split('.') + samples[path] = getVal(globals()['odrives'], keys) + + return samples + +def callFunc(odrives, keyList): + try: + #index = int(''.join([char for char in keyList.pop(0) if char.isnumeric()])) + odrv = keyList.pop(0) + RO = odrives[odrv] + for key in keyList: + RO = getattr(RO, key) + if hasattr(RO, '__call__'): + RO.__call__() + except fibre.ObjectLostError: + handle_disconnect(odrv) + except: + print("fcn call failed") + +if __name__ == "__main__": + print("args from python: " + str(sys.argv[1:0])) + #print(sys.argv[1:]) + # try to import based on command line arguments or config file + + for optPath in sys.argv[1:]: + print("adding " + str(optPath.rstrip()) + " to import path for odrive_server.py") + sys.path.insert(0,optPath.rstrip()) + + import odrive + import odrive.utils # for dump_errors() + import fibre + + # global for holding references to all connected odrives + globals()['odrives'] = {} + # global dict {'odriveX': True/False} where True/False reflects status of connection + # on handle_disconnect, set it to False. On connection, set it to True + globals()['odrives_status'] = {} + globals()['discovered_devices'] = [] + # spinlock + globals()['inUse'] = False + + log = fibre.Logger(verbose=False) + shutdown = fibre.Event() + + socketio.run(app, host='0.0.0.0', port=5000) diff --git a/GUI/src/App.vue b/GUI/src/App.vue new file mode 100644 index 000000000..2d6005255 --- /dev/null +++ b/GUI/src/App.vue @@ -0,0 +1,571 @@ + + + + + diff --git a/GUI/src/assets/dashboards/Tuning_0_4_12.json b/GUI/src/assets/dashboards/Tuning_0_4_12.json new file mode 100644 index 000000000..c1a4ae784 --- /dev/null +++ b/GUI/src/assets/dashboards/Tuning_0_4_12.json @@ -0,0 +1,127 @@ +{ + "name": "Tuning", + "component": "Dashboard", + "id": "deadb33f", + "controls": [ + { + "controlType": "CtrlNumeric", + "path": "odrives.odrive0.axis0.motor.config.current_lim" + }, + { + "controlType": "CtrlNumeric", + "path": "odrives.odrive0.axis0.controller.config.vel_limit" + }, + { + "controlType": "CtrlSlider", + "path": "odrives.odrive0.axis0.controller.config.pos_gain" + }, + { + "controlType": "CtrlSlider", + "path": "odrives.odrive0.axis0.controller.config.vel_gain" + }, + { + "controlType": "CtrlSlider", + "path": "odrives.odrive0.axis0.controller.config.vel_integrator_gain" + }, + { + "controlType": "CtrlEnum", + "path": "odrives.odrive0.axis0.requested_state", + "options": [ + { + "text": "Undefined", + "value": 0 + }, + { + "text": "Idle", + "value": 1 + }, + { + "text": "Startup Sequence", + "value": 2 + }, + { + "text": "Full Calibration Sequence", + "value": 3 + }, + { + "text": "Motor Calibration", + "value": 4 + }, + { + "text": "Sensorless Control", + "value": 5 + }, + { + "text": "Encoder Index Search", + "value": 6 + }, + { + "text": "Encoder Offset Calibration", + "value": 7 + }, + { + "text": "Closed Loop Control", + "value": 8 + }, + { + "text": "Lockin Spin", + "value": 9 + }, + { + "text": "Encoder Direction Find", + "value": 10 + }, + { + "text": "Homing", + "value": 11 + } + ] + }, + { + "controlType": "CtrlFunction", + "path": "odrives.odrive0.save_configuration" + } + ], + "actions": [ + { + "actionType": "Action", + "id": "a699c462-cf8d-4b4e-aedf-0b200e84f97a", + "path": "odrives.odrive0.axis0.controller.pos_setpoint", + "val": 0 + }, + { + "actionType": "Action", + "id": "db267ae4-fdf4-4770-a2d3-eb61de026659", + "path": "odrives.odrive0.axis0.controller.pos_setpoint", + "val": 50000 + } + ], + "plots": [ + { + "name": "65a2768d-e61e-4dd5-bf80-dba153838f74", + "vars": [ + { + "path": "odrives.odrive0.axis0.controller.pos_setpoint", + "color": "#1f77b4" + }, + { + "path": "odrives.odrive0.axis0.encoder.pos_estimate", + "color": "#ff7f0e" + } + ] + }, + { + "name": "d9ee2474-3e53-408f-9eab-1656342eb531", + "vars": [ + { + "path": "odrives.odrive0.axis0.controller.vel_setpoint", + "color": "#1f77b4" + }, + { + "path": "odrives.odrive0.axis0.encoder.vel_estimate", + "color": "#ff7f0e" + } + ] + } + ] +} \ No newline at end of file diff --git a/GUI/src/assets/dashboards/Tuning_0_5_1.json b/GUI/src/assets/dashboards/Tuning_0_5_1.json new file mode 100644 index 000000000..8e18af98c --- /dev/null +++ b/GUI/src/assets/dashboards/Tuning_0_5_1.json @@ -0,0 +1,127 @@ +{ + "name": "Tuning", + "component": "Dashboard", + "id": "deadb33f", + "controls": [ + { + "controlType": "CtrlNumeric", + "path": "odrives.odrive0.axis0.motor.config.current_lim" + }, + { + "controlType": "CtrlNumeric", + "path": "odrives.odrive0.axis0.controller.config.vel_limit" + }, + { + "controlType": "CtrlSlider", + "path": "odrives.odrive0.axis0.controller.config.pos_gain" + }, + { + "controlType": "CtrlSlider", + "path": "odrives.odrive0.axis0.controller.config.vel_gain" + }, + { + "controlType": "CtrlSlider", + "path": "odrives.odrive0.axis0.controller.config.vel_integrator_gain" + }, + { + "controlType": "CtrlEnum", + "path": "odrives.odrive0.axis0.requested_state", + "options": [ + { + "text": "Undefined", + "value": 0 + }, + { + "text": "Idle", + "value": 1 + }, + { + "text": "Startup Sequence", + "value": 2 + }, + { + "text": "Full Calibration Sequence", + "value": 3 + }, + { + "text": "Motor Calibration", + "value": 4 + }, + { + "text": "Sensorless Control", + "value": 5 + }, + { + "text": "Encoder Index Search", + "value": 6 + }, + { + "text": "Encoder Offset Calibration", + "value": 7 + }, + { + "text": "Closed Loop Control", + "value": 8 + }, + { + "text": "Lockin Spin", + "value": 9 + }, + { + "text": "Encoder Direction Find", + "value": 10 + }, + { + "text": "Homing", + "value": 11 + } + ] + }, + { + "controlType": "CtrlFunction", + "path": "odrives.odrive0.save_configuration" + } + ], + "actions": [ + { + "actionType": "Action", + "id": "a699c462-cf8d-4b4e-aedf-0b200e84f97a", + "path": "odrives.odrive0.axis0.controller.input_pos", + "val": 0 + }, + { + "actionType": "Action", + "id": "db267ae4-fdf4-4770-a2d3-eb61de026659", + "path": "odrives.odrive0.axis0.controller.input_pos", + "val": 10 + } + ], + "plots": [ + { + "name": "65a2768d-e61e-4dd5-bf80-dba153838f74", + "vars": [ + { + "path": "odrives.odrive0.axis0.controller.input_pos", + "color": "#1f77b4" + }, + { + "path": "odrives.odrive0.axis0.encoder.pos_estimate", + "color": "#ff7f0e" + } + ] + }, + { + "name": "d9ee2474-3e53-408f-9eab-1656342eb531", + "vars": [ + { + "path": "odrives.odrive0.axis0.controller.vel_setpoint", + "color": "#1f77b4" + }, + { + "path": "odrives.odrive0.axis0.encoder.vel_estimate", + "color": "#ff7f0e" + } + ] + } + ] +} \ No newline at end of file diff --git a/GUI/src/assets/images/24v_300x300.png b/GUI/src/assets/images/24v_300x300.png new file mode 100644 index 000000000..dc58e82f8 Binary files /dev/null and b/GUI/src/assets/images/24v_300x300.png differ diff --git a/GUI/src/assets/images/56v_300x300.png b/GUI/src/assets/images/56v_300x300.png new file mode 100644 index 000000000..8a4d6eea3 Binary files /dev/null and b/GUI/src/assets/images/56v_300x300.png differ diff --git a/GUI/src/assets/images/D5065_300x300.png b/GUI/src/assets/images/D5065_300x300.png new file mode 100644 index 000000000..b2e3d9e9e Binary files /dev/null and b/GUI/src/assets/images/D5065_300x300.png differ diff --git a/GUI/src/assets/images/D6374_300x300.png b/GUI/src/assets/images/D6374_300x300.png new file mode 100644 index 000000000..6a23dd9a2 Binary files /dev/null and b/GUI/src/assets/images/D6374_300x300.png differ diff --git a/GUI/src/assets/images/M0M1_300x300.png b/GUI/src/assets/images/M0M1_300x300.png new file mode 100644 index 000000000..dc0d58d06 Binary files /dev/null and b/GUI/src/assets/images/M0M1_300x300.png differ diff --git a/GUI/src/assets/images/M0_300x300.png b/GUI/src/assets/images/M0_300x300.png new file mode 100644 index 000000000..a3702ee60 Binary files /dev/null and b/GUI/src/assets/images/M0_300x300.png differ diff --git a/GUI/src/assets/images/M1_300x300.png b/GUI/src/assets/images/M1_300x300.png new file mode 100644 index 000000000..f1d7e1172 Binary files /dev/null and b/GUI/src/assets/images/M1_300x300.png differ diff --git a/GUI/src/assets/images/amt102-v_300x300.png b/GUI/src/assets/images/amt102-v_300x300.png new file mode 100644 index 000000000..0b18ae395 Binary files /dev/null and b/GUI/src/assets/images/amt102-v_300x300.png differ diff --git a/GUI/src/assets/images/hall_effect_300x300.png b/GUI/src/assets/images/hall_effect_300x300.png new file mode 100644 index 000000000..95821f22c Binary files /dev/null and b/GUI/src/assets/images/hall_effect_300x300.png differ diff --git a/GUI/src/assets/images/temp.png b/GUI/src/assets/images/temp.png new file mode 100644 index 000000000..ff8084cf6 Binary files /dev/null and b/GUI/src/assets/images/temp.png differ diff --git a/GUI/src/assets/odrive-interface.yaml b/GUI/src/assets/odrive-interface.yaml new file mode 100644 index 000000000..f17aca633 --- /dev/null +++ b/GUI/src/assets/odrive-interface.yaml @@ -0,0 +1,700 @@ +--- +version: 0.0.1 +ns: com.odriverobotics +summary: ODrive Interface Definitions + +dictionary: [ODrive] # Prevent the word 'ODrive' from being detected as two words 'O' and 'Drive' + +interfaces: + ODrive: + c_is_class: True + attributes: + vbus_voltage: readonly float32 + ibus: readonly float32 + serial_number: readonly uint64 + hw_version_major: readonly uint8 + hw_version_minor: readonly uint8 + hw_version_variant: readonly uint8 + fw_version_major: readonly uint8 + fw_version_minor: readonly uint8 + fw_version_revision: readonly uint8 + fw_version_unreleased: + type: readonly uint8 + doc: 0 for official releases, 1 otherwise + brake_resistor_armed: readonly bool + brake_resistor_saturated: bool + system_stats: + c_is_class: False + attributes: + uptime: readonly uint32 + min_heap_space: readonly uint32 + min_stack_space_axis0: readonly uint32 + min_stack_space_axis1: readonly uint32 + min_stack_space_comms: readonly uint32 + min_stack_space_usb: readonly uint32 + min_stack_space_uart: readonly uint32 + min_stack_space_can: readonly uint32 + min_stack_space_usb_irq: readonly uint32 + min_stack_space_startup: readonly uint32 + stack_usage_axis0: readonly uint32 + stack_usage_axis1: readonly uint32 + stack_usage_comms: readonly uint32 + stack_usage_usb: readonly uint32 + stack_usage_uart: readonly uint32 + stack_usage_usb_irq: readonly uint32 + stack_usage_startup: readonly uint32 + stack_usage_can: readonly uint32 + usb: + c_is_class: False + attributes: + rx_cnt: readonly uint32 + tx_cnt: readonly uint32 + tx_overrun_cnt: readonly uint32 + i2c: + c_is_class: False + attributes: + addr: readonly uint8 + addr_match_cnt: readonly uint32 + rx_cnt: readonly uint32 + error_cnt: readonly uint32 + config: + c_is_class: False + attributes: + enable_uart: + type: bool + doc: 'TODO: changing this currently requires a reboot - fix this' + uart_baudrate: + type: uint32 + doc: "Defines the baudrate used on the UART interface. + Some baudrates will have a small timing error due to hardware limitations. + + Here's an (incomplete) list of baudrates for ODrive v3.x: + + Configured | Actual | Error [%] + -------------|---------------|----------- + 1.2 KBps | 1.2 KBps | 0 + 2.4 KBps | 2.4 KBps | 0 + 9.6 KBps | 9.6 KBps | 0 + 19.2 KBps | 19.195 KBps | 0.02 + 38.4 KBps | 38.391 KBps | 0.02 + 57.6 KBps | 57.613 KBps | 0.02 + 115.2 KBps | 115.068 KBps | 0.11 + 230.4 KBps | 230.769 KBps | 0.16 + 460.8 KBps | 461.538 KBps | 0.16 + 921.6 KBps | 913.043 KBps | 0.93 + 1.792 MBps | 1.826 MBps | 1.9 + 1.8432 MBps | 1.826 MBps | 0.93 + + For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the STM datasheet: + https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf" + enable_i2c_instead_of_can: + type: bool + doc: 'Changing this requires a reboot' + enable_ascii_protocol_on_usb: bool + max_regen_current: float32 + brake_resistance: + type: float32 + unit: Ohm + doc: Value of the brake resistor connected to the ODrive. Set to 0 to disable. + + dc_bus_undervoltage_trip_level: + type: float32 + unit: V + doc: Minimum voltage below which the motor stops operating. + dc_bus_overvoltage_trip_level: + type: float32 + unit: V + doc: Maximum voltage above which the motor stops operating. + This protects against cases in which the power supply fails to dissipate + the brake power if the brake resistor is disabled. + The default is 26V for the 24V board version and 52V for the 48V board version. + + enable_dc_bus_overvoltage_ramp: + type: bool + doc: 'If enabled, if the measured DC voltage exceeds `dc_bus_overvoltage_ramp_start`, + the ODrive will sink more power than usual into the the brake resistor + in an attempt to bring the voltage down again. + + The brake duty cycle is increased by the following amount: + vbus_voltage == dc_bus_overvoltage_ramp_start => brake_duty_cycle += 0% + vbus_voltage == dc_bus_overvoltage_ramp_end => brake_duty_cycle += 100% + + Remarks: + - This feature is active even when all motors are disarmed. + - This feature is disabled if `brake_resistance` is non-positive.' + dc_bus_overvoltage_ramp_start: + type: float32 + doc: See `enable_dc_bus_overvoltage_ramp`. + Do not set this lower than your usual vbus_voltage, + unless you like fried brake resistors. + dc_bus_overvoltage_ramp_end: + type: float32 + doc: See `enable_dc_bus_overvoltage_ramp`. + Must be larger than `dc_bus_overvoltage_ramp_start`, + otherwise the ramp feature is disabled. + + dc_max_positive_current: + type: float32 + unit: A + doc: Max current the power supply can source. + dc_max_negative_current: + type: float32 + unit: A + doc: Max current the power supply can sink. You most likely want a non-positive value here. Set to -INFINITY to disable. + + gpio1_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[0]'} # TODO: disable for ODrive v3.2 and older + gpio2_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[1]'} # TODO: disable for ODrive v3.2 and older + gpio3_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[2]'} # TODO: disable for ODrive v3.2 and older + gpio4_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[3]'} + gpio3_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[2]'} + gpio4_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[3]'} + user_config_loaded: readonly bool + + axis0: {type: Axis, c_name: get_axis(0)} + axis1: {type: Axis, c_name: get_axis(1)} + can: {type: Can, c_name: get_can()} + test_property: uint32 + + functions: + test_function: {in: {delta: int32}, out: {cnt: int32}} + get_oscilloscope_val: {in: {index: uint32}, out: {val: float32}} + get_adc_voltage: {in: {gpio: uint32}, out: {voltage: float32}} + save_configuration: + erase_configuration: + reboot: + enter_dfu_mode: + + ODrive.Can: + c_is_class: True + attributes: + error: + nullflag: None + flags: {DuplicateCanIds: } + config: + c_is_class: False + attributes: + baud_rate: readonly uint32 + protocol: Protocol + functions: + set_baud_rate: {in: {baudRate: uint32}} + + ODrive.Endpoint: + c_is_class: False + attributes: + endpoint: endpoint_ref + min: float32 + max: float32 + + ODrive.Axis: + c_is_class: True + attributes: + error: + nullflag: 'None' + flags: + InvalidState: + doc: An invalid state was requested. + DcBusUnderVoltage: + DcBusOverVoltage: + CurrentMeasurementTimeout: + BrakeResistorDisarmed: + doc: The brake resistor was unexpectedly disarmed. + MotorDisarmed: + doc: The motor was unexpectedly disarmed. + MotorFailed: + doc: Check `motor.error` for more information. + SensorlessEstimatorFailed: + EncoderFailed: + doc: Check `encoder.error` for more information. + ControllerFailed: + PosCtrlDuringSensorless: + doc: DEPRECATED + WatchdogTimerExpired: + MinEndstopPressed: + MaxEndstopPressed: + EstopRequested: + HomingWithoutEndstop: + bit: 17 + doc: the min endstop was not enabled during homing + step_dir_active: readonly bool + current_state: readonly AxisState + requested_state: AxisState + loop_counter: readonly uint32 + lockin_state: + typeargs: {fibre.Property.mode: readonly} + values: + Inactive: + Ramp: + Accelerate: + ConstVel: + is_homed: {type: bool, c_name: homing_.is_homed} + config: + c_is_class: False + attributes: + startup_motor_calibration: + type: bool + doc: run motor calibration at startup, skip otherwise + startup_encoder_index_search: + type: bool + doc: run encoder index search after startup, skip otherwise this only has an effect if encoder.config.use_index is also true + startup_encoder_offset_calibration: + type: bool + doc: run encoder offset calibration after startup, skip otherwise + startup_closed_loop_control: + type: bool + doc: enable closed loop control after calibration/startup + startup_sensorless_control: + type: bool + doc: enable sensorless control after calibration/startup + startup_homing: + type: bool + doc: enable homing after calibration/startup + enable_step_dir: + type: bool + doc: Enable step/dir input after calibration. + For M0 this has no effect if `enable_uart` is true. + step_dir_always_on: + type: bool + doc: Keep step/dir enabled while the motor is disabled. + This is ignored if enable_step_dir is false. + This setting only takes effect on a state transition + into idle or out of closed loop control. + counts_per_step: float32 + watchdog_timeout: + type: float32 + unit: s + doc: 0 disables watchdog + enable_watchdog: bool + step_gpio_pin: {type: uint16, c_setter: 'set_step_gpio_pin'} + dir_gpio_pin: {type: uint16, c_setter: 'set_dir_gpio_pin'} + calibration_lockin: # TODO: this is a subset of lockin state + c_is_class: False + attributes: + current: float32 + ramp_time: float32 + ramp_distance: float32 + accel: float32 + vel: float32 + sensorless_ramp: LockinConfig + general_lockin: LockinConfig + can_node_id: + type: uint32 + doc: Both axes will have the same id to start + can_node_id_extended: bool + can_heartbeat_rate_ms: uint32 + motor: Motor + controller: Controller + encoder: Encoder + sensorless_estimator: SensorlessEstimator + trap_traj: TrapezoidalTrajectory + min_endstop: Endstop + max_endstop: Endstop + functions: + watchdog_feed: + doc: Feed the watchdog to prevent watchdog timeouts. + clear_errors: + doc: Check the watchdog timer for expiration. Also sets the watchdog error bit if expired. + + ODrive.Axis.LockinConfig: + c_is_class: False + attributes: + current: + type: float32 + unit: A + ramp_time: + type: float32 + unit: s + ramp_distance: + type: float32 + unit: rad + accel: + type: float32 + unit: rad/s^2 + vel: + type: float32 + unit: rad/s + finish_distance: + type: float32 + unit: rad + finish_on_vel: bool + finish_on_distance: bool + finish_on_enc_idx: bool + + + ODrive.Motor: + c_is_class: True + attributes: + error: + nullflag: None + flags: + PhaseResistanceOutOfRange: + PhaseInductanceOutOfRange: + AdcFailed: + DrvFault: + ControlDeadlineMissed: + NotImplementedMotorType: + BrakeCurrentOutOfRange: + ModulationMagnitude: + BrakeDeadtimeViolation: + UnexpectedTimerCallback: + CurrentSenseSaturation: + InverterOverTemp: + CurrentLimitViolation: + BrakeDutyCycleNan: + DcBusOverRegenCurrent: {doc: too much current pushed into the power supply} + DcBusOverCurrent: {doc: too much current pulled out of the power supply} + armed_state: + typeargs: {fibre.Property.mode: readonly} + values: + Disarmed: + WaitingForTimings: + WaitingForUpdate: + Armed: + is_calibrated: readonly bool + current_meas_phB: {type: readonly float32, c_name: current_meas_.phB} + current_meas_phC: {type: readonly float32, c_name: current_meas_.phC} + DC_calib_phB: {type: float32, c_name: DC_calib_.phB} + DC_calib_phC: {type: float32, c_name: DC_calib_.phC} + phase_current_rev_gain: float32 + thermal_current_lim: readonly float32 + inverter_temp: + type: readonly float32 + unit: °C + doc: NaN while the ODrive is initializing. + current_control: + c_is_class: False + attributes: + p_gain: float32 + i_gain: float32 + v_current_control_integral_d: float32 + v_current_control_integral_q: float32 + Ibus: float32 + final_v_alpha: float32 + final_v_beta: float32 + Id_setpoint: float32 + Iq_setpoint: readonly float32 + Iq_measured: float32 + Id_measured: float32 + I_measured_report_filter_k: float32 + max_allowed_current: readonly float32 + overcurrent_trip_level: readonly float32 + acim_rotor_flux: float32 + async_phase_vel: readonly float32 + async_phase_offset: float32 + gate_driver: + c_name: gate_driver_exported_ + c_is_class: False + attributes: + drv_fault: + typeargs: {fibre.Property.mode: readonly} + nullflag: NoFault + flags: + FetLowCOvercurrent: {bit: 0, doc: FET Low side, Phase C Over Current fault} + FetHighCOvercurrent: {bit: 1, doc: FET High side, Phase C Over Current fault} + FetLowBOvercurrent: {bit: 2, doc: FET Low side, Phase B Over Current fault} + FetHighBOvercurrent: {bit: 3, doc: FET High side, Phase B Over Current fault} + FetLowAOvercurrent: {bit: 4, doc: FET Low side, Phase A Over Current fault} + FetHighAOvercurrent: {bit: 5, doc: FET High side, Phase A Over Current fault} + OvertemperatureWarning: {bit: 6, doc: Over Temperature Warning fault} + OvertemperatureShutdown: {bit: 7, doc: Over Temperature Shut Down fault} + PVddUndervoltage: {bit: 8, doc: Power supply Vdd Under Voltage fault} + GVddUndervoltage: {bit: 9, doc: DRV8301 Vdd Under Voltage fault} + GVddOvervoltage: {bit: 10, doc: DRV8301 Vdd Over Voltage fault} + # status_reg_1: readonly uint32 + # status_reg_2: readonly uint32 + # ctrl_reg_1: readonly uint32 + # ctrl_reg_2: readonly uint32 + timing_log: + c_is_class: False + attributes: + general: {type: readonly uint16, c_name: 'get(TIMING_LOG_GENERAL)'} + adc_cb_i: {type: readonly uint16, c_name: 'get(TIMING_LOG_ADC_CB_I)'} + adc_cb_dc: {type: readonly uint16, c_name: 'get(TIMING_LOG_ADC_CB_DC)'} + meas_r: {type: readonly uint16, c_name: 'get(TIMING_LOG_MEAS_R)'} + meas_l: {type: readonly uint16, c_name: 'get(TIMING_LOG_MEAS_L)'} + enc_calib: {type: readonly uint16, c_name: 'get(TIMING_LOG_ENC_CALIB)'} + idx_search: {type: readonly uint16, c_name: 'get(TIMING_LOG_IDX_SEARCH)'} + foc_voltage: {type: readonly uint16, c_name: 'get(TIMING_LOG_FOC_VOLTAGE)'} + foc_current: {type: readonly uint16, c_name: 'get(TIMING_LOG_FOC_CURRENT)'} + spi_start: {type: readonly uint16, c_name: 'get(TIMING_LOG_SPI_START)'} + sample_now: {type: readonly uint16, c_name: 'get(TIMING_LOG_SAMPLE_NOW)'} + spi_end: {type: readonly uint16, c_name: 'get(TIMING_LOG_SPI_END)'} + config: + c_is_class: False + attributes: + pre_calibrated: {type: bool, c_setter: set_pre_calibrated} + pole_pairs: int32 + calibration_current: float32 + resistance_calib_max_voltage: float32 + phase_inductance: {type: float32, c_setter: set_phase_inductance} + phase_resistance: {type: float32, c_setter: set_phase_resistance} + direction: int32 + motor_type: MotorType + current_lim: float32 + current_lim_margin: float32 + inverter_temp_limit_lower: float32 + inverter_temp_limit_upper: float32 + requested_current_range: float32 + current_control_bandwidth: {type: float32, c_setter: set_current_control_bandwidth} + acim_slip_velocity: float32 + acim_gain_min_flux: float32 + acim_autoflux_min_Id: float32 + acim_autoflux_enable: bool + acim_autoflux_attack_gain: float32 + acim_autoflux_decay_gain: float32 + + + ODrive.Controller: + c_is_class: True + attributes: + error: + nullflag: None + flags: + Overspeed: + InvalidInputMode: + UnstableGain: + InvalidMirrorAxis: + InvalidLoadEncoder: + InvalidEstimate: + input_pos: {type: float32, c_setter: set_input_pos} + input_vel: float32 + input_current: float32 + pos_setpoint: readonly float32 + vel_setpoint: readonly float32 + current_setpoint: readonly float32 + trajectory_done: readonly bool + vel_integrator_current: float32 + anticogging_valid: bool + config: + c_is_class: False + attributes: + gain_scheduling_width: float32 + enable_vel_limit: bool + enable_current_mode_vel_limit: + type: bool + doc: Enable velocity limit in current control mode (requires a valid velocity estimator). + enable_gain_scheduling: bool + enable_overspeed_error: bool + control_mode: ControlMode + input_mode: InputMode + pos_gain: + type: float32 + unit: (counts/s) / counts + vel_gain: + type: float32 + unit: 'A/(counts/s) (or A/(rad/s) in sensorless mode' + vel_integrator_gain: + type: float32 + unit: A/(counts/s * s) + vel_limit: + type: float32 + unit: counts/s + doc: Infinity to disable. + vel_limit_tolerance: + type: float32 + doc: Ratio to `vel_limit`. Infinity to disable. + vel_ramp_rate: float32 + current_ramp_rate: + type: float32 + unit: A / sec + homing_speed: + type: float32 + unit: counts/s + inertia: + type: float32 + unit: A/(count/s^2) + axis_to_mirror: uint8 + mirror_ratio: float32 + load_encoder_axis: + type: uint8 + # TODO: this is meaningless for a user. Should there be a separate developer note? + doc: Default depends on Axis number and is set in load_configuration() + input_filter_bandwidth: + type: float32 + unit: 1/s + c_setter: set_input_filter_bandwidth + anticogging: + c_is_class: False + attributes: + index: readonly uint32 + pre_calibrated: bool + calib_anticogging: readonly bool + calib_pos_threshold: float32 + calib_vel_threshold: float32 + cogging_ratio: readonly float32 + anticogging_enabled: bool + functions: + move_incremental: {in: {displacement: float32, from_input_pos: bool}} + start_anticogging_calibration: + + + ODrive.Encoder: + c_is_class: True + attributes: + error: + nullflag: None + flags: + UnstableGain: + CprPolepairsMismatch: + NoResponse: + UnsupportedEncoderMode: + IllegalHallState: + IndexNotFoundYet: + AbsSpiTimeout: + AbsSpiComFail: + AbsSpiNotReady: + is_ready: readonly bool + index_found: readonly bool + shadow_count: readonly int32 + count_in_cpr: readonly int32 + interpolation: readonly float32 + phase: readonly float32 + pos_estimate: readonly float32 + pos_cpr: readonly float32 + hall_state: readonly uint8 + vel_estimate: readonly float32 + calib_scan_response: readonly float32 + pos_abs: int32 + spi_error_rate: readonly float32 + config: + c_is_class: False + attributes: + mode: Mode + use_index: {type: bool, c_setter: set_use_index} + find_idx_on_lockin_only: {type: bool, c_setter: set_find_idx_on_lockin_only} + abs_spi_cs_gpio_pin: {type: uint16, c_setter: set_abs_spi_cs_gpio_pin} + zero_count_on_find_idx: bool + cpr: int32 + offset: int32 + pre_calibrated: {type: bool, c_setter: set_pre_calibrated} + offset_float: float32 + enable_phase_interpolation: bool + bandwidth: {type: float32, c_setter: set_bandwidth} + calib_range: float32 + calib_scan_distance: float32 + calib_scan_omega: float32 + idx_search_unidirectional: bool + ignore_illegal_hall_state: bool + sincos_gpio_pin_sin: uint16 + sincos_gpio_pin_cos: uint16 + functions: + set_linear_count: {in: {count: int32}} + + + ODrive.SensorlessEstimator: + c_is_class: True + attributes: + error: + nullflag: None + flags: + UnstableGain: + phase: float32 + pll_pos: float32 + vel_estimate: float32 + # pll_kp: float32 + # pll_ki: float32 + config: + c_is_class: False + attributes: + observer_gain: float32 + pll_bandwidth: float32 + pm_flux_linkage: float32 + + + ODrive.TrapezoidalTrajectory: + c_is_class: True + attributes: + config: + c_is_class: False + attributes: + vel_limit: float32 + accel_limit: float32 + decel_limit: float32 + + + ODrive.Endstop: + c_is_class: True + attributes: + endstop_state: readonly bool + config: + c_is_class: False + attributes: + gpio_num: {type: uint16, c_setter: set_gpio_num} + enabled: {type: bool, c_setter: set_enabled} + offset: float32 + is_active_high: bool + pullup: bool + debounce_ms: {type: uint32, c_setter: set_debounce_ms} + + +valuetypes: + ODrive.Can.Protocol: + values: {Simple: } + + ODrive.Axis.AxisState: # TODO: remove redundant "Axis" in name + values: + Undefined: + doc: will fall through to idle + Idle: + doc: disable PWM and do nothing + StartupSequence: + doc: the actual sequence is defined by the config.startup... flags + FullCalibrationSequence: + doc: run all calibration procedures, then idle + MotorCalibration: + doc: run motor calibration + SensorlessControl: + doc: run sensorless control + EncoderIndexSearch: + doc: run encoder index search + EncoderOffsetCalibration: + doc: run encoder offset calibration + ClosedLoopControl: + doc: run closed loop control + LockinSpin: + doc: run lockin spin + EncoderDirFind: + Homing: + doc: run axis homing function + + ODrive.Encoder.Mode: + values: + Incremental: + Hall: + Sincos: + SpiAbsCui: + value: 0x100 + doc: compatible with CUI AMT23xx + SpiAbsAms: + value: 0x101 + doc: compatible with AMS AS5047P, AS5048A/AS5048B (no daisy chain support) + SpiAbsAeat: + value: 0x102 + doc: not yet implemented + + ODrive.Controller.ControlMode: + values: + # Note: these should be sorted from lowest level of control to + # highest level of control, to allow "<" style comparisons. + VoltageControl: + CurrentControl: + VelocityControl: + PositionControl: + + ODrive.Controller.InputMode: + values: + Inactive: + Passthrough: + VelRamp: + PosFilter: + MixChannels: + TrapTraj: + CurrentRamp: + Mirror: + + + ODrive.Motor.MotorType: + values: + HighCurrent: + #LowCurrent: # not implemented + Gimbal: {value: 2} + Acim: \ No newline at end of file diff --git a/GUI/src/assets/odriveEnums.json b/GUI/src/assets/odriveEnums.json new file mode 100644 index 000000000..4998a3115 --- /dev/null +++ b/GUI/src/assets/odriveEnums.json @@ -0,0 +1,131 @@ +{ + "GPIO_MODE_DIGITAL": 0, + "GPIO_MODE_DIGITAL_PULL_UP": 1, + "GPIO_MODE_DIGITAL_PULL_DOWN": 2, + "GPIO_MODE_ANALOG_IN": 3, + "GPIO_MODE_UART_A": 4, + "GPIO_MODE_UART_B": 5, + "GPIO_MODE_UART_C": 6, + "GPIO_MODE_CAN_A": 7, + "GPIO_MODE_I2C_A": 8, + "GPIO_MODE_SPI_A": 9, + "GPIO_MODE_PWM": 10, + "GPIO_MODE_ENC0": 11, + "GPIO_MODE_ENC1": 12, + "GPIO_MODE_ENC2": 13, + "GPIO_MODE_MECH_BRAKE": 14, + "GPIO_MODE_STATUS": 15, + "STREAM_PROTOCOL_TYPE_FIBRE": 0, + "STREAM_PROTOCOL_TYPE_ASCII": 1, + "STREAM_PROTOCOL_TYPE_STDOUT": 2, + "STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT": 3, + "PROTOCOL_SIMPLE": 1, + "AXIS_STATE_UNDEFINED": 0, + "AXIS_STATE_IDLE": 1, + "AXIS_STATE_STARTUP_SEQUENCE": 2, + "AXIS_STATE_FULL_CALIBRATION_SEQUENCE": 3, + "AXIS_STATE_MOTOR_CALIBRATION": 4, + "AXIS_STATE_ENCODER_INDEX_SEARCH": 6, + "AXIS_STATE_ENCODER_OFFSET_CALIBRATION": 7, + "AXIS_STATE_CLOSED_LOOP_CONTROL": 8, + "AXIS_STATE_LOCKIN_SPIN": 9, + "AXIS_STATE_ENCODER_DIR_FIND": 10, + "AXIS_STATE_HOMING": 11, + "AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION": 12, + "AXIS_STATE_ENCODER_HALL_PHASE_CALIBRATION": 13, + "ENCODER_MODE_INCREMENTAL": 0, + "ENCODER_MODE_HALL": 1, + "ENCODER_MODE_SINCOS": 2, + "ENCODER_MODE_SPI_ABS_CUI": 256, + "ENCODER_MODE_SPI_ABS_AMS": 257, + "ENCODER_MODE_SPI_ABS_AEAT": 258, + "ENCODER_MODE_SPI_ABS_RLS": 259, + "ENCODER_MODE_SPI_ABS_MA732": 260, + "CONTROL_MODE_VOLTAGE_CONTROL": 0, + "CONTROL_MODE_TORQUE_CONTROL": 1, + "CONTROL_MODE_VELOCITY_CONTROL": 2, + "CONTROL_MODE_POSITION_CONTROL": 3, + "INPUT_MODE_INACTIVE": 0, + "INPUT_MODE_PASSTHROUGH": 1, + "INPUT_MODE_VEL_RAMP": 2, + "INPUT_MODE_POS_FILTER": 3, + "INPUT_MODE_MIX_CHANNELS": 4, + "INPUT_MODE_TRAP_TRAJ": 5, + "INPUT_MODE_TORQUE_RAMP": 6, + "INPUT_MODE_MIRROR": 7, + "INPUT_MODE_TUNING": 8, + "MOTOR_TYPE_HIGH_CURRENT": 0, + "MOTOR_TYPE_GIMBAL": 2, + "MOTOR_TYPE_ACIM": 3, + "ODRIVE_ERROR_NONE": 0, + "ODRIVE_ERROR_CONTROL_ITERATION_MISSED": 1, + "ODRIVE_ERROR_DC_BUS_UNDER_VOLTAGE": 2, + "ODRIVE_ERROR_DC_BUS_OVER_VOLTAGE": 4, + "ODRIVE_ERROR_DC_BUS_OVER_REGEN_CURRENT": 8, + "ODRIVE_ERROR_DC_BUS_OVER_CURRENT": 16, + "ODRIVE_ERROR_BRAKE_DEADTIME_VIOLATION": 32, + "ODRIVE_ERROR_BRAKE_DUTY_CYCLE_NAN": 64, + "ODRIVE_ERROR_INVALID_BRAKE_RESISTANCE": 128, + "CAN_ERROR_NONE": 0, + "CAN_ERROR_DUPLICATE_CAN_IDS": 1, + "AXIS_ERROR_NONE": 0, + "AXIS_ERROR_INVALID_STATE": 1, + "AXIS_ERROR_WATCHDOG_TIMER_EXPIRED": 2048, + "AXIS_ERROR_MIN_ENDSTOP_PRESSED": 4096, + "AXIS_ERROR_MAX_ENDSTOP_PRESSED": 8192, + "AXIS_ERROR_ESTOP_REQUESTED": 16384, + "AXIS_ERROR_HOMING_WITHOUT_ENDSTOP": 131072, + "AXIS_ERROR_OVER_TEMP": 262144, + "AXIS_ERROR_UNKNOWN_POSITION": 524288, + "MOTOR_ERROR_NONE": 0, + "MOTOR_ERROR_PHASE_RESISTANCE_OUT_OF_RANGE": 1, + "MOTOR_ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE": 2, + "MOTOR_ERROR_DRV_FAULT": 8, + "MOTOR_ERROR_CONTROL_DEADLINE_MISSED": 16, + "MOTOR_ERROR_MODULATION_MAGNITUDE": 128, + "MOTOR_ERROR_CURRENT_SENSE_SATURATION": 1024, + "MOTOR_ERROR_CURRENT_LIMIT_VIOLATION": 4096, + "MOTOR_ERROR_MODULATION_IS_NAN": 65536, + "MOTOR_ERROR_MOTOR_THERMISTOR_OVER_TEMP": 131072, + "MOTOR_ERROR_FET_THERMISTOR_OVER_TEMP": 262144, + "MOTOR_ERROR_TIMER_UPDATE_MISSED": 524288, + "MOTOR_ERROR_CURRENT_MEASUREMENT_UNAVAILABLE": 1048576, + "MOTOR_ERROR_CONTROLLER_FAILED": 2097152, + "MOTOR_ERROR_I_BUS_OUT_OF_RANGE": 4194304, + "MOTOR_ERROR_BRAKE_RESISTOR_DISARMED": 8388608, + "MOTOR_ERROR_SYSTEM_LEVEL": 16777216, + "MOTOR_ERROR_BAD_TIMING": 33554432, + "MOTOR_ERROR_UNKNOWN_PHASE_ESTIMATE": 67108864, + "MOTOR_ERROR_UNKNOWN_PHASE_VEL": 134217728, + "MOTOR_ERROR_UNKNOWN_TORQUE": 268435456, + "MOTOR_ERROR_UNKNOWN_CURRENT_COMMAND": 536870912, + "MOTOR_ERROR_UNKNOWN_CURRENT_MEASUREMENT": 1073741824, + "MOTOR_ERROR_UNKNOWN_VBUS_VOLTAGE": 2147483648, + "MOTOR_ERROR_UNKNOWN_VOLTAGE_COMMAND": 4294967296, + "MOTOR_ERROR_UNKNOWN_GAINS": 8589934592, + "MOTOR_ERROR_CONTROLLER_INITIALIZING": 17179869184, + "MOTOR_ERROR_UNBALANCED_PHASES": 34359738368, + "CONTROLLER_ERROR_NONE": 0, + "CONTROLLER_ERROR_OVERSPEED": 1, + "CONTROLLER_ERROR_INVALID_INPUT_MODE": 2, + "CONTROLLER_ERROR_UNSTABLE_GAIN": 4, + "CONTROLLER_ERROR_INVALID_MIRROR_AXIS": 8, + "CONTROLLER_ERROR_INVALID_LOAD_ENCODER": 16, + "CONTROLLER_ERROR_INVALID_ESTIMATE": 32, + "CONTROLLER_ERROR_INVALID_CIRCULAR_RANGE": 64, + "CONTROLLER_ERROR_SPINOUT_DETECTED": 128, + "ENCODER_ERROR_NONE": 0, + "ENCODER_ERROR_UNSTABLE_GAIN": 1, + "ENCODER_ERROR_CPR_POLEPAIRS_MISMATCH": 2, + "ENCODER_ERROR_NO_RESPONSE": 4, + "ENCODER_ERROR_UNSUPPORTED_ENCODER_MODE": 8, + "ENCODER_ERROR_ILLEGAL_HALL_STATE": 16, + "ENCODER_ERROR_INDEX_NOT_FOUND_YET": 32, + "ENCODER_ERROR_ABS_SPI_TIMEOUT": 64, + "ENCODER_ERROR_ABS_SPI_COM_FAIL": 128, + "ENCODER_ERROR_ABS_SPI_NOT_READY": 256, + "ENCODER_ERROR_HALL_NOT_CALIBRATED_YET": 512, + "SENSORLESS_ESTIMATOR_ERROR_NONE": 0, + "SENSORLESS_ESTIMATOR_ERROR_UNSTABLE_GAIN": 1, + "SENSORLESS_ESTIMATOR_ERROR_UNKNOWN_CURRENT_MEASUREMENT": 2 +} \ No newline at end of file diff --git a/GUI/src/assets/odrive_logo.png b/GUI/src/assets/odrive_logo.png new file mode 100644 index 000000000..617c636b8 Binary files /dev/null and b/GUI/src/assets/odrive_logo.png differ diff --git a/GUI/src/assets/styles/style.css b/GUI/src/assets/styles/style.css new file mode 100644 index 000000000..d17e84dfa --- /dev/null +++ b/GUI/src/assets/styles/style.css @@ -0,0 +1,23 @@ +.card { + padding: 10px; + margin: 10px; + background-color: #fff; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.4); +} + +.close-button { + font-weight: bold; + cursor: pointer; + padding: 0 5px; + margin-right: 10px; + border: 1px solid black; +} + +.close-button:active { + background-color: var(--bg-color); +} + +.choice-inactive { + background-color: var(--bg-color); + color: grey; +} \ No newline at end of file diff --git a/GUI/src/assets/styles/vars.css b/GUI/src/assets/styles/vars.css new file mode 100644 index 000000000..1e48dbd0e --- /dev/null +++ b/GUI/src/assets/styles/vars.css @@ -0,0 +1,6 @@ +:root { + --bg-color: rgb(235, 235, 235); + --fg-color: #fff; + --top-height: 39px; + --bottom-height: 29px; +} \ No newline at end of file diff --git a/GUI/src/assets/wizard/configTemplate.json b/GUI/src/assets/wizard/configTemplate.json new file mode 100644 index 000000000..6bfab803f --- /dev/null +++ b/GUI/src/assets/wizard/configTemplate.json @@ -0,0 +1,77 @@ +{ + "config": { + "brake_resistance": null + }, + "axis0": { + "motor": { + "config": { + "pole_pairs": null, + "phase_resistance": null, + "phase_inductance": null, + "motor_type": null, + "current_lim": null, + "torque_constant": null + } + }, + "encoder": { + "config": { + "mode": null, + "use_index": null, + "cpr": null, + "calib_scan_distance": null, + "abs_spi_cs_gpio_pin": null + } + }, + "controller": { + "config": { + "input_mode": null, + "control_mode": null, + "vel_limit": null, + "inertia": null + } + }, + "trap_traj": { + "config": { + "vel_limit": null, + "accel_limit": null, + "decel_limit": null + } + } + }, + "axis1": { + "motor": { + "config": { + "pole_pairs": null, + "phase_resistance": null, + "phase_inductance": null, + "motor_type": null, + "current_lim": null, + "torque_constant": null + } + }, + "encoder": { + "config": { + "mode": null, + "use_index": null, + "cpr": null, + "calib_scan_distance": null, + "abs_spi_cs_gpio_pin": null + } + }, + "controller": { + "config": { + "input_mode": null, + "control_mode": null, + "vel_limit": null, + "inertia": null + } + }, + "trap_traj": { + "config": { + "vel_limit": null, + "accel_limit": null, + "decel_limit": null + } + } + } +} \ No newline at end of file diff --git a/GUI/src/assets/wizard/wizard.js b/GUI/src/assets/wizard/wizard.js new file mode 100644 index 000000000..aa0486f4b --- /dev/null +++ b/GUI/src/assets/wizard/wizard.js @@ -0,0 +1,929 @@ +import odriveEnums from "../odriveEnums.json"; + +// wizard is an object of pages. +// each page has a number, title, vue component name, next destination, back destination, and array of choices +// certain choices require a hook - for example, hall effect encoder cpr is 6 * motor.config.pole_pairs +// each hook takes a config object, modifies it, and returns it after running appropriate calculations. +// see choiceHandler() in Wizard.vue for how this happens + +// Pages can also have custom components displayed below the choice tiles +// Example - on motor pages, there are buttons to run motor calibration and clear errors. +export let pages = { + ODrive: { + title: "Which ODrive do you have?", + link: "ODrive Version", + component: "wizardPage", + next: "Brake", + back: "ODrive", + nextTooltip: "Make a choice!", + choiceMade: false, + choices: [ + { + component: "wizardChoice", + data: { + imageURL: require("../images/24v_300x300.png"), + }, + title: "ODrive v3.6 24V", + requirements: [], + hooks: [], + tooltip: null, + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/56v_300x300.png"), + }, + title: "ODrive v3.6 56V", + requirements: [], + hooks: [], + tooltip: null, + altTooltip: null, + }, + ], + customComponents: [], + pageComponents: [], + }, + Brake: { + title: "Brake Resistor", + link: "Brake Resistor", + component: "wizardPage", + next: "Motor_0", + back: "ODrive", + nextTooltip: "Make a choice!", + choiceMade: false, + choices: [ + { + component: "wizardBrake", + data: {}, + title: "Brake Resistor", + requirements: [], + hooks: [], + tooltip: "When you slow down a motor, the energy has to go somewhere.\n\r \ + It either goes to the brake resistor or back to your power supply.", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: null, + configStub: { + config: { + brake_resistance: 0, + } + }, + }, + title: "I don't have a brake resistor", + hooks: [], + requirements: [], + tooltip: "Only operate without a brake resistor if your power source can handle reverse current, like a battery." + } + ], + }, + Motor_0: { + title: "Which motor are you using on Axis 0?", + link: "Motor 0", + component: "wizardPage", + next: "Encoder_0", + back: "Brake", + nextTooltip: "ODrive needs to know the inductance and resistance of your motor in order to control it.\ + Click the Calibrate Motor button to start the measurement process. The motor will beep\ + during calibration.", + choiceMade: false, + choices: [ + { + component: "wizardChoice", + data: { + imageURL: require("../images/D5065_300x300.png"), + configStub: { + axis0: { + motor: { + config: { + pole_pairs: 7, + torque_constant: 8.27 / 270, + // R and L come from "Calibrate Motor" button on page + //phase_resistance: 0.039, // from test rig + //phase_inductance: 1.57e-5, + motor_type: odriveEnums.MOTOR_TYPE_HIGH_CURRENT, // MOTOR_TYPE_HIGH_CURRENT + }, + }, + }, + }, + }, + title: "ODrive D5065", + requirements: [], + hooks: [], + tooltip: "D5065 motor from the ODrive shop", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/D6374_300x300.png"), + configStub: { + axis0: { + motor: { + config: { + pole_pairs: 7, + torque_constant: 8.27 / 150, + // R and L come from "Calibrate Motor" button on page + //phase_resistance: 0.041, // measurement from PJ + //phase_inductance: 2.23e-5, + motor_type: odriveEnums.MOTOR_TYPE_HIGH_CURRENT, // MOTOR_TYPE_HIGH_CURRENT, + }, + }, + }, + }, + }, + title: "ODrive D6374", + requirements: [], + hooks: [], + tooltip: "D6374 motor from the ODrive shop", + altTooltip: null, + }, + { + component: "wizardMotor", + data: { + axis: "axis0", + }, + title: "Other Motor", + requirements: [], + hooks: [], + tooltip: "Bring your own motor!", + altTooltip: null, + }, + ], + pageComponents: [ + { + component: "wizardMotorCal", + id: 0, + data: { + axis: "axis0", + } + }, + { + component: "wizardCalStatus", + id: 1, + data: { + success: "Calibration succeeded!", + failure: "Calibration failed", + tooltip: "Double check your motor connections. If the connections are good, it is \ + possible that your motor has a higher resistance or lower inductance than ODrive expects." + } + } + ] + }, + Encoder_0: { + title: "Which encoder are you using for Axis 0?", + link: "Encoder 0", + component: "wizardPage", + next: "Control_0", + back: "Motor_0", + nextTooltip: "ODrive needs to know the relation between the encoder position and motor position in order to function. \ + Click the Calibrate Encoder button to start the measurement process. The motor will slowly rotate back \ + and forth during calibration.", + choiceMade: false, + choices: [ + { + component: "wizardChoice", + data: { + imageURL: require("../images/amt102-v_300x300.png"), + configStub: { + axis0: { + encoder: { + config: { + cpr: 8192, + use_index: true, + mode: odriveEnums.ENCODER_MODE_INCREMENTAL, // ENCODER_MODE_INCREMENTAL + } + } + } + }, + }, + title: "CUI AMT102V", + requirements: [], + hooks: [], + tooltip: "Incremental 8192cpr encoder from the ODrive shop", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/hall_effect_300x300.png"), + configStub: { + axis0: { + encoder: { + config: { + use_index: false, + mode: odriveEnums.ENCODER_MODE_HALL, + } + } + } + }, + }, + title: "Hall Effect", + requirements: [], + hooks: [ + // hook takes a config object (normally this.wizardConfig in Wizard.vue), creates and returns a copy + (configObj) => { + let newConfig = configObj; + newConfig.axis0.encoder.config.cpr = 6 * newConfig.axis0.motor.config.pole_pairs; + return newConfig; + }, + ], + tooltip: "If your motor is 'sensored' and you don't have another encoder, select this option", + altTooltip: null, + }, + { + component: "wizardEncoderIncremental", + data: { + axis: "axis0", + }, + title: "Incremental Without Index", + requirements: [], + hooks: [], + tooltip: "Generic incremental encoder without index. Set cpr to 4 * PPR (pulses per revolution).", + altTooltip: null, + }, + { + component: "wizardEncoderIncrementalIndex", + data: { + axis: "axis0", + }, + title: "Incremental With Index", + requirements: [], + hooks: [], + tooltip: "Generic incremental encoder with index. Set cpr to 4 * PPR (pulses per revolution).", + altTooltip: null, + }, + ], + pageComponents: [ + { + component: "wizardEncoderCal", + id: 0, + data: { + axis: "axis0", + } + }, + { + component: "wizardCalStatus", + id: 1, + data: { + success: "Calibration succeeded!", + failure: "Calibration failed", + tooltip: "Double check your encoder connections. If the connections are good, you \ + have a wrong setting for motor pole pairs or encoder cpr." + } + } + ] + }, + Control_0: { + title: "Control mode for Axis 0", + link: "Control Modes 0", + component: "wizardPage", + next: "Misc_0", + back: "Encoder_0", + nextTooltip: "Make a choice!", + choiceMade: false, + choices: [ + { + component: "wizardChoice", + data: { + imageURL: require("../images/temp.png"), + configStub: { + axis0: { + controller: { + config: { + control_mode: odriveEnums.CONTROL_MODE_POSITION_CONTROL, + }, + }, + }, + }, + }, + title: "Position Control", + requirements: [], + hooks: [], + tooltip: "Use this mode to control the position of the motor", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/temp.png"), + configStub: { + axis0: { + controller: { + config: { + control_mode: odriveEnums.CONTROL_MODE_VELOCITY_CONTROL, + }, + }, + }, + }, + }, + title: "Velocity Control", + requirements: [], + hooks: [], + tooltip: "Use this mode to control the velocity of the motor", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/temp.png"), + configStub: { + axis0: { + controller: { + config: { + control_mode: odriveEnums.CONTROL_MODE_TORQUE_CONTROL, + }, + }, + }, + }, + }, + title: "Torque Control", + requirements: [], + hooks: [], + tooltip: "Use this mode to control the torque output of the motor", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/temp.png"), + configStub: { + axis0: { + controller: { + config: { + control_mode: odriveEnums.CONTROL_MODE_VOLTAGE_CONTROL, + }, + }, + }, + }, + }, + title: "Voltage Control", + requirements: [], + hooks: [], + tooltip: "This mode is used for gimbal motors", + altTooltip: null, + }, + ], + customComponents: [], + pageComponents: [], + }, + /* + Input_0: { + title: "Input mode selection for Axis 0", + link: "Input Modes 0", + component: "wizardPage", + next: "Misc_0", + back: "Control_0", + nextTooltip: "Make a choice!", + choiceMade: false, + choices: [ + { + component: "wizardChoice", + data: { + imageURL: require("../images/temp.png"), + configStub: { + axis0: { + controller: { + config: { + input_mode: odriveEnums.INPUT_MODE_PASSTHROUGH, + }, + }, + }, + }, + }, + title: "Input Passthrough", + requirements: [], + hooks: [], + tooltip: "This is the default input mode. Input commands are passed to the controller without modification.", + altTooltip: null, + }, + { + component: "wizardInputFiltered", + data: { + axis: "axis0", + }, + title: "Position Input Filtering", + requirements: [ + (configObj) => { + return configObj.axis0.controller.config.control_mode == odriveEnums.CONTROL_MODE_POSITION_CONTROL; + }, + ], + hooks: [], + tooltip: "Filtered position mode. This provides smoother motion than the passthrough mode. \ + Input position commands are sent through a 2nd order filter. A higher bandwidth will cause quicker position changes.", + altTooltip: "Control mode must be Position Control for this input mode", + }, + { + component: "wizardInputVelRamp", + data: { + axis: "axis0", + }, + title: "Velocity Input Ramping", + requirements: [ + (configObj) => { + return configObj.axis0.controller.config.control_mode == odriveEnums.CONTROL_MODE_VELOCITY_CONTROL; + }, + ], + hooks: [], + tooltip: "For velocity control mode, you can choose to have the input velocities ramped between values. vel_ramp_rate is the \ + acceleration between velocities.", + altTooltip: "Control mode must be Velocity Control for this input mode", + }, + { + component: "wizardInputTrajectory", + data: { + axis: "axis0", + }, + title: "Trajectory Planning", + requirements: [ + (configObj) => { + return configObj.axis0.controller.config.control_mode == odriveEnums.CONTROL_MODE_POSITION_CONTROL; + }, + ], + hooks: [], + tooltip: "This mode lets you smoothly accelerate, coast, and decelerate from one position to another. vel_limit is your coasting speed, \ + accel_limit is the maximum acceleration in turns/s^2, decel_limit is the maximum decelaration in turns/s^2 and inertia is an \ + optional value to correlate acceleration with motor torque.", + altTooltip: "Control mode must be Position Control for this input mode", + } + ], + pageComponents: [], + }, + */ + Misc_0: { + title: "Finishing touches for Axis 0", + link: "Misc 0", + component: "wizardPage", + next: "Motor_1", + back: "Control_0", + nextTooltip: "Make a choice!", + choiceMade: false, + choices: [ + { + component: "wizardMisc", + data: { + axis: "axis0", + }, + title: "Miscellaneous Parameters", + requirements: [], + hooks: [], + tooltip: null, + altTooltip: null, + }, + ], + pageComponents: [], + }, + Motor_1: { + title: "Which motor are you using for Axis 1?", + link: "Motor 1", + component: "wizardPage", + next: "Encoder_1", + back: "Misc_0", + nextTooltip: "ODrive needs to know the inductance and resistance of your motor in order to control it.\ + Click the Calibrate Motor button to start the measurement process. The motor will beep\ + during calibration.", + choiceMade: false, + choices: [ + { + component: "wizardChoice", + data: { + imageURL: require("../images/D5065_300x300.png"), + configStub: { + axis1: { + motor: { + config: { + pole_pairs: 7, + torque_constant: 8.27 / 270, + // R and L come from "Calibrate Motor" button on page + //phase_resistance: 0.039, // from test rig + //phase_inductance: 1.57e-5, + motor_type: odriveEnums.MOTOR_TYPE_HIGH_CURRENT, // MOTOR_TYPE_HIGH_CURRENT + }, + }, + }, + }, + }, + title: "ODrive D5065", + requirements: [], + hooks: [], + tooltip: "D5065 motor from the ODrive shop", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/D6374_300x300.png"), + configStub: { + axis1: { + motor: { + config: { + pole_pairs: 7, + torque_constant: 8.27 / 150, + // R and L come from "Calibrate Motor" button on page + //phase_resistance: 0.041, // measurement from PJ + //phase_inductance: 2.23e-5, + motor_type: odriveEnums.MOTOR_TYPE_HIGH_CURRENT, // MOTOR_TYPE_HIGH_CURRENT, + }, + }, + }, + }, + }, + title: "ODrive D6374", + requirements: [], + hooks: [], + tooltip: "D6374 motor from the ODrive shop", + altTooltip: null, + }, + { + component: "wizardMotor", + data: { + axis: "axis1", + }, + title: "Other Motor", + requirements: [], + hooks: [], + tooltip: "Bring your own motor!", + altTooltip: null, + }, + ], + pageComponents: [ + { + component: "wizardMotorCal", + id: 0, + data: { + axis: "axis1", + } + }, + { + component: "wizardCalStatus", + id: 1, + data: { + success: "Calibration succeeded!", + failure: "Calibration failed", + tooltip: "Double check your motor connections. If the connections are good, it is \ + possible that your motor has a higher resistance or lower inductance than ODrive expects." + } + } + ] + }, + Encoder_1: { + title: "Which encoder are you using for Axis 1?", + link: "Encoder 1", + component: "wizardPage", + next: "Control_1", + back: "Motor_1", + nextTooltip: "ODrive needs to know the relation between the encoder position and motor position in order to function. \ + Click the Calibrate Encoder button to start the measurement process. The motor will slowly rotate back \ + and forth during calibration.", + choiceMade: false, + choices: [ + { + component: "wizardChoice", + data: { + imageURL: require("../images/amt102-v_300x300.png"), + configStub: { + axis1: { + encoder: { + config: { + cpr: 8192, + use_index: true, + mode: odriveEnums.ENCODER_MODE_INCREMENTAL, // ENCODER_MODE_INCREMENTAL + } + } + } + }, + }, + title: "CUI AMT102V", + requirements: [], + hooks: [], + tooltip: "Incremental 8192cpr encoder from the ODrive shop", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/hall_effect_300x300.png"), + configStub: { + axis1: { + encoder: { + config: { + use_index: false, + mode: odriveEnums.ENCODER_MODE_HALL, + } + } + } + }, + }, + title: "Hall Effect", + requirements: [], + hooks: [ + // hook takes a config object (normally this.wizardConfig in Wizard.vue), creates and returns a copy + (configObj) => { + let newConfig = configObj; + newConfig.axis1.encoder.config.cpr = 6 * newConfig.axis1.motor.config.pole_pairs; + return newConfig; + }, + ], + tooltip: "If your motor is 'sensored' and you don't have another encoder, select this option", + altTooltip: null, + }, + { + component: "wizardEncoderIncremental", + data: { + axis: "axis1", + }, + title: "Incremental Without Index", + requirements: [], + hooks: [], + tooltip: "Generic incremental encoder without index. Set cpr to 4 * PPR (pulses per revolution).", + altTooltip: null, + }, + { + component: "wizardEncoderIncrementalIndex", + data: { + axis: "axis1", + }, + title: "Incremental With Index", + requirements: [], + hooks: [], + tooltip: "Generic incremental encoder with index. Set cpr to 4 * PPR (pulses per revolution).", + altTooltip: null, + }, + ], + pageComponents: [ + { + component: "wizardEncoderCal", + id: 0, + data: { + axis: "axis1", + } + }, + { + component: "wizardCalStatus", + id: 1, + data: { + success: "Calibration succeeded!", + failure: "Calibration failed", + tooltip: "Double check your encoder connections. If the connections are good, you either \ + have a wrong setting for motor pole pairs or encoder cpr." + } + } + ] + }, + Control_1: { + title: "Control mode for Axis 1", + link: "Control Modes 1", + component: "wizardPage", + next: "Misc_1", + back: "Encoder_1", + nextTooltip: "Make a choice!", + choiceMade: false, + choices: [ + { + component: "wizardChoice", + data: { + imageURL: require("../images/temp.png"), + configStub: { + axis1: { + controller: { + config: { + control_mode: odriveEnums.CONTROL_MODE_POSITION_CONTROL, + }, + }, + }, + }, + }, + title: "Position Control", + requirements: [], + hooks: [], + tooltip: "Use this mode to control the position of the motor", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/temp.png"), + configStub: { + axis1: { + controller: { + config: { + control_mode: odriveEnums.CONTROL_MODE_VELOCITY_CONTROL, + }, + }, + }, + }, + }, + title: "Velocity Control", + requirements: [], + hooks: [], + tooltip: "Use this mode to control the velocity of the motor", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/temp.png"), + configStub: { + axis1: { + controller: { + config: { + control_mode: odriveEnums.CONTROL_MODE_TORQUE_CONTROL, + }, + }, + }, + }, + }, + title: "Torque Control", + requirements: [], + hooks: [], + tooltip: "Use this mode to control the torque output of the motor", + altTooltip: null, + }, + { + component: "wizardChoice", + data: { + imageURL: require("../images/temp.png"), + configStub: { + axis1: { + controller: { + config: { + control_mode: odriveEnums.CONTROL_MODE_VOLTAGE_CONTROL, + }, + }, + }, + }, + }, + title: "Voltage Control", + requirements: [], + hooks: [], + tooltip: "This mode is used for gimbal motors", + altTooltip: null, + }, + ], + customComponents: [], + pageComponents: [], + }, + /* + Input_1: { + title: "Input mode selection for Axis 1", + link: "Input Modes 1", + component: "wizardPage", + next: "Misc_1", + back: "Control_1", + nextTooltip: "Make a choice!", + choiceMade: false, + choices: [ + { + component: "wizardChoice", + data: { + imageURL: require("../images/temp.png"), + configStub: { + axis1: { + controller: { + config: { + input_mode: odriveEnums.INPUT_MODE_PASSTHROUGH, + }, + }, + }, + }, + }, + title: "Input Passthrough", + requirements: [], + hooks: [], + tooltip: "This is the default input mode. Input commands are passed to the controller without modification.", + altTooltip: null, + }, + { + component: "wizardInputFiltered", + data: { + axis: "axis1", + }, + title: "Position Input Filtering", + requirements: [ + (configObj) => { + return configObj.axis1.controller.config.control_mode == odriveEnums.CONTROL_MODE_POSITION_CONTROL; + }, + ], + hooks: [], + tooltip: "Filtered position mode. This provides smoother motion than the passthrough mode. \ + Input position commands are sent through a 2nd order filter. A higher bandwidth will cause quicker position changes.", + altTooltip: "Control mode must be Position Control for this input mode", + }, + { + component: "wizardInputVelRamp", + data: { + axis: "axis1", + }, + title: "Velocity Input Ramping", + requirements: [ + (configObj) => { + return configObj.axis1.controller.config.control_mode == odriveEnums.CONTROL_MODE_VELOCITY_CONTROL; + }, + ], + hooks: [], + tooltip: "For velocity control mode, you can choose to have the input velocities ramped between values. vel_ramp_rate is the \ + acceleration between velocities.", + altTooltip: "Control mode must be Velocity Control for this input mode", + }, + { + component: "wizardInputTrajectory", + data: { + axis: "axis1", + }, + title: "Trajectory Planning", + requirements: [ + (configObj) => { + return configObj.axis1.controller.config.control_mode == odriveEnums.CONTROL_MODE_POSITION_CONTROL; + }, + ], + hooks: [], + tooltip: "This mode lets you smoothly accelerate, coast, and decelerate from one position to another. vel_limit is your coasting speed, \ + accel_limit is the maximum acceleration in turns/s^2, decel_limit is the maximum decelaration in turns/s^2 and inertia is an \ + optional value to correlate acceleration with motor torque.", + altTooltip: "Control most must be Position Control for this input mode", + } + ], + pageComponents: [], + }, + */ + Misc_1: { + title: "Finishing touches for Axis 1", + link: "Misc 1", + component: "wizardPage", + next: "End", + back: "Control_1", + nextTooltip: "Make a choice!", + choiceMade: false, + choices: [ + { + component: "wizardMisc", + data: { + axis: "axis1", + }, + title: "Miscellaneous Parameters", + requirements: [], + hooks: [], + tooltip: null, + altTooltip: null, + }, + ], + pageComponents: [], + }, + End: { + title: "Review choices", + link: "Review and Apply", + component: "wizardPage", + next: "End", + back: "Misc_1", + choiceMade: false, + choices: [ + { + component: "wizardEnd", + data: {}, + title: null, + requirements: [], + hooks: [], + tooltip: "Old config values are on the left, new config values are on the right in green", + altTooltip: null, + } + ], + pageComponents: [], + } +} + +export let enumVars = { + // for wizardEnd.vue + motor_type: { + 0: "MOTOR_TYPE_HIGH_CURRENT", + 2: "MOTOR_TYPE_GIMBAL", + 3: "MOTOR_TYPE_ACIM", + }, + mode: { + 0: "ENCODER_MODE_INCREMENTAL", + 1: "ENCODER_MODE_HALL", + 2: "ENCODER_MODE_SINCOS", + 256: "ENCODER_MODE_SPI_ABS_CUI", + 257: "ENCODER_MODE_SPI_ABS_AMS", + 258: "ENCODER_MODE_SPI_ABS_AEAT", + 259: "ENCODER_MODE_SPI_ABS_RLS", + }, + input_mode: { + 0: "INPUT_MODE_INACTIVE", + 1: "INPUT_MODE_PASSTHROUGH", + 2: "INPUT_MODE_VEL_RAMP", + 3: "INPUT_MODE_POS_FILTER", + 4: "INPUT_MODE_MIX_CHANNELS", + 5: "INPUT_MODE_TRAP_TRAJ", + 6: "INPUT_MODE_TORQUE_RAMP", + 7: "INPUT_MODE_MIRROR", + }, + control_mode: { + 0: "CONTROL_MODE_VOLTAGE_CONTROL", + 1: "CONTROL_MODE_TORQUE_CONTROL", + 2: "CONTROL_MODE_VELOCITY_CONTROL", + 3: "CONTROL_MODE_POSITION_CONTROL", + }, +} \ No newline at end of file diff --git a/GUI/src/background.js b/GUI/src/background.js new file mode 100644 index 000000000..382155fec --- /dev/null +++ b/GUI/src/background.js @@ -0,0 +1,186 @@ +'use strict' + +import { app, protocol, BrowserWindow, ipcMain } from 'electron' +import { createProtocol } from 'vue-cli-plugin-electron-builder/lib' +import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer' +const isDevelopment = process.env.NODE_ENV !== 'production' + +const { spawnSync, execSync } = require('child_process'); +const spawn = require('child_process').spawn; +const path = require('path'); + +// Keep a global reference of the window object, if you don't, the window will +// be closed automatically when the JavaScript object is garbage collected. +let win; + +// var for python server +let server; + +// Scheme must be registered before the app is ready +protocol.registerSchemesAsPrivileged([ + { scheme: 'app', privileges: { secure: true, standard: true } } +]) + +// function to get determine correct command for python +function getPyCmd() { + // call both 'python' and 'python3' to figure out the correct command + let spawnRet = spawnSync('python', ['-V']); + let success = spawnRet.status != null; + let outputString; + let cmd = ''; + if (success) { + if (spawnRet.stdout.toString().length > 1) { + outputString = spawnRet.stdout.toString(); + } + else { + outputString = spawnRet.stderr.toString(); + } + if (outputString.includes("Python 3")) { + cmd = 'python'; + } + } + if (cmd == '') { + spawnRet = spawnSync('python3', ['-V']); + success = spawnRet.status != null; + if (success) { + if (spawnRet.stdout.toString().length > 1) { + outputString = spawnRet.stdout.toString(); + } + else { + outputString = spawnRet.stderr.toString(); + } + if (outputString.includes("Python 3")) { + cmd = 'python3'; + } + } + } + return cmd; +} + +function createWindow() { + + // Create the browser window. + win = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + // Use pluginOptions.nodeIntegration, leave this alone + // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info + nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION, + preload: path.join(__dirname, 'preload.js') + }, + icon: path.join(__static, 'icon.png') + }) + win.maximize(); + win.setMenu(null); + + if (process.env.WEBPACK_DEV_SERVER_URL) { + // Load the url of the dev server if in development mode + win.loadURL(process.env.WEBPACK_DEV_SERVER_URL) + if (!process.env.IS_TEST) win.webContents.openDevTools() + } else { + createProtocol('app') + // Load the index.html when not in development + win.loadURL('app://./index.html') + } + + win.on('closed', () => { + win = null + }) +} + +// Quit when all windows are closed. +app.on('window-all-closed', () => { + // kill flask server + if (process.platform !== 'win32') { + execSync('kill $(ps aux | grep \'[o]drive_server.py\' | awk \'{print $2}\')'); + console.log("killed flask server"); + } + // On macOS it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { + app.quit() + } +}) + +app.on('activate', () => { + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (win === null) { + createWindow() + } +}) + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +// Some APIs can only be used after this event occurs. +app.on('ready', async () => { + if (isDevelopment && !process.env.IS_TEST) { + // Install Vue Devtools + try { + await installExtension(VUEJS_DEVTOOLS) + } catch (e) { + console.error('Vue Devtools failed to install:', e.toString()) + } + } + createWindow() + // Figure out correct path and arguments to launch python server + let scriptFilename = path.join(app.getAppPath(), '../server', 'odrive_server.py'); + const args = process.argv; + let effectiveCommand = []; + effectiveCommand.push(scriptFilename); + if (app.isPackaged === true) { + for (const arg of args.slice(1)) { + effectiveCommand.push(arg); + } + } + else { + for (const arg of args.slice(2)) { + effectiveCommand.push(arg); + } + } + // launch python server on event from renderer process (gui) and pipe stdout/stderr to it + ipcMain.on('start-server', () => { + server = spawn(getPyCmd(), effectiveCommand); + server.stdout.on('data', function (data) { + try { + console.log(data.toString('utf8')); + } catch (error) { + console.log(error); + } + try { + win.webContents.send('server-stdout', String(data.toString('utf8'))); + } catch (error) { + console.log(error); + } + }); + server.stderr.on('data', function (data) { + console.log(data.toString('utf8')); + try { + win.webContents.send('server-stderr', String(data.toString('utf8'))); + } catch (error) { + console.log(error); + } + }); + }) + ipcMain.on('kill-server', () => { + server.kill('SIGINT'); + }) +}) + +// Exit cleanly on request from parent process in development mode. +if (isDevelopment) { + if (process.platform === 'win32') { + process.on('message', (data) => { + if (data === 'graceful-exit') { + server.kill('SIGINT'); + app.quit() + } + }) + } else { + process.on('SIGTERM', () => { + server.kill('SIGINT'); + app.quit() + }) + } +} diff --git a/GUI/src/comms/socketio.js b/GUI/src/comms/socketio.js new file mode 100644 index 000000000..4bd9ff95d --- /dev/null +++ b/GUI/src/comms/socketio.js @@ -0,0 +1,33 @@ +import io from 'socket.io-client'; + +let socket = undefined; +let url = 'https://0.0.0.0:8080'; + +function initSocket(url) { + socket = io(url); +} + +export function setUrl(path) { + if (socket) { + socket.close(); + socket = undefined; + } + url = path; + initSocket(url); +} + +export function closeSocket() { + socket.close(); + socket = undefined; +} + +export function addEventListener(event) { + if (!socket) { + initSocket(url); + } + socket.on(event.type, event.callback); +} + +export function sendEvent(event) { + socket.emit(event.type, event.data); +} \ No newline at end of file diff --git a/GUI/src/components/Axis.vue b/GUI/src/components/Axis.vue new file mode 100644 index 000000000..ee488c8f8 --- /dev/null +++ b/GUI/src/components/Axis.vue @@ -0,0 +1,202 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/actions/Action.vue b/GUI/src/components/actions/Action.vue new file mode 100644 index 000000000..ebc224297 --- /dev/null +++ b/GUI/src/components/actions/Action.vue @@ -0,0 +1,101 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/actions/ActionEnum.vue b/GUI/src/components/actions/ActionEnum.vue new file mode 100644 index 000000000..96662bdbe --- /dev/null +++ b/GUI/src/components/actions/ActionEnum.vue @@ -0,0 +1,95 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/clearErrors.vue b/GUI/src/components/clearErrors.vue new file mode 100644 index 000000000..35bd01eef --- /dev/null +++ b/GUI/src/components/clearErrors.vue @@ -0,0 +1,38 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/controls/CtrlBoolean.vue b/GUI/src/components/controls/CtrlBoolean.vue new file mode 100644 index 000000000..455d379c4 --- /dev/null +++ b/GUI/src/components/controls/CtrlBoolean.vue @@ -0,0 +1,90 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/controls/CtrlEnum.vue b/GUI/src/components/controls/CtrlEnum.vue new file mode 100644 index 000000000..98decc9b7 --- /dev/null +++ b/GUI/src/components/controls/CtrlEnum.vue @@ -0,0 +1,107 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/controls/CtrlFunction.vue b/GUI/src/components/controls/CtrlFunction.vue new file mode 100644 index 000000000..485b908f2 --- /dev/null +++ b/GUI/src/components/controls/CtrlFunction.vue @@ -0,0 +1,57 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/controls/CtrlNumeric.vue b/GUI/src/components/controls/CtrlNumeric.vue new file mode 100644 index 000000000..fa7f38949 --- /dev/null +++ b/GUI/src/components/controls/CtrlNumeric.vue @@ -0,0 +1,109 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/controls/CtrlSlider.vue b/GUI/src/components/controls/CtrlSlider.vue new file mode 100644 index 000000000..419c6e6c3 --- /dev/null +++ b/GUI/src/components/controls/CtrlSlider.vue @@ -0,0 +1,151 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/plots/LineChart.js b/GUI/src/components/plots/LineChart.js new file mode 100644 index 000000000..96c67459a --- /dev/null +++ b/GUI/src/components/plots/LineChart.js @@ -0,0 +1,14 @@ +import { Line, mixins } from 'vue-chartjs'; +import 'chartjs-plugin-streaming'; +const { reactiveProp } = mixins; + +export default { + extends: Line, + mixins: [reactiveProp], + props: ['options'], + mounted () { + // this.chartData is created in the mixin. + // If you want to pass options please create a local options object + this.renderChart(this.chartData, this.options) + } +} \ No newline at end of file diff --git a/GUI/src/components/plots/Plot.vue b/GUI/src/components/plots/Plot.vue new file mode 100644 index 000000000..8ae4d27ff --- /dev/null +++ b/GUI/src/components/plots/Plot.vue @@ -0,0 +1,167 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/choices/wizardBrake.vue b/GUI/src/components/wizard/choices/wizardBrake.vue new file mode 100644 index 000000000..42a69e736 --- /dev/null +++ b/GUI/src/components/wizard/choices/wizardBrake.vue @@ -0,0 +1,91 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/choices/wizardChoice.vue b/GUI/src/components/wizard/choices/wizardChoice.vue new file mode 100644 index 000000000..b988b8bc7 --- /dev/null +++ b/GUI/src/components/wizard/choices/wizardChoice.vue @@ -0,0 +1,56 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/choices/wizardEncoderIncremental.vue b/GUI/src/components/wizard/choices/wizardEncoderIncremental.vue new file mode 100644 index 000000000..2c296abd4 --- /dev/null +++ b/GUI/src/components/wizard/choices/wizardEncoderIncremental.vue @@ -0,0 +1,100 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/choices/wizardEncoderIncrementalIndex.vue b/GUI/src/components/wizard/choices/wizardEncoderIncrementalIndex.vue new file mode 100644 index 000000000..85357d416 --- /dev/null +++ b/GUI/src/components/wizard/choices/wizardEncoderIncrementalIndex.vue @@ -0,0 +1,101 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/choices/wizardEnd.vue b/GUI/src/components/wizard/choices/wizardEnd.vue new file mode 100644 index 000000000..e26a9756e --- /dev/null +++ b/GUI/src/components/wizard/choices/wizardEnd.vue @@ -0,0 +1,127 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/choices/wizardInputFiltered.vue b/GUI/src/components/wizard/choices/wizardInputFiltered.vue new file mode 100644 index 000000000..6b56f89db --- /dev/null +++ b/GUI/src/components/wizard/choices/wizardInputFiltered.vue @@ -0,0 +1,117 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/choices/wizardInputTrajectory.vue b/GUI/src/components/wizard/choices/wizardInputTrajectory.vue new file mode 100644 index 000000000..7be09d860 --- /dev/null +++ b/GUI/src/components/wizard/choices/wizardInputTrajectory.vue @@ -0,0 +1,160 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/choices/wizardInputVelRamp.vue b/GUI/src/components/wizard/choices/wizardInputVelRamp.vue new file mode 100644 index 000000000..c16512974 --- /dev/null +++ b/GUI/src/components/wizard/choices/wizardInputVelRamp.vue @@ -0,0 +1,117 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/choices/wizardMisc.vue b/GUI/src/components/wizard/choices/wizardMisc.vue new file mode 100644 index 000000000..f5aaa4845 --- /dev/null +++ b/GUI/src/components/wizard/choices/wizardMisc.vue @@ -0,0 +1,166 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/choices/wizardMotor.vue b/GUI/src/components/wizard/choices/wizardMotor.vue new file mode 100644 index 000000000..6fbc71bc8 --- /dev/null +++ b/GUI/src/components/wizard/choices/wizardMotor.vue @@ -0,0 +1,198 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/page_components/wizardCalStatus.vue b/GUI/src/components/wizard/page_components/wizardCalStatus.vue new file mode 100644 index 000000000..bf9e20b06 --- /dev/null +++ b/GUI/src/components/wizard/page_components/wizardCalStatus.vue @@ -0,0 +1,38 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/page_components/wizardEncoderCal.vue b/GUI/src/components/wizard/page_components/wizardEncoderCal.vue new file mode 100644 index 000000000..653fe5aaa --- /dev/null +++ b/GUI/src/components/wizard/page_components/wizardEncoderCal.vue @@ -0,0 +1,35 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/page_components/wizardMotorCal.vue b/GUI/src/components/wizard/page_components/wizardMotorCal.vue new file mode 100644 index 000000000..43961c4d4 --- /dev/null +++ b/GUI/src/components/wizard/page_components/wizardMotorCal.vue @@ -0,0 +1,36 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/components/wizard/wizardPage.vue b/GUI/src/components/wizard/wizardPage.vue new file mode 100644 index 000000000..de9762113 --- /dev/null +++ b/GUI/src/components/wizard/wizardPage.vue @@ -0,0 +1,190 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/lib/odrive_utils.js b/GUI/src/lib/odrive_utils.js new file mode 100644 index 000000000..abec20d7f --- /dev/null +++ b/GUI/src/lib/odrive_utils.js @@ -0,0 +1,242 @@ +import store from "../store.js"; +import * as socketio from "../comms/socketio.js"; +import {wait, waitFor} from "./utils.js"; +import odriveEnums from "../assets/odriveEnums.json" + +// helper functions and utilities for getting ODrive values + +// given a path like "odrive0.axis0.config.blah", return the value +export function getParam(path) { + let keys = path.split('.'); + if (store.state.ODrivesConnected[keys[0]]) { + let odriveObj = store.state.odrives; + for (const key of keys) { + odriveObj = odriveObj[key]; + } + return odriveObj; + } + else { + console.log("getParam for " + path + " is for disconnected ODrive"); + return undefined; + } +} + +// wrapper for val field +export function getVal(path) { + return getParam(path + ".val"); +} + +// wrapper for readonly field +export function getReadonly(path) { + return getParam(path + '.readonly'); +} + +export function fetchParam(path) { + if (store.state.serverConnected){ + socketio.sendEvent({ + type: "getProperty", + data: {path: path}, + }); + } +} + +export function parseMath(inString) { + // given an input string that is valid arithmetic, use eval() to evaluate it + //let allowedChars = "0123456789eE/*-+.()"; + let send = true; + //for (const c of inString) { + // if (!allowedChars.includes(c)) { + // send = false; + // } + //} + if (send) { + return eval(inString); + } + else { + return false; + } +} + +export function putVal(path, value) { + console.log("path: " + path + ", val: " + value + ", type: " + typeof value); + if (store.state.ODrivesConnected[path.split('.')[0]]) { + if (value == Number.POSITIVE_INFINITY) { + value = "Infinity"; + } + else if (value == Number.NEGATIVE_INFINITY) { + value = "-Infinity"; + } + socketio.sendEvent({ + type: "setProperty", + data: {path: path, val: value, type: typeof value} + }); + } + else { + console.log("requesting " + path + " from disconnected odrive") + } +} + +// path is path to function, args is list of parameters +export function callFcn(path, args = []) { + console.log("calling function: " + path); + socketio.sendEvent({ + type: "callFunction", + data: {path: path, args: args} + }); +} + +export function odriveMinorVersion(odrive) { + // given an odrive object, return the minor version + return odrive.fw_version_minor.val; +} + +export function getUnit(odrive, path) { + let unit = undefined; + for ( const key of Object.keys(odriveUnits[odriveMinorVersion(odrive)]) ) { + if (path.includes(key)) { + unit = odriveUnits[odriveMinorVersion(odrive)][key]; + } + } + return unit; +} + +export function clearErrors(odrive, axis) { + // odrive is odrive path like 'odrive0' + // axis is 'axis0', etc + let paths = []; + [".error", ".motor.error", ".encoder.error", ".controller.error"].forEach((path) => { + paths.push(odrive + axis + path); + }); + paths.forEach((path) => { + putVal(path, 0); + }) +} + +export async function motorCalibration(odrive, axis) { + // set up our continuous fetch + let state; + let motorError; + + let updateVals = () => { + fetchParam(odrive + axis + ".current_state"); + fetchParam(odrive + axis + ".motor.config.phase_resistance"); + fetchParam(odrive + axis + ".motor.config.phase_inductance"); + fetchParam(odrive + axis + ".motor.is_calibrated"); + fetchParam(odrive + axis + ".motor.error") + + state = getVal(odrive + axis + ".current_state"); + motorError = getVal(odrive + axis + ".motor.error"); + } + + // set up our state watch function + let end = () => { + // return {done: boolean, data } + if (state != odriveEnums.AXIS_STATE_MOTOR_CALIBRATION) { + return {done: true, data: motorError} + } + else { + return {done: false} + } + } + + // start getting live updates + let cylicUpdate = setInterval(updateVals, 100); + + // send motor calibration command to correct odrive and axis + putVal(odrive + axis + ".requested_state", odriveEnums.AXIS_STATE_MOTOR_CALIBRATION); + + // give it some time to start + await wait(500); + + // only two things can happen now, either we successfully calibrate or we error out for some reason + const result = await waitFor(end); + + // stop our parameter updates + clearInterval(cylicUpdate); + + return result; +} + +export async function encoderCalibration(odrive,axis) { + // set up our continuous fetch + let state; + let encoderError; + + let updateVals = () => { + fetchParam(odrive + axis + ".current_state"); + fetchParam(odrive + axis + ".encoder.is_ready"); + fetchParam(odrive + axis + ".encoder.error") + + state = getVal(odrive + axis + ".current_state"); + encoderError = getVal(odrive + axis + ".encoder.error"); + } + + // set up our state watch function + let end = () => { + // return {done: boolean, data } + if (state != odriveEnums.AXIS_STATE_ENCODER_OFFSET_CALIBRATION) { + return {done: true, data: encoderError} + } + else { + return {done: false} + } + } + + // start getting live updates + let cylicUpdate = setInterval(updateVals, 100); + + // start encoder offset cal + putVal(odrive + axis + ".requested_state", odriveEnums.AXIS_STATE_ENCODER_OFFSET_CALIBRATION); + + // give it some time to start + await wait(500); + + // only two things can happen now, either we successfully calibrate or we error out for some reason + const result = await waitFor(end); + + // stop our parameter updates + clearInterval(cylicUpdate); + + return result; +} + +// standins for wizard and gui unit displays +// TODO - convert between odrive api units and user-defined units like degrees and rpm +export let odriveUnits = { + "4": { + "config.brake_resistance": "Ohms", + "controller.pos_setpoint": "counts", + "controller.vel_setpoint": "counts/s", + "controller.current_setpoint": "Amps", + "controller.config.vel_limit": "counts/s", + "controller.config.vel_ramp_rate": "counts/s^2", + "encoder.pos_estimate": "counts", + "encoder.vel_estimate": "counts/s", + "trap_traj.config.vel_limit": "counts/s", + "trap_traj.config.accel_limit": "counts/s^2", + "trap_traj.config.decel_limit": "counts/s^2", + "motor.config.phase_resistance": "Ohms", + "motor.config.phase_inductance": "Henries", + "motor.config.current_lim": "Amps", + "vbus_voltage": "Volts", + }, + "5": { + "config.brake_resistance": "Ohms", + "controller.input_pos": "turns", + "controller.input_vel": "turns/s", + "controller.torque_setpoint": "Amps", + "controller.input_torque": "N/m", + "controller.config.vel_limit": "turns/s", + "controller.config.vel_ramp_rate": "turns/s^2", + "encoder.pos_estimate": "turns", + "encoder.vel_estimate": "turns/s", + "trap_traj.config.vel_limit": "turns/s", + "trap_traj.config.accel_limit": "turns/s^2", + "trap_traj.config.decel_limit": "turns/s^2", + "motor.config.phase_resistance": "Ohms", + "motor.config.phase_inductance": "Henries", + "motor.config.current_lim": "Amps", + "motor.config.torque_lim": "N/m", + "vbus_voltage": "Volts", + } +} \ No newline at end of file diff --git a/GUI/src/lib/utils.js b/GUI/src/lib/utils.js new file mode 100644 index 000000000..0148d3691 --- /dev/null +++ b/GUI/src/lib/utils.js @@ -0,0 +1,93 @@ +// general utilities for use in the GUI + +// given an object and a test function, return a new object where only +// the values where test(value) == true are kept, as well as the parents +// of those objects. Nested non-matching children are set to {} +// ex: obj = {parent: {child1: {child2: "hello"}, child1a: {child2a: "world"}}}, test = ((o) => o == "hello"), +// filterBy(obj, test) -> {parent: {child1: {child2: "hello"}, child1a: {}}} + +export let filterBy = (obj, test) => { + let retobj = {}; + Object.entries(obj).forEach(([key, val]) => { + if (test(val)) { + retobj[key] = val; + } + else if (typeof val == 'object' && val != null) { + retobj[key] = filterBy(val, test); + } + }) + return retobj; +} + +// given an object and test function, delete values where test(val) == true +// modifies obj in place +export let deleteBy = (obj, test) => { + Object.keys(obj).forEach((key) => { + if (typeof obj[key] == 'object' && obj[key] != null) { + deleteBy(obj[key], test); + } + if (test(obj[key])) { + delete obj[key]; + } + }) +} + +// sets up a timeout to wait for fun() to evaluate to true; +// fun must return {done: boolean, data: } +export let waitFor = (fun) => { + return new Promise(resolve => { + let check = () => { + let res = fun(); + if (res.done) { + resolve(res.data); + } + else { + setTimeout(check,100); + } + } + check(); + }); +} + +// utility function to allow time for things to happen +// async wait +export let wait = (time) => { + return new Promise(resolve => { + setTimeout(() => resolve(), time); + }); +} + +export let pathsFromTree = (tree) => { + let flatpaths = []; + let path = []; + let pathFromTree = (tree) => { + for (const key of Object.keys(tree)) { + path.push(key); + if (tree[key] != null && typeof tree[key] == "object") { + pathFromTree(tree[key]); + } + else { + flatpaths.push(path.join('.')); + } + path.pop(); + } + } + // array path + pathFromTree(tree); + return flatpaths; +} + +export let numberDisplay = (val) => { + // if a number can be represented with 3 decimals, return it in that form + // otherwise, return scientific notation + let retVal = ''; + try { + retVal = parseFloat(val).toFixed(3); + if (retVal == '0.000' && val != 0 || retVal.length > 7) { + retVal = parseFloat(val).toExponential(3); + } + } catch (error) { + console.log(error); + } + return retVal; +} \ No newline at end of file diff --git a/GUI/src/main.js b/GUI/src/main.js new file mode 100644 index 000000000..4c313025d --- /dev/null +++ b/GUI/src/main.js @@ -0,0 +1,14 @@ +import Vue from 'vue' +import App from './App.vue' +import store from './store' +import Tooltip from 'vue-directive-tooltip'; +import 'vue-directive-tooltip/dist/vueDirectiveTooltip.css'; +import 'typeface-roboto/index.css'; + +Vue.config.productionTip = false +Vue.use(Tooltip); + +new Vue({ + store, + render: h => h(App) +}).$mount('#app') diff --git a/GUI/src/preload.js b/GUI/src/preload.js new file mode 100644 index 000000000..c92da27b9 --- /dev/null +++ b/GUI/src/preload.js @@ -0,0 +1,2 @@ +import { ipcRenderer } from 'electron' +window.ipcRenderer = ipcRenderer \ No newline at end of file diff --git a/GUI/src/store.js b/GUI/src/store.js new file mode 100644 index 000000000..158d25b49 --- /dev/null +++ b/GUI/src/store.js @@ -0,0 +1,420 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import TuningDashOld from "./assets/dashboards/Tuning_0_4_12.json"; +import TuningDashNew from "./assets/dashboards/Tuning_0_5_1.json"; +import * as socketio from "./comms/socketio"; +import {filterBy, deleteBy } from "./lib/utils.js"; +//import { v4 as uuidv4 } from "uuid"; + + + + +Vue.use(Vuex); + +export default new Vuex.Store({ + // state is the data for this app + state: { + odrives: Object, + odriveConfigs: Object, + odriveParameters: Object, + odriveFunctions: Object, + axes: Array, + odriveServerAddress: String, + serverConnected: Boolean, + ODrivesConnected: Object, + serverOutput: [], + dashboards: [ + { + name: "Start", + component: "Start", + }, + ], + timeSampleStart: 0, + sampledProperties: [], // make this an object where the full path is a key and the value is the sampled var + propSamples: { time: [] }, // {time: [time values], ...path: [path var values]} + newData: false, + sampling: false, + currentDash: "Start", + firstConn: false, + wizardMotor: "odrive0", + }, + // mutations are functions that change the data + mutations: { + setWizardMotor(state, odrive) { + // odrive is string, "odrive0" or "odriveN" + state.wizardMotor = odrive; + }, + setDash(state, dashName) { + state.currentDash = dashName; + }, + setOdrives(state, odrives) { + state.odrives = odrives; + if (state.firstConn == false) { + // first time we're getting odrive data, add correct config page + let TuningDash; + if (state.odrives.odrive0.fw_version_minor.val == "5") { + TuningDash = TuningDashNew; + } + else if (state.odrives.odrive0.fw_version_minor.val == "4") { + TuningDash = TuningDashOld; + } + else { + TuningDash = TuningDashNew; + } + state.dashboards.push({ + name: "Wizard", + component: "Wizard", + }); + state.dashboards.push(TuningDash); + // plots will have variables associated, add them to sampled variables list + for (const plot of TuningDash.plots) { + for (const plotVar of plot.vars) { + //addsampledprop(path); + let path = plotVar.path; + if (!(path in state.sampledProperties)) { + let newPath = path.split('.'); + newPath.splice(0, 1); + state.sampledProperties.push(newPath.join('.')); + state.propSamples[newPath.join('.')] = []; + } + socketio.sendEvent({ + type: 'sampledVarNames', + data: { + paths: state.sampledProperties + } + }); + } + } + } + state.firstConn = true; + }, + setOdriveConfigs(state, payload) { + state.odriveConfigs['full'] = payload.full; + state.odriveConfigs['functions'] = payload.functions; + state.odriveConfigs['params'] = payload.params; + state.odriveConfigs['writeAble'] = payload.writeAble; + state.odriveConfigs['writeAbleNumeric'] = payload.writeAbleNumeric; + }, + setODrivesStatus(state, obj) { + // obj is {"odriveX": true/false} + for (const odrive of Object.keys(obj)){ + state.ODrivesConnected[odrive] = obj[odrive]; + console.log(state.ODrivesConnected); + } + }, + setAxes(state, axes) { + state.axes = axes; + }, + setServerAddress(state, address) { + state.odriveServerAddress = address; + }, + updateOdriveProp(state, payload) { + // need to use Vue.set!!! + // payload is {path, value} + // + const createNestedObject = (odrive, path) => { + let ref = odrive; + let keys = path.split('.'); + for (const key of keys) { + ref = ref[key]; + } + return ref; + }; + let val = payload.val + if (val == "Infinity") { + val = Number.POSITIVE_INFINITY; + console.log("val is infinity"); + } + else if (val == "-Infinity") { + val = Number.NEGATIVE_INFINITY; + } + Vue.set(createNestedObject(state.odrives, payload.path), "val", val); + + }, + addSampledProperty(state, path) { + if (!(path in state.sampledProperties)) { + let newPath = path.split('.'); + newPath.splice(0, 1); + state.sampledProperties.push(newPath.join('.')); + state.propSamples[newPath.join('.')] = []; + } + socketio.sendEvent({ + type: 'sampledVarNames', + data: { + paths: state.sampledProperties + } + }); + }, + removeSampledProperty(state, path) { + let newPath = path.split('.'); + newPath.splice(0, 1); + const index = state.sampledProperties.indexOf(newPath.join('.')); + if (index > -1) { + state.sampledProperties.splice(index, 1); + } + }, + updateSampledProperty(state, payload) { + // payload is object of paths and values + for (const path of Object.keys(payload)) { + state.propSamples[path].push(payload[path]); + if (state.propSamples[path].length > 250) { + state.propSamples[path].splice(0, 1); // emulate circular buffer + } + } + state.propSamples["time"].push((Date.now() - state.timeSampleStart) / 1000); + if (state.propSamples["time"].length > 250) { + state.propSamples["time"].splice(0, 1); + } + state.newData = true; + }, + logServerMessage(state, payload) { + // payload is string + state.serverOutput.push(payload); + // cut off to 1000 lines + if (state.serverOutput.length > 1000) { + state.serverOutput.splice(0,1); + } + }, + setServerStatus(state, val) { + state.serverConnected = val; + }, + removeCtrlFromDash(state, obj) { + // obj is {dash: dashID, path: control path} + for (const dash of state.dashboards) { + if (obj.dashID == dash.id) { + for (const control of dash.controls) { + if (obj.path == control.path) { + dash.controls.splice(dash.controls.indexOf(control), 1); + break; + } + } + break; + } + } + }, + removeActionFromDash(state, obj) { + // obj is {dashID: dashID, actionID: action ID} + for (const dash of state.dashboards) { + if (obj.dashID == dash.id) { + for (const action of dash.actions) { + if (obj.actionID == action.id) { + dash.actions.splice(dash.actions.indexOf(action), 1); + break; + } + } + break; + } + } + }, + removePlotFromDash(state, obj) { + // obj is {dashID: dash ID, plotID: plot ID} + for (const dash of state.dashboards) { + if (obj.dashID == dash.id) { + for (const plot of dash.plots) { + if (obj.plotID == plot.name) { + dash.plots.splice(dash.plots.indexOf(plot), 1); + break; + } + } + break; + } + } + }, + setActionVal(state, obj) { + // obj is {dashID: dash ID, actionID: action ID, val: val} + for (const dash of state.dashboards) { + if (obj.dashID == dash.id) { + for (const action of dash.actions) { + if (obj.actionID == action.id) { + action.val = obj.val; + break; + } + } + break; + } + } + }, + }, + // actions trigger mutations + actions: { + getOdrives() { + socketio.sendEvent({ + type: 'getODrives', + }); + }, + getOdriveConfigs(context) { + // transform ODrive JSON + function treeParse(odriveObj) { + let retObj = {}; + for (const key of Object.keys(odriveObj)) { + if (typeof odriveObj[key] === 'object' && odriveObj[key] !== null) { + // check if "val" is a valid key + if (Object.prototype.hasOwnProperty.call(odriveObj[key], "val")) { + // parse from string to a type that we care about + switch (odriveObj[key]["type"]) { + case "str": { + // for handling infinity + let val; + if (odriveObj[key]["val"] == 'Infinity') { + val = Number.POSITIVE_INFINITY; + } + else if (odriveObj[key]["val"] == '-Infinity') { + val = Number.NEGATIVE_INFINITY; + } + retObj[key] = val; + break; + } + case "float": + retObj[key] = parseFloat(parseFloat(odriveObj[key]["val"]).toFixed(3)); + break; + case "int8": + case "int16": + case "int32": + case "int64": + case "uint8": + case "uint16": + case "uint32": + case "uint64": + retObj[key] = parseInt(odriveObj[key]["val"]); + break; + case "bool": + retObj[key] = odriveObj[key]["val"] == 'True'; + break; + default: + retObj[key] = odriveObj[key]["val"]; + } + } + else { + retObj[key] = treeParse(odriveObj[key]); + } + } + else if (odriveObj[key] == "function") { + retObj[key] = "function"; + } + else { + retObj[key] = odriveObj[key]; + } + } + return retObj; + } + // full parameter tree + // create object containing only ODrive functions + let fullTree = treeParse(context.state.odrives); + let fcns = filterBy(fullTree, ((o) => o == "function")); + deleteBy(fcns, ((o) => o.constructor == Object && Object.keys(o).length == 0)); + + // create object containing only ODrive parameters + let params = filterBy(fullTree, ((o) => o != "function" && typeof o != "object")); + deleteBy(params, ((o) => o.constructor == Object && Object.keys(o).length == 0)) + + // create object containing only ODrive parameters with write access + let writeAble = filterBy(context.state.odrives, ((o) => o['readonly'] == false)); + deleteBy(writeAble, ((o) => o.constructor == Object && Object.keys(o).length == 0)) + + // create object of only ODrive parameters that are writeable and numeric + let writeAbleNumeric = filterBy(context.state.odrives, ((o) => o['readonly'] == false && ['float', 'int', 'bool'].includes(o['type']))); + deleteBy(writeAbleNumeric, ((o) => o.constructor == Object && Object.keys(o).length == 0)) + + context.commit('setOdriveConfigs', {full: fullTree, + functions: fcns, + params: params, + writeAble: treeParse(writeAble), + writeAbleNumeric: treeParse(writeAbleNumeric)}); + }, + getAxes(context) { + let axes = []; + //for each connected odrive, collect axes and display them + for (const odrive of Object.keys(context.state.odrives)) { + if ('axis0' in context.state.odrives[odrive]) { + axes.push({ + name: `${odrive}.axis0`, + }); + } + if ('axis1' in context.state.odrives[odrive]) { + axes.push({ + name: `${odrive}.axis1`, + }); + } + } + context.commit('setAxes', axes); + }, + setServerAddress(context, address) { + context.commit('setServerAddress', address); + socketio.setUrl(address); + socketio.addEventListener({ + type: "connect", + callback: () => { + context.commit("setServerStatus", true); + console.log('connected to server'); + //context.dispatch("getOdrives"); + socketio.sendEvent({ + type: "findODrives", + }); + } + }); + socketio.addEventListener({ + type: "odrive-found", + callback: () => { + console.log("odrive-found recieved from server"); + context.dispatch("getOdrives"); + } + }) + socketio.addEventListener({ + type: "disconnect", + callback: () => { + context.commit("setServerStatus", false); + console.log('server disconnect'); + socketio.closeSocket(); + context.commit('setAxes', []); + } + }); + socketio.addEventListener({ + type: "sampledData", + callback: message => { + context.commit("updateSampledProperty", JSON.parse(message)); + } + }); + socketio.addEventListener({ + type: "samplingEnabled", + callback: () => { + socketio.sendEvent({ + type: "startSampling" + }); + } + }); + socketio.addEventListener({ + type: "odrives", + callback: odrives => { + context.commit('setOdrives', JSON.parse(odrives)); + context.dispatch('getOdriveConfigs'); + context.dispatch('getAxes'); + } + }); + // getVal event gets sent to server, server emits ODriveProperty event with path and val of property + socketio.addEventListener({ + type: "ODriveProperty", + callback: retmsg => { + // retmsg is {path, val} + context.commit('updateOdriveProp', JSON.parse(retmsg)); + } + }); + socketio.addEventListener({ + type: "odrive-disconnected", + callback: (odrive_name) => { + console.log(odrive_name + " disconnected"); + //console.log("restarting server..."); + //window.ipcRenderer.send('kill-server'); + //window.ipcRenderer.send('start-server'); + //context.dispatch('setServerAddress', context.state.odriveServerAddress); + } + }); + socketio.addEventListener({ + type: "odrives-status", + callback: (odrives_status) => { + console.log("From odrives-status msg " + odrives_status); + context.commit('setODrivesStatus', JSON.parse(odrives_status)); + } + }); + } + } +}) \ No newline at end of file diff --git a/GUI/src/views/Dashboard.vue b/GUI/src/views/Dashboard.vue new file mode 100644 index 000000000..2964d79d1 --- /dev/null +++ b/GUI/src/views/Dashboard.vue @@ -0,0 +1,557 @@ + + + + + \ No newline at end of file diff --git a/GUI/src/views/Start.vue b/GUI/src/views/Start.vue new file mode 100644 index 000000000..6134d797b --- /dev/null +++ b/GUI/src/views/Start.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/GUI/src/views/Wizard.vue b/GUI/src/views/Wizard.vue new file mode 100644 index 000000000..ee5b2905f --- /dev/null +++ b/GUI/src/views/Wizard.vue @@ -0,0 +1,395 @@ + + + + + \ No newline at end of file diff --git a/GUI/vue.config.js b/GUI/vue.config.js new file mode 100644 index 000000000..b0d916838 --- /dev/null +++ b/GUI/vue.config.js @@ -0,0 +1,31 @@ +// this file is used for configuring electron-builder + +module.exports = { + pluginOptions: { + electronBuilder: { + preload: 'src/preload.js', + //nodeIntegration: true, + builderOptions: { + "productName": "ODriveGUI", + "asar": false, + "extraResources": "server", + "artifactName": "${name}.${ext}", + "win" : { + "target" : [ + { + "target": "portable", + } + ] + }, + "linux" : { + "target" : [ + { + "target": "AppImage", + } + ] + } + }, + //mainProcessArgs: ['C:/Users/pajoh/Desktop/ODrive_work/ODrive/tools', 'C:/Users/pajoh/Desktop/ODrive_work/ODrive/Firmware'] + } + } +} \ No newline at end of file diff --git a/ODrive_Workspace.code-workspace b/ODrive_Workspace.code-workspace index 8c1fe5c3c..34f5b335c 100644 --- a/ODrive_Workspace.code-workspace +++ b/ODrive_Workspace.code-workspace @@ -62,7 +62,46 @@ "iomanip": "cpp", "optional": "cpp", "sstream": "cpp", - "utils.h": "c" + "utils.h": "c", + "atomic": "cpp", + "hash_map": "cpp", + "strstream": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cinttypes": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "forward_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "unordered_set": "cpp", + "iterator": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "random": "cpp", + "ratio": "cpp", + "string": "cpp", + "hash_set": "cpp", + "slist": "cpp", + "mutex": "cpp", + "ranges": "cpp", + "shared_mutex": "cpp", + "stop_token": "cpp", + "thread": "cpp", + "cfenv": "cpp", + "typeindex": "cpp", + "valarray": "cpp", + "variant": "cpp", + "types": "cpp", + "config": "cpp", + "containers": "cpp", + "readerwriter": "cpp", + "charconv": "cpp", + "image": "cpp", + "imageutils": "cpp" } } } diff --git a/analysis/Simulation/MotorSim.py b/analysis/Simulation/MotorSim.py new file mode 100644 index 000000000..6d457db10 --- /dev/null +++ b/analysis/Simulation/MotorSim.py @@ -0,0 +1,225 @@ +# this file is for the simulation of a 3-phase synchronous motor + +import numpy as np +import scipy as sp +import scipy.signal as signal +import scipy.integrate +import matplotlib.pyplot as plt +import time + +def sign(num): + if num > 0: + return 1 + elif num < 0: + return -1 + else: + return 0 + +C = np.array([0, 1/5, 3/10, 4/5, 8/9, 1]) +A = np.array([ + [0, 0, 0, 0, 0], + [1/5, 0, 0, 0, 0], + [3/40, 9/40, 0, 0, 0], + [44/45, -56/15, 32/9, 0, 0], + [19372/6561, -25360/2187, 64448/6561, -212/729, 0], + [9017/3168, -355/33, 46732/5247, 49/176, -5103/18656] +]) +B = np.array([35/384, 0, 500/1113, 125/192, -2187/6784, 11/84]) + +# rk_step from scipy.integrate rk.py +def rk_step(fun, t, y, f, h, A, B, C, K): + """Perform a single Runge-Kutta step. + This function computes a prediction of an explicit Runge-Kutta method and + also estimates the error of a less accurate method. + Notation for Butcher tableau is as in [1]_. + Parameters + ---------- + fun : callable + Right-hand side of the system. + t : float + Current time. + y : ndarray, shape (n,) + Current state. + f : ndarray, shape (n,) + Current value of the derivative, i.e., ``fun(x, y)``. + h : float + Step to use. + A : ndarray, shape (n_stages, n_stages) + Coefficients for combining previous RK stages to compute the next + stage. For explicit methods the coefficients at and above the main + diagonal are zeros. + B : ndarray, shape (n_stages,) + Coefficients for combining RK stages for computing the final + prediction. + C : ndarray, shape (n_stages,) + Coefficients for incrementing time for consecutive RK stages. + The value for the first stage is always zero. + K : ndarray, shape (n_stages + 1, n) + Storage array for putting RK stages here. Stages are stored in rows. + The last row is a linear combination of the previous rows with + coefficients + Returns + ------- + y_new : ndarray, shape (n,) + Solution at t + h computed with a higher accuracy. + f_new : ndarray, shape (n,) + Derivative ``fun(t + h, y_new)``. + References + ---------- + .. [1] E. Hairer, S. P. Norsett G. Wanner, "Solving Ordinary Differential + Equations I: Nonstiff Problems", Sec. II.4. + """ + K[0] = f + for s, (a, c) in enumerate(zip(A[1:], C[1:]), start=1): + dy = np.dot(K[:s].T, a[:s]) * h + K[s] = fun(t + c * h, y + dy) + + y_new = y + h * np.dot(K[:-1].T, B) + f_new = fun(t + h, y_new) + + K[-1] = f_new + + return y_new, f_new + + +# example params for d5065 motor +# phase_R = 0.039 Ohms +# phase_L = 0.0000157 H +# pole_pairs = 7 +# KV = 270 +# J = 1e-4 +# b_coulomb = 0.001 +# b_viscous = 0.001 + +class motor_pmsm_mechanical: + def __init__(self, J, b_coulomb, b_viscous): + # J is moment of inertia + # b_coulomb is coulomb friction coefficient + # b_viscous is viscous friction coefficient + self.J = J + self.b_c = b_coulomb + self.b_v = b_viscous + + def diff_eqs(self, t, y, torque): + theta = y[0] + theta_dot = y[1] + + theta_ddot = (1/self.J) * (torque - self.b_v * theta_dot - self.b_c * sign(theta_dot)) + + return np.array([theta_dot, theta_ddot]) + +def inverter(vbus, timings, current): + # this function should take the relevant inputs and output voltages in dq reference frame. + pass + +class motor: + def __init__(self, J, b_coulomb, b_viscous, R, L_q, L_d, KV, pole_pairs, dT): + self.dT = dT + self.b_coulomb = b_coulomb + self.b_viscous = b_viscous + self.KV = KV + self.pole_pairs = pole_pairs + kt = 8.27/KV + self.lambda_m = 2*kt/(3*pole_pairs) #speed constant in Vs/rad (electrical rad) + self.R = R + self.L_q = L_q + self.L_d = L_d + self.J = J + + # state variables for motor + self.theta = 0 # mechanical! + self.theta_dot = 0 # mechanical! + self.I_d = 0 + self.I_q = 0 + + # K matrix. For integrator? + # np.empty((self.n_stages + 1, self.n_stages), dtype=self.y.dtype) + self.K = np.empty((7, 4)) + + def simulate(self, t, u, x0): + # t is timesteps [t0, t1, ...] + # u is [T_load, V_d, V_q] + # x0 is initial states, [theta, theta_dot, I_d, I_q] + (self.theta, self.theta_dot, self.I_d, self.I_q) = x0 + time = [] + pos = [] + vel = [] + I_d = [] + I_q = [] + for i in range(len(t)): + self.single_step_rk(u[2],u[1],u[0]) + time.append(i*self.dT) + pos.append(self.theta) + vel.append(self.theta_dot) + I_d.append(self.I_d) + I_q.append(self.I_q) + + return [time,pos,vel,I_d,I_q] + + def inputs(self, V_q, V_d, T_load): + self.V_q = V_q + self.V_d = V_d + self.T_load = T_load + + def diff_eqs(self, t, y): + # inputs are self.V_q, self.V_d, self.T_load + # state is y, y = [theta, theta_dot, I_d, I_q] + # set_inputs must be called before this if the inputs have changed. + theta = y[0] + theta_dot = y[1] + I_d = y[2] + I_q = y[3] + + torque = 3*self.pole_pairs/2 * (self.lambda_m * I_q + (self.L_d - self.L_q)*I_d*I_q) - self.T_load + + if theta_dot == 0 and -1*self.b_coulomb < torque < self.b_coulomb: + torque = 0 + + # theta_dot = theta_dot, no ode here + theta_ddot = (1/self.J) * (torque - self.b_viscous * theta_dot - self.b_coulomb * sign(theta_dot)) + I_d_dot = self.V_d / self.L_d - self.R / self.L_d * I_d + theta_dot*self.pole_pairs * self.L_q / self.L_d * I_q + I_q_dot = self.V_q / self.L_q - self.R / self.L_q * I_q - theta_dot*self.pole_pairs * self.L_d / self.L_q * I_d - theta_dot*self.pole_pairs * self.lambda_m / self.L_q + + return np.array([theta_dot, theta_ddot, I_d_dot, I_q_dot]) + + def single_step_rk(self, V_q, V_d, T_load): + # given inputs + self.inputs(V_q, V_d, T_load) + x = (d5065.theta, d5065.theta_dot, d5065.I_d, d5065.I_q) + ((d5065.theta, d5065.theta_dot, d5065.I_d, d5065.I_q), _) = rk_step(d5065.diff_eqs, 0, x, d5065.diff_eqs(0, x), d5065.dT, A, B, C, d5065.K) + +if __name__ == "__main__": + d5065 = motor(J = 1e-4, b_coulomb = 0, b_viscous = 0.01, R = 0.039, L_q = 1.57e-5, L_d = 1.57e-5, KV = 270, pole_pairs = 7, dT = 1/48000) + x0 = [0,0,0,0] # initial state of theta, theta_dot, I_d, I_q + u = [0,0,1] # input for simulation as [T_load, V_d, V_q] + t = [i*1/48000 for i in range(12000)] # half second of runtime at Fs=48kHz + + data = d5065.simulate(t=t, u=u, x0=x0) + dT = 1/48000 + states = [] + pos = [] + vel = [] + I_d = [] + I_q = [] + pos = data[1] + vel = data[2] + I_d = data[3] + I_q = data[4] + + fig, axs = plt.subplots(4) + + axs[0].plot(t, pos) + axs[0].set_title('pos') + axs[0].set_ylabel('Theta (eRad)') + axs[1].plot(t, vel) + axs[1].set_title('vel') + axs[1].set_ylabel('Omega (eRad/s)') + axs[2].plot(t,I_d) + axs[2].set_title('I_d') + axs[2].set_ylabel('Current (A)') + axs[3].plot(t,I_q) + axs[3].set_title('I_q') + axs[3].set_ylabel('Current (A)') + axs[3].set_xlabel('time (s)') + + plt.show() \ No newline at end of file diff --git a/analysis/Simulation/TranslationalMass.py b/analysis/Simulation/TranslationalMass.py index 3cb1a4aa2..db7fbd64c 100644 --- a/analysis/Simulation/TranslationalMass.py +++ b/analysis/Simulation/TranslationalMass.py @@ -1,6 +1,8 @@ import os import matplotlib.pyplot as plt from control.matlab import * +import numpy as np + # Input: Current (A) # Output: Torque (Nm) @@ -26,10 +28,35 @@ def mass(m, b, k): def pulley(r): return tf(r, 1) -sys = series(motor(2.5), pulley(0.015), mass(0.10, 0, 0)) +# Make s a transfer function s/1 +s = tf('s') +print(s) + +# build a new transfer function using our variable s as a handy placeholder +sys = 1 / (s*s + s + 1) +print(sys) + +# Hit the system with a step command +yout, T = step(sys) +plt.plot(T, yout) + +# convert our continuous time model to discrete time via Tustin at 0.01s timestep +sysd = c2d(tf(sys), 0.01, method='tustin') +print(sysd) + +# Hit the discrete system with a step command, and sample it at 0.01 timestep from 0 to 14 seconds +yout, T = step(sysd, np.arange(0, 14, 0.01)) +plt.plot(T, yout) +plt.legend(['Continuous', 'Discrete']) + +# Build a system based on the series connection of the motor, pulley, and mass "blocks" +sys = series(motor(2.5), pulley(0.015), mass(0.10, .1, .1)) +print(tf(sys)) + +# Step our series system, returning y (outputs) and x (states) yout, T, xout = step(sys, return_x=True) -print(yout) -# plt.plot(T, yout) +plt.figure() +plt.plot(T, yout) plt.plot(T, xout) -plt.legend(['Displacement', 'Velocity']) +plt.legend(['Displacement', r'$x$', r'$\dot{x}$']) plt.show() \ No newline at end of file diff --git a/analysis/motor_analysis/ac_induction_motor.py b/analysis/motor_analysis/ac_induction_motor.py index ccc06ec8c..b8820850d 100644 --- a/analysis/motor_analysis/ac_induction_motor.py +++ b/analysis/motor_analysis/ac_induction_motor.py @@ -7,6 +7,7 @@ filename = "oscilloscope.csv" +USE_TEST_DATA = False PLOT_INITAL = True DO_FITTING = False PLOT_PROGRESS = False @@ -109,6 +110,7 @@ def run(self, time_series, voltage, omega_stator, omega_rotor): def print_parameter_info(self): print() print('Given parameters:') + print('pole_pairs = {}'.format(EngNumber(pole_pairs))) print('rotor_resistance = {}ohm'.format(EngNumber(assumed_rotor_resistance))) print() @@ -164,8 +166,10 @@ def plot_data(t, y, ref, title): # load test data t = np.arange(4096)/8000.0 voltage_step = 1.0 -with open(filename, 'r') as fp: - test_response = np.array([float(x) for x in fp.readlines()]) +if USE_TEST_DATA: + with open(filename, 'r') as fp: + test_response = np.array([float(x) for x in fp.readlines()]) +else: test_response = None inital_parameters = np.zeros(len(ACMotor.parameter_definitions)) diff --git a/analysis/thermistors.py b/analysis/thermistors.py index 5d6a30e20..e0fc72be5 100644 --- a/analysis/thermistors.py +++ b/analysis/thermistors.py @@ -1,7 +1,7 @@ #%% from odrive.utils import calculate_thermistor_coeffs -Rload = 3300 +Rload = 3300 # 2000 for ODrive v4 R_25 = 10000 Beta = 3434 Tmin = 0 diff --git a/dockerbuild.sh b/dockerbuild.sh index 779b9bf13..acf77e9d9 100755 --- a/dockerbuild.sh +++ b/dockerbuild.sh @@ -1,6 +1,6 @@ function cleanup { echo "Removing previous build artifacts" - rm -rf build + rm -rf build/ Firmware/autogen Firmware/build Firmware/.tup docker rm odrive-build-cont } @@ -13,14 +13,11 @@ function gc { function build { cleanup - echo "Building the firmware" + echo "Building the build-environment image" docker build -t odrive-build-img . - echo "Create container" - docker create --name odrive-build-cont odrive-build-img:latest - - echo "Extract build artifacts" - docker cp odrive-build-cont:ODrive/Firmware/build . + echo "Build in container" + docker run -v $(pwd):/ODrive --name odrive-build-cont odrive-build-img:latest } function usage { diff --git a/docs/_data/index.yaml b/docs/_data/index.yaml index 1fca464e6..f0bee327f 100644 --- a/docs/_data/index.yaml +++ b/docs/_data/index.yaml @@ -45,6 +45,8 @@ sections: url: /step-direction - title: RC PWM url: /rc-pwm + - title: Analog Input + url: /analog-input - title: Homing & Endstops url: /endstops - title: Thermistors diff --git a/docs/analog-input.md b/docs/analog-input.md index e69de29bb..2b0186d67 100644 --- a/docs/analog-input.md +++ b/docs/analog-input.md @@ -0,0 +1,5 @@ +# Analog Input + +Analog inputs can be used to measure voltages between 0 and 3.3V. ODrive uses a 12 bit ADC (4096 steps) and so has a maximum resolution of 0.8 mV. A GPIO must be configured with `.config.gpioX_mode = GPIO_MODE_ANALOG_IN` before it can be used as an analog input. To read the voltage on GPIO1 in odrivetool the following would be entered: `odrv0.get_adc_voltage(1)`. + +Similar to RC PWM input, analog inputs can also be used to feed any of the numerical properties that are visible in `odrivetool`. This is done by configuring `odrv0.config.gpio3_analog_mapping` and `odrv0.config.gpio4_analog_mapping`. Refer to [RC PWM](rc-pwm) for instructions on how to configure the mappings. diff --git a/docs/anticogging.md b/docs/anticogging.md new file mode 100644 index 000000000..91b494b54 --- /dev/null +++ b/docs/anticogging.md @@ -0,0 +1,53 @@ +# Anti-cogging + +ODrive supports an anti-cogging algorithm that attempts to compensate for the rather high cogging torques seen in hobby motors. + +`..controller.config.anticogging` is a configuration structure that contains the following items: + +Name | Type | Use +-- | -- | -- +index | uint32 | The current position being used for calibration +pre_calibrated | bool | If true and using index or absolute encoder, load anticogging map from NVM at startup +calib_anticogging | bool | True when calibration is ongoing +calib_pos_threshold | float32 | (pos_estimate - index) must be < this value to calibrate. Larger values speed up calibration but hurt accuracy +calib_vel_threshold | float32 | (vel_estimate) must be < this value to calibrate. Larger values speed up calibration but hurt accuracy. +cogging_ratio | float32 | Deprecated +anticogging_enabled | bool | Enable or disable anticogging. A valid anticogging map can be ignored by setting this to `false` + +## Calibration + +To calibrate anticogging, first make sure you can adequately control the motor in . It should respond to position commands. + +Start by putting the axis in `AXIS_STATE_CLOSED_LOOP` with `CONTROL_MODE_POSITION_CONTROL` and `INPUT_MODE_PASSTHROUGH`. Make sure you have good control of the motor in this state (it responds to position commands). Now, tune the motor to be very stiff - high `pos_gain` and relatively high `vel_integrator_gain`. This will help in calibration. + +Run `controller.start_anticogging_calibration()`. The motor will start turning slowly, calibrating each point. If you like, you can start a liveplotter session before running this command so that you can watch the position move. + +Once it's complete (it should take about 1 minute), the motor will return to 0 and the value `controller.anticogging_valid` should report True. If `controller.config.anticogging.anticogging_enabled` == True, anticogging will now be running on this axis. + +## Saving to NVM + +As of v0.5.1, the anticogging map is saved to NVM after calibrating and calling `odrv0.save_configuration()` + +The anticogging map can be reloaded automatically at startup by setting `controller.config.anticogging.pre_calibrated = True` and saving the configuration. However, this map is only valid and will only be loaded for absolute encoders, or encoders with index pins after the index search. + +## Example + +``` Py +odrv0.axis0.encoder.config.use_index = True +odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE +odrv0.axis0.encoder.config.pre_calibrated = True +odrv0.axis0.motor.config.pre_calibrated = True + +odrv0.axis0.controller.config.control_mode = CONTROL_MODE_POSITION_CONTROL +odrv0.axis0.controller.config.input_mode = INPUT_MODE_PASSTHROUGH +odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL + +odrv0.axis0.controller.start_anticogging_calibration() + +# Wait until controller.config.anticogging.calib_anticogging == False + +odrv0.axis0.controller.config.anticogging.pre_calibrated = True + +odrv0.save_configuration() +odrv0.reboot() +``` diff --git a/docs/ascii-protocol.md b/docs/ascii-protocol.md index 512bcdf75..0b483f730 100644 --- a/docs/ascii-protocol.md +++ b/docs/ascii-protocol.md @@ -4,7 +4,7 @@ ## How to send commands * **Via USB:** - * **Windows:** Use the Zadig utility to set the ODrive's driver to "usbser". Windows will then make the device available as COM port. You can use [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/) to manually send commands or open the COM port using your favorite programming language + * **Windows:** Use [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/) to manually send commands or open the COM port using your favorite programming language * **Linux/macOS:** Run `/dev/tty*` to list all serial ports. The ODrive will show up as `/dev/ttyACM0` (or similar) on Linux and `/dev/tty.usbmodem[...]` on macOS. Once you know the name, you can use `screen /dev/ttyACM0` (with the correct name) to send commands manually or open the device using your favorite programming language. Serial ports on Unix can be opened, written to and read from like a normal file. * **Via UART:** Connect the ODrive's TX (GPIO1) to your host's RX. Connect your ODrive's RX (GPIO2) to your host's TX. See [UART](uart) for more info. * **Arduino:** You can use the [ODrive Arduino library](https://github.com/madcowswe/ODrive/tree/master/Arduino/ODriveArduino) to talk to the ODrive. @@ -142,3 +142,4 @@ Not all parameters can be accessed via the ASCII protocol but at least all param * `ss` - Save config * `se` - Erase config * `sr` - Reboot +* `sc` - Clear errors diff --git a/docs/can-protocol.md b/docs/can-protocol.md index e193b0c4b..00a6a5536 100644 --- a/docs/can-protocol.md +++ b/docs/can-protocol.md @@ -33,34 +33,36 @@ Be careful that you don't assign too many nodeIDs per PDO group. Four CAN Simpl ### Messages -CMD ID | Name | Signals | Start byte | Signal Type | Bits | Factor | Offset | Byte Order ---: | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- -0x000 | CANOpen NMT Message\*\* | - | - | - | - | - | - | - -0x001 | ODrive Heartbeat Message | Axis Error
Axis Current State | 0
4 | Unsigned Int
Unsigned Int | 32
32 | 1
1 | 0
0 | Intel
Intel -0x002 | ODrive Estop Message | - | - | - | - | - | - | - -0x003 | Get Motor Error\* | Motor Error | 0 | Unsigned Int | 32 | 1 | 0 | Intel -0x004 | Get Encoder Error\* | Encoder Error | 0 | Unsigned Int | 32 | 1 | 0 | Intel -0x005 | Get Sensorless Error\* | Sensorless Error | 0 | Unsigned Int | 32 | 1 | 0 | Intel -0x006 | Set Axis Node ID | Axis CAN Node ID | 0 | Unsigned Int | 32 | 1 | 0 | Intel -0x007 | Set Axis Requested State | Axis Requested State | 0 | Unsigned Int | 32 | 1 | 0 | Intel -0x008 | Set Axis Startup Config | - Not yet implemented - | - | - | - | - | - | - -0x009 | Get Encoder Estimates\* | Encoder Pos Estimate
Encoder Vel Estimate | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
32 | 1
1 | 0
0 | Intel
Intel -0x00A | Get Encoder Count\* | Encoder Shadow Count
Encoder Count in CPR | 0
4 | Signed Int
Signed Int | 32
32 | 1
1 | 0
0 | Intel
Intel -0x00B | Set Controller Modes | Control Mode
Input Mode | 0
4 | Signed Int
Signed Int | 32
32 | 1
1 | 0
0 | Intel
Intel -0x00C | Set Input Pos | Input Pos
Vel FF
Torque FF | 0
4
6 | IEEE 754 Float
Signed Int
Signed Int | 32
16
16 | 1
0.001
0.001 | 0
0
0 | Intel
Intel
Intel -0x00D | Set Input Vel | Input Vel
Torque FF | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
32 | 1
1 | 0
0 | Intel
Intel -0x00E | Set Input Torque | Input Torque | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel -0x00F | Set Velocity Limit | Velocity Limit | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel -0x010 | Start Anticogging | - | - | - | - | - | - | - -0x011 | Set Traj Vel Limit | Traj Vel Limit | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel -0x012 | Set Traj Accel Limits | Traj Accel Limit
Traj Decel Limit | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
32 | 1
1 | 0
0 | Intel
Intel -0x013 | Set Traj Inertia | Traj Inertia | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel -0x014 | Get IQ\* | Iq Setpoint
Iq Measured | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
32 | 1
1 | 0
0 | Intel
Intel -0x015 | Get Sensorless Estimates\* | Sensorless Pos Estimate
Sensorless Vel Estimate | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
32 | 1
1 | 0
0 | Intel
Intel -0x016 | Reboot ODrive\*\*\* | - | - | - | - | - | - | - -0x017 | Get Vbus Voltage\*\*\* | Vbus Voltage | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel -0x018 | Clear Errors | - | - | - | - | - | - | - -0x700 | CANOpen Heartbeat Message\*\* | - | - | - | - | - | - | - +CMD ID | Name | Sender | Signals | Start byte | Signal Type | Bits | Factor | Offset | Byte Order +--: | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- +0x000 | CANOpen NMT Message\*\* | Master | - | - | - | - | - | - | - +0x001 | ODrive Heartbeat Message | Axis | Axis Error
Axis Current State | 0
4 | Unsigned Int
Unsigned Int | 32
32 | 1
1 | 0
0 | Intel
Intel +0x002 | ODrive Estop Message | Master | - | - | - | - | - | - | - +0x003 | Get Motor Error\* | Axis | Motor Error | 0 | Unsigned Int | 64 | 1 | 0 | Intel +0x004 | Get Encoder Error\* | Axis | Encoder Error | 0 | Unsigned Int | 32 | 1 | 0 | Intel +0x005 | Get Sensorless Error\* | Axis | Sensorless Error | 0 | Unsigned Int | 32 | 1 | 0 | Intel +0x006 | Set Axis Node ID | Master | Axis CAN Node ID | 0 | Unsigned Int | 32 | 1 | 0 | Intel +0x007 | Set Axis Requested State | Master | Axis Requested State | 0 | Unsigned Int | 32 | 1 | 0 | Intel +0x008 | Set Axis Startup Config | Master | - Not yet implemented - | - | - | - | - | - | - +0x009 | Get Encoder Estimates\* | Master | Encoder Pos Estimate
Encoder Vel Estimate | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
32 | 1
1 | 0
0 | Intel
Intel +0x00A | Get Encoder Count\* | Master | Encoder Shadow Count
Encoder Count in CPR | 0
4 | Signed Int
Signed Int | 32
32 | 1
1 | 0
0 | Intel
Intel +0x00B | Set Controller Modes | Master | Control Mode
Input Mode | 0
4 | Signed Int
Signed Int | 32
32 | 1
1 | 0
0 | Intel
Intel +0x00C | Set Input Pos | Master | Input Pos
Vel FF
Torque FF | 0
4
6 | IEEE 754 Float
Signed Int
Signed Int | 32
16
16 | 1
0.001
0.001 | 0
0
0 | Intel
Intel
Intel +0x00D | Set Input Vel | Master | Input Vel
Torque FF | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
32 | 1
1 | 0
0 | Intel
Intel +0x00E | Set Input Torque | Master | Input Torque | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel +0x00F | Set Limits | Master | Velocity Limit
Current Limit | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
| 1
1 | 0
0 | Intel +0x010 | Start Anticogging | Master | - | - | - | - | - | - | - +0x011 | Set Traj Vel Limit | Master | Traj Vel Limit | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel +0x012 | Set Traj Accel Limits | Master | Traj Accel Limit
Traj Decel Limit | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
32 | 1
1 | 0
0 | Intel
Intel +0x013 | Set Traj Inertia | Master | Traj Inertia | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel +0x014 | Get IQ\* | Axis | Iq Setpoint
Iq Measured | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
32 | 1
1 | 0
0 | Intel
Intel +0x015 | Get Sensorless Estimates\* | Master | Sensorless Pos Estimate
Sensorless Vel Estimate | 0
4 | IEEE 754 Float
IEEE 754 Float | 32
32 | 1
1 | 0
0 | Intel
Intel +0x016 | Reboot ODrive | Master\*\*\* | - | - | - | - | - | - | - +0x017 | Get Vbus Voltage | Master\*\*\* | Vbus Voltage | 0 | IEEE 754 Float | 32 | 1 | 0 | Intel +0x018 | Clear Errors | Master | - | - | - | - | - | - | - +0x019 | Set Linear Count | Master | Position | 0 | Signed Int | 32 | 1 | 0 | Intel +0x700 | CANOpen Heartbeat Message\*\* | Slave | - | - | - | - | - | - | - +-|-|-|----------------------------------|-|--------------------|-|-|-|_ \* Note: These messages are call & response. The Master node sends a message with the RTR bit set, and the axis responds with the same ID and specified payload. \*\* Note: These CANOpen messages are reserved to avoid bus collisions with CANOpen devices. They are not used by CAN Simple. @@ -70,7 +72,7 @@ CMD ID | Name | Signals | Start byte | Signal Type | Bits | Factor | Offset | By ## Configuring ODrive for CAN Configuration of the CAN parameters should be done via USB before putting the device on the bus. -To set the desired baud rate, use `.can.set_baud_rate()`. The baud rate can be done without rebooting the device. If you'd like to keep the baud rate, simply call `.save_configuration()` before rebooting. +To set the desired baud rate, use `.can.config.baud_rate = `. Each axis looks like a separate node on the bus. Thus, they both have the two properties `can_node_id` and `can_node_id_extended`. The node ID can be from 0 to 63 (0x3F) inclusive, or, if extended CAN IDs are used, from 0 to 16777215 (0xFFFFFF). If you want to connect more than one ODrive on a CAN bus, you must set different node IDs for the second ODrive or they will conflict and crash the bus. @@ -79,7 +81,7 @@ Each axis looks like a separate node on the bus. Thus, they both have the two pr ``` odrv0.axis0.config.can_node_id = 3 odrv0.axis1.config.can_node_id = 1 -odrv0.can.set_baud_rate(500000) +odrv0.can.config.baud_rate = 500000 odrv0.save_configuration() odrv0.reboot() ``` diff --git a/docs/commands.md b/docs/commands.md index b5ca64d24..8f4ab9e22 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -31,7 +31,6 @@ The ODrive will sequence all enabled startup actions selected in the order shown * `.config.startup_encoder_index_search` * `.config.startup_encoder_offset_calibration` * `.config.startup_closed_loop_control` -* `.config.startup_sensorless_control` See [here](api/odrive.axis.axisstate) for a description of each state. @@ -81,22 +80,26 @@ All variables that are part of a `[...].config` object can be saved to non-volat * `.hw_version_major`, `.hw_version_minor`, `.hw_version_revision`: The hardware version of your ODrive. ## Setting up sensorless -The ODrive can run without encoder/hall feedback, but there is a minimum speed, usually around a few hunderd RPM. +The ODrive can run without encoder/hall feedback, but there is a minimum speed, usually around a few hundred RPM. In other words, sensorless mode does not support stopping or changing direction! + +Sensorless mode starts by ramping up the motor speed in open loop control and then switches to closed loop control automatically. The sensorless speed ramping parameters are in `axis.config.sensorless_ramp` The `vel` and `accel` (in [radians/s] and [radians/s^2]) control the speed that the ramp tries to reach and how quickly it gets there. When the ramp reaches `sensorless_ramp.vel`, `controller.input_vel` is automatically set to the same velocity, in [turns/s], and the state switches to closed loop control. + +If your motor comes to a stop after the ramp, try incrementally raising the `vel` parameter. The goal is to be above the minimum speed necessary for sensorless position and speed feedback to converge - this is not well-parameterized per motor. The parameters suggested below work for the D5065 motor, with 270KV and 7 pole pairs. If your motor grinds and skips during the ramp, lower the `accel` parameter until it is tolerable. + +Below are some suggested starting parameters that you can use for the ODrive D5065 motor. Note that you _must_ set the `pm_flux_linkage` correctly for sensorless mode to work. Motor calibration and setup must also be completed before sensorless mode will work. -Below are some suggested starting parameters that you can use. Note that you _must_ set the `pm_flux_linkage` correctly for sensorless mode to work. Motor calibration and setup must also be completed before sensorless mode will work. ``` odrv0.axis0.controller.config.vel_gain = 0.01 odrv0.axis0.controller.config.vel_integrator_gain = 0.05 -odrv0.axis0.controller.config.control_mode = 2 -odrv0.axis0.controller.input_vel = 10 -odrv0.axis0.controller.config.vel_limit = +odrv0.axis0.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL +odrv0.axis0.controller.config.vel_limit = )> odrv0.axis0.motor.config.current_lim = 2 * odrv0.axis0.config.sensorless_ramp.current -odrv0.axis0.motor.config.direction = 1 odrv0.axis0.sensorless_estimator.config.pm_flux_linkage = 5.51328895422 / ( * ) +odrv0.axis0.config.enable_sensorless_mode = True ``` To start the motor: ``` -.requested_state = AXIS_STATE_SENSORLESS_CONTROL +.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL ``` diff --git a/docs/developer-guide.md b/docs/developer-guide.md index fdeaf78db..62b542a09 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -56,6 +56,7 @@ sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa sudo apt-get update sudo apt-get install gcc-arm-embedded sudo apt-get install openocd +sudo apt-get install git-lfs sudo add-apt-repository ppa:jonathonf/tup && sudo apt-get update && sudo apt-get install tup sudo apt-get install python3 python3-yaml python3-jinja2 python3-jsonschema ``` @@ -64,6 +65,7 @@ sudo apt-get install python3 python3-yaml python3-jinja2 python3-jsonschema ```bash sudo apt install gcc-arm-embedded sudo apt install openocd +sudo apt install git-lfs sudo apt install tup sudo apt install python3 python3-yaml python3-jinja2 python3-jsonschema ``` @@ -72,6 +74,7 @@ sudo apt install python3 python3-yaml python3-jinja2 python3-jsonschema ```bash sudo pacman -S arm-none-eabi-gcc arm-none-eabi-binutils sudo pacman -S arm-none-eabi-gdb +sudo pacman -S git-lfs sudo pacman -S tup sudo pacman -S python python-yaml python-jinja python-jsonschema ``` @@ -81,8 +84,9 @@ sudo pacman -S python python-yaml python-jinja python-jsonschema First install [Homebrew](https://brew.sh/). Then you can run these commands in Terminal: ```bash brew install armmbed/formulae/arm-none-eabi-gcc -brew cask install osxfuse && brew install tup +brew install --cask osxfuse && brew install tup brew install openocd +brew install git-lfs pip3 install PyYAML Jinja2 jsonschema ``` @@ -109,18 +113,6 @@ To customize the compile time parameters, copy or rename the file `Firmware/tup. __CONFIG_BOARD_VERSION__: The board version you're using. Can be `v3.1`, `v3.2`, `v3.3`, `v3.4-24V`, `v3.4-48V`, `v3.5-24V`, `v3.5-48V`, etc. Check for a label on the upper side of the ODrive to find out which version you have. Some ODrive versions don't specify the voltage: in that case you can read the value of the main capacitors: 120uF are 48V ODrives, 470uF are 24V ODrives. -__CONFIG_USB_PROTOCOL__: Defines which protocol the ODrive should use on the USB interface. - * `native`: The native ODrive protocol. Use this if you want to use the python tools in this repo. Can maybe work with macOS. - * `native-stream`: Like the native ODrive protocol, but the ODrive will treat the USB connection exactly as if it was a UART connection. __You may need to use this if you're on macOS__. This is necessary because macOS doesn't grant our python tools sufficient low-level access to treat the device as the USB device that it is. - * `none`: Disable USB. The device will still show up when plugged in but it will ignore any commands. - - **Note**: There is a second USB interface that is always a serial port. - -__CONFIG_UART_PROTOCOL__: Defines which protocol the ODrive should use on the UART interface (GPIO1 and GPIO2). Note that UART is only supported on ODrive v3.3 and higher. - * `native`: The native ODrive protocol. Use this if you're connecting the ODrive to a PC using UART and want to use the python tools to control and setup the ODrive. - * `ascii`: The ASCII protocol. Use this option if you control the ODrive with an Arduino. The ODrive Arduino library is not yet updated to the native protocol. - * `none`: Disable UART. - __CONFIG_DEBUG__: Defines wether debugging will be enabled when compiling the firmware; specifically the `-g -gdwarf-2` flags. Note that printf debugging will only function if your tup.config specifies the `USB_PROTOCOL` or `UART_PROTOCOL` as stdout and `DEBUG_PRINT` is defined. See the IDE specific documentation for more information. You can also modify the compile-time defaults for all `.config` parameters. You will find them if you search for `AxisConfig`, `MotorConfig`, etc. @@ -143,11 +135,7 @@ If the flashing worked, you can connect to the board using the [odrivetool](gett

## Testing -The script `tools/run_tests.py` runs a sequence of automated tests for several firmware features as well as high power burn-in tests. Some tests only need one ODrive and one motor/encoder pair while other tests need a back-to-back test rig such as [this one](https://cad.onshape.com/documents/026bda35ad5dff4d73c1d37f/w/ae302174f402737e1fdb3783/e/5ca143a6e5e24daf1fe8e434). In any case, to run the tests you need to provide a YAML file that lists the parameters of your test setup. An example can be found at [`tools/test-rig-parallel.yaml`](tools/test-rig-parallel.yaml`). The programmer serial number can be found by running `Firmware/find_programmer.sh` (make sure it has the latest firmware from STM). - -
The test script commands the ODrive to high currents and high motor speeds so if your ODrive is connected to anything other than a stirdy test-rig (or free spinning motors), it will probably break your machine.
- -Example usage: `./run_tests.py --test-rig-yaml ../tools/test-rig-parallel.yaml` +_Main article: [Testing](testing.md)_

## Debugging @@ -245,6 +233,34 @@ This happens from time to time. 4. Power on the ODrive 5. Run `make flash` again +### `Warn : Cannot identify target as a STM32 family.` when flashing using openocd + +**Problem:** When I try to flash ODrive v4.1 with `make flash` then I get: +``` +[...] +** Programming Started ** +auto erase enabled +Info : device id = 0x10006452 +Warn : Cannot identify target as a STM32 family. +Error: auto_probe failed +embedded:startup.tcl:487: Error: ** Programming Failed ** +in procedure 'program' +in procedure 'program_error' called at file "embedded:startup.tcl", line 543 +at file "embedded:startup.tcl", line 487 +``` + +**Solution:** +Compile and install a recent version of openocd from source. The latest official release (0.10.0 as of Nov 2020) doesn't support the STM32F722 yet. +``` +sudo apt-get install libtool libusb-1.0 +git clone https://git.code.sf.net/p/openocd/code openocd +cd openocd/ +./bootstrap +./configure --enable-stlink +make +sudo make install +``` + ## Documentation All *.md files in the `docs/` directory of the master branch are served up by GitHub Pages on [this domain](https://docs.odriverobotics.com). @@ -270,23 +286,27 @@ bundle exec jekyll serve --incremental --host=0.0.0.0 On Ubuntu 18.04, prerequisites are: `ruby ruby-dev zlib1g-dev`. +## Modifying libfibre + +If you need to modify libfibre add `CONFIG_BUILD_LIBFIBRE=true` to your tup.config and rerun `make`. After this you can start `odrivetool` (on your local PC) and it will use the updated libfibre. + +To cross-compile libfibre for the Raspberry Pi, run `make libfibre-linux-armhf` or `make libfibre-all`. This will require a docker container. See [fibre-cpp readme](../Firmware/fibre-cpp/README.md) for details. + +docker run -it -v "$(pwd)":/build -v /tmp/build:/build/build -w /build fibre-compiler configs/linux-armhf.config + +If you're satisfied with the changes don't forget to generate binaries for all +supported systems using `make libfibre-all`. + ## Releases We use GitHub Releases to provide firmware releases. 1. Cut off the changelog to reflect the new release 2. Merge the release candidate into master. -3. Push a (lightweight) tag to the master branch. - - Releases should be named like `fw-v0.1.23` - - Prereleases should be named like `fw-v0.1.23-rc` or `fw-v0.1.23-rc4` -4. Go to the GitHub Releases page and review the draft that will have been created by a GitHub Action by now. If you're satisfied, go ahead and publish it by pressing Edit > **Publish Release**. -5. Download `python-odrive-*.tar.gz` from the Releases page. The following step assumes that the version is `0.1.2` -6. Run - ``` - mv {python-,}odrive-0.1.23.tar.gz - pip3 install twine - python3 -m twine upload odrive-0.1.23.tar.gz # Only works once per version! - ``` +3. Push a (lightweight) tag to the master branch. Follow the existing naming convention. +4. If you changed something in libfibre, regenerate the binaries using `make libfibre-all`. See [Modifying libfibre](#modifying-libfibre) for details. +5. Push the python tools to PyPI (see setup.py for details). +6. Edit the release on GitHub to add a title and description (copy&paste from changelog). ## Other code maintenance notes The cortex M4F processor has hardware single precision float unit. However double precision operations are not accelerated, and hence should be avoided. The following regex is helpful for cleaning out double constants: @@ -294,5 +314,22 @@ find: `([-+]?[0-9]+\.[0-9]+(?:[eE][-+]?[0-9]+)?)([^f0-9e])` replace: `\1f\2`

+ ## Notes for Contributors -In general the project uses the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html), except that the default indendtation is 4 spaces, and that the 80 character limit is not very strictly enforced, merely encouraged. +In general the project uses the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html), with a few exceptions: + + - The default indentation is 4 spaces. + - The 80 character limit is not very strictly enforced, merely encouraged. + - The file extensions *.cpp and *.hpp are used instead of *.cc and *.h. + +Your help is welcome! However before you start working on a feature/change that will take you a non-negligible amount of time and that you plan to upstream please discuss your plans with us on GitHub or Discord. This will ensure that your implementation is in line with the direction that ODrive is going. + +When filing a PR please go through this checklist: + + - Make sure you adhere to the same coding style that we use (see note above). + - Update CHANGELOG.md. + - If you removed/moved/renamed things in `odrive-interface.yaml` make sure to add corresponding bullet points tp the "API migration notes" section in the changelog. Use git to compare against the `devel` branch. + - Also, for each removed/moved/renamed API item use your IDE's search feature to search for occurrences of this name. Update the places you found (this will usually be documentation and test scripts). + - If you added things to `odrive-interface.yaml` make sure the new things have decent documentation in the YAML file. We don't expect 100% coverage but use good sense of what to document. + - Make sure your PR doesn't contain spurious changes that unnecessarily add or remove whitespace. These add noise and make the reviewer's lifes harder. + - If you changed any enums in `odrive-interface.yaml`, make sure you update [enums.py](../tools/odrive/enums.py). The file includes instructions on how to do this. Check the diff to verify that none of the existing enumerators changed their value. diff --git a/docs/encoders.md b/docs/encoders.md index a4d727690..f01426e4c 100644 --- a/docs/encoders.md +++ b/docs/encoders.md @@ -21,7 +21,7 @@ To verify everything went well, check the following variables: * `.error` should be 0. * `.encoder.config.offset` - This should print a number, like -326 or 1364. - * `.motor.config.direction` - This should print 1 or -1. + * `.encoder.config.direction` - This should print 1 or -1. ### Encoder with index signal If you have an encoder with an index (Z) signal, you can avoid doing the offset calibration on every startup, and instead use the index signal to re-sync the encoder to a stored calibration. @@ -62,6 +62,8 @@ When the encoder mode is set to hall feedback, the pinout on the encoder port is | B | Hall B | | Z | Hall C | +To use hall effect encoders, the calibration sequence is different than incremental or absolute encoders. You must first run `AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION` before `AXIS_STATE_ENCODER_OFFSET_CALIBRATION` The hall polarity calibration will automatically determine the order and polarity of the hall signals. When using `AXIS_STATE_FULL_CALIBRATION_SEQUENCE`, these steps are automatically used if the encoder is set to hall mode. + ### Startup sequence notes The following are variables that MUST be set up for your encoder configuration. Your values will vary depending on your encoder: @@ -148,6 +150,30 @@ If you are using an encoder with an index signal, another problem that has been * when performing an index_search, the motor does not return to the same position each time. One easy step that _might_ fix the noise on the Z input is to solder a 22nF-47nF capacitor to the Z pin and the GND pin on the underside of the ODrive board. +## Hall feedback pinout +If position accuracy is not a concern, you can use A/B/C hall effect encoders for position feedback. + +To use this mode, configure the corresponding encoder mode: `.config.mode = ENCODER_MODE_HALL`. Configure the corresponding GPIOs as digital inputs: + +For encoder 0: + + .config.gpio9_mode = GPIO_MODE_DIGITAL + .config.gpio10_mode = GPIO_MODE_DIGITAL + .config.gpio11_mode = GPIO_MODE_DIGITAL + +For encoder 1: + + .config.gpio12_mode = GPIO_MODE_DIGITAL + .config.gpio13_mode = GPIO_MODE_DIGITAL + .config.gpio14_mode = GPIO_MODE_DIGITAL + +In this mode, the pinout on the encoder port is as follows: + +| Label on ODrive | Hall feedback | +|-----------------|---------------| +| A | Hall A | +| B | Hall B | +| Z | Hall C | ## SPI Encoders @@ -181,3 +207,4 @@ If you are having calibration problems, make sure that your magnet is centered o Sometimes the encoder takes longer than the ODrive to start, in which case you need to clear the errors after every restart. If you are having calibration problems - make sure your magnet is centered on the axis of rotation on the motor, some users report this has a significant impact on calibration. Also make sure your magnet height is within range of the spec sheet. + diff --git a/docs/endstops.md b/docs/endstops.md index 3e361d5e8..971e48e8d 100644 --- a/docs/endstops.md +++ b/docs/endstops.md @@ -65,6 +65,7 @@ Match the pullup value to the configuration. If `true`, it enables the GPIO pul If we want to configure a 3D printer-style (configuration 4) minimum endstop for homing on GPIO 5 and we want our motor to move away from the endstop about a quarter turn with a 8192 cpr encoder, we would set: ``` +.config.gpio5_mode = GPIO_MODE_DIGITAL ..min_endstop.config.gpio_num = 5 ..min_endstop.config.is_active_high = False ..min_endstop.config.offset = -1.0*(8912/4) diff --git a/docs/getting-started.md b/docs/getting-started.md index a71399527..0b09783ec 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -19,7 +19,6 @@ permalink: / - [Other control modes](#other-control-modes) - [Watchdog Timer](#watchdog-timer) - [What's next?](#whats-next) -- [Upgrading from 0.4.12](#upgrading-from-0412) @@ -90,9 +89,6 @@ Most instructions in this guide refer to a utility called `odrivetool`, so you s * __Anaconda__: In the start menu, type `Anaconda Prompt` Enter * __Standalone Python__: In the start menu, type `cmd` Enter 3. Install the ODrive tools by typing `pip install --upgrade odrive` Enter -4. Plug in a USB cable into the microUSB connector on ODrive, and connect it to your PC. -5. Use the [Zadig](http://zadig.akeo.ie/) utility to set ODrive driver to libusb-win32. - * Check 'List All Devices' from the options menu, and select 'ODrive 3.x Native Interface (Interface 2)'. With that selected in the device list choose 'libusb-win32' from the target driver list and then press the large 'install driver' button. ### OSX @@ -120,16 +116,22 @@ pip3 install --upgrade odrive __Troubleshooting__ 1. Permission Errors: Just run the previous command in sudo -```bash -sudo pip3 install --upgrade odrive -``` + ```bash + sudo pip3 install --upgrade odrive + ``` 2. Dependency Errors: If the installer doesn't complete and you get a dependency error (Ex. "No module..." or "module_name not found") -```bash -sudo pip3 install module_name -``` -Try step 5 again + ```bash + sudo pip3 install module_name + ``` + Try step 5 again + +3. Other Install Errors: If the installer fails at installing dependencies, try + ```bash + sudo pip3 install odrive --no-deps + ``` + If you do this, brace yourself for runtime errors when you run `odrivetool` (the basic functionality should work though). ### Linux @@ -151,7 +153,7 @@ Your board should come preflashed with firmware. If you run into problems, follo Your board does **not** come preflashed with any firmware. Follow the instructions [here](odrivetool.md#device-firmware-update) on the ST Link procedure before you continue. ## Start `odrivetool` -To launch the main interactive ODrive tool, type `odrivetool` Enter. Connect your ODrive and wait for the tool to find it. Now you can, for instance type `odrv0.vbus_voltage` Enter to inpect the boards main supply voltage. +To launch the main interactive ODrive tool, type `odrivetool` Enter. Connect your ODrive and wait for the tool to find it. If it doesn't connect after a few seconds refer to the [troubleshooting page](troubleshooting.md#usb-connectivity-issues). Now you can, for instance type `odrv0.vbus_voltage` Enter to inpect the boards main supply voltage. It should look something like this: ```text @@ -168,6 +170,9 @@ The tool you're looking at is a fully capable Python command prompt, so you can You can read more about `odrivetool` [here](odrivetool.md). +## Debugging +If any of the following steps fail, print the errors by running `dump_errors(odrv0)` in `odrivetool`. You can clear errors by running `odrv0.clear_errors()`. + ## Configure M0
Read this section carefully, else you risk breaking something.
There is a [separate guide](hoverboard.md) specifically for hoverboard motors.
@@ -198,8 +203,14 @@ The motor will be limited to this speed. Again the default value is quite slow. You can change `odrv0.axis0.motor.config.calibration_current` [A] to the largest value you feel comfortable leaving running through the motor continuously when the motor is stationary. If you are using a small motor (i.e. 15A current rated) you may need to reduce `calibration_current` to a value smaller than the default. ### 2. Set other hardware parameters +`odrv0.config.enable_brake_resistor` +Set this to `True` if using a brake resistor. You need to save the ODrive configuration and reboot the ODrive for this to take effect. + `odrv0.config.brake_resistance` [Ohm] -This is the resistance of the brake resistor. If you are not using it, you may set it to `0`. Note that there may be some extra resistance in your wiring and in the screw terminals, so if you are getting issues while braking you may want to increase this parameter by around 0.05 ohm. +This is the resistance of the brake resistor. You can leave this at the default setting if you are not using a brake resistor. Note that there may be some extra resistance in your wiring and in the screw terminals, so if you are getting issues while braking you may want to increase this parameter by around 0.05 ohm. + +`odrv0.config.dc_max_negative_current` [Amps] +This is the amount of current allowed to flow back into the power supply. The convention is that it is negative. By default, it is set to a conservative value of 10mA. If you are using a brake resistor and getting `DC_BUS_OVER_REGEN_CURRENT` errors, raise it slightly. If you are not using a brake resistor and you intend to send braking current back to the power supply, set this to a safe level for your power source. Note that in that case, it should be higher than your motor current limit + current limit margin. `odrv0.axis0.motor.config.pole_pairs` This is the number of **magnet poles** in the rotor, **divided by two**. To find this, you can simply count the number of permanent magnets in the rotor, if you can see them. @@ -258,7 +269,7 @@ Let's get motor 0 up and running. The procedure for motor 1 is exactly the same, Check the encoder wiring and that the encoder is firmly connected to the motor. Check the value of `dump_errors(odrv0)` and then refer to the [error code documentation](troubleshooting.md#error-codes) for details. - Once you understand the error and have fixed its cause, you may clear the error state with (`dump_errors(odrv0, True)` Enter) and retry. + Once you understand the error and have fixed its cause, you may clear the error state with (`odrv0.clear_errors()` Enter) and retry. 2. Type `odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL` Enter. From now on the ODrive will try to hold the motor's position. If you try to turn it by hand, it will fight you gently. That is unless you bump up `odrv0.axis0.motor.config.current_lim`, in which case it will fight you more fiercely. If the motor begins to vibrate either immediately or after being disturbed you will need to [lower the controller gains](control.md). diff --git a/docs/hoverboard.md b/docs/hoverboard.md index c38a87f91..ec568fe32 100644 --- a/docs/hoverboard.md +++ b/docs/hoverboard.md @@ -45,6 +45,9 @@ The hall feedback has 6 states for every pole pair in the motor. Since we have 1 odrv0.axis0.encoder.config.mode = ENCODER_MODE_HALL odrv0.axis0.encoder.config.cpr = 90 odrv0.axis0.encoder.config.calib_scan_distance = 150 +odrv0.config.gpio9_mode = GPIO_MODE_DIGITAL +odrv0.config.gpio10_mode = GPIO_MODE_DIGITAL +odrv0.config.gpio11_mode = GPIO_MODE_DIGITAL ``` Since the hall feedback only has 90 counts per revolution, we want to reduce the velocity tracking bandwidth to get smoother velocity estimates. @@ -92,6 +95,22 @@ odrv0.axis0.motor.config.pre_calibrated = True Next step is to check the alignment between the motor and the hall sensor. Because of this step you are allowed to plug the motor phases in random order and also the hall signals can be random. Just don't change it after calibration. Make sure the motor is free to move and run: +```txt +odrv0.axis0.requested_state = AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION +``` + +Check the status of the encoder object: +```txt +odrv0.axis0.encoder +``` + +Check that there are no errors. +```txt + error = 0x0000 (int) +``` + +If the hall encoder polarity calibration was successful, run the encoder offset calibration. + ```txt odrv0.axis0.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION ``` @@ -132,6 +151,7 @@ Lets use GPIO 3/4 for the velocity inputs so that we don't have to disable UART. Then let's map the full stick range of these inputs to some suitable velocity setpoint range. We also have to reboot to activate the PWM input. ```txt +<<<<<<< HEAD odrv0.config.gpio3_pwm_mapping.min = -2 odrv0.config.gpio3_pwm_mapping.max = 2 odrv0.config.gpio3_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['input_vel'] @@ -139,6 +159,15 @@ odrv0.config.gpio3_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attribu odrv0.config.gpio4_pwm_mapping.min = -2 odrv0.config.gpio4_pwm_mapping.max = 2 odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis1.controller._remote_attributes['input_vel'] +======= +odrv0.config.gpio3_pwm_mapping.min = -200 +odrv0.config.gpio3_pwm_mapping.max = 200 +odrv0.config.gpio3_pwm_mapping.endpoint = odrv0.axis0.controller._input_vel_property + +odrv0.config.gpio4_pwm_mapping.min = -200 +odrv0.config.gpio4_pwm_mapping.max = 200 +odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis1.controller._input_vel_property +>>>>>>> fw-v0.5.2rc1 odrv0.save_configuration() odrv0.reboot() diff --git a/docs/mechanical-brakes.md b/docs/mechanical-brakes.md new file mode 100644 index 000000000..ab2164e19 --- /dev/null +++ b/docs/mechanical-brakes.md @@ -0,0 +1,61 @@ +# Mechanical Brake + +Some systems employ mechanical brakes on motors as a safety feature. These brakes can also be engaged as a power-saving function if the motor is not moving, but still under load. + +ODrive supports the use of its GPIO pins to connect to external brake drive electronics. + +When the ODrive engages the drive electronics, the brake will be disabled. When the drive enters a fault or idle state, the brake will be re-engaged. + +--- + +## Mechanical Brake Configuration +Each axis supports one mechanical brake. The following properties are accessible through `odrivetool`: + +Name | Type | Default +--- | -- | -- +gpio_num | int | 0 +is_active_low | boolean | true + +### gpio_num +The GPIO pin number, according to the silkscreen labels on ODrive. Set with these commands: +``` +..mechanical_brake.config.gpio_num = <1, 2, 3, 4, 5, 6, 7, 8> +``` +After GPIO pin number is changed, you'll need to run `.save_configuration()` and `.reboot()` for changes to take effect. + +### is_active_low +Most safety braking systems are active low, e.g. when the power is off, the brake is on. If the system uses brake drive electronics which use active high logic, flip this bit then reconsider the safety implications of your design... + +### Enabling +The configuration of the mechanical brake will enable the brake functionality. There's no need to specifically 'enable' this feature. + + +### Example + +Let's say we're hacking away on an old ABB robotic arm. We've wired a 24V brake drive circuit triggered by GPIO5. When GPIO5 is driven, it will release the brakes on the axis we're moving. + +We need to notify the axis of the GPIO number we've attached our brake to, and configure the ODrive pin mode to `GPIO_MODE_MECH_BRAKE`: +``` +..mechanical_brake.config.gpio_num = 5 +.config.gpio5_mode = GPIO_MODE_MECH_BRAKE +``` + +Pin configurations only take effect after a save/reboot so don't forget to run: +``` +.save_configuration() +.reboot() +``` + +### Testing The Mechanical Brakes +Depending on your system this could be a dangerous experiment. Ensure that you have taken all necessary precautions to confirm if the wrong brake were inadvertently released it would not lead to injury or damage to equipment. + +``` +..mechanical_brake.release() +``` +Note: If a brake is configured, it will be automatically engaged/disengaged during the next state machine step. + +After you're satisfied with the testing, you can re-enable the brake using the command + +``` +..mechanical_brake.engage() +``` diff --git a/docs/migration.md b/docs/migration.md index 5870b89a4..b95b99b5b 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -1,5 +1,44 @@ # Migration Guide +## v0.5.1 -> v0.5.2 + +The change from v0.5.1 to v0.5.2 had fewer breaking changes than the v0.4.12 to v0.5.1 change. + +## GPIO modes +The GPIO configuration is now more explicit. For example, to use `gpio1` for for step signals (as part of a step/dir interface), it must be set to +``` +odrv0.config.gpio1_mode = GPIO_MODE_DIGITAL +``` + +## Braking behavior +Before using the brake resistor, it must be explicitly enabled as follows: +``` +odrv0.config.enable_brake_resistor = True +``` +and then save the configuration and reboot for the setting to take effect. + +## Step/direction settings +Previously, steps were added incrementally to `input_pos`. This caused issues with accumulated floating point rounding error. Now, an absolute step count is used. This change requires that the circular setpoints mode is used `odrv0.axis0.controller.config.circular_setpoints = True` when step/dir signals are used. + +In addition, `odrv0.axis0.config.turns_per_step` has been removed and `odrv0.axis0.controller.config.steps_per_circular_range` is used. For example: +``` +# previous +odrv0.axis0.config.turns_per_step = 1.0/1024.0 + +# v0.5.2 +odrv0.axis0.controller.config.circular_setpoints = True +odrv0.axis0.controller.config.circular_setpoint_range = 1.0 +odrv0.axis0.controller.config.steps_per_circular_range = 1024 +``` + +For best results, set both the circular range and steps per circular range to powers of 2. + +## API changes + +For other API changes, see the Changelog file on github. + +## v0.4.12 -> v0.5.1 + Certain changes occurred between firmware versions v0.4.12 and v0.5.1 that will break existing configurations. This document is a guide for how to take a working v0.4.12 ODrive config and change it to work with firmware v0.5.1. ## Unit Changes diff --git a/docs/odrivetool.md b/docs/odrivetool.md index 558e92f1c..142c73576 100644 --- a/docs/odrivetool.md +++ b/docs/odrivetool.md @@ -158,7 +158,7 @@ sudo dfu-util -a 0 -s 0x08000000 -D build/ODriveFirmware.bin First, you need to install the arm development tools to copy the binary into the appropriate format. ```text -$ brew cask install gcc-arm-embedded +$ brew install --cask gcc-arm-embedded ``` Then convert the binary to .bin format diff --git a/docs/pinout.md b/docs/pinout.md index 225c3f378..047874b29 100644 --- a/docs/pinout.md +++ b/docs/pinout.md @@ -1,35 +1,42 @@ # Pinout -| GPIO | primary | step/dir | other | -|-----------|-----------|---------------|-------------------------| -| GPIO1 | UART TX | Axis0 Step | Analog input, PWM input | -| GPIO2 | UART RX | Axis0 Dir | Analog input, PWM input | -| GPIO3 | | Axis1 Step (+)| Analog input, PWM input | -| GPIO4 | | Axis1 Dir (+) | Analog input, PWM input | -| GPIO5 | | | Analog input (*) | -| GPIO6 (*) | | | | -| GPIO7 (*) | | Axis1 Step (*)| | -| GPIO8 (*) | | Axis1 Dir (*) | | - -(+) on ODrive v3.4 and earlier
-(*) ODrive v3.5 and later - -Notes: +## ODrive v4.1 + +**TODO** + +## ODrive v3.x + +| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART_A` | `GPIO_MODE_UART_B` | `GPIO_MODE_PWM` | `GPIO_MODE_CAN_A` | `GPIO_MODE_I2C_A` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` | `GPIO_MODE_MECH_BRAKE` | +|----|---------------|------------------------|-----------------------|--------------------|--------------------|-----------------|------------------|-------------------|------------------|------------------|------------------------| +| 0 | _not a pin_ | | | | | | | | | | | +| 1 | GPIO1 (+) | general purpose | analog input | **UART_A.TX** | | PWM0.0 | | | | | mechanical brake | +| 2 | GPIO2 (+) | general purpose | analog input | **UART_A.RX** | | PWM0.1 | | | | | mechanical brake | +| 3 | GPIO3 | general purpose | **analog input** | | **UART_B.TX** | PWM0.2 | | | | | mechanical brake | +| 4 | GPIO4 | general purpose | **analog input** | | **UART_B.RX** | PWM0.3 | | | | | mechanical brake | +| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | | | mechanical brake | +| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 9 | M0.A | general purpose | | | | | | | **ENC0.A** | | | +| 10 | M0.B | general purpose | | | | | | | **ENC0.B** | | | +| 11 | M0.Z | **general purpose** | | | | | | | | | | +| 12 | M1.A | general purpose | | | | | | I2C.SCL | | **ENC1.A** | | +| 13 | M1.B | general purpose | | | | | | I2C.SDA | | **ENC1.B** | | +| 14 | M1.Z | **general purpose** | | | | | | | | | | +| 15 | _not exposed_ | general purpose | | | | | **CAN_A.RX** | I2C.SCL | | | | +| 16 | _not exposed_ | general purpose | | | | | **CAN_A.TX** | I2C.SDA | | | | + + +(*) ODrive v3.5 and later
+(+) On ODrive v3.5 and later these pins have noise suppression filters. This is useful for step/dir input.
+ +## Notes + +* Changes to the pin configuration only take effect after `odrv0.save_configuration()` and `odrv0.reboot()` +* Bold font marks the default configuration. +* If a GPIO is set to an unsupported mode it will be left uninitialized. +* When setting a GPIO to a special purpose mode (e.g. `GPIO_MODE_UART_A`) you must also enable the corresponding feature (e.g. `.config.enable_uart_a`). +* Digital mode is a general purpose mode that can be used for these functions: step, dir, enable, encoder index, hall effect encoder, SPI encoder nCS. * You must also connect GND between ODrive and your other board. * ODrive v3.3 and onward have 5V tolerant GPIO pins. -* ODrive v3.5 and later have some noise suppression filters on the default step/dir pins -* You can change the step/dir pins using `axis.config._gpio_pin`. - -### Pin function priorities -1. PWM in, if enabled. Disabled by default. -1. UART, **Enabled by default**. -1. Step/Dir, if enabled. Disabled by default. -1. Analog, default behavior if not overridden (only on supported pins). -1. Digital in, default behavior on pins not capable of analog input. - -For predictable results, try to have only one feature enabled for any one pin. When changing pin assignments you must: -* `odrv0.save_configuration()` -* `odrv0.reboot()` - -### Analog input -Analog inputs can be used to measure voltages between 0 and 3.3V. Odrive uses a 12 bit ADC (4096 steps) and so has a maximum resolution of 0.8 mV. Some GPIO pins require the appropriate pin priority (see above) to be set before they can be used as an analog input. To read the voltage on GPIO1 in odrive tool the following would be entered: `odrv0.get_adc_voltage(1)` \ No newline at end of file +* Simultaneous operation of UART_A and UART_B is currently not supported. diff --git a/docs/rc-pwm.md b/docs/rc-pwm.md index 2a1f4c8af..f916fd19b 100644 --- a/docs/rc-pwm.md +++ b/docs/rc-pwm.md @@ -1,24 +1,22 @@ # RC PWM input -You can control the ODrive directly from an hobby RC receiver. +You can control the ODrive directly from a hobby RC receiver. -Some GPIO pins can be used for PWM input, if they are not allocated to other functions. For example, you must disable the UART to use GPIO 1,2. See the [pin function priorities](pinout.md#pin-function-priorities) for more detail. - -Any of the numerical parameters that are writable from the ODrive Tool can be hooked up to a PWM input. -As an example, we'll configure GPIO4 to control the angle of axis 0. We want the axis to move within a range of -2 to 2 turns. +Any of the numerical parameters that are writable from the ODrive Tool can be hooked up to a PWM input. The [Pinout](#pinout) tells you which pins are PWM input capable. As an example, we'll configure GPIO4 to control the angle of axis 0. We want the axis to move within a range of -2 to 2 turns. 1. Make sure you're able control the axis 0 angle by writing to `odrv0.axis0.controller.input_pos`. If you need help with this follow the [getting started guide](getting-started.md). 2. If you want to control your ODrive with the PWM input without using anything else to activate the ODrive, you can configure the ODrive such that axis 0 automatically goes operational at startup. See [here](commands.md#startup-procedure) for more information. 3. In ODrive Tool, configure the PWM input mapping ``` - In [1]: odrv0.config.gpio4_pwm_mapping.min = -2 - In [2]: odrv0.config.gpio4_pwm_mapping.max = 2 - In [3]: odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['input_pos'] + odrv0.config.gpio4_mode = GPIO_MODE_PWM + odrv0.config.gpio4_pwm_mapping.min = -2 + odrv0.config.gpio4_pwm_mapping.max = 2 + odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis0.controller._input_pos_property ``` Note: you can disable the input by setting `odrv0.config.gpio4_pwm_mapping.endpoint = None` 4. Save the configuration and reboot ``` - In [4]: odrv0.save_configuration() - In [5]: odrv0.reboot() + odrv0.save_configuration() + odrv0.reboot() ``` 5. With the ODrive powered off, connect the RC receiver ground to the ODrive's GND and one of the RC receiver signals to GPIO4. You may try to power the receiver from the ODrive's 5V supply if it doesn't draw too much power. Power up the the RC transmitter. You should now be able to control axis 0 from one of the RC sticks. diff --git a/docs/resources.md b/docs/resources.md new file mode 100644 index 000000000..5212dfb35 --- /dev/null +++ b/docs/resources.md @@ -0,0 +1,132 @@ + +Most information in this file can be reproduced by running `dump_interrupts(odrv0)`, `dump_dma(odrv0)` and `dump_threads(odrv0)` in `odrivetool` (minor manual postprocessing was applied to the output of those functions). + +Take this info with a grain of salt as we might forget to update it from time to time. When in doubt check the file history. + +# ODrive v3.6 + +## Interrupt Vectors + + - lowest priority: 15 + - highest priority: 0 + +| # | Name | Prio | +|-----|-------------------------|------| +| -12 | MemoryManagement_IRQn | 0 | +| -11 | BusFault_IRQn | 0 | +| -10 | UsageFault_IRQn | 0 | +| -5 | SVCall_IRQn | 3 | +| -4 | DebugMonitor_IRQn | 0 | +| -2 | PendSV_IRQn | 15 | +| -1 | SysTick_IRQn | 15 | +| 6 | EXTI0_IRQn | 1 | +| 7 | EXTI1_IRQn | 1 | +| 8 | EXTI2_IRQn | 1 | +| 9 | EXTI3_IRQn | 1 | +| 10 | EXTI4_IRQn | 1 | +| 11 | DMA1_Stream0_IRQn | 4 | +| 13 | DMA1_Stream2_IRQn | 10 | +| 15 | DMA1_Stream4_IRQn | 10 | +| 16 | DMA1_Stream5_IRQn | 10 | +| 17 | DMA1_Stream6_IRQn | 10 | +| 19 | CAN1_TX_IRQn | 9 | +| 20 | CAN1_RX0_IRQn | 9 | +| 21 | CAN1_RX1_IRQn | 9 | +| 22 | CAN1_SCE_IRQn | 9 | +| 23 | EXTI9_5_IRQn | 1 | +| 40 | EXTI15_10_IRQn | 1 | +| 44 | TIM8_UP_TIM13_IRQn | 0 | +| 45 | TIM8_TRG_COM_TIM14_IRQn | 6 | +| 47 | DMA1_Stream7_IRQn | 3 | +| 50 | TIM5_IRQn | 1 | +| 52 | UART4_IRQn | 10 | +| 67 | OTG_FS_IRQn | 6 | +| 77 | OTG_HS_IRQn (aka ControlLoop_IRQn) | 5 | + + +## DMA Streams + + - lowest priority: 0 + - highest priority: 3 + +| Name | Prio | Channel | High Level Func | +|--------------|------|----------------------------------|-----------------| +| DMA1_Stream0 | 1 | 0 (SPI3_RX) | SPI_A | +| DMA1_Stream2 | 0 | 4 (UART4_RX) | UART_A | +| DMA1_Stream4 | 0 | 4 (UART4_TX) | UART_A | +| DMA1_Stream5 | 0 | 4 (USART2_RX) | UART_B | +| DMA1_Stream6 | 0 | 4 (USART2_TX) | UART_B | +| DMA1_Stream7 | 2 | 0 (SPI3_TX) | SPI_A | +| DMA2_Stream0 | 0 | 0 (ADC1) | freerunning ADC | + + +## Threads + + - lowest priority: -3 + - highest priority: 3 + +| Name | Stack Size [B] | Prio | +|---------|----------------|------| +| axis0 | 2048 | 3 | +| axis1 | 2048 | 2 | +| can | 1024 | 0 | +| startup | 2048 | 0 | +| uart | 4096 | 0 | +| usb | 4096 | 0 | + + +# ODrive v4.0 + +## Interrupt Vectors + + - lowest priority: 15 + - highest priority: 0 + +| # | Name | Prio | +|-----|-------------------------|------| +| -12 | MemoryManagement_IRQn | 0 | +| -11 | BusFault_IRQn | 0 | +| -10 | UsageFault_IRQn | 0 | +| -5 | SVCall_IRQn | 0 | +| -4 | DebugMonitor_IRQn | 0 | +| -2 | PendSV_IRQn | 15 | +| -1 | SysTick_IRQn | 15 | +| 11 | DMA1_Stream0_IRQn | 5 | +| 14 | DMA1_Stream3_IRQn | 5 | +| 15 | DMA1_Stream4_IRQn | 5 | +| 16 | DMA1_Stream5_IRQn | 5 | +| 17 | DMA1_Stream6_IRQn | 5 | +| 18 | ADC_IRQn | 1 | +| 19 | CAN1_TX_IRQn | 6 | +| 20 | CAN1_RX0_IRQn | 6 | +| 21 | CAN1_RX1_IRQn | 6 | +| 22 | CAN1_SCE_IRQn | 6 | +| 26 | TIM1_TRG_COM_TIM11_IRQn | 2 | +| 35 | SPI1_IRQn | 5 | +| 36 | SPI2_IRQn | 5 | +| 38 | USART2_IRQn | 5 | +| 45 | TIM8_TRG_COM_TIM14_IRQn | 0 | +| 47 | DMA1_Stream7_IRQn | 0 | +| 51 | SPI3_IRQn | 5 | +| 59 | DMA2_Stream3_IRQn | 5 | +| 77 | OTG_HS_IRQn | 5 | + +## DMA Streams + + - lowest priority: 0 + - highest priority: 3 + +| Name | Prio | Channel | High Level Func | +|--------------|------|----------------------------------|-----------------| +| DMA1_Stream0 | 1 | 0 (SPI3_RX) | Onboard SPI | +| DMA1_Stream3 | 0 | 0 (SPI2_RX) | Offboard SPI | +| DMA1_Stream4 | 0 | 0 (SPI2_TX) | Offboard SPI | +| DMA1_Stream5 | 0 | 4 (USART2_RX) | UART1 | +| DMA1_Stream6 | 0 | 4 (USART2_TX) | UART1 | +| DMA1_Stream7 | 1 | 0 (SPI3_TX) | Onboard SPI | +| DMA2_Stream0 | 0 | 0 (ADC1) | freerunning ADC | +| DMA2_Stream3 | 0 | 3 (SPI1_TX) | Status LED | + +## Threads + +**TODO** diff --git a/docs/step-direction.md b/docs/step-direction.md index 459c869f9..70af9fe3a 100644 --- a/docs/step-direction.md +++ b/docs/step-direction.md @@ -1,14 +1,39 @@ # Step/direction This is the simplest possible way of controlling the ODrive. It is also the most primitive and fragile one. So don't use it unless you must interoperate with other hardware that you don't control. -Pinout: -* Step/dir signals: see [Pinout](pinout.md). Note in that section how to reassign the pins. +### Pinout +* Step/dir signals: Any GPIOs can be used. Also see [Pinout](pinout.md) for more info. * GND: you must connect the grounds of the devices together. Use any GND pin on J3 of the ODrive. -To enable step/dir mode for the GPIO, set `.config.enable_step_dir` to true for each axis that you wish to use this on. -Axis 0 step/dir pins conflicts with UART, and the UART takes priority. So to be able to use step/dir on Axis 0, you must also set `odrv0.config.enable_uart = False`. See the [pin function priorities](pinout.md#pin-function-priorities) for more detail. Don't forget to save configuration and reboot. +### How to configure -There is also a config variable called `.config.turns_per_step`, which specifies how many turns a "step" corresponds to. The default value is 1.0f/1024.0f. It can be any floating point value. -The recommended maximum step rate is 50kHz + 1. Choose any two of the unused GPIOs for step/dir input. Let's say you chose GPIO7 for the step signal and GPIO8 for the dir signal. + 2. Configure the GPIO modes: + + .config.gpio7_mode = GPIO_MODE_DIGITAL_PULL_DOWN + .config.gpio8_mode = GPIO_MODE_DIGITAL + + 3. Configure the axis: + + .config.step_gpio_pin = 7 + .config.dir_gpio_pin = 8 + .config.enable_step_dir = True + + 4. Enable circular setpoints + + .controller.config.circular_setpoints = True + +After this, step and direction will be enabled when you put the axis into closed loop control. Note that to change out of step/dir, you need to set `.config.enable_step_dir = False`, go to `AXIS_STATE_IDLE`, and then back into closed loop control. + +Circular setpoints are used to keep floating point error at a manageable error for systems where the motor can rotate large amounts. If the motor is commanded out of the circular range, the position setpoint automatically wraps around to stay in the range. Two parameters are used to control this behavior: `..controller.config.circular_setpoint_range` and `..controller.config.steps_per_circular_range`. The circular setpoint range sets the operating range of input_pos, starting at 0.0. The `steps per circular range` setting controls how many steps are needed to traverse the entire range. For example, to use 1024 steps per 1 full motor turn, set + +``` +..controller.config.circular_setpoint_range = 1.0 [turns] +..controller.config.steps_per_circular_range = 1024 [steps] +``` + +The circular range is a floating point value and the steps per circular range parameter is an integer. For best results, set both parameters to powers of 2. + +The maximum step rate is pending tests, but 250kHz step rates with both axes in closed loop has been achieved. Please be aware that there is no enable line right now, and the step/direction interface is enabled by default, and remains active as long as the ODrive is in position control mode. To get the ODrive to go into position control mode at bootup, see how to configure the [startup procedure](commands.md#startup-procedure). diff --git a/docs/testing.md b/docs/testing.md index 5ba1160b7..2f1a5d868 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -44,7 +44,7 @@ If your test rig differs, you may be able to run some but not all of the tests. ## How to set up a Raspberry Pi as testing host - 1. Install Raspbian Lite on a Raspberry Pi 4.0. I used the NOOBS installer for this. + 1. Install Raspbian Lite on a Raspberry Pi 4.0. This is easiest if you have a keyboard, mouse and screen (micro-HDMI!). I used the [NOOBS Lite installer](https://www.raspberrypi.org/downloads/noobs/) for this. Paste the ZIP-file's contents onto a FAT32 formatted SD card (fs type `0b` in `fdisk`) and boot it. Then follow the on-screen instructions. 2. Prepare the installation: sudo systemctl enable ssh @@ -52,6 +52,7 @@ If your test rig differs, you may be able to run some but not all of the tests. # Transfer your public key for passwordless SSH. All subsequent steps can be done via SSH. sudo apt-get update sudo apt-get upgrade + # Change /etc/hostname to something meaningful 3. Add the following lines to `/boot/config.txt`: - `enable_uart=1` @@ -62,42 +63,50 @@ If your test rig differs, you may be able to run some but not all of the tests. 4. Remove the following arguments from `/boot/cmdline.txt`: - `console=serial0,115200` - 5. Reboot. + 5. Append `ODRIVE_TEST_RIG_NAME=[test-rig-name]` to `/etc/environment`. The HWIL tests use this to look up the the file `[test-rig-name].yaml` which is supposed to describe your test rig. - 6. Install the prerequisites: + 6. Reboot. - sudo apt-get install ipython3 python3-appdirs python3-yaml python3-usb python3-serial python3-can python3-scipy git openocd + 7. Install the prerequisites: + + sudo apt-get install ipython3 python3-appdirs python3-yaml python3-jinja2 python3-usb python3-serial python3-can python3-scipy python3-matplotlib python3-ipdb git openocd # Optionally, to be able to compile the firmware: sudo apt-get install gcc-arm-none-eabi - 7. Install Teensyduino and teensy-loader-cli: + 8. Install Teensyduino and teensy-loader-cli: sudo apt-get install libfontconfig libxft2 libusb-dev - wget https://downloads.arduino.cc/arduino-1.8.12-linuxarm.tar.xz - tar -xf arduino-1.8.12-linuxarm.tar.xz - wget https://www.pjrc.com/teensy/td_151/TeensyduinoInstall.linuxarm + wget https://downloads.arduino.cc/arduino-1.8.13-linuxarm.tar.xz + tar -xf arduino-1.8.13-linuxarm.tar.xz + wget https://www.pjrc.com/teensy/td_153/TeensyduinoInstall.linuxarm chmod +x TeensyduinoInstall.linuxarm - ./TeensyduinoInstall.linuxarm --dir=arduino-1.8.12 - sudo cp -R arduino-1.8.12 /usr/share/arduino + ./TeensyduinoInstall.linuxarm --dir=arduino-1.8.13 + sudo cp -R arduino-1.8.13 /usr/share/arduino sudo ln -s /usr/share/arduino/arduino /usr/bin/arduino git clone https://github.com/PaulStoffregen/teensy_loader_cli pushd teensy_loader_cli + make sudo cp teensy_loader_cli /usr/bin/ sudo ln -s /usr/bin/teensy_loader_cli /usr/bin/teensy-loader-cli popd + curl https://www.pjrc.com/teensy/49-teensy.rules | sudo tee /etc/udev/rules.d/49-teensy.rules - 8. Add the following lines to `/etc/udev/rules.d/49-stlinkv2`: + 9. Add the following lines to `/etc/udev/rules.d/49-stlinkv2.rules`: SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE:="0666" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE:="0666" - 9. `sudo ../../odrivetool udev-setup` + 10. `sudo mkdir /opt/odrivetest && sudo chown $USER /opt/odrivetest` + + 11. At this point you need the ODrive repository. See next section to sync it from your main PC. We assume now that you navigated to `tools/odrive/tests/`. + + 12. `sudo ../../odrivetool udev-setup` - 10. `sudo udevadm trigger` + 13. `sudo udevadm trigger` - 11. Run once after every reboot: `sudo ipython3 --pdb test_runner.py -- --setup-host --test-rig-yaml ../../test-rig-rpi.yaml` + 14. Run once after every reboot: `sudo -E ipython3 --pdb test_runner.py -- --setup-host` ## SSH testing flow @@ -109,14 +118,14 @@ To flash and start remote debugging: 1. Start OpenOCD remotely, along with a tunnel to localhost: `ssh -t odrv -L3333:localhost:3333 bash -c "\"openocd '-f' 'interface/stlink-v2.cfg' '-f' 'target/stm32f4x_stlink.cfg'\""` You can keep this open for multiple debug sessions. Press Ctrl+C to quit. - 2. Compile the firmware - 3. In VSCode, select the run configuration "Debug ODrive via external server" and press Run. In contrast to the other configurations, this will flash the new firmware before dropping you into the debugger. + 2. Compile the firmware. + 3. In VSCode, select the run configuration "Debug ODrive v3.x/v4.x - Remote" and press Run. This will flash the new firmware before dropping you into the debugger. To run a test: - rsync -avh -e ssh /path/to/ODriveFirmware/ odrv:/opt/odrivetest --exclude="Firmware/build" --exclude="Firmware/.tup" --exclude=".git" --delete + rsync -avh -e ssh /path/to/ODriveFirmware/ odrv:/opt/odrivetest --exclude="Firmware/build" --exclude="Firmware/.tup" --exclude=".git" --exclude="GUI" --delete ssh odrv > cd /opt/odrivetest/tools/odrive/tests/ - > ipython3 --pdb uart_ascii_test.py -- --test-rig-yaml ../../test-rig-rpi.yaml + > ipython3 --pdb uart_ascii_test.py diff --git a/docs/thermistors.md b/docs/thermistors.md index ec13679e5..144734fe6 100644 --- a/docs/thermistors.md +++ b/docs/thermistors.md @@ -4,11 +4,11 @@ Thermistors are elements that change their resistance based on the temperature. They can be used to electrically measure temperature. The ODrive itself has thermistors on board near the FETs to ensure that they don't burn themselves out. In addition to this it's possible to connect your own thermistor to measure the temperature of the connected motors. There are two types of thermistors, Negative Temperature Coefficient (NTC) and Positive Temperature Coefficient (PTC). This indicates whether the resistance goes up or down when the temperature goes up or down. The ODrive only supports the NTC type thermistor. ## FET thermistor -The temperature of the onboard FET thermistors can be read out by using the `odrivetool` under `.fet_thermistor.temp`. The odrive will automatically start current limiting the motor when the `.fet_thermistor.config.temp_limit_lower` threshold is exceeded and once `.fet_thermistor.config.temp_limit_upper` is exceeded the ODrive will stop controlling the motor and set an error. The lower and upper threshold can be changed, but this is not recommended. +The temperature of the onboard FET thermistors can be read out by using the `odrivetool` under `.motor.fet_thermistor.temperature`. The odrive will automatically start current limiting the motor when the `.motor.fet_thermistor.config.temp_limit_lower` threshold is exceeded and once `.motor.fet_thermistor.config.temp_limit_upper` is exceeded the ODrive will stop controlling the motor and set an error. The lower and upper threshold can be changed, but this is not recommended. ## Connecting motor thermistors -To use your own thermistors with the ODrive a few things have to be clarified first. The use of your own thermistor requires one analog input pin. Under `.motor_thermistor.config` the configuration of your own thermistor is available with the following fields: +To use your own thermistors with the ODrive a few things have to be clarified first. The use of your own thermistor requires one analog input pin. Under `.motor.motor_thermistor.config` the configuration of your own thermistor is available with the following fields: * `gpio_pin`: The GPIO input in used for this thermistor. * `poly_coefficient_0` to `poly_coefficient_3`: Coefficient that needs to be set for your specific setup more on that in [Thermistor coefficients](#thermistor-coefficients). @@ -25,7 +25,7 @@ The way this works is that the thermistor is connected in series with a known re To use a thermistor with the ODrive a voltage divider circuit has to be made that uses `VCCA` as the power source with `GNDA` as the ground. The voltage divider output can be connected to a GPIO pin that supports analog input. ## Thermistor coefficients -Every thermistor and voltage divider circuit is different and thus it's necessary to let the ODrive know how to relate a voltage it measures at the GPIO pin to a temperature. The `poly_coefficient_0` to `poly_coefficient_3` under `.motor_thermistor.config` are used for this. The `odrivetool` has a convenience function `set_motor_thermistor_coeffs(axis, Rload, R_25, Beta, Tmin, Tmax)` which can be used to calculate and set these coefficients. +Every thermistor and voltage divider circuit is different and thus it's necessary to let the ODrive know how to relate a voltage it measures at the GPIO pin to a temperature. The `poly_coefficient_0` to `poly_coefficient_3` under `.motor.motor_thermistor.config` are used for this. The `odrivetool` has a convenience function `set_motor_thermistor_coeffs(axis, Rload, R_25, Beta, Tmin, Tmax)` which can be used to calculate and set these coefficients. * `axis`: Which axis do set the motor thermistor coefficients for (`odrv0.axis0` or `odrv0.axis1`). * `Rload`: The Ohm value of the resistor used in the voltage divider circuit. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 764e9975b..12c138a20 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -11,7 +11,7 @@ Table of Contents: ## Error codes -If your ODrive is not working as expected, run `odrivetool` and type `dump_errors(odrv0)` Enter. This will dump a list of all the errors that are present. To also clear all the errors, you can run `dump_errors(odrv0, True)`. +If your ODrive is not working as expected, run `odrivetool` and type `dump_errors(odrv0)` Enter. This will dump a list of all the errors that are present. To clear all the errors, you can run `odrv0.clear_errors()`. With this information you can look up the API documentation for your error(s): * Axis error flags documented [here](api/odrive.axis.error). @@ -59,7 +59,7 @@ when you call `dump_errors()`, you have a version mismatch between odrivetool an * **Linux**: Type `lsusb` to list all USB devices. Verify that your ODrive is listed. * **Linux**: Make sure you [set up your udev rules](getting-started#downloading-and-installing-tools) correctly. * **Windows**: Right-click on the start menu and open "Device Manager". Verify that your ODrive is listed. - * **Windows**: Use the [Zadig utility](http://zadig.akeo.ie/) to verify the driver is set to `libusb-win32`. Note that there are two options listed in Zadig for Odrive: `ODrive 3.x Native Interface (Interface 2)` and `ODrive 3.x CDC Interface (Interface 0)`. Only the native interface should have `libusb-win32` while the CDC interface should use `WinUSB`. + * **Windows**: Use the [Zadig utility](http://zadig.akeo.ie/) to verify the driver is set to `WinUSB` or `libusb-win32`. Note that there are two options listed in Zadig for ODrive: `ODrive 3.x Native Interface (Interface 2)` and `ODrive 3.x CDC Interface (Interface 0)`. Only the driver setting of the native interface is important to `odrivetool`. * Ensure that no other ODrive program is running * Run `odrivetools` with the `--verbose` option. * Run `PYUSB_DEBUG=debug odrivetools` to get even more log output. diff --git a/docs/uart.md b/docs/uart.md index 371774410..0dd4cf85c 100644 --- a/docs/uart.md +++ b/docs/uart.md @@ -1,12 +1,25 @@ # UART Interface -The ODrive's UART interface is enabled by default (see `odrv0.config.enable_uart`). Currently it runs both the [Native Protocol](native-protocol) and the [ASCII Protocol](ascii-protocol) at the same time. +The ODrive's UART_A interface is enabled by default with a baudrate of 115200 on the pins as shown in [Pinout](pinout). -The baudrate is 115200 by default and can be changed using `odrv0.config.uart_baudrate`. Changes to the UART configuration require a reboot to take effect. +To use UART connect it like this: + +* Tx of the ODrive <=> Rx of other device +* Rx of the ODrive <=> Tx of other device +* GND of the ODrive (use any GND pin on J3 of the ODrive) <=> GND of the other device The logic level of the ODrive is 3.3V. The GPIOs are 5V tolerant. -Pinout: -* GPIO 1: Tx (connect to Rx of other device) -* GPIO 2: Rx (connect to Tx of other device) -* GND: you must connect the grounds of the devices together. Use any GND pin on J3 of the ODrive. +You can use `odrv0.config.uart_a_baudrate` to change the baudrate and `odrv0.config.enable_uart_a` to disable/reenable UART_A. The UART_A port can run the [Native Protocol](native-protocol) or the [ASCII Protocol](ascii-protocol), but not both at the same time. You can configure this by setting `odrv0.config.uart0_protocol` to either `STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT` for the ASCII protocol or `STREAM_PROTOCOL_TYPE_FIBRE` for the native protocol. + +### How to use UART on GPIO3/4 + +If you need GPIO1/2 for some function other than UART you can disable UART_A and instead use UART_B on GPIO3/4. Here's how you do it: + + odrv0.config.enable_uart_a = False + odrv0.config.gpio1_mode = GPIO_MODE_DIGITAL + odrv0.config.gpio2_mode = GPIO_MODE_DIGITAL + odrv0.config.enable_uart_b = True + odrv0.config.gpio3_mode = GPIO_MODE_UART_B + odrv0.config.gpio4_mode = GPIO_MODE_UART_B + odrv0.reboot() diff --git a/tools/.vscode/launch.json b/tools/.vscode/launch.json index 9a36a0760..5247bf4a1 100644 --- a/tools/.vscode/launch.json +++ b/tools/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "python", "request": "launch", "stopOnEntry": true, - "pythonPath": "${command:python.pythonPath}", + "python": "${command:python.pythonPath}", "program": "${file}", "cwd": "${workspaceRoot}", "env": {}, diff --git a/tools/enums_template.j2 b/tools/enums_template.j2 index bb20ca379..58d5bfc38 100644 --- a/tools/enums_template.j2 +++ b/tools/enums_template.j2 @@ -8,7 +8,7 @@ # [[enum.fullname]] [%- for k, value in enum['values'].items() %] -[[(((enum.parent.name if enum.name in ['Error', 'Mode'] else '') + enum.name + k) | to_macro_case).ljust(40)]] = [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %] +[[((((enum.parent.name if enum.name in ['Error', 'Mode'] else '') + enum.name) | to_macro_case) + "_" + (k | to_macro_case)).ljust(40)]] = [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %] [%- endfor %] [%- endif %] [%- endfor %] diff --git a/Firmware/fibre/tools/fibre-shell b/tools/fibre-tools/fibre-shell similarity index 95% rename from Firmware/fibre/tools/fibre-shell rename to tools/fibre-tools/fibre-shell index b67654522..195591659 100755 --- a/Firmware/fibre/tools/fibre-shell +++ b/tools/fibre-tools/fibre-shell @@ -45,7 +45,6 @@ parser.set_defaults(path="usb,tcp:localhost:9910") args = parser.parse_args() logger = Logger(verbose=args.verbose) -app_shutdown_token = Event() def print_banner(): pass @@ -54,4 +53,4 @@ def print_help(args, have_devices): pass import fibre -fibre.launch_shell(args, {}, print_banner, print_help, logger, app_shutdown_token) +fibre.launch_shell(args, {}, print_banner, print_help, logger) diff --git a/Firmware/fibre/tools/interface_generator.py b/tools/fibre-tools/interface_generator.py similarity index 72% rename from Firmware/fibre/tools/interface_generator.py rename to tools/fibre-tools/interface_generator.py index 2dcf9c837..63def69d2 100644 --- a/Firmware/fibre/tools/interface_generator.py +++ b/tools/fibre-tools/interface_generator.py @@ -19,6 +19,10 @@ c_name: {type: string} brief: {type: string} doc: {type: string} + implements: + anyOf: + - {"$ref": "#/definitions/intf_type_ref"} + - {type: array, items: {"$ref": "#/definitions/intf_type_ref"}} functions: type: object additionalProperties: {"$ref": "#/definitions/function"} @@ -42,6 +46,11 @@ __column__: {type: object} additionalProperties: false + intf_type_ref: + anyOf: + - {"type": "string"} + - {"$ref": "#/definitions/interface"} + intf_or_val_type: anyOf: - {"$ref": "#/definitions/interface"} @@ -86,11 +95,15 @@ valuetypes: type: object additionalProperties: { "$ref": "#/definitions/valuetype" } + userdata: + type: object __line__: {type: object} __column__: {type: object} additionalProperties: false """)) +# TODO: detect duplicate keys in yaml dictionaries + # Source: https://stackoverflow.com/a/53647080/3621512 class SafeLineLoader(yaml.SafeLoader): pass @@ -118,10 +131,13 @@ def construct_mapping(loader, node): def get_words(string): """ - Splits a string in PascalCase into a list of lower case words + Splits a string in PascalCase or MACRO_CASE into a list of lower case words """ - regex = ''.join((re.escape(w) + '|') for w in dictionary) + '[a-z0-9]+|[A-Z][a-z0-9]*' - return [(w if w in dictionary else w.lower()) for w in re.findall(regex, string)] + if string.isupper(): + return [w.lower() for w in string.split('_')] + else: + regex = ''.join((re.escape(w) + '|') for w in dictionary) + '[a-z0-9]+|[A-Z][a-z0-9]*' + return [(w if w in dictionary else w.lower()) for w in re.findall(regex, string)] def join_name(*names, delimiter: str = '.'): """ @@ -159,56 +175,11 @@ def to_kebab_case(s): return '-'.join(get_words(s)).lower() }) enums = OrderedDict() - interfaces = OrderedDict() - -def make_property_type(typeargs): - value_type = resolve_valuetype('', typeargs['fibre.Property.type']) - mode = typeargs.get('fibre.Property.mode', 'readwrite') - name = 'Property<' + value_type['fullname'] + ', ' + mode + '>' - fullname = join_name('fibre', name) - if fullname in interfaces: - return interfaces[fullname] - - c_name = 'Property<' + ('const ' if mode == 'readonly' else '') + value_type['c_name'] + '>' - prop_type = { - 'name': name, - 'fullname': fullname, - 'purename': 'fibre.Property', - 'c_name': c_name, - 'value_type': value_type, # TODO: should be a metaarg - 'mode': mode, # TODO: should be a metaarg - 'builtin': True, - 'attributes': OrderedDict(), - 'functions': OrderedDict() - } - if mode != 'readonly': - prop_type['functions']['exchange'] = { - 'name': 'exchange', - 'fullname': join_name(fullname, 'exchange'), - 'in': OrderedDict([('obj', {'name': 'obj', 'type': {'c_name': c_name}}), ('value', {'name': 'value', 'type': value_type, 'optional': True})]), - 'out': OrderedDict([('value', {'name': 'value', 'type': value_type})]), - #'implementation': 'fibre_property_exchange<' + value_type['c_name'] + '>' - } - else: - prop_type['functions']['read'] = { - 'name': 'read', - 'fullname': join_name(fullname, 'read'), - 'in': OrderedDict([('obj', {'name': 'obj', 'type': {'c_name': c_name}})]), - 'out': OrderedDict([('value', {'name': 'value', 'type': value_type})]), - #'implementation': 'fibre_property_read<' + value_type['c_name'] + '>' - } - - interfaces[fullname] = prop_type - return prop_type - -generics = { - 'fibre.Property': make_property_type # TODO: improve generic support -} - +userdata = OrderedDict() # Arbitrary data passed from the definition file to the template def make_ref_type(interface): - name = 'Ref<' + interface['fullname'] + '>' + name = 'Ref<' + interface.fullname + '>' fullname = join_name('fibre', name) if fullname in interfaces: return interfaces[fullname] @@ -217,7 +188,7 @@ def make_ref_type(interface): 'builtin': True, 'name': name, 'fullname': fullname, - 'c_name': interface['fullname'].replace('.', 'Intf::') + 'Intf*' + 'c_name': interface.fullname.replace('.', 'Intf::') + 'Intf*' } value_types[fullname] = ref_type @@ -256,13 +227,14 @@ def regularize_attribute(parent, name, elem, c_is_class): elem['type'] = {} if 'attributes' in elem: elem['type']['attributes'] = elem.pop('attributes') if 'functions' in elem: elem['type']['functions'] = elem.pop('functions') + if 'implements' in elem: elem['type']['implements'] = elem.pop('implements') if 'c_is_class' in elem: elem['type']['c_is_class'] = elem.pop('c_is_class') if 'values' in elem: elem['type']['values'] = elem.pop('values') if 'flags' in elem: elem['type']['flags'] = elem.pop('flags') if 'nullflag' in elem: elem['type']['nullflag'] = elem.pop('nullflag') elem['name'] = name - elem['fullname'] = join_name(parent['fullname'], name) + elem['fullname'] = join_name(parent.fullname, name) elem['parent'] = parent elem['typeargs'] = elem.get('typeargs', {}) elem['c_name'] = elem.get('c_name', None) or (elem['name'] + ('_' if c_is_class else '')) @@ -273,40 +245,145 @@ def regularize_attribute(parent, name, elem, c_is_class): if isinstance(elem['type'], str) and elem['type'].startswith('readonly '): elem['typeargs']['fibre.Property.mode'] = 'readonly' elem['typeargs']['fibre.Property.type'] = elem['type'][len('readonly '):] - elem['type'] = 'fibre.Property' + elem['type'] = InterfaceRefElement(parent.fullname, None, 'fibre.Property', elem['typeargs']) if elem['typeargs']['fibre.Property.mode'] == 'readonly' and 'c_setter' in elem: elem.pop('c_setter') elif ('flags' in elem['type']) or ('values' in elem['type']): elem['typeargs']['fibre.Property.mode'] = elem['typeargs'].get('fibre.Property.mode', None) or 'readwrite' - elem['typeargs']['fibre.Property.type'] = regularize_valuetype(parent['fullname'], to_pascal_case(name), elem['type']) - elem['type'] = 'fibre.Property' + elem['typeargs']['fibre.Property.type'] = regularize_valuetype(parent.fullname, to_pascal_case(name), elem['type']) + elem['type'] = InterfaceRefElement(parent.fullname, None, 'fibre.Property', elem['typeargs']) if elem['typeargs']['fibre.Property.mode'] == 'readonly' and 'c_setter' in elem: elem.pop('c_setter') else: - elem['type'] = regularize_interface(parent['fullname'], to_pascal_case(name), elem['type']) + elem['type'] = InterfaceRefElement(parent.fullname, to_pascal_case(name), elem['type'], elem['typeargs']) return elem +class InterfaceRefElement(): + def __init__(self, scope, name, elem, typeargs): + if isinstance(elem, str): + self._intf = None + self._scope = scope + self._name = elem + else: + self._intf = InterfaceElement(scope, name, elem) + self._scope = None + self._name = None + self._typeargs = typeargs + + def resolve(self): + """ + Resolves this interface reference to an actual InterfaceElement instance. + The innermost scope is searched first. + At every scope level, if no matching interface is found, it is checked if a + matching value type exists. If so, the interface type fibre.Property + is returned. + """ + if not self._intf is None: + return self._intf + + typeargs = self._typeargs + if 'fibre.Property.type' in typeargs: + typeargs['fibre.Property.type'] = resolve_valuetype(self._scope, typeargs['fibre.Property.type']) + + scope = self._scope.split('.') + for probe_scope in [join_name(*scope[:(len(scope)-i)]) for i in range(len(scope)+1)]: + probe_name = join_name(probe_scope, self._name) + #print('probing ' + probe_name) + if probe_name in interfaces: + return interfaces[probe_name] + elif probe_name in value_types: + typeargs['fibre.Property.type'] = value_types[probe_name] + return make_property_type(typeargs) + elif probe_name in generics: + return generics[probe_name](typeargs) + + raise Exception('could not resolve type {} in {}. Known interfaces are: {}. Known value types are: {}'.format(self._name, self._scope, list(interfaces.keys()), list(value_types.keys()))) + +class InterfaceElement(): + def __init__(self, path, name, elem): + if elem is None: + elem = {} + assert(isinstance(elem, dict)) + + path = join_name(path, name) + interfaces[path] = self -def regularize_interface(path, name, elem): - if elem is None: - elem = {} - if isinstance(elem, str): - return elem # will be resolved during type resolution - #if path is None: - # max_anonymous_type = max([int((re.findall('^' + join_name(path, 'AnonymousType') + '([1-9]+)$', x) + ['0'])[0]) for x in interfaces.keys()]) - # path = 'AnonymousType' + str(max_anonymous_type + 1) - elem['name'] = split_name(name)[-1] - elem['fullname'] = path = join_name(path, name) - elem['c_name'] = elem.get('c_name', elem['fullname'].replace('.', 'Intf::')) + 'Intf' - interfaces[path] = elem - elem['functions'] = OrderedDict((name, regularize_func(path, name, func, {'obj': {'type': make_ref_type(elem)}})) - for name, func in get_dict(elem, 'functions').items()) - if not 'c_is_class' in elem: - raise Exception(elem) - treat_as_class = elem['c_is_class'] # TODO: add command line arg to make this selectively optional - elem['attributes'] = OrderedDict((name, regularize_attribute(elem, name, prop, treat_as_class)) - for name, prop in get_dict(elem, 'attributes').items()) - elem['interfaces'] = [] - elem['enums'] = [] - return elem + self.name = split_name(name)[-1] + self.fullname = path + self.c_name = elem.get('c_name', self.fullname.replace('.', 'Intf::')) + 'Intf' + + if not 'implements' in elem: + elem['implements'] = [] + elif isinstance(elem['implements'], str): + elem['implements'] = [elem['implements']] + self.implements = [InterfaceRefElement(path, None, elem, {}) for elem in elem['implements']] + self.functions = OrderedDict((name, regularize_func(path, name, func, {'obj': {'type': make_ref_type(self)}})) + for name, func in get_dict(elem, 'functions').items()) + if not 'c_is_class' in elem: + raise Exception(elem) + treat_as_class = elem['c_is_class'] # TODO: add command line arg to make this selectively optional + self.attributes = OrderedDict((name, regularize_attribute(self, name, prop, treat_as_class)) + for name, prop in get_dict(elem, 'attributes').items()) + self.interfaces = [] + self.enums = [] + + def get_all_attributes(self, stack=[]): + result = OrderedDict() + for intf in self.implements: + assert(not self in stack) + result.update(intf.get_all_attributes(stack + [self])) + result.update(self.attributes) + return result + + def get_all_functions(self, stack=[]): + result = OrderedDict() + for intf in self.implements: + assert(not self in stack) + result.update(intf.get_all_functions(stack + [self])) + result.update(self.functions) + return result + +class PropertyInterfaceElement(InterfaceElement): + def __init__(self, name, fullname, mode, value_type): + self.name = name + self.fullname = fullname + self.purename = 'fibre.Property' + self.c_name = 'Property<' + ('const ' if mode == 'readonly' else '') + value_type['c_name'] + '>' + self.value_type = value_type # TODO: should be a metaarg + self.mode = mode # TODO: should be a metaarg + self.builtin = True + self.attributes = OrderedDict() + self.functions = OrderedDict() + if mode != 'readonly': + self.functions['exchange'] = { + 'name': 'exchange', + 'fullname': join_name(fullname, 'exchange'), + 'in': OrderedDict([('obj', {'name': 'obj', 'type': {'c_name': self.c_name}}), ('value', {'name': 'value', 'type': value_type, 'optional': True})]), + 'out': OrderedDict([('value', {'name': 'value', 'type': value_type})]), + #'implementation': 'fibre_property_exchange<' + value_type['c_name'] + '>' + } + else: + self.functions['read'] = { + 'name': 'read', + 'fullname': join_name(fullname, 'read'), + 'in': OrderedDict([('obj', {'name': 'obj', 'type': {'c_name': self.c_name}})]), + 'out': OrderedDict([('value', {'name': 'value', 'type': value_type})]), + #'implementation': 'fibre_property_read<' + value_type['c_name'] + '>' + } + + interfaces[fullname] = self # TODO: not good to write to a global here + +def make_property_type(typeargs): + value_type = resolve_valuetype('', typeargs['fibre.Property.type']) + mode = typeargs.get('fibre.Property.mode', 'readwrite') + name = 'Property<' + value_type['fullname'] + ', ' + mode + '>' + fullname = join_name('fibre', name) + if fullname in interfaces: + return interfaces[fullname] + else: + return PropertyInterfaceElement(name, fullname, mode, value_type) + +generics = { + 'fibre.Property': make_property_type # TODO: improve generic support +} def regularize_valuetype(path, name, elem): if elem is None: @@ -347,34 +424,6 @@ def regularize_valuetype(path, name, elem): return elem -def resolve_interface(scope, name, typeargs): - """ - Resolves a type name (i.e. interface name or value type name) given as a - string to an interface object. The innermost scope is searched first. - At every scope level, if no matching interface is found, it is checked if a - matching value type exists. If so, the interface type fibre.Property - is returned. - """ - if not isinstance(name, str): - return name - - if 'fibre.Property.type' in typeargs: - typeargs['fibre.Property.type'] = resolve_valuetype(scope, typeargs['fibre.Property.type']) - - scope = scope.split('.') - for probe_scope in [join_name(*scope[:(len(scope)-i)]) for i in range(len(scope)+1)]: - probe_name = join_name(probe_scope, name) - #print('probing ' + probe_name) - if probe_name in interfaces: - return interfaces[probe_name] - elif probe_name in value_types: - typeargs['fibre.Property.type'] = value_types[probe_name] - return make_property_type(typeargs) - elif probe_name in generics: - return generics[probe_name](typeargs) - - raise Exception('could not resolve type {} in {}. Known interfaces are: {}. Known value types are: {}'.format(name, join_name(*scope), list(interfaces.keys()), list(value_types.keys()))) - def resolve_valuetype(scope, name): """ Resolves a type name given as a string to the type object. @@ -394,25 +443,35 @@ def resolve_valuetype(scope, name): def map_to_fibre01_type(t): if t.get('is_enum', False): - return 'int32' + max_val = max(v['value'] for v in t['values'].values()) + if max_val <= 0xff: + return 'uint8' + elif max_val <= 0xffff: + return 'uint16' + elif max_val <= 0xffffffff: + return 'uint32' + elif max_val <= 0xffffffffffffffff: + return 'uint64' + else: + raise Exception("enum with a maximum value of " + str(max_val) + " not supported") elif t['fullname'] == 'float32': return 'float' return t['fullname'] def generate_endpoint_for_property(prop, attr_bindto, idx): - prop_intf = interfaces[prop['type']['fullname']] + prop_intf = interfaces[prop['type'].fullname] endpoint = { 'id': idx, - 'function': prop_intf['functions']['read' if prop['type']['mode'] == 'readonly' else 'exchange'], + 'function': prop_intf.functions['read' if prop['type'].mode == 'readonly' else 'exchange'], 'in_bindings': OrderedDict([('obj', attr_bindto)]), 'out_bindings': OrderedDict() } endpoint_definition = { 'name': prop['name'], 'id': idx, - 'type': map_to_fibre01_type(prop['type']['value_type']), - 'access': 'r' if prop['type']['mode'] == 'readonly' else 'rw', + 'type': map_to_fibre01_type(prop['type'].value_type), + 'access': 'r' if prop['type'].mode == 'readonly' else 'rw', } return endpoint, endpoint_definition @@ -426,10 +485,10 @@ def generate_endpoint_table(intf, bindto, idx): endpoint_definitions = [] cnt = 0 - for k, prop in intf['attributes'].items(): - property_value_type = re.findall('^fibre\.Property<([^>]*), (readonly|readwrite)>$', prop['type']['fullname']) + for k, prop in intf.get_all_attributes().items(): + property_value_type = re.findall('^fibre\.Property<([^>]*), (readonly|readwrite)>$', prop['type'].fullname) #attr_bindto = join_name(bindto, bindings_map.get(join_name(intf['fullname'], k), k + ('_' if len(intf['functions']) or (intf['fullname'] in treat_as_classes) else ''))) - attr_bindto = intf['c_name'] + '::get_' + prop['name'] + '(' + bindto + ')' + attr_bindto = intf.c_name + '::get_' + prop['name'] + '(' + bindto + ')' if len(property_value_type): # Special handling for Property<...> attributes: they resolve to one single endpoint endpoint, endpoint_definition = generate_endpoint_for_property(prop, attr_bindto, idx + cnt) @@ -446,7 +505,7 @@ def generate_endpoint_table(intf, bindto, idx): }) cnt += inner_cnt - for k, func in intf['functions'].items(): + for k, func in intf.get_all_functions().items(): endpoints.append({ 'id': idx + cnt, 'function': func, @@ -459,14 +518,14 @@ def generate_endpoint_table(intf, bindto, idx): endpoint, endpoint_definition = generate_endpoint_for_property({ 'name': arg['name'], 'type': make_property_type({'fibre.Property.type': arg['type'], 'fibre.Property.mode': 'readwrite'}) - }, intf['c_name'] + '::get_' + func['name'] + '_in_' + k_arg + '_' + '(' + bindto + ')', idx + cnt + 1 + i) + }, intf.c_name + '::get_' + func['name'] + '_in_' + k_arg + '_' + '(' + bindto + ')', idx + cnt + 1 + i) endpoints.append(endpoint) in_def.append(endpoint_definition) for i, (k_arg, arg) in enumerate(func['out'].items()): endpoint, endpoint_definition = generate_endpoint_for_property({ 'name': arg['name'], 'type': make_property_type({'fibre.Property.type': arg['type'], 'fibre.Property.mode': 'readonly'}) - }, intf['c_name'] + '::get_' + func['name'] + '_out_' + k_arg + '_' + '(' + bindto + ')', idx + cnt + len(func['in']) + i) + }, intf.c_name + '::get_' + func['name'] + '_out_' + k_arg + '_' + '(' + bindto + ')', idx + cnt + len(func['in']) + i) endpoints.append(endpoint) out_def.append(endpoint_definition) @@ -529,6 +588,7 @@ def generate_endpoint_table(intf, bindto, idx): raise Exception(err.message + '\nat ' + str(list(err.absolute_path))) interfaces.update(get_dict(file_content, 'interfaces')) value_types.update(get_dict(file_content, 'valuetypes')) + userdata.update(get_dict(file_content, 'userdata')) dictionary += file_content.get('dictionary', None) or [] @@ -536,7 +596,7 @@ def generate_endpoint_table(intf, bindto, idx): # Regularize everything into a wellknown form for k, item in list(interfaces.items()): - regularize_interface('', k, item) + InterfaceElement('', k, item) for k, item in list(value_types.items()): regularize_valuetype('', k, item) @@ -549,15 +609,16 @@ def generate_endpoint_table(intf, bindto, idx): print("**Error**: Found both an interface and a value type with the name {}. This is not allowed, interfaces and value types (such as enums) share the same namespace.".format(clashing_names[0]), file=sys.stderr) sys.exit(1) -# Resolve all types into references +# Resolve all types to references for _, item in list(interfaces.items()): - for _, prop in item['attributes'].items(): - prop['type'] = resolve_interface(item['fullname'], prop['type'], prop['typeargs']) - for _, func in item['functions'].items(): + item.implements = [ref.resolve() for ref in item.implements] + for _, prop in item.attributes.items(): + prop['type'] = prop['type'].resolve() + for _, func in item.functions.items(): for _, arg in func['in'].items(): - arg['type'] = resolve_valuetype(item['fullname'], arg['type']) + arg['type'] = resolve_valuetype(item.fullname, arg['type']) for _, arg in func['out'].items(): - arg['type'] = resolve_valuetype(item['fullname'], arg['type']) + arg['type'] = resolve_valuetype(item.fullname, arg['type']) # Attach interfaces to their parents toplevel_interfaces = [] @@ -568,8 +629,8 @@ def generate_endpoint_table(intf, bindto, idx): else: if k[:-1] != ['fibre']: # TODO: remove special handling parent = interfaces[join_name(*k[:-1])] - parent['interfaces'].append(item) - item['parent'] = parent + parent.interfaces.append(item) + item.parent = parent toplevel_enums = [] for k, item in list(enums.items()): k = split_name(k) @@ -578,7 +639,7 @@ def generate_endpoint_table(intf, bindto, idx): else: if k[:-1] != ['fibre']: # TODO: remove special handling parent = interfaces[join_name(*k[:-1])] - parent['enums'].append(item) + parent.enums.append(item) item['parent'] = parent @@ -638,7 +699,7 @@ def token_transform(token): if not attr is None: return attribute_transform(token, attr) - print('Warning: cannot resolve "{}" in {}'.format(token, interface['fullname'])) + print('Warning: cannot resolve "{}" in {}'.format(token, interface.fullname)) return "`" + token + "`" return re.sub(r'`([A-Za-z\._]+)`', token_transform, text) @@ -661,6 +722,7 @@ def token_transform(token): 'interfaces': interfaces, 'value_types': value_types, 'toplevel_interfaces': toplevel_interfaces, + 'userdata': userdata, 'endpoints': endpoints, 'embedded_endpoint_definitions': embedded_endpoint_definitions } diff --git a/tools/odrive/__init__.py b/tools/odrive/__init__.py index f962cc9d8..97fe55437 100644 --- a/tools/odrive/__init__.py +++ b/tools/odrive/__init__.py @@ -1,20 +1,56 @@ import os import sys -sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname( - os.path.dirname(os.path.realpath(__file__)))), - "Firmware", "fibre", "python")) - -# Syntactic sugar to make usage more intuative. -# Try/pass used to break install-time dep issues -try: - import fibre - find_any = fibre.find_any - find_all = fibre.find_all -except: - pass + +# We want to use the fibre package that is included with the odrive package +# in order to avoid any version mismatch issues, +sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "pyfibre")) + +import fibre # Standard convention is to add a __version__ attribute to the package from .version import get_version_str __version__ = get_version_str() del get_version_str + +from .utils import get_serial_number_str, get_serial_number_str_sync +import threading + +default_usb_search_path = 'usb:idVendor=0x1209,idProduct=0x0D32,bInterfaceClass=0,bInterfaceSubClass=1,bInterfaceProtocol=0' +default_search_path = default_usb_search_path + +def find_any(path=default_search_path, serial_number=None, + search_cancellation_token=None, channel_termination_token=None, + timeout=None, logger=fibre.Logger(verbose=False)): + """ + Blocks until the first matching ODrive object is connected and then returns that object + """ + + result = [] + + done_signal = fibre.Event(search_cancellation_token) + channel_termination_token = fibre.Event(channel_termination_token) + + async def discovered_object(obj): + if not (serial_number is None) and ((await get_serial_number_str(obj)) != serial_number): + return # ignore this device + + obj._on_lost.add_done_callback(lambda x: channel_termination_token.set()) + result.append(obj) + done_signal.set() + + def domain_thread(): + with fibre.Domain(path) as domain: + discovery = domain.run_discovery(discovered_object) + channel_termination_token.wait() + discovery.stop() + + threading.Thread(target=domain_thread).start() + + try: + done_signal.wait(timeout=timeout) + except: + channel_termination_token.set() + raise + + return result[0] if len(result) > 0 else None diff --git a/tools/odrive/configuration.py b/tools/odrive/configuration.py index ff220a8dc..afc18adc5 100644 --- a/tools/odrive/configuration.py +++ b/tools/odrive/configuration.py @@ -2,39 +2,58 @@ import json import os import tempfile -import fibre.remote_object +import fibre.libfibre +import odrive from odrive.utils import OperationAbortedException, yes_no_prompt -def get_dict(obj, is_config_object): +def obj_to_path(root, obj): + for k in dir(root): + v = getattr(root, k) + if not k.startswith('_') and isinstance(v, fibre.libfibre.RemoteObject): + if v == obj: + return k + subpath = obj_to_path(v, obj) + if not subpath is None: + return k + "." + subpath + return None + +def get_dict(root, obj, is_config_object): result = {} - for (k,v) in obj._remote_attributes.items(): - if isinstance(v, fibre.remote_object.RemoteProperty) and is_config_object: - result[k] = v.get_value() - elif isinstance(v, fibre.remote_object.RemoteObject): - sub_dict = get_dict(v, k == 'config') + + for k in dir(obj): + v = getattr(obj, k) + if k.startswith('_') and k.endswith('_property') and is_config_object: + v = v.read() + if isinstance(v, fibre.libfibre.RemoteObject): + v = obj_to_path(root, v) + result[k[1:-9]] = v + elif not k.startswith('_') and isinstance(v, fibre.libfibre.RemoteObject): + sub_dict = get_dict(root, v, (k == 'config') or is_config_object) if sub_dict != {}: result[k] = sub_dict + return result def set_dict(obj, path, config_dict): errors = [] for (k,v) in config_dict.items(): name = path + ("." if path != "" else "") + k - if not k in obj._remote_attributes: + if not k in dir(obj): errors.append("Could not restore {}: property not found on device".format(name)) continue - remote_attribute = obj._remote_attributes[k] - if isinstance(remote_attribute, fibre.remote_object.RemoteObject): - errors += set_dict(remote_attribute, name, v) + if isinstance(v, dict): + errors += set_dict(getattr(obj, k), name, v) else: try: - remote_attribute.set_value(v) + remote_attribute = getattr(obj, '_' + k + '_property') + #if isinstance(v, str) and isinstance() + remote_attribute.exchange(v) except Exception as ex: errors.append("Could not restore {}: {}".format(name, str(ex))) return errors def get_temp_config_filename(device): - serial_number = fibre.utils.get_serial_number_str(device) + serial_number = odrive.get_serial_number_str_sync(device) safe_serial_number = ''.join(filter(str.isalnum, serial_number)) return os.path.join(tempfile.gettempdir(), 'odrive-config-{}.json'.format(safe_serial_number)) @@ -54,7 +73,7 @@ def backup_config(device, filename, logger): if not yes_no_prompt("The file {} already exists. Do you want to override it?".format(filename), True): raise OperationAbortedException() - data = get_dict(device, False) + data = get_dict(device, device, False) with open(filename, 'w') as file: json.dump(data, file) logger.info("Configuration saved.") @@ -78,5 +97,8 @@ def restore_config(device, filename, logger): if errors: logger.warn("Some of the configuration could not be restored.") - device.save_configuration() + try: + device.save_configuration() + except fibre.libfibre.ObjectLostError: + pass # Saving configuration makes the device reboot logger.info("Configuration restored.") diff --git a/tools/odrive/dfu.py b/tools/odrive/dfu.py index 561ed776e..b7aab470c 100755 --- a/tools/odrive/dfu.py +++ b/tools/odrive/dfu.py @@ -223,19 +223,19 @@ def put_into_dfu_mode(device, cancellation_token): Puts the specified device into DFU mode """ if not hasattr(device, "enter_dfu_mode"): - print("The firmware on device {} cannot soft enter DFU mode.\n" + print("The firmware on device {:08X} cannot soft enter DFU mode.\n" "Please remove power, put the DFU switch into DFU mode,\n" "then apply power again. Then try again.\n" "If it still doesn't work, you can try to use the DeFuse app or \n" "dfu-util, see the odrive documentation.\n" "You can also flash the firmware using STLink (`make flash`)" - .format(device.__channel__.usb_device.serial_number)) + .format(device.serial_number)) return - print("Putting device {} into DFU mode...".format(device.__channel__.usb_device.serial_number)) + print("Putting device {:08X} into DFU mode...".format(device.serial_number)) try: device.enter_dfu_mode() - except fibre.ChannelBrokenException: + except fibre.ObjectLostError: pass # this is expected because the device reboots if platform.system() == "Windows": show_deferred_message("Still waiting for the device to reappear.\n" @@ -265,6 +265,7 @@ def update_device(device, firmware, logger, cancellation_token): """ if isinstance(device, usb.core.Device): + found_in_dfu = True serial_number = device.serial_number dfudev = DfuDevice(device) if (logger._verbose): @@ -281,7 +282,8 @@ def update_device(device, firmware, logger, cancellation_token): else: hw_version = (0, 0, 0) else: - serial_number = device.__channel__.usb_device.serial_number + found_in_dfu = False + serial_number = "{:08X}".format(device.serial_number) dfudev = None # Read hardware version as reported from firmware @@ -379,9 +381,11 @@ def update_device(device, firmware, logger, cancellation_token): # Erase try: - for i, (sector, data) in enumerate(touched_sectors): - print("Erasing... (sector {}/{}) \r".format(i, len(touched_sectors)), end='', flush=True) - dfudev.erase_sector(sector) + internal_flash_sectors = [sector for sector in dfudev.sectors if sector['name'] == 'Internal Flash'] + for i, sector in enumerate(dfudev.sectors): + if sector['name'] == 'Internal Flash': + print("Erasing... (sector {}/{}) \r".format(i, len(internal_flash_sectors)), end='', flush=True) + dfudev.erase_sector(sector) print('Erasing... done \r', end='', flush=True) finally: print('', flush=True) @@ -421,13 +425,15 @@ def update_device(device, firmware, logger, cancellation_token): # Jump to application dfudev.jump_to_application(0x08000000) - logger.info("Waiting for the device to reappear...") - device = odrive.find_any("usb", serial_number, - cancellation_token, cancellation_token, timeout=30) + if not found_in_dfu: + logger.info("Waiting for the device to reappear...") + device = odrive.find_any(odrive.default_usb_search_path, serial_number, + cancellation_token, cancellation_token, timeout=30) - if do_backup_config: - odrive.configuration.restore_config(device, None, logger) - os.remove(odrive.configuration.get_temp_config_filename(device)) + if do_backup_config: + temp_config_filename = odrive.configuration.get_temp_config_filename(device) + odrive.configuration.restore_config(device, None, logger) + os.remove(temp_config_filename) logger.success("Device firmware update successful.") @@ -455,7 +461,7 @@ def find_device_in_dfu_mode_thread(): # Scan for ODrives not in DFU mode # We only scan on USB because DFU is only implemented over USB - devices[1] = odrive.find_any("usb", serial_number, + devices[1] = odrive.find_any(odrive.default_usb_search_path, serial_number, find_odrive_cancellation_token, cancellation_token) find_odrive_cancellation_token.set() diff --git a/tools/odrive/dfuse/DfuDevice.py b/tools/odrive/dfuse/DfuDevice.py index 57035715e..49a17b291 100644 --- a/tools/odrive/dfuse/DfuDevice.py +++ b/tools/odrive/dfuse/DfuDevice.py @@ -4,6 +4,7 @@ import array import time from odrive.dfuse.DfuState import DfuState +import math DFU_REQUEST_SEND = 0x21 DFU_REQUEST_RECEIVE = 0xa1 @@ -189,7 +190,7 @@ def write_sector(self, sector, data): self.set_alternate_safe(sector['alt']) self.set_address_safe(sector['addr']) - transfer_size = fractions.gcd(sector['len'], MAX_TRANSFER_SIZE) + transfer_size = math.gcd(sector['len'], MAX_TRANSFER_SIZE) blocks = [data[i:i + transfer_size] for i in range(0, len(data), transfer_size)] for blocknum, block in enumerate(blocks): @@ -208,7 +209,7 @@ def read_sector(self, sector): self.set_alternate_safe(sector['alt']) self.set_address_safe(sector['addr']) - transfer_size = fractions.gcd(sector['len'], MAX_TRANSFER_SIZE) + transfer_size = math.gcd(sector['len'], MAX_TRANSFER_SIZE) #blocknum_offset = int((sector['addr'] - sector['baseaddr']) / transfer_size) diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index c836f39d1..292ee3be1 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -3,8 +3,32 @@ # To regenerate this file, nagivate to the top level of the ODrive repository and run: # python Firmware/interface_generator_stub.py --definitions Firmware/odrive-interface.yaml --template tools/enums_template.j2 --output tools/odrive/enums.py +# ODrive.GpioMode +GPIO_MODE_DIGITAL = 0 +GPIO_MODE_DIGITAL_PULL_UP = 1 +GPIO_MODE_DIGITAL_PULL_DOWN = 2 +GPIO_MODE_ANALOG_IN = 3 +GPIO_MODE_UART_A = 4 +GPIO_MODE_UART_B = 5 +GPIO_MODE_UART_C = 6 +GPIO_MODE_CAN_A = 7 +GPIO_MODE_I2C_A = 8 +GPIO_MODE_SPI_A = 9 +GPIO_MODE_PWM = 10 +GPIO_MODE_ENC0 = 11 +GPIO_MODE_ENC1 = 12 +GPIO_MODE_ENC2 = 13 +GPIO_MODE_MECH_BRAKE = 14 +GPIO_MODE_STATUS = 15 + +# ODrive.StreamProtocolType +STREAM_PROTOCOL_TYPE_FIBRE = 0 +STREAM_PROTOCOL_TYPE_ASCII = 1 +STREAM_PROTOCOL_TYPE_STDOUT = 2 +STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT = 3 + # ODrive.Can.Protocol -PROTOCOL_SIMPLE = 0 +PROTOCOL_SIMPLE = 0x00000001 # ODrive.Axis.AxisState AXIS_STATE_UNDEFINED = 0 @@ -12,17 +36,14 @@ AXIS_STATE_STARTUP_SEQUENCE = 2 AXIS_STATE_FULL_CALIBRATION_SEQUENCE = 3 AXIS_STATE_MOTOR_CALIBRATION = 4 -AXIS_STATE_SENSORLESS_CONTROL = 5 AXIS_STATE_ENCODER_INDEX_SEARCH = 6 AXIS_STATE_ENCODER_OFFSET_CALIBRATION = 7 AXIS_STATE_CLOSED_LOOP_CONTROL = 8 AXIS_STATE_LOCKIN_SPIN = 9 AXIS_STATE_ENCODER_DIR_FIND = 10 AXIS_STATE_HOMING = 11 - -# ODrive.ThermistorCurrentLimiter.Error -THERMISTOR_CURRENT_LIMITER_ERROR_NONE = 0x00000000 -THERMISTOR_CURRENT_LIMITER_ERROR_OVER_TEMP = 0x00000001 +AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION = 12 +AXIS_STATE_ENCODER_HALL_PHASE_CALIBRATION = 13 # ODrive.Encoder.Mode ENCODER_MODE_INCREMENTAL = 0 @@ -31,6 +52,8 @@ ENCODER_MODE_SPI_ABS_CUI = 256 ENCODER_MODE_SPI_ABS_AMS = 257 ENCODER_MODE_SPI_ABS_AEAT = 258 +ENCODER_MODE_SPI_ABS_RLS = 259 +ENCODER_MODE_SPI_ABS_MA732 = 260 # ODrive.Controller.ControlMode CONTROL_MODE_VOLTAGE_CONTROL = 0 @@ -47,12 +70,24 @@ INPUT_MODE_TRAP_TRAJ = 5 INPUT_MODE_TORQUE_RAMP = 6 INPUT_MODE_MIRROR = 7 +INPUT_MODE_TUNING = 8 # ODrive.Motor.MotorType MOTOR_TYPE_HIGH_CURRENT = 0 MOTOR_TYPE_GIMBAL = 2 MOTOR_TYPE_ACIM = 3 +# ODrive.Error +ODRIVE_ERROR_NONE = 0x00000000 +ODRIVE_ERROR_CONTROL_ITERATION_MISSED = 0x00000001 +ODRIVE_ERROR_DC_BUS_UNDER_VOLTAGE = 0x00000002 +ODRIVE_ERROR_DC_BUS_OVER_VOLTAGE = 0x00000004 +ODRIVE_ERROR_DC_BUS_OVER_REGEN_CURRENT = 0x00000008 +ODRIVE_ERROR_DC_BUS_OVER_CURRENT = 0x00000010 +ODRIVE_ERROR_BRAKE_DEADTIME_VIOLATION = 0x00000020 +ODRIVE_ERROR_BRAKE_DUTY_CYCLE_NAN = 0x00000040 +ODRIVE_ERROR_INVALID_BRAKE_RESISTANCE = 0x00000080 + # ODrive.Can.Error CAN_ERROR_NONE = 0x00000000 CAN_ERROR_DUPLICATE_CAN_IDS = 0x00000001 @@ -60,66 +95,43 @@ # ODrive.Axis.Error AXIS_ERROR_NONE = 0x00000000 AXIS_ERROR_INVALID_STATE = 0x00000001 -AXIS_ERROR_DC_BUS_UNDER_VOLTAGE = 0x00000002 -AXIS_ERROR_DC_BUS_OVER_VOLTAGE = 0x00000004 -AXIS_ERROR_CURRENT_MEASUREMENT_TIMEOUT = 0x00000008 -AXIS_ERROR_BRAKE_RESISTOR_DISARMED = 0x00000010 -AXIS_ERROR_MOTOR_DISARMED = 0x00000020 -AXIS_ERROR_MOTOR_FAILED = 0x00000040 -AXIS_ERROR_SENSORLESS_ESTIMATOR_FAILED = 0x00000080 -AXIS_ERROR_ENCODER_FAILED = 0x00000100 -AXIS_ERROR_CONTROLLER_FAILED = 0x00000200 -AXIS_ERROR_POS_CTRL_DURING_SENSORLESS = 0x00000400 AXIS_ERROR_WATCHDOG_TIMER_EXPIRED = 0x00000800 AXIS_ERROR_MIN_ENDSTOP_PRESSED = 0x00001000 AXIS_ERROR_MAX_ENDSTOP_PRESSED = 0x00002000 AXIS_ERROR_ESTOP_REQUESTED = 0x00004000 AXIS_ERROR_HOMING_WITHOUT_ENDSTOP = 0x00020000 AXIS_ERROR_OVER_TEMP = 0x00040000 - -# ODrive.Axis.LockinState -LOCKIN_STATE_INACTIVE = 0 -LOCKIN_STATE_RAMP = 1 -LOCKIN_STATE_ACCELERATE = 2 -LOCKIN_STATE_CONST_VEL = 3 +AXIS_ERROR_UNKNOWN_POSITION = 0x00080000 # ODrive.Motor.Error MOTOR_ERROR_NONE = 0x00000000 MOTOR_ERROR_PHASE_RESISTANCE_OUT_OF_RANGE = 0x00000001 MOTOR_ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE = 0x00000002 -MOTOR_ERROR_ADC_FAILED = 0x00000004 MOTOR_ERROR_DRV_FAULT = 0x00000008 MOTOR_ERROR_CONTROL_DEADLINE_MISSED = 0x00000010 -MOTOR_ERROR_NOT_IMPLEMENTED_MOTOR_TYPE = 0x00000020 -MOTOR_ERROR_BRAKE_CURRENT_OUT_OF_RANGE = 0x00000040 MOTOR_ERROR_MODULATION_MAGNITUDE = 0x00000080 -MOTOR_ERROR_BRAKE_DEADTIME_VIOLATION = 0x00000100 -MOTOR_ERROR_UNEXPECTED_TIMER_CALLBACK = 0x00000200 MOTOR_ERROR_CURRENT_SENSE_SATURATION = 0x00000400 MOTOR_ERROR_CURRENT_LIMIT_VIOLATION = 0x00001000 -MOTOR_ERROR_BRAKE_DUTY_CYCLE_NAN = 0x00002000 -MOTOR_ERROR_DC_BUS_OVER_REGEN_CURRENT = 0x00004000 -MOTOR_ERROR_DC_BUS_OVER_CURRENT = 0x00008000 - -# ODrive.Motor.ArmedState -ARMED_STATE_DISARMED = 0 -ARMED_STATE_WAITING_FOR_TIMINGS = 1 -ARMED_STATE_WAITING_FOR_UPDATE = 2 -ARMED_STATE_ARMED = 3 - -# ODrive.Motor.GateDriver.DrvFault -DRV_FAULT_NO_FAULT = 0x00000000 -DRV_FAULT_FET_LOW_C_OVERCURRENT = 0x00000001 -DRV_FAULT_FET_HIGH_C_OVERCURRENT = 0x00000002 -DRV_FAULT_FET_LOW_B_OVERCURRENT = 0x00000004 -DRV_FAULT_FET_HIGH_B_OVERCURRENT = 0x00000008 -DRV_FAULT_FET_LOW_A_OVERCURRENT = 0x00000010 -DRV_FAULT_FET_HIGH_A_OVERCURRENT = 0x00000020 -DRV_FAULT_OVERTEMPERATURE_WARNING = 0x00000040 -DRV_FAULT_OVERTEMPERATURE_SHUTDOWN = 0x00000080 -DRV_FAULT_P_VDD_UNDERVOLTAGE = 0x00000100 -DRV_FAULT_G_VDD_UNDERVOLTAGE = 0x00000200 -DRV_FAULT_G_VDD_OVERVOLTAGE = 0x00000400 +MOTOR_ERROR_MODULATION_IS_NAN = 0x00010000 +MOTOR_ERROR_MOTOR_THERMISTOR_OVER_TEMP = 0x00020000 +MOTOR_ERROR_FET_THERMISTOR_OVER_TEMP = 0x00040000 +MOTOR_ERROR_TIMER_UPDATE_MISSED = 0x00080000 +MOTOR_ERROR_CURRENT_MEASUREMENT_UNAVAILABLE = 0x00100000 +MOTOR_ERROR_CONTROLLER_FAILED = 0x00200000 +MOTOR_ERROR_I_BUS_OUT_OF_RANGE = 0x00400000 +MOTOR_ERROR_BRAKE_RESISTOR_DISARMED = 0x00800000 +MOTOR_ERROR_SYSTEM_LEVEL = 0x01000000 +MOTOR_ERROR_BAD_TIMING = 0x02000000 +MOTOR_ERROR_UNKNOWN_PHASE_ESTIMATE = 0x04000000 +MOTOR_ERROR_UNKNOWN_PHASE_VEL = 0x08000000 +MOTOR_ERROR_UNKNOWN_TORQUE = 0x10000000 +MOTOR_ERROR_UNKNOWN_CURRENT_COMMAND = 0x20000000 +MOTOR_ERROR_UNKNOWN_CURRENT_MEASUREMENT = 0x40000000 +MOTOR_ERROR_UNKNOWN_VBUS_VOLTAGE = 0x80000000 +MOTOR_ERROR_UNKNOWN_VOLTAGE_COMMAND = 0x100000000 +MOTOR_ERROR_UNKNOWN_GAINS = 0x200000000 +MOTOR_ERROR_CONTROLLER_INITIALIZING = 0x400000000 +MOTOR_ERROR_UNBALANCED_PHASES = 0x800000000 # ODrive.Controller.Error CONTROLLER_ERROR_NONE = 0x00000000 @@ -129,6 +141,8 @@ CONTROLLER_ERROR_INVALID_MIRROR_AXIS = 0x00000008 CONTROLLER_ERROR_INVALID_LOAD_ENCODER = 0x00000010 CONTROLLER_ERROR_INVALID_ESTIMATE = 0x00000020 +CONTROLLER_ERROR_INVALID_CIRCULAR_RANGE = 0x00000040 +CONTROLLER_ERROR_SPINOUT_DETECTED = 0x00000080 # ODrive.Encoder.Error ENCODER_ERROR_NONE = 0x00000000 @@ -141,7 +155,9 @@ ENCODER_ERROR_ABS_SPI_TIMEOUT = 0x00000040 ENCODER_ERROR_ABS_SPI_COM_FAIL = 0x00000080 ENCODER_ERROR_ABS_SPI_NOT_READY = 0x00000100 +ENCODER_ERROR_HALL_NOT_CALIBRATED_YET = 0x00000200 # ODrive.SensorlessEstimator.Error SENSORLESS_ESTIMATOR_ERROR_NONE = 0x00000000 SENSORLESS_ESTIMATOR_ERROR_UNSTABLE_GAIN = 0x00000001 +SENSORLESS_ESTIMATOR_ERROR_UNKNOWN_CURRENT_MEASUREMENT = 0x00000002 diff --git a/tools/odrive/pyfibre/.gitattributes b/tools/odrive/pyfibre/.gitattributes new file mode 100644 index 000000000..4a3829382 --- /dev/null +++ b/tools/odrive/pyfibre/.gitattributes @@ -0,0 +1,3 @@ +libfibre*.so filter=lfs diff=lfs merge=lfs -text +libfibre*.dll filter=lfs diff=lfs merge=lfs -text +libfibre*.dylib filter=lfs diff=lfs merge=lfs -text diff --git a/Firmware/fibre/python/.gitignore b/tools/odrive/pyfibre/.gitignore similarity index 100% rename from Firmware/fibre/python/.gitignore rename to tools/odrive/pyfibre/.gitignore diff --git a/tools/odrive/pyfibre/README.md b/tools/odrive/pyfibre/README.md new file mode 100644 index 000000000..cfaec2885 --- /dev/null +++ b/tools/odrive/pyfibre/README.md @@ -0,0 +1,17 @@ +# PyFibre + +This directory provides Python bindings for [Fibre](https://github.com/samuelsadok/fibre). Its home is located [here](https://github.com/samuelsadok/fibre/tree/master/python). There's also a standalone repository for this directory [here](https://github.com/samuelsadok/pyfibre). + +## Current Status + +Currently only client-side features are implemented, that means you can discover objects but you cannot publish objects. + +## How to use + +```python +import fibre + +with fibre.Domain("tcp-client:address=localhost,port=14220") as domain: + obj = domain.discover_one() + obj.test_function() +``` diff --git a/tools/odrive/pyfibre/fibre/__init__.py b/tools/odrive/pyfibre/fibre/__init__.py new file mode 100644 index 000000000..1ee712e92 --- /dev/null +++ b/tools/odrive/pyfibre/fibre/__init__.py @@ -0,0 +1,4 @@ + +from .utils import Event, Logger, TimeoutError +from .shell import launch_shell +from .libfibre import Domain, ObjectLostError diff --git a/tools/odrive/pyfibre/fibre/libfibre-linux-aarch64.so b/tools/odrive/pyfibre/fibre/libfibre-linux-aarch64.so new file mode 100755 index 000000000..29d0665d6 --- /dev/null +++ b/tools/odrive/pyfibre/fibre/libfibre-linux-aarch64.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ab6280eac8e1a2c029afc7765335a3c865145e6c3938339ac0c3176042e2236 +size 712336 diff --git a/tools/odrive/pyfibre/fibre/libfibre-linux-amd64.so b/tools/odrive/pyfibre/fibre/libfibre-linux-amd64.so new file mode 100755 index 000000000..9b390ddc1 --- /dev/null +++ b/tools/odrive/pyfibre/fibre/libfibre-linux-amd64.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6770fc8b6c821e7489cde5e0f8f33df23a8b1925f1ec6af071490e5d5e0e9753 +size 753000 diff --git a/tools/odrive/pyfibre/fibre/libfibre-linux-armhf.so b/tools/odrive/pyfibre/fibre/libfibre-linux-armhf.so new file mode 100755 index 000000000..b4ab9b0ff --- /dev/null +++ b/tools/odrive/pyfibre/fibre/libfibre-linux-armhf.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5eff2f0530008595cd65d19b866f3c2dbc902e36c61af57357107129721aaf14 +size 782696 diff --git a/tools/odrive/pyfibre/fibre/libfibre-macos-x86.dylib b/tools/odrive/pyfibre/fibre/libfibre-macos-x86.dylib new file mode 100644 index 000000000..eaf781cbf --- /dev/null +++ b/tools/odrive/pyfibre/fibre/libfibre-macos-x86.dylib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e477eb89031c3f4b48b148c1ee59f378f33e2b777b0cd0f36dc25060b5b630f3 +size 676524 diff --git a/tools/odrive/pyfibre/fibre/libfibre-windows-amd64.dll b/tools/odrive/pyfibre/fibre/libfibre-windows-amd64.dll new file mode 100755 index 000000000..7b22d6803 --- /dev/null +++ b/tools/odrive/pyfibre/fibre/libfibre-windows-amd64.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d5fc21f3858d20dc9776b85c9ae3e99840c1c68a191329ee22ef79bdd79cc7e +size 1231886 diff --git a/tools/odrive/pyfibre/fibre/libfibre.py b/tools/odrive/pyfibre/fibre/libfibre.py new file mode 100644 index 000000000..8b922bef7 --- /dev/null +++ b/tools/odrive/pyfibre/fibre/libfibre.py @@ -0,0 +1,1059 @@ +#!/bin/python + +from ctypes import * +import asyncio +import os +from itertools import count, takewhile +import struct +from types import MethodType +import concurrent +import threading +import time +import platform +from .utils import Logger, Event +import sys + +# Enable this for better tracebacks in some cases +#import tracemalloc +#tracemalloc.start(10) + +lib_names = { + ('Linux', 'x86_64'): 'libfibre-linux-amd64.so', + ('Linux', 'armv7l'): 'libfibre-linux-armhf.so', + ('Linux', 'aarch64'): 'libfibre-linux-aarch64.so', + ('Windows', 'AMD64'): 'libfibre-windows-amd64.dll', + ('Darwin', 'x86_64'): 'libfibre-macos-x86.dylib' +} + +system_desc = (platform.system(), platform.machine()) + +script_dir = os.path.dirname(os.path.realpath(__file__)) +fibre_cpp_paths = [ + os.path.join(os.path.dirname(os.path.dirname(script_dir)), "cpp"), + os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(script_dir)))), "Firmware", "fibre-cpp") +] + +def get_first(lst, predicate, default): + for item in lst: + if predicate(item): + return item + return default + +if not system_desc in lib_names: + fibre_cpp_path = get_first(fibre_cpp_paths, os.path.isdir, None) + + if fibre_cpp_path is None: + instructions = ("Go to https://github.com/samuelsadok/fibre-cpp for " + "instructions on how to compile libfibre. Once you have compiled it, " + "add it to this folder.") + else: + instructions = ("Go to {} and run `make`. Then edit this file (libfibre.py) " + "to include the name of the binary that was generated by `make`.".format(fibre_cpp_path)) + + raise ModuleNotFoundError("libfibre is not supported on your platform ({} {}). {}".format(*system_desc, instructions)) + +lib_name = lib_names[system_desc] +search_paths = fibre_cpp_paths + [script_dir] + +lib_path = get_first( + (os.path.join(p, lib_name) for p in search_paths), + os.path.isfile, None) + +if lib_path is None: + raise ModuleNotFoundError("{} was not found in {}".format(lib_name, search_paths)) + +if os.path.getsize(lib_path) < 1000: + raise ModuleNotFoundError("{} is too small. Did you forget to init git lfs? Try this:\n" + " 1. Install git lfs (https://git-lfs.github.com/)\n" + " 2. Run `cd {}`\n" + " 3. Run `git lfs install`\n" + " 4. Run `git lfs pull`".format(lib_path, os.path.dirname(lib_path))) + +if os.name == 'nt': + dll_dir = os.path.dirname(lib_path) + try: + # New way in python 3.8+ + os.add_dll_directory(dll_dir) + except: + os.environ['PATH'] = dll_dir + os.pathsep + os.environ['PATH'] + lib = windll.LoadLibrary(lib_path) +else: + lib = cdll.LoadLibrary(lib_path) + +# libfibre definitions --------------------------------------------------------# + +PostSignature = CFUNCTYPE(c_int, CFUNCTYPE(None, c_void_p), POINTER(c_int)) +RegisterEventSignature = CFUNCTYPE(c_int, c_int, c_uint32, CFUNCTYPE(None, c_void_p, c_int), POINTER(c_int)) +DeregisterEventSignature = CFUNCTYPE(c_int, c_int) +CallLaterSignature = CFUNCTYPE(c_void_p, c_float, CFUNCTYPE(None, c_void_p), POINTER(c_int)) +CancelTimerSignature = CFUNCTYPE(c_int, c_void_p) + +OnFoundObjectSignature = CFUNCTYPE(None, c_void_p, c_void_p, c_void_p) +OnLostObjectSignature = CFUNCTYPE(None, c_void_p, c_void_p) +OnStoppedSignature = CFUNCTYPE(None, c_void_p, c_int) + +OnAttributeAddedSignature = CFUNCTYPE(None, c_void_p, c_void_p, c_void_p, c_size_t, c_void_p, c_void_p, c_size_t) +OnAttributeRemovedSignature = CFUNCTYPE(None, c_void_p, c_void_p) +OnFunctionAddedSignature = CFUNCTYPE(None, c_void_p, c_void_p, c_void_p, c_size_t, POINTER(c_char_p), POINTER(c_char_p), POINTER(c_char_p), POINTER(c_char_p)) +OnFunctionRemovedSignature = CFUNCTYPE(None, c_void_p, c_void_p) + +OnCallCompletedSignature = CFUNCTYPE(c_int, c_void_p, c_int, c_void_p, c_void_p, POINTER(c_void_p), POINTER(c_size_t), POINTER(c_void_p), POINTER(c_size_t)) +OnTxCompletedSignature = CFUNCTYPE(None, c_void_p, c_void_p, c_int, c_void_p) +OnRxCompletedSignature = CFUNCTYPE(None, c_void_p, c_void_p, c_int, c_void_p) + +kFibreOk = 0 +kFibreBusy = 1 +kFibreCancelled = 2 +kFibreClosed = 3 +kFibreInvalidArgument = 4 +kFibreInternalError = 5 +kFibreProtocolError = 6 +kFibreHostUnreachable = 7 + +class LibFibreVersion(Structure): + _fields_ = [ + ("major", c_uint16), + ("minor", c_uint16), + ("patch", c_uint16), + ] + + def __repr__(self): + return "{}.{}.{}".format(self.major, self.minor, self.patch) + +class LibFibreEventLoop(Structure): + _fields_ = [ + ("post", PostSignature), + ("register_event", RegisterEventSignature), + ("deregister_event", DeregisterEventSignature), + ("call_later", CallLaterSignature), + ("cancel_timer", CancelTimerSignature), + ] + +libfibre_get_version = lib.libfibre_get_version +libfibre_get_version.argtypes = [] +libfibre_get_version.restype = POINTER(LibFibreVersion) + +version = libfibre_get_version().contents +if (version.major, version.minor) != (0, 1): + raise Exception("Incompatible libfibre version: {}".format(version)) + +libfibre_open = lib.libfibre_open +libfibre_open.argtypes = [LibFibreEventLoop] +libfibre_open.restype = c_void_p + +libfibre_close = lib.libfibre_close +libfibre_close.argtypes = [c_void_p] +libfibre_close.restype = None + +libfibre_open_domain = lib.libfibre_open_domain +libfibre_open_domain.argtypes = [c_void_p, c_char_p, c_size_t] +libfibre_open_domain.restype = c_void_p + +libfibre_close_domain = lib.libfibre_close_domain +libfibre_close_domain.argtypes = [c_void_p] +libfibre_close_domain.restype = None + +libfibre_start_discovery = lib.libfibre_start_discovery +libfibre_start_discovery.argtypes = [c_void_p, c_void_p, OnFoundObjectSignature, OnLostObjectSignature, OnStoppedSignature, c_void_p] +libfibre_start_discovery.restype = None + +libfibre_stop_discovery = lib.libfibre_stop_discovery +libfibre_stop_discovery.argtypes = [c_void_p] +libfibre_stop_discovery.restype = None + +libfibre_subscribe_to_interface = lib.libfibre_subscribe_to_interface +libfibre_subscribe_to_interface.argtypes = [c_void_p, OnAttributeAddedSignature, OnAttributeRemovedSignature, OnFunctionAddedSignature, OnFunctionRemovedSignature, c_void_p] +libfibre_subscribe_to_interface.restype = None + +libfibre_get_attribute = lib.libfibre_get_attribute +libfibre_get_attribute.argtypes = [c_void_p, c_void_p, POINTER(c_void_p)] +libfibre_get_attribute.restype = c_int + +libfibre_call = lib.libfibre_call +libfibre_call.argtypes = [c_void_p, POINTER(c_void_p), c_int, c_void_p, c_size_t, c_void_p, c_size_t, POINTER(c_void_p), POINTER(c_void_p), OnCallCompletedSignature, c_void_p] +libfibre_call.restype = c_int + +libfibre_start_tx = lib.libfibre_start_tx +libfibre_start_tx.argtypes = [c_void_p, c_char_p, c_size_t, OnTxCompletedSignature, c_void_p] +libfibre_start_tx.restype = None + +libfibre_cancel_tx = lib.libfibre_cancel_tx +libfibre_cancel_tx.argtypes = [c_void_p] +libfibre_cancel_tx.restype = None + +libfibre_start_rx = lib.libfibre_start_rx +libfibre_start_rx.argtypes = [c_void_p, c_char_p, c_size_t, OnRxCompletedSignature, c_void_p] +libfibre_start_rx.restype = None + +libfibre_cancel_rx = lib.libfibre_cancel_rx +libfibre_cancel_rx.argtypes = [c_void_p] +libfibre_cancel_rx.restype = None + + +# libfibre wrapper ------------------------------------------------------------# + +class ObjectLostError(Exception): + def __init__(self): + super(Exception, self).__init__("the object disappeared") + +def _get_exception(status): + if status == kFibreOk: + return None + elif status == kFibreCancelled: + return asyncio.CancelledError() + elif status == kFibreClosed: + return EOFError() + elif status == kFibreInvalidArgument: + return ArgumentError() + elif status == kFibreInternalError: + return Exception("internal libfibre error") + elif status == kFibreProtocolError: + return Exception("peer misbehaving") + elif status == kFibreHostUnreachable: + return ObjectLostError() + else: + return Exception("unknown libfibre error {}".format(status)) + +class StructCodec(): + """ + Generic serializer/deserializer based on struct pack + """ + def __init__(self, struct_format, target_type): + self._struct_format = struct_format + self._target_type = target_type + def get_length(self): + return struct.calcsize(self._struct_format) + def serialize(self, libfibre, value): + value = self._target_type(value) + return struct.pack(self._struct_format, value) + def deserialize(self, libfibre, buffer): + value = struct.unpack(self._struct_format, buffer) + value = value[0] if len(value) == 1 else value + return self._target_type(value) + +class ObjectPtrCodec(): + """ + Serializer/deserializer for an object reference + + libfibre transcodes object references internally from/to something that can + be sent over the wire and understood by the remote instance. + """ + def get_length(self): + return struct.calcsize("P") + def serialize(self, libfibre, value): + if value is None: + return struct.pack("P", 0) + elif isinstance(value, RemoteObject): + assert(value._obj_handle) # Cannot serialize reference to a lost object + return struct.pack("P", value._obj_handle) + else: + raise TypeError("Expected value of type RemoteObject or None but got '{}'. An example for a RemoteObject is this expression: odrv0.axis0.controller._input_pos_property".format(type(value).__name__)) + def deserialize(self, libfibre, buffer): + handle = struct.unpack("P", buffer)[0] + return None if handle == 0 else libfibre._objects[handle] + + +codecs = { + 'int8': StructCodec(" 0) # Ensure progress + +class RxStream(): + """Python wrapper for libfibre's LibFibreRxStream interface""" + + def __init__(self, libfibre, rx_stream_handle): + self._libfibre = libfibre + self._rx_stream_handle = rx_stream_handle + self._future = None + self._rx_buf = None + self._c_on_rx_completed = OnRxCompletedSignature(self._on_rx_completed) + self.is_closed = False + + def _on_rx_completed(self, ctx, rx_stream, status, rx_end): + rx_start = cast(self._rx_buf, c_void_p).value + + n_read = rx_end - rx_start + assert(n_read <= len(self._rx_buf)) + data = self._rx_buf[:n_read] + future = self._future + self._future = None + self._rx_buf = None + + if status == kFibreClosed: + self.is_closed = True + + if status == kFibreOk or status == kFibreClosed: + future.set_result(data) + else: + future.set_exception(_get_exception(status)) + + def read(self, n_read): + """ + Reads up to the specified number of bytes from the stream. + + If more than zero bytes are requested, this function will either read at + least one byte, set is_closed to True or throw an Exception (through the + future). + + Currently only one write call may be active at a time (this may change + in the future). + + Returns: A future that either completes with a buffer containing the + bytes that were read or completes with an Exception. + """ + assert(self._future is None) + self._future = future = self._libfibre.loop.create_future() + self._rx_buf = bytes(n_read) + + libfibre_start_rx(self._rx_stream_handle, + cast(self._rx_buf, c_char_p), len(self._rx_buf), + self._c_on_rx_completed, None) + + return future + + async def read_all(self, n_read): + """ + Reads the specified number of bytes from the stream or throws an + Exception. + + If zero bytes are requested, the underlying stream's read function + is still called at least once. + + Returns: A future that either completes with a buffer of size n_read or + an Exception. + """ + + data = bytes() + while True: + chunk = await self.read(n_read - len(data)) + data += chunk + if n_read == len(data): + break + elif self.is_closed: + raise EOFError() + assert(len(chunk) > 0) # Ensure progress + return data + + +class Call(object): + """ + This call behaves as you would expect an async generator to behave. This is + used to provide compatibility down to Python 3.5. + """ + def __init__(self, func): + self._func = func + self._call_handle = c_void_p(0) + self._is_started = False + self._should_close = False + self._is_closed = False + self._tx_buf = None + + def __aiter__(self): + return self + + async def asend(self, val): + assert(self._is_started == (not val is None)) + if not val is None: + self._tx_buf, self._rx_len, self._should_close = val + return await self.__anext__() + + async def __anext__(self): + if not self._is_started: + self._is_started = True + return None # This immitates the weird starting behavior of Python 3.6+ async generators iterators + + if self._is_closed: + raise StopAsyncIteration + + tx_end = c_void_p(0) + rx_end = c_void_p(0) + + rx_buf = b'\0' * self._rx_len + + call_id = insert_with_new_id(self._func._libfibre._calls, self) + + status = libfibre_call(self._func._func_handle, byref(self._call_handle), + kFibreClosed if self._should_close else kFibreOk, + cast(self._tx_buf, c_char_p), len(self._tx_buf), + cast(rx_buf, c_char_p), len(rx_buf), + byref(tx_end), byref(rx_end), self._func._libfibre.c_on_call_completed, call_id) + + if status == kFibreBusy: + self.ag_await = self._func._libfibre.loop.create_future() + status, tx_end, rx_end = await self.ag_await + self.ag_await = None + + if status != kFibreOk and status != kFibreClosed: + raise _get_exception(status) + + n_written = tx_end - cast(self._tx_buf, c_void_p).value + self._tx_buf = self._tx_buf[n_written:] + n_read = rx_end - cast(rx_buf, c_void_p).value + rx_buf = rx_buf[:n_read] + + if status != kFibreOk: + self._is_closed = True + return self._tx_buf, rx_buf, self._is_closed + + async def cancel(): + # TODO: this doesn't follow the official Python async generator protocol. Should implement aclose() instead. + status = libfibre_call(self._func._func_handle, byref(self._call_handle), kFibreOk, + 0, 0, 0, 0, 0, 0, self._func._libfibre.c_on_call_completed, call_id) + + #async def aclose(self): + # assert(self._is_started and not self._is_closed) + # return self._tx_buf, rx_buf, self._is_closed + + +class RemoteFunction(object): + """ + Represents a callable function that maps to a function call on a remote object. + """ + def __init__(self, libfibre, func_handle, inputs, outputs): + self._libfibre = libfibre + self._func_handle = func_handle + self._inputs = inputs + self._outputs = outputs + self._rx_size = sum(codec.get_length() for _, _, codec in self._outputs) + + async def async_call(self, args, cancellation_token): + #print("making call on " + hex(args[0]._obj_handle)) + tx_buf = bytes() + for i, arg in enumerate(self._inputs): + tx_buf += arg[2].serialize(self._libfibre, args[i]) + rx_buf = bytes() + + agen = Call(self) + + if not cancellation_token is None: + cancellation_token.add_done_callback(agen.cancel) + + try: + assert(await agen.asend(None) is None) + + is_closed = False + while not is_closed: + tx_buf, rx_chunk, is_closed = await agen.asend((tx_buf, self._rx_size - len(rx_buf), True)) + rx_buf += rx_chunk + + finally: + if not cancellation_token is None: + cancellation_token.remove_done_callback(agen.cancel) + + assert(len(rx_buf) == self._rx_size) + + outputs = [] + for arg in self._outputs: + arg_length = arg[2].get_length() + outputs.append(arg[2].deserialize(self._libfibre, rx_buf[:arg_length])) + rx_buf = rx_buf[arg_length:] + + if len(outputs) == 0: + return + elif len(outputs) == 1: + return outputs[0] + else: + return tuple(outputs) + + def __call__(self, *args, cancellation_token = None): + """ + Starts invoking the remote function. The first argument is usually a + remote object. + If this function is called from the Fibre thread then it is nonblocking + and returns an asyncio.Future. If it is called from another thread then + it blocks until the function completes and returns the result(s) of the + invokation. + """ + + if threading.current_thread() != libfibre_thread: + return run_coroutine_threadsafe(self._libfibre.loop, lambda: self.__call__(*args)) + + if (len(self._inputs) != len(args)): + raise TypeError("expected {} arguments but have {}".format(len(self._inputs), len(args))) + + coro = self.async_call(args, cancellation_token) + return asyncio.ensure_future(coro, loop=self._libfibre.loop) + + def __get__(self, instance, owner): + return MethodType(self, instance) if instance else self + + def _dump(self, name): + print_arglist = lambda arglist: ", ".join("{}: {}".format(arg_name, codec_name) for arg_name, codec_name, codec in arglist) + return "{}({}){}".format(name, + print_arglist(self._inputs), + "" if len(self._outputs) == 0 else + " -> " + print_arglist(self._outputs) if len(self._outputs) == 1 else + " -> (" + print_arglist(self._outputs) + ")") + +class RemoteAttribute(object): + def __init__(self, libfibre, attr_handle, intf_handle, intf_name, magic_getter, magic_setter): + self._libfibre = libfibre + self._attr_handle = attr_handle + self._intf_handle = intf_handle + self._intf_name = intf_name + self._magic_getter = magic_getter + self._magic_setter = magic_setter + + def _get_obj(self, instance): + obj_handle = c_void_p(0) + status = libfibre_get_attribute(instance._obj_handle, self._attr_handle, byref(obj_handle)) + if status != kFibreOk: + raise _get_exception(status) + + obj = self._libfibre._load_py_obj(obj_handle.value, self._intf_handle) + if obj in instance._children: + self._libfibre._release_py_obj(obj_handle.value) + else: + # the object will be released when the parent is released + instance._children.add(obj) + + return obj + + def __get__(self, instance, owner): + if not instance: + return self + + if self._magic_getter: + if threading.current_thread() == libfibre_thread: + # read() behaves asynchronously when run on the fibre thread + # which means it returns an awaitable which _must_ be awaited + # (otherwise it's a bug). However hasattr(...) internally calls + # __get__ and does not await the result. Thus the safest thing + # is to just disallow __get__ from run as an async method. + raise Exception("Cannot use magic getter on Fibre thread. Use _[prop_name]_propery.read() instead.") + return self._get_obj(instance).read() + else: + return self._get_obj(instance) + + def __set__(self, instance, val): + if self._magic_setter: + return self._get_obj(instance).exchange(val) + else: + raise Exception("this attribute cannot be written to") + +class EmptyInterface(): + def __str__(self): + return "[lost object]" + def __repr__(self): + return self.__str__() + +class RemoteObject(object): + """ + Base class for interfaces of remote objects. + """ + __sealed__ = False + + def __init__(self, libfibre, obj_handle): + self.__class__._refcount += 1 + self._refcount = 0 + self._children = set() + + self._libfibre = libfibre + self._obj_handle = obj_handle + self._on_lost = concurrent.futures.Future() # TODO: maybe we can do this with conc + + # Ensure that assignments to undefined attributes raise an exception + self.__sealed__ = True + + def __setattr__(self, key, value): + if self.__sealed__ and not hasattr(self, key): + raise AttributeError("Attribute {} not found".format(key)) + object.__setattr__(self, key, value) + + #def __del__(self): + # print("unref") + # libfibre_unref_obj(self._obj_handle) + + def _dump(self, indent, depth): + if self._obj_handle is None: + return "[object lost]" + + try: + if depth <= 0: + return "..." + lines = [] + for key in dir(self.__class__): + if key.startswith('_'): + continue + class_member = getattr(self.__class__, key) + if isinstance(class_member, RemoteFunction): + lines.append(indent + class_member._dump(key)) + elif isinstance(class_member, RemoteAttribute): + val = getattr(self, key) + if isinstance(val, RemoteObject) and not class_member._magic_getter: + lines.append(indent + key + (": " if depth == 1 else ":\n") + val._dump(indent + " ", depth - 1)) + else: + if isinstance(val, RemoteObject) and class_member._magic_getter: + val_str = get_user_name(val) + else: + val_str = str(val) + property_type = str(class_member._get_obj(self).__class__.read._outputs[0][1]) + lines.append(indent + key + ": " + val_str + " (" + property_type + ")") + else: + lines.append(indent + key + ": " + str(type(val))) + except: + return "[failed to dump object]" + + return "\n".join(lines) + + def __str__(self): + return self._dump("", depth=2) + + def __repr__(self): + return self.__str__() + + def _destroy(self): + libfibre = self._libfibre + on_lost = self._on_lost + children = self._children + + self._libfibre = None + self._obj_handle = None + self._on_lost = None + self._children = set() + + for child in children: + libfibre._release_py_obj(child._obj_handle) + + self.__class__._refcount -= 1 + if self.__class__._refcount == 0: + libfibre.interfaces.pop(self.__class__._handle) + + self.__class__ = EmptyInterface # ensure that this object has no more attributes + on_lost.set_result(True) + + +class LibFibre(): + def __init__(self): + self.loop = asyncio.get_event_loop() + + # We must keep a reference to these function objects so they don't get + # garbage collected. + self.c_post = PostSignature(self._post) + self.c_register_event = RegisterEventSignature(self._register_event) + self.c_deregister_event = DeregisterEventSignature(self._deregister_event) + self.c_call_later = CallLaterSignature(self._call_later) + self.c_cancel_timer = CancelTimerSignature(self._cancel_timer) + self.c_on_found_object = OnFoundObjectSignature(self._on_found_object) + self.c_on_lost_object = OnLostObjectSignature(self._on_lost_object) + self.c_on_discovery_stopped = OnStoppedSignature(self._on_discovery_stopped) + self.c_on_attribute_added = OnAttributeAddedSignature(self._on_attribute_added) + self.c_on_attribute_removed = OnAttributeRemovedSignature(self._on_attribute_removed) + self.c_on_function_added = OnFunctionAddedSignature(self._on_function_added) + self.c_on_function_removed = OnFunctionRemovedSignature(self._on_function_removed) + self.c_on_call_completed = OnCallCompletedSignature(self._on_call_completed) + + self.timer_map = {} + self.eventfd_map = {} + self.interfaces = {} # key: libfibre handle, value: python class + self.discovery_processes = {} # key: ID, value: python dict + self._objects = {} # key: libfibre handle, value: python class + self._calls = {} # key: libfibre handle, value: Call object + + event_loop = LibFibreEventLoop() + event_loop.post = self.c_post + event_loop.register_event = self.c_register_event + event_loop.deregister_event = self.c_deregister_event + event_loop.call_later = self.c_call_later + event_loop.cancel_timer = self.c_cancel_timer + + self.ctx = c_void_p(libfibre_open(event_loop)) + assert(self.ctx) + + def _post(self, callback, ctx): + self.loop.call_soon_threadsafe(callback, ctx) + return 0 + + def _register_event(self, event_fd, events, callback, ctx): + self.eventfd_map[event_fd] = events + if (events & 1): + self.loop.add_reader(event_fd, lambda x: callback(x, 1), ctx) + if (events & 4): + self.loop.add_writer(event_fd, lambda x: callback(x, 4), ctx) + if (events & 0xfffffffa): + raise Exception("unsupported event mask " + str(events)) + return 0 + + def _deregister_event(self, event_fd): + events = self.eventfd_map.pop(event_fd) + if (events & 1): + self.loop.remove_reader(event_fd) + if (events & 4): + self.loop.remove_writer(event_fd) + return 0 + + def _call_later(self, delay, callback, ctx): + timer_id = insert_with_new_id(self.timer_map, self.loop.call_later(delay, callback, ctx)) + return timer_id + + def _cancel_timer(self, timer_id): + self.timer_map.pop(timer_id).cancel() + return 0 + + def _load_py_intf(self, name, intf_handle): + """ + Creates a new python type for the specified libfibre interface handle or + returns the existing python type if one was already create before. + + Behind the scenes the python type will react to future events coming + from libfibre, such as functions/attributes being added/removed. + """ + if intf_handle in self.interfaces: + return self.interfaces[intf_handle] + else: + if name is None: + name = "anonymous_interface_" + str(intf_handle) + py_intf = self.interfaces[intf_handle] = type(name, (RemoteObject,), {'_handle': intf_handle, '_refcount': 0}) + #exit(1) + libfibre_subscribe_to_interface(intf_handle, self.c_on_attribute_added, self.c_on_attribute_removed, self.c_on_function_added, self.c_on_function_removed, intf_handle) + return py_intf + + def _load_py_obj(self, obj_handle, intf_handle): + if not obj_handle in self._objects: + name = None # TODO: load from libfibre + py_intf = self._load_py_intf(name, intf_handle) + py_obj = py_intf(self, obj_handle) + self._objects[obj_handle] = py_obj + else: + py_obj = self._objects[obj_handle] + + # Note: this refcount does not count the python references to the object + # but rather mirrors the libfibre-internal refcount of the object. This + # is so that we can destroy the Python object when libfibre releases it. + py_obj._refcount += 1 + return py_obj + + def _release_py_obj(self, obj_handle): + py_obj = self._objects[obj_handle] + py_obj._refcount -= 1 + if py_obj._refcount <= 0: + self._objects.pop(obj_handle) + py_obj._destroy() + + def _on_found_object(self, ctx, obj, intf): + py_obj = self._load_py_obj(obj, intf) + discovery = self.discovery_processes[ctx] + discovery._unannounced.append(py_obj) + old_future = discovery._future + discovery._future = self.loop.create_future() + old_future.set_result(None) + + def _on_lost_object(self, ctx, obj): + assert(obj) + self._release_py_obj(obj) + + def _on_discovery_stopped(self, ctx, result): + print("discovery stopped") + + def _on_attribute_added(self, ctx, attr, name, name_length, subintf, subintf_name, subintf_name_length): + name = string_at(name, name_length).decode('utf-8') + subintf_name = None if subintf_name is None else string_at(subintf_name, subintf_name_length).decode('utf-8') + intf = self.interfaces[ctx] + + magic_getter = not subintf_name is None and subintf_name.startswith("fibre.Property<") and subintf_name.endswith(">") + magic_setter = not subintf_name is None and subintf_name.startswith("fibre.Property") + + setattr(intf, name, RemoteAttribute(self, attr, subintf, subintf_name, magic_getter, magic_setter)) + if magic_getter or magic_setter: + setattr(intf, "_" + name + "_property", RemoteAttribute(self, attr, subintf, subintf_name, False, False)) + + def _on_attribute_removed(self, ctx, attr): + print("attribute removed") # TODO + + def _on_function_added(self, ctx, func, name, name_length, input_names, input_codecs, output_names, output_codecs): + name = string_at(name, name_length).decode('utf-8') + inputs = list(decode_arg_list(input_names, input_codecs)) + outputs = list(decode_arg_list(output_names, output_codecs)) + intf = self.interfaces[ctx] + setattr(intf, name, RemoteFunction(self, func, inputs, outputs)) + + def _on_function_removed(self, ctx, func): + print("function removed") # TODO + + def _on_call_completed(self, ctx, status, tx_end, rx_end, tx_buf, tx_len, rx_buf, rx_len): + call = self._calls.pop(ctx) + + call.ag_await.set_result((status, tx_end, rx_end)) + + return kFibreBusy + +class Discovery(): + """ + All public members of this class are thread-safe. + """ + + def __init__(self, domain): + self._domain = domain + self._id = 0 + self._discovery_handle = c_void_p(0) + self._unannounced = [] + self._future = domain._libfibre.loop.create_future() + + async def _next(self): + if len(self._unannounced) == 0: + await self._future + return self._unannounced.pop(0) + + def _stop(self): + self._domain._libfibre.discovery_processes.pop(self._id) + libfibre_stop_discovery(self._discovery_handle) + self._future.set_exception(asyncio.CancelledError()) + + def stop(self): + if threading.current_thread() == libfibre_thread: + self._stop() + else: + run_coroutine_threadsafe(self._domain._libfibre.loop, self._stop) + +class _Domain(): + """ + All public members of this class are thread-safe. + """ + + def __init__(self, libfibre, handle): + self._libfibre = libfibre + self._domain_handle = handle + + def _close(self): + libfibre_close_domain(self._domain_handle) + self._domain_handle = None + #decrement_lib_refcount() + + def _start_discovery(self): + discovery = Discovery(self) + discovery._id = insert_with_new_id(self._libfibre.discovery_processes, discovery) + libfibre_start_discovery(self._domain_handle, byref(discovery._discovery_handle), self._libfibre.c_on_found_object, self._libfibre.c_on_lost_object, self._libfibre.c_on_discovery_stopped, discovery._id) + return discovery + + async def _discover_one(self): + discovery = self._start_discovery() + obj = await discovery._next() + discovery._stop() + return obj + + def discover_one(self): + """ + Blocks until exactly one object is discovered. + """ + return run_coroutine_threadsafe(self._libfibre.loop, self._discover_one) + + def run_discovery(self, callback): + """ + Invokes `callback` for every object that is discovered. The callback is + invoked on the libfibre thread and can be an asynchronous function. + Returns a `Discovery` object on which `stop()` can be called to + terminate the discovery. + """ + discovery = run_coroutine_threadsafe(self._libfibre.loop, self._start_discovery) + async def loop(): + while True: + obj = await discovery._next() + await callback(obj) + self._libfibre.loop.call_soon_threadsafe(lambda: asyncio.ensure_future(loop())) + return discovery + + +class Domain(): + def __init__(self, path): + increment_lib_refcount() + self._opened_domain = run_coroutine_threadsafe(libfibre.loop, lambda: Domain._open(path)) + + def _open(path): + assert(libfibre_thread == threading.current_thread()) + buf = path.encode('ascii') + domain_handle = libfibre_open_domain(libfibre.ctx, buf, len(buf)) + return _Domain(libfibre, domain_handle) + + def __enter__(self): + return self._opened_domain + + def __exit__(self, type, value, traceback): + run_coroutine_threadsafe(self._opened_domain._libfibre.loop, self._opened_domain._close) + self._opened_domain = None + decrement_lib_refcount() + +libfibre = None + +def _run_event_loop(): + global libfibre + global terminate_libfibre + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + terminate_libfibre = loop.create_future() + libfibre = LibFibre() + + libfibre.loop.run_until_complete(terminate_libfibre) + + libfibre_close(libfibre.ctx) + + # Detach all objects that still exist + # TODO: the proper way would be either of these + # - provide a libfibre function to destroy an object on-demand which we'd + # call before libfibre_close(). + # - have libfibre_close() report the destruction of all objects + + while len(libfibre._objects): + libfibre._objects.pop(list(libfibre._objects.keys())[0])._destroy() + assert(len(libfibre.interfaces) == 0) + + libfibre = None + + +lock = threading.Lock() +libfibre_refcount = 0 +libfibre_thread = None + +def increment_lib_refcount(): + global libfibre_refcount + global libfibre_thread + + with lock: + libfibre_refcount += 1 + #print("inc refcount to {}".format(libfibre_refcount)) + + if libfibre_refcount == 1: + libfibre_thread = threading.Thread(target = _run_event_loop) + libfibre_thread.start() + + while libfibre is None: + time.sleep(0.1) + +def decrement_lib_refcount(): + global libfibre_refcount + global libfibre_thread + + with lock: + #print("dec refcount from {}".format(libfibre_refcount)) + libfibre_refcount -= 1 + + if libfibre_refcount == 0: + libfibre.loop.call_soon_threadsafe(lambda: terminate_libfibre.set_result(True)) + + # It's unlikely that releasing fibre from a fibre callback is ok. If + # there is a valid scenario for this then we can remove the assert. + assert(libfibre_thread != threading.current_thread()) + + libfibre_thread.join() + libfibre_thread = None + +def get_user_name(obj): + """ + Can be overridden by the application to return the user-facing name of an + object. + """ + return "[anonymous object]" diff --git a/tools/odrive/pyfibre/fibre/libwinpthread-1.dll b/tools/odrive/pyfibre/fibre/libwinpthread-1.dll new file mode 100755 index 000000000..cc8ca20e7 Binary files /dev/null and b/tools/odrive/pyfibre/fibre/libwinpthread-1.dll differ diff --git a/Firmware/fibre/python/fibre/protocol.py b/tools/odrive/pyfibre/fibre/protocol.py similarity index 98% rename from Firmware/fibre/python/fibre/protocol.py rename to tools/odrive/pyfibre/fibre/protocol.py index c9d657359..d7d38e30f 100644 --- a/Firmware/fibre/python/fibre/protocol.py +++ b/tools/odrive/pyfibre/fibre/protocol.py @@ -74,7 +74,7 @@ class ChannelDamagedException(Exception): """ pass -class ChannelBrokenException(Exception): +class ObjectLostError(Exception): """ Raised when the channel is permanently broken """ @@ -305,13 +305,13 @@ def remote_endpoint_operation(self, endpoint_id, input, expect_ack, output_lengt # Wait for ACK until the resend timeout is exceeded try: if wait_any(self._resend_timeout, ack_event, self._channel_broken) != 0: - raise ChannelBrokenException() + raise ObjectLostError() except TimeoutError: attempt += 1 continue # resend return self._responses.pop(seq_no) # TODO: record channel statistics - raise ChannelBrokenException() # Too many resend attempts + raise ObjectLostError() # Too many resend attempts finally: self._expected_acks.pop(seq_no) self._responses.pop(seq_no, None) diff --git a/tools/odrive/pyfibre/fibre/shell.py b/tools/odrive/pyfibre/fibre/shell.py new file mode 100644 index 000000000..274215670 --- /dev/null +++ b/tools/odrive/pyfibre/fibre/shell.py @@ -0,0 +1,150 @@ + +import sys +import platform +import threading +import fibre + +async def discovered_device(device, + interactive_variables, discovered_devices, + mount, shutdown_token, logger): + """ + Handles the discovery of new devices by displaying a + message and making the device available to the interactive + console + """ + mount_result = await mount(device) + if mount_result is None: + logger.debug("ignoring device") + return + + display_name, var_name = mount_result + + if display_name in discovered_devices: + verb = "Reconnected" + index = discovered_devices.index(display_name) + else: + verb = "Connected" + discovered_devices.append(display_name) + index = len(discovered_devices) - 1 + + var_name = var_name + str(index) + + # Publish new device to interactive console + interactive_variables[var_name] = device + globals()[var_name] = device # Add to globals so tab complete works + logger.notify("{} to {} as {}".format(verb, display_name, var_name)) + + # Subscribe to disappearance of the device + device._on_lost.add_done_callback(lambda x: lost_device(var_name, shutdown_token, logger)) + +def lost_device(interactive_name, shutdown_token, logger): + """ + Handles the disappearance of a device by displaying + a message. + """ + if not shutdown_token[0]: + logger.warn("Oh no {} disappeared".format(interactive_name)) + +def get_user_name(interactive_variables, obj): + queue = [(k, v) for k, v in interactive_variables.items() if isinstance(v, fibre.libfibre.RemoteObject)] + + if not isinstance(obj, fibre.libfibre.RemoteObject): + return None + + while len(queue): + k, v = queue.pop(0) + if v == obj: + return k + for key in dir(v.__class__): + class_member = getattr(v.__class__, key) + if not key.startswith('_') and isinstance(class_member, fibre.libfibre.RemoteAttribute): + queue.append((k + "." + (key if not class_member._magic_getter else "_" + key + "_property"), class_member._get_obj(v))) + + return "anonymous_remote_object_" + str(self._obj_handle) + +def launch_shell(args, mount, + interactive_variables, + print_banner, print_help, + logger): + """ + Launches an interactive python or IPython command line + interface. + As devices are connected they are made available as + "dev0", "dev1", ... + The names of the variables can be customized by setting branding_short. + """ + + discovered_devices = [] + shutdown_token = [False] + globals().update(interactive_variables) + + fibre.libfibre.get_user_name = lambda obj: get_user_name(interactive_variables, obj) + + # Connect to device + with fibre.Domain(args.path) as domain: + on_discovery = lambda dev: discovered_device(dev, interactive_variables, discovered_devices, mount, shutdown_token, logger) + discovery = domain.run_discovery(on_discovery) + + # Check if IPython is installed + if args.no_ipython: + use_ipython = False + else: + try: + import IPython + use_ipython = True + except: + print("Warning: you don't have IPython installed.") + print("If you want to have an improved interactive console with pretty colors,") + print("you should install IPython\n") + use_ipython = False + + interactive_variables["help"] = lambda: print_help(args, len(discovered_devices) > 0) + + # If IPython is installed, embed IPython shell, otherwise embed regular shell + if use_ipython: + # Override help function # pylint: disable=W0612 + help = lambda: print_help(args, len(discovered_devices) > 0) + # to fix broken "%run -i script.py" + locals()['__name__'] = globals()['__name__'] + console = IPython.terminal.embed.InteractiveShellEmbed(banner1='') + + # hack to make IPython look like the regular console + console.runcode = console.run_cell + interact = console + + # Catch ObjectLostError (since disconnect is not always an error) + default_exception_hook = console._showtraceback + def filtered_exception_hook(ex_class, ex, trace): + if(ex_class.__module__+'.'+ex_class.__name__ != 'fibre.libfibre.ObjectLostError'): + default_exception_hook(ex_class,ex,trace) + + console._showtraceback = filtered_exception_hook + else: + # Enable tab complete if possible + try: + import readline # Works only on Unix + readline.parse_and_bind("tab: complete") + except: + sudo_prefix = "" if platform.system() == "Windows" else "sudo " + print("Warning: could not enable tab-complete. User experience will suffer.\n" + "Run `{}pip install readline` and then restart this script to fix this." + .format(sudo_prefix)) + + import code + console = code.InteractiveConsole(locals=interactive_variables) + interact = lambda: console.interact(banner='') + + # Catch ObjectLostError (since disconnect is not alway an error) + console.runcode("import sys") + console.runcode("default_exception_hook = sys.excepthook") + console.runcode("def filtered_exception_hook(ex_class, ex, trace):\n" + " if ex_class.__module__ + '.' + ex_class.__name__ != 'fibre.libfibre.ObjectLostError':\n" + " default_exception_hook(ex_class,ex,trace)") + console.runcode("sys.excepthook=filtered_exception_hook") + + # Launch shell + print_banner() + logger._skip_bottom_line = True + interact() + + shutdown_token[0] = True diff --git a/Firmware/fibre/python/fibre/utils.py b/tools/odrive/pyfibre/fibre/utils.py similarity index 95% rename from Firmware/fibre/python/fibre/utils.py rename to tools/odrive/pyfibre/fibre/utils.py index d0bbe9463..4e38c2384 100644 --- a/Firmware/fibre/python/fibre/utils.py +++ b/tools/odrive/pyfibre/fibre/utils.py @@ -23,12 +23,6 @@ class TimeoutError(Exception): else: TimeoutError = TimeoutError -def get_serial_number_str(device): - if hasattr(device, 'serial_number'): - return format(device.serial_number, 'x').upper() - else: - return "[unknown serial number]" - ## Threading utils ## class Event(): """ @@ -52,27 +46,33 @@ def set(self): Sets the event and invokes all subscribers if the event was not already set """ + subscribers = [] self._mutex.acquire() try: if not self._evt.is_set(): self._evt.set() for s in self._subscribers: - s() + subscribers.append(s) finally: self._mutex.release() + # Invoke subscribes with the mutex released to prevent deadlocks + for s in subscribers: + s() + def subscribe(self, handler): """ Invokes the specified handler exactly once as soon as the specified event is set. If the event is already set, the handler is invoked immediately. + The subscribers are called in the reverse order in which they subscribed. Returns a function that can be invoked to unsubscribe. """ if handler is None: raise TypeError self._mutex.acquire() try: - self._subscribers.append(handler) + self._subscribers.insert(0, handler) if self._evt.is_set(): handler() finally: diff --git a/Firmware/fibre/python/setup.py b/tools/odrive/pyfibre/setup.py similarity index 97% rename from Firmware/fibre/python/setup.py rename to tools/odrive/pyfibre/setup.py index 9edccf6b1..85f266f30 100644 --- a/Firmware/fibre/python/setup.py +++ b/tools/odrive/pyfibre/setup.py @@ -76,9 +76,7 @@ license='MIT', url = 'https://github.com/samuelsadok/fibre', keywords = ['communication', 'transport-layer', 'rpc'], - install_requires = [ - 'appdirs', # Used to find caching directory - ], + install_requires = [], #package_data={'': ['version.txt']}, classifiers = [], ) diff --git a/tools/odrive/shell.py b/tools/odrive/shell.py index 661416d58..d6f6ce7d3 100644 --- a/tools/odrive/shell.py +++ b/tools/odrive/shell.py @@ -5,7 +5,7 @@ import fibre import odrive import odrive.enums -from odrive.utils import calculate_thermistor_coeffs, set_motor_thermistor_coeffs, start_liveplotter, dump_errors, oscilloscope_dump, BulkCapture, step_and_plot +from odrive.utils import * def print_banner(): print("Website: https://odriverobotics.com/") @@ -41,39 +41,19 @@ def print_help(args, have_devices): discovered_devices = [] -def did_discover_device(odrive, logger, app_shutdown_token): - """ - Handles the discovery of new devices by displaying a - message and making the device available to the interactive - console - """ - serial_number = odrive.serial_number if hasattr(odrive, 'serial_number') else "[unknown serial number]" - if serial_number in discovered_devices: - verb = "Reconnected" - index = discovered_devices.index(serial_number) - else: - verb = "Connected" - discovered_devices.append(serial_number) - index = len(discovered_devices) - 1 - interactive_name = "odrv" + str(index) +def benchmark(odrv): + import asyncio + import time - # Publish new ODrive to interactive console - interactive_variables[interactive_name] = odrive - globals()[interactive_name] = odrive # Add to globals so tab complete works - logger.notify("{} to ODrive {:012X} as {}".format(verb, serial_number, interactive_name)) + async def measure_async(): + start = time.monotonic() + futures = [odrv.vbus_voltage for i in range(1000)] +# data = [await f for f in futures] +# print("took " + str(time.monotonic() - start) + " seconds. Average is " + str(sum(data) / len(data))) - # Subscribe to disappearance of the device - odrive.__channel__._channel_broken.subscribe(lambda: did_lose_device(interactive_name, logger, app_shutdown_token)) + fibre.libfibre.libfibre.loop.call_soon_threadsafe(lambda: asyncio.ensure_future(measure_async())) -def did_lose_device(interactive_name, logger, app_shutdown_token): - """ - Handles the disappearance of a device by displaying - a message. - """ - if not app_shutdown_token.is_set(): - logger.warn("Oh no {} disappeared".format(interactive_name)) - -def launch_shell(args, logger, app_shutdown_token): +def launch_shell(args, logger): """ Launches an interactive python or IPython command line interface. @@ -84,7 +64,12 @@ def launch_shell(args, logger, app_shutdown_token): interactive_variables = { 'start_liveplotter': start_liveplotter, 'dump_errors': dump_errors, + 'benchmark': benchmark, 'oscilloscope_dump': oscilloscope_dump, + 'dump_interrupts': dump_interrupts, + 'dump_threads': dump_threads, + 'dump_dma': dump_dma, + 'dump_timing': dump_timing, 'BulkCapture': BulkCapture, 'step_and_plot': step_and_plot, 'calculate_thermistor_coeffs': calculate_thermistor_coeffs, @@ -94,8 +79,13 @@ def launch_shell(args, logger, app_shutdown_token): # Expose all enums from odrive.enums interactive_variables.update({k: v for (k, v) in odrive.enums.__dict__.items() if not k.startswith("_")}) - fibre.launch_shell(args, + async def mount(obj): + serial_number_str = await odrive.utils.get_serial_number_str(obj) + if ((not args.serial_number is None) and (serial_number_str != args.serial_number)): + return None # reject this object + return ("ODrive " + serial_number_str, "odrv") + + fibre.launch_shell(args, mount, interactive_variables, print_banner, print_help, - logger, app_shutdown_token, - branding_short="odrv", branding_long="ODrive") + logger) diff --git a/tools/odrive/tests/analog_input_test.py b/tools/odrive/tests/analog_input_test.py index 733f2a8d7..dd9648bab 100644 --- a/tools/odrive/tests/analog_input_test.py +++ b/tools/odrive/tests/analog_input_test.py @@ -56,17 +56,16 @@ class TestAnalogInput(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): for odrive_gpio_num, odrive_gpio in [(2, odrive.gpio3), (3, odrive.gpio4)]: - analog_out_options = [] - lpf_gpio = [gpio for lpf in testrig.get_connected_components(odrive_gpio, LowPassFilterComponent) - for gpio in testrig.get_connected_components(lpf.en, LinuxGpioComponent)] - for teensy_gpio in testrig.get_connected_components(odrive_gpio, TeensyGpio): - teensy = teensy_gpio.parent - analog_reset_options = [] - for gpio in teensy.gpios: - for local_gpio in testrig.get_connected_components(gpio, LinuxGpioComponent): - analog_reset_options.append((gpio, local_gpio)) - analog_out_options.append((teensy, teensy_gpio, analog_reset_options)) - yield (odrive, lpf_gpio, odrive_gpio_num, analog_out_options) + alternatives = [] + lpfs = [(gpio, TestFixture.all_of(tf1, tf2)) for lpf, tf1 in testrig.get_connected_components(odrive_gpio, LowPassFilterComponent) + for gpio, tf2 in testrig.get_connected_components(lpf.en, LinuxGpioComponent)] + for lpf, tf1 in lpfs: + for teensy_gpio, tf2 in testrig.get_connected_components(odrive_gpio, TeensyGpio): + teensy = teensy_gpio.parent + for gpio in teensy.gpios: + for local_gpio, tf3 in testrig.get_connected_components(gpio, LinuxGpioComponent): + alternatives.append([odrive, lpf, odrive_gpio_num, teensy, teensy_gpio, gpio, local_gpio, TestFixture.all_of(tf1, tf2, tf3)]) + yield AnyTestCase(*alternatives) def run_test(self, odrive: ODriveComponent, lpf_enable: LinuxGpioComponent, analog_in_num: int, teensy: TeensyComponent, teensy_analog_out: Component, teensy_analog_reset: Component, analog_reset_gpio: LinuxGpioComponent, logger: Logger): @@ -91,8 +90,9 @@ def run_test(self, odrive: ODriveComponent, lpf_enable: LinuxGpioComponent, anal None, #odrive.handle.config.gpio5_analog_mapping, ][analog_in_num] - odrive.unuse_gpios() - analog_mapping.endpoint = odrive.handle.axis0.controller._remote_attributes['input_pos'] + odrive.disable_mappings() + setattr(odrive.handle.config, 'gpio' + str(analog_in_num+1) + '_mode', GPIO_MODE_ANALOG_IN) + analog_mapping.endpoint = odrive.handle.axis0.controller._input_pos_property analog_mapping.min = min_val analog_mapping.max = max_val odrive.save_config_and_reboot() @@ -105,9 +105,9 @@ def run_test(self, odrive: ODriveComponent, lpf_enable: LinuxGpioComponent, anal full_range = abs(max_val - min_val) slope, offset, fitted_curve = fit_sawtooth(data, min_val, max_val, sigma=30) test_assert_eq(slope, (max_val - min_val) / period, accuracy=0.005) - test_curve_fit(data, fitted_curve, max_mean_err = full_range * 0.02, inlier_range = full_range * 0.05, max_outliers = len(data[:,0]) * 0.02) - + test_curve_fit(data, fitted_curve, max_mean_err = full_range * 0.03, inlier_range = full_range * 0.05, max_outliers = len(data[:,0]) * 0.02) +tests = [TestAnalogInput()] if __name__ == '__main__': - test_runner.run(TestAnalogInput()) + test_runner.run(tests) diff --git a/tools/odrive/tests/calibration_test.py b/tools/odrive/tests/calibration_test.py index 4b9b6788d..eace8c55b 100644 --- a/tools/odrive/tests/calibration_test.py +++ b/tools/odrive/tests/calibration_test.py @@ -18,24 +18,25 @@ class TestMotorCalibration(): def get_test_cases(self, testrig: TestRig): """Returns all axes that are connected to a motor, along with the corresponding motor(s)""" - for odrive in testrig.get_components(ODriveComponent): - for axis in odrive.axes: - for motor in testrig.get_connected_components(axis, MotorComponent): - yield (axis, motor) + for axis in testrig.get_components(ODriveAxisComponent): + for motor, tf in testrig.get_connected_components({'phases': axis}, MotorComponent): + yield (axis, motor, tf) def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, logger: Logger): # reset old calibration values - if axis_ctx.handle.encoder.config.mode != ENCODER_MODE_INCREMENTAL: - axis_ctx.handle.encoder.config.mode = ENCODER_MODE_INCREMENTAL - axis_ctx.parent.save_config_and_reboot() + axis_ctx.parent.erase_config_and_reboot() + #if axis_ctx.handle.encoder.config.mode != ENCODER_MODE_INCREMENTAL: + # axis_ctx.handle.encoder.config.mode = ENCODER_MODE_INCREMENTAL + # axis_ctx.parent.save_config_and_reboot() axis_ctx.handle.motor.config.phase_resistance = 0.0 axis_ctx.handle.motor.config.phase_inductance = 0.0 axis_ctx.handle.motor.config.pre_calibrated = False axis_ctx.handle.config.enable_watchdog = False + axis_ctx.parent.handle.config.dc_max_negative_current = -1.0 - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() # run calibration request_state(axis_ctx, AXIS_STATE_MOTOR_CALIBRATION) @@ -56,10 +57,9 @@ class TestDisconnectedMotorCalibration(): def get_test_cases(self, testrig: TestRig): """Returns all axes that are disconnected""" - for odrive in testrig.get_components(ODriveComponent): - for axis in odrive.axes: - if axis.yaml == 'floating': - yield (axis,) + for axis in testrig.get_components(ODriveAxisComponent): + if axis.yaml == 'floating': + yield (axis, None) def run_test(self, axis_ctx: ODriveAxisComponent, logger: Logger): axis = axis_ctx.handle @@ -69,13 +69,12 @@ def run_test(self, axis_ctx: ODriveAxisComponent, logger: Logger): axis_ctx.handle.motor.config.phase_inductance = 0.0 axis_ctx.handle.motor.config.pre_calibrated = False - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() # run test request_state(axis_ctx, AXIS_STATE_MOTOR_CALIBRATION) time.sleep(6) test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) - test_assert_eq(axis_ctx.handle.error, AXIS_ERROR_MOTOR_FAILED) test_assert_eq(axis_ctx.handle.motor.error, MOTOR_ERROR_PHASE_RESISTANCE_OUT_OF_RANGE) @@ -85,17 +84,7 @@ class TestEncoderDirFind(): """ def get_test_cases(self, testrig: TestRig): - for odrive in testrig.get_components(ODriveComponent): - for num in range(2): - encoders = testrig.get_connected_components({ - 'a': (odrive.encoders[num].a, False), - 'b': (odrive.encoders[num].b, False) - }, EncoderComponent) - motors = testrig.get_connected_components(odrive.axes[num], MotorComponent) - - for motor, encoder in itertools.product(motors, encoders): - if encoder.impl in testrig.get_connected_components(motor): - yield (odrive.axes[num], motor, encoder) + return testrig.get_closed_loop_combos(init=False) def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): axis = axis_ctx.handle @@ -107,10 +96,10 @@ def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc axis_ctx.handle.motor.config.pre_calibrated = True # Set calibration settings - axis_ctx.handle.motor.config.direction = 0 + axis_ctx.handle.encoder.config.direction = 0 axis_ctx.handle.config.calibration_lockin.vel = 12.566 # 2 electrical revolutions per second - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() # run test request_state(axis_ctx, AXIS_STATE_ENCODER_DIR_FIND) @@ -120,7 +109,7 @@ def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) test_assert_no_error(axis_ctx) - test_assert_eq(axis_ctx.handle.motor.config.direction in [-1, 1], True) + test_assert_eq(axis_ctx.handle.encoder.config.direction in [-1, 1], True) class TestEncoderOffsetCalibration(): @@ -129,17 +118,7 @@ class TestEncoderOffsetCalibration(): """ def get_test_cases(self, testrig: TestRig): - for odrive in testrig.get_components(ODriveComponent): - for num in range(2): - encoders = testrig.get_connected_components({ - 'a': (odrive.encoders[num].a, False), - 'b': (odrive.encoders[num].b, False) - }, EncoderComponent) - motors = testrig.get_connected_components(odrive.axes[num], MotorComponent) - - for motor, encoder in itertools.product(motors, encoders): - if encoder.impl in testrig.get_connected_components(motor): - yield (odrive.axes[num], motor, encoder) + return testrig.get_closed_loop_combos(init=False) def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): axis = axis_ctx.handle @@ -151,23 +130,23 @@ def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc axis_ctx.handle.motor.config.pre_calibrated = True # Set calibration settings - axis_ctx.handle.motor.config.direction = 0 + axis_ctx.handle.encoder.config.direction = 0 axis_ctx.handle.encoder.config.use_index = False axis_ctx.handle.encoder.config.calib_scan_omega = 12.566 # 2 electrical revolutions per second axis_ctx.handle.encoder.config.calib_scan_distance = 50.265 # 8 revolutions - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() # run test request_state(axis_ctx, AXIS_STATE_ENCODER_OFFSET_CALIBRATION) - time.sleep(9) # actual calibration takes 8 seconds + time.sleep(9.1) # actual calibration takes 9.0 seconds test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) test_assert_no_error(axis_ctx) test_assert_eq(axis_ctx.handle.encoder.is_ready, True) - test_assert_eq(axis_ctx.handle.motor.config.direction in [-1, 1], True) + test_assert_eq(axis_ctx.handle.encoder.config.direction in [-1, 1], True) class TestEncoderIndexSearch(): @@ -178,18 +157,11 @@ class TestEncoderIndexSearch(): """ def get_test_cases(self, testrig: TestRig): - for odrive in testrig.get_components(ODriveComponent): - for num in range(2): - encoders = testrig.get_connected_components({ - 'a': (odrive.encoders[num].a, False), - 'b': (odrive.encoders[num].b, False) - }, EncoderComponent) - motors = testrig.get_connected_components(odrive.axes[num], MotorComponent) - z_gpio = list(testrig.get_connected_components((odrive.encoders[num].z, False), LinuxGpioComponent)) - - for motor, encoder in itertools.product(motors, encoders): - if encoder.impl in testrig.get_connected_components(motor): - yield (odrive.axes[num], motor, encoder, z_gpio) + for axis, motor, encoder, tf1 in testrig.get_closed_loop_combos(init=False): + alternatives = [] + for z_gpio, tf2 in testrig.get_connected_components((axis.parent.encoders[axis.num].z, False), LinuxGpioComponent): + alternatives.append((axis, motor, encoder, z_gpio, TestFixture.all_of(tf1, tf2))) + yield AnyTestCase(*alternatives) def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, z_gpio: LinuxGpioComponent, logger: Logger): axis = axis_ctx.handle @@ -208,7 +180,7 @@ def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc # Set calibration settings axis_ctx.handle.config.calibration_lockin.vel = 12.566 # 2 electrical revolutions per second - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() # run test request_state(axis_ctx, AXIS_STATE_ENCODER_INDEX_SEARCH) @@ -227,15 +199,16 @@ def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc test_assert_eq(axis_ctx.handle.encoder.shadow_count, 0.0, range=50) test_assert_eq(modpm(axis_ctx.handle.encoder.count_in_cpr, cpr), 0.0, range=50) test_assert_eq(axis_ctx.handle.encoder.pos_estimate, 0.0, range=50) - test_assert_eq(modpm(axis_ctx.handle.encoder.pos_cpr, cpr), 0.0, range=50) + test_assert_eq(modpm(axis_ctx.handle.encoder.pos_cpr_counts, cpr), 0.0, range=50) test_assert_eq(axis_ctx.handle.encoder.pos_abs, 0.0, range=50) +tests = [ + TestMotorCalibration(), + TestDisconnectedMotorCalibration(), + TestEncoderDirFind(), + TestEncoderOffsetCalibration(), + TestEncoderIndexSearch() +] if __name__ == '__main__': - test_runner.run([ - TestMotorCalibration(), - TestDisconnectedMotorCalibration(), - TestEncoderDirFind(), - TestEncoderOffsetCalibration(), - TestEncoderIndexSearch() - ]) + test_runner.run(tests) diff --git a/tools/odrive/tests/can_test.py b/tools/odrive/tests/can_test.py index 3195a806c..8656e45d0 100644 --- a/tools/odrive/tests/can_test.py +++ b/tools/odrive/tests/can_test.py @@ -104,35 +104,46 @@ class TestSimpleCAN(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): can_interfaces = list(testrig.get_connected_components(odrive.can, CanInterfaceComponent)) - yield (odrive, can_interfaces, 0, False) # standard ID - yield (odrive, can_interfaces, 0xfedcba, True) # extended ID + yield AnyTestCase(*[(odrive, intf, 0, False, tf) for intf, tf in can_interfaces]) # standard ID + yield AnyTestCase(*[(odrive, intf, 0xfedcba, True, tf) for intf, tf in can_interfaces]) # extended ID def run_test(self, odrive: ODriveComponent, canbus: CanInterfaceComponent, node_id: int, extended_id: bool, logger: Logger): - - # make sure no gpio input is overwriting our values - odrive.unuse_gpios() + odrive.disable_mappings() + if odrive.yaml['board-version'].startswith("v3."): + odrive.handle.config.gpio15_mode = GPIO_MODE_CAN_A + odrive.handle.config.gpio16_mode = GPIO_MODE_CAN_A + elif odrive.yaml['board-version'].startswith("v4."): + pass # CAN pin configuration is hardcoded + else: + raise Exception("unknown board version {}".format(odrive.yaml['board-version'])) + odrive.handle.config.enable_can_a = True + odrive.save_config_and_reboot() axis = odrive.handle.axis0 axis.config.enable_watchdog = False - axis.clear_errors() - axis.config.can_node_id = node_id - axis.config.can_node_id_extended = extended_id + odrive.handle.clear_errors() + axis.config.can.node_id = node_id + axis.config.can.is_extended = extended_id time.sleep(0.1) def my_cmd(cmd_name, **kwargs): command(canbus.handle, node_id, extended_id, cmd_name, **kwargs) def my_req(cmd_name, **kwargs): return asyncio.run(request(canbus.handle, node_id, extended_id, cmd_name, **kwargs)) def fence(): my_req('get_vbus_voltage') # fence to ensure the CAN command was sent + def flush_rx(): + while not canbus.handle.recv(timeout = 0) is None: pass + logger.debug('sending request...') test_assert_eq(my_req('get_vbus_voltage')['vbus_voltage'], odrive.handle.vbus_voltage, accuracy=0.01) my_cmd('set_node_id', node_id=node_id+20) + time.sleep(0.1) # TODO: remove this hack (see note in firmware) asyncio.run(request(canbus.handle, node_id+20, extended_id, 'get_vbus_voltage')) - test_assert_eq(axis.config.can_node_id, node_id+20) + test_assert_eq(axis.config.can.node_id, node_id+20) # Reset node ID to default value command(canbus.handle, node_id+20, extended_id, 'set_node_id', node_id=node_id) fence() - test_assert_eq(axis.config.can_node_id, node_id) + test_assert_eq(axis.config.can.node_id, node_id) # Check that extended node IDs are not carelessly projected to 6-bit IDs extended_id = not extended_id @@ -142,6 +153,7 @@ def fence(): my_req('get_vbus_voltage') # fence to ensure the CAN command was se test_assert_eq(axis.error, AXIS_ERROR_NONE) axis.encoder.set_linear_count(123) + flush_rx() # drop previous encoder estimates that were sent by the ODrive at a constant rate test_assert_eq(my_req('get_encoder_estimates')['encoder_pos_estimate'], 123.0 / axis.encoder.config.cpr, accuracy=0.01) test_assert_eq(my_req('get_encoder_count')['encoder_shadow_count'], 123.0, accuracy=0.01) @@ -214,22 +226,23 @@ def fence(): my_req('get_vbus_voltage') # fence to ensure the CAN command was se test_watchdog(axis, lambda: my_cmd('set_input_torque', input_torque=0.0), logger) logger.debug('testing heartbeat...') - # note that this will include the heartbeats that were received during the - # watchdog test (which takes 4.8s). - heartbeats = asyncio.run(get_all(record_messages(canbus.handle, node_id, extended_id, 'heartbeat', timeout = 1.0))) - test_assert_eq(len(heartbeats), 5.8 / 0.1, accuracy=0.05) - test_assert_eq([msg['error'] for msg in heartbeats[0:35]], [0] * 35) # before watchdog expiry - test_assert_eq([msg['error'] for msg in heartbeats[-10:]], [AXIS_ERROR_WATCHDOG_TIMER_EXPIRED] * 10) # after watchdog expiry + flush_rx() # Flush RX buffer to get a clean state for the heartbeat test + heartbeats = asyncio.run(get_all(record_messages(canbus.handle, node_id, extended_id, 'heartbeat', timeout = 2.0))) + test_assert_eq(len(heartbeats), 2.0 / 0.1, accuracy=0.05) + test_assert_eq([msg['error'] for msg in heartbeats], [AXIS_ERROR_WATCHDOG_TIMER_EXPIRED] * len(heartbeats)) test_assert_eq([msg['current_state'] for msg in heartbeats], [1] * len(heartbeats)) logger.debug('testing reboot...') + test_assert_eq(odrive.handle._on_lost.done(), False) my_cmd('reboot') time.sleep(0.5) - if len(odrive.handle._remote_attributes) != 0: + if not odrive.handle._on_lost is None: raise TestFailed("device didn't seem to reboot") odrive.handle = None time.sleep(2.0) odrive.prepare(logger) +tests = [TestSimpleCAN()] + if __name__ == '__main__': - test_runner.run(TestSimpleCAN()) + test_runner.run(tests) diff --git a/tools/odrive/tests/closed_loop_test.py b/tools/odrive/tests/closed_loop_test.py index 263c8181c..65c63a6f2 100644 --- a/tools/odrive/tests/closed_loop_test.py +++ b/tools/odrive/tests/closed_loop_test.py @@ -14,69 +14,18 @@ class TestClosedLoopControlBase(): """ Base class for close loop control tests. """ - - def get_test_cases(self, testrig: TestRig): - for odrive in testrig.get_components(ODriveComponent): - for num in range(2): - encoders = testrig.get_connected_components({ - 'a': (odrive.encoders[num].a, False), - 'b': (odrive.encoders[num].b, False) - }, EncoderComponent) - motors = testrig.get_connected_components(odrive.axes[num], MotorComponent) - - for motor, encoder in itertools.product(motors, encoders): - if encoder.impl in testrig.get_connected_components(motor): - yield (odrive.axes[num], motor, encoder) - - def prepare(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): - # Make sure there are no funny configurations active - logger.debug('Setting up clean configuration...') - axis_ctx.parent.erase_config_and_reboot() - - # Set motor calibration values - axis_ctx.handle.motor.config.phase_resistance = float(motor_ctx.yaml['phase-resistance']) - axis_ctx.handle.motor.config.phase_inductance = float(motor_ctx.yaml['phase-inductance']) - axis_ctx.handle.motor.config.pre_calibrated = True - - # Set calibration settings - axis_ctx.handle.motor.config.direction = 0 - axis_ctx.handle.encoder.config.use_index = False - axis_ctx.handle.encoder.config.calib_scan_omega = 12.566 # 2 electrical revolutions per second - axis_ctx.handle.encoder.config.calib_scan_distance = 50.265 # 8 revolutions - axis_ctx.handle.encoder.config.bandwidth = 1000 - - - axis_ctx.handle.clear_errors() - - logger.debug('Calibrating encoder offset...') - request_state(axis_ctx, AXIS_STATE_ENCODER_OFFSET_CALIBRATION) - - time.sleep(9) # actual calibration takes 8 seconds - - test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) - test_assert_no_error(axis_ctx) - - - # Return a context that can be used in a with-statement. - class safe_terminator(): - def __enter__(self): - pass - def __exit__(self, exc_type, exc_val, exc_tb): - logger.debug('clearing config...') - axis_ctx.handle.requested_state = AXIS_STATE_IDLE - time.sleep(0.005) - axis_ctx.parent.erase_config_and_reboot() - return safe_terminator() - + pass class TestClosedLoopControl(TestClosedLoopControlBase): """ Tests position and velocity control """ + def get_test_cases(self, testrig: TestRig): + return testrig.get_closed_loop_combos() def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): - with self.prepare(axis_ctx, motor_ctx, enc_ctx, logger): - nominal_rps = 1.0 + with SafeTerminator(logger, axis_ctx): + nominal_rps = 3.0 nominal_vel = nominal_rps logger.debug(f'Testing closed loop velocity control at {nominal_rps} rounds/s...') @@ -85,12 +34,14 @@ def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc axis_ctx.handle.controller.input_vel = 0 request_state(axis_ctx, AXIS_STATE_CLOSED_LOOP_CONTROL) + axis_ctx.handle.controller.config.vel_limit = nominal_vel + axis_ctx.handle.controller.config.vel_limit_tolerance = 2 axis_ctx.handle.controller.input_vel = nominal_vel data = record_log(lambda: [axis_ctx.handle.encoder.vel_estimate, axis_ctx.handle.encoder.pos_estimate], duration=5.0) - test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_CLOSED_LOOP_CONTROL) test_assert_no_error(axis_ctx) + test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_CLOSED_LOOP_CONTROL) request_state(axis_ctx, AXIS_STATE_IDLE) # encoder.vel_estimate @@ -109,6 +60,7 @@ def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc axis_ctx.handle.controller.config.control_mode = CONTROL_MODE_POSITION_CONTROL axis_ctx.handle.controller.input_pos = 0 + axis_ctx.handle.controller.input_vel = 0 # turn off velocity feed-forward axis_ctx.handle.controller.config.vel_limit = 5.0 # max 5 rps axis_ctx.handle.encoder.set_linear_count(0) @@ -173,26 +125,27 @@ class TestRegenProtection(TestClosedLoopControlBase): Note: If this test fails then try to run it at a DC voltage of 24V. Ibus seems to be more noisy/sensitive at lower DC voltages. """ + def get_test_cases(self, testrig: TestRig): + return testrig.get_closed_loop_combos() def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): - with self.prepare(axis_ctx, motor_ctx, enc_ctx, logger): + with SafeTerminator(logger, axis_ctx): nominal_rps = 15.0 - nominal_vel = nominal_rps - max_current = 30.0 + max_current = 20.0 # Accept a bit of noise on Ibus - axis_ctx.parent.handle.config.dc_max_negative_current = -0.2 + axis_ctx.parent.handle.config.dc_max_negative_current = -0.5 logger.debug(f'Brake control test from {nominal_rps} rounds/s...') - axis_ctx.handle.controller.config.vel_limit = 25.0 # max 15 rps + axis_ctx.handle.controller.config.vel_limit = 25.0 axis_ctx.handle.motor.config.current_lim = max_current axis_ctx.handle.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL axis_ctx.handle.controller.config.input_mode = INPUT_MODE_PASSTHROUGH request_state(axis_ctx, AXIS_STATE_CLOSED_LOOP_CONTROL) # accelerate... - axis_ctx.handle.controller.input_vel = nominal_vel + axis_ctx.handle.controller.input_vel = nominal_rps time.sleep(1.0) test_assert_no_error(axis_ctx) @@ -201,18 +154,45 @@ def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc time.sleep(1.0) test_assert_no_error(axis_ctx) + + logger.debug(f'Brake control test with brake resistor disabled') # once more, but this time without brake resistor - axis_ctx.parent.handle.config.brake_resistance = 0 + axis_ctx.parent.handle.config.enable_brake_resistor = False # accelerate... - axis_ctx.handle.controller.input_vel = nominal_vel + axis_ctx.handle.controller.input_vel = nominal_rps + time.sleep(1.0) + test_assert_no_error(axis_ctx) + + # ... and brake + axis_ctx.parent.handle.config.dc_max_negative_current = -0.2 + axis_ctx.handle.controller.input_vel = 10 # this should fail almost instantaneously + time.sleep(0.1) + test_assert_eq(axis_ctx.parent.handle.error, ODRIVE_ERROR_DC_BUS_OVER_REGEN_CURRENT) + test_assert_eq(axis_ctx.handle.motor.error & MOTOR_ERROR_SYSTEM_LEVEL, MOTOR_ERROR_SYSTEM_LEVEL) + test_assert_eq(axis_ctx.handle.error, 0) + + # Do test again with wrong brake resistance setting + logger.debug(f'Brake control test with brake resistor = 100') + axis_ctx.parent.handle.clear_errors() + time.sleep(1.0) + axis_ctx.parent.handle.config.brake_resistance = 100 + axis_ctx.parent.handle.config.dc_max_negative_current = -0.5 + request_state(axis_ctx, AXIS_STATE_CLOSED_LOOP_CONTROL) + + # accelerate... + axis_ctx.handle.controller.input_vel = nominal_rps time.sleep(1.0) test_assert_no_error(axis_ctx) # ... and brake - axis_ctx.handle.controller.input_vel = 0 # this should fail almost instantaneously + axis_ctx.handle.controller.input_vel = 0 + time.sleep(1.0) # expect DC_BUS_OVER_REGEN_CURRENT time.sleep(0.1) - test_assert_eq(axis_ctx.handle.error, AXIS_ERROR_MOTOR_DISARMED | AXIS_ERROR_BRAKE_RESISTOR_DISARMED) - test_assert_eq(axis_ctx.handle.motor.error, MOTOR_ERROR_DC_BUS_OVER_REGEN_CURRENT) + test_assert_eq(axis_ctx.parent.handle.error, ODRIVE_ERROR_DC_BUS_OVER_REGEN_CURRENT) + test_assert_eq(axis_ctx.handle.motor.error & MOTOR_ERROR_SYSTEM_LEVEL, MOTOR_ERROR_SYSTEM_LEVEL) + test_assert_eq(axis_ctx.handle.error, 0) + + class TestVelLimitInTorqueControl(TestClosedLoopControlBase): @@ -220,9 +200,11 @@ class TestVelLimitInTorqueControl(TestClosedLoopControlBase): Ensures that the current setpoint in torque control is always within the parallelogram that arises from -Ilim, +Ilim, vel_limit and vel_gain. """ + def get_test_cases(self, testrig: TestRig): + return testrig.get_closed_loop_combos() def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): - with self.prepare(axis_ctx, motor_ctx, enc_ctx, logger): + with SafeTerminator(logger, axis_ctx): max_rps = 20.0 max_vel = max_rps absolute_max_vel = max_vel * 1.2 @@ -231,7 +213,7 @@ def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc axis_ctx.handle.controller.config.vel_gain /= 10 # reduce the slope to make it easier to see what's going on vel_gain = axis_ctx.handle.controller.config.vel_gain - direction = axis_ctx.handle.motor.config.direction + direction = axis_ctx.handle.encoder.config.direction logger.debug(f'vel gain is {vel_gain}') axis_ctx.handle.controller.config.vel_limit = max_vel @@ -297,14 +279,17 @@ def data_getter(): axis_ctx.handle.requested_state=1 test_curve_fit(dataA[:,(0,3)], dataA[:,4], max_mean_err=0.02, inlier_range=0.05, max_outliers=len(dataA[:,0]*0.01)) - test_curve_fit(dataB[:,(0,3)], dataB[:,4], max_mean_err=0.1, inlier_range=0.2, max_outliers=len(dataB[:,0])*0.01) + test_curve_fit(dataB[:,(0,3)], dataB[:,4], max_mean_err=0.1, inlier_range=0.3, max_outliers=len(dataB[:,0])*0.01) class TestTorqueLimit(TestClosedLoopControlBase): """ Checks that the torque limit is respected in position, velocity, and torque control modes """ + def get_test_cases(self, testrig: TestRig): + return testrig.get_closed_loop_combos() + def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): - with self.prepare(axis_ctx, motor_ctx, enc_ctx, logger): + with SafeTerminator(logger, axis_ctx): max_rps = 15.0 max_vel = max_rps max_current = 30.0 @@ -381,11 +366,12 @@ def data_getter(): test_assert_no_error(axis_ctx) axis_ctx.handle.requested_state=1 +tests = [ + TestClosedLoopControl(), + TestRegenProtection(), + TestVelLimitInTorqueControl(), + TestTorqueLimit() +] if __name__ == '__main__': - test_runner.run([ - TestClosedLoopControl(), - TestRegenProtection(), - TestVelLimitInTorqueControl(), - TestTorqueLimit() - ]) + test_runner.run(tests) diff --git a/tools/odrive/tests/dp800.py b/tools/odrive/tests/dp800.py new file mode 100644 index 000000000..0962148ff --- /dev/null +++ b/tools/odrive/tests/dp800.py @@ -0,0 +1,104 @@ +# ## Resources ## +# +# - Overview over the most important commands: +# https://btbm.ch/rigol-dp832-lxi-commands-with-python/ +# +# - Programming Guide: +# http://beyondmeasure.rigoltech.com/acton/attachment/1579/f-03a1/1/-/-/-/-/DP800%20Programming%20Guide.pdf +# + +class Dp800(object): + """ + Provides an command and control interface to power supplies implementing + the USB Test and Measurement Class (TMC) protocol. + + It was tested on a Rigol DP832 but is likely to work with other USB TMC + devices too. + """ + def __init__(self, **device_filter): + # If something doesn't work it sometimes helps to reset the USB device + #import usb.core + #dev = usb.core.find(idVendor=0x1ab1, idProduct=0x0e11, **device_filter) + #dev.reset() + #cfg = dev.get_active_configuration() + #intf = cfg.interfaces()[0] + #usb.util.claim_interface(dev, intf) + #ep_in = intf.endpoints()[0] + #ep_in = intf.endpoints()[1] + #ep_out = intf.endpoints()[2] + ##ep_out.write(b"*IDN?\n") + #import ipdb; ipdb.set_trace() + + import usbtmc + self._dev = usbtmc.Instrument(0x1ab1, 0x0e11) + self._id = self._dev.ask("*IDN?") + + def _get_setpoint(self, channel): + """ + Returns the currently configured voltage and current setpoints. + channel: The channel number (starting at 1). + Returns: A tuple of the form (voltage, current). + """ + response = self._dev.ask(f":SOUR{channel}:VOLT?;:SOUR{channel}:CURR?") + return tuple((float(v) for v in response.split(";"))) + + def _apply(self, channel, voltage, current): + """ + Sets the voltage and current setpoint for the specified channel. + channel: The channel number (starting at 1). + """ + self._dev.write(f":APPL CH{channel},{voltage},{current}") + + def _measure(self, channel): + """ + Measures the voltage, current and power on the selected channel. + channel: The channel number (starting at 1). + Returns: A tuple of the form (voltage, current, power). + """ + response = self._dev.ask(f":MEAS:ALL? CH{channel}") + return tuple((float(v) for v in response.split(","))) + +class Dp800Channel(object): + """ + Represents one logical power channel on a TMC power supply. This usually + corresponds to one real channel on the device but can also bundle multiple + real channels that are connected in series or in parallel. + """ + def __init__(self, device, channels, conn_type): + """ + channels: A list of channel numbers. + conn_type: "series": the channels are connected in series. + "parallel": the channels are connected in parallel. + This has no effect if channels has length 1. + """ + self._dev = device + self._channels = channels + self._in_series = {'series': True, 'parallel': False}[conn_type] + + def get_setpoint(self): + setpoints = [self._dev._get_setpoint(c) for c in self._channels] + setpoint = sum(v for v, _ in setpoints), sum(c for _, c in setpoints) + if self._in_series: + return setpoint[0], setpoint[1] / len(self._channels) + else: + return setpoint[0] / len(self._channels), setpoint[1] + + def apply(self, voltage, current): + if self._in_series: + voltage /= len(self._channels) + else: + current /= len(self._channels) + for ch in self._channels: + self._dev._apply(ch, voltage, current) + + def measure(self): + setpoints = [self._dev._measure(c) for c in self._channels] + setpoint = sum(v for v, _, _ in setpoints), sum(c for _, c, _ in setpoints), sum(p for _, _, p in setpoints) + if self._in_series: + return setpoint[0], setpoint[1] / len(self._channels), setpoint[2] + else: + return setpoint[0] / len(self._channels), setpoint[1], setpoint[2] + + +mydev = Dp800() +mychannel = Dp800Channel(mydev, [1, 2], 'series') diff --git a/tools/odrive/tests/encoder_test.py b/tools/odrive/tests/encoder_test.py index d94059d52..8e525f311 100644 --- a/tools/odrive/tests/encoder_test.py +++ b/tools/odrive/tests/encoder_test.py @@ -9,7 +9,6 @@ from odrive.enums import * from test_runner import * - class TestEncoderBase(): """ Base class for encoder tests. @@ -48,12 +47,7 @@ def run_generic_encoder_test(self, encoder, true_cpr, true_rps, noise=1): # encoder.count_in_cpr slope, offset, fitted_curve = fit_sawtooth(data[:,(0,2)], true_cpr if reverse else 0, 0 if reverse else true_cpr) test_assert_eq(slope, true_cps, accuracy=0.005) - test_curve_fit(data[:,(0,2)], fitted_curve, max_mean_err = true_cpr * 0.02, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02) - - # encoder.phase - slope, offset, fitted_curve = fit_sawtooth(data[:,(0,3)], pi if reverse else -pi, -pi if reverse else pi, sigma=5) - test_assert_eq(slope / 7, 2*pi*true_rps, accuracy=0.05) - test_curve_fit(data[:,(0,3)], fitted_curve, max_mean_err = true_cpr * 0.02, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02) + test_curve_fit(data[:,(0,2)], fitted_curve, max_mean_err = true_cpr * 0.02, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02 * noise) # encoder.pos_estimate slope, offset, fitted_curve = fit_line(data[:,(0,4)]) @@ -68,7 +62,7 @@ def run_generic_encoder_test(self, encoder, true_cpr, true_rps, noise=1): # encoder.vel_estimate slope, offset, fitted_curve = fit_line(data[:,(0,6)]) test_assert_eq(slope, 0.0, range = true_cpr * abs(true_rps) * 0.01) - test_assert_eq(offset, true_cpr * true_rps, accuracy = 0.02) + test_assert_eq(offset, true_cpr * true_rps, accuracy = 0.03) test_curve_fit(data[:,(0,6)], fitted_curve, max_mean_err = true_cpr * 0.05, inlier_range = true_cpr * 0.05 * noise, max_outliers = len(data[:,0]) * 0.05) @@ -112,12 +106,12 @@ def get_test_cases(self, testrig: TestRig): ] valid_combinations = [ - (combination[0].parent,) + tuple(combination) + (encoder, combination[0].parent,) + tuple(combination) + (None,) for combination in itertools.product(*gpio_conns) if ((len(set(c.parent for c in combination)) == 1) and isinstance(combination[0].parent, TeensyComponent)) ] - yield (encoder, valid_combinations) + yield AnyTestCase(*valid_combinations) def run_test(self, enc: ODriveEncoderComponent, teensy: TeensyComponent, teensy_gpio_a: TeensyGpio, teensy_gpio_b: TeensyGpio, logger: Logger): @@ -172,12 +166,12 @@ def get_test_cases(self, testrig: TestRig): ] valid_combinations = [ - (combination[0].parent,) + tuple(combination) + (odrive.encoders[0], combination[0].parent,) + tuple(combination) + (None,) for combination in itertools.product(*gpio_conns) if ((len(set(c.parent for c in combination)) == 1) and isinstance(combination[0].parent, TeensyComponent)) ] - yield (odrive.encoders[0], valid_combinations) + yield AnyTestCase(*valid_combinations) def run_test(self, enc: ODriveEncoderComponent, teensy: TeensyComponent, teensy_gpio_sin: TeensyGpio, teensy_gpio_cos: TeensyGpio, logger: Logger): @@ -185,13 +179,15 @@ def run_test(self, enc: ODriveEncoderComponent, teensy: TeensyComponent, teensy_ teensy.compile_and_program(code) if enc.handle.config.mode != ENCODER_MODE_SINCOS: - enc.parent.unuse_gpios() + enc.parent.disable_mappings() + enc.parent.handle.config.gpio3_mode = GPIO_MODE_ANALOG_IN + enc.parent.handle.config.gpio4_mode = GPIO_MODE_ANALOG_IN enc.handle.config.mode = ENCODER_MODE_SINCOS + enc.handle.config.bandwidth = 100 enc.parent.save_config_and_reboot() else: time.sleep(1.0) # wait for PLLs to stabilize - enc.handle.config.bandwidth = 100 self.run_generic_encoder_test(enc.handle, 6283, 1.0, 2.0) @@ -239,12 +235,12 @@ def get_test_cases(self, testrig: TestRig): ] valid_combinations = [ - (combination[0].parent,) + tuple(combination) + (encoder, combination[0].parent,) + tuple(combination) + (None,) for combination in itertools.product(*gpio_conns) if ((len(set(c.parent for c in combination)) == 1) and isinstance(combination[0].parent, TeensyComponent)) ] - yield (encoder, valid_combinations) + yield AnyTestCase(*valid_combinations) def run_test(self, enc: ODriveEncoderComponent, teensy: TeensyComponent, teensy_gpio_a: TeensyGpio, teensy_gpio_b: TeensyGpio, teensy_gpio_c: TeensyGpio, logger: Logger): @@ -256,6 +252,15 @@ def run_test(self, enc: ODriveEncoderComponent, teensy: TeensyComponent, teensy_ if enc.handle.config.mode != ENCODER_MODE_HALL: enc.handle.config.mode = ENCODER_MODE_HALL + enc.handle.config.hall_polarity_calibrated = True + if enc.num: + enc.parent.handle.config.gpio9_mode = GPIO_MODE_DIGITAL + enc.parent.handle.config.gpio10_mode = GPIO_MODE_DIGITAL + enc.parent.handle.config.gpio11_mode = GPIO_MODE_DIGITAL + else: + enc.parent.handle.config.gpio12_mode = GPIO_MODE_DIGITAL + enc.parent.handle.config.gpio13_mode = GPIO_MODE_DIGITAL + enc.parent.handle.config.gpio14_mode = GPIO_MODE_DIGITAL enc.parent.save_config_and_reboot() else: time.sleep(1.0) # wait for PLLs to stabilize @@ -263,7 +268,7 @@ def run_test(self, enc: ODriveEncoderComponent, teensy: TeensyComponent, teensy_ enc.handle.config.bandwidth = 100 self.run_generic_encoder_test(enc.handle, true_cpr, true_rps) - enc.handle.config.cpr = 8192 + enc.parent.erase_config_and_reboot() @@ -408,28 +413,24 @@ def __init__(self, mode: int): self.mode = mode def get_test_cases(self, testrig: TestRig): - for odrive in testrig.get_components(ODriveComponent): - for encoder in odrive.encoders: - odrive_ncs_gpio = odrive.gpio7 # this GPIO choice is completely arbitrary + for encoder in testrig.get_components(ODriveEncoderComponent): + odrive = encoder.parent + odrive_ncs_gpio = odrive.gpio7 # this GPIO choice is completely arbitrary + + for teensy in testrig.get_components(TeensyComponent): gpio_conns = [ - testrig.get_connected_components(odrive.sck, TeensyGpio), - testrig.get_connected_components(odrive.miso, TeensyGpio), - testrig.get_connected_components(odrive.mosi, TeensyGpio), - testrig.get_connected_components(odrive_ncs_gpio, TeensyGpio), + testrig.net_by_component.get(odrive.sck, set()).intersection(set(teensy.gpios)), + testrig.net_by_component.get(odrive.miso, set()).intersection(set(teensy.gpios)), + testrig.net_by_component.get(odrive.mosi, set()).intersection(set(teensy.gpios)), + testrig.net_by_component.get(odrive_ncs_gpio, set()).intersection(set(teensy.gpios)), + teensy.gpios ] - valid_combinations = [] - for combination in itertools.product(*gpio_conns): - if (len(set(c.parent for c in combination)) != 1): - continue - teensy = combination[0].parent - reset_pin_options = [] - for gpio in teensy.gpios: - for local_gpio in testrig.get_connected_components(gpio, LinuxGpioComponent): - reset_pin_options.append((gpio, local_gpio)) - valid_combinations.append((teensy, *combination, reset_pin_options)) - - yield (encoder, 7, valid_combinations) + alternatives = [] + for gpio1, gpio2, gpio3, gpio4, gpio5 in itertools.product(*gpio_conns): + for local_reset_gpio, tf in testrig.get_connected_components(gpio5, LinuxGpioComponent): + alternatives.append((encoder, 7, teensy, gpio1, gpio2, gpio3, gpio4, gpio5, local_reset_gpio, tf)) + yield AnyTestCase(*alternatives) def run_test(self, enc: ODriveEncoderComponent, odrive_ncs_gpio: int, teensy: TeensyComponent, teensy_gpio_sck: TeensyGpio, teensy_gpio_miso: TeensyGpio, teensy_gpio_mosi: TeensyGpio, teensy_gpio_ncs: TeensyGpio, teensy_gpio_reset: TeensyGpio, reset_gpio: LinuxGpioComponent, logger: Logger): @@ -450,6 +451,7 @@ def run_test(self, enc: ODriveEncoderComponent, odrive_ncs_gpio: int, teensy: Te logger.debug(f'Configuring absolute encoder in mode 0x{self.mode:x}...') enc.handle.config.mode = self.mode + setattr(enc.parent.handle.config, 'gpio' + str(odrive_ncs_gpio) + '_mode', GPIO_MODE_ANALOG_IN) enc.handle.config.abs_spi_cs_gpio_pin = odrive_ncs_gpio enc.handle.config.cpr = true_cpr # Also put the other encoder into SPI mode to make it more interesting @@ -480,7 +482,7 @@ def run_test(self, enc: ODriveEncoderComponent, odrive_ncs_gpio: int, teensy: Te # Check absolute position after 1.5s time.sleep(1.5) true_delta_t = time.monotonic() - release_time - test_assert_eq(enc.handle.pos_abs, (true_delta_t * true_rps * true_cpr) % true_cpr, range = true_cpr*0.001) + test_assert_eq(enc.handle.pos_abs, (true_delta_t * true_rps * true_cpr) % true_cpr, range = true_cpr*0.002) test_assert_eq(enc.handle.error, 0) reset_gpio.write(True) @@ -495,18 +497,18 @@ def run_test(self, enc: ODriveEncoderComponent, odrive_ncs_gpio: int, teensy: Te # Check absolute position after 1.5s time.sleep(1.5) true_delta_t = time.monotonic() - release_time - test_assert_eq(enc.handle.pos_abs, (true_delta_t * true_rps * true_cpr) % true_cpr, range = true_cpr*0.001) + test_assert_eq(enc.handle.pos_abs, (true_delta_t * true_rps * true_cpr) % true_cpr, range = true_cpr*0.002) self.run_generic_encoder_test(enc.handle, true_cpr, true_rps) enc.handle.config.cpr = 8192 - +tests = [ + TestIncrementalEncoder(), + TestSinCosEncoder(), + TestHallEffectEncoder(), + TestSpiEncoder(ENCODER_MODE_SPI_ABS_AMS), + TestSpiEncoder(ENCODER_MODE_SPI_ABS_CUI), +] if __name__ == '__main__': - test_runner.run([ - TestIncrementalEncoder(), - TestSinCosEncoder(), - TestHallEffectEncoder(), - TestSpiEncoder(ENCODER_MODE_SPI_ABS_AMS), - TestSpiEncoder(ENCODER_MODE_SPI_ABS_CUI), - ]) + test_runner.run(tests) diff --git a/tools/odrive/tests/endstop_test.py b/tools/odrive/tests/endstop_manualtest.py similarity index 100% rename from tools/odrive/tests/endstop_test.py rename to tools/odrive/tests/endstop_manualtest.py diff --git a/tools/odrive/tests/fibre_test.py b/tools/odrive/tests/fibre_test.py index 75801a5b2..764ce6655 100644 --- a/tools/odrive/tests/fibre_test.py +++ b/tools/odrive/tests/fibre_test.py @@ -13,7 +13,7 @@ class FibreFunctionalTest(): """ def get_test_cases(self, testrig: TestRig): - return testrig.get_components(ODriveComponent) + return [(odrv, None) for odrv in testrig.get_components(ODriveComponent)] def run_test(self, odrive: ODriveComponent, logger: Logger): # Test property read/write @@ -23,7 +23,7 @@ def run_test(self, odrive: ODriveComponent, logger: Logger): test_assert_eq(odrive.handle.test_property, 0xffffffff) # Test function call - val = odrive.handle.get_adc_voltage(0) + val = odrive.handle.get_adc_voltage(2) # ADC pin on both ODrive 3 and 4 test_assert_within(val, 0.01, 3.29) # Test custom setter (aka property write hook) @@ -41,16 +41,17 @@ class FibreBurnInTest(): """ def get_test_cases(self, testrig: TestRig): - return testrig.get_components(ODriveComponent) + return [(odrv, None) for odrv in testrig.get_components(ODriveComponent)] def run_test(self, odrive: ODriveComponent, logger: Logger): data = record_log(lambda: [odrive.handle.vbus_voltage], duration=10.0) expected_data = np.mean(data[:,1]) * np.ones(data[:,1].size) test_curve_fit(data, expected_data, max_mean_err = 0.1, inlier_range = 0.5, max_outliers = 0) +tests = [ + FibreFunctionalTest(), + FibreBurnInTest(), +] if __name__ == '__main__': - test_runner.run([ - FibreFunctionalTest(), - FibreBurnInTest(), - ]) + test_runner.run(tests) diff --git a/tools/odrive/tests/fighting_test.py b/tools/odrive/tests/fighting_test.py new file mode 100644 index 000000000..31d725ccf --- /dev/null +++ b/tools/odrive/tests/fighting_test.py @@ -0,0 +1,69 @@ + +import test_runner + +import time +from math import pi, inf +import os + +from fibre.utils import Logger +from test_runner import * +from odrive.enums import * + +class FightingTest(): + def get_test_cases(self, testrig: TestRig): + # Iterate through all pairs of axis+motor+encoder combos on the test rig + # and return the ones that are connected through the motor shafts. + all_combos = testrig.get_closed_loop_combos() + for (axis0, motor0, encoder0, test_fixture0), (axis1, motor1, encoder1, test_fixture1) in itertools.combinations(all_combos, 2): + is_connected, test_fixture2 = testrig.check_connection(motor0.shaft, motor1.shaft) + if not is_connected: + continue # These two motors are not on the same physical axis + test_fixture = TestFixture.all_of(test_fixture0, test_fixture1, test_fixture2) + yield axis0, motor0, encoder0, axis1, motor1, encoder1, test_fixture + + def run_test(self, axis0_ctx: ODriveAxisComponent, motor0_ctx: MotorComponent, enc0_ctx: EncoderComponent, + axis1_ctx: ODriveAxisComponent, motor1_ctx: MotorComponent, enc1_ctx: EncoderComponent, logger: Logger): + + with SafeTerminator(logger, axis0_ctx, axis1_ctx): + nominal_rps = 3.0 + nominal_vel = nominal_rps + logger.debug(f'Testing closed loop velocity control at {nominal_rps} rounds/s...') + + axis0_ctx.handle.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL + axis0_ctx.handle.controller.config.input_mode = INPUT_MODE_PASSTHROUGH + axis0_ctx.handle.controller.input_vel = 0 + + request_state(axis0_ctx, AXIS_STATE_CLOSED_LOOP_CONTROL) + axis0_ctx.handle.controller.config.vel_limit = nominal_vel + axis0_ctx.handle.controller.config.vel_limit_tolerance = 2 + axis0_ctx.handle.controller.input_vel = nominal_vel + + data = record_log(lambda: [axis0_ctx.handle.encoder.vel_estimate, axis1_ctx.handle.encoder.vel_estimate, axis0_ctx.handle.encoder.pos_estimate], duration=5.0) + + test_assert_no_error(axis0_ctx) + test_assert_eq(axis0_ctx.handle.current_state, AXIS_STATE_CLOSED_LOOP_CONTROL) + request_state(axis0_ctx, AXIS_STATE_IDLE) + + # axis0: encoder.vel_estimate + slope, offset, fitted_curve = fit_line(data[:,(0,1)]) + test_assert_eq(slope, 0.0, range = nominal_vel * 0.02) + test_assert_eq(offset, nominal_vel, accuracy = 0.05) + test_curve_fit(data[:,(0,1)], fitted_curve, max_mean_err = nominal_vel * 0.3, inlier_range = nominal_vel * 0.5, max_outliers = len(data[:,0]) * 0.1) + + # axis1: encoder.vel_estimate + slope, offset, fitted_curve = fit_line(data[:,(0,2)]) + test_assert_eq(slope, 0.0, range = nominal_vel * 0.02) + test_assert_eq(offset, -nominal_vel, accuracy = 0.05) + test_curve_fit(data[:,(0,2)], fitted_curve, max_mean_err = nominal_vel * 0.3, inlier_range = nominal_vel * 0.5, max_outliers = len(data[:,0]) * 0.1) + + # axis0: encoder.pos_estimate + slope, offset, fitted_curve = fit_line(data[:,(0,3)]) + test_assert_eq(slope, nominal_vel, accuracy = 0.01) + test_curve_fit(data[:,(0,3)], fitted_curve, max_mean_err = nominal_vel * 0.01, inlier_range = nominal_vel * 0.1, max_outliers = len(data[:,0]) * 0.01) + +tests = [ + FightingTest() +] + +if __name__ == '__main__': + test_runner.run(tests) diff --git a/tools/odrive/tests/gpio_test.py b/tools/odrive/tests/gpio_test.py new file mode 100644 index 000000000..5336c24e9 --- /dev/null +++ b/tools/odrive/tests/gpio_test.py @@ -0,0 +1,102 @@ + +import test_runner + +import time +import math +import os +import numpy as np + +from odrive.enums import * +from odrive.utils import set_motor_thermistor_coeffs +from test_runner import * + +teensy_code_template = """ +int gpios[] = {{gpios}}; + +void setup() { + for (auto num: gpios) { + pinMode(num, OUTPUT); + digitalWrite(num, LOW); + } +} + +int active_gpio = 0; +void loop() { + digitalWrite(gpios[active_gpio], LOW); + active_gpio = (active_gpio + 1) % (sizeof(gpios) / sizeof(gpios[0])); + digitalWrite(gpios[active_gpio], HIGH); + delay(100); +} +""" + +class TestInputs(): + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + alternatives = [] + for teensy in testrig.get_components(TeensyComponent): + # all GPIOs except 8, 12 and 15 are digital input capable + odrive_gpios = [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 14, 16, 17, 18, 19, 20, 21, 22] + + connections = [ + testrig.net_by_component.get(getattr(odrive, f'gpio{gpio}'), set()).intersection(set(teensy.gpios)) + for gpio in odrive_gpios + ] + disconnected_gpios = [gpio for i, gpio in enumerate(odrive_gpios) if not connections[i]] + + if any(disconnected_gpios): + logger.debug(f"note: can't run GPIO test on {testrig.get_component_name(odrive)} because GPIOs {disconnected_gpios} are not connected to a Teensy.") + else: + alternatives.append((teensy, tuple(odrive_gpios), tuple([teensy_gpios.pop() for teensy_gpios in connections]))) + + yield AnyTestCase(*[(odrive, teensy, odrive_gpios, teensy_gpios, None) for teensy, odrive_gpios, teensy_gpios in alternatives]) + + def run_test(self, odrive: ODriveComponent, teensy: TeensyComponent, odrive_gpios: tuple, teensy_gpios: tuple, logger: Logger): + switch_interval = 0.1 # [s] + + code = teensy_code_template.replace("{gpios}", ", ".join(str(gpio.num) for gpio in teensy_gpios)) + teensy.compile_and_program(code) + + for gpio_num in odrive_gpios: + setattr(odrive.handle.config, f'gpio{gpio_num}_mode', GPIO_MODE_DIGITAL) + odrive.save_config_and_reboot() + + mask = sum((1 << num) for num in odrive_gpios) + + # Wait for a change in the GPIO states (but at most 3x the expected interval) + initial_state = odrive.handle.get_gpio_states() & mask + for i in range(30): + if odrive.handle.get_gpio_states() & mask != initial_state: + break + time.sleep(0.1 * switch_interval) + + # Sync to the middle of the GPIO switch frame + time.sleep(0.5 * switch_interval) + + # Take one sample for each switch frame + meas = [] + for _ in range(len(odrive_gpios)): + meas.append(odrive.handle.get_gpio_states() & mask) + time.sleep(1.0 * switch_interval) + + expected = [(1 << num) for num in odrive_gpios] + + # Find the best rotation of the measurement array + best_match = None + best_match_val = -1 + for _ in range(len(odrive_gpios)): + meas = meas[1:] + [meas[0]] + similarity = sum([m == expected[i] for i, m in enumerate(meas)]) + if similarity > best_match_val: + best_match = meas + best_match_val = similarity + + # Check if all values match + test_assert_eq(best_match, expected) + + +tests = [ + TestInputs() +] + +if __name__ == '__main__': + test_runner.run(tests) diff --git a/tools/odrive/tests/integration_test.py b/tools/odrive/tests/integration_test.py index 0f66d8c6d..5d5a6e80e 100644 --- a/tools/odrive/tests/integration_test.py +++ b/tools/odrive/tests/integration_test.py @@ -106,10 +106,12 @@ def prepare(self, odrive: ODriveComponent, canbus: CanInterfaceComponent, axis_c # Make sure there are no funny configurations active logger.debug('Setting up clean configuration...') axis_ctx.parent.erase_config_and_reboot() + axis_ctx.parent.handle.config.enable_brake_resistor = True + axis_ctx.parent.save_config_and_reboot() # run calibration axis_ctx.handle.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE - while axis_ctx.handle.current_state != AXIS_STATE_IDLE: + while axis_ctx.handle.requested_state != AXIS_STATE_UNDEFINED or axis_ctx.handle.current_state != AXIS_STATE_IDLE: time.sleep(1) test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) test_assert_no_error(axis_ctx) @@ -127,17 +129,11 @@ def __exit__(self, exc_type, exc_val, exc_tb): def get_test_cases(self, testrig: TestRig): - for odrive in testrig.get_components(ODriveComponent): - can_interfaces = list(testrig.get_connected_components(odrive.can, CanInterfaceComponent)) - for num in range(2): - encoders = testrig.get_connected_components({ - 'a': (odrive.encoders[num].a, False), - 'b': (odrive.encoders[num].b, False) - }, EncoderComponent) - motors = testrig.get_connected_components(odrive.axes[num], MotorComponent) - for motor, encoder in itertools.product(motors, encoders): - if encoder.impl in testrig.get_connected_components(motor): - yield (odrive, can_interfaces, odrive.axes[num], motor, encoder, 0, False) + for axis, motor, encoder, tf1 in testrig.get_closed_loop_combos(init=False): + yield AnyTestCase(*[ + (axis.parent, canbus, axis, motor, encoder, 0, False, TestFixture.all_of(tf1, tf2)) + for canbus, tf2 in testrig.get_connected_components(axis.parent.can, CanInterfaceComponent) + ]) def run_test(self, odrive: ODriveComponent, canbus: CanInterfaceComponent, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, node_id: int, extended_id: bool, logger: Logger): # this test is a sanity check to make sure that closed loop operation works @@ -147,29 +143,29 @@ def run_test(self, odrive: ODriveComponent, canbus: CanInterfaceComponent, axis_ def my_cmd(cmd_name, **kwargs): command(canbus.handle, node_id, extended_id, cmd_name, **kwargs) def my_req(cmd_name, **kwargs): return asyncio.run(request(canbus.handle, node_id, extended_id, cmd_name, **kwargs)) def fence(): my_req('get_vbus_voltage') # fence to ensure the CAN command was sent + def flush_rx(): + while not canbus.handle.recv(timeout = 0) is None: pass - # make sure no gpio input is overwriting our values - odrive.unuse_gpios() - axis_ctx.handle.config.enable_watchdog = False - axis_ctx.handle.clear_errors() - axis_ctx.handle.config.can_node_id = node_id - axis_ctx.handle.config.can_node_id_extended = extended_id + odrive.handle.clear_errors() + axis_ctx.handle.config.can.node_id = node_id + axis_ctx.handle.config.can.is_extended = extended_id time.sleep(0.1) my_cmd('set_node_id', node_id=node_id+20) + flush_rx() asyncio.run(request(canbus.handle, node_id+20, extended_id, 'get_vbus_voltage')) - test_assert_eq(axis_ctx.handle.config.can_node_id, node_id+20) + test_assert_eq(axis_ctx.handle.config.can.node_id, node_id+20) # Reset node ID to default value command(canbus.handle, node_id+20, extended_id, 'set_node_id', node_id=node_id) fence() - test_assert_eq(axis_ctx.handle.config.can_node_id, node_id) + test_assert_eq(axis_ctx.handle.config.can.node_id, node_id) vel_limit = 15.0 nominal_vel = 10.0 axis_ctx.handle.controller.config.vel_limit = vel_limit - axis_ctx.handle.motor.config.current_lim = 30.0 + axis_ctx.handle.motor.config.current_lim = 20.0 my_cmd('set_requested_state', requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL) fence() @@ -226,5 +222,7 @@ def fence(): my_req('get_vbus_voltage') # fence to ensure the CAN command was se fence() test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) +tests = [TestSimpleCANClosedLoop()] + if __name__ == '__main__': - test_runner.run(TestSimpleCANClosedLoop()) \ No newline at end of file + test_runner.run(tests) \ No newline at end of file diff --git a/tools/odrive/tests/not_a_test.py b/tools/odrive/tests/not_a_test.py index 7be3e9954..7b89bb0cb 100644 --- a/tools/odrive/tests/not_a_test.py +++ b/tools/odrive/tests/not_a_test.py @@ -11,20 +11,18 @@ class EncoderPassthrough(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - for num in range(1): - encoders = testrig.get_connected_components({ - 'a': (odrive.encoders[num].a, False), - 'b': (odrive.encoders[num].b, False), - 'z': (odrive.encoders[num].z, False) - }, EncoderComponent) - motors = testrig.get_connected_components(odrive.axes[num], MotorComponent) - - for motor, encoder in itertools.product(motors, encoders): - if encoder.impl in testrig.get_connected_components(motor): - yield (odrive.axes[num], motor, encoder) - - def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): + encoders = testrig.get_connected_components({ + 'a': (odrive.encoders[0].a, False), + 'b': (odrive.encoders[0].b, False), + 'z': (odrive.encoders[0].z, False) + }, EncoderComponent) + + yield AnyTestCase(*[(odrive.axes[0], test_fixture) for encoder, test_fixture in encoders]) + + def run_test(self, axis_ctx: ODriveAxisComponent, logger: Logger): logger.debug(f'Encoder {axis_ctx.num} was passed through') +tests = [EncoderPassthrough()] + if __name__ == '__main__': - test_runner.run(EncoderPassthrough()) + test_runner.run(tests) diff --git a/tools/odrive/tests/nvm_test.py b/tools/odrive/tests/nvm_test.py index 72ed20618..6bcf5d028 100644 --- a/tools/odrive/tests/nvm_test.py +++ b/tools/odrive/tests/nvm_test.py @@ -16,7 +16,7 @@ class TestStoreAndReboot(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - yield (odrive,) + yield (odrive, None) def run_with_values(self, odrive: ODriveComponent, values: list, logger: Logger): logger.debug("storing configuration and rebooting...") @@ -24,13 +24,7 @@ def run_with_values(self, odrive: ODriveComponent, values: list, logger: Logger) for value in values: odrive.handle.config.brake_resistance = value - odrive.handle.save_configuration() - try: - odrive.handle.reboot() - except fibre.ChannelBrokenException: - pass # this is expected - odrive.handle = None - time.sleep(2) + odrive.save_config_and_reboot() odrive.prepare(logger) @@ -42,5 +36,7 @@ def run_test(self, odrive: ODriveComponent, logger: Logger): self.run_with_values(odrive, [2.5, 3.7], logger) self.run_with_values(odrive, [0.47], logger) +tests = [TestStoreAndReboot()] + if __name__ == '__main__': - test_runner.run(TestStoreAndReboot()) + test_runner.run(tests) diff --git a/tools/odrive/tests/old_tests.py b/tools/odrive/tests/old_tests.py index fac90f1de..48ae48c2c 100644 --- a/tools/odrive/tests/old_tests.py +++ b/tools/odrive/tests/old_tests.py @@ -225,7 +225,7 @@ def run_test(self, odrv_ctx: ODriveTestContext, logger): # this is a firmware issue since it persists when unplugging/replugging # but goes away when power cycling the device odrv_ctx.handle.reboot() - except fibre.ChannelBrokenException: + except fibre.ObjectLostError: pass # this is expected time.sleep(0.5) diff --git a/tools/odrive/tests/pwm_input_test.py b/tools/odrive/tests/pwm_input_test.py index a1945997b..672023df9 100644 --- a/tools/odrive/tests/pwm_input_test.py +++ b/tools/odrive/tests/pwm_input_test.py @@ -49,11 +49,23 @@ class TestPwmInput(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - # Run a separate test for each PWM-capable GPIO. Use different min/max settings for each test. - yield (odrive, 1, -50, 200, list(testrig.get_connected_components(odrive.gpio1, TeensyGpio))) - yield (odrive, 2, 20, 400, list(testrig.get_connected_components(odrive.gpio2, TeensyGpio))) - yield (odrive, 3, -1000, 0, list(testrig.get_connected_components(odrive.gpio3, TeensyGpio))) - yield (odrive, 4, -20000, 20000, list(testrig.get_connected_components(odrive.gpio4, TeensyGpio))) + if odrive.yaml['board-version'].startswith('v3.'): + # Run a separate test for each PWM-capable GPIO. Use different min/max settings for each test. + test_cases = [(1, -50, 200, odrive.gpio1), + (2, 20, 400, odrive.gpio2), + (3, -1000, 0, odrive.gpio3), + (4, -20000, 20000, odrive.gpio4)] + elif odrive.yaml['board-version'].startswith('v4.'): + # Run a separate test for each PWM-capable GPIO. Use different min/max settings for each test. + test_cases = [(14, -50, 200, odrive.gpio14), + (19, 20, 400, odrive.gpio19), + (20, -20000, 20000, odrive.gpio20), + (21, -1000, 0, odrive.gpio21)] + else: + raise Exception(f"unknown board version {odrive.yaml['board-version']}") + + for test_case in test_cases: + yield AnyTestCase(*[(odrive,) + tuple(test_case[:-1]) + (teensy_gpio,tf,) for teensy_gpio, tf in testrig.get_connected_components(test_case[-1], TeensyGpio)]) def run_test(self, odrive: ODriveComponent, odrive_gpio_num: int, min_val: float, max_val: float, teensy_gpio: Component, logger: Logger): teensy = teensy_gpio.parent @@ -61,16 +73,11 @@ def run_test(self, odrive: ODriveComponent, odrive_gpio_num: int, min_val: float teensy.compile_and_program(code) logger.debug("Set up PWM input...") - odrive.unuse_gpios() + odrive.disable_mappings() - pwm_mapping = [ - odrive.handle.config.gpio1_pwm_mapping, - odrive.handle.config.gpio2_pwm_mapping, - odrive.handle.config.gpio3_pwm_mapping, - odrive.handle.config.gpio4_pwm_mapping - ][odrive_gpio_num - 1] - - pwm_mapping.endpoint = odrive.handle.axis0.controller._remote_attributes['input_pos'] + setattr(odrive.handle.config, f'gpio{odrive_gpio_num}_mode', GPIO_MODE_PWM) + pwm_mapping = getattr(odrive.handle.config, f'gpio{odrive_gpio_num}_pwm_mapping') + pwm_mapping.endpoint = odrive.handle.axis0.controller._input_pos_property pwm_mapping.min = min_val pwm_mapping.max = max_val @@ -83,7 +90,7 @@ def run_test(self, odrive: ODriveComponent, odrive_gpio_num: int, min_val: float test_assert_eq(slope, full_scale / 1.0, accuracy=0.001) test_curve_fit(data, fitted_curve, max_mean_err = full_scale * 0.05, inlier_range = full_scale * 0.05, max_outliers = len(data[:,0]) * 0.01) - +tests = [TestPwmInput()] if __name__ == '__main__': - test_runner.run(TestPwmInput()) + test_runner.run(tests) diff --git a/tools/odrive/tests/results.html.j2 b/tools/odrive/tests/results.html.j2 new file mode 100644 index 000000000..cb644f3d6 --- /dev/null +++ b/tools/odrive/tests/results.html.j2 @@ -0,0 +1,77 @@ + + + +Test Results + + + +

Test Results

+

Status: {{ status }}

+

This summary was generated on {{ date.isoformat() }}

+ + + + + +{% for name, test_cases in test_results %} + + + + +{% endfor %} +
TestResult
+{% if test_cases | fails | length %} +
+{% elif test_cases | passes | length %} +
+{% else %} +
+{% endif %} + {{name}} +
{{test_cases | passes | length}} / {{test_cases | length}}
+ + diff --git a/tools/odrive/tests/run_all_tests.sh b/tools/odrive/tests/run_all_tests.sh new file mode 100755 index 000000000..94a90f616 --- /dev/null +++ b/tools/odrive/tests/run_all_tests.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +declare -a tests=('analog_input_test.py' + 'calibration_test.py' + 'can_test.py' + 'closed_loop_test.py' + 'encoder_test.py' + 'fibre_test.py' + 'integration_test.py' + 'nvm_test.py' + 'pwm_input_test.py' + 'sensor_test.py' + 'step_dir_test.py' + 'uart_ascii_test.py' + ) +summary="" + +for test in "${tests[@]}"; do + (ipython3 "$test" || true) | tee /tmp/odrivetest.log + if grep "All tests passed!" /tmp/odrivetest.log; then + summary="$summary - $test: passed"$'\n' + else + summary="$summary - $test: failed"$'\n' + fi + + echo "########################" + echo "Current status:" + echo -n "$summary" + echo "########################" +done diff --git a/tools/odrive/tests/sensor_test.py b/tools/odrive/tests/sensor_test.py new file mode 100644 index 000000000..e6a39b4bc --- /dev/null +++ b/tools/odrive/tests/sensor_test.py @@ -0,0 +1,130 @@ + +import test_runner + +import time +import math +import os +import numpy as np + +from odrive.enums import * +from odrive.utils import set_motor_thermistor_coeffs +from test_runner import * + +def test_assert_not_stale(data): + if len(set(data)) <= 2: + raise TestFailed(f"data seems stale: {set(data)}") + +class TestVbusVoltage(): + """ + Checks if the onboard DC voltage sensor measures the expected test rig + voltage without too much noise. + """ + + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + yield odrive, None + + def run_test(self, odrive: ODriveComponent, logger: Logger): + # TODO: vary voltage with programmable power supply + expected_voltage = float(odrive.yaml['vbus-voltage']) + + data = record_log(lambda: [odrive.handle.vbus_voltage], duration=5.0) + slope, offset, fitted_curve = fit_line(data[:,(0,1)]) + test_assert_not_stale(data[:,1]) + test_assert_eq(offset, expected_voltage, accuracy=0.02) + test_assert_eq(slope, 0, range=0.01) + test_curve_fit(data[:,(0,1)], fitted_curve, max_mean_err = expected_voltage * 0.01, inlier_range = expected_voltage * 0.02, max_outliers = len(data[:,0]) * 0.01) + +class TestOnboardTempSensors(): + """ + Checks if the onboard FET thermistor measures a plausible non-stale + temperature without too much noise. + """ + + def get_test_cases(self, testrig: TestRig): + for axis in testrig.get_components(ODriveAxisComponent): + yield axis, None + + def run_test(self, axis: ODriveAxisComponent, logger: Logger): + data = record_log(lambda: [axis.handle.motor.fet_thermistor.temperature], duration=5.0) + slope, offset, fitted_curve = fit_line(data[:,(0,1)]) + test_assert_not_stale(data[:,1]) + test_assert_within(offset, 10, 80) # fet temp expected to be between 10°C and 80°C + test_assert_eq(slope, 0, range=0.01) + test_curve_fit(data[:,(0,1)], fitted_curve, max_mean_err = 0.2, inlier_range = 0.5, max_outliers = len(data[:,0]) * 0.01) + + +teensy_code_template = """ +void setup() { + analogWriteResolution(10); + // base clock of the PWM timer is 150MHz (on Teensy 4.0) + int freq = 150000000/1024; // ~146.5kHz PWM frequency + analogWriteFrequency({analog_out}, freq); +} + +const float r_load = 1000.0f; +const float r_testrig = 150.0f; // resistance between Teensy and ODrive +const float r_25 = 10000.0f; +const float beta = 3434; +const float r_inf = r_25 * exp(-beta/(25.0f + 273.15f)); + +const float t_min = 20.0f; +const float t_max = 100.0f; +const float period_ms = 1000.0f; + +int time_ms = 0; +void loop() { + time_ms = (time_ms + 1) % 1000; + + // simulated temperature + float temp = t_min + time_ms / period_ms * (t_max - t_min) + 273.15f; + + // simulated thermistor resistance + float resistance = r_inf * exp(beta/temp); + + // actual output voltage (relative to Vcc) + float output = (resistance - r_testrig) / (resistance + r_load); + + analogWrite({analog_out}, 1023 * min(max(output, 0.0f), 1.0f)); + delay(1); +} +""" + +class TestOffboardTempSensors(): + """ + Verifies the motor thermistor input. + + The PWM output of a Teensy is used to simulate a thermistor that goes from + 20°C to 100°C in one second. + On ODrive v4.x the motor temp input must be connected to the Teensy GPIO + through a 150 Ohm resistor. The onboard low pass filter is used to converts + the Teensy's PWM to an analog signal. + """ + def get_test_cases(self, testrig: TestRig): + for odrive in testrig.get_components(ODriveComponent): + if odrive.yaml['board-version'].startswith('v4.'): + teensy_gpios = testrig.get_connected_components(odrive.gpio2, TeensyGpio) + yield AnyTestCase(*[(odrive.axes[0], teensy_gpio.parent, teensy_gpio, tf) for teensy_gpio, tf in teensy_gpios]) + + def run_test(self, axis: ODriveAxisComponent, teensy: TeensyComponent, teensy_gpio: TeensyGpio, logger: Logger): + code = teensy_code_template.replace("{analog_out}", str(teensy_gpio.num)) + teensy.compile_and_program(code) + + set_motor_thermistor_coeffs(axis.handle, Rload=1000, R_25=10000, Beta=3434, + Tmin=0, Tmax=140, thermistor_bottom=True) + axis.parent.handle.config.gpio2_mode = GPIO_MODE_ANALOG_IN + axis.parent.save_config_and_reboot() + + data = record_log(lambda: [axis.handle.motor.motor_thermistor.temperature], duration=5.0) + slope, offset, fitted_curve = fit_sawtooth(data[:,(0,1)], 20, 100) + test_assert_eq(slope, (100 - 20) / 1.0, accuracy=0.005) + test_curve_fit(data[:,(0,1)], fitted_curve, max_mean_err = 3.0, inlier_range = 6.0, max_outliers = len(data[:,0]) * 0.02) + +tests = [ + TestVbusVoltage(), + TestOnboardTempSensors(), + TestOffboardTempSensors() +] + +if __name__ == '__main__': + test_runner.run(tests) diff --git a/tools/odrive/tests/step_dir_test.py b/tools/odrive/tests/step_dir_test.py index 948a5eca1..3f0efa4d0 100644 --- a/tools/odrive/tests/step_dir_test.py +++ b/tools/odrive/tests/step_dir_test.py @@ -19,32 +19,31 @@ class TestStepDir(): """ def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - gpio_conns = [ - list(testrig.get_connected_components((odrive.gpio1, False), LinuxGpioComponent)), - list(testrig.get_connected_components((odrive.gpio2, False), LinuxGpioComponent)), - #list(testrig.get_connected_components((odrive.gpio3, False), LinuxGpioComponent)), # connected to LPF on test rig - #list(testrig.get_connected_components((odrive.gpio4, False), LinuxGpioComponent)), # connected to LPF on test rig - list(testrig.get_connected_components((odrive.gpio5, False), LinuxGpioComponent)), - list(testrig.get_connected_components((odrive.gpio6, False), LinuxGpioComponent)), - list(testrig.get_connected_components((odrive.gpio7, False), LinuxGpioComponent)), - list(testrig.get_connected_components((odrive.gpio8, False), LinuxGpioComponent)), - ] - - yield (odrive.axes[0], 1, gpio_conns[0], 2, gpio_conns[1]) - yield (odrive.axes[0], 5, gpio_conns[2], 6, gpio_conns[3]) - yield (odrive.axes[0], 7, gpio_conns[4], 8, gpio_conns[5]) # broken - # yield (odrive.axes[0], 7, gpio_conns[6], 8, gpio_conns[7]) # broken + def stepdir_test_case(axis, step_gpio_num, dir_gpio_num): + alternatives = [] + for step_ctrl_gpio, tf1 in testrig.get_connected_components((getattr(odrive, f'gpio{step_gpio_num}'), False), LinuxGpioComponent): + for dir_ctrl_gpio, tf2 in testrig.get_connected_components((getattr(odrive, f'gpio{dir_gpio_num}'), False), LinuxGpioComponent): + alternatives.append((axis, step_gpio_num, step_ctrl_gpio, dir_gpio_num, dir_ctrl_gpio, TestFixture.all_of(tf1, tf2))) + return AnyTestCase(*alternatives) + + yield stepdir_test_case(odrive.axes[0], 1, 2) + yield stepdir_test_case(odrive.axes[0], 5, 6) + yield stepdir_test_case(odrive.axes[0], 7, 8) - yield (odrive.axes[1], 7, gpio_conns[4], 8, gpio_conns[5]) + # test other axes + for i in range(1, len(odrive.axes)): + yield stepdir_test_case(odrive.axes[i], 7, 8) - def run_test(self, axis: ODriveAxisComponent, step_gpio_num: int, step_gpio: LinuxGpioComponent, dir_gpio_num: int, dir_gpio: LinuxGpioComponent, logger: Logger): + def run_test(self, axis: ODriveAxisComponent, step_gpio_num: int, step_gpio: LinuxGpioComponent, dir_gpio_num: int, dir_gpio: LinuxGpioComponent, logger: Logger): step_gpio.config(output=True) step_gpio.write(False) dir_gpio.config(output=True) dir_gpio.write(True) - if axis.num == 0: - axis.parent.handle.config.enable_uart = False + axis.parent.erase_config_and_reboot() + setattr(axis.parent.handle.config, 'gpio' + str(step_gpio_num) + '_mode', GPIO_MODE_DIGITAL) + setattr(axis.parent.handle.config, 'gpio' + str(dir_gpio_num) + '_mode', GPIO_MODE_DIGITAL) + axis.parent.save_config_and_reboot() axis.handle.config.enable_step_dir = True axis.handle.config.step_dir_always_on = True # needed for testing axis.handle.config.step_gpio_pin = step_gpio_num @@ -52,6 +51,7 @@ def run_test(self, axis: ODriveAxisComponent, step_gpio_num: int, step_gpio: Li request_state(axis, AXIS_STATE_IDLE) # apply step_dir_always_on config + axis.handle.controller.input_pos = 0 ref = axis.handle.controller.input_pos axis.handle.config.turns_per_step = turns_per_step = 10 @@ -59,6 +59,7 @@ def run_test(self, axis: ODriveAxisComponent, step_gpio_num: int, step_gpio: Li for i in range(100): step_gpio.write(True) + test_assert_eq(axis.handle.controller.input_pos, ref + (i + 1) * turns_per_step, range = 0.4 * turns_per_step) step_gpio.write(False) test_assert_eq(axis.handle.controller.input_pos, ref + (i + 1) * turns_per_step, range = 0.4 * turns_per_step) @@ -67,6 +68,7 @@ def run_test(self, axis: ODriveAxisComponent, step_gpio_num: int, step_gpio: Li for i in range(100): step_gpio.write(True) + test_assert_eq(axis.handle.controller.input_pos, ref - (i + 1) * turns_per_step, range = 0.4 * turns_per_step) step_gpio.write(False) test_assert_eq(axis.handle.controller.input_pos, ref - (i + 1) * turns_per_step, range = 0.4 * turns_per_step) @@ -76,6 +78,7 @@ def run_test(self, axis: ODriveAxisComponent, step_gpio_num: int, step_gpio: Li for i in range(100): step_gpio.write(True) + test_assert_eq(axis.handle.controller.input_pos, ref + (i + 1) * turns_per_step, range = 0.4 * turns_per_step) step_gpio.write(False) test_assert_eq(axis.handle.controller.input_pos, ref + (i + 1) * turns_per_step, range = 0.4 * turns_per_step) @@ -84,9 +87,11 @@ def run_test(self, axis: ODriveAxisComponent, step_gpio_num: int, step_gpio: Li for i in range(100): step_gpio.write(True) + test_assert_eq(axis.handle.controller.input_pos, ref + (i + 1) * turns_per_step, range = 0.4 * abs(turns_per_step)) step_gpio.write(False) test_assert_eq(axis.handle.controller.input_pos, ref + (i + 1) * turns_per_step, range = 0.4 * abs(turns_per_step)) +tests = [TestStepDir()] if __name__ == '__main__': - test_runner.run(TestStepDir()) + test_runner.run(tests) diff --git a/tools/odrive/tests/test_runner.py b/tools/odrive/tests/test_runner.py old mode 100644 new mode 100755 index 9633b92f8..c55a3be7a --- a/tools/odrive/tests/test_runner.py +++ b/tools/odrive/tests/test_runner.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + # Provides utilities for standalone test scripts. # This script is not intended to be run directly. @@ -7,6 +9,7 @@ import stat import odrive from odrive.enums import * +import odrive.utils import fibre from fibre import Logger, Event import argparse @@ -16,7 +19,10 @@ import time import tempfile import io +import re +import datetime from typing import Union, Tuple +import traceback # needed for curve fitting import numpy as np @@ -69,14 +75,14 @@ def test_assert_within(observed, lower_bound, upper_bound, accuracy=0.0): def disjoint_sets(list_of_sets: list): while len(list_of_sets): current_set, list_of_sets = list_of_sets[0], list_of_sets[1:] - did_update = True - while did_update: - did_update = False + updated = True + while updated: + updated = False for i, s in enumerate(list_of_sets): if len(current_set.intersection(s)): current_set = current_set.union(s) list_of_sets = list_of_sets[:i] + list_of_sets[(i+1):] - did_update = True + updated = True yield current_set def is_list_like(arg): @@ -196,6 +202,31 @@ def test_watchdog(axis, feed_func, logger: Logger): time.sleep(1.3) # let the watchdog expire test_assert_eq(axis.error, AXIS_ERROR_WATCHDOG_TIMER_EXPIRED) +class SafeTerminator(): + """ + Context that can be used in a "with" statement to facilitate safe shutdown + of the test rig when the test ends (succeeds or fails). + """ + def __init__(self, logger, *axes): + self.logger = logger + self.axes = axes + def __enter__(self): + pass + def __exit__(self, exc_type, exc_val, exc_tb): + # TODO: cut power supply + self.logger.debug('clearing config...') + idle_axes = [] + for axis_ctx in self.axes: + try: + axis_ctx.handle.requested_state = AXIS_STATE_IDLE + idle_axes.append(axis_ctx) + except: + self.logger.error(f"can't put axis {axis_ctx} into idle") + # TODO: review if erase_configuration is safe during active PWM + time.sleep(0.005) + for axis_ctx in set(axis for axis in idle_axes): + axis_ctx.parent.erase_config_and_reboot() + # Test Components -------------------------------------------------------------# class Component(object): @@ -206,10 +237,19 @@ class ODriveComponent(Component): def __init__(self, yaml: dict): self.handle = None self.yaml = yaml - #self.axes = [ODriveAxisComponent(None), ODriveAxisComponent(None)] - self.encoders = [ODriveEncoderComponent(self, 0, yaml['encoder0']), ODriveEncoderComponent(self, 1, yaml['encoder1'])] - self.axes = [ODriveAxisComponent(self, 0, yaml['motor0']), ODriveAxisComponent(self, 1, yaml['motor1'])] - for i in range(1,9): + + if yaml['board-version'].startswith("v3."): + self.encoders = [ODriveEncoderComponent(self, 0, yaml['encoder0']), ODriveEncoderComponent(self, 1, yaml['encoder1'])] + self.axes = [ODriveAxisComponent(self, 0, yaml['motor0']), ODriveAxisComponent(self, 1, yaml['motor1'])] + gpio_nums = range(1,9) + elif yaml['board-version'].startswith("v4."): + self.encoders = [ODriveEncoderComponent(self, 0, yaml['encoder0'])] + self.axes = [ODriveAxisComponent(self, 0, yaml['motor0'])] + gpio_nums = range(23) + else: + raise Exception("unknown board version {}".format(yaml['board-version'])) + + for i in gpio_nums: self.__setattr__('gpio' + str(i), Component(self)) self.can = Component(self) self.sck = Component(self) @@ -221,8 +261,9 @@ def get_subcomponents(self): yield 'encoder' + str(enc_ctx.num), enc_ctx for axis_ctx in self.axes: yield 'axis' + str(axis_ctx.num), axis_ctx - for i in range(1,9): - yield ('gpio' + str(i)), getattr(self, 'gpio' + str(i)) + for k in dir(self): + if k.startswith('gpio'): + yield k, getattr(self, k) yield 'can', self.can yield 'spi.sck', self.sck yield 'spi.miso', self.miso @@ -236,33 +277,28 @@ def prepare(self, logger: Logger): return logger.debug('waiting for {} ({})'.format(self.yaml['name'], self.yaml['serial-number'])) - self.handle = odrive.find_any( - path="usb", serial_number=self.yaml['serial-number'], timeout=60)#, printer=print) + self.handle = odrive.find_any(channel_termination_token=shutdown_token, serial_number=self.yaml['serial-number'], timeout=60)#, printer=print) assert(self.handle) #for axis_idx, axis_ctx in enumerate(self.axes): - # axis_ctx.handle = self.handle.__dict__['axis{}'.format(axis_idx)] + # axis_ctx.handle = getattr(self.handle, f'axis{axis_idx}') for encoder_idx, encoder_ctx in enumerate(self.encoders): - encoder_ctx.handle = self.handle.__dict__['axis{}'.format(encoder_idx)].encoder + encoder_ctx.handle = getattr(self.handle, f'axis{encoder_idx}').encoder # TODO: distinguish between axis and motor context for axis_idx, axis_ctx in enumerate(self.axes): - axis_ctx.handle = self.handle.__dict__['axis{}'.format(axis_idx)] - - def unuse_gpios(self): - self.handle.config.enable_uart = False - self.handle.axis0.config.enable_step_dir = False - self.handle.axis1.config.enable_step_dir = False - self.handle.config.gpio1_pwm_mapping.endpoint = None - self.handle.config.gpio2_pwm_mapping.endpoint = None - self.handle.config.gpio3_pwm_mapping.endpoint = None - self.handle.config.gpio4_pwm_mapping.endpoint = None - self.handle.config.gpio3_analog_mapping.endpoint = None - self.handle.config.gpio4_analog_mapping.endpoint = None + axis_ctx.handle = getattr(self.handle, f'axis{axis_idx}') + + def disable_mappings(self): + for k in dir(self.handle.config): + if re.match(r'gpio[0-9]+_pwm_mapping', k): + getattr(self.handle.config, k).endpoint = None + for k in dir(self.handle.config): + if re.match(r'gpio[0-9]+_analog_mapping', k): + getattr(self.handle.config, k).endpoint = None def save_config_and_reboot(self): - self.handle.save_configuration() try: - self.handle.reboot() - except fibre.ChannelBrokenException: + self.handle.save_configuration() + except fibre.ObjectLostError: pass # this is expected self.handle = None time.sleep(2) @@ -271,7 +307,7 @@ def save_config_and_reboot(self): def erase_config_and_reboot(self): try: self.handle.erase_configuration() - except fibre.ChannelBrokenException: + except fibre.ObjectLostError: pass # this is expected self.handle = None time.sleep(2) @@ -280,6 +316,11 @@ def erase_config_and_reboot(self): class MotorComponent(Component): def __init__(self, yaml: dict): self.yaml = yaml + self.shaft = Component(self) + self.phases = Component(self) + + def get_subcomponents(self): + return [('shaft', self.shaft), ('phases', self.phases)] def prepare(self, logger: Logger): pass @@ -317,9 +358,10 @@ def __init__(self, parent: Component, yaml: dict): self.z = Component(self) self.a = Component(self) self.b = Component(self) + self.shaft = Component(self) def get_subcomponents(self): - return [('z', self.z), ('a', self.a), ('b', self.b)] + return [('z', self.z), ('a', self.a), ('b', self.b), ('shaft', self.shaft)] class GeneralPurposeComponent(Component): def __init__(self, yaml: dict): @@ -353,10 +395,12 @@ class SerialPortComponent(Component): def __init__(self, parent: Component, yaml: dict): Component.__init__(self, parent) self.yaml = yaml + self.tx = Component(self) + self.rx = Component(self) def get_subcomponents(self): - yield 'tx', Component(self) - yield 'rx', Component(self) + yield 'tx', self.tx + yield 'rx', self.rx def open(self, baudrate: int): import serial @@ -384,7 +428,12 @@ class TeensyComponent(Component): def __init__(self, testrig, yaml: dict): self.testrig = testrig self.yaml = yaml - self.gpios = [TeensyGpio(self, i) for i in range(24)] + if self.yaml['board-version'] == 'teensy:avr:teensy40': + self.gpios = [TeensyGpio(self, i) for i in range(24)] + elif self.yaml['board-version'] == 'teensy:avr:teensy41': + self.gpios = [TeensyGpio(self, i) for i in range(42)] + else: + raise Exception(f"unknown Arduino board {self.yaml['board-version']}") self.routes = [] self.previous_routes = object() @@ -427,7 +476,7 @@ def compile(self, sketchfile, hexfile): env = os.environ.copy() env['ARDUINO_COMPILE_DESTINATION'] = hexfile run_shell( - ['arduino', '--board', 'teensy:avr:teensy40', '--verify', sketchfile], + ['arduino', '--board', self.yaml['board-version'], '--verify', sketchfile], logger, env = env, timeout = 120) def program(self, hex_file_path: str, logger: Logger): @@ -446,7 +495,7 @@ def program(self, hex_file_path: str, logger: Logger): time.sleep(0.1) program_gpio.write(True) - run_shell(["teensy-loader-cli", "-mmcu=imxrt1062", "-w", hex_file_path], logger, timeout = 5) + run_shell(["teensy-loader-cli", "-mmcu=" + self.yaml['board-version'].rpartition(':')[2].upper(), "-w", hex_file_path], logger, timeout = 5) time.sleep(0.5) # give it some time to boot def compile_and_program(self, code: str): @@ -469,27 +518,121 @@ def __init__(self, parent: Component): def get_subcomponents(self): yield 'en', self.en +class TestFixture(object): + """ + Base class for test fixtures. A test fixture is what defines the + prerequisites for a paricular test. + """ -class ProxiedComponent(Component): - def __init__(self, impl, *gpio_tuples): - """ - Each element in gpio_tuples should be a tuple of the form: - (teensy: TeensyComponent, gpio_in, gpio_out, gpio_noise_enable) - """ - Component.__init__(self, getattr(impl, 'parent', None)) - self.impl = impl - assert(all([len(t) == 4 for t in gpio_tuples])) - self.gpio_tuples = list(gpio_tuples) + def all_of(*test_fixtures): + test_fixtures = [(tf._subfixtures if isinstance(tf, CompositeTestFixture) else [[tf]]) + for tf in test_fixtures if not tf is None] + # Each item in test_fixtures is now in disjunctive normal form and we + # flatten this into a single expression in disjunctive normal form + combinations = [] + for combination in itertools.product([[]], *test_fixtures): + # flatten list of lists + combination = [item for c in combination for item in c] + combinations.append(combination) + + if len(combinations) == 1 and len(combinations[0]) == 0: + return None + elif len(combinations) == 1 and len(combinations[0]) == 1: + return combinations[0][0] + else: + return CompositeTestFixture(combinations) + + def any_of(*test_fixtures): + test_fixtures = [(tf._subfixtures if isinstance(tf, CompositeTestFixture) else [[tf]]) + for tf in test_fixtures] + # Flatten list of lists into list + combinations = [item for c in test_fixtures for item in c] + if len(combinations) == 1 and len(combinations[0]) == 0: + return None + elif len(combinations) == 1 and len(combinations[0]) == 1: + return combinations[0][0] + else: + return CompositeTestFixture(combinations) + +class CompositeTestFixture(TestFixture): + """ + Represents the combination of several test fixtures. The combinations are + stored as a list of lists representing a disjunctive normal form. + Do not construct this class directly, use TestFixture.any_of or + TestFixture.all_of instead. + """ + def __init__(self, subfixtures: list): + self._subfixtures = subfixtures + + +class TeensyForwardingFixture(TestFixture): + def __init__(self, teensy: TeensyComponent, high_z: TeensyGpio, low_z: TeensyGpio): + self.teensy = teensy + self.high_z = high_z + self.low_z = low_z + self.noise_enable = None + + def prepare(self, logger: Logger): + self.teensy.add_route(self.high_z, self.low_z, self.noise_enable) + + def get_resources(self): + return [(self.teensy, False), (self.high_z, True), (self.low_z, True), (self.noise_enable, True)] + +class ClosedLoopControlFixture(TestFixture): + def __init__(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent): + self.axis_ctx, self.motor_ctx, self.enc_ctx = (axis_ctx, motor_ctx, enc_ctx) - def __repr__(self): - return testrig.get_component_name(self.impl) + ' (routed via ' + ', '.join((testrig.get_component_name(t) + ': ' + str(i.num) + ' => ' + str(o.num)) for t, i, o, n in self.gpio_tuples) + ')' + def get_resources(self): + return [] + + def prepare(self, logger: Logger): + # Make sure no funny configuration is active + logger.debug('Setting up clean configuration...') + self.axis_ctx.parent.erase_config_and_reboot() + + # Set motor calibration values + self.axis_ctx.handle.motor.config.phase_resistance = float(self.motor_ctx.yaml['phase-resistance']) + self.axis_ctx.handle.motor.config.phase_inductance = float(self.motor_ctx.yaml['phase-inductance']) + self.axis_ctx.handle.motor.config.pre_calibrated = True + + # Set brake resistor settings + if 'brake-resistance' in self.axis_ctx.parent.yaml: + logger.debug(f"brake resistor set to {self.axis_ctx.parent.yaml['brake-resistance']} Ohm") + self.axis_ctx.parent.handle.config.brake_resistance = float(self.axis_ctx.parent.yaml['brake-resistance']) + # The docs say this requires a reboot but here's a small secret: + # Since the brake resistor is also started in clear_errors() this + # circumvents the need for a reboot. + self.axis_ctx.parent.handle.config.enable_brake_resistor = True + else: + logger.debug("brake resistor disabled") + # TODO: set vbus voltage trip level based on yaml + self.axis_ctx.parent.handle.config.dc_max_negative_current = -1 + self.axis_ctx.parent.handle.config.enable_brake_resistor = False - def __eq__(self, obj): - return isinstance(obj, ProxiedComponent) and (self.impl == obj.impl) # and (self.gpio_tuples == obj.gpio_tuples) + # Set calibration settings + self.axis_ctx.handle.encoder.config.direction = 0 + self.axis_ctx.handle.encoder.config.use_index = False + self.axis_ctx.handle.encoder.config.calib_scan_omega = 12.566 # 2 electrical revolutions per second + self.axis_ctx.handle.encoder.config.calib_scan_distance = 50.265 # 8 revolutions + self.axis_ctx.handle.encoder.config.bandwidth = 1000 - def prepare(self): - for teensy, gpio_in, gpio_out, gpio_noise_enable in self.gpio_tuples: - teensy.add_route(gpio_in, gpio_out, gpio_noise_enable) + self.axis_ctx.parent.handle.clear_errors() + + logger.debug('Calibrating encoder offset...') + request_state(self.axis_ctx, AXIS_STATE_ENCODER_OFFSET_CALIBRATION) + + time.sleep(9) # actual calibration takes 8 seconds + + test_assert_eq(self.axis_ctx.handle.current_state, AXIS_STATE_IDLE) + test_assert_no_error(self.axis_ctx) + +class AnyTestCase(): + """ + Helper to specifiy that the test case may be run with any of the specified + parameter combinations. + """ + def __init__(self, *alternatives): + self.alternatives = alternatives class TestRig(): def __init__(self, yaml: dict, logger: Logger): @@ -510,7 +653,7 @@ def add_component(name, component): add_component(component_yaml['name'], ODriveComponent(component_yaml)) elif component_yaml['type'] == 'generalpurpose': add_component(component_yaml['name'], GeneralPurposeComponent(component_yaml)) - elif component_yaml['type'] == 'teensy': + elif component_yaml['type'] == 'arduino': add_component(component_yaml['name'], TeensyComponent(self, component_yaml)) elif component_yaml['type'] == 'motor': add_component(component_yaml['name'], MotorComponent(component_yaml)) @@ -534,14 +677,80 @@ def add_component(name, component): for port in s: self.net_by_component[port] = s + def get_closed_loop_combos(self, init: bool = True): + """ + Fetches all connected (odrive axis, motor, encoder) combos in the test rig. + + Returns a generator of tuples where each tuple follows the form: + (axis, motor, encoder, test_fixture). + """ + all_axes = self.get_components(ODriveAxisComponent) + all_motors = self.get_components(MotorComponent) + all_encoders = self.get_components(EncoderComponent) + for axis, motor, encoder in itertools.product(all_axes, all_motors, all_encoders): + is_connected, tf1 = self.check_connections([ + (axis, motor.phases), + (motor.shaft, encoder.shaft), + (encoder.a, axis.parent.encoders[axis.num].a), + (encoder.b, axis.parent.encoders[axis.num].b) + ]) + if not is_connected: + continue + tf2 = ClosedLoopControlFixture(axis, motor, encoder) if init else None + test_fixture = TestFixture.all_of(tf1, tf2) + yield axis, motor, encoder, test_fixture + + def check_connection(self, component1: Component, component2: Component, mode: str = 'indirect'): + """ + Checks if the specified components are connected or connectable through + a test fixture. + + If there are one-directional connections, they must point from + component1 to component2. + + Returns: A tuple (connectable: bool, text_fixture: TestFixture). If + connectable is True then the test_fixture, if not None, must + be prepared before the components are actually connected. + """ + assert(mode in ['direct', 'indirect']) + + net1 = self.net_by_component.get(component1, set([component1])) + if component2 in net1: + return True, None # The components are directly connected + if mode == 'direct': + return False, None + + net2 = self.net_by_component.get(component2, set([component2])) + + possible_test_fixtures = [] + + for port1, port2 in itertools.product(net1, net2): + if (isinstance(port1, TeensyGpio) and isinstance(port2, TeensyGpio) and port1.parent == port2.parent): + possible_test_fixtures.append(TeensyForwardingFixture(port1.parent, port1, port2)) + + if not len(possible_test_fixtures): + return False, None # no forwarding is possible between the two components + else: + return True, TestFixture.any_of(*possible_test_fixtures) + + def check_connections(self, components: list, mode: str = 'indirect'): + tfs = [] + for component1, component2 in components: + is_connected, tf = self.check_connection(component1, component2, mode) + if not is_connected: + return False, None + tfs.append(tf) + return True, TestFixture.all_of(*tfs) + + def get_components(self, t: type): """Returns a tuple (name, component) for all components that are of the specified type""" return (comp for comp in self.names_by_component.keys() if isinstance(comp, t)) def get_component_name(self, component: Component): - if isinstance(component, ProxiedComponent): - return self.names_by_component[component.impl] - else: + #if isinstance(component, ProxiedComponent): + # return self.names_by_component[component.impl] + #else: return self.names_by_component[component] def get_directly_connected_components(self, component: Union[str, Component]): @@ -572,41 +781,38 @@ def get_connected_components(self, src: Union[dict, Tuple[Union[Component, str], A type can be specified to filter the connected components. """ + candidates = self.get_components(comp_type) + if isinstance(src, dict): - component_list = [] - for name, subsrc in src.items(): - component_list.append([c for c in self.get_connected_components(subsrc) if self.get_component_name(c).endswith('.' + name)]) - - for combination in itertools.product(*component_list): - if len(set(c.parent for c in combination)) != 1: - continue # parent of the components don't match - proxied_dst = combination[0].parent - if comp_type and not isinstance(proxied_dst, comp_type): - continue # not the requested type - gpio_tuples = [c2 for c in combination for c2 in c.gpio_tuples if isinstance(c, ProxiedComponent)] - if len(gpio_tuples): - yield ProxiedComponent(proxied_dst, *gpio_tuples) - else: - yield proxied_dst + for candidate in candidates: + subcomponents = dict(candidate.get_subcomponents()) - else: + if len(set(src.keys()) - set(subcomponents.keys())): + continue # candidate doesn't have all of the requested ports - if isinstance(src, tuple): - src, dir = src - else: - dir = None + test_fixtures = [] + for name, subsrc in src.items(): + srcport, direction = subsrc if isinstance(subsrc, tuple) else (subsrc, False) + is_connected, test_fixture = (self.check_connection(srcport, subcomponents[name]) if direction else + self.check_connection(subcomponents[name], srcport)) + if not is_connected: + break + test_fixtures.append(test_fixture) - for dst in self.get_directly_connected_components(src): - if (not comp_type) or isinstance(dst, comp_type): - yield dst - if (not dir is None) and isinstance(getattr(dst, 'parent', None), TeensyComponent): - teensy = dst.parent - for gpio2 in teensy.gpios: - for proxied_dst in self.get_directly_connected_components(gpio2): - if (not comp_type) or isinstance(proxied_dst, comp_type): - yield ProxiedComponent(proxied_dst, (teensy, dst if dir else gpio2, gpio2 if dir else dst, None)) + if len(test_fixtures) < len(src.items()): + continue # not all of the ports are connected to the candidate's ports + + yield candidate, TestFixture.all_of(*test_fixtures) + else: + src, direction = src if isinstance(src, tuple) else (src, False) + assert(isinstance(src, Component)) + for candidate in candidates: + is_connected, test_fixture = (self.check_connection(src, candidate) if direction else + self.check_connection(candidate, src)) + if is_connected: + yield candidate, test_fixture # Helper functions ------------------------------------------------------------# @@ -620,24 +826,16 @@ def request_state(axis_ctx: ODriveAxisComponent, state, expect_success=True): test_assert_eq(axis_ctx.handle.error, AXIS_ERROR_INVALID_STATE) axis_ctx.handle.error = AXIS_ERROR_NONE # reset error -def get_errors(axis_ctx: ODriveAxisComponent): - errors = [] - if axis_ctx.handle.motor.error != 0: - errors.append("motor failed with error 0x{:04X}".format(axis_ctx.handle.motor.error)) - if axis_ctx.handle.encoder.error != 0: - errors.append("encoder failed with error 0x{:04X}".format(axis_ctx.handle.encoder.error)) - if axis_ctx.handle.sensorless_estimator.error != 0: - errors.append("sensorless_estimator failed with error 0x{:04X}".format(axis_ctx.handle.sensorless_estimator.error)) - if axis_ctx.handle.error != 0: - errors.append("axis failed with error 0x{:04X}".format(axis_ctx.handle.error)) - elif len(errors) > 0: - errors.append("and by the way: axis reports no error even though there is one") - return errors - def test_assert_no_error(axis_ctx: ODriveAxisComponent): - errors = get_errors(axis_ctx) - if len(errors) > 0: - raise TestFailed("\n".join(errors)) + any_error = (axis_ctx.handle.motor.error | + axis_ctx.handle.encoder.error | + #axis_ctx.handle.sensorless_estimator.error | # TODO: reenable + axis_ctx.handle.error) != 0 # TODO: this is not the complete list of components + + if any_error: + lines = [] + odrive.utils.dump_errors(axis_ctx.parent.handle, printfunc = lines.append) + raise TestFailed("\n".join(lines)) def run_shell(command_line, logger, env=None, timeout=None): """ @@ -659,98 +857,150 @@ def run_shell(command_line, logger, env=None, timeout=None): raise TestFailed("command {} failed".format(command_line)) -def get_combinations(param_options): - if isinstance(param_options, tuple): - if len(param_options) > 0: - for part1, part2 in itertools.product( - get_combinations(param_options[0]), - get_combinations(param_options[1:]) if (len(param_options) > 1) else [()]): - assert(isinstance(part1, tuple)) - assert(isinstance(part2, tuple)) - yield part1 + part2 - elif is_list_like(param_options): - for item in param_options: - for c in get_combinations(item): - yield c - else: - yield (param_options,) - -def select_params(param_options): - # Select parameters from the resource list - # (this could be arbitrarily complex to improve parallelization of the tests) - for combination in get_combinations(param_options): - if all_unique([x for x in combination if isinstance(x, Component)]): - return list(combination) +def render_html_summary(status, test_results, output_file): + import jinja2 + with open(os.path.join(os.path.dirname(__file__), "results.html.j2")) as fp: + env = jinja2.Environment() + env.filters['passes'] = lambda x: [res for res in x if res == True] + env.filters['fails'] = lambda x: [res for res in x if res != True] + template = env.from_string(fp.read()) + html = template.render( + status=status, + date=datetime.datetime.utcnow(), + test_results=test_results + ) + with open(output_file, 'w') as fp: + fp.write(html) - return None +def is_feasible(params, test_fixtures): + """ + Checks if the specified parameter and test fixture combination is feasible. + A combination is feasible if none of the test fixture resources appear in + the parameters and if all of the exclusive-use test fixture resources are + only used by one test fixture. + """ + exclusive_tf_resources = [] + shared_tf_resources = set() + for r, ex in [(r, ex) for tf in test_fixtures if not tf is None for r, ex in tf.get_resources() if not r is None]: + if ex: + exclusive_tf_resources.append(r) + else: + shared_tf_resources.add(r) + if len(exclusive_tf_resources + list(shared_tf_resources)) > len(set(exclusive_tf_resources).union(shared_tf_resources)): + return False # At least one exclusive-use resource is used twice in the test fixtures + if len(set(exclusive_tf_resources).union(shared_tf_resources).intersection(params)): + return False # At least one test fixture resource appears in the params too + if len(shared_tf_resources.intersection(params)): + return False # At least one test fixture resource appears in the params too + return True def run(tests): + test_results = [] + if not isinstance(tests, list): tests = [tests] for test in tests: - # The result of get_test_cases can be described in ABNF grammar: - # test-case-list = *arglist - # arglist = *flexible-arg - # flexible-arg = component / *argvariant - # argvariant = component / arglist - # - # If for a particular test-case, the components are not given plainly - # but in some selectable form, the test driver will select exactly one - # of those options. - # In other words, it will bring arglist from the form *flexible-arg - # into the form *component before calling the test. + # The result of get_test_cases must be a generator or a list of which + # each item is one of the following: + # - A tuple, each element of which is to be passed as an argument to + # the run_test() function, except the last argument. The last + # argument must be a TestFixture object. + # - An AnyTestCase object. In this case, each of the alternatives in + # the test case must follow the tuple form described above. # # All of the provided test-cases are executed. If none is provided, # a warning is reported. A warning is also reported if for a particular - # test case no component combination can be resolved. + # test case no alternative is feasible (e.g. because there are component + # conflicts). + logger.debug("loading...") test_cases = list(test.get_test_cases(testrig)) + test_name = type(test).__name__ + test_case_results = [] + test_results.append((test_name, test_case_results)) if len(test_cases) == 0: - logger.warn('no test cases are available to conduct the test {}'.format(type(test).__name__)) + logger.warn(f'no test cases are available to conduct the test {test_name}') continue for test_case in test_cases: - params = select_params(test_case) + # Flatten all test case options and test fixture options into a list of candidates + candidates = [] + for candidate in test_case.alternatives if isinstance(test_case, AnyTestCase) else [test_case]: + test_fixture = candidate[-1] + assert(isinstance(test_fixture, TestFixture) or test_fixture is None) + if isinstance(test_fixture, CompositeTestFixture): + candidates += [tuple(candidate[:-1]) + (tf,) for tf in test_fixture._subfixtures] + else: + candidates.append(tuple(candidate[:-1]) + (([] if test_fixture is None else [test_fixture]),)) + + # Select the first candidate that is feasible + params, test_fixture = (None, None) + for candidate in candidates: + if is_feasible(candidate[:-1], candidate[-1]): + params, test_fixture = (candidate[:-1], candidate[-1]) + break + if params is None: - logger.warn('no resources are available to conduct the test {}'.format(type(test).__name__)) + logger.warn(f'I found a {type(test).__name__} test case with {len(candidates)} possible parameter combination candidates but none of them is feasible.') continue logger.notify('* preparing {} with {}...'.format(type(test).__name__, [(testrig.get_component_name(p) if isinstance(p, Component) else str(p)) for p in params])) - + + # Prepare all components + # TODO: less hardcoded priority assignment + teensies = set() for param in params: - if isinstance(param, ProxiedComponent): - param.prepare() - for teensy, _, _, _ in param.gpio_tuples: - teensies.add(teensy) + #if isinstance(param, ProxiedComponent): + # continue + if hasattr(param, 'prepare'): + param.prepare(logger) + + teensies = set() + for tf in test_fixture: + if isinstance(tf, ClosedLoopControlFixture): + continue + tf.prepare(logger) + if isinstance(tf, TeensyForwardingFixture): + teensies.add(tf.teensy) + # Post-prepare step required if teensy-forwarding is involved for teensy in teensies: teensy.commit_routing_config(logger) - # prepare all components - teensies = set() - for param in params: - if isinstance(param, ProxiedComponent): + for tf in test_fixture: + if not isinstance(tf, ClosedLoopControlFixture): continue - if hasattr(param, 'prepare'): - param.prepare(logger) + tf.prepare(logger) logger.notify('* running {} on {}...'.format(type(test).__name__, [(testrig.get_component_name(p) if isinstance(p, Component) else str(p)) for p in params])) - # Resolve routed components - for i, param in enumerate(params): - if isinstance(param, ProxiedComponent): - params[i] = param.impl + try: + test.run_test(*params, logger) + test_case_results.append(True) + except Exception as ex: + traceback.print_exc() + test_case_results.append(ex) - test.run_test(*params, logger) + if args.html: + render_html_summary('running...', test_results, args.html) + if args.html: + render_html_summary('finished', test_results, args.html) - logger.success('All tests passed!') + passes = [res for t in test_results for res in t[1] if res == True] + fails = [res for t in test_results for res in t[1] if res != True] + if len(fails): + logger.error(f'{len(fails)} out of {len(fails) + len(passes)} test cases failed.') + else: + logger.success('All tests passed!') + shutdown_token.set() + return test_results # Load test engine ------------------------------------------------------------# @@ -759,20 +1009,34 @@ def run(tests): parser.add_argument("--ignore", metavar='DEVICE', action='store', nargs='+', help="Ignore (disable) one or more components of the test rig") # TODO: implement -parser.add_argument("--test-rig-yaml", type=argparse.FileType('r'), required=True, - help="test rig YAML file") +parser.add_argument("--test-rig-yaml", type=argparse.FileType('r'), + help="Test rig YAML file. Can be omitted if the environment variable ODRIVE_TEST_RIG_NAME is set.") parser.add_argument("--setup-host", action='store_true', default=False, help="configure operating system functions such as GPIOs (requires root)") +parser.add_argument("--all", action='store_true', default=False, + help="Run all tests in the test runner's directory") +parser.add_argument("--html", type=str, + help="If provided, the an HTML summary is written to the specified file.") parser.set_defaults(ignore=[]) args = parser.parse_args() +if args.test_rig_yaml is None: + test_rig_name = os.environ.get('ODRIVE_TEST_RIG_NAME', '') + if test_rig_name == '': + print("You must either provide a --test-rig-yaml argument or set the environment variable ODRIVE_TEST_RIG_NAME.") + sys.exit(1) + path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))), test_rig_name + '.yaml') + args.test_rig_yaml = open(path, 'r') + + + # Load objects test_rig_yaml = yaml.load(args.test_rig_yaml, Loader=yaml.BaseLoader) logger = Logger() testrig = TestRig(test_rig_yaml, logger) - +shutdown_token = fibre.Event() if args.setup_host: for gpio in testrig.get_components(LinuxGpioComponent): @@ -803,9 +1067,35 @@ def run(tests): # Bring up CAN interface(s) for intf in testrig.get_components(CanInterfaceComponent): name = intf.yaml['interface'] + path = intf.yaml.get('path', None) logger.debug('bringing up {}...'.format(name)) run_shell('ip link set dev {} down'.format(name), logger) - run_shell('ip link set dev {} type can bitrate 250000'.format(name), logger) - run_shell('ip link set dev {} type can loopback off'.format(name), logger) + if not path is None: + run_shell(f'slcand -o -c -s5 \'{path}\' {name}', logger) + else: + run_shell('ip link set dev {} type can bitrate 250000'.format(name), logger) + run_shell('ip link set dev {} type can loopback off'.format(name), logger) run_shell('ip link set dev {} up'.format(name), logger) + +if __name__ == '__main__': + test_scripts = [] + tests = [] + + if args.all: + test_scripts = [file for file in os.listdir(os.path.dirname(__file__)) + if file.lower().endswith("_test.py")] + + for script in test_scripts: + import importlib.util + spec = importlib.util.spec_from_file_location("test_module", script) + test_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(test_module) + if not hasattr(test_module, 'tests') or not isinstance(test_module.tests, list): + logger.error(f"{script} does not have a list named `tests`") + tests += test_module.tests + + logger.notify(f"found {len(tests)} tests in {len(test_scripts)} modules") + if any(tests): + test_results = test_module.test_runner.run(tests) + diff --git a/tools/odrive/tests/uart_ascii_test.py b/tools/odrive/tests/uart_ascii_test.py index ff5a41de4..08fbd17d1 100644 --- a/tools/odrive/tests/uart_ascii_test.py +++ b/tools/odrive/tests/uart_ascii_test.py @@ -30,25 +30,69 @@ def reset_state(ser): time.sleep(0.1) # wait for any response that this may generate ser.flushInput() # discard response -class TestUartAscii(): +class UartTest(): """ - Tests the most important functions of the ASCII protocol. + Base class for UART tests """ def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - ports = list(testrig.get_connected_components({ - 'rx': (odrive.gpio1, True), - 'tx': (odrive.gpio2, False) - }, SerialPortComponent)) - yield (odrive, ports) - - def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger): - logger.debug('Enabling UART...') + if odrive.yaml['board-version'].startswith('v3.'): + ports = testrig.get_connected_components({ + 'rx': (odrive.gpio1, True), + 'tx': (odrive.gpio2, False) + }, SerialPortComponent) + yield AnyTestCase(*[(odrive, 0, 1, 2, port, tf) for port, tf in ports]) + + # Enable the line below to manually test UART_B. For this you need + # to manually move to the wires go to GPIO1/2 to GPIO3/4. The ones + # that normally go to GPIO3/4 have a low pass filter. + #yield (odrive, 1, 3, 4, ports) + elif odrive.yaml['board-version'].startswith('v4.'): + ports = list(testrig.get_connected_components({ + 'rx': (odrive.gpio15, True), + 'tx': (odrive.gpio14, False) + }, SerialPortComponent)) + yield AnyTestCase(*[(odrive, 0, 15, 14, port, tf) for port, tf in ports]) + else: + raise TestFailed("unknown board version") + + def prepare(self, odrive: ODriveComponent, uart_num: int, tx_gpio: list, rx_gpio: list, logger: Logger): + logger.debug('Enabling UART {}...'.format(chr(ord('A') + uart_num))) + # GPIOs might be in use by something other than UART and some components # might be configured so that they would fail in the later test. - odrive.erase_config_and_reboot() - odrive.handle.config.enable_uart = True + odrive.disable_mappings() + odrive.handle.config.enable_uart_a = False + odrive.handle.config.uart_a_baudrate = 115200 + odrive.handle.config.enable_uart_b = False + odrive.handle.config.uart_b_baudrate = 115200 + odrive.handle.config.enable_uart_c = False + odrive.handle.config.uart_c_baudrate = 115200 + + if uart_num == 0: + odrive.handle.config.enable_uart_a = True + mode = GPIO_MODE_UART_A + elif uart_num == 1: + odrive.handle.config.enable_uart_b = True + mode = GPIO_MODE_UART_B + elif uart_num == 2: + odrive.handle.config.enable_uart_c = True + mode = GPIO_MODE_UART_C + else: + raise TestFailed(f"unknown UART: {uart_num}") + setattr(odrive.handle.config, f'gpio{tx_gpio}_mode', mode) + setattr(odrive.handle.config, f'gpio{rx_gpio}_mode', mode) + + odrive.save_config_and_reboot() + +class TestUartAscii(UartTest): + """ + Tests the most important functions of the ASCII protocol. + """ + + def run_test(self, odrive: ODriveComponent, uart_num: int, tx_gpio: list, rx_gpio: list, port: SerialPortComponent, logger: Logger): + self.prepare(odrive, uart_num, tx_gpio, rx_gpio, logger) with port.open(115200) as ser: # reset port to known state @@ -74,7 +118,10 @@ def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: L # Test GCode checksum and comments ser.write(b'r vbus_voltage *12\n') # invalid checksum test_assert_eq(ser.readline(), b'') - ser.write(append_checksum(b'r vbus_voltage ') + b' ; this is a comment\n') # valid checksum + cmd = append_checksum(b'r vbus_voltage ') + b' ; this is a comment\n' + # cmd evalutates to "r vbus_voltage *93 ; this is a comment" + logger.debug(f'sending command with checksum: "{cmd}"') + ser.write(cmd) # valid checksum response = float(strip_checksum(ser.readline()).strip()) test_assert_eq(response, odrive.handle.vbus_voltage, accuracy=0.1) @@ -147,24 +194,15 @@ def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: L # TODO: test cases for 't', 'ss', 'se', 'sr' commands -class TestUartBaudrate(): +class TestUartBaudrate(UartTest): """ Tests if the UART baudrate setting works as intended. """ - def get_test_cases(self, testrig: TestRig): - for odrive in testrig.get_components(ODriveComponent): - ports = list(testrig.get_connected_components({ - 'rx': (odrive.gpio1, True), - 'tx': (odrive.gpio2, False) - }, SerialPortComponent)) - yield (odrive, ports) - - def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger): - odrive.handle.axis0.config.enable_step_dir = False - odrive.handle.config.enable_uart = True + def run_test(self, odrive: ODriveComponent, uart_num: int, tx_gpio: list, rx_gpio: list, port: SerialPortComponent, logger: Logger): + self.prepare(odrive, uart_num, tx_gpio, rx_gpio, logger) - odrive.handle.config.uart_baudrate = 9600 + odrive.handle.config.uart_a_baudrate = 9600 odrive.save_config_and_reboot() # Control test: talk to the ODrive with the wrong baudrate @@ -184,26 +222,17 @@ def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: L response = float(ser.readline().strip()) test_assert_eq(response, odrive.handle.vbus_voltage, accuracy=0.1) - odrive.handle.config.uart_baudrate = 115200 + odrive.handle.config.uart_a_baudrate = 115200 odrive.save_config_and_reboot() -class TestUartBurnIn(): +class TestUartBurnIn(UartTest): """ Tests if the ASCII protocol can handle 64kB of random data being thrown at it. """ - def get_test_cases(self, testrig: TestRig): - for odrive in testrig.get_components(ODriveComponent): - ports = list(testrig.get_connected_components({ - 'rx': (odrive.gpio1, True), - 'tx': (odrive.gpio2, False) - }, SerialPortComponent)) - yield (odrive, ports) - - def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger): - odrive.handle.axis0.config.enable_step_dir = False - odrive.handle.config.enable_uart = True + def run_test(self, odrive: ODriveComponent, uart_num: int, tx_gpio: list, rx_gpio: list, port: SerialPortComponent, logger: Logger): + self.prepare(odrive, uart_num, tx_gpio, rx_gpio, logger) with port.open(115200) as ser: with open('/dev/random', 'rb') as rand: @@ -219,52 +248,59 @@ def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: L test_assert_eq(response, odrive.handle.vbus_voltage, accuracy=0.1) -class TestUartNoise(): +class TestUartNoise(UartTest): """ Tests if the UART can handle invalid signals. """ def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - # For every ODrive, find a connected serial port which has a teensy - # in between, so that we can inject noise, - - ports = list(testrig.get_connected_components({ - 'rx': (odrive.gpio1, True), - 'tx': (odrive.gpio2, False) - }, SerialPortComponent)) - - # Hack the bus objects to enable noise_enable functionality on the TX line. - - def get_noise_gpio(bus): - teensy = bus.gpio_tuples[1][0] - for teensy_gpio in teensy.gpios: - for other_gpio in testrig.get_directly_connected_components(teensy_gpio): - if isinstance(other_gpio, LinuxGpioComponent): - return teensy_gpio, other_gpio - return None - - for idx, bus in enumerate(ports): - noise_gpio_on_teensy, noise_gpio_on_rpi = get_noise_gpio(bus) - assert(noise_gpio_on_rpi) - t, i, o, _ = bus.gpio_tuples[1] - bus.gpio_tuples[1] = (t, i, o, noise_gpio_on_teensy) - ports[idx] = (bus, noise_gpio_on_rpi) - - yield (odrive, ports) - - def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, noise_enable: LinuxGpioComponent, logger: Logger): + if odrive.yaml['board-version'].startswith('v3.'): + uart_num, tx_gpio, rx_gpio = (0, 1, 2) + elif odrive.yaml['board-version'].startswith('v4.'): + uart_num, tx_gpio, rx_gpio = (0, 15, 14) + + alternatives = [] + + # Find a Teensy that sits between a linux serial port and the ODrive + # with an additional wire that runs to the linux PC and will be used + # as noise-enable line + + for teensy in testrig.get_components(TeensyComponent): + for port in testrig.get_components(SerialPortComponent): + gpio_conns = [ + testrig.net_by_component.get(getattr(odrive, f'gpio{tx_gpio}'), set()).intersection(set(teensy.gpios)), + testrig.net_by_component.get(getattr(odrive, f'gpio{rx_gpio}'), set()).intersection(set(teensy.gpios)), + testrig.net_by_component.get(port.tx, set()).intersection(set(teensy.gpios)), + testrig.net_by_component.get(port.rx, set()).intersection(set(teensy.gpios)), + teensy.gpios + ] + + for gpio1, gpio2, gpio3, gpio4, gpio5 in itertools.product(*gpio_conns): + for noise_ctrl_gpio, tf3 in testrig.get_connected_components(gpio5, LinuxGpioComponent): + tf1 = TeensyForwardingFixture(teensy, gpio3, gpio2) + tf2 = TeensyForwardingFixture(teensy, gpio1, gpio4) + tf1.noise_enable = gpio5 + alternatives.append((odrive, uart_num, tx_gpio, rx_gpio, port, noise_ctrl_gpio, TestFixture.all_of(tf1, tf2, tf3))) + + yield AnyTestCase(*alternatives) + + def run_test(self, odrive: ODriveComponent, uart_num: int, tx_gpio: list, rx_gpio: list, port: SerialPortComponent, noise_enable: LinuxGpioComponent, logger: Logger): noise_enable.config(output=True) noise_enable.write(False) time.sleep(0.1) - odrive.handle.axis0.config.enable_step_dir = False - odrive.handle.config.enable_uart = True + self.prepare(odrive, uart_num, tx_gpio, rx_gpio, logger) with port.open(115200) as ser: # reset port to known state reset_state(ser) + # First try + ser.write(b'r vbus_voltage\n') + response = float(ser.readline().strip()) + test_assert_eq(response, odrive.handle.vbus_voltage, accuracy=0.1) + # Enable square wave of ~1.6MHz on the ODrive's RX line noise_enable.write(True) @@ -291,11 +327,12 @@ def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, noise_ena response = float(ser.readline().strip()) test_assert_eq(response, odrive.handle.vbus_voltage, accuracy=0.1) +tests = [ + TestUartAscii(), + TestUartBaudrate(), + TestUartBurnIn(), + TestUartNoise() +] if __name__ == '__main__': - test_runner.run([ - TestUartAscii(), - TestUartBaudrate(), - TestUartBurnIn(), - TestUartNoise(), - ]) + test_runner.run(tests) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index f5699991a..6729fc738 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -6,8 +6,6 @@ import platform import subprocess import os -import numpy as np -import matplotlib.pyplot as plt from fibre.utils import Event import odrive.enums from odrive.enums import * @@ -34,7 +32,20 @@ 'default': '\x1b[0m' } -def calculate_thermistor_coeffs(degree, Rload, R_25, Beta, Tmin, Tmax, plot = False): +async def get_serial_number_str(device): + if hasattr(device, '_serial_number_property'): + return format(await device._serial_number_property.read(), 'x').upper() + else: + return "[unknown serial number]" + +def get_serial_number_str_sync(device): + if hasattr(device, '_serial_number_property'): + return format(device._serial_number_property.read(), 'x').upper() + else: + return "[unknown serial number]" + +def calculate_thermistor_coeffs(degree, Rload, R_25, Beta, Tmin, Tmax, thermistor_bottom = False, plot = False): + import numpy as np T_25 = 25 + 273.15 #Kelvin temps = np.linspace(Tmin, Tmax, 1000) tempsK = temps + 273.15 @@ -42,13 +53,17 @@ def calculate_thermistor_coeffs(degree, Rload, R_25, Beta, Tmin, Tmax, plot = Fa # https://en.wikipedia.org/wiki/Thermistor#B_or_%CE%B2_parameter_equation r_inf = R_25 * np.exp(-Beta/T_25) R_temps = r_inf * np.exp(Beta/tempsK) - V = Rload / (Rload + R_temps) + if thermistor_bottom: + V = R_temps / (Rload + R_temps) + else: + V = Rload / (Rload + R_temps) fit = np.polyfit(V, temps, degree) p1 = np.poly1d(fit) fit_temps = p1(V) if plot: + import matplotlib.pyplot as plt print(fit) plt.plot(V, temps, label='actual') plt.plot(V, fit_temps, label='fit') @@ -62,57 +77,64 @@ def calculate_thermistor_coeffs(degree, Rload, R_25, Beta, Tmin, Tmax, plot = Fa class OperationAbortedException(Exception): pass -def set_motor_thermistor_coeffs(axis, Rload, R_25, Beta, Tmin, TMax): - coeffs = calculate_thermistor_coeffs(3, Rload, R_25, Beta, Tmin, TMax) - axis.motor_thermistor.config.poly_coefficient_0 = float(coeffs[3]) - axis.motor_thermistor.config.poly_coefficient_1 = float(coeffs[2]) - axis.motor_thermistor.config.poly_coefficient_2 = float(coeffs[1]) - axis.motor_thermistor.config.poly_coefficient_3 = float(coeffs[0]) +def set_motor_thermistor_coeffs(axis, Rload, R_25, Beta, Tmin, Tmax, thermistor_bottom = False): + coeffs = calculate_thermistor_coeffs(3, Rload, R_25, Beta, Tmin, Tmax, thermistor_bottom) + axis.motor.motor_thermistor.config.poly_coefficient_0 = float(coeffs[3]) + axis.motor.motor_thermistor.config.poly_coefficient_1 = float(coeffs[2]) + axis.motor.motor_thermistor.config.poly_coefficient_2 = float(coeffs[1]) + axis.motor.motor_thermistor.config.poly_coefficient_3 = float(coeffs[0]) -def dump_errors(odrv, clear=False): - axes = [(name, axis) for name, axis in odrv._remote_attributes.items() if 'axis' in name] +def dump_errors(odrv, clear=False, printfunc = print): + axes = [(name, getattr(odrv, name)) for name in dir(odrv) if name.startswith('axis')] axes.sort() + + def dump_errors_for_module(indent, name, obj, path, errorcodes): + prefix = indent + name.strip('0123456789') + ": " + for elem in path.split('.'): + if not hasattr(obj, elem): + printfunc(prefix + _VT100Colors['yellow'] + "not found" + _VT100Colors['default']) + return + parent = obj + obj = getattr(obj, elem) + if obj != 0: + printfunc(indent + name + ": " + _VT100Colors['red'] + "Error(s):" + _VT100Colors['default']) + for bit in range(64): + if obj & (1 << bit) != 0: + printfunc(indent + " " + errorcodes.get((1 << bit), 'UNKNOWN ERROR: 0x{:08X}'.format(1 << bit))) + else: + printfunc(indent + name + ": " + _VT100Colors['green'] + "no error" + _VT100Colors['default']) + + system_error_codes = {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("ODRIVE_ERROR_")} + dump_errors_for_module("", "system", odrv, 'error', system_error_codes) + for name, axis in axes: - print(name) + printfunc(name) # Flatten axis and submodules - # (name, remote_obj, errorcode) + # (name, obj, path, errorcode) module_decode_map = [ - ('axis', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("AXIS_ERROR_")}), - ('motor', axis.motor, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}), - ('fet_thermistor', axis.fet_thermistor, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), - ('motor_thermistor', axis.motor_thermistor, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), - ('encoder', axis.encoder, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}), - ('controller', axis.controller, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}), + ('axis', axis, 'error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("AXIS_ERROR_")}), + ('motor', axis, 'motor.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}), + ('sensorless_estimator', axis, 'sensorless_estimator.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("SENSORLESS_ESTIMATOR_ERROR")}), + ('encoder', axis, 'encoder.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}), + ('controller', axis, 'controller.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}), ] - # Module error decode - for name, remote_obj, errorcodes in module_decode_map: - prefix = ' '*2 + name + ": " - if (remote_obj.error != 0): - foundError = False - print(prefix + _VT100Colors['red'] + "Error(s):" + _VT100Colors['default']) - errorcodes_tup = [(name, val) for name, val in errorcodes.items() if 'ERROR_' in name] - for codename, codeval in errorcodes_tup: - if remote_obj.error & codeval != 0: - foundError = True - print(" " + codename) - if not foundError: - print(" " + 'UNKNOWN ERROR!') - if clear: - remote_obj.error = 0 - else: - print(prefix + _VT100Colors['green'] + "no error" + _VT100Colors['default']) + for name, obj, path, errorcodes in module_decode_map: + dump_errors_for_module(" ", name, obj, path, errorcodes) + + if clear: + odrv.clear_errors() def oscilloscope_dump(odrv, num_vals, filename='oscilloscope.csv'): with open(filename, 'w') as f: for x in range(num_vals): - f.write(str(odrv.get_oscilloscope_val(x))) + f.write(str(odrv.oscilloscope.get_val(x))) f.write('\n') -data_rate = 100 +data_rate = 200 plot_rate = 10 -num_samples = 1000 +num_samples = 500 def start_liveplotter(get_var_callback): """ Starts a liveplotter. @@ -149,10 +171,10 @@ def plot_data(): plt.ion() # Make sure the script terminates when the user closes the plotter - def did_close(evt): + def closed(evt): cancellation_token.set() fig = plt.figure() - fig.canvas.mpl_connect('close_event', did_close) + fig.canvas.mpl_connect('close_event', closed) while not cancellation_token.is_set(): plt.clf() @@ -297,7 +319,7 @@ def show_oscilloscope(odrv): size = 18000 values = [] for i in range(size): - values.append(odrv.get_oscilloscope_val(i)) + values.append(odrv.oscilloscope.get_val(i)) import matplotlib.pyplot as plt plt.plot(values) @@ -315,7 +337,7 @@ def rate_test(device): numFrames = 10000 vals = [] for _ in range(numFrames): - vals.append(device.axis0.loop_counter) + vals.append(device.n_evt_control_loop) loopsPerFrame = (vals[-1] - vals[0])/numFrames loopsPerSec = (168000000/(6*3500)) @@ -364,3 +386,248 @@ def yes_no_prompt(question, default=None): return False elif choice == '' and default is not None: return default + +def dump_interrupts(odrv): + interrupts = [ + (-12, "MemoryManagement_IRQn"), + (-11, "BusFault_IRQn"), + (-10, "UsageFault_IRQn"), + (-5, "SVCall_IRQn"), + (-4, "DebugMonitor_IRQn"), + (-2, "PendSV_IRQn"), + (-1, "SysTick_IRQn"), + (0, "WWDG_IRQn"), + (1, "PVD_IRQn"), + (2, "TAMP_STAMP_IRQn"), + (3, "RTC_WKUP_IRQn"), + (4, "FLASH_IRQn"), + (5, "RCC_IRQn"), + (6, "EXTI0_IRQn"), + (7, "EXTI1_IRQn"), + (8, "EXTI2_IRQn"), + (9, "EXTI3_IRQn"), + (10, "EXTI4_IRQn"), + (11, "DMA1_Stream0_IRQn"), + (12, "DMA1_Stream1_IRQn"), + (13, "DMA1_Stream2_IRQn"), + (14, "DMA1_Stream3_IRQn"), + (15, "DMA1_Stream4_IRQn"), + (16, "DMA1_Stream5_IRQn"), + (17, "DMA1_Stream6_IRQn"), + (18, "ADC_IRQn"), + (19, "CAN1_TX_IRQn"), + (20, "CAN1_RX0_IRQn"), + (21, "CAN1_RX1_IRQn"), + (22, "CAN1_SCE_IRQn"), + (23, "EXTI9_5_IRQn"), + (24, "TIM1_BRK_TIM9_IRQn"), + (25, "TIM1_UP_TIM10_IRQn"), + (26, "TIM1_TRG_COM_TIM11_IRQn"), + (27, "TIM1_CC_IRQn"), + (28, "TIM2_IRQn"), + (29, "TIM3_IRQn"), + (30, "TIM4_IRQn"), + (31, "I2C1_EV_IRQn"), + (32, "I2C1_ER_IRQn"), + (33, "I2C2_EV_IRQn"), + (34, "I2C2_ER_IRQn"), + (35, "SPI1_IRQn"), + (36, "SPI2_IRQn"), + (37, "USART1_IRQn"), + (38, "USART2_IRQn"), + (39, "USART3_IRQn"), + (40, "EXTI15_10_IRQn"), + (41, "RTC_Alarm_IRQn"), + (42, "OTG_FS_WKUP_IRQn"), + (43, "TIM8_BRK_TIM12_IRQn"), + (44, "TIM8_UP_TIM13_IRQn"), + (45, "TIM8_TRG_COM_TIM14_IRQn"), + (46, "TIM8_CC_IRQn"), + (47, "DMA1_Stream7_IRQn"), + (48, "FMC_IRQn"), + (49, "SDMMC1_IRQn"), + (50, "TIM5_IRQn"), + (51, "SPI3_IRQn"), + (52, "UART4_IRQn"), + (53, "UART5_IRQn"), + (54, "TIM6_DAC_IRQn"), + (55, "TIM7_IRQn"), + (56, "DMA2_Stream0_IRQn"), + (57, "DMA2_Stream1_IRQn"), + (58, "DMA2_Stream2_IRQn"), + (59, "DMA2_Stream3_IRQn"), + (60, "DMA2_Stream4_IRQn"), + (61, "ETH_IRQn"), + (62, "ETH_WKUP_IRQn"), + (63, "CAN2_TX_IRQn"), + (64, "CAN2_RX0_IRQn"), + (65, "CAN2_RX1_IRQn"), + (66, "CAN2_SCE_IRQn"), + (67, "OTG_FS_IRQn"), + (68, "DMA2_Stream5_IRQn"), + (69, "DMA2_Stream6_IRQn"), + (70, "DMA2_Stream7_IRQn"), + (71, "USART6_IRQn"), + (72, "I2C3_EV_IRQn"), + (73, "I2C3_ER_IRQn"), + (74, "OTG_HS_EP1_OUT_IRQn"), + (75, "OTG_HS_EP1_IN_IRQn"), + (76, "OTG_HS_WKUP_IRQn"), + (77, "OTG_HS_IRQn"), + # gap + (80, "RNG_IRQn"), + (81, "FPU_IRQn"), + (82, "UART7_IRQn"), + (83, "UART8_IRQn"), + (84, "SPI4_IRQn"), + (85, "SPI5_IRQn"), + # gap + (87, "SAI1_IRQn"), + # gap + (91, "SAI2_IRQn"), + (92, "QUADSPI_IRQn"), + (93, "LPTIM1_IRQn"), + # gap + (103, "SDMMC2_IRQn") + ] + + print("| # | Name | Prio | En | Count |") + print("|-----|-------------------------|------|----|---------|") + for irqn, irq_name in interrupts: + status = odrv.get_interrupt_status(irqn) + if (status != 0): + print("| {} | {} | {} | {} | {} |".format( + str(irqn).rjust(3), + irq_name.ljust(23), + str(status & 0xff).rjust(4), + " *" if (status & 0x80000000) else " ", + str((status >> 8) & 0x7fffff).rjust(7))) + +def dump_threads(odrv): + prefixes = ["max_stack_usage_", "stack_size_", "prio_"] + keys = [k[len(prefix):] for k in dir(odrv.system_stats) for prefix in prefixes if k.startswith(prefix)] + good_keys = set([k for k in set(keys) if keys.count(k) == len(prefixes)]) + if len(good_keys) > len(set(keys)): + print("Warning: incomplete thread information for threads {}".format(set(keys) - good_keys)) + + print("| Name | Stack Size [B] | Max Ever Stack Usage [B] | Prio |") + print("|---------|----------------|--------------------------|------|") + for k in sorted(good_keys): + sz = getattr(odrv.system_stats, "stack_size_" + k) + use = getattr(odrv.system_stats, "max_stack_usage_" + k) + print("| {} | {} | {} | {} |".format( + k.ljust(7), + str(sz).rjust(14), + "{} ({:.1f}%)".format(use, use / sz * 100).rjust(24), + str(getattr(odrv.system_stats, "prio_" + k)).rjust(4) + )) + + +def dump_dma(odrv): + if odrv.hw_version_major == 3: + dma_functions = [[ + # https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf Table 42 + ["SPI3_RX", "-", "SPI3_RX", "SPI2_RX", "SPI2_TX", "SPI3_TX", "-", "SPI3_TX"], + ["I2C1_RX", "-", "TIM7_UP", "-", "TIM7_UP", "I2C1_RX", "I2C1_TX", "I2C1_TX"], + ["TIM4_CH1", "-", "I2S3_EXT_RX", "TIM4_CH2", "I2S2_EXT_TX", "I2S3_EXT_TX", "TIM4_UP", "TIM4_CH3"], + ["I2S3_EXT_RX", "TIM2_UP/TIM2_CH3", "I2C3_RX", "I2S2_EXT_RX", "I2C3_TX", "TIM2_CH1", "TIM2_CH2/TIM2_CH4", "TIM2_UP/TIM2_CH4"], + ["UART5_RX", "USART3_RX", "UART4_RX", "USART3_TX", "UART4_TX", "USART2_RX", "USART2_TX", "UART5_TX"], + ["UART8_TX", "UART7_TX", "TIM3_CH4/TIM3_UP", "UART7_RX", "TIM3_CH1/TIM3_TRIG", "TIM3_CH2", "UART8_RX", "TIM3_CH3"], + ["TIM5_CH3/TIM5_UP", "TIM5_CH4/TIM5_TRIG", "TIM5_CH1", "TIM5_CH4/TIM5_TRIG", "TIM5_CH2", "-", "TIM5_UP", "-"], + ["-", "TIM6_UP", "I2C2_RX", "I2C2_RX", "USART3_TX", "DAC1", "DAC2", "I2C2_TX"], + ], [ + # https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf Table 43 + ["ADC1", "SAI1_A", "TIM8_CH1/TIM8_CH2/TIM8_CH3", "SAI1_A", "ADC1", "SAI1_B", "TIM1_CH1/TIM1_CH2/TIM1_CH3", "-"], + ["-", "DCMI", "ADC2", "ADC2", "SAI1_B", "SPI6_TX", "SPI6_RX", "DCMI"], + ["ADC3", "ADC3", "-", "SPI5_RX", "SPI5_TX", "CRYP_OUT", "CRYP_IN", "HASH_IN"], + ["SPI1_RX", "-", "SPI1_RX", "SPI1_TX", "-", "SPI1_TX", "-", "-"], + ["SPI4_RX", "SPI4_TX", "USART1_RX", "SDIO", "-", "USART1_RX", "SDIO", "USART1_TX"], + ["-", "USART6_RX", "USART6_RX", "SPI4_RX", "SPI4_TX", "-", "USART6_TX", "USART6_TX"], + ["TIM1_TRIG", "TIM1_CH1", "TIM1_CH2", "TIM1_CH1", "TIM1_CH4/TIM1_TRIG/TIM1_COM", "TIM1_UP", "TIM1_CH3", "-"], + ["-", "TIM8_UP", "TIM8_CH1", "TIM8_CH2", "TIM8_CH3", "SPI5_RX", "SPI5_TX", "TIM8_CH4/TIM8_TRIG/TIM8_COM"], + ]] + elif odrv.hw_version_major == 4: + dma_functions = [[ + # https://www.st.com/resource/en/reference_manual/dm00305990-stm32f72xxx-and-stm32f73xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf Table 26 + ["SPI3_RX", "-", "SPI3_RX", "SPI2_RX", "SPI2_TX", "SPI3_TX", "-", "SPI3_TX"], + ["I2C1_RX", "I2C3_RX", "TIM7_UP", "-", "TIM7_UP", "I2C1_RX", "I2C1_TX", "I2C1_TX"], + ["TIM4_CH1", "-", "-", "TIM4_CH2", "-", "-", "TIM4_UP", "TIM4_CH3"], + ["-", "TIM2_UP/TIM2_CH3", "I2C3_RX", "-", "I2C3_TX", "TIM2_CH1", "TIM2_CH2/TIM2_CH4", "TIM2_UP/TIM2_CH4"], + ["UART5_RX", "USART3_RX", "UART4_RX", "USART3_TX", "UART4_TX", "USART2_RX", "USART2_TX", "UART5_TX"], + ["UART8_TX", "UART7_TX", "TIM3_CH4/TIM3_UP", "UART7_RX", "TIM3_CH1/TIM3_TRIG", "TIM3_CH2", "UART8_RX", "TIM3_CH3"], + ["TIM5_CH3/TIM5_UP", "TIM5_CH4/TIM5_TRIG", "TIM5_CH1", "TIM5_CH4/TIM5_TRIG", "TIM5_CH2", "-", "TIM5_UP", "-"], + ["-", "TIM6_UP", "I2C2_RX", "I2C2_RX", "USART3_TX", "DAC1", "DAC2", "I2C2_TX"], + ], [ + # https://www.st.com/resource/en/reference_manual/dm00305990-stm32f72xxx-and-stm32f73xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf Table 27 + ["ADC1", "SAI1_A", "TIM8_CH1/TIM8_CH2/TIM8_CH3", "SAI1_A", "ADC1", "SAI1_B", "TIM1_CH1/TIM1_CH2/TIM1_CH3", "SAI2_B"], + ["-", "-", "ADC2", "ADC2", "SAI1_B", "-", "-", "-"], + ["ADC3", "ADC3", "-", "SPI5_RX", "SPI5_TX", "AES_OUT", "AES_IN", "-"], + ["SPI1_RX", "-", "SPI1_RX", "SPI1_TX", "SAI2_A", "SPI1_TX", "SAI2_B", "QUADSPI"], + ["SPI4_RX", "SPI4_TX", "USART1_RX", "SDMMC1", "-", "USART1_RX", "SDMMC1", "USART1_TX"], + ["-", "USART6_RX", "USART6_RX", "SPI4_RX", "SPI4_TX", "-", "USART6_TX", "USART6_TX"], + ["TIM1_TRIG", "TIM1_CH1", "TIM1_CH2", "TIM1_CH1", "TIM1_CH4/TIM1_TRIG/TIM1_COM", "TIM1_UP", "TIM1_CH3", "-"], + ["-", "TIM8_UP", "TIM8_CH1", "TIM8_CH2", "TIM8_CH3", "SPI5_RX", "SPI5_TX", "TIM8_CH4/TIM8_TRIG/TIM8_COM"], + None, + None, + None, + ["SDMMC2", "-", "-", "-", "-", "SDMMC2", "-", "-"], + ]] + + print("| Name | Prio | Channel | Configured |") + print("|--------------|------|----------------------------------|------------|") + for stream_num in range(16): + status = odrv.get_dma_status(stream_num) + if (status != 0): + channel = (status >> 2) & 0x7 + ch_name = dma_functions[stream_num >> 3][channel][stream_num & 0x7] + print("| DMA{}_Stream{} | {} | {} {} | {} |".format( + (stream_num >> 3) + 1, + (stream_num & 0x7), + (status & 0x3), + channel, + ("(" + ch_name + ")").ljust(30), + "*" if (status & 0x80000000) else " ")) + +def dump_timing(odrv, n_samples=100, path='/tmp/timings.png'): + import matplotlib.pyplot as plt + import re + import numpy as np + + timings = [] + + for attr in dir(odrv.task_times): + if not attr.startswith('_'): + timings.append((attr, getattr(odrv.task_times, attr), [], [])) # (name, obj, start_times, lengths) + for k in dir(odrv): + if re.match(r'axis[0-9]+', k): + for attr in dir(getattr(odrv, k).task_times): + if not attr.startswith('_'): + timings.append((k + '.' + attr, getattr(getattr(odrv, k).task_times, attr), [], [])) # (name, obj, start_times, lengths) + + # Take a couple of samples + print("sampling...") + for i in range(n_samples): + odrv.task_timers_armed = True # Trigger sample and wait for it to finish + while odrv.task_timers_armed: pass + for name, obj, start_times, lengths in timings: + start_times.append(obj.start_time) + lengths.append(obj.length) + print("done") + + # sort by start time + timings = sorted(timings, key = lambda x: np.mean(x[2])) + + plt.rcParams['figure.figsize'] = 21, 9 + plt.figure() + plt.grid(True) + plt.barh( + [-i for i in range(len(timings))], # y positions + [np.mean(lengths) for name, obj, start_times, lengths in timings], # lengths + left = [np.mean(start_times) for name, obj, start_times, lengths in timings], # starts + xerr = ( + [np.std(lengths) for name, obj, start_times, lengths in timings], # error bars to the left side + [(min(obj.max_length, 20100) - np.mean(lengths)) for name, obj, start_times, lengths in timings], # error bars to the right side - TODO: remove artificial min() + ), + tick_label = [name for name, obj, start_times, lengths in timings], # labels + ) + plt.savefig(path, bbox_inches='tight') diff --git a/tools/odrive/version.py b/tools/odrive/version.py index 73bd8166d..49169997b 100644 --- a/tools/odrive/version.py +++ b/tools/odrive/version.py @@ -28,7 +28,7 @@ def get_version_from_git(): script_dir = os.path.dirname(os.path.realpath(__file__)) try: # Determine the current git commit version - git_tag = subprocess.check_output(["git", "describe", "--always", "--tags", "--dirty=*"], + git_tag = subprocess.check_output(["git", "describe", "--always", "--tags", "--match=*fw*", "--dirty=*"], cwd=script_dir) git_tag = git_tag.decode(sys.stdout.encoding or 'ascii').rstrip('\n') diff --git a/tools/odrivetool b/tools/odrivetool index 60ed99bb4..5d0e9776b 100755 --- a/tools/odrivetool +++ b/tools/odrivetool @@ -5,19 +5,24 @@ ODrive command line utility from __future__ import print_function import sys + +# We require Python 3.5 for the "async def" syntax. +if sys.version_info <= (3, 5): + print("Your Python version (Python {}.{}) is too old. Please install Python 3.5 or newer.".format( + sys.version_info.major, sys.version_info.minor + )) + exit(1) + +import sys import os import argparse import time import math -sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname( - os.path.realpath(__file__))), - "Firmware", "fibre", "python")) -import fibre.discovery -from fibre import Logger, Event import odrive from odrive.utils import OperationAbortedException from odrive.configuration import * +from fibre import Logger, Event # Flush stdout by default # Source: @@ -105,7 +110,7 @@ parser.add_argument("-v", "--verbose", action="store_true", parser.add_argument("--version", action="store_true", help="print version information and exit") -parser.set_defaults(path="usb") +parser.set_defaults(path="usb:idVendor=0x1209,idProduct=0x0D32,bInterfaceClass=0,bInterfaceSubClass=1,bInterfaceProtocol=0") args = parser.parse_args() # Default command @@ -135,7 +140,7 @@ try: print(" or better yet, submit a pull request to fix it.") print("") import odrive.shell - odrive.shell.launch_shell(args, logger, app_shutdown_token) + odrive.shell.launch_shell(args, logger) elif args.command == 'dfu': print_version() diff --git a/tools/setup.py b/tools/setup.py index 83d5c1ed2..f0b158d6b 100644 --- a/tools/setup.py +++ b/tools/setup.py @@ -67,25 +67,13 @@ if creating_package: if is_post_release: version += str(post_rel_num) - elif (devnum > 0): - version += str(devnum) + #elif (devnum > 0): + # version += str(devnum) + version+= str(devnum) version_file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'odrive', 'version.txt') with open(version_file_path, mode='w') as version_file: version_file.write(version) - - # Temporarily link fibre into the python tools directory - # TODO: distribute a fibre package separately - fibre_src = os.path.join(os.path.dirname(os.path.dirname( - os.path.realpath(__file__))), - "Firmware", "fibre", "python", "fibre") - fibre_link = os.path.join(os.path.dirname( - os.path.realpath(__file__)), "fibre") - if not os.path.exists(fibre_link): - if sys.version_info > (3, 3): - os.symlink(fibre_src, fibre_link, target_is_directory=True) - else: - os.symlink(fibre_src, fibre_link) # TODO: find a better place for this if not creating_package: @@ -99,7 +87,7 @@ try: setup( name = 'odrive', - packages = ['odrive', 'odrive.dfuse', 'fibre'], + packages = ['odrive', 'odrive.dfuse', 'odrive.pyfibre.fibre'], scripts = ['odrivetool', 'odrivetool.bat', 'odrive_demo.py'], version = version, description = 'Control utilities for the ODrive high performance motor controller', @@ -110,16 +98,20 @@ keywords = ['odrive', 'motor', 'motor control'], install_requires = [ 'ipython', # Used to do the interactive parts of the odrivetool - 'PyUSB', # Required to access USB devices from Python through libusb - 'PySerial', # Required to access serial devices from Python + 'PyUSB', # Only required for DFU. Normal communication happens through libfibre. 'requests', # Used to by DFU to load firmware files 'IntelHex', # Used to by DFU to download firmware from github 'matplotlib', # Required to run the liveplotter 'monotonic', # For compatibility with older python versions - 'appdirs', # Used to find caching directory + 'setuptools', # ubuntu-latest on GitHub Actions fails to install odrive without this dependency 'pywin32 >= 222; platform_system == "Windows"' # Required for fancy terminal features on Windows ], - package_data={'': ['version.txt']}, + package_data={'': [ + 'version.txt', + 'pyfibre/fibre/*.so', + 'pyfibre/fibre/*.dll', + 'pyfibre/fibre/*.dylib' + ]}, classifiers = [], ) @@ -129,4 +121,3 @@ # clean up if creating_package: os.remove(version_file_path) - os.remove(fibre_link) diff --git a/tools/setup_hall_as_index.py b/tools/setup_hall_as_index.py index 8ba871a8c..e068963f9 100644 --- a/tools/setup_hall_as_index.py +++ b/tools/setup_hall_as_index.py @@ -28,7 +28,6 @@ ax.encoder.config.cpr = 4096 ax.encoder.config.use_index = True ax.encoder.config.find_idx_on_lockin_only = True - ax.encoder.config.idx_search_unidirectional = True ax.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL ax.controller.config.vel_limit = 10000 @@ -93,5 +92,5 @@ def wait_and_exit_on_error(ax): odrv.save_configuration() try: odrv.reboot() - except odrive.fibre.ChannelBrokenException: + except odrive.fibre.ObjectLostError: pass diff --git a/tools/test-rig-jw.yaml b/tools/test-rig-jw.yaml new file mode 100644 index 000000000..db064dc01 --- /dev/null +++ b/tools/test-rig-jw.yaml @@ -0,0 +1,62 @@ + +components: + - type: generalpurpose + name: homenet + net: homenet + + - type: generalpurpose + name: rpi + ssh: odrv + net: homenet + components: + - type: can + name: can0 + interface: can0 + path: /dev/serial/by-id/usb-Protofusion_Labs_CANable_1205aa6_https:__github.com_normaldotcom_cantact-fw_002080015852430220363530-if00 + connected-to: odrive.can + + - type: odrive + name: odrive-v4_1 + board-version: v4.1-58V + serial-number: "2060307C4E53" + encoder0: virtual_encoder0 + motor0: D6374_150KV_0 + + - type: odrive + name: odrive-v4_0 + board-version: v4.0-58V + serial-number: "206730834E53" # smart motor + encoder0: virtual_encoder1 + motor0: D6374_150KV_1 + + - type: motor + name: D6374_150KV_0 + phase-resistance: 0.065 + phase-inductance: 2.15e-05 + + - type: motor + name: D6374_150KV_1 + phase-resistance: 0.065 + phase-inductance: 2.15e-05 + + - type: encoder + name: quadrature_encoder0 + cpr: 8192 + max-rpm: 7000 + + - type: encoder + name: quadrature_encoder1 + cpr: 8192 + max-rpm: 7000 + +connections: + - ['odrive-v4_1.can', 'rpi.can0'] + - ['odrive-v4_1.axis0', 'D6374_150KV_0.phases'] + - ['odrive-v4_0.axis0', 'D6374_150KV_1.phases'] + - ['quadrature_encoder0.shaft', 'D6374_150KV_0.shaft', 'D6374_150KV_1.shaft', 'quadrature_encoder1.shaft'] + - ['odrive-v4_1.encoder0.a', 'quadrature_encoder0.a'] + - ['odrive-v4_1.encoder0.b', 'quadrature_encoder0.b'] + - ['odrive-v4_1.encoder0.z', 'quadrature_encoder0.z'] + - ['odrive-v4_0.encoder0.a', 'quadrature_encoder1.a'] + - ['odrive-v4_0.encoder0.b', 'quadrature_encoder1.b'] + - ['odrive-v4_0.encoder0.z', 'quadrature_encoder1.z'] diff --git a/tools/test-rig-rpi.yaml b/tools/test-rig-pj.yaml similarity index 91% rename from tools/test-rig-rpi.yaml rename to tools/test-rig-pj.yaml index b99540498..59491ae3a 100644 --- a/tools/test-rig-rpi.yaml +++ b/tools/test-rig-pj.yaml @@ -30,8 +30,8 @@ components: - type: odrive name: odrive board-version: v3.6-58V - serial-number: "20703595524B" - brake-resistance: 0.47 + serial-number: "2061398A4D4D" + brake-resistance: 0.50 usb: auto can: main_canbus vbus-voltage: 24 # [V] @@ -56,8 +56,9 @@ components: cpr: 8192 max-rpm: 7000 - - type: teensy + - type: arduino name: teensy + board-version: teensy:avr:teensy40 - {type: lpf, name: lpf0} - {type: lpf, name: lpf1} @@ -89,8 +90,8 @@ connections: - ['teensy.gpio3', 'odrive.spi.mosi'] - ['teensy.gpio4', 'odrive.spi.miso'] - ['teensy.gpio5', 'odrive.spi.sck'] - - ['odrive.axis0', 'D5065-270KV_0'] - - ['D5065-270KV_0', 'real_encoder'] + - ['odrive.axis0', 'D5065-270KV_0.phases'] + - ['D5065-270KV_0.shaft', 'real_encoder.shaft'] - ['odrive.gpio3', 'lpf0'] - ['odrive.gpio4', 'lpf1'] - ['lpf0.en', 'lpf1.en', 'rpi.gpio16'] diff --git a/tools/test-rig-ss3.yaml b/tools/test-rig-ss3.yaml new file mode 100644 index 000000000..8329abdad --- /dev/null +++ b/tools/test-rig-ss3.yaml @@ -0,0 +1,93 @@ + +components: + - type: generalpurpose + name: homenet + net: homenet + + - type: generalpurpose + name: rpi + ssh: odrv + net: homenet + components: + - type: uart + name: uart0 + port: /dev/ttyS0 + connected-to: main_uart + - type: can + name: can0 + interface: can0 + connected-to: odrive.can + # need to specify GPIOs explicitly for the generalpurpose type + - {type: gpio, num: 16} + - {type: gpio, num: 19} + - {type: gpio, num: 20} + - {type: gpio, num: 26} + + - type: odrive + name: odrive + board-version: v3.6-56V + serial-number: "20703595524B" + brake-resistance: 2.0 + usb: auto + can: main_canbus + vbus-voltage: 20.5 # [V] + max-brake-power: 150 # [W] + encoder0: virtual_encoder0 + encoder1: virtual_encoder1 + motor0: D5065-270KV_0 + motor1: floating + + - type: motor + name: D5065-270KV_0 + phase-resistance: 0.039 + phase-inductance: 1.57e-05 + pole-pairs: 7 + direction: 1 + kv: 270 + max-current: 70 + max-voltage: 40 + + - type: encoder + name: real_encoder + cpr: 8192 + max-rpm: 7000 + + - type: arduino + name: teensy + board-version: teensy:avr:teensy40 + + - {type: lpf, name: lpf0} + - {type: lpf, name: lpf1} + +connections: + - ['odrive.can', 'rpi.can0'] + - ['teensy.program', 'rpi.gpio26'] + - ['teensy.gpio12', 'rpi.uart0.tx'] + - ['teensy.gpio13', 'rpi.uart0.rx'] + - ['teensy.gpio11', 'odrive.gpio1'] + - ['teensy.gpio10', 'odrive.gpio2'] + - ['teensy.gpio9', 'odrive.gpio3'] + - ['teensy.gpio8', 'odrive.gpio4'] + - ['teensy.gpio14', 'odrive.gpio5'] + - ['teensy.gpio15', 'odrive.gpio6'] + - ['teensy.gpio16', 'odrive.gpio7'] + - ['teensy.gpio17', 'odrive.gpio8'] + - ['teensy.gpio6', 'rpi.gpio20'] + - ['teensy.gpio7', 'rpi.gpio19'] + - ['teensy.gpio23', 'odrive.encoder0.z'] + - ['teensy.gpio22', 'odrive.encoder0.b'] + - ['teensy.gpio21', 'odrive.encoder0.a'] + - ['teensy.gpio20', 'odrive.encoder1.z'] + - ['teensy.gpio19', 'odrive.encoder1.b'] + - ['teensy.gpio18', 'odrive.encoder1.a'] + - ['teensy.gpio0', 'real_encoder.z'] + - ['teensy.gpio1', 'real_encoder.a'] + - ['teensy.gpio2', 'real_encoder.b'] + - ['teensy.gpio3', 'odrive.spi.mosi'] + - ['teensy.gpio4', 'odrive.spi.miso'] + - ['teensy.gpio5', 'odrive.spi.sck'] + - ['odrive.axis0', 'D5065-270KV_0.phases'] + - ['D5065-270KV_0.shaft', 'real_encoder.shaft'] + - ['odrive.gpio3', 'lpf0'] + - ['odrive.gpio4', 'lpf1'] + - ['lpf0.en', 'lpf1.en', 'rpi.gpio16'] diff --git a/tools/test-rig-ss4.yaml b/tools/test-rig-ss4.yaml new file mode 100644 index 000000000..8c6583196 --- /dev/null +++ b/tools/test-rig-ss4.yaml @@ -0,0 +1,102 @@ + +components: + - type: generalpurpose + name: homenet + net: homenet + + - type: generalpurpose + name: rpi + ssh: odrv + net: homenet + components: + - type: uart + name: uart0 + port: /dev/ttyS0 + connected-to: main_uart + # need to specify GPIOs explicitly for the generalpurpose type + - {type: gpio, num: 4} + - {type: gpio, num: 18} + - {type: gpio, num: 23} + + - type: odrive + name: odrive + board-version: v4.1-58V + serial-number: "206730814E53" + brake-resistance: 0.0 + usb: auto + can: main_canbus + vbus-voltage: 24 # [V] + max-brake-power: 0 # [W] + encoder0: virtual_encoder0 + motor0: D5065-270KV_0 + + - type: motor + name: D5065-270KV_0 + phase-resistance: 0.039 + phase-inductance: 1.57e-05 + pole-pairs: 7 + direction: 1 + kv: 270 + max-current: 70 + max-voltage: 40 + + - type: encoder + name: real_encoder + cpr: 8192 + max-rpm: 7000 + + - type: arduino + name: teensy + board-version: teensy:avr:teensy41 + + - {type: lpf, name: lpf0} + - {type: lpf, name: lpf1} + +connections: + - ['teensy.program', 'rpi.gpio4'] + - ['teensy.gpio14', 'rpi.uart0.tx'] + - ['teensy.gpio15', 'rpi.uart0.rx'] + - ['teensy.gpio16', 'rpi.gpio18'] + - ['teensy.gpio17', 'rpi.gpio23'] + + # J8 + - ['teensy.gpio0', 'odrive.gpio6'] + - ['teensy.gpio1', 'odrive.gpio5'] + - ['teensy.gpio2', 'odrive.gpio4'] + - ['teensy.gpio3', 'odrive.gpio3'] + - ['teensy.gpio4', 'odrive.gpio0'] + - ['teensy.gpio5', 'odrive.gpio1'] + - ['teensy.gpio6', 'odrive.gpio2'] + + - ['teensy.gpio8', 'odrive.gpio16'] + - ['teensy.gpio9', 'odrive.gpio17'] + - ['teensy.gpio10', 'odrive.gpio18'] + - ['teensy.gpio11', 'odrive.gpio19'] + - ['teensy.gpio12', 'odrive.gpio20'] + - ['teensy.gpio24', 'odrive.gpio21'] + - ['teensy.gpio25', 'odrive.gpio22'] + + - ['teensy.gpio26', 'odrive.gpio10'] + - ['teensy.gpio27', 'odrive.gpio11'] + - ['teensy.gpio28', 'odrive.gpio15'] + - ['teensy.gpio29', 'odrive.gpio14'] + - ['teensy.gpio30', 'odrive.gpio13'] + - ['teensy.gpio31', 'odrive.gpio12'] + + - ['teensy.gpio33', 'odrive.gpio8'] + - ['teensy.gpio34', 'odrive.gpio7'] + - ['teensy.gpio35', 'odrive.gpio9'] + + - ['teensy.gpio23', 'real_encoder.b'] + - ['teensy.gpio22', 'real_encoder.a'] + - ['teensy.gpio21', 'real_encoder.z'] + - ['odrive.axis0', 'D5065-270KV_0.phases'] + - ['D5065-270KV_0.shaft', 'real_encoder.shaft'] + + - ['odrive.encoder0.a', 'odrive.gpio0'] + - ['odrive.encoder0.b', 'odrive.gpio5'] + - ['odrive.encoder0.z', 'odrive.gpio6'] # TODO + +# - ['odrive.encoder1.a', 'odrive.gpio0'] +# - ['odrive.encoder1.b', 'odrive.gpio5'] +# - ['odrive.encoder1.z', 'odrive.gpio6'] # TODO