diff --git a/.github/workflows/e2e_test.yaml b/.github/workflows/e2e_test.yaml new file mode 100644 index 0000000..7a79d7b --- /dev/null +++ b/.github/workflows/e2e_test.yaml @@ -0,0 +1,114 @@ +name: E2E Tests + +on: + pull_request: + paths: + - '.github/workflows/robot-tests.yml' + - 'test/**' + - '!test/README.md' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + runs-on: ${{ matrix.os }} + continue-on-error: true + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-13] + arch: [amd64, arm64] + include: + - os: windows-latest + target: win + - os: ubuntu-latest + target: lin + - os: macos-13 + target: mac + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + + - name: Install pip dependencies + run: | + pip install --upgrade pip + pip install -r test/requirements.txt + + - name: Setup vcpkg environment + uses: soumeh01/actions/vcpkg@testwindows + with: + config: "./test/vcpkg-configuration.json" + vcpkg-root: "${{ github.workspace }}/.vcpkg" + cache: "-" + + - name: Activate Arm tool license + run: | + armlm activate --server https://mdk-preview.keil.arm.com --product KEMDK-COM0 + working-directory: ./test + + - name: Run Test + run: | + python -m robot --outputdir reports-${{ matrix.target }}-${{ matrix.arch }} --settag ${{ matrix.target }}-${{ matrix.arch }} --name ${{ matrix.target }}-${{ matrix.arch }} ./test + + - name: Archieve test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: reports-${{ matrix.target }}-${{ matrix.arch }} + path: reports-${{ matrix.target }}-${{ matrix.arch }} + + report: + runs-on: ubuntu-latest + if: always() + needs: test + permissions: write-all + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + + - name: Install pip dependencies + run: | + pip install --upgrade pip + pip install -r test/requirements.txt + + - name: Download reports + uses: actions/download-artifact@v4 + with: + path: artifacts + pattern: reports-* + + - name: Consolidate robot test results + working-directory: artifacts + run: | + python -m robot.rebot --name Collective_Robot_Results --outputdir collective_robot_results --output output.xml \ + ./reports-win-amd64/output.xml ./reports-win-arm64/output.xml \ + ./reports-lin-amd64/output.xml ./reports-lin-arm64/output.xml \ + ./reports-mac-amd64/output.xml ./reports-mac-arm64/output.xml + + - name: Generate Summary report + if: always() + run: | + python ./test/lib/execution_summary.py artifacts/collective_robot_results/output.xml + + - name: Print E2E Report + if: always() + run: cat summary_report.md >> $GITHUB_STEP_SUMMARY + + - name: Archieve consolidated test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: consolidated-reports + path: artifacts/collective_robot_results diff --git a/.gitignore b/.gitignore index 378eac2..92b0c45 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ -build +# Directories +/build*/ +**/run +**/__pycache__ + +# Files +**/stdout.txt \ No newline at end of file diff --git a/ADR System test framework.md b/ADR System test framework.md new file mode 100644 index 0000000..50ab22c --- /dev/null +++ b/ADR System test framework.md @@ -0,0 +1,49 @@ +# System test framework + +The objective of this document is to conduct a thorough evaluation of various system test frameworks while taking into account our specific use cases. Our aim is to gain insight into the advantages and disadvantages of the frameworks under consideration, thereby facilitating a fair comparison. + +## Contributors + +- Sourabh Mehta + +## Reviewer + +- Daniel Brondani +- Jonatan Antony +- Samuel Pelegrinello Caipers +- Evgueni Driouk +- Thorsten de Buhr + +## Context + +The end-to-end (E2E) tests developed using decided framework aim to validate the fully integrated applications, assessing the interactions among different utilities within the entire system. The overarching goal is to simulate user interactions and ensure that the desired build output is achieved through comprehensive system testing. + +## Basic expectation from framework + +- Access to system env variables +- Flexibility to run EXEs, capture return code, stdout, stderr +- Golden file comparison +- Generates HTML report + +## Comparison + +| Sr. No | key points |GoogleTest | PyTest | RobotTest | +| :----- |:-- |:--------- | :----- | :-------- | +|1|Language Support|C++|python|keyword/macro based (backed by python)| +|2|Debug|yes|yes|debug with prints or logs| +|3|Dependencies|external dep (need to maintain as submodule in repo)| external dep need to be installed|external dep need to be installed| +|4|HTML Report|Generates XML, 3rd party lib is needed to generate HTML|HTML reports with pytest-html plugin|Inbuilt [HTML report](https://robotframework.org/RobotDemo/report.html) generation| +|5|Ease of use|yes|yes|yes (Needs learning)| +|6|Flexibility and Customization|yes|yes|yes| +|7|Integration with CI/CD|yes|yes|yes| +|8|Feedback Mechanisms & diagnosis|easy to diagnose|easy to diagnose|use [debug logs](https://robotframework.org/RobotDemo/log.html) for diagnosis| + +## Considerations + +- cbuildgen has toochain related tests (AC6, GCC, IAR) + - Are we running all toolchains tests on cmsis-toolbox? +- cpackget test should be migrated + +## Design Decision + +It was determined that the `robot test` would be the optimal choice. This option requires no language-specific learning and offers a more user-friendly reporting mechanism. Additionally, the robot library boasts extensive support and numerous prebuilt keywords. diff --git a/test/RobotTests.md b/test/RobotTests.md new file mode 100644 index 0000000..e01e9f9 --- /dev/null +++ b/test/RobotTests.md @@ -0,0 +1,96 @@ +# Installation and Running Robot Framework Tests + +This guide will walk you through the installation process and running of Robot Framework tests. + +## Prerequisites + +Before running Robot Framework tests, ensure you have the following prerequisites installed on your system: + +- Python (minimum recommended version **3.11**) +- pip (python package manager) +- [vcpkg](https://github.com/Open-CMSIS-Pack/cmsis-toolbox/blob/main/docs/installation.md#vcpkg---setup-using-cli) + +## Install Test Environment + +### Install Robot Framework + +Install Robot Framework and its dependencies using pip: + +```bash +cd /test +pip install --upgrade pip +pip install -r requirements.txt +``` + +### Install Toolchains using vcpkg + +These commands will install all the required toolchains listed [here](./vcpkg-configuration.json): + +```bash +cd /test +vcpkg x-update-registry --all +vcpkg activate +``` + +```txt +👉 Important: + The AC6 toolchain installation includes the "fromelf" utility, which can be found in the AC6 toolchain installation + directory. Ensure that "fromelf" utlity is in PATH. +``` + +## Running Tests + +### Run all tests + +This command will run all tests located in the `test` directory and place the test reports and logs under specified directory. + +```bash +robot -d +``` + +for e.g. + +```bash +vcpkg activate +robot -d results ./test +``` + +### Running Specific Tests + +To run specific tests, use the `--test` options: + +```bash +robot --test +``` + +for e.g. + +```bash +vcpkg activate +robot --test "Validate build-c Example" test/test.robot +``` + +## Adding Tests + +The test cases in [test.robot](./test.robot) are implemented in a data-driven style, where each test case utilizes a single higher-level keyword to encapsulate the test workflow. To incorporate a new example for validation, follow the steps outlined below. + +- Add Example under [data](./data/) directory. +- Add test details under **Test Cases** section following below conventions + + ```robot + # + # .csolution.yml file> + ``` + + for e.g. + + ```robot + Validate USB Example + ${TEST_DATA_DIR}${/}${USB}${/}solution.csolution.yml ${0} ${USB} + ``` + +```txt +☑️ Note: + All options in the tests should be separated by "TABs". + For more information on robot test follow https://docs.robotframework.org/docs/testcase_styles/datadriven +``` diff --git a/test/__init__.robot b/test/__init__.robot new file mode 100644 index 0000000..4dd5d47 --- /dev/null +++ b/test/__init__.robot @@ -0,0 +1,3 @@ +*** Settings *** +Documentation Initialization for the robot tests in this directory +Test Timeout 10 minutes diff --git a/test/data/build-asm/project/AC6/AsmArm.s b/test/data/build-asm/project/AC6/AsmArm.s new file mode 100644 index 0000000..a20db50 --- /dev/null +++ b/test/data/build-asm/project/AC6/AsmArm.s @@ -0,0 +1,16 @@ + + AREA DATA + + IF HEXADECIMAL_TEST != 11259375 ; 0xABCDEF + INFO 1, "HEXADECIMAL_TEST failed!" + ENDIF + + IF DECIMAL_TEST != 1234567890 + INFO 1, "DECIMAL_TEST failed!" + ENDIF + + IF STRING_TEST != "String0" + INFO 1, "STRING_TEST failed!" + ENDIF + + END diff --git a/test/data/build-asm/project/AC6/Auto.s b/test/data/build-asm/project/AC6/Auto.s new file mode 100644 index 0000000..28048d6 --- /dev/null +++ b/test/data/build-asm/project/AC6/Auto.s @@ -0,0 +1,9 @@ + + AREA DATA + + IF :LNOT::DEF:AUTO_DEF + INFO 1, "AUTO_DEF is not defined!" + ENDIF + + END + \ No newline at end of file diff --git a/test/data/build-asm/project/AC6/GnuSyntax.s b/test/data/build-asm/project/AC6/GnuSyntax.s new file mode 100644 index 0000000..02ca75f --- /dev/null +++ b/test/data/build-asm/project/AC6/GnuSyntax.s @@ -0,0 +1,10 @@ + + .syntax unified + .arch armv7-m + + .ifndef GAS_DEF + .error "GAS_DEF is not defined!" + .endif + + .end + \ No newline at end of file diff --git a/test/data/build-asm/project/AC6/PreProcessed.S b/test/data/build-asm/project/AC6/PreProcessed.S new file mode 100644 index 0000000..4fd3eaf --- /dev/null +++ b/test/data/build-asm/project/AC6/PreProcessed.S @@ -0,0 +1,9 @@ + + .syntax unified + .arch armv7-m + +#ifndef PRE_PROCESSED_DEF + .error "PRE_PROCESSED_DEF is not defined!" +#endif + + .end diff --git a/test/data/build-asm/project/GCC/GAS.s b/test/data/build-asm/project/GCC/GAS.s new file mode 100644 index 0000000..39daeca --- /dev/null +++ b/test/data/build-asm/project/GCC/GAS.s @@ -0,0 +1,9 @@ + + .syntax unified + .arch armv7-m + + .ifndef GAS_DEF + .error "GAS_DEF is not defined!" + .endif + + .end diff --git a/test/data/build-asm/project/GCC/NonPreProcessed.s b/test/data/build-asm/project/GCC/NonPreProcessed.s new file mode 100644 index 0000000..8c374a9 --- /dev/null +++ b/test/data/build-asm/project/GCC/NonPreProcessed.s @@ -0,0 +1,13 @@ + + .syntax unified + .arch armv7-m + +#ifndef PRE_PROCESSED_DEF + .equ SET_ERR_DEF,1 +#endif + + .ifndef SET_ERR_DEF + .error "SET_ERR_DEF is not defined! It seems this file was preprocessed but it shouldn't!" + .endif + + .end diff --git a/test/data/build-asm/project/GCC/PreProcessed.S b/test/data/build-asm/project/GCC/PreProcessed.S new file mode 100644 index 0000000..4fd3eaf --- /dev/null +++ b/test/data/build-asm/project/GCC/PreProcessed.S @@ -0,0 +1,9 @@ + + .syntax unified + .arch armv7-m + +#ifndef PRE_PROCESSED_DEF + .error "PRE_PROCESSED_DEF is not defined!" +#endif + + .end diff --git a/test/data/build-asm/project/IAR/Asm.s b/test/data/build-asm/project/IAR/Asm.s new file mode 100644 index 0000000..30354d8 --- /dev/null +++ b/test/data/build-asm/project/IAR/Asm.s @@ -0,0 +1,15 @@ + MODULE ?Asm + +#ifndef IAR_ASM_DEF +#error "IAR_ASM_DEF is not defined!" +#endif + + EXTERN Reset_Handler_C + + PUBWEAK Reset_Handler + SECTION .text:CODE:REORDER:NOROOT(2) +Reset_Handler + LDR R0, =Reset_Handler_C + BX R0 + + END diff --git a/test/data/build-asm/project/main.c b/test/data/build-asm/project/main.c new file mode 100644 index 0000000..2e169c3 --- /dev/null +++ b/test/data/build-asm/project/main.c @@ -0,0 +1,6 @@ +#include "RTE_Components.h" +#include CMSIS_device_header + +int main(void) { + return 0; +} diff --git a/test/data/build-asm/project/project.cproject.yml b/test/data/build-asm/project/project.cproject.yml new file mode 100644 index 0000000..c492c5e --- /dev/null +++ b/test/data/build-asm/project/project.cproject.yml @@ -0,0 +1,53 @@ +project: + + components: + - component: ARM::CMSIS:CORE + - component: ARM::Device:Startup&C Startup + + groups: + - group: Source + files: + - file: ./main.c + + - group: GCC-CLANG + for-compiler: [GCC, CLANG] + files: + - file: ./GCC/GAS.s + for-compiler: GCC + define: + - GAS_DEF + - file: ./GCC/PreProcessed.S + define: + - PRE_PROCESSED_DEF + - file: ./GCC/NonPreProcessed.s + misc: + - ASM: + - -DPRE_PROCESSED_DEF + + - group: AC6 + for-compiler: AC6 + files: + - file: ./AC6/AsmArm.s + define: + - HEXADECIMAL_TEST: 0xABCDEF + - DECIMAL_TEST: 1234567890 + - STRING_TEST: "\"String0\"" + - file: ./AC6/GnuSyntax.s + define: + - GAS_DEF + misc: + - ASM: + - -masm=gnu + - file: ./AC6/PreProcessed.S + define: + - PRE_PROCESSED_DEF + - file: ./AC6/Auto.s + define: + - AUTO_DEF + + - group: IAR + for-compiler: IAR + files: + - file: ./IAR/Asm.s + define: + - IAR_ASM_DEF diff --git a/test/data/build-asm/solution.csolution.yml b/test/data/build-asm/solution.csolution.yml new file mode 100644 index 0000000..2c8cfb3 --- /dev/null +++ b/test/data/build-asm/solution.csolution.yml @@ -0,0 +1,27 @@ +solution: + created-for: CMSIS-Toolbox@2.2.1 + cdefault: + + packs: + - pack: ARM::Cortex_DFP + - pack: ARM::CMSIS + + target-types: + - type: ARMCM0 + device: ARMCM0 + + build-types: + - type: AC6 + compiler: AC6 + + - type: GCC + compiler: GCC + + # - type: IAR + # compiler: IAR + + - type: CLANG + compiler: CLANG + + projects: + - project: ./project/project.cproject.yml diff --git a/test/data/build-c/project/main.c b/test/data/build-c/project/main.c new file mode 100644 index 0000000..2e169c3 --- /dev/null +++ b/test/data/build-c/project/main.c @@ -0,0 +1,6 @@ +#include "RTE_Components.h" +#include CMSIS_device_header + +int main(void) { + return 0; +} diff --git a/test/data/build-c/project/project.cproject.yml b/test/data/build-c/project/project.cproject.yml new file mode 100644 index 0000000..0d21eac --- /dev/null +++ b/test/data/build-c/project/project.cproject.yml @@ -0,0 +1,10 @@ +project: + + components: + - component: ARM::CMSIS:CORE + - component: ARM::Device:Startup&C Startup + + groups: + - group: Source + files: + - file: ./main.c diff --git a/test/data/build-c/solution.csolution.yml b/test/data/build-c/solution.csolution.yml new file mode 100644 index 0000000..0392558 --- /dev/null +++ b/test/data/build-c/solution.csolution.yml @@ -0,0 +1,31 @@ +solution: + created-for: CMSIS-Toolbox@2.2.1 + cdefault: + + packs: + - pack: ARM::Cortex_DFP + - pack: ARM::CMSIS + + target-types: + - type: ARMCM0 + device: ARMCM0 + + build-types: + - type: AC6 + compiler: AC6 + + - type: GCC + compiler: GCC + misc: + - Library: + - -lm + - -lc + + # - type: IAR + # compiler: IAR + + - type: CLANG + compiler: CLANG + + projects: + - project: ./project/project.cproject.yml diff --git a/test/data/build-cpp/project/main.cpp b/test/data/build-cpp/project/main.cpp new file mode 100644 index 0000000..c3103ed --- /dev/null +++ b/test/data/build-cpp/project/main.cpp @@ -0,0 +1,8 @@ +#include "RTE_Components.h" +#include CMSIS_device_header +#include + +int main(void) { + std::size_t s = 0; + return s; +} diff --git a/test/data/build-cpp/project/project.cproject.yml b/test/data/build-cpp/project/project.cproject.yml new file mode 100644 index 0000000..7198351 --- /dev/null +++ b/test/data/build-cpp/project/project.cproject.yml @@ -0,0 +1,10 @@ +project: + + components: + - component: ARM::CMSIS:CORE + - component: ARM::Device:Startup&C Startup + + groups: + - group: Source + files: + - file: ./main.cpp diff --git a/test/data/build-cpp/solution.csolution.yml b/test/data/build-cpp/solution.csolution.yml new file mode 100644 index 0000000..b95b8df --- /dev/null +++ b/test/data/build-cpp/solution.csolution.yml @@ -0,0 +1,27 @@ +solution: + created-for: CMSIS-Toolbox@2.2.1 + cdefault: + + packs: + - pack: ARM::Cortex_DFP + - pack: ARM::CMSIS + + target-types: + - type: ARMCM55 + device: ARMCM55 + + build-types: + - type: AC6 + compiler: AC6 + + - type: GCC + compiler: GCC + + # - type: IAR + # compiler: IAR + + - type: CLANG + compiler: CLANG + + projects: + - project: ./project/project.cproject.yml diff --git a/test/data/include-define/project/inc1/inc.h b/test/data/include-define/project/inc1/inc.h new file mode 100644 index 0000000..04ff366 --- /dev/null +++ b/test/data/include-define/project/inc1/inc.h @@ -0,0 +1 @@ +#define INC1 1 diff --git a/test/data/include-define/project/inc2/inc.h b/test/data/include-define/project/inc2/inc.h new file mode 100644 index 0000000..0c9d146 --- /dev/null +++ b/test/data/include-define/project/inc2/inc.h @@ -0,0 +1 @@ +#define INC2 1 diff --git a/test/data/include-define/project/inc3/inc.h b/test/data/include-define/project/inc3/inc.h new file mode 100644 index 0000000..2a9b589 --- /dev/null +++ b/test/data/include-define/project/inc3/inc.h @@ -0,0 +1 @@ +#define INC3 1 diff --git a/test/data/include-define/project/main.c b/test/data/include-define/project/main.c new file mode 100644 index 0000000..2e169c3 --- /dev/null +++ b/test/data/include-define/project/main.c @@ -0,0 +1,6 @@ +#include "RTE_Components.h" +#include CMSIS_device_header + +int main(void) { + return 0; +} diff --git a/test/data/include-define/project/not-supported/unknown.c b/test/data/include-define/project/not-supported/unknown.c new file mode 100644 index 0000000..e69de29 diff --git a/test/data/include-define/project/project.cproject.yml b/test/data/include-define/project/project.cproject.yml new file mode 100644 index 0000000..c6f1826 --- /dev/null +++ b/test/data/include-define/project/project.cproject.yml @@ -0,0 +1,50 @@ +project: + + components: + - component: ARM::CMSIS:CORE + del-path: + - ./inc1 + undefine: + - DEF1 + - component: ARM::Device:Startup&C Startup + add-path: + - ./inc2 + define: + - DEF2: 1 + + groups: + - group: Source1 + files: + - file: source1.c + add-path: + - ./inc1 + define: + - DEF1: 1 + groups: + - group: Source2 + files: + - file: source2.c + add-path: + - ./inc2 + define: + - DEF2: 1 + del-path: + - ./inc1 + undefine: + - DEF1 + + - group: Main + files: + - file: ./main.c + add-path: + - ./inc2 + define: + - DEF2 + del-path: + - ./not-supported + undefine: + - not-supported + + - group: Headers + files: + - file: ./inc3/inc.h diff --git a/test/data/include-define/project/source1.c b/test/data/include-define/project/source1.c new file mode 100644 index 0000000..9dbf266 --- /dev/null +++ b/test/data/include-define/project/source1.c @@ -0,0 +1,13 @@ +#include "inc.h" + +#ifndef INC1 +#error "INC1 is not defined" +#endif + +#ifndef DEF1 +#error "DEF1 is not defined" +#endif + +int source1(void) { + return 0; +} diff --git a/test/data/include-define/project/source2.c b/test/data/include-define/project/source2.c new file mode 100644 index 0000000..c9fe683 --- /dev/null +++ b/test/data/include-define/project/source2.c @@ -0,0 +1,21 @@ +#include "inc.h" + +#ifdef INC1 +#error "INC1 is defined" +#endif + +#ifndef INC2 +#error "INC2 is not defined" +#endif + +#ifdef DEF1 +#error "DEF1 is defined" +#endif + +#ifndef DEF2 +#error "DEF2 is not defined" +#endif + +int source2(void) { + return 0; +} diff --git a/test/data/include-define/solution.csolution.yml b/test/data/include-define/solution.csolution.yml new file mode 100644 index 0000000..aa53463 --- /dev/null +++ b/test/data/include-define/solution.csolution.yml @@ -0,0 +1,27 @@ +solution: + created-for: CMSIS-Toolbox@2.2.1 + cdefault: + + packs: + - pack: ARM::Cortex_DFP + - pack: ARM::CMSIS + + target-types: + - type: ARMCM0 + device: ARMCM0 + + build-types: + - type: AC6 + compiler: AC6 + + - type: GCC + compiler: GCC + + # - type: IAR + # compiler: IAR + + - type: CLANG + compiler: CLANG + + projects: + - project: ./project/project.cproject.yml diff --git a/test/data/language-scope/pack/ARM.RteTest.pdsc b/test/data/language-scope/pack/ARM.RteTest.pdsc new file mode 100644 index 0000000..cc39b3b --- /dev/null +++ b/test/data/language-scope/pack/ARM.RteTest.pdsc @@ -0,0 +1,37 @@ + + + + ARM + RteTest + www.keil.com/pack/ + + Language and Scope Testing + + + + + initial version + + + + + + + + + + + + Component for testing language and scope attributes + + + + + + + + + + + + \ No newline at end of file diff --git a/test/data/language-scope/pack/LanguageAndScope/Dummy1.c b/test/data/language-scope/pack/LanguageAndScope/Dummy1.c new file mode 100644 index 0000000..3b03dde --- /dev/null +++ b/test/data/language-scope/pack/LanguageAndScope/Dummy1.c @@ -0,0 +1,24 @@ +#include "Hidden.h" +#include "Private.h" +#include "Public.h" +#include "Visible.h" + +#ifndef HIDDEN_H +#error "HIDDEN_H is not defined" +#endif + +#ifndef PRIVATE_H +#error "PRIVATE_H is not defined" +#endif + +#ifndef PUBLIC_H +#error "PUBLIC_H is not defined" +#endif + +#ifndef VISIBLE_H +#error "VISIBLE_H is not defined" +#endif + +int Dummy1(int i) { + return 1; +} diff --git a/test/data/language-scope/pack/LanguageAndScope/Dummy2.cpp b/test/data/language-scope/pack/LanguageAndScope/Dummy2.cpp new file mode 100644 index 0000000..590eab0 --- /dev/null +++ b/test/data/language-scope/pack/LanguageAndScope/Dummy2.cpp @@ -0,0 +1,26 @@ +#include "Hidden.h" +#include "Private.h" +#include "Visible.h" + +#ifndef HIDDEN_H +#error "HIDDEN_H is not defined" +#endif + +#ifndef PRIVATE_H +#error "PRIVATE_H is not defined" +#endif + +#ifndef VISIBLE_H +#error "VISIBLE_H is not defined" +#endif + +#if __has_include("Public.h") +#include "Public.h" +#endif +#ifdef PUBLIC_H +#error "PUBLIC_H is defined" +#endif + +int Dummy2(int i) { + return 1; +} diff --git a/test/data/language-scope/pack/LanguageAndScope/Hidden/Hidden.h b/test/data/language-scope/pack/LanguageAndScope/Hidden/Hidden.h new file mode 100644 index 0000000..0e5ec8f --- /dev/null +++ b/test/data/language-scope/pack/LanguageAndScope/Hidden/Hidden.h @@ -0,0 +1,4 @@ +#ifndef HIDDEN_H +#define HIDDEN_H + +#endif // HIDDEN_H diff --git a/test/data/language-scope/pack/LanguageAndScope/Private/Private.h b/test/data/language-scope/pack/LanguageAndScope/Private/Private.h new file mode 100644 index 0000000..1167708 --- /dev/null +++ b/test/data/language-scope/pack/LanguageAndScope/Private/Private.h @@ -0,0 +1,4 @@ +#ifndef PRIVATE_H +#define PRIVATE_H + +#endif // PRIVATE_H diff --git a/test/data/language-scope/pack/LanguageAndScope/Public/Public.h b/test/data/language-scope/pack/LanguageAndScope/Public/Public.h new file mode 100644 index 0000000..255dab7 --- /dev/null +++ b/test/data/language-scope/pack/LanguageAndScope/Public/Public.h @@ -0,0 +1,4 @@ +#ifndef PUBLIC_H +#define PUBLIC_H + +#endif // PUBLIC_H diff --git a/test/data/language-scope/pack/LanguageAndScope/Visible/Visible.h b/test/data/language-scope/pack/LanguageAndScope/Visible/Visible.h new file mode 100644 index 0000000..7a9b62e --- /dev/null +++ b/test/data/language-scope/pack/LanguageAndScope/Visible/Visible.h @@ -0,0 +1,4 @@ +#ifndef VISIBLE_H +#define VISIBLE_H + +#endif // VISIBLE_H diff --git a/test/data/language-scope/project/main.c b/test/data/language-scope/project/main.c new file mode 100644 index 0000000..ce13f40 --- /dev/null +++ b/test/data/language-scope/project/main.c @@ -0,0 +1,34 @@ +#include "RTE_Components.h" +#include CMSIS_device_header + +#if __has_include("Hidden.h") +#include "Hidden.h" +#endif +#ifdef HIDDEN_H +#error "HIDDEN_H is defined" +#endif + +#if __has_include("Private.h") +#include "Private.h" +#endif +#ifdef PRIVATE_H +#error "PRIVATE_H is defined" +#endif + +#if __has_include("Public.h") +#include "Public.h" +#endif +#ifndef PUBLIC_H +#error "PUBLIC_H is not defined" +#endif + +#if __has_include("Visible.h") +#include "Visible.h" +#endif +#ifndef VISIBLE_H +#error "VISIBLE_H is not defined" +#endif + +int main(void) { + return 0; +} diff --git a/test/data/language-scope/project/project.cproject.yml b/test/data/language-scope/project/project.cproject.yml new file mode 100644 index 0000000..9756286 --- /dev/null +++ b/test/data/language-scope/project/project.cproject.yml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + + components: + - component: ARM::CMSIS:CORE + - component: ARM::Device:Startup&C Startup + - component: RteTest:LanguageAndScope + + groups: + - group: Source + files: + - file: ./main.c diff --git a/test/data/language-scope/solution.csolution.yml b/test/data/language-scope/solution.csolution.yml new file mode 100644 index 0000000..74492e4 --- /dev/null +++ b/test/data/language-scope/solution.csolution.yml @@ -0,0 +1,33 @@ +solution: + created-for: CMSIS-Toolbox@2.2.1 + cdefault: + + packs: + - pack: ARM::Cortex_DFP + - pack: ARM::CMSIS + - pack: ARM::RteTest + path: ./pack + + target-types: + - type: ARMCM0 + device: ARMCM0 + + build-types: + - type: AC6 + compiler: AC6 + + - type: GCC + compiler: GCC + misc: + - Library: + - -lm + - -lc + + # - type: IAR + # compiler: IAR + + - type: CLANG + compiler: CLANG + + projects: + - project: ./project/project.cproject.yml diff --git a/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/ac6_linker_script.sct.src b/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/ac6_linker_script.sct.src new file mode 100644 index 0000000..7820e1f --- /dev/null +++ b/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/ac6_linker_script.sct.src @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023 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. + */ + +/* ---------------------------------------------------------------------------- + Stack seal size definition + *----------------------------------------------------------------------------*/ +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#define __STACKSEAL_SIZE 8 +#else +#define __STACKSEAL_SIZE 0 +#endif + +/*---------------------------------------------------------------------------- + Scatter File Definitions definition + *----------------------------------------------------------------------------*/ + +LR_ROM0 __ROM0_BASE __ROM0_SIZE { + + ER_ROM0 __ROM0_BASE __ROM0_SIZE { + *.o (RESET, +First) + *(InRoot$$Sections) + *(+RO +XO) + } + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + ER_CMSE_VENEER AlignExpr(+0, 32) (__ROM0_SIZE - AlignExpr(ImageLength(ER_ROM0), 32)) { + *(Veneer$$CMSE) + } +#endif + + RW_NOINIT __RAM0_BASE UNINIT (__RAM0_SIZE - __HEAP_SIZE - __STACK_SIZE - __STACKSEAL_SIZE) { + *.o(.bss.noinit) + *.o(.bss.noinit.*) + } + + RW_RAM0 AlignExpr(+0, 8) (__RAM0_SIZE - __HEAP_SIZE - __STACK_SIZE - __STACKSEAL_SIZE - AlignExpr(ImageLength(RW_NOINIT), 8)) { + *(+RW +ZI) + } + +#if __HEAP_SIZE > 0 + ARM_LIB_HEAP (AlignExpr(+0, 8)) EMPTY __HEAP_SIZE { ; Reserve empty region for heap + } +#endif + + ARM_LIB_STACK (__RAM0_BASE + __RAM0_SIZE - __STACKSEAL_SIZE) EMPTY -__STACK_SIZE { ; Reserve empty region for stack + } + +#if __STACKSEAL_SIZE > 0 + STACKSEAL +0 EMPTY __STACKSEAL_SIZE { ; Reserve empty region for stack seal immediately after stack + } +#endif + +#if __RAM1_SIZE > 0 + RW_RAM1 __RAM1_BASE __RAM1_SIZE { + .ANY (+RW +ZI) + } +#endif + +#if __RAM2_SIZE > 0 + RW_RAM2 __RAM2_BASE __RAM2_SIZE { + .ANY (+RW +ZI) + } +#endif + +#if __RAM3_SIZE > 0 + RW_RAM3 __RAM3_BASE __RAM3_SIZE { + .ANY (+RW +ZI) + } +#endif +} + +#if __ROM1_SIZE > 0 +LR_ROM1 __ROM1_BASE __ROM1_SIZE { + ER_ROM1 +0 __ROM1_SIZE { + .ANY (+RO +XO) + } +} +#endif + +#if __ROM2_SIZE > 0 +LR_ROM2 __ROM2_BASE __ROM2_SIZE { + ER_ROM2 +0 __ROM2_SIZE { + .ANY (+RO +XO) + } +} +#endif + +#if __ROM3_SIZE > 0 +LR_ROM3 __ROM3_BASE __ROM3_SIZE { + ER_ROM3 +0 __ROM3_SIZE { + .ANY (+RO +XO) + } +} +#endif diff --git a/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/clang_linker_script.ld.src b/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/clang_linker_script.ld.src new file mode 100644 index 0000000..db07749 --- /dev/null +++ b/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/clang_linker_script.ld.src @@ -0,0 +1,374 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2019 Keith Packard + * + * 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 the copyright holder 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. + */ + +/* ---------------------------------------------------------------------------- + Stack seal size definition + *----------------------------------------------------------------------------*/ +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#define __STACKSEAL_SIZE ( 8 ) +#else +#define __STACKSEAL_SIZE ( 0 ) +#endif + +/* ---------------------------------------------------------------------------- + Memory definition + *----------------------------------------------------------------------------*/ +MEMORY +{ + ROM0 (rx!w) : ORIGIN = __ROM0_BASE, LENGTH = __ROM0_SIZE +#if __ROM1_SIZE > 0 + ROM1 (rx!w) : ORIGIN = __ROM1_BASE, LENGTH = __ROM1_SIZE +#endif +#if __ROM2_SIZE > 0 + ROM2 (rx!w) : ORIGIN = __ROM2_BASE, LENGTH = __ROM2_SIZE +#endif +#if __ROM3_SIZE > 0 + ROM3 (rx!w) : ORIGIN = __ROM3_BASE, LENGTH = __ROM3_SIZE +#endif + + RAM0 (w!rx) : ORIGIN = __RAM0_BASE, LENGTH = __RAM0_SIZE +#if __RAM1_SIZE > 0 + RAM1 (w!rx) : ORIGIN = __RAM1_BASE, LENGTH = __RAM1_SIZE +#endif +#if __RAM2_SIZE > 0 + RAM2 (w!rx) : ORIGIN = __RAM2_BASE, LENGTH = __RAM2_SIZE +#endif +#if __RAM3_SIZE > 0 + RAM3 (w!rx) : ORIGIN = __RAM3_BASE, LENGTH = __RAM3_SIZE +#endif +} + +ENTRY(Reset_Handler) + +PHDRS +{ + text PT_LOAD; + ram PT_LOAD; + ram_init PT_LOAD; + tls PT_TLS; +} + +SECTIONS +{ + .init : { + KEEP (*(.vectors)) + KEEP (*(.text.init.enter)) + KEEP (*(.data.init.enter)) + KEEP (*(SORT_BY_NAME(.init) SORT_BY_NAME(.init.*))) + } >ROM0 AT>ROM0 :text + + .text : { + + /* code */ + *(.text.unlikely .text.unlikely.*) + *(.text.startup .text.startup.*) + *(.text .text.* .opd .opd.*) + *(.gnu.linkonce.t.*) + KEEP (*(.fini .fini.*)) + __text_end = .; + + PROVIDE (__etext = __text_end); + PROVIDE (_etext = __text_end); + PROVIDE (etext = __text_end); + + /* read-only data */ + *(.rdata) + *(.rodata .rodata.*) + *(.gnu.linkonce.r.*) + + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata .srodata.*) + *(.data.rel.ro .data.rel.ro.*) + *(.got .got.*) + + /* Need to pre-align so that the symbols come after padding */ + . = ALIGN(8); + + /* lists of constructors and destructors */ + PROVIDE_HIDDEN ( __preinit_array_start = . ); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN ( __preinit_array_end = . ); + + PROVIDE_HIDDEN ( __init_array_start = . ); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array .ctors)) + PROVIDE_HIDDEN ( __init_array_end = . ); + + PROVIDE_HIDDEN ( __fini_array_start = . ); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array .dtors)) + PROVIDE_HIDDEN ( __fini_array_end = . ); + + } >ROM0 AT>ROM0 :text + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + .veneers : + { + . = ALIGN(32); + KEEP(*(.gnu.sgstubs)) + } > ROM0 AT>ROM0 :text +#endif + + .toc : { + *(.toc .toc.*) + } >ROM0 AT>ROM0 :text + + /* additional sections when compiling with C++ exception support */ + + .except_ordered : { + *(.gcc_except_table *.gcc_except_table.*) + KEEP (*(.eh_frame .eh_frame.*)) + *(.ARM.extab* .gnu.linkonce.armextab.*) + } >ROM0 AT>ROM0 :text + + .except_unordered : { + . = ALIGN(8); + + PROVIDE(__exidx_start = .); + *(.ARM.exidx*) + PROVIDE(__exidx_end = .); + } >ROM0 AT>ROM0 :text + + + /* + * Data values which are preserved across reset + */ + .preserve (NOLOAD) : { + PROVIDE(__preserve_start__ = .); + KEEP(*(SORT_BY_NAME(.preserve.*))) + KEEP(*(.preserve)) + PROVIDE(__preserve_end__ = .); + } >RAM0 AT>RAM0 :ram + + .data : { + *(.data .data.*) + *(.gnu.linkonce.d.*) + + /* Need to pre-align so that the symbols come after padding */ + . = ALIGN(8); + + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.sdata .sdata.* .sdata2.*) + *(.gnu.linkonce.s.*) + } >RAM0 AT>ROM0 :ram_init + PROVIDE(__data_start = ADDR(.data)); + PROVIDE(__data_source = LOADADDR(.data)); + + /* Thread local initialized data. This gets + * space allocated as it is expected to be placed + * in ram to be used as a template for TLS data blocks + * allocated at runtime. We're slightly abusing that + * by placing the data in flash where it will be copied + * into the allocate ram addresses by the existing + * data initialization code in crt0 + */ + .tdata : { + *(.tdata .tdata.* .gnu.linkonce.td.*) + PROVIDE(__data_end = .); + PROVIDE(__tdata_end = .); + } >RAM0 AT>ROM0 :tls :ram_init + PROVIDE( __tls_base = ADDR(.tdata)); + PROVIDE( __tdata_start = ADDR(.tdata)); + PROVIDE( __tdata_source = LOADADDR(.tdata) ); + PROVIDE( __tdata_source_end = LOADADDR(.tdata) + SIZEOF(.tdata) ); + PROVIDE( __data_source_end = __tdata_source_end ); + PROVIDE( __tdata_size = SIZEOF(.tdata) ); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata),ALIGNOF(.tbss)) ); + + PROVIDE( __edata = __data_end ); + PROVIDE( _edata = __data_end ); + PROVIDE( edata = __data_end ); + PROVIDE( __data_size = __data_end - __data_start ); + PROVIDE( __data_source_size = __data_source_end - __data_source ); + + .tbss (NOLOAD) : { + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + PROVIDE( __tls_end = . ); + PROVIDE( __tbss_end = . ); + } >RAM0 AT>RAM0 :tls :ram + PROVIDE( __bss_start = ADDR(.tbss)); + PROVIDE( __tbss_start = ADDR(.tbss)); + PROVIDE( __tbss_offset = ADDR(.tbss) - ADDR(.tdata) ); + PROVIDE( __tbss_size = SIZEOF(.tbss) ); + PROVIDE( __tls_size = __tls_end - __tls_base ); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + PROVIDE( __arm64_tls_tcb_offset = MAX(16, __tls_align) ); + + /* + * The linker special cases .tbss segments which are + * identified as segments which are not loaded and are + * thread_local. + * + * For these segments, the linker does not advance 'dot' + * across them. We actually need memory allocated for tbss, + * so we create a special segment here just to make room + */ + /* + .tbss_space (NOLOAD) : { + . = ADDR(.tbss); + . = . + SIZEOF(.tbss); + } >RAM0 AT>RAM0 :ram + */ + + .bss (NOLOAD) : { + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + + /* Align the end of this section, so next can start on aligned address */ + . = ALIGN(8); + __bss_end = .; + } >RAM0 AT>RAM0 :ram + PROVIDE( __non_tls_bss_start = ADDR(.bss) ); + PROVIDE( __bss_size = __bss_end - __bss_start ); + + /* This section contains data that is not initialized during load, + or during the application's initialization sequence. */ + .noinit (NOLOAD) : { + __noinit_start__ = .; + *(.noinit) + *(.noinit.*) + + /* Align the end of this section, so next (heap) can start on aligned address */ + . = ALIGN(8); + __noinit_end__ = .; + } >RAM0 AT>RAM0 :ram + + PROVIDE( __end = . ); + PROVIDE( _end = . ); + PROVIDE( end = . ); + + /* Make the rest of memory available for heap storage */ + PROVIDE (__heap_start = __end); +#ifdef __HEAP_SIZE + PROVIDE (__heap_end = __heap_start + __HEAP_SIZE); + PROVIDE (__heap_size = __HEAP_SIZE); +#else + PROVIDE (__heap_end = __stack - __STACK_SIZE); + PROVIDE (__heap_size = __heap_end - __heap_start); +#endif + .heap (NOLOAD) : { + . += __heap_size; + } >RAM0 :ram + + /* Define a stack region to make sure it fits in memory */ + PROVIDE(__stack = ORIGIN(RAM0) + LENGTH(RAM0) - __STACKSEAL_SIZE); + PROVIDE(__stack_limit = __stack - __STACK_SIZE); + .stack (__stack_limit) (NOLOAD) : { + . += __STACK_SIZE; + } >RAM0 :ram + +#if __STACKSEAL_SIZE > 0 + PROVIDE(__stack_seal = __stack); + .stackseal (__stack) (NOLOAD) : + { + . += __STACKSEAL_SIZE; + } >RAM0 :ram +#endif + + /* Throw away C++ exception handling information */ + + /* + + /DISCARD/ : { + *(.note .note.*) + *(.eh_frame .eh_frame.*) + *(.ARM.extab* .gnu.linkonce.armextab.*) + *(.ARM.exidx*) + } + + */ + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions. */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2. */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2. */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions. */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3. */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF 5. */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + .debug_sup 0 : { *(.debug_sup) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } +} +/* + * Check that sections that are copied from flash to RAM have matching + * padding, so that a single memcpy() of __data_size copies the correct bytes. + */ +ASSERT( __data_size == __data_source_size, + "ERROR: .data/.tdata flash size does not match RAM size"); diff --git a/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/gcc_linker_script.ld.src b/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/gcc_linker_script.ld.src new file mode 100644 index 0000000..7cd986d --- /dev/null +++ b/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/gcc_linker_script.ld.src @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2023 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. + */ + +/* ---------------------------------------------------------------------------- + Stack seal size definition + *----------------------------------------------------------------------------*/ +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#define __STACKSEAL_SIZE ( 8 ) +#else +#define __STACKSEAL_SIZE ( 0 ) +#endif + +/* ---------------------------------------------------------------------------- + Memory definition + *----------------------------------------------------------------------------*/ +MEMORY +{ + ROM0 (rx) : ORIGIN = __ROM0_BASE, LENGTH = __ROM0_SIZE +#if __ROM1_SIZE > 0 + ROM1 (rx) : ORIGIN = __ROM1_BASE, LENGTH = __ROM1_SIZE +#endif +#if __ROM2_SIZE > 0 + ROM2 (rx) : ORIGIN = __ROM2_BASE, LENGTH = __ROM2_SIZE +#endif +#if __ROM3_SIZE > 0 + ROM3 (rx) : ORIGIN = __ROM3_BASE, LENGTH = __ROM3_SIZE +#endif + + RAM0 (rwx) : ORIGIN = __RAM0_BASE, LENGTH = __RAM0_SIZE +#if __RAM1_SIZE > 0 + RAM1 (rwx) : ORIGIN = __RAM1_BASE, LENGTH = __RAM1_SIZE +#endif +#if __RAM2_SIZE > 0 + RAM2 (rwx) : ORIGIN = __RAM2_BASE, LENGTH = __RAM2_SIZE +#endif +#if __RAM3_SIZE > 0 + RAM3 (rwx) : ORIGIN = __RAM3_BASE, LENGTH = __RAM3_SIZE +#endif +} + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext (deprecated) + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __noinit_start + * __noinit_end + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > ROM0 + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + .gnu.sgstubs : + { + . = ALIGN(32); + } > ROM0 +#endif + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > ROM0 + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > ROM0 + __exidx_end = .; + + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + + LONG (LOADADDR(.data)) + LONG (ADDR(.data)) + LONG (SIZEOF(.data) / 4) + + /* Add each additional data section here */ +/* + LONG (LOADADDR(.data2)) + LONG (ADDR(.data2)) + LONG (SIZEOF(.data2) / 4) +*/ + __copy_table_end__ = .; + } > ROM0 + + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + +/* .bss initialization to zero is already done during C Run-Time Startup. + LONG (ADDR(.bss)) + LONG (SIZEOF(.bss) / 4) +*/ + + /* Add each additional bss section here */ +/* + LONG (ADDR(.bss2)) + LONG (SIZEOF(.bss2) / 4) +*/ + __zero_table_end__ = .; + } > ROM0 + + /* + * This __etext variable is kept for backward compatibility with older, + * ASM based startup files. + */ + PROVIDE(__etext = LOADADDR(.data)); + + .data : ALIGN(4) + { + __data_start__ = .; + *(vtable) + *(.data) + *(.data.*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM0 AT > ROM0 + + /* + * Secondary data section, optional + * + * Remember to add each additional data section + * to the .copy.table above to assure proper + * initialization during startup. + */ +/* + .data2 : ALIGN(4) + { + . = ALIGN(4); + __data2_start__ = .; + *(.data2) + *(.data2.*) + . = ALIGN(4); + __data2_end__ = .; + + } > RAM1 AT > ROM0 +*/ + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss) + *(.bss.*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM0 AT > RAM0 + + /* + * Secondary bss section, optional + * + * Remember to add each additional bss section + * to the .zero.table above to assure proper + * initialization during startup. + */ +/* + .bss2 : + { + . = ALIGN(4); + __bss2_start__ = .; + *(.bss2) + *(.bss2.*) + . = ALIGN(4); + __bss2_end__ = .; + } > RAM1 AT > RAM1 +*/ + + /* This section contains data that is not initialized during load, + or during the application's initialization sequence. */ + .noinit (NOLOAD) : + { + . = ALIGN(4); + __noinit_start = .; + *(.noinit) + *(.noinit.*) + . = ALIGN(4); + __noinit_end = .; + } > RAM0 + + .heap (NOLOAD) : + { + . = ALIGN(8); + __end__ = .; + PROVIDE(end = .); + . = . + __HEAP_SIZE; + . = ALIGN(8); + __HeapLimit = .; + } > RAM0 + + .stack (ORIGIN(RAM0) + LENGTH(RAM0) - __STACK_SIZE - __STACKSEAL_SIZE) (NOLOAD) : + { + . = ALIGN(8); + __StackLimit = .; + . = . + __STACK_SIZE; + . = ALIGN(8); + __StackTop = .; + } > RAM0 + PROVIDE(__stack = __StackTop); + +#if __STACKSEAL_SIZE > 0 + .stackseal (ORIGIN(RAM0) + LENGTH(RAM0) - __STACKSEAL_SIZE) (NOLOAD) : + { + . = ALIGN(8); + __StackSeal = .; + . = . + 8; + . = ALIGN(8); + } > RAM0 +#endif + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/iar_linker_script.icf.src b/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/iar_linker_script.icf.src new file mode 100644 index 0000000..5689ea8 --- /dev/null +++ b/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/iar_linker_script.icf.src @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 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. + */ + +define memory mem with size = 4G; + +#if __ROM0_SIZE > 0 + define region ROM0_region = mem:[from __ROM0_BASE to (__ROM0_BASE+__ROM0_SIZE-1)]; +#else + define region ROM0_region = []; +#endif + +#if __ROM1_SIZE > 0 + define region ROM1_region = mem:[from __ROM1_BASE to (__ROM0_BASE+__ROM1_SIZE-1)]; +#else + define region ROM1_region = []; +#endif + +#if __ROM2_SIZE > 0 + define region ROM2_region = mem:[from __ROM2_BASE to (__ROM2_BASE+__ROM2_SIZE-1)]; +#else + define region ROM2_region = []; +#endif + +#if __ROM3_SIZE > 0 + define region ROM3_region = mem:[from __ROM3_BASE to (__ROM3_BASE+__ROM3_SIZE-1)]; +#else + define region ROM3_region = []; +#endif + +define region ROM_region = ROM0_region | ROM1_region | ROM2_region | ROM3_region; + +#if __RAM0_SIZE > 0 + define region RAM0_region = mem:[from __RAM0_BASE to (__RAM0_BASE+__RAM0_SIZE-1)]; +#else + define region RAM0_region = []; +#endif + +#if __RAM1_SIZE > 0 + define region RAM1_region = mem:[from __RAM1_BASE to (__RAM0_BASE+__RAM1_SIZE-1)]; +#else + define region RAM1_region = []; +#endif + +#if __RAM2_SIZE > 0 + define region RAM2_region = mem:[from __RAM2_BASE to (__RAM2_BASE+__RAM2_SIZE-1)]; +#else + define region RAM2_region = []; +#endif + +#if __RAM3_SIZE > 0 + define region RAM3_region = mem:[from __RAM3_BASE to (__RAM3_BASE+__RAM3_SIZE-1)]; +#else + define region RAM3_region = []; +#endif + +define region RAM_region = RAM0_region | RAM1_region | RAM2_region | RAM3_region; + +do not initialize { section .noinit }; +initialize by copy { readwrite }; +if (isdefinedsymbol(__USE_DLIB_PERTHREAD)) +{ + // Required in a multi-threaded application + initialize by copy with packing = none { section __DLIB_PERTHREAD }; +} + +place at address mem:__ROM0_BASE { readonly section .intvec }; + +if (!isempty(ROM_region)) +{ + place in ROM_region { readonly }; +} + +if (!isempty(RAM_region)) +{ + define block CSTACK with alignment = 8, size = __STACK_SIZE { }; + define block PROC_STACK with alignment = 8, size = 0 { }; + define block HEAP with alignment = 8, size = __HEAP_SIZE { }; + place in RAM_region { readwrite, block CSTACK, block PROC_STACK, block HEAP }; +} diff --git a/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/regions_ARMCM0.h b/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/regions_ARMCM0.h new file mode 100644 index 0000000..c40d7aa --- /dev/null +++ b/test/data/linker-pre-processing/project/RTE/Device/ARMCM0/regions_ARMCM0.h @@ -0,0 +1,60 @@ +#ifndef REGIONS_ARMCM0_H +#define REGIONS_ARMCM0_H + + +//-------- <<< Use Configuration Wizard in Context Menu >>> -------------------- + +// Device pack: ARM::Cortex_DFP@1.0.0 +// Device pack used to generate this file + +// ROM Configuration +// ======================= +// ROM=<__ROM0> +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +// Default: 0x00000000 +#define __ROM0_BASE 0x00000000 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +// Default: 0x00040000 +#define __ROM0_SIZE 0x00040000 +// Default region +// Enables memory region globally for the application. +#define __ROM0_DEFAULT 1 +// Startup +// Selects region to be used for startup code. +#define __ROM0_STARTUP 1 +// + +// + +// RAM Configuration +// ======================= +// RAM=<__RAM0> +// Base address <0x0-0xFFFFFFFF:8> +// Defines base address of memory region. +// Default: 0x20000000 +#define __RAM0_BASE 0x20000000 +// Region size [bytes] <0x0-0xFFFFFFFF:8> +// Defines size of memory region. +// Default: 0x00020000 +#define __RAM0_SIZE 0x00020000 +// Default region +// Enables memory region globally for the application. +#define __RAM0_DEFAULT 1 +// No zero initialize +// Excludes region from zero initialization. +#define __RAM0_NOINIT 0 +// + +// + +// Stack / Heap Configuration +// Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> +// Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> +#define __STACK_SIZE 0x00000200 +#define __HEAP_SIZE 0x00000C00 +// + + +#endif /* REGIONS_ARMCM0_H */ diff --git a/test/data/linker-pre-processing/project/main.c b/test/data/linker-pre-processing/project/main.c new file mode 100644 index 0000000..2e169c3 --- /dev/null +++ b/test/data/linker-pre-processing/project/main.c @@ -0,0 +1,6 @@ +#include "RTE_Components.h" +#include CMSIS_device_header + +int main(void) { + return 0; +} diff --git a/test/data/linker-pre-processing/project/project.cproject.yml b/test/data/linker-pre-processing/project/project.cproject.yml new file mode 100644 index 0000000..fa74a6b --- /dev/null +++ b/test/data/linker-pre-processing/project/project.cproject.yml @@ -0,0 +1,14 @@ +project: + + components: + - component: ARM::CMSIS:CORE + - component: ARM::Device:Startup&C Startup + + groups: + - group: Source + files: + - file: ./main.c + + linker: + - define: + - DEF_LD_PP diff --git a/test/data/linker-pre-processing/solution.csolution.yml b/test/data/linker-pre-processing/solution.csolution.yml new file mode 100644 index 0000000..b2a7aa5 --- /dev/null +++ b/test/data/linker-pre-processing/solution.csolution.yml @@ -0,0 +1,35 @@ +solution: + created-for: CMSIS-Toolbox@2.2.1 + cdefault: + + packs: + - pack: ARM::Cortex_DFP + - pack: ARM::CMSIS + + target-types: + - type: ARMCM0 + device: ARMCM0 + + build-types: + - type: AC6 + compiler: AC6 + + - type: GCC + compiler: GCC + + # - type: IAR + # compiler: IAR + + - type: CLANG + compiler: CLANG + + projects: + - project: ./project/project.cproject.yml + + misc: + - for-compiler: AC6 + Link: + - --info summarysizes + - --info sizes + - --info totals + - --info unused diff --git a/test/data/pre-include/pack/ARM.PreIncludeTestPack.pdsc b/test/data/pre-include/pack/ARM.PreIncludeTestPack.pdsc new file mode 100644 index 0000000..3876757 --- /dev/null +++ b/test/data/pre-include/pack/ARM.PreIncludeTestPack.pdsc @@ -0,0 +1,56 @@ + + + + ARM + https://www.arm.com/ + PreIncludeTestPack + PreIncludeTestPack + + + + Initial release of PreIncludeTestPack. + + + + + PreIncludeTestPack + + + + + + Test Device + + + + + + + TestGlobal + + // enabling global pre include + #define GLOBAL_TEST 0x1 + + + + + + + + + TestLocal + + // enabling local pre include + #define LOCAL_TEST 0x2 + + + + + + + + + + + + diff --git a/test/data/pre-include/pack/Files/config-header1.h b/test/data/pre-include/pack/Files/config-header1.h new file mode 100644 index 0000000..2873dd0 --- /dev/null +++ b/test/data/pre-include/pack/Files/config-header1.h @@ -0,0 +1 @@ +#define PRE_INCLUDE_GLOBAL_CONFIG 1 diff --git a/test/data/pre-include/pack/Files/config-header2.h b/test/data/pre-include/pack/Files/config-header2.h new file mode 100644 index 0000000..cab0646 --- /dev/null +++ b/test/data/pre-include/pack/Files/config-header2.h @@ -0,0 +1 @@ +#define PRE_INCLUDE_LOCAL_CONFIG 1 diff --git a/test/data/pre-include/pack/Files/header1.h b/test/data/pre-include/pack/Files/header1.h new file mode 100644 index 0000000..8c01106 --- /dev/null +++ b/test/data/pre-include/pack/Files/header1.h @@ -0,0 +1 @@ +#define PRE_INCLUDE_GLOBAL 1 diff --git a/test/data/pre-include/pack/Files/header2.h b/test/data/pre-include/pack/Files/header2.h new file mode 100644 index 0000000..10e7050 --- /dev/null +++ b/test/data/pre-include/pack/Files/header2.h @@ -0,0 +1 @@ +#define PRE_INCLUDE_LOCAL 1 diff --git a/test/data/pre-include/pack/Files/test1.c b/test/data/pre-include/pack/Files/test1.c new file mode 100644 index 0000000..8694e8c --- /dev/null +++ b/test/data/pre-include/pack/Files/test1.c @@ -0,0 +1,13 @@ +#ifndef PRE_INCLUDE_GLOBAL +#error "PRE_INCLUDE_GLOBAL is not defined" +#endif + +#ifndef PRE_INCLUDE_GLOBAL_CONFIG +#error "PRE_INCLUDE_GLOBAL_CONFIG is not defined" +#endif + +#ifndef GLOBAL_TEST +#error "GLOBAL_TEST is not defined" +#endif + +int PreIncludeGlobal; diff --git a/test/data/pre-include/pack/Files/test2.c b/test/data/pre-include/pack/Files/test2.c new file mode 100644 index 0000000..be0f782 --- /dev/null +++ b/test/data/pre-include/pack/Files/test2.c @@ -0,0 +1,13 @@ +#ifndef PRE_INCLUDE_LOCAL +#error "PRE_INCLUDE_LOCAL is not defined" +#endif + +#ifndef PRE_INCLUDE_LOCAL_CONFIG +#error "PRE_INCLUDE_LOCAL_CONFIG is not defined" +#endif + +#ifndef LOCAL_TEST +#error "LOCAL_TEST is not defined" +#endif + +int PreIncludeLocal; diff --git a/test/data/pre-include/project/main.c b/test/data/pre-include/project/main.c new file mode 100644 index 0000000..2e169c3 --- /dev/null +++ b/test/data/pre-include/project/main.c @@ -0,0 +1,6 @@ +#include "RTE_Components.h" +#include CMSIS_device_header + +int main(void) { + return 0; +} diff --git a/test/data/pre-include/project/project.cproject.yml b/test/data/pre-include/project/project.cproject.yml new file mode 100644 index 0000000..2824b15 --- /dev/null +++ b/test/data/pre-include/project/project.cproject.yml @@ -0,0 +1,12 @@ +project: + + components: + - component: ARM::CMSIS:CORE + - component: ARM::Device:Startup&C Startup + - component: ARM::TestClass:TestGlobal + - component: ARM::TestClass:TestLocal + + groups: + - group: Source + files: + - file: ./main.c diff --git a/test/data/pre-include/solution.csolution.yml b/test/data/pre-include/solution.csolution.yml new file mode 100644 index 0000000..f078e69 --- /dev/null +++ b/test/data/pre-include/solution.csolution.yml @@ -0,0 +1,29 @@ +solution: + created-for: CMSIS-Toolbox@2.2.1 + cdefault: + + packs: + - pack: ARM::Cortex_DFP + - pack: ARM::CMSIS + - pack: ARM::PreIncludeTestPack + path: ./pack + + target-types: + - type: ARMCM0 + device: ARMCM0 + + build-types: + - type: AC6 + compiler: AC6 + + - type: GCC + compiler: GCC + + # - type: IAR + # compiler: IAR + + - type: CLANG + compiler: CLANG + + projects: + - project: ./project/project.cproject.yml diff --git a/test/data/trustzone/nonsecurecode/main_ns.c b/test/data/trustzone/nonsecurecode/main_ns.c new file mode 100644 index 0000000..e349db2 --- /dev/null +++ b/test/data/trustzone/nonsecurecode/main_ns.c @@ -0,0 +1,74 @@ +#include "interface.h" // Interface API +#include "cmsis_os2.h" // ARM::CMSIS:RTOS2:Keil RTX5 + +static osStatus_t Status; + +static osThreadId_t ThreadAdd_Id; +static osThreadId_t ThreadMul_Id; + +static const osThreadAttr_t ThreadAttr = { + .tz_module = 1U, +}; + +void AddThread (void *argument); +void MulThread (void *argument); + +extern volatile int valueA; +extern volatile int valueB; + +volatile int valueA; +volatile int valueB; + + +static int Addition (int val1, int val2) { + return (val1 + val2); +} + +__attribute__((noreturn)) +void AddThread (void *argument) { + (void)argument; + + for (;;) { + valueA = function_1 (valueA); + valueA = function_2 (Addition, valueA, 2); + osDelay(2U); + } +} + +static int Multiply (int val1, int val2) { + uint32_t flags; + + flags = osThreadFlagsWait (1U, osFlagsWaitAny, osWaitForever); + if (flags == 1U) { + return (val1*val2); + } + else { + return (0); + } +} + + +__attribute__((noreturn)) +void MulThread (void *argument) { + (void)argument; + + for (;;) { + valueB = function_1 (valueB); + valueB = function_2 (Multiply, valueB, 2); + } +} + + +int main (void) { + Status = osKernelInitialize(); + + ThreadAdd_Id = osThreadNew(AddThread, NULL, &ThreadAttr); + ThreadMul_Id = osThreadNew(MulThread, NULL, &ThreadAttr); + + Status = osKernelStart(); + + for(;;); +} + + + diff --git a/test/data/trustzone/nonsecurecode/nonsecure.cproject.yml b/test/data/trustzone/nonsecurecode/nonsecure.cproject.yml new file mode 100644 index 0000000..73329dd --- /dev/null +++ b/test/data/trustzone/nonsecurecode/nonsecure.cproject.yml @@ -0,0 +1,21 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + misc: + - for-compiler: AC6 + C: [-fshort-enums, -fshort-wchar] + + add-path: + - $OutDir(secure)$ + + components: + - component: ARM::Device:Startup&C Startup + - component: ARM::CMSIS:CORE + - component: ARM::CMSIS:RTOS2:Keil RTX5&Library_NS + + groups: + - group: Non-secure Code + files: + - file: main_ns.c + - file: $ProjectDir(secure)$/interface.h + - file: $cmse-lib(secure)$ \ No newline at end of file diff --git a/test/data/trustzone/securecode/interface.c b/test/data/trustzone/securecode/interface.c new file mode 100644 index 0000000..1336adc --- /dev/null +++ b/test/data/trustzone/securecode/interface.c @@ -0,0 +1,24 @@ +#include + +#include "interface.h" + +/* typedef for non-secure callback functions */ +typedef funcptr funcptr_NS __attribute__((cmse_nonsecure_call)); + +/* Non-secure callable (entry) function */ +int function_1(int x) __attribute__((cmse_nonsecure_entry)) { + return x+5; +} + +/* Non-secure callable (entry) function, calling a non-secure callback function */ +int function_2(funcptr callback, int x, int y) __attribute__((cmse_nonsecure_entry)) { + funcptr_NS callback_NS; // non-secure callback function pointer + int res; + + /* return function pointer with cleared LSB */ + callback_NS = (funcptr_NS)cmse_nsfptr_create(callback); + res = callback_NS (x, y); + + return (res*10); +} + diff --git a/test/data/trustzone/securecode/interface.h b/test/data/trustzone/securecode/interface.h new file mode 100644 index 0000000..71b7f6c --- /dev/null +++ b/test/data/trustzone/securecode/interface.h @@ -0,0 +1,6 @@ +// function pointer +typedef int(*funcptr)(int,int); + +/* Non-secure callable functions */ +extern int function_1(int x); +extern int function_2(funcptr callback, int x, int y); diff --git a/test/data/trustzone/securecode/main_s.c b/test/data/trustzone/securecode/main_s.c new file mode 100644 index 0000000..0f1afbc --- /dev/null +++ b/test/data/trustzone/securecode/main_s.c @@ -0,0 +1,24 @@ +/* Use CMSE intrinsics */ +#include + +#include "RTE_Components.h" +#include CMSIS_device_header + +#ifndef START_NS +#define START_NS (0x200000U) +#endif + +typedef void (*funcptr) (void) __attribute__((cmse_nonsecure_call)); + +int main(void) { + funcptr ResetHandler; + + __TZ_set_MSP_NS(*((uint32_t *)(START_NS))); + ResetHandler = (funcptr)(*((uint32_t *)((START_NS) + 4U))); + ResetHandler(); + + while (1) { + __NOP(); + } +} + diff --git a/test/data/trustzone/securecode/secure.cproject.yml b/test/data/trustzone/securecode/secure.cproject.yml new file mode 100644 index 0000000..9c89540 --- /dev/null +++ b/test/data/trustzone/securecode/secure.cproject.yml @@ -0,0 +1,23 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + packs: + - pack: ARM::CMSIS@5.9.0 + + processor: + trustzone: secure + + components: + - component: ARM::Device:Startup&C Startup + - component: ARM::CMSIS:CORE + + groups: + - group: Secure Code + files: + - file: main_s.c + + - group: CMSE + files: + - file: interface.c + - file: interface.h + - file: tz_context.c diff --git a/test/data/trustzone/securecode/tz_context.c b/test/data/trustzone/securecode/tz_context.c new file mode 100644 index 0000000..2bcb8d8 --- /dev/null +++ b/test/data/trustzone/securecode/tz_context.c @@ -0,0 +1,34 @@ +#include "RTE_Components.h" +#include CMSIS_device_header +#include "tz_context.h" + +__attribute__((cmse_nonsecure_entry)) +uint32_t TZ_InitContextSystem_S(void) { + return 1U; +} + +__attribute__((cmse_nonsecure_entry)) +TZ_MemoryId_t TZ_AllocModuleContext_S(TZ_ModuleId_t module) { + (void)module; + return 1U; +} + +__attribute__((cmse_nonsecure_entry)) +uint32_t TZ_FreeModuleContext_S(TZ_MemoryId_t id) { + (void)id; + return 1U; +} + + +__attribute__((cmse_nonsecure_entry)) +uint32_t TZ_LoadContext_S(TZ_MemoryId_t id) { + (void)id; + return 2U; +} + +__attribute__((cmse_nonsecure_entry)) +uint32_t TZ_StoreContext_S(TZ_MemoryId_t id) { + (void)id; + return 2U; +} + diff --git a/test/data/trustzone/solution.csolution.yml b/test/data/trustzone/solution.csolution.yml new file mode 100644 index 0000000..fe9d64e --- /dev/null +++ b/test/data/trustzone/solution.csolution.yml @@ -0,0 +1,28 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + target-types: + - type: CM33 + device: ARMCM33_DSP_FP_TZ + + - type: CM35P + device: ARMCM35P_DSP_FP_TZ + + build-types: + - type: Debug + compiler: AC6 + misc: + - for-compiler: AC6 + C: + - -O1 + - -g + - type: Release + compiler: AC6 + misc: + - for-compiler: AC6 + C: + - -O3 + + projects: + - project: ./securecode/secure.cproject.yml + - project: ./nonsecurecode/nonsecure.cproject.yml diff --git a/test/data/whitespace/project/ma in.c b/test/data/whitespace/project/ma in.c new file mode 100644 index 0000000..2e169c3 --- /dev/null +++ b/test/data/whitespace/project/ma in.c @@ -0,0 +1,6 @@ +#include "RTE_Components.h" +#include CMSIS_device_header + +int main(void) { + return 0; +} diff --git a/test/data/whitespace/project/project.cproject.yml b/test/data/whitespace/project/project.cproject.yml new file mode 100644 index 0000000..3f24303 --- /dev/null +++ b/test/data/whitespace/project/project.cproject.yml @@ -0,0 +1,14 @@ +project: + + components: + - component: ARM::CMSIS:CORE + - component: ARM::Device:Startup&C Startup + + groups: + - group: Source + files: + - file: ./ma in.c + - group: Header + files: + - file: ./tes t/tes t.h + diff --git a/test/data/whitespace/project/tes t/tes t.h b/test/data/whitespace/project/tes t/tes t.h new file mode 100644 index 0000000..ac96203 --- /dev/null +++ b/test/data/whitespace/project/tes t/tes t.h @@ -0,0 +1 @@ +#define TES_T 1 diff --git a/test/data/whitespace/solution.csolution.yml b/test/data/whitespace/solution.csolution.yml new file mode 100644 index 0000000..0392558 --- /dev/null +++ b/test/data/whitespace/solution.csolution.yml @@ -0,0 +1,31 @@ +solution: + created-for: CMSIS-Toolbox@2.2.1 + cdefault: + + packs: + - pack: ARM::Cortex_DFP + - pack: ARM::CMSIS + + target-types: + - type: ARMCM0 + device: ARMCM0 + + build-types: + - type: AC6 + compiler: AC6 + + - type: GCC + compiler: GCC + misc: + - Library: + - -lm + - -lc + + # - type: IAR + # compiler: IAR + + - type: CLANG + compiler: CLANG + + projects: + - project: ./project/project.cproject.yml diff --git a/test/lib/elf_compare.py b/test/lib/elf_compare.py new file mode 100644 index 0000000..67c77b8 --- /dev/null +++ b/test/lib/elf_compare.py @@ -0,0 +1,99 @@ +import os +import subprocess +from robot.api import logger +from robot.utils import asserts + +def compare_elf_information(input_file, cbuildgen_out_dir, cbuild2cmake_out_dir): + logger.debug('Input file: %s' % input_file) + logger.debug('Cbuildgen out dir: %s' % cbuildgen_out_dir) + logger.debug('Cbuild2cmake out dir: %s' % cbuild2cmake_out_dir) + return CompareELF(input_file, cbuildgen_out_dir, cbuild2cmake_out_dir).compare_elf_files() + +class Context: + def __init__(self, strContext: str): + self.project = "" + self.build = "" + self.target = "" + self.parse_context(strContext) + + def parse_context(self, strContext: str): + parts = strContext.split('.') + if len(parts) == 2: + self.project = parts[0] + temp_parts = parts[1].split('+') + if len(temp_parts) == 2: + self.build = temp_parts[0] + self.target = temp_parts[1] + else: + self.target = parts[1] + else: + print("Invalid context format. Expected format: 'project.build+target'") + +class Utils: + @staticmethod + def run_command(exe_path, args): + try: + command = exe_path + ' ' + ' '.join(args) + logger.info(f"Running Command: {' '.join(command)}") + processOut = subprocess.run(command, check=True, shell=True, universal_newlines=True, capture_output=True, timeout=300) + return True, processOut.stdout + except subprocess.CalledProcessError as e: + logger.error(f"Error executing command: {e}") + return False, e.stderr + except subprocess.TimeoutExpired as e: + logger.error(f"Command execution timed out: {e}") + return False, "Command execution timed out" + except Exception as e: + logger.error(f"An unexpected error occurred: {e}") + return False, "An unexpected error occurred" + +class CompareELF: + def __init__(self, input_file, cbuildgen_out_dir, cbuild2cmake_out_dir): + self.input_file = input_file + self.cbuildgen_out_dir = cbuildgen_out_dir + self.cbuild2cmake_out_dir = cbuild2cmake_out_dir + + def compare_elf_files(self): + context_list = self.get_contexts() + if len(context_list) == 0: + return False + + success = True + for context in context_list: + logger.info('Processing context: %s.%s+%s' % (context.project, context.build, context.target)) + path = self.get_elf_file_path(self.cbuildgen_out_dir, context) + logger.info('Path: %s' % path) + if path != '': + cbuildgen_elf_file = os.path.join(self.cbuildgen_out_dir, path) + cbuild2cmake_elf_file = os.path.join(self.cbuild2cmake_out_dir, path) + res1, stdout1 = self.get_elf_info(cbuildgen_elf_file) + res2, stdout2 = self.get_elf_info(cbuild2cmake_elf_file) + logger.info('Object Info %s' % stdout1) + logger.info('Object Info %s' % stdout2) + success &= (res1 == res2) + return success + + def get_elf_file_path(self, basePath, context: Context): + file_extns = [".axf", ".elf"] + logger.info('basePath: %s' % basePath) + for extn in file_extns: + filePath = os.path.join(basePath, context.project, context.target, context.build, context.project+extn) + if os.path.exists(filePath): + elf_file_path = '/'.join([context.project, context.target, context.build, context.project+extn]) + return elf_file_path + return '' + + def get_elf_info(self, elf_file): + args = [elf_file, "-z"] + return Utils.run_command("fromelf", args) + + def get_contexts(self): + args = ["list", "contexts", self.input_file] + success, output = Utils.run_command("cbuild", args) + contextList = [] + logger.debug('cbuild list contexts %s' % output) + if success == True: + strContext = output.splitlines() + for context in strContext: + contextList.append(Context(context)) + return contextList diff --git a/test/lib/execution_summary.py b/test/lib/execution_summary.py new file mode 100644 index 0000000..c57aaec --- /dev/null +++ b/test/lib/execution_summary.py @@ -0,0 +1,76 @@ +# This script is designed to process the output.xml file generated by Robot +# Framework, extract the test statuses, and create a markdown file based +# on the parsed information. +# +# execution_summary.py [output file summary.md] + +import os +import re +import shutil +import subprocess +import sys +from robot.api import ExecutionResult, ResultVisitor +from robot.result.model import TestCase +from robot.result.executionresult import Result + +class ResultVisitorEx(ResultVisitor): + def __init__(self, markdown_file='summary_report.md'): + self.failed_tests = {} + self.passed_tests = {} + self.skipped_tests = {} + self.markdown_file = markdown_file + + # Remove existing markdown file if it exists + if os.path.exists(markdown_file): + os.remove(markdown_file) + + def visit_test(self, test: TestCase): + tags = ", ".join(test.tags) + duration = "{:.2f} s".format(test.elapsed_time.total_seconds()) + status = (test.name, test.message, duration, test.parent.name) + test_status = self.failed_tests if test.status == 'FAIL' else ( + self.passed_tests if test.status == 'PASS' else self.skipped_tests) + if tags not in test_status: + test_status[tags] = [] + test_status[tags].append(status) + + def end_result(self, result: Result): + with open(self.markdown_file, "w") as f: + f.write("# Robot Framework Report\n\n") + f.write("## Summary\n\n") + f.write("|:white_check_mark: Passed|:x: Failed|:fast_forward: Skipped|Total|\n") + f.write("|:----:|:----:|:-----:|:---:|\n") + f.write(f"|{result.statistics.total.passed}|{result.statistics.total.failed}|{result.statistics.total.skipped}|{result.statistics.total.total}|\n") + self.__write_test_section(f, self.passed_tests, "Passed Tests", "|Tag|Test|:clock1030: Duration|Suite|\n") + self.__write_test_section(f, self.failed_tests, "Failed Tests", "|Tag|Test|Message|:clock1030: Duration|Suite|\n") + self.__write_test_section(f, self.skipped_tests, "Skipped Tests", "|Tag|Test|Suite|\n") + + def __write_test_section(self, file, test_dict, section_header, table_header): + if len(test_dict) != 0: + file.write(f"\n## {section_header}\n\n") + file.write(table_header) + tokens = table_header.split('|') + table_sep = "|:-" + num_req_sep = len(tokens) - 2 + for i in range(num_req_sep): + table_sep += "--:|" + if i != (num_req_sep - 1): + table_sep += ":-" + file.write(table_sep + "\n") + for key, value in test_dict.items(): + for name, msg, duration, suite in value: + if section_header.startswith("Pass"): + file.write(f"|{key}|{name}|{duration}|{suite}|\n") + elif section_header.startswith("Fail"): + file.write(f"|{key}|{name}|{msg}|{duration}|{suite}|\n") + elif section_header.startswith("Skip"): + file.write(f"|{key}|{name}|{suite}|\n") + +if __name__ == '__main__': + # Get output and markdown file paths from command line arguments + output_file = sys.argv[1] if len(sys.argv) > 1 else "output.xml" + markdown_file = sys.argv[2] if len(sys.argv) > 2 else "summary_report.md" + + # Parse the Robot Framework output file and generate the summary report + result = ExecutionResult(output_file) + result.visit(ResultVisitorEx(markdown_file)) diff --git a/test/lib/utils.py b/test/lib/utils.py new file mode 100644 index 0000000..1a449d1 --- /dev/null +++ b/test/lib/utils.py @@ -0,0 +1,13 @@ +import glob +from pathlib import Path + +def glob_files_in_directory(directory:str, pattern: str, recursive: bool): + yaml_files = glob.glob(directory + '/**/' + pattern, recursive=recursive) + return yaml_files + +def get_parent_directory_name(file_path:str): + parent_dir = Path(file_path).parent + return parent_dir.name + +def get_parent_directory_path(file_path:str): + return Path(file_path).parent.absolute() diff --git a/test/local_example_tests.robot b/test/local_example_tests.robot new file mode 100644 index 0000000..3a048a9 --- /dev/null +++ b/test/local_example_tests.robot @@ -0,0 +1,67 @@ +*** Settings *** +Documentation Tests to compile & execute local csolution examples +Suite Setup Global Setup +Suite Teardown Global Teardown +Resource resources${/}global.resource +Resource resources${/}utils.resource +Library String +Library Collections +Library lib${/}elf_compare.py +Library lib${/}utils.py +Test Template Run CSolution Project + +*** Variables *** +# The directory name of the example to be built +${build-asm} build-asm +${build-c} build-c +${build-cpp} build-cpp +${include-define} include-define +${language-scope} language-scope +${linker-pre-processing} linker-pre-processing +${pre-include} pre-include +${whitespace} whitespace +${trustzone} trustzone +${Hello} Hello + +*** Test Cases *** +# +# + +Validate build-asm Example + ${TEST_DATA_DIR}${/}${build-asm}${/}solution.csolution.yml ${Pass} + +Validate build-c Example + ${TEST_DATA_DIR}${/}${build-c}${/}solution.csolution.yml ${Pass} + +Validate build-cpp Example + ${TEST_DATA_DIR}${/}${build-cpp}${/}solution.csolution.yml ${Pass} + +Validate include-define Example + ${TEST_DATA_DIR}${/}${include-define}${/}solution.csolution.yml ${Pass} + +Validate language-scope Example + ${TEST_DATA_DIR}${/}${language-scope}${/}solution.csolution.yml ${Pass} + +Validate linker-pre-processing Example + ${TEST_DATA_DIR}${/}${linker-pre-processing}${/}solution.csolution.yml ${Pass} + +Validate pre-include Example + ${TEST_DATA_DIR}${/}${pre-include}${/}solution.csolution.yml ${Pass} + +Validate whitespace Example + ${TEST_DATA_DIR}${/}${whitespace}${/}solution.csolution.yml ${Pass} + +Validate trustzone Example + ${TEST_DATA_DIR}${/}${trustzone}${/}solution.csolution.yml ${Pass} + +*** Keywords *** +Run Csolution Project + [Arguments] ${input_file} ${expect} ${args}=@{EMPTY} + Run Project With cbuildgen ${input_file} ${expect} ${args} + Run Project with cbuild2cmake ${input_file} ${expect} ${args} + ${parent_path}= Get Parent Directory Path ${input_file} + ${result}= Run Keyword And Return + ... Compare Elf Information ${input_file} + ... ${parent_path}${/}${Out_Dir}${/}${Default_Out_Dir} + ... ${parent_path}${/}${Default_Out_Dir} + Should Be True ${result} diff --git a/test/remote_example_tests.robot b/test/remote_example_tests.robot new file mode 100644 index 0000000..20093fa --- /dev/null +++ b/test/remote_example_tests.robot @@ -0,0 +1,52 @@ +*** Settings *** +Documentation Tests to compile & execute remote csolution examples +Suite Setup Global Setup +Suite Teardown Global Teardown +Library lib${/}utils.py +Library String +Library Collections +Library lib${/}elf_compare.py +Resource resources${/}global.resource +Resource resources${/}utils.resource +Test Template Test github examples + + +*** Variables *** +${csolution-examples} https://github.com/Open-CMSIS-Pack/csolution-examples +${Hello_B-U585I-IOT02A} https://github.com/Arm-Examples/Hello_B-U585I-IOT02A +${Hello_FRDM-K32L3A6} https://github.com/Arm-Examples/Hello_FRDM-K32L3A6 + + +*** Test Cases *** +# Test Csolution example +# ${csolution-examples} ${Pass} + +# Test Hello_1 example +# ${Hello_B-U585I-IOT02A} ${Pass} + +# Test Hello_2 example +# ${Hello_FRDM-K32L3A6} ${Pass} + +*** Keywords *** +Test github examples + [Arguments] ${github_url} ${expect} + ${dest_dir}= Evaluate "${github_url}".split('/')[-1] + ${dest_dir}= Set Variable ${TEST_DATA_DIR}${/}${Remote_Example_Dir}${/}${dest_dir} + Checkout GitHub Repository ${github_url} ${dest_dir} + ${files} Glob Files In Directory ${dest_dir} *.csolution.* ${True} + FOR ${file} IN @{files} + ${example_name}= Get Parent Directory Name ${file} + Run Keyword If '${example_name}' != 'CubeMX' + ... Run Csolution Project ${file} ${expect} + END + +Run Csolution Project + [Arguments] ${input_file} ${expect} ${args}=@{EMPTY} + Run Project With cbuildgen ${input_file} ${expect} ${args} + Run Project with cbuild2cmake ${input_file} ${expect} ${args} + ${parent_path}= Get Parent Directory Path ${input_file} + ${result}= Run Keyword And Return + ... Compare Elf Information ${input_file} + ... ${parent_path}${/}${Out_Dir}${/}${Default_Out_Dir} + ... ${parent_path}${/}${Default_Out_Dir} + Should Be True ${result} diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 0000000..e7f6d4a --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1,2 @@ +# Requirements needed +robotframework >= 7.0 diff --git a/test/resources/global.resource b/test/resources/global.resource new file mode 100644 index 0000000..ada8a58 --- /dev/null +++ b/test/resources/global.resource @@ -0,0 +1,9 @@ +*** Variables *** +${RESOURCES} ${CURDIR} # Path to the resource directory +${Build} build # Directory name where example build files are copied +${Data} data # Directory name of test data source +${Pass} ${0} # Exit status pass +${Fail} ${1} # Exit status fail +${Out_Dir} out_dir # +${Default_Out_Dir} out # Default name of the output directory +${Remote_Example_Dir} remote_examples # Directory name for remote examples \ No newline at end of file diff --git a/test/resources/utils.resource b/test/resources/utils.resource new file mode 100644 index 0000000..8a89daa --- /dev/null +++ b/test/resources/utils.resource @@ -0,0 +1,83 @@ +*** Settings *** +Documentation A collection of commonly used keywords across multiple test suites +Library Collections +Library Process +Library OperatingSystem +Library ..${/}lib${/}utils.py +Resource global.resource + + +*** Keywords *** +Global Setup + ${parent_dir}= Join Path ${CURDIR} .. + ${src_dir}= Join Path ${parent_dir} ${Data} + ${dest_dir}= Get Test Data Directory + Set Global Variable ${TEST_DATA_DIR} ${dest_dir} + Copy Directory ${src_dir} ${dest_dir} + +Global Teardown + Remove Directory with Content ${TEST_DATA_DIR} + +Get Test Data Directory + [Documentation] Retrieve the directory path for test input data + ${parent_dir}= Join Path ${CURDIR} .. + ${test_data_dir}= Join Path ${parent_dir} ${Build} + RETURN ${test_data_dir} + +Run Program + [Documentation] Run specified executable with arguments + [Arguments] ${exe_path} ${input_File} @{args} + ${result} Run Process ${exe_path} ${input_File} @{args} + ... shell=True stdout=${CURDIR}/stdout.txt + ${ret_code}= Set Variable If ${result.rc} == ${0} ${result.rc} ${1} + Log Many StdOut: ${result.stdout} + RETURN ${ret_code} + +Run cbuild + [Documentation] Run cbuild with specified arguments + [Arguments] ${input_file} ${args}=@{EMPTY} + Append To List ${args} -p -r --update-rte + ${ret_code}= Run Program cbuild ${input_file} @{args} + RETURN ${ret_code} + +Change Directory Permissions + [Documentation] Change directory permissions + [Arguments] ${target_dir} + ${result} Run Process chmod -R 755 ${target_dir} + Should Be Equal ${result.rc} ${0} + +Remove Directory with Content + [Documentation] Remove directory and its content from target directory + [Arguments] ${target_dir} + Change Directory Permissions ${target_dir} + Remove Directory ${target_dir} recursive=${True} + Wait Until Removed ${target_dir} timeout=5 seconds + +Checkout GitHub Repository + [Documentation] Checkout github repository + [Arguments] ${github_repo_url} ${dest_path} + ${result}= Run Process git clone ${github_repo_url} ${dest_path} + Log ${result.stdout} + Log ${result.stderr} + Should Be Equal As Integers ${result.rc} ${0} + +Run Project with cbuildgen + [Documentation] Run the csolution project with cbuildgen + [Arguments] ${input_file} ${expect} ${args}=@{EMPTY} + ${parent_path}= Get Parent Directory Path ${input_file} + ${ex_args}= Append Additional Arguments ${args} --output ${parent_path}${/}${Out_Dir} + ${ret_code}= Run cbuild ${input_file} ${ex_args} + Should Be Equal ${ret_code} ${expect} msg=Unexpected status returned by cbuildgen execution + +Run Project With cbuild2cmake + [Documentation] Run the csolution project with cbuild2cmake + [Arguments] ${input_file} ${expect} ${args}=@{EMPTY} + ${ex_args}= Append Additional Arguments ${args} --cbuild2cmake + ${ret_code}= Run cbuild ${input_file} ${ex_args} + Should Be Equal ${ret_code} ${expect} msg=Unexpected status returned by cbuild2cmake execution + +Append Additional Arguments + [Documentation] Append values to the list + [Arguments] ${list} @{values} + ${args}= Combine Lists ${list} ${values} + RETURN ${args} diff --git a/test/vcpkg-configuration.json b/test/vcpkg-configuration.json new file mode 100644 index 0000000..72b29dd --- /dev/null +++ b/test/vcpkg-configuration.json @@ -0,0 +1,22 @@ +{ + "registries": [ + { + "kind": "artifact", + "location": "https://aka.ms/vcpkg-ce-default", + "name": "microsoft" + }, + { + "kind": "artifact", + "location": "https://artifacts.tools.arm.com/vcpkg-ce-registry/registry.zip", + "name": "arm" + } + ], + "requires": { + "microsoft:cmake": "^3.25.2", + "microsoft:ninja": "^1.10.2", + "arm:compilers/arm/armclang":"^6.21.0", + "arm:compilers/arm/arm-none-eabi-gcc": "^13.2.1", + "arm:tools/open-cmsis-pack/cmsis-toolbox": "^2.3.0", + "arm:compilers/arm/llvm-embedded": "^17.0.1" + } +}