diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..33edc166e5c1b61fc295b1545071a0c5bff56e36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +.venv +zephyr/ + +build/ +.vscode + +.west +bootloader/ +tools/edtt/ +tools/net-tools/ + +modules/bsim_hw_models/ +modules/crypto/ +modules/debug/ +modules/fs/ +modules/lib/ +modules/tee/ +modules/hal/* +!modules/hal/phytium/ +!modules/hal/phytium/hal +!modules/hal/phytium/zephyr +modules/hal/phytium/hal/phytium-standalone-sdk diff --git a/.west/config b/.west/config new file mode 100644 index 0000000000000000000000000000000000000000..2e61a771c4dce5d4ae8f4f80a00652f468494874 --- /dev/null +++ b/.west/config @@ -0,0 +1,6 @@ +[manifest] +path = zephyr +file = west.yml + +[zephyr] +base = zephyr \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..8f71f43fee3f78649d238238cbde51e6d7055c82 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + diff --git a/README.en.md b/README.en.md deleted file mode 100644 index ca35faf0fd5479fb62f0cee778b539b72dd59143..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# Phytium-Zephyr-SDK - -#### Description -此项目是一个开源软件,专为物联网领域设计,基于Zephyr实时操作系统,针对Phytium系列CPU进行了专门的适配和应用开发。 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 1b04b1f37391c9e1d7ab2171409bffcd8424e659..96a9e50374653823a27501ff7d491482d8742fd7 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,271 @@ # Phytium-Zephyr-SDK -#### 介绍 -此项目是一个开源软件,专为物联网领域设计,基于Zephyr实时操作系统,针对Phytium系列CPU进行了专门的适配和应用开发。 +**v1.0.0** [ReleaseNote](./doc/ChangeLog.md) -#### 软件架构 -软件架构说明 +## 1. 仓库介绍 +## 1.1 本仓库特色 -#### 安装教程 +此项目是一个开源软件,专为物联网领域设计,基于Zephyr实时操作系统,针对Phytium系列CPU进行了专门的适配和应用开发。我们的目标是降低开发者的使用门槛,提供了部署文档和使用指南,旨在帮助开发者轻松在Zephyr平台上扩展Phytium系列CPU的功能。本项目适合所有希望在Zephyr实时操作系统上充分利用Phytium系列CPU能力的开发者。 -1. xxxx -2. xxxx -3. xxxx +## 1.2 Zephyr 简介 -#### 使用说明 +[Zephyr Project Documentation — Zephyr Project Documentation](https://docs.zephyrproject.org/latest/index.html) -1. xxxx -2. xxxx -3. xxxx +Zephyr是一个开源的实时操作系统(RTOS),主要面向在资源受限的嵌入式设备上运行。它由Linux Foundation托管,目的是提供一个轻量级、模块化的系统,以支持物联网(IoT)和边缘计算应用。Zephyr项目自2016年开始,迅速发展成为一个活跃的开源社区,汇集了来自全球的开发者和公司贡献代码和知识。下面是根据您提到的几个方面对Zephyr系统的介绍: -#### 参与贡献 +### 内核特点 -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +1. **轻量级和高度模块化**:Zephyr设计为可配置且可扩展,允许开发者根据需要选择包含在最终固件中的组件,这有助于减少资源消耗。 +2. **实时性**:作为RTOS,Zephyr提供实时任务调度,确保关键任务能够及时准确地执行。 +3. **多平台支持**:支持多种不同架构的CPU芯片,包括ARM、Intel x86、RISC-V等,涵盖从低功耗微控制器到更复杂的系统级芯片(SoC)。 +4. **安全性**:提供了包括数据加密和安全启动在内的多种安全特性,以保护设备免受攻击。 +### 应用开发 -#### 特技 +1. **灵活的开发环境**:Zephyr可以通过其SDK和集成的开发环境(IDE)插件进行开发,支持多种编程语言,包括C和C++。 +2. **设备驱动和中间件支持**:提供丰富的设备驱动程序和中间件,支持网络、蓝牙、文件系统等多种通信协议和服务。 +3. **微内核和单片机(MCU)内核选项**:开发者可以根据应用需求,选择适合的内核配置,以最佳方式利用硬件资源。 -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +### 生态特色 + +1. **强大的社区支持**:作为一个活跃的开源项目,Zephyr拥有广泛的社区支持,提供了大量的文档、教程和案例,帮助开发者快速上手。 +2. **与主流技术兼容**:Zephyr可以与多种主流的开源技术栈整合,如MCUboot引导加载器、Mbed TLS安全库等,确保开发者能够构建安全、可靠的应用。 +3. **跨行业应用**:适用于多种应用场景,包括但不限于智能家居、可穿戴设备、工业控制和汽车电子。 + +## 1.3 适配框架 + +本开源项目由3个仓库(图1)组成,分别是Phytium-Zephyr-SDK、Zephyr OS、Standalone-For-Zephyr。以下是关于这三个开源仓库的介绍。 + +Phytium-Zephyr-SDK:此仓库作为整个开源项目的入口,提供了项目说明、部署手册、版本管理、测试例程等开发者在调试过程中会使用到的必要内容。开发者如果在部署开发过程中如果了问题,可以在此仓库下的issue下提交给我们。 + +Zephyr OS: 此仓库作为Zephyr 官方的镜像站使用,其中会包含Phytium 系列CPU 的设备树以及相应的驱动代码和配置文件。 + +Standalone-For-Zephyr:此仓库作为Zephyr项目依赖模块(modules)中的硬件抽象层,是由Phytium提供的Phytium-Standalone-SDK与驱动适配层构成,此仓库为Zephyr整个构建过程提供驱动部分的软件底座。 + +![1712020444273](figs/readme/1712020444273.png) + + 图1 项目整体架构图 + +## 2. 仓库使用说明 + +- 本例程目前仅应用于Ubuntu开发环境 +- 本模块主要分为3大内容:1. 开发环境搭建;2. 软件部署;3. 例程使用; + +### 2.1 开发环境搭建 + +1. 升级ubuntu环境,建议使用Ubuntu22.04版本 + +``` +sudo apt update +sudo apt upgrade +``` + +2. 如果使用的是早于22.04版本的Ubuntu,为了满足上述主要依赖项的最低版本要求,需要添加额外的软件仓库。在这种情况下,下载、检查并执行Kitware存档脚本,以将Kitware APT仓库添加到你的软件源列表中。关于kitware-archive.sh的详细解释可以在这里找到:kitware第三方apt仓库。 + +``` +sudo bash ./tools/kitware-archive.sh +``` + +3. 通过apt 安装必备软件 + +``` +sudo apt install --no-install-recommends git cmake ninja-build gperf \ + ccache dfu-util device-tree-compiler wget \ + python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \ + make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1 +``` + +4. 检查必备的软件否成功 + +- 请确保当前安装版本符合要求,备注为最低版本要求 +``` +cmake --version (minimum required is "3.20") +python3 --version (minimum required is "3.8") +dtc --version (minimum required is "1.4.6") +``` + +5. 安装python3虚拟化环境 + +``` +sudo apt install python3-venv +``` + +6. 安装west 软件 + +``` +pip install west +``` + +7. 安装Zephyr SDK软件 + +- 可在[Zephyr SDK bundle](https://github.com/zephyrproject-rtos/sdk-ng/releases/tag/v0.16.5-1)中手动下载并放置在 ~/ 文件夹下,然后从第三步开始执行 + +![zephy_sdk_install](figs/readme/zephy_sdk_install.png) + +``` +cd ~ +wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/zephyr-sdk-0.16.5-1_linux-x86_64.tar.xz +wget -O - https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/sha256.sum | shasum --check --ignore-missing +tar xvf zephyr-sdk-0.16.5-1_linux-x86_64.tar.xz +cd zephyr-sdk-0.16.5-1 +./setup.sh +``` + +### 2.2 软件部署 + +1. 拉取本仓库代码到一个合适的目录,一般来说,代码目录名称为:phytium-zephyr-sdk + +2. 进入sdk目录,创建一个Python虚拟化环境 + +``` +cd phytium-zephyr-sdk +python3 -m venv ./.venv +``` + +3. 通过source 激活您的虚拟环境 + +``` +source ./.venv/bin/activate +``` + +4. 在当前目录下执行以下命令初始化Zephyr OS,后续Zephyr OS 将会保存在zephyr目录下 + +``` +./tools/pull_zephyr_os.sh +``` + +5. 更新Zephyr OS中的其他项目,这个过程可能会耗时较长,请耐心等待 +>如果出现某些项目更新中断或失败,可以尝试再次使用命令进行更新 + +``` +west update +``` + +6. 安装必备软件至python虚拟环境中,这个过程可能会耗时较长,请耐心等待 +>如果出现某些软件安装中断或失败,可以尝试再次使用命令进行安装 + +``` +pip install -r zephyr/scripts/requirements.txt +``` + +## 3. 如何部署一个软件 + +详细的实现方法请参考 [Application Development — Zephyr Project Documentation](https://docs.zephyrproject.org/latest/develop/application/index.html) + +- 部署软件最好的方法是从 Phytium-Zephyr-SDK 例子或者Zephyr 官方的例子出发进行部署与构建 。 + +Zephyr官方的例程路径为:zephyr/sample +Phytium-Zephyr-SDK 提供的例程路径为 : app/ + +## 3.1 应用程序概述 + +Zephyr 的构建系统基于 CMake。 + +该构建系统以应用程序为中心,要求基于 Zephyr 的应用程序启动构建 Zephyr 源代码。应用程序构建控制了应用程序本身和 Zephyr 的配置和构建过程,将它们编译成一个单独的二进制文件。 + +主要的 zephyr 仓库包含了 Zephyr 的源代码、配置文件和构建系统。您可能还安装了各种模块(外部项目)与 zephyr 仓库一起,这些模块提供了第三方源代码集成。 + +应用程序目录中的文件将 Zephyr 和任何模块与应用程序连接起来。该目录包含所有特定于应用程序的文件,如特定于应用程序的配置文件和源代码。 + +以下是一个简单的 Zephyr 应用程序中的文件: + +``` + + +├── CMakeLists.txt +├── app.overlay +├── prj.conf +├── VERSION +└── src + └── main.c +``` + +这些内容包括: + +CMakeLists.txt:这个文件告诉构建系统在哪里找到其他应用程序文件,并将应用程序目录与 Zephyr 的 CMake 构建系统链接起来。这种链接提供了 Zephyr 构建系统支持的功能,如特定于板子的配置文件,运行和调试在真实或模拟硬件上编译的二进制文件的能力等等。 + +app.overlay:这是一个设备树覆盖文件,指定应用于任何构建板的基础设备树的应用程序特定更改。设备树覆盖的目的通常是配置应用程序使用的硬件相关内容。 + +构建系统默认会查找 app.overlay,但您可以添加更多设备树覆盖,还会搜索其他默认文件。 + +prj.conf:这是一个 Kconfig 片段,用于指定一个或多个 Kconfig 选项的应用程序特定值。这些应用程序设置与其他设置合并,以产生最终配置。Kconfig 片段的目的通常是配置应用程序使用的软件功能。 + +构建系统默认会查找 prj.conf,但您可以添加更多 Kconfig 片段,还会搜索其他默认文件。 + +有关 Kconfig 配置的更多信息,请参阅下文的 Kconfig 配置部分。 + +VERSION:一个包含几个版本信息字段的文本文件。这些字段让您管理应用程序的生命周期,并在签名应用程序镜像时自动提供应用程序版本。 + +有关此文件及其使用方式的更多信息,请参阅应用程序版本管理。 + +main.c:一个源代码文件。应用程序通常包含用 C、C++ 或汇编语言编写的源文件。Zephyr 的惯例是将它们放在 `` 的一个名为 src 的子目录中。 + +一旦定义了应用程序,您将使用 CMake 生成一个构建目录,其中包含构建应用程序和 Zephyr 所需的文件,然后将它们链接成一个最终二进制文件,您可以在板子上运行。这样做的最简单方法是使用 west build,但您也可以直接使用 CMake。应用程序构建工件始终生成在一个单独的构建目录中:Zephyr 不支持“树内”构建。 + +## 3.2 构建与重建应用程序 + +Zephyr的构建系统将应用程序的所有组件编译和链接成一个单独的应用程序镜像,可以在模拟硬件或实际硬件上运行。 + +与任何其他基于CMake的系统一样,构建过程分为两个阶段。首先,使用cmake命令行工具生成构建文件(也称为构建系统),同时指定一个生成器。该生成器确定构建系统将在第二阶段使用的本地构建工具。第二阶段运行本地构建工具,实际构建源文件并生成一个镜像。要了解更多关于这些概念的信息,请参考官方CMake文档中的CMake简介。 + +尽管Zephyr的默认构建工具是west,Zephyr的元工具,它在后台调用cmake和底层构建工具(ninja或make),但您也可以选择直接调用cmake,如果您更喜欢的话。在Linux和macOS上,您可以在make和ninja生成器(即构建工具)之间进行选择,而在Windows上,您需要使用ninja,因为该平台不支持make。为简单起见,本指南将在整个过程中使用ninja,并且如果您选择使用west build构建应用程序,则需要知道它在幕后默认使用ninja。 + +- 在构建过程中,我们常用的构建方法为: + +``` +west build -b -DOVERLAY_CONFIG= +``` + +上面west 命令中 ,board_name 代表开发板的型号,project_path 代表需要编译例程的相对路径 ,prj.conf为Kconfig 片段 。 + +一个具体示例为: 在app/system/kernel_feature/schedule 路径下,使用 “west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf” 命令,将会使用e2000q_demo 开发板,编译app/system/kernel_feature/schedule 下的源码例程,并且采用当前路径下的prj.conf 配置。 + +- 我们常用的清除指令为: + +``` +west build -t clean +``` + +clean 指令 ,主要为了清除 `/build`下的编译缓存信息,以这样的方式实现构建系统的重建 。 + +## 4. 硬件支持情况 + +| 硬件平台 | 支持的架构 | 备注 | +|-----------------|------------|----------------| +| E2000D/Q DEMO板 | aarch64 | 支持SMP模式 | +| E2000D/Q DEMO板 | aarch32 | | + +## 5. 例程展示 + +### 5.1 内核测试 + +[schedule](./app/system/kernel_feature/schedule/README.md) +[interrupt](./app/system/interrupt/README.md) +[smp_test](./app/system/smp/smp_test/README.md) + +### 5.2 peripheral测试 + +[echo](./app/peripheral/net/echo/README.md) +[telnet](./app/peripheral/net/telnet/README.md) +[tf_test](./app/peripheral/sdio/tf_test/README.md) + +## 例程介绍 + +# FIQ + +Q:在phytium-zephyr-sdk目录中使用```west update```命令时,出现某个仓库下载超时的情况,应该如何处理? + +A:zephyr依赖的大部分modules仓库部署在github上,请确保您的网络能够正常github + +Q:如果出现以下打印应该如何解决: + + ![1712058336378](figs/readme/1712058336378.png) + +A:在拉取Zephyr OS的时候,出现了错误,需要先将当前目录下的zephyr目录删除,然后再重复初始化Zephyr OS + +Q:当执行```pip install -r /zephyr/scripts/requirements.txt```命令时出现以下超时问题时,该怎么办: + + ![1712107861173](figs/readme/1712107861173.png) + +A:可以在```pip install -r /zephyr/scripts/requirements.txt```后面追加```-i https://pypi.tuna.tsinghua.edu.cn/simple```,将pip源设置为国内源 \ No newline at end of file diff --git a/app/peripheral/iopad/CMakeLists.txt b/app/peripheral/iopad/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8acee3a23eb9578183a1ca0fdfebee8286e12a12 --- /dev/null +++ b/app/peripheral/iopad/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +set(EXTRA_ZEPHYR_MODULES "$ENV{ZEPHYR_BASE}/../modules/hal/phytium") +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(schedule_test) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/app/peripheral/iopad/makefile b/app/peripheral/iopad/makefile new file mode 100644 index 0000000000000000000000000000000000000000..6ad7264c81864cd5babbedc69408d9a5a71e31c9 --- /dev/null +++ b/app/peripheral/iopad/makefile @@ -0,0 +1,16 @@ +boot: + west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + +boot_aarch32: + west build -b e2000q_a32_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + +clean: + west build -t clean + rm -rf build + +gdb: + gdb-multiarch -x .gdbinit \ No newline at end of file diff --git a/app/peripheral/iopad/prj.conf b/app/peripheral/iopad/prj.conf new file mode 100644 index 0000000000000000000000000000000000000000..474c72e7e1bab0c8c6c1bff03e54ed153f1ddb5d --- /dev/null +++ b/app/peripheral/iopad/prj.conf @@ -0,0 +1,24 @@ +# General config +CONFIG_MAIN_STACK_SIZE=8192 + +# schedule +CONFIG_TIMESLICE_PER_THREAD=y + +CONFIG_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=1 +CONFIG_LOG_MODE_IMMEDIATE=y + +# SHELL 开关 +CONFIG_SHELL=y +CONFIG_SHELL_LOG_LEVEL_INF=y +CONFIG_SHELL_STACK_SIZE=8192 + +# pinctrl +CONFIG_PINCTRL=y +CONFIG_PINCTRL_PHYTIUM=y +# CONFIG_HAL_STANDALONE_SDK_DEBUG=y + +# CONFIG_NET_MGMT_EVENT_STACK_SIZE=4096 +# CONFIG_KERNEL_VM_BASE=0x10000000 +# 将 0 ~ 0x80000000 的地址空间作为虚拟内存使用 2G +# CONFIG_KERNEL_VM_SIZE=0x80000000 diff --git a/app/peripheral/iopad/src/main.c b/app/peripheral/iopad/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..3fd45b0ec8fdff6fd0cdba69a384c2a73a9f91a8 --- /dev/null +++ b/app/peripheral/iopad/src/main.c @@ -0,0 +1,39 @@ + + +#include +#include +#include +#include +#include + +#define UART1 DT_NODELABEL(pinctrl) + +#define UART1_NODE DT_NODELABEL(uart1) + +PINCTRL_DT_DEFINE(UART1_NODE); + +void test_pinctrl_dt_define(void) +{ + // 获取配置结构体 + const struct pinctrl_dev_config *config = &Z_PINCTRL_DEV_CONFIG_NAME(UART1_NODE); + // 打印配置结构体的信息 + printf("Pinctrl Dev Config for UART1:\n"); + printf("Number of states: %d\n", config->state_cnt); + for (int i = 0; i < config->state_cnt; i++) { + printf("State %d:\n", i); + printf(" Pins: %d\n", config->states[i].pin_cnt); + for (int j = 0; j < config->states[i].pin_cnt; j++) { + printf(" Pin %d: reg_offset=0x%x, pin_func=%d\n", + j, config->states[i].pins[j].pin_reg_offset, config->states[i].pins[j].func_num); + } + } + pinctrl_apply_state(&(*config), PINCTRL_STATE_DEFAULT); +} + + +int main(void) +{ + test_pinctrl_dt_define(); + return 0 ; +} + diff --git a/app/peripheral/net/echo/.gdbinit b/app/peripheral/net/echo/.gdbinit new file mode 100644 index 0000000000000000000000000000000000000000..62e67faf3ca5ebe82d73723d9a78e2ab40b13012 --- /dev/null +++ b/app/peripheral/net/echo/.gdbinit @@ -0,0 +1,61 @@ +# increase the default remote timeout in gdb because +# Windows libusb transactions could be slow +set remotetimeout 100000 + +# gdb connect to openocd in port 3333 +target extended-remote localhost:3333 + +# start openocd working queue, monitor followed with openocd command here +monitor init + +# force to use hardware breakpoint, otherwise use software breakpoint +# for e2000d/q, num of hardware breakpoints supposed to be 6 +monitor gdb_breakpoint_override hardware + +# load elf image +load ./build/zephyr/zephyr.elf + +# in case symbols skip load,load agin +file ./build/zephyr/zephyr.elf + +# we can break at the beginning of code by address or by symbol +#break _boot + +# add more breakpoints in application +#break JtagTouchRegisters +#break JtagTouchMemory +# break bubbleSort` +# break JtagPostSort +#break bubbleSortCXX + +# show all breakspoints we before running +# info breakpoints + +# show [-0x10 ~ +0x10 ] range of instructions when breaked +# display /10i $pc-16 + +# start running +# continue +layout src +# break eth_phytium_xmac_iface_init +# break eth_phytium_xmac_isr +# break init_app +# break bg_thread_main +break virt_region_init +break eth_phytium_xmac_dev_init +# break eth_phytium_xmac_setuphandler +# break eth_phytium_xmac_send +# break eth_phytium_xmac_send +# break eth_phytium_xmac_send +# break net_config_init_by_iface +# break check_interface +# break z_arm64_sync_exc +# break sdmmc_read_scr +# break sdmmc_write_blocks +# break z_arm64_prep_c +# break sdmmc_switch +# break sdmmc_read_csd +# break sdmmc_read_blocks +# break sd_idle +# break sd_send_interface_condition +# continue \ No newline at end of file diff --git a/app/peripheral/net/echo/CMakeLists.txt b/app/peripheral/net/echo/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..da5489a76804be5e0685235fe87ba5a6d2111541 --- /dev/null +++ b/app/peripheral/net/echo/CMakeLists.txt @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +set(EXTRA_ZEPHYR_MODULES "$ENV{ZEPHYR_BASE}/../modules/hal/phytium") +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(sockets_echo) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +include(${ZEPHYR_BASE}/samples/net/common/common.cmake) diff --git a/app/peripheral/net/echo/README.md b/app/peripheral/net/echo/README.md new file mode 100644 index 0000000000000000000000000000000000000000..321a61fb7c278dd41fdd8fa998c7197699900168 --- /dev/null +++ b/app/peripheral/net/echo/README.md @@ -0,0 +1,78 @@ +# net/echo卡测试 + +## 1. 例程介绍 + +函数 `main` 实现了一个简单的单线程TCP echo服务器。服务器监听指定的端口(这里是4242端口),接收来自客户端的连接,然后回显(echo)客户端发送的所有数据。这个过程包含以下关键步骤: + +1. **创建套接字(socket)**:通过调用 `socket()` 函数创建一个TCP套接字,用于监听来自客户端的连接请求。 +2. **绑定地址和端口**:将套接字与本地地址和指定的端口号绑定。这里使用的是 `INADDR_ANY`,表示服务器将接受指向所有本地接口的连接。 +3. **监听端口**:通过调用 `listen()` 函数,使得服务器套接字进入监听状态,等待来自客户端的连接请求。 +4. **等待并接受连接**:服务器进入一个无限循环,使用 `accept()` 函数等待并接受来自客户端的连接请求。一旦接受到一个连接,就为该连接创建一个新的套接字。 +5. **处理客户端数据**:服务器通过新创建的套接字接收来自客户端的数据。接收到的数据将被回显给客户端,即服务器读取客户端发送的每一条消息,然后将相同的消息发送回客户端。 +6. **关闭客户端连接**:当客户端断开连接或发送数据出现错误时,服务器将关闭与该客户端的连接,并准备接受下一个连接请求。 + +特别注意的是,此代码既可以在普通的POSIX兼容操作系统上运行(如Linux),也可以在Zephyr RTOS上运行,这得益于条件编译指令(`#ifndef __ZEPHYR__`)用于区分不同的环境所需的头文件和API调用。 + +## 2. 如何使用例程 + +本例程需要以下硬件, + +- E2000D/Q Demo板 + +### 2.1 硬件配置方法 + +保障串口正常工作后,需要确认网络是否连接好 + +### 2.2 SDK配置方法 + +- 请根据自己的网络环境,修改prj.conf文件中的CONFIG_NET_CONFIG_MY_IPV4_ADDR 条目为您当前网络环境中分配给开发板的IP. +- 本例子已经提供好具体的编译指令,以下进行介绍: + +1. ``west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf``,编译命令, 使用west工具构建当前目录下的Zephyr项目,指定目标板为e2000q_demo,并使用prj.conf配置文件覆盖默认配置 ,最终生成的执行文件将会保存在./build/zephyr/zephyr.elf +2. ``west build -t clean``, 清除缓存 ,使用west工具的clean目标清理Zephyr构建系统可能生成的任何其他临时文件或缓存 + +### 2.3 构建和下载 + +- 编译例程 + +``west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf`` + +- 编译主机测侧设置重启tftp服务器 + +``` +sudo service tftpd-hpa restart +``` + +- 开发板侧使用bootelf命令跳转 + +``` +setenv ipaddr 192.168.4.20 +setenv serverip 192.168.4.50 +setenv gatewayip 192.168.4.1 +tftpboot 0x90100000 zephyr.elf +bootelf -p 0x90100000 +``` + +### 2.4 输出与实验现象 + +- 所有用例均提供一系列可变配置,可在例程全局变量中修改 + +#### 2.4.1 echo 例程使用 + +1. 将zephyr.elf 在开发板上加载起来之后会打印以下内容: + +![1713412848289](figs/README/1713412848289.png) + +2. 在PC主机上输入以下命令,则可以连接开发板echo服务器: + +``` +telnet 192.168.4.7 4242 +``` + +3. 当PC主机连接成功之后,开发板上会打印以下内容: + +![1713417340810](figs/README/1713417340810.png) + +## 3. 如何解决问题 + +## 4. 修改历史记录 diff --git a/app/peripheral/net/echo/README.rst b/app/peripheral/net/echo/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..005036ca5382e0a87ae771b7bfb7b9a3db74db1e --- /dev/null +++ b/app/peripheral/net/echo/README.rst @@ -0,0 +1,84 @@ +.. zephyr:code-sample:: sockets-echo + :name: Echo server (simple) + :relevant-api: bsd_sockets + + Implements a simple IPv4 TCP echo server using BSD sockets. + +Overview +******** + +The sockets/echo sample application for Zephyr implements an IPv4 TCP echo +server using a BSD Sockets compatible API. The purpose of this sample is to +show how it's possible to develop a sockets application portable to both +POSIX and Zephyr. As such, it is kept minimal and supports only IPv4 and TCP. + +The source code for this sample application can be found at: +:zephyr_file:`samples/net/sockets/echo`. + +Requirements +************ + +- :ref:`networking_with_host` +- or, a board with hardware networking + +Building and Running +******************** + +Build the Zephyr version of the sockets/echo application like this: + +.. zephyr-app-commands:: + :zephyr-app: samples/net/sockets/echo + :board: + :goals: build + :compact: + +After the sample starts, it expects connections at 192.0.2.1, port 4242. +The easiest way to connect is: + +.. code-block:: console + + $ telnet 192.0.2.1 4242 + +After a connection is made, the application will echo back any line sent +to it. The application implements a single-threaded server using blocking +sockets, and thus can serve only one client connection at time. After the +current client disconnects, the next connection can proceed. + +Running application on POSIX Host +================================= + +The same application source code can be built for a POSIX system, e.g. +Linux. (Note: if you look at the source, you will see that the code is +the same except the header files are different for Zephyr vs POSIX.) + +To build for a host POSIX OS: + +.. code-block:: console + + $ make -f Makefile.posix + +To run: + +.. code-block:: console + + $ ./socket_echo + +To test: + +.. code-block:: console + + $ telnet 127.0.0.1 4242 + +As can be seen, the behavior of the application is the same as the Zephyr +version. + +Running on cc3220sf_launchxl +============================ + +See the note on Provisioning and Fast Connect in :ref:`cc3220sf_launchxl`. + +After having connected to an Access Point using the sample Wi-Fi shell, +the IP address will be printed to the console upon running this echo +application. + +Proceed to test as above. diff --git a/app/peripheral/net/echo/figs/README/1713412848289.png b/app/peripheral/net/echo/figs/README/1713412848289.png new file mode 100644 index 0000000000000000000000000000000000000000..7c13be920a0f0c9746c4ccc5194ca1cdd1ea747f Binary files /dev/null and b/app/peripheral/net/echo/figs/README/1713412848289.png differ diff --git a/app/peripheral/net/echo/figs/README/1713417340810.png b/app/peripheral/net/echo/figs/README/1713417340810.png new file mode 100644 index 0000000000000000000000000000000000000000..8f4c0f3850b2a418a470dd91dd69cbae96a189e2 Binary files /dev/null and b/app/peripheral/net/echo/figs/README/1713417340810.png differ diff --git a/app/peripheral/net/echo/makefile b/app/peripheral/net/echo/makefile new file mode 100644 index 0000000000000000000000000000000000000000..1ba5faeaf883ca8a46cafab7227419fc402b4336 --- /dev/null +++ b/app/peripheral/net/echo/makefile @@ -0,0 +1,21 @@ + + +boot: + west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + +boot_aarch32: + + west build -b e2000q_a32_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + +clean: + west build -t clean + rm -rf build + +gdb: + gdb-multiarch -x .gdbinit + + diff --git a/app/peripheral/net/echo/prj.conf b/app/peripheral/net/echo/prj.conf new file mode 100644 index 0000000000000000000000000000000000000000..237b93d84b99d7581acbabc7862ac120d5ec83fe --- /dev/null +++ b/app/peripheral/net/echo/prj.conf @@ -0,0 +1,53 @@ +# General config +# CONFIG_NEWLIB_LIBC=y +CONFIG_MAIN_STACK_SIZE=4096 + +# Networking config +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=n +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POSIX_NAMES=y + +# Network driver config +CONFIG_TEST_RANDOM_GENERATOR=y + +# Network address config +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_CONFIG_NEED_IPV4=y +CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.168.4.7" + +CONFIG_NET_L2_ETHERNET=y + +CONFIG_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=1 +# CONFIG_LOG_MODE_IMMEDIATE=y +# CONFIG_HAL_STANDALONE_SDK_DEBUG=y + +CONFIG_NET_IF_LOG_LEVEL_DBG=y +CONFIG_NET_L2_ETHERNET_LOG_LEVEL_DBG=y + + +# Core IP options +CONFIG_NETWORKING=y +CONFIG_NET_SHELL=y +CONFIG_NET_SHELL_DYN_CMD_COMPLETION=y +CONFIG_NET_IP_ADDR_CHECK=y +CONFIG_NET_ICMPV4_ACCEPT_BROADCAST=y +CONFIG_NET_PROMISC_LOG_LEVEL_DBG=y +CONFIG_NET_PROMISCUOUS_MODE=y +CONFIG_NET_TEST=y +CONFIG_NET_TX_DEFAULT_PRIORITY=5 +CONFIG_NET_MAX_NEXTHOPS=20 +CONFIG_NET_MAX_ROUTES=5 + + +CONFIG_NET_ETHERNET_BRIDGE_SHELL=y +CONFIG_NET_STATISTICS_ETHERNET=y +CONFIG_NET_STATISTICS=y + +# SHELL 开关 +CONFIG_SHELL=y +CONFIG_SHELL_LOG_LEVEL_INF=y +CONFIG_SHELL_STACK_SIZE=8192 diff --git a/app/peripheral/net/echo/sample.yaml b/app/peripheral/net/echo/sample.yaml new file mode 100644 index 0000000000000000000000000000000000000000..300c13a4a6f585451025003f03b89ad91a039794 --- /dev/null +++ b/app/peripheral/net/echo/sample.yaml @@ -0,0 +1,19 @@ +sample: + description: BSD Sockets API TCP echo server sample + name: socket_echo +common: + harness: net + depends_on: netif + filter: TOOLCHAIN_HAS_NEWLIB == 1 +tests: + sample.net.sockets.echo: + tags: + - net + - socket + sample.net.sockets.echo.offload.simplelink: + platform_allow: cc3220sf_launchxl + tags: + - net + - socket + - offload + - simplelink diff --git a/app/peripheral/net/echo/src/socket_echo.c b/app/peripheral/net/echo/src/socket_echo.c new file mode 100644 index 0000000000000000000000000000000000000000..cf4d05f5780b9e0c6478e97a620cf3d2958d9861 --- /dev/null +++ b/app/peripheral/net/echo/src/socket_echo.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#ifndef __ZEPHYR__ + +#include +#include +#include +#include + +#else + +#include +#include + +#endif + +#define BIND_PORT 4242 + +int main(void) +{ + int serv; + struct sockaddr_in bind_addr; + static int counter; + + serv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (serv < 0) { + printf("error: socket: %d\n", errno); + exit(1); + } + + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = htonl(INADDR_ANY); + bind_addr.sin_port = htons(BIND_PORT); + + if (bind(serv, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) < 0) { + printf("error: bind: %d\n", errno); + exit(1); + } + + if (listen(serv, 5) < 0) { + printf("error: listen: %d\n", errno); + exit(1); + } + + printf("Single-threaded TCP echo server waits for a connection on " + "port %d...\n", BIND_PORT); + + while (1) { + struct sockaddr_in client_addr; + socklen_t client_addr_len = sizeof(client_addr); + char addr_str[32]; + int client = accept(serv, (struct sockaddr *)&client_addr, + &client_addr_len); + + if (client < 0) { + printf("error: accept: %d\n", errno); + continue; + } + + inet_ntop(client_addr.sin_family, &client_addr.sin_addr, + addr_str, sizeof(addr_str)); + printf("Connection #%d from %s\n", counter++, addr_str); + + while (1) { + char buf[128], *p; + int len = recv(client, buf, sizeof(buf), 0); + int out_len; + + if (len <= 0) { + if (len < 0) { + printf("error: recv: %d\n", errno); + } + break; + } + + p = buf; + do { + out_len = send(client, p, len, 0); + if (out_len < 0) { + printf("error: send: %d\n", errno); + goto error; + } + p += out_len; + len -= out_len; + } while (len); + } + +error: + close(client); + printf("Connection from %s closed\n", addr_str); + } + return 0; +} diff --git a/app/peripheral/net/telnet/CMakeLists.txt b/app/peripheral/net/telnet/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0d8150f13ec599591267ce25cdc11c5e5b9d0188 --- /dev/null +++ b/app/peripheral/net/telnet/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +set(EXTRA_ZEPHYR_MODULES "$ENV{ZEPHYR_BASE}/../modules/hal/phytium") +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(telnet) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/app/peripheral/net/telnet/README.md b/app/peripheral/net/telnet/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b8f0ddbef8571136978e13be8539b5a71cc98a80 --- /dev/null +++ b/app/peripheral/net/telnet/README.md @@ -0,0 +1,83 @@ +# telnet测试 + +## 1. 例程介绍 + +本例程实现了网络接口的配置和初始化,支持多种网络配置方案,包括静态IPv4地址分配、DHCPv4客户端配置以及IPv6地址配置。这个函数是网络应用程序的入口点,目的是为了展示如何在网络设备上设置不同类型的网络地址。具体的操作步骤包括: + +1. **获取默认网络接口**:通过调用 `net_if_get_default()` 函数获取系统默认的网络接口。 + +2. **配置IPv4地址**(如果启用了CONFIG_NET_IPV4且未启用CONFIG_NET_DHCPV4): + - 如果定义了静态IPv4地址(`CONFIG_NET_CONFIG_MY_IPV4_ADDR`),则调用 `setup_ipv4()` 函数配置该地址到网络接口上。 + - 该函数将静态IPv4地址添加到网络接口,并打印该地址到日志。 + +3. **启动DHCPv4客户端**(如果启用了CONFIG_NET_DHCPV4): + - 调用 `setup_dhcpv4()` 函数启动DHCPv4客户端,以便自动从DHCP服务器获取IPv4地址。 + - 该函数还注册了一个网络管理事件回调,以处理IPv4地址添加事件。当获得DHCP分配的IPv4地址时,它会打印出地址、租约时间、子网掩码和默认网关。 + +4. **配置IPv6地址**(如果启用了CONFIG_NET_IPV6): + - 调用 `setup_ipv6()` 函数配置静态IPv6地址(`CONFIG_NET_CONFIG_MY_IPV6_ADDR`)到网络接口上,并尝试添加一个IPv6组播地址(`MCAST_IP6ADDR`)。 + - 该函数将静态IPv6地址添加到网络接口,并打印该地址到日志。同时,也尝试添加一个IPv6组播地址并进行相应的处理。 + + +## 2. 如何使用例程 + +本例程需要以下硬件, + +- E2000D/Q Demo板 + +### 2.1 硬件配置方法 + +保障串口正常工作后,需要确认网络是否连接好 + +### 2.2 SDK配置方法 + +- 请根据自己的网络环境,修改prj.conf文件中的CONFIG_NET_CONFIG_MY_IPV4_ADDR与CONFIG_NET_CONFIG_MY_IPV6_ADDR 条目为您当前网络环境中分配给开发板的IP. + +- 本例子已经提供好具体的编译指令,以下进行介绍: + +1. ``west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf``,编译命令, 使用west工具构建当前目录下的Zephyr项目,指定目标板为e2000q_demo,并使用prj.conf配置文件覆盖默认配置 ,最终生成的执行文件将会保存在./build/zephyr/zephyr.elf +2. ``west build -t clean``, 清除缓存 ,使用west工具的clean目标清理Zephyr构建系统可能生成的任何其他临时文件或缓存 + +### 2.3 构建和下载 + +- 编译例程 + +``west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf`` + +- 编译主机测侧设置重启tftp服务器 + +``` +sudo service tftpd-hpa restart +``` + +- 开发板侧使用bootelf命令跳转 + +``` +setenv ipaddr 192.168.4.20 +setenv serverip 192.168.4.50 +setenv gatewayip 192.168.4.1 +tftpboot 0x90100000 zephyr.elf +bootelf -p 0x90100000 +``` + +### 2.4 输出与实验现象 + +- 所有用例均提供一系列可变配置,可在例程全局变量中修改 + +#### 2.4.1 telnet 例程使用 + +1. 将zephyr.elf 在开发板上加载起来. + +2. 在PC主机上输入以下命令,则可以连接开发板echo服务器: + +``` +telnet 192.168.4.7 +``` + +3. 当PC主机连接成功之后,开发板上会打印以下内容: + +![1713424173372](figs/README/1713424173372.png) + +## 3. 如何解决问题 + +## 4. 修改历史记录 diff --git a/app/peripheral/net/telnet/figs/README/1713424173372.png b/app/peripheral/net/telnet/figs/README/1713424173372.png new file mode 100644 index 0000000000000000000000000000000000000000..383d9c26801ecbcb77b7d7cbd05fb83b7839f057 Binary files /dev/null and b/app/peripheral/net/telnet/figs/README/1713424173372.png differ diff --git a/app/peripheral/net/telnet/makefile b/app/peripheral/net/telnet/makefile new file mode 100644 index 0000000000000000000000000000000000000000..7fb9ef2b3d9d64cd9a8f4a75abb019cd8c1d5df6 --- /dev/null +++ b/app/peripheral/net/telnet/makefile @@ -0,0 +1,28 @@ + + +boot_smp: + west build -b e2000q_demo_smp ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + + +boot: + west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + + +boot_aarch32: + + west build -b e2000q_a32_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + + +clean: + west build -t clean + rm -rf build + + +gdb: + gdb-multiarch -x .gdbinit \ No newline at end of file diff --git a/app/peripheral/net/telnet/prj.conf b/app/peripheral/net/telnet/prj.conf new file mode 100644 index 0000000000000000000000000000000000000000..15f29b5a3404d02bd2375781818a0a4275ebf9d9 --- /dev/null +++ b/app/peripheral/net/telnet/prj.conf @@ -0,0 +1,69 @@ +CONFIG_PRINTK=y +CONFIG_MAIN_STACK_SIZE=4096 + +CONFIG_NETWORKING=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV4=y +CONFIG_NET_ARP=y +CONFIG_NET_UDP=y +CONFIG_NET_TCP=y + +CONFIG_NET_DHCPV4=n + +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=2 +CONFIG_NET_MAX_CONTEXTS=20 + +CONFIG_NET_PKT_RX_COUNT=64 +CONFIG_NET_PKT_TX_COUNT=64 +CONFIG_NET_BUF_RX_COUNT=64 +CONFIG_NET_BUF_TX_COUNT=64 +CONFIG_NET_BUF_DATA_SIZE=1500 + + +CONFIG_TEST_RANDOM_GENERATOR=y + +CONFIG_INIT_STACKS=y + + + +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.168.4.7" + +CONFIG_NET_SHELL=y +CONFIG_KERNEL_SHELL=y +CONFIG_SHELL_BACKEND_TELNET=y +CONFIG_SHELL_TELNET_SUPPORT_COMMAND=y + + +# SHELL 开关 +CONFIG_SHELL=y +CONFIG_SHELL_LOG_LEVEL_INF=y +CONFIG_SHELL_STACK_SIZE=8192 + + +# enable fragmenting +CONFIG_NET_IPV4_FRAGMENT=y +CONFIG_NET_IPV4_FRAGMENT_MAX_COUNT=16 +CONFIG_NET_IPV4_FRAGMENT_TIMEOUT=1 +CONFIG_NET_IPV4_FRAGMENT_MAX_PKT=64 + +CONFIG_NET_IPV6_FRAGMENT=y +CONFIG_NET_IPV6_FRAGMENT_MAX_COUNT=16 +CONFIG_NET_IPV6_FRAGMENT_TIMEOUT=15 +CONFIG_NET_IPV6_FRAGMENT_MAX_PKT=32 + +CONFIG_NET_TCP_WORKQ_STACK_SIZE=4096 +CONFIG_NET_RX_STACK_SIZE=4096 +CONFIG_NET_MGMT_EVENT_STACK_SIZE=4096 + +# 网络调试信息 +# CONFIG_NET_BUF_LOG=n +# CONFIG_NET_LOG=y +# CONFIG_NET_CONN_LOG_LEVEL_DBG=y +# CONFIG_NET_CONTEXT_LOG_LEVEL_DBG=y +# CONFIG_NET_TCP_LOG_LEVEL_DBG=y +# CONFIG_NET_IPV4_LOG_LEVEL_DBG=y +# CONFIG_NET_IPV6_LOG_LEVEL_DBG=y +# CONFIG_NET_CORE_LOG_LEVEL_DBG=y diff --git a/app/peripheral/net/telnet/sample.yaml b/app/peripheral/net/telnet/sample.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e5e737cbbcda28cbfd9e985a9e9c3af514b821e0 --- /dev/null +++ b/app/peripheral/net/telnet/sample.yaml @@ -0,0 +1,9 @@ +sample: + name: Telnet Server +tests: + sample.net.telnet: + harness: net + depends_on: netif + tags: + - net + - telnet diff --git a/app/peripheral/net/telnet/src/telnet.c b/app/peripheral/net/telnet/src/telnet.c new file mode 100644 index 0000000000000000000000000000000000000000..e1cc53095a4a5be0c5a65096e81aefc5e2224688 --- /dev/null +++ b/app/peripheral/net/telnet/src/telnet.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(net_telnet_sample, LOG_LEVEL_DBG); + +#include +#include +#include +#include + +#include +#include +#include + +#if defined(CONFIG_NET_DHCPV4) +static struct net_mgmt_event_callback mgmt_cb; + +static void ipv4_addr_add_handler(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, + struct net_if *iface) +{ + char hr_addr[NET_IPV4_ADDR_LEN]; + int i = 0; + + if (mgmt_event != NET_EVENT_IPV4_ADDR_ADD) { + /* Spurious callback. */ + return; + } + + for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { + struct net_if_addr *if_addr = + &iface->config.ip.ipv4->unicast[i]; + + if (if_addr->addr_type != NET_ADDR_DHCP || !if_addr->is_used) { + continue; + } + + LOG_INF("IPv4 address: %s", + net_addr_ntop(AF_INET, + &if_addr->address.in_addr, + hr_addr, NET_IPV4_ADDR_LEN)); + LOG_INF("Lease time: %u seconds", + iface->config.dhcpv4.lease_time); + LOG_INF("Subnet: %s", + net_addr_ntop(AF_INET, + &iface->config.ip.ipv4->netmask, + hr_addr, NET_IPV4_ADDR_LEN)); + LOG_INF("Router: %s", + net_addr_ntop(AF_INET, + &iface->config.ip.ipv4->gw, + hr_addr, NET_IPV4_ADDR_LEN)); + break; + } +} + +static void setup_dhcpv4(struct net_if *iface) +{ + LOG_INF("Running dhcpv4 client..."); + + net_mgmt_init_event_callback(&mgmt_cb, ipv4_addr_add_handler, + NET_EVENT_IPV4_ADDR_ADD); + net_mgmt_add_event_callback(&mgmt_cb); + + net_dhcpv4_start(iface); +} + +#else +#define setup_dhcpv4(...) +#endif /* CONFIG_NET_DHCPV4 */ + +#if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_DHCPV4) + +#if !defined(CONFIG_NET_CONFIG_MY_IPV4_ADDR) +#error "You need to define an IPv4 Address or enable DHCPv4!" +#endif + +static void setup_ipv4(struct net_if *iface) +{ + char hr_addr[NET_IPV4_ADDR_LEN]; + struct in_addr addr; + + if (net_addr_pton(AF_INET, CONFIG_NET_CONFIG_MY_IPV4_ADDR, &addr)) { + LOG_ERR("Invalid address: %s", CONFIG_NET_CONFIG_MY_IPV4_ADDR); + return; + } + + net_if_ipv4_addr_add(iface, &addr, NET_ADDR_MANUAL, 0); + + LOG_INF("IPv4 address: %s", + net_addr_ntop(AF_INET, &addr, hr_addr, + NET_IPV4_ADDR_LEN)); +} + +#else +#define setup_ipv4(...) +#endif /* CONFIG_NET_IPV4 && !CONFIG_NET_DHCPV4 */ + +#if defined(CONFIG_NET_IPV6) + +#define MCAST_IP6ADDR "ff84::2" + +#ifndef CONFIG_NET_CONFIG_MY_IPV6_ADDR +#error "You need to define an IPv6 Address!" +#endif + +static void setup_ipv6(struct net_if *iface) +{ + char hr_addr[NET_IPV6_ADDR_LEN]; + struct in6_addr addr; + + if (net_addr_pton(AF_INET6, CONFIG_NET_CONFIG_MY_IPV6_ADDR, &addr)) { + LOG_ERR("Invalid address: %s", CONFIG_NET_CONFIG_MY_IPV6_ADDR); + return; + } + + net_if_ipv6_addr_add(iface, &addr, NET_ADDR_MANUAL, 0); + + LOG_INF("IPv6 address: %s", + net_addr_ntop(AF_INET6, &addr, hr_addr, NET_IPV6_ADDR_LEN)); + + if (net_addr_pton(AF_INET6, MCAST_IP6ADDR, &addr)) { + LOG_ERR("Invalid address: %s", MCAST_IP6ADDR); + return; + } + + net_if_ipv6_maddr_add(iface, &addr); +} + +#else +#define setup_ipv6(...) +#endif /* CONFIG_NET_IPV6 */ + +int main(void) +{ + struct net_if *iface = net_if_get_default(); + + LOG_INF("Starting Telnet sample"); + + setup_ipv4(iface); + + setup_dhcpv4(iface); + + setup_ipv6(iface); + return 0; +} diff --git a/app/peripheral/pcie/pcie_ecam_test/.gdbinit b/app/peripheral/pcie/pcie_ecam_test/.gdbinit new file mode 100644 index 0000000000000000000000000000000000000000..49db8cc85c15265f247a742062ccbcf47d098deb --- /dev/null +++ b/app/peripheral/pcie/pcie_ecam_test/.gdbinit @@ -0,0 +1,48 @@ +# increase the default remote timeout in gdb because +# Windows libusb transactions could be slow +set remotetimeout 100000 + +# gdb connect to openocd in port 3333 +target extended-remote localhost:3333 + +# start openocd working queue, monitor followed with openocd command here +monitor init + +# force to use hardware breakpoint, otherwise use software breakpoint +# for e2000d/q, num of hardware breakpoints supposed to be 6 +monitor gdb_breakpoint_override hardware + +# load elf image +load ./build/zephyr/zephyr.elf + +# in case symbols skip load,load agin +file ./build/zephyr/zephyr.elf + +# we can break at the beginning of code by address or by symbol +#break _boot + +# add more breakpoints in application +#break JtagTouchRegisters +#break JtagTouchMemory +# break bubbleSort` +# break JtagPostSort +#break bubbleSortCXX + +# show all breakspoints we before running +# info breakpoints + +# show [-0x10 ~ +0x10 ] range of instructions when breaked +# display /10i $pc-16 + +# start running +# continue +layout src +break boot_banner +# break sdmmc_write_blocks +# break z_arm64_prep_c +# break sdmmc_switch +# break sdmmc_read_csd +# break sdmmc_read_blocks +# break sd_idle +# break sd_send_interface_condition +# continue \ No newline at end of file diff --git a/app/peripheral/pcie/pcie_ecam_test/CMakeLists.txt b/app/peripheral/pcie/pcie_ecam_test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a8231e470717a2c3edbb9a3fc662e306bd942e4 --- /dev/null +++ b/app/peripheral/pcie/pcie_ecam_test/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +set(EXTRA_ZEPHYR_MODULES "$ENV{ZEPHYR_BASE}/../modules/hal/phytium") +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(pcie_ecam_test) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0") +endif() \ No newline at end of file diff --git a/app/peripheral/pcie/pcie_ecam_test/README.md b/app/peripheral/pcie/pcie_ecam_test/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2ba72395cfe9d3a913f9a354b3ece01504e17f6e --- /dev/null +++ b/app/peripheral/pcie/pcie_ecam_test/README.md @@ -0,0 +1,68 @@ +# PCIE ECAM测试 + +## 1. 例程介绍 + +本例程用于测试zephyr原生PCIE ECAM驱动,设备树中pcie相关配置后,驱动会自动进行枚举操作 + +## 2. 如何使用例程 + +本例程需要以下硬件, + +- E2000D/Q Demo板 +- pcie功能性设备或桥设备(转接卡) + +### 2.1 硬件配置方法 + +将pcie功能性设备或桥设备(转接卡)插入开发板对应插槽 + +### 2.2 SDK配置方法 + +- 本例程提供如下编译指令: + +1. ``make boot``,编译命令, 使用west工具构建当前目录下的Zephyr项目,默认指定的目标板为e2000q_demo,可以在makefile文件中修改(如有修改,sample.yaml文件中的相关配置也需要一并修改),并使用prj.conf配置文件覆盖默认配置 ,最终生成的执行文件将会保存在./build/zephyr/zephyr.elf +2. ``make clean``, 清除缓存 ,使用west工具的clean目标清理Zephyr构建系统可能生成的任何其他临时文件或缓存 + +### 2.3 构建和下载 + +- 编译例程 + +``make boot`` + +- 编译主机测侧设置重启tftp服务器 + +``` +sudo service tftpd-hpa restart +``` + +- 开发板侧使用bootelf命令跳转 + +``` +setenv ipaddr 192.168.4.20 +setenv serverip 192.168.4.50 +setenv gatewayip 192.168.4.1 +tftpboot 0x90100000 zephyr.elf +bootelf -p 0x90100000 +``` + +### 2.4 输出与实验现象 + +- 所有用例均提供一系列可变配置,可在例程全局变量中修改 + +### 2.4.1 PCIE ECAM枚举测试 + +进入系统后将出现如下打印,表示BAR空间的分配: + +![pcie_bar_region](figs/README/pcie_bar_region.jpg) + +输入```pcie```,将出现如下打印,表示枚举的结果: + +![pcie_enum_result](figs/README/pcie_enum_result.jpg) + +输入```pcie ls 4:0.0 dump```,将出现如下打印,表示单个pcie(BDF = 4:0.0)设备的配置空间与基本特性展示: + +![pcie_ls_result](figs/README/pcie_ls_result.jpg) + + +## 3. 如何解决问题 + +## 4. 修改历史记录 \ No newline at end of file diff --git a/app/peripheral/pcie/pcie_ecam_test/app.overlay b/app/peripheral/pcie/pcie_ecam_test/app.overlay new file mode 100644 index 0000000000000000000000000000000000000000..ac8ef2062f16bf9e5e35c4590ce1d3a3173a15e0 --- /dev/null +++ b/app/peripheral/pcie/pcie_ecam_test/app.overlay @@ -0,0 +1,11 @@ +&sdhc1 { + status = "disabled"; + sdmmc { + compatible = "zephyr,sdmmc-disk"; + status = "disabled"; + }; +}; + +&pinctrl { + status = "disabled"; +}; \ No newline at end of file diff --git a/app/peripheral/pcie/pcie_ecam_test/figs/README/pcie_bar_region.jpg b/app/peripheral/pcie/pcie_ecam_test/figs/README/pcie_bar_region.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1498878ac2946785664db8327dc5fe87d864cb76 Binary files /dev/null and b/app/peripheral/pcie/pcie_ecam_test/figs/README/pcie_bar_region.jpg differ diff --git a/app/peripheral/pcie/pcie_ecam_test/figs/README/pcie_enum_result.jpg b/app/peripheral/pcie/pcie_ecam_test/figs/README/pcie_enum_result.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1cb689bd98305af237475f94746dc0eaca1f6774 Binary files /dev/null and b/app/peripheral/pcie/pcie_ecam_test/figs/README/pcie_enum_result.jpg differ diff --git a/app/peripheral/pcie/pcie_ecam_test/figs/README/pcie_ls_result.jpg b/app/peripheral/pcie/pcie_ecam_test/figs/README/pcie_ls_result.jpg new file mode 100644 index 0000000000000000000000000000000000000000..134e49d7b1669b3950324cd4c4ad5f5d50355f83 Binary files /dev/null and b/app/peripheral/pcie/pcie_ecam_test/figs/README/pcie_ls_result.jpg differ diff --git a/app/peripheral/pcie/pcie_ecam_test/makefile b/app/peripheral/pcie/pcie_ecam_test/makefile new file mode 100644 index 0000000000000000000000000000000000000000..6e0b985528e34ce4c20feeff3c01abec33467411 --- /dev/null +++ b/app/peripheral/pcie/pcie_ecam_test/makefile @@ -0,0 +1,21 @@ +boot: + west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + +boot_smp: + west build -b e2000q_demo_smp ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + +boot_aarch32: + west build -b e2000q_a32_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + +clean: + west build -t clean + rm -rf build + +gdb: + gdb-multiarch -x .gdbinit \ No newline at end of file diff --git a/app/peripheral/pcie/pcie_ecam_test/prj.conf b/app/peripheral/pcie/pcie_ecam_test/prj.conf new file mode 100644 index 0000000000000000000000000000000000000000..4980fcd2034dc90db1d2fbd44d94c98d0651e478 --- /dev/null +++ b/app/peripheral/pcie/pcie_ecam_test/prj.conf @@ -0,0 +1,20 @@ +CONFIG_DISK_ACCESS=y + +# 开启shell +CONFIG_SHELL=y + +# 开启启动的BANNER +CONFIG_BOOT_BANNER=y + +# PCIE相关 +CONFIG_PCIE=y +CONFIG_PCIE_CONTROLLER=y +CONFIG_PCIE_ECAM=y +CONFIG_PCIE_SHELL=y + +# log相关 +CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=4096 + +# 加大虚拟内存 +CONFIG_KERNEL_VM_SIZE=0x20000000 \ No newline at end of file diff --git a/app/peripheral/pcie/pcie_ecam_test/sample.yaml b/app/peripheral/pcie/pcie_ecam_test/sample.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bd682f5a36f8eddb8d779281058ee9c5d1f85b35 --- /dev/null +++ b/app/peripheral/pcie/pcie_ecam_test/sample.yaml @@ -0,0 +1,13 @@ +sample: + description: pcie ecam test + name: pcie ecam test +tests: + sample.peripheral.pcie_ecam_test: + depends_on: pcie + tags: + - pcie + harness: console + platform_allow: + - e2000q_demo + integration_platforms: + - e2000q_demo \ No newline at end of file diff --git a/app/peripheral/pcie/pcie_ecam_test/src/main.c b/app/peripheral/pcie/pcie_ecam_test/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..c08b8fad12a1952f4bd80ab6616e159c41fce801 --- /dev/null +++ b/app/peripheral/pcie/pcie_ecam_test/src/main.c @@ -0,0 +1,27 @@ +// Phytium is pleased to support the open source community by making Zephyr-SDK available. + +// +// Copyright (C) 2024 Phytium Technology Co., Ltd. All rights reserved. +// +// Licensed under the Apache-2.0 License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/license/apache-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. + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(main); + +int main(void) +{ + printf("This is PCIE ecam test example.\r\n"); + return 0 ; +} \ No newline at end of file diff --git a/app/peripheral/sdio/tf_test/.gdbinit b/app/peripheral/sdio/tf_test/.gdbinit new file mode 100644 index 0000000000000000000000000000000000000000..49db8cc85c15265f247a742062ccbcf47d098deb --- /dev/null +++ b/app/peripheral/sdio/tf_test/.gdbinit @@ -0,0 +1,48 @@ +# increase the default remote timeout in gdb because +# Windows libusb transactions could be slow +set remotetimeout 100000 + +# gdb connect to openocd in port 3333 +target extended-remote localhost:3333 + +# start openocd working queue, monitor followed with openocd command here +monitor init + +# force to use hardware breakpoint, otherwise use software breakpoint +# for e2000d/q, num of hardware breakpoints supposed to be 6 +monitor gdb_breakpoint_override hardware + +# load elf image +load ./build/zephyr/zephyr.elf + +# in case symbols skip load,load agin +file ./build/zephyr/zephyr.elf + +# we can break at the beginning of code by address or by symbol +#break _boot + +# add more breakpoints in application +#break JtagTouchRegisters +#break JtagTouchMemory +# break bubbleSort` +# break JtagPostSort +#break bubbleSortCXX + +# show all breakspoints we before running +# info breakpoints + +# show [-0x10 ~ +0x10 ] range of instructions when breaked +# display /10i $pc-16 + +# start running +# continue +layout src +break boot_banner +# break sdmmc_write_blocks +# break z_arm64_prep_c +# break sdmmc_switch +# break sdmmc_read_csd +# break sdmmc_read_blocks +# break sd_idle +# break sd_send_interface_condition +# continue \ No newline at end of file diff --git a/app/peripheral/sdio/tf_test/CMakeLists.txt b/app/peripheral/sdio/tf_test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2175f415e7030a84e9695b2d23843ae43ec3f28c --- /dev/null +++ b/app/peripheral/sdio/tf_test/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +set(EXTRA_ZEPHYR_MODULES "$ENV{ZEPHYR_BASE}/../modules/hal/phytium") +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(tf_test) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/app/peripheral/sdio/tf_test/README.docx b/app/peripheral/sdio/tf_test/README.docx new file mode 100644 index 0000000000000000000000000000000000000000..4f21a584b398805a77b6437261b29f6b098b5887 Binary files /dev/null and b/app/peripheral/sdio/tf_test/README.docx differ diff --git a/app/peripheral/sdio/tf_test/README.md b/app/peripheral/sdio/tf_test/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0a3200c12c40bdebeae15082947e8be3fc7b3cf1 --- /dev/null +++ b/app/peripheral/sdio/tf_test/README.md @@ -0,0 +1,70 @@ +# TF卡测试 + +## 1. 例程介绍 + +函数 `sd_perf_test` 是设计用来执行SD卡性能测试的,具体测试包括写入速度和读取速度。这一功能通过向SD卡写入预定的数据块,然后再读取这些数据块来实现,整个过程中计算并展示写入和读取的速度。测试的详细步骤如下: + +1. 验证SDHC(Secure Digital High Capacity)设备是否准备就绪,并确认SD卡是否存在。 +2. 初始化SD卡并获取其扇区(sector)的大小和数量。 +3. 准备写入缓冲区,这里模拟的是将一系列的数据填充进缓冲区。 +4. 进行数据写入操作。这一步中,数据被分批写入SD卡。每次写入一定数量的块(block),直到达到预定的总块数。在写入过程中,展示进度条以反映当前的进度。 +5. 计算写入数据所需的总时间,并据此计算写入速度(单位为MB/s)。 +6. 进行数据读取操作。类似于写入过程,数据也是分批读取的。在读取过程中,同样展示进度条以反映当前进度。 +7. 计算读取数据所需的总时间,并据此计算读取速度(单位为MB/s)。 +8. 读取数据后,对比原始数据和读取后的数据以验证数据的一致性。如果发现数据不一致,表示在写入或读取过程中发生了错误。 +9. 如果所有数据块都被成功写入和读取,且数据一致性验证通过,则输出测试成功的消息,并展示写入和读取速度。 + +## 2. 如何使用例程 + +本例程需要以下硬件, + +- E2000D/Q Demo板 + +### 2.1 硬件配置方法 + +保障串口正常工作后,需要插入一张 SDHC/SDXC 存储卡 + +### 2.2 SDK配置方法 + +- 本例子已经提供好具体的编译指令,以下进行介绍: + +1. ``west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf``,编译命令, 使用west工具构建当前目录下的Zephyr项目,指定目标板为e2000q_demo,并使用prj.conf配置文件覆盖默认配置 ,最终生成的执行文件将会保存在./build/zephyr/zephyr.elf +2. ``west build -t clean``, 清除缓存 ,使用west工具的clean目标清理Zephyr构建系统可能生成的任何其他临时文件或缓存 + +### 2.3 构建和下载 + +- 编译例程 + +``west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf`` + +- 编译主机测侧设置重启tftp服务器 + +``` +sudo service tftpd-hpa restart +``` + +- 开发板侧使用bootelf命令跳转 + +``` +setenv ipaddr 192.168.4.20 +setenv serverip 192.168.4.50 +setenv gatewayip 192.168.4.1 +tftpboot 0x90100000 zephyr.elf +bootelf -p 0x90100000 +``` + +### 2.4 输出与实验现象 + +- 所有用例均提供一系列可变配置,可在例程全局变量中修改 + +### 2.4.1 SD 速度测试 + +``` +sdtest +``` + +![1713411631705](figs/README/1713411631705.png) + +## 3. 如何解决问题 + +## 4. 修改历史记录 diff --git a/app/peripheral/sdio/tf_test/figs/README/1713411631705.png b/app/peripheral/sdio/tf_test/figs/README/1713411631705.png new file mode 100644 index 0000000000000000000000000000000000000000..f7e8e42b331b044ddbabc4a564b0d34db73de649 Binary files /dev/null and b/app/peripheral/sdio/tf_test/figs/README/1713411631705.png differ diff --git a/app/peripheral/sdio/tf_test/makefile b/app/peripheral/sdio/tf_test/makefile new file mode 100644 index 0000000000000000000000000000000000000000..8cca72ee363f718846a3ecdf860f80aabe4368e5 --- /dev/null +++ b/app/peripheral/sdio/tf_test/makefile @@ -0,0 +1,28 @@ + + +boot: + west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + + +boot_smp: + west build -b e2000q_demo_smp ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + + +boot_aarch32: + + west build -b e2000q_a32_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + + +clean: + west build -t clean + rm -rf build + + +gdb: + gdb-multiarch -x .gdbinit \ No newline at end of file diff --git a/app/peripheral/sdio/tf_test/prj.conf b/app/peripheral/sdio/tf_test/prj.conf new file mode 100644 index 0000000000000000000000000000000000000000..7724eee60c7e5ca9f565acfc23ed984dbae7fb7c --- /dev/null +++ b/app/peripheral/sdio/tf_test/prj.conf @@ -0,0 +1,32 @@ +CONFIG_DISK_ACCESS=y + +CONFIG_FILE_SYSTEM=y +CONFIG_FAT_FILESYSTEM_ELM=y + + +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_SDHC_BUFFER_ALIGNMENT=512 +CONFIG_PHYTIUM_USDHC_DMA_SUPPORT=y +CONFIG_PHYTIUM_SDIF_DMA_DESC_LENGTH=1024 +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_SHELL=y +CONFIG_FILE_SYSTEM_SHELL_TEST_COMMANDS=y + +# 开启shell +CONFIG_SHELL=y +# 开启启动的BANNER +CONFIG_BOOT_BANNER=y + + +#debug +CONFIG_LOG_PRINTK=n +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_LOG_MAX_LEVEL=4 +CONFIG_DEBUG_INFO=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_PHYTIUM=y +# 开启 zephyrproject/zephyr/subsys/sd/sd.c debug +# CONFIG_SD_LOG_LEVEL_DBG=y +# 开启 zephyrproject/zephyr/drivers/sdhc/phytium_sdhc.c debug 打印 +# CONFIG_SDHC_LOG_LEVEL_DBG=y diff --git a/app/peripheral/sdio/tf_test/sample.yaml b/app/peripheral/sdio/tf_test/sample.yaml new file mode 100644 index 0000000000000000000000000000000000000000..24fb341e7bd604e59e9c1ec1e27f45c921a4d81b --- /dev/null +++ b/app/peripheral/sdio/tf_test/sample.yaml @@ -0,0 +1,13 @@ +sample: + description: tf test + name: tf test +tests: + sample.peripheral.tf_test: + depends_on: sdhc + tags: + - sdhc + harness: console + platform_allow: + - e2000q_demo + integration_platforms: + - e2000q_demo diff --git a/app/peripheral/sdio/tf_test/src/main.c b/app/peripheral/sdio/tf_test/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..19ea1ec24688434c60d0c92fde3b13a6db7fc689 --- /dev/null +++ b/app/peripheral/sdio/tf_test/src/main.c @@ -0,0 +1,156 @@ +// Phytium is pleased to support the open source community by making Zephyr-SDK available. + +// +// Copyright (C) 2024 Phytium Technology Co., Ltd. All rights reserved. +// +// Licensed under the Apache-2.0 License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/license/apache-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. + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(main); + +#define MB_SIZE (1024*1024) +#define SD_TOTAL_MB 1 +#define SD_TOTAL_MB_SIZE (SD_TOTAL_MB*MB_SIZE) +#define SD_BLOCK_SIZE 512UL /* most tested sd memory cards have 512 block size by default */ + +#define WRITE_BLOCKS 1024 // 这里假设每次写入40块,即20KB +#define TOTAL_BLOCKS SD_TOTAL_MB_SIZE / SD_BLOCK_SIZE // 总共要写入20MB数据 + +uint8_t write_buf[SD_BLOCK_SIZE * WRITE_BLOCKS] __attribute__((aligned(CONFIG_SDHC_BUFFER_ALIGNMENT))) ; +uint8_t read_buf[SD_BLOCK_SIZE * WRITE_BLOCKS] __attribute__((aligned(CONFIG_SDHC_BUFFER_ALIGNMENT))) ; + + +// 函数用于打印进度条 +void print_progress(int current, int total) { + int progress = current * 100 / total; + printf("\rProgress: %d%% [", progress); + for (int i = 0; i < 100; i += 10) { + if (i < progress) { + printf("="); + } else if (i == progress) { + printf(">"); + } else { + printf(" "); + } + } + printf("]"); + fflush(stdout); // 确保即时打印到控制台 +} + + +void sd_perf_test(const struct shell *shell, size_t argc, char **argv) +{ + struct sd_card card; + int ret; + const struct device *const sdhc_dev = DEVICE_DT_GET(DT_NODELABEL(sdhc1)); + uint32_t sector_size; + uint32_t sector_count; + int64_t start_time, end_time; + double write_duration, read_duration, write_speed, read_speed; + + if(!device_is_ready(sdhc_dev)) + { + LOG_ERR("Sd device is not ready"); + } + + ret = sd_is_card_present(sdhc_dev); + if(!ret) + { + LOG_ERR("SD card not present in slot"); + } + + ret = sd_init(sdhc_dev, &card); + if(ret) + { + LOG_ERR("SD card sd_init is not ok \r\n"); + } + + ret = sdmmc_ioctl(&card, DISK_IOCTL_GET_SECTOR_COUNT, §or_count); + printf("SD card reports sector count of %d\n", sector_count); + + ret = sdmmc_ioctl(&card, DISK_IOCTL_GET_SECTOR_SIZE, §or_size); + printf("SD card reports sector size of %d\n", sector_size); + + // 初始化write_buf + for (int i = 0; i < sizeof(write_buf); ++i) { + write_buf[i] = i % 256; + } + + // 写入数据 + printf("Writing data...\n"); + start_time = k_uptime_get(); + for (int i = 0; i < TOTAL_BLOCKS / WRITE_BLOCKS; ++i) + { + if (sdmmc_write_blocks(&card, write_buf, i * WRITE_BLOCKS, WRITE_BLOCKS) != 0) { + printf("Write failed at block %d\n", i * WRITE_BLOCKS); + return; + } + print_progress(i + 1, TOTAL_BLOCKS / WRITE_BLOCKS); + } + end_time = k_uptime_get(); + printf("\nWrite completed.\n"); + write_duration = (end_time - start_time) / 1000.0; // 将毫秒转换为秒 + printf("\nWrite completed in %.2f seconds.\n", write_duration); + write_speed = SD_TOTAL_MB / write_duration; // 计算写速度,单位MB/s + printf("\nWrite completed in %.2f seconds at %.2f MB/s.\n", write_duration, write_speed); + + // 读取并验证数据 + printf("Reading data...\n"); + start_time = k_uptime_get(); + for (int i = 0; i < TOTAL_BLOCKS / WRITE_BLOCKS; ++i) + { + if (sdmmc_read_blocks(&card, read_buf, i * WRITE_BLOCKS, WRITE_BLOCKS) != 0) { + printf("Read failed at block %d\n", i * WRITE_BLOCKS); + return; + } + + print_progress(i + 1, TOTAL_BLOCKS / WRITE_BLOCKS); + } + end_time = k_uptime_get(); + read_duration = (end_time - start_time) / 1000.0; // 将毫秒转换为秒 + printf("\nRead completed in %.2f seconds.\n", read_duration); + read_speed = SD_TOTAL_MB / read_duration; // 计算读速度,单位MB/s + printf("\nRead completed in %.2f seconds at %.2f MB/s.\n", read_duration, read_speed); + + + for (int i = 0; i < TOTAL_BLOCKS / WRITE_BLOCKS; ++i) + { + memset(read_buf, 0, sizeof(read_buf)); + if (sdmmc_read_blocks(&card, read_buf, i * WRITE_BLOCKS, WRITE_BLOCKS) != 0) { + printf("Read failed at block %d\n", i * WRITE_BLOCKS); + return; + } + + if (memcmp(write_buf, read_buf, SD_BLOCK_SIZE * WRITE_BLOCKS) != 0) { + printf("Data mismatch detected at block %d\n", i * WRITE_BLOCKS); + return; + } + } + + + printf("\n SDHC performance test completed successfully.\n"); + + return ; +} + + +SHELL_CMD_ARG_REGISTER(sdtest, NULL, "Start SD card performance test", sd_perf_test, 1, 0); + +int main(void) +{ + return 0 ; +} diff --git a/app/system/interrupt/.gdbinit b/app/system/interrupt/.gdbinit new file mode 100644 index 0000000000000000000000000000000000000000..3fbb1f2341616f0d885277638b545f2fb0eb6339 --- /dev/null +++ b/app/system/interrupt/.gdbinit @@ -0,0 +1,48 @@ +# increase the default remote timeout in gdb because +# Windows libusb transactions could be slow +set remotetimeout 100000 + +# gdb connect to openocd in port 3333 +target extended-remote localhost:3333 + +# start openocd working queue, monitor followed with openocd command here +monitor init + +# force to use hardware breakpoint, otherwise use software breakpoint +# for e2000d/q, num of hardware breakpoints supposed to be 6 +monitor gdb_breakpoint_override hardware + +# load elf image +load ./build/zephyr/zephyr.elf + +# in case symbols skip load,load agin +file ./build/zephyr/zephyr.elf + +# we can break at the beginning of code by address or by symbol +#break _boot + +# add more breakpoints in application +#break JtagTouchRegisters +#break JtagTouchMemory +# break bubbleSort` +# break JtagPostSort +#break bubbleSortCXX + +# show all breakspoints we before running +# info breakpoints + +# show [-0x10 ~ +0x10 ] range of instructions when breaked +# display /10i $pc-16 + +# start running +# continue +layout src +# break boot_banner +# break sdmmc_write_blocks +# break z_arm64_prep_c +# break sdmmc_switch +# break sdmmc_read_csd +# break sdmmc_read_blocks +# break sd_idle +# break sd_send_interface_condition +# continue \ No newline at end of file diff --git a/app/system/interrupt/CMakeLists.txt b/app/system/interrupt/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..16f50871475e67db065f3a3119bce8b7b481b7ff --- /dev/null +++ b/app/system/interrupt/CMakeLists.txt @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(interrupt) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +# target_include_directories(app PRIVATE +# ${ZEPHYR_BASE}/kernel/include +# ${ZEPHYR_BASE}/arch/${ARCH}/include +# ) + +# target_sources(app PRIVATE +# src/prevent_irq.c +# src/nested_irq.c +# src/main.c +# ) + +# target_sources_ifdef(CONFIG_DYNAMIC_INTERRUPTS app PRIVATE src/dynamic_isr.c) +# target_sources_ifdef(CONFIG_X86 app PRIVATE src/regular_isr.c) +# target_sources_ifdef(CONFIG_SHARED_INTERRUPTS app PRIVATE src/static_shared_irq.c) + +# if (CONFIG_SHARED_INTERRUPTS) +# target_sources_ifdef(CONFIG_DYNAMIC_INTERRUPTS app PRIVATE src/dynamic_shared_irq.c) +# endif() diff --git a/app/system/interrupt/README.md b/app/system/interrupt/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ef30f04a53c6030d38bf8243d720176a04364a8c --- /dev/null +++ b/app/system/interrupt/README.md @@ -0,0 +1,158 @@ +# Interrupt 测试 + +## 1. 例程介绍 + +### dynamic_isr.c + +`test_isr_dynamic`函数的目的是验证动态中断服务例程(ISR)安装的功能。这是内核中断测试的一部分,特别是在支持动态中断连接的系统上非常关键。以下是该测试函数完成的主要验证步骤: + +1. **查找未使用的软件ISR表项**:该测试通过遍历软件ISR表(`_sw_isr_table`),寻找一个未被使用的项,即其中断服务例程指针等于 `z_irq_spurious`的项。`z_irq_spurious`是一个用于处理无效或未配置的中断的“伪”ISR。 +2. **安装动态ISR**:找到未使用的表项后,测试使用 `arch_irq_connect_dynamic`函数安装一个新的动态ISR。该函数允许在运行时动态地连接中断服务例程,不需要在编译时预先定义。测试中,动态ISR为 `dyn_isr`函数,它接受一个指向整数 `i`的指针作为参数。 +3. **验证ISR安装成功**:安装动态ISR后,测试通过检查软件ISR表的相应项来验证ISR是否成功安装。成功的标志是表项的中断服务例程指针等于 `dyn_isr`,并且参数 `arg`与传递给 `arch_irq_connect_dynamic`的参数相匹配。 + +### dynamic_shared_irq.c + +- test_dynamic_shared_irq_disconnect_write + +此函数的目标是验证动态共享中断(ISR)在断开(disconnect)一个ISR/arg对后的行为。测试流程如下: + +1. **预先设置**:首先确保中断服务例程(ISR)通过之前的设置步骤已经被动态地注册到了指定的IRQ位置。 +2. **中断解除共享**:通过调用 `arch_irq_disconnect_dynamic`函数,从共享的中断源中移除一个特定的ISR/arg对。此操作后,中断应该不再被视为“共享”的,因为它只剩下一个ISR/arg对。 +3. **验证中断解除共享后的状态**:检查软件ISR表(`_sw_isr_table`)中的项是否更新为剩余的ISR/arg对,确保中断现在直接连接到了剩余的ISR,而不是通过共享机制。 +4. **触发中断**:使能并触发中断,然后验证剩余的ISR是否被正确调用。 +5. **检查结果**:对比预期结果和实际调用情况,确保只有剩余的ISR/arg对被调用,其他ISR没有被误调。 + +- test_dynamic_shared_irq_write + +此函数的目的是测试动态共享中断在多个ISR/arg对注册后的行为。具体测试步骤包括: + +1. **预先设置**:与上一个测试类似,确保通过之前的设置步骤,已经有多个ISR被动态地注册到指定的IRQ。 +2. **触发中断**:使能并触发相关的两个中断(`fixture.irq1`和 `fixture.irq2`),这两个中断分别有不同的ISR/arg对注册。 +3. **延时等待**:提供足够的时间确保中断处理完成。 +4. **检查执行结果**:验证预期的ISR/arg对是否都被按顺序正确调用。这包括检查全局变量 `test_vector`与预期的结果 `result_vector`是否匹配,以此来确保每个注册的ISR都按预期执行了。 + +- nested_irq.c + +`test_nested_isr`函数主要目的是验证中断嵌套的功能。中断嵌套允许一个正在执行的中断服务例程(ISR)被更高优先级的中断打断,当高优先级的ISR处理完成后,较低优先级的ISR继续执行。这是实时操作系统(RTOS)中一个重要的特性,确保系统能够响应更紧急的任务,同时保持较低优先级任务的状态。以下是该测试函数执行的关键步骤: + +1. **初始化**:确定用于测试的两个中断线(IRQs)编号。这些中断被分配了不同的优先级,其中 `irq_line_0`具有较低的优先级,而 `irq_line_1`具有更高的优先级。 +2. **连接并启用中断**:使用 `arch_irq_connect_dynamic`函数动态地连接两个中断服务例程(`isr0`和 `isr1`)到对应的中断线,并使能这些中断。这一步确保当中断触发时,对应的ISR能够被执行。 +3. **触发较低优先级的中断**:通过 `trigger_irq`函数触发 `irq_line_0`,启动 `isr0`的执行。在 `isr0`中,首先设置验证令牌 `isr0_result`,然后触发更高优先级的 `irq_line_1`,启动 `isr1`的执行。 +4. **执行较高优先级的ISR**:在 `isr1`中,设置验证令牌 `isr1_result`并返回。此时,由于 `isr1`具有更高的优先级,`isr0`的执行将被暂停,直到 `isr1`执行完成。 +5. **中断嵌套验证**:在 `isr0`中继续执行,验证 `isr1_result`令牌确保 `isr1`已经成功执行。然后,`isr0`完成剩余的处理并返回。 +6. **最终结果验证**:在测试线程中,验证 `isr0_result`令牌以确保 `isr0`也已经成功执行。 + +### prevent_irq.c + +`test_prevent_interruption`函数的主要目的是验证内核是否能够有效地阻止中断的发生。这是通过锁定中断并进行忙等待(busy-wait)来实现的,以检验系统计时器中断在中断锁定期间是否被处理;此外,这个测试还验证了在解锁中断后系统计时器中断是否能被正确处理。以下是该测试执行的关键步骤: + +1. **锁定中断**:使用 `irq_lock`函数锁定系统中断。这一步骤是为了阻止任何中断的发生,包括系统计时器中断。 +2. **初始化并启动计时器**:通过 `k_timer_init`和 `k_timer_start`函数初始化并启动一个计时器。计时器在指定的延迟后应触发 `timer_handler`函数。 +3. **忙等待**:在中断锁定的状态下执行忙等待一段时间。如果中断没有被锁定,这段时间内计时器的中断应该会触发。但由于中断被锁定,我们预期 `timer_handler`中设置的 `handler_result`不会在此期间被更新。 +4. **验证计时器中断是否被锁定**:检查 `handler_result`变量,确认在中断锁定期间计时器的中断处理程序是否没有被执行。如果 `handler_result`在忙等待期间未被更新,则表明计时器中断未被处理,验证了中断锁定的有效性。 +5. **解锁中断**:使用 `irq_unlock`函数解锁中断。此操作后,之前锁定的中断应该能够被处理。 +6. **再次忙等待**:在解锁中断后再次执行忙等待,以便系统有机会处理任何挂起的中断,特别是我们之前启动的计时器中断。 +7. **验证计时器中断是否被处理**:再次检查 `handler_result`变量,确认在解锁中断后计时器的中断处理程序是否被执行。如果此时 `handler_result`被更新为预期的令牌,则表明在解锁中断后计时器中断被正确处理。 + +### static_shared_irq.c + +`test_static_shared_irq_write` 函数的主要目的是验证静态共享中断(static shared interrupts)的功能。这个测试检验了是否可以成功地共享中断,即当触发了它们注册的中断时,是否能调用多个中断服务程序(ISR)及其参数(arg)对。 + +### 功能验证步骤: + +1. **中断连接**:通过 `IRQ_CONNECT`宏,函数连接了两个不同的ISR(`test_isr_0` 和 `test_isr_1`)到同一个中断号 `GIC_IRQ1`上,并连接了 `test_isr_2`到另一个中断号 `GIC_IRQ2`上。这模拟了在实际应用中,多个设备可能需要在同一个中断线上注册它们的处理函数的场景。 +2. **中断验证**:函数验证了在 `_sw_isr_table`中对应 `GIC_IRQ1`的条目是否正确地设置为了共享中断处理函数 `z_shared_isr`,并且 `GIC_IRQ2`是否保留了它的原始ISR(`test_isr_2`)。 +3. **参数验证**:进一步验证了 `_sw_isr_table`中 `GIC_IRQ1`和 `GIC_IRQ2`对应条目的参数(arg)是否正确设置。特别是对于 `GIC_IRQ1`,期望它的参数指向共享ISR表中的一个条目,而 `GIC_IRQ2`的参数应直接是 `test_isr_2`的参数。 +4. **客户端数量验证**:检查了与 `GIC_IRQ1`关联的共享ISR条目中的客户端数量是否正确地反映了两个ISR的注册,以及 `GIC_IRQ2`是否没有共享客户端,因为它没有共享ISR。 +5. **触发中断**:通过 `trigger_irq`函数触发了 `GIC_IRQ1`和 `GIC_IRQ2`对应的中断,并等待一段时间,以便ISR有机会执行。 +6. **结果验证**:通过比较 `test_vector`和 `result_vector`的值,验证了当中断被触发时,所有注册的ISR是否都被正确地执行了。这个向量数组用于记录ISR执行的验证标记,以确保它们如预期那样被调用。 +7. **中断禁用与断开连接**:最后,通过 `irq_disable`和 `arch_irq_disconnect_dynamic`函数禁用并断开之前连接的ISR,以清理测试环境。 + +## 2. 如何使用例程 + +本例程需要以下硬件, + +- E2000D/Q Demo板 + +### 2.1 硬件配置方法 + +保障串口正常工作后,不需要额外配置硬件 + +### 2.2 SDK配置方法 + +- 本例子已经提供好具体的编译指令,以下进行介绍: + +1. ``west build -b e2000q_demo_smp ./ -DOVERLAY_CONFIG=prj.conf``,编译命令, 使用west工具构建当前目录下的Zephyr项目,指定目标板为e2000q_demo_smp,并使用prj.conf配置文件覆盖默认配置 ,最终生成的执行文件将会保存在./build/zephyr/zephyr.elf +2. ``west build -t clean``, 清除缓存 ,使用west工具的clean目标清理Zephyr构建系统可能生成的任何其他临时文件或缓存 + +### 2.3 构建和下载 + +- 编译例程 + +``west build -b e2000q_demo_smp ./ -DOVERLAY_CONFIG=prj.conf`` + +- 编译主机测侧设置重启tftp服务器 + +``` +sudo service tftpd-hpa restart +``` + +- 开发板侧使用bootelf命令跳转 + +``` +setenv ipaddr 192.168.4.20 +setenv serverip 192.168.4.50 +setenv gatewayip 192.168.4.1 +tftpboot 0x90100000 zephyr.elf +bootelf -p 0x90100000 +``` + +### 2.4 输出与实验现象 + +- 所有用例均提供一系列可变配置,可在例程全局变量中修改 + +#### 2.4.1 测试动态ISR + +``` +isr_test dynamic_isr +``` + +![dynamic_isr_result](figs/README/1713410482686.png) + +#### 2.4.2 测试动态共享IRQ + +``` +isr_test dynamic_shared_irq +``` + +![dynamic_shared_irq_result](figs/README/1713410509524.png) + +#### 2.4.3 测试嵌套ISR + +``` +isr_test nested_irq +``` + +![nested_irq_result](figs/README/1713410560510.png) + +#### 2.4.4 测试中断防护 + +``` +isr_test prevent_irq +``` + +![prevent_irq_result](figs/README/1713410586639.png) + +#### 2.4.5 测试静态共享IRQ写入 + +``` +isr_test static_shared_irq +``` + +![static_shared_irq_result](figs/README/1713410631027.png) + + +## 3. 如何解决问题 + + +## 4. 修改历史记录 diff --git a/app/system/interrupt/figs/README/1713410482686.png b/app/system/interrupt/figs/README/1713410482686.png new file mode 100644 index 0000000000000000000000000000000000000000..e2e8ad01f122f40c60470247d0990ce1f13180bc Binary files /dev/null and b/app/system/interrupt/figs/README/1713410482686.png differ diff --git a/app/system/interrupt/figs/README/1713410509524.png b/app/system/interrupt/figs/README/1713410509524.png new file mode 100644 index 0000000000000000000000000000000000000000..e5fd2dfee698d1cf2b57057799c3b33f0473df71 Binary files /dev/null and b/app/system/interrupt/figs/README/1713410509524.png differ diff --git a/app/system/interrupt/figs/README/1713410560510.png b/app/system/interrupt/figs/README/1713410560510.png new file mode 100644 index 0000000000000000000000000000000000000000..56253a91f2580c86a17e6bf67ad56ed559d687ba Binary files /dev/null and b/app/system/interrupt/figs/README/1713410560510.png differ diff --git a/app/system/interrupt/figs/README/1713410586639.png b/app/system/interrupt/figs/README/1713410586639.png new file mode 100644 index 0000000000000000000000000000000000000000..53496ecd9e88a5cb536bb05e52c900de2091b587 Binary files /dev/null and b/app/system/interrupt/figs/README/1713410586639.png differ diff --git a/app/system/interrupt/figs/README/1713410631027.png b/app/system/interrupt/figs/README/1713410631027.png new file mode 100644 index 0000000000000000000000000000000000000000..378c206f86faeb12dab83cefc5b6bf004de472fe Binary files /dev/null and b/app/system/interrupt/figs/README/1713410631027.png differ diff --git a/app/system/interrupt/makefile b/app/system/interrupt/makefile new file mode 100644 index 0000000000000000000000000000000000000000..58932a4de561a1121936f453c8411ad219bc353d --- /dev/null +++ b/app/system/interrupt/makefile @@ -0,0 +1,27 @@ + + +boot_smp: + west build -b e2000q_demo_smp ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + +boot: + west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + + +boot_aarch32: + + west build -b e2000q_a32_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + + +clean: + west build -t clean + rm -rf build + + +gdb: + gdb-multiarch -x .gdbinit \ No newline at end of file diff --git a/app/system/interrupt/prj.conf b/app/system/interrupt/prj.conf new file mode 100644 index 0000000000000000000000000000000000000000..90df9c567afc6ce4bf15232042917dcb3ade21a0 --- /dev/null +++ b/app/system/interrupt/prj.conf @@ -0,0 +1,26 @@ + + + +# General config +CONFIG_MAIN_STACK_SIZE=4096 + +# schedule +CONFIG_TIMESLICE_PER_THREAD=y + + +CONFIG_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=1 +CONFIG_LOG_MODE_IMMEDIATE=y +CONFIG_HAL_STANDALONE_SDK_DEBUG=y + +# SHELL 开关 +CONFIG_SHELL=y +CONFIG_SHELL_LOG_LEVEL_INF=y +CONFIG_SHELL_STACK_SIZE=8192 + +# test +CONFIG_DYNAMIC_INTERRUPTS=y +CONFIG_MP_MAX_NUM_CPUS=1 +CONFIG_THREAD_STACK_INFO=y +CONFIG_GEN_ISR_TABLES=y +CONFIG_SHARED_INTERRUPTS=y \ No newline at end of file diff --git a/app/system/interrupt/src/dynamic_isr.c b/app/system/interrupt/src/dynamic_isr.c new file mode 100644 index 0000000000000000000000000000000000000000..7994e2241f2f77018942fe082a718372bab0627c --- /dev/null +++ b/app/system/interrupt/src/dynamic_isr.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "interrupt_util.h" + +#define ISR_DYN_ARG 0xab249cfd + +static unsigned int handler_has_run; +static uintptr_t handler_test_result; + +static void dyn_isr(const void *arg) +{ + ARG_UNUSED(arg); + handler_test_result = (uintptr_t)arg; + handler_has_run++; +} + + +extern struct _isr_table_entry _sw_isr_table[]; + +/** + * @brief Test dynamic ISR installation + * + * @ingroup kernel_interrupt_tests + * + * @details This routine locates an unused entry in the software ISR table, + * installs a dynamic ISR to the unused entry by calling the dynamic + * configured function, and verifies that the ISR is successfully installed + * by checking the software ISR table entry. + * + * @see arch_irq_connect_dynamic() + */ +void test_isr_dynamic(void) +{ + int i; + const void *argval; + + for (i = 0; i < (CONFIG_NUM_IRQS - CONFIG_GEN_IRQ_START_VECTOR); i++) { + if (_sw_isr_table[i].isr == z_irq_spurious) { + break; + } + } + + if (!(_sw_isr_table[i].isr == z_irq_spurious)) { + printk("ERROR: Could not find slot for dynamic ISR.\n"); + } else { + printk("SUCCESS: Found slot for dynamic ISR at %d.\n", CONFIG_GEN_IRQ_START_VECTOR + i); + } + + printk("installing dynamic ISR for IRQ %d\n", + CONFIG_GEN_IRQ_START_VECTOR + i); + + argval = (const void *)&i; + arch_irq_connect_dynamic(i + CONFIG_GEN_IRQ_START_VECTOR, 0, dyn_isr, + argval, 0); + + if (!(_sw_isr_table[i].isr == dyn_isr && _sw_isr_table[i].arg == argval)) { + printk("ERROR: Dynamic ISR did not install successfully.\n"); + } else { + printk("SUCCESS: Dynamic ISR installed successfully for IRQ %d.\n", CONFIG_GEN_IRQ_START_VECTOR + i); + } +} diff --git a/app/system/interrupt/src/dynamic_shared_irq.c b/app/system/interrupt/src/dynamic_shared_irq.c new file mode 100644 index 0000000000000000000000000000000000000000..22b9333dda7afb839e0bae0f47dfb71cc2b0664f --- /dev/null +++ b/app/system/interrupt/src/dynamic_shared_irq.c @@ -0,0 +1,292 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "test_shared_irq.h" + +struct shared_irq_fixture { + unsigned int irq1; + unsigned int irq2; + unsigned int irq1_table_idx; + unsigned int irq2_table_idx; + unsigned int irq_priority; +}; + +static struct shared_irq_fixture fixture; + +static void reset_test_vector(void) +{ + int i; + + for (i = 0; i < TEST_VECTOR_SIZE; i++) { + test_vector[i] = 0; + } +} + +static void dynamic_shared_irq_suite_after(void *data) +{ + ARG_UNUSED(data); + + /* note: no need to check the state of the SW ISR tables after + * all these disconnect operations. If there's something wrong + * it should be detected by dynamic_shared_irq_suite_before(). + */ + arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority, + test_isr_0, 0, 0); + arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority, + test_isr_1, (void *)1, 0); + arch_irq_disconnect_dynamic(fixture.irq2, fixture.irq_priority, + test_isr_2, (void *)2, 0); +} + +static void dummy_isr(const void *data) +{ + ARG_UNUSED(data); + + test_vector[0] = TEST_DUMMY_ISR_VAL; +} + +static unsigned int get_irq_slot(unsigned int start) +{ + unsigned int i, table_idx; + + for (i = start; i <= CONFIG_GEN_IRQ_START_VECTOR + CONFIG_NUM_IRQS - 1; i++) { + table_idx = i - CONFIG_GEN_IRQ_START_VECTOR; + + if (_sw_isr_table[table_idx].isr == &z_irq_spurious) { + test_vector[0] = 0; + + /* check to see if we can trigger this IRQ */ + arch_irq_connect_dynamic(i, IRQ_PRIORITY, dummy_isr, + NULL, 0); + irq_enable(i); + trigger_irq(i); + + /* wait a bit */ + k_busy_wait(100); + + if (test_vector[0] == TEST_DUMMY_ISR_VAL) { + /* found a valid INTID */ + irq_disable(i); + + arch_irq_disconnect_dynamic(i, IRQ_PRIORITY, + dummy_isr, NULL, 0); + return i; + } + } + } + + return TEST_INVALID_IRQ; +} + +static void *dynamic_shared_irq_suite_setup(void) +{ + fixture.irq1 = get_irq_slot(CONFIG_GEN_IRQ_START_VECTOR); + + if (fixture.irq1 == TEST_INVALID_IRQ) { + printk("ERROR: No suitable value found for irq1.\n"); + } else { + printk("SUCCESS: Suitable value found for irq1: %d\n", fixture.irq1); + } + + fixture.irq2 = get_irq_slot(fixture.irq1 + 1); + + if (fixture.irq2 == TEST_INVALID_IRQ) { + printk("ERROR: No suitable value found for irq2.\n"); + } else { + printk("SUCCESS: Suitable value found for irq2: %d\n", fixture.irq2); + } + fixture.irq_priority = IRQ_PRIORITY; + + fixture.irq1_table_idx = fixture.irq1 - CONFIG_GEN_IRQ_START_VECTOR; + fixture.irq2_table_idx = fixture.irq2 - CONFIG_GEN_IRQ_START_VECTOR; + + return NULL; +} + +static void dynamic_shared_irq_suite_before(void *data) +{ + ARG_UNUSED(data); + + arch_irq_connect_dynamic(fixture.irq1, fixture.irq_priority, + test_isr_0, 0, 0); + + + if (_sw_isr_table[fixture.irq1_table_idx].isr != test_isr_0) { + printk("ERROR: Wrong _sw_isr_table ISR at irq1.\n"); + } else { + printk("SUCCESS: Correct _sw_isr_table ISR at irq1.\n"); + } + + if (_sw_isr_table[fixture.irq1_table_idx].arg) { + printk("ERROR: Wrong _sw_isr_table argument at irq1.\n"); + } else { + printk("SUCCESS: Correct _sw_isr_table argument at irq1.\n"); + } + + if (z_shared_sw_isr_table[fixture.irq1_table_idx].client_num != 0) { + printk("ERROR: Wrong client number at irq1.\n"); + } else { + printk("SUCCESS: Correct client number at irq1.\n"); + } + + arch_irq_connect_dynamic(fixture.irq1, fixture.irq_priority, + test_isr_1, (void *)1, 0); + + + if (_sw_isr_table[fixture.irq1_table_idx].isr != z_shared_isr) { + printk("ERROR: Wrong _sw_isr_table ISR at irq1 after second registration.\n"); + } else { + printk("SUCCESS: Correct _sw_isr_table ISR at irq1 after second registration.\n"); + } + + if (_sw_isr_table[fixture.irq1_table_idx].arg != &z_shared_sw_isr_table[fixture.irq1_table_idx]) { + printk("ERROR: Wrong _sw_isr_table argument at irq1 after second registration.\n"); + } else { + printk("SUCCESS: Correct _sw_isr_table argument at irq1 after second registration.\n"); + } + + if (z_shared_sw_isr_table[fixture.irq1_table_idx].client_num != 2) { + printk("ERROR: Incorrect client number at irq1 after second registration; expected 2, got %zu.\n", z_shared_sw_isr_table[fixture.irq1_table_idx].client_num); + } else { + printk("SUCCESS: Correct client number at irq1 after second registration.\n"); + } + + + + arch_irq_connect_dynamic(fixture.irq2, fixture.irq_priority, + test_isr_2, (void *)2, 0); + + if (_sw_isr_table[fixture.irq2_table_idx].isr != test_isr_2) { + printk("ERROR: Wrong _sw_isr_table ISR at irq2.\n"); + } else { + printk("SUCCESS: Correct _sw_isr_table ISR installed for irq2.\n"); + } + + if (_sw_isr_table[fixture.irq2_table_idx].arg != (void *)2) { + printk("ERROR: Wrong _sw_isr_table argument at irq2; expected (void *)2, got %p.\n", _sw_isr_table[fixture.irq2_table_idx].arg); + } else { + printk("SUCCESS: Correct _sw_isr_table argument set for irq2.\n"); + } + + if (z_shared_sw_isr_table[fixture.irq2_table_idx].client_num != 0) { + printk("ERROR: Incorrect client number at irq2; expected 0, got %zu.\n", z_shared_sw_isr_table[fixture.irq2_table_idx].client_num); + } else { + printk("SUCCESS: Correct client number at irq2, no shared ISR clients.\n"); + } + + + reset_test_vector(); +} + +/** + * @brief Test writing to a vector with a shared interrupt + * + * @ingroup kernel_interrupt_tests + * + * @details This tests if interrupts are dynamically shared successfully + * (i.e: multiple ISR/arg pairs are called whenever the interrupt + * they were registered for is triggered). + */ +void test_dynamic_shared_irq_write(void) +{ + int i; + + irq_enable(fixture.irq1); + irq_enable(fixture.irq2); + + trigger_irq(fixture.irq1); + trigger_irq(fixture.irq2); + + /* wait 5ms before checking the results */ + k_busy_wait(5000); + + for (i = 0; i < TEST_VECTOR_SIZE; i++) { + if (test_vector[i] != result_vector[i]) { + printk("ERROR: Wrong test_vector value at %d: 0x%x vs 0x%x\n", i, test_vector[i], result_vector[i]); + } else { + printk("SUCCESS: Correct test_vector value at %d: 0x%x\n", i, test_vector[i]); + } + } + + irq_disable(fixture.irq1); + irq_disable(fixture.irq2); +} + +/** + * @brief Test writing to a vector after an ISR/arg disconnect. + * + * @ingroup kernel_interrupt_tests + * + * @details This tests if ISR/arg pairs are disconnected successfully + * and the interrupts are "unshared" whenever a single ISR/arg pair is + * left. + */ +void test_dynamic_shared_irq_disconnect_write(void) +{ + int i; + + /* remove test_isr_0/NULL pair. After this statement we expect + * irq1 to be unshared. + */ + arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority, + test_isr_0, 0, 0); + + if (_sw_isr_table[fixture.irq1_table_idx].isr != test_isr_1) { + printk("ERROR: Wrong _sw_isr_table ISR at irq1 after unsharing.\n"); + } else { + printk("SUCCESS: Correct _sw_isr_table ISR at irq1 after unsharing.\n"); + } + + if (_sw_isr_table[fixture.irq1_table_idx].arg != (void *)1) { + printk("ERROR: Wrong _sw_isr_table arg at irq1 after unsharing; expected (void *)1, got %p.\n", _sw_isr_table[fixture.irq1_table_idx].arg); + } else { + printk("SUCCESS: Correct _sw_isr_table arg at irq1 after unsharing.\n"); + } + + if (z_shared_sw_isr_table[fixture.irq1_table_idx].client_num != 0) { + printk("ERROR: Wrong client number at irq1 after unsharing; expected 0, got %zu.\n", z_shared_sw_isr_table[fixture.irq1_table_idx].client_num); + } else { + printk("SUCCESS: Correct client number at irq1 after unsharing, no clients left.\n"); + } + + irq_enable(fixture.irq1); + trigger_irq(fixture.irq1); + + /* wait 5ms before checking the results */ + k_busy_wait(5000); + + for (i = 0; i < TEST_VECTOR_SIZE; i++) { + if (i == 1) { + if (test_vector[i] != result_vector[i]) { + printk("ERROR: Wrong test_vector at %d: 0x%x vs 0x%x\n", i, test_vector[i], result_vector[i]); + } else { + printk("SUCCESS: Correct test_vector at %d.\n", i); + } + } else { + if (test_vector[i]) { + printk("ERROR: Wrong test_vector value at %d: 0x%x vs 0x%x\n", i, test_vector[i], result_vector[i]); + } else { + printk("SUCCESS: Correct test_vector value at %d, no unwanted interrupts.\n", i); + } + } + } + + irq_disable(fixture.irq1); +} + +void dynamic_shared_irq(void) +{ + + dynamic_shared_irq_suite_setup() ; + dynamic_shared_irq_suite_before(NULL) ; + test_dynamic_shared_irq_disconnect_write() ; + dynamic_shared_irq_suite_after(NULL) ; + + dynamic_shared_irq_suite_before(NULL) ; + test_dynamic_shared_irq_write() ; + dynamic_shared_irq_suite_after(NULL) ; + +} diff --git a/app/system/interrupt/src/interrupt_util.h b/app/system/interrupt/src/interrupt_util.h new file mode 100644 index 0000000000000000000000000000000000000000..158dc49b988d33081783f3a74f15f97bb2aa789e --- /dev/null +++ b/app/system/interrupt/src/interrupt_util.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef INTERRUPT_UTIL_H_ +#define INTERRUPT_UTIL_H_ + +#define MS_TO_US(ms) (ms * USEC_PER_MSEC) + + +#include +#include + +static inline void trigger_irq(int irq) +{ + printk("Triggering irq : %d\n", irq); + + /* Ensure that the specified IRQ number is a valid SGI interrupt ID */ + // zassert_true(irq <= 15, "%u is not a valid SGI interrupt ID", irq); + if (!(irq <= 15)) { + printk("ERROR: %u is not a valid SGI interrupt ID.\n", irq); + return; + } + + /* + * Generate a software generated interrupt and forward it to the + * requesting CPU. + */ +#if CONFIG_GIC_VER <= 2 + sys_write32(GICD_SGIR_TGTFILT_REQONLY | GICD_SGIR_SGIINTID(irq), + GICD_SGIR); +#else + uint64_t mpidr = GET_MPIDR(); + uint8_t aff0 = MPIDR_AFFLVL(mpidr, 0); + + gic_raise_sgi(irq, mpidr, BIT(aff0)); +#endif +} + + +#endif /* INTERRUPT_UTIL_H_ */ diff --git a/app/system/interrupt/src/main.c b/app/system/interrupt/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..8c878baff81012286924984f99ec80e7b22f8c2e --- /dev/null +++ b/app/system/interrupt/src/main.c @@ -0,0 +1,74 @@ +// Phytium is pleased to support the open source community by making Zephyr-SDK available. + +// +// Copyright (C) 2024 Phytium Technology Co., Ltd. All rights reserved. +// +// Licensed under the Apache-2.0 License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/license/apache-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. +#include +#include +#include "test_interrupt.h" +LOG_MODULE_REGISTER(app); + +int main(void) +{ + return 0; +} + + +static int cmd_dynamic_isr(const struct shell *sh, + size_t argc, char **argv) +{ + test_isr_dynamic() ; + return 0 ; +} + +static int cmd_dynamic_shared_irq(const struct shell *sh, + size_t argc, char **argv) +{ + dynamic_shared_irq() ; + return 0 ; +} + +static int cmd_nested_irq(const struct shell *sh, + size_t argc, char **argv) +{ + test_nested_isr() ; + return 0 ; +} + +static int cmd_prevent_irq(const struct shell *sh, + size_t argc, char **argv) +{ + test_prevent_interruption() ; + return 0 ; +} + + +static int cmd_static_shared_irq(const struct shell *sh, + size_t argc, char **argv) +{ + test_static_shared_irq_write() ; + return 0 ; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_isr_tests, + SHELL_CMD(dynamic_isr, NULL, "Test dynamic ISR.", cmd_dynamic_isr), + SHELL_CMD(dynamic_shared_irq, NULL, "Test dynamic shared IRQ.", cmd_dynamic_shared_irq), + SHELL_CMD(nested_irq, NULL, "Test nested ISR.", cmd_nested_irq), + SHELL_CMD(prevent_irq, NULL, "Test prevention of interruption.", cmd_prevent_irq), + SHELL_CMD(static_shared_irq, NULL, "Test static shared IRQ write.", cmd_static_shared_irq), + SHELL_SUBCMD_SET_END +); + +/* Register 'isr_test' as a subcommand of 'kernel' */ +SHELL_CMD_REGISTER(isr_test, &sub_isr_tests, "ISR testing commands", NULL); + + diff --git a/app/system/interrupt/src/nested_irq.c b/app/system/interrupt/src/nested_irq.c new file mode 100644 index 0000000000000000000000000000000000000000..dbf99cf6ec3316102a0d191e94a862cc77d22d07 --- /dev/null +++ b/app/system/interrupt/src/nested_irq.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2020 Stephanos Ioannidis + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "interrupt_util.h" + +/* + * Run the nested interrupt test for the supported platforms only. + */ +#if defined(CONFIG_CPU_CORTEX_M) || defined(CONFIG_ARC) || \ + defined(CONFIG_GIC) +#define TEST_NESTED_ISR +#endif + +#define DURATION 5 + +#define ISR0_TOKEN 0xDEADBEEF +#define ISR1_TOKEN 0xCAFEBABE + +/* + * This test uses two IRQ lines selected within the range of available IRQs on + * the target SoC. These IRQs are platform and interrupt controller-specific, + * and must be specified for every supported platform. + * + * In terms of priority, the IRQ1 is triggered from the ISR of the IRQ0; + * therefore, the priority of IRQ1 must be greater than that of the IRQ0. + */ + +/* + * For the platforms that use the ARM GIC, use the SGI (software generated + * interrupt) lines 13 and 14 for testing. + */ +#define IRQ0_LINE 14 +#define IRQ1_LINE 13 + +/* + * Choose lower prio for IRQ0 and higher priority for IRQ1 + * Minimum legal value of GICC BPR is '3' ie + * Hence choosing default priority and highest possible priority + * '0x0' as the priorities so that the preemption rule applies + * generically to all GIC versions and security states. + */ +#define IRQ0_PRIO IRQ_DEFAULT_PRIORITY +#define IRQ1_PRIO 0x0 + + + +static uint32_t irq_line_0; +static uint32_t irq_line_1; + +static uint32_t isr0_result; +static uint32_t isr1_result; + +void isr1(const void *param) +{ + ARG_UNUSED(param); + + printk("isr1: Enter\n"); + + /* Set verification token */ + isr1_result = ISR1_TOKEN; + + printk("isr1: Leave\n"); +} + +void isr0(const void *param) +{ + ARG_UNUSED(param); + + printk("isr0: Enter\n"); + + /* Set verification token */ + isr0_result = ISR0_TOKEN; + + /* Trigger nested IRQ 1 */ + trigger_irq(irq_line_1); + + /* Wait for interrupt */ + k_busy_wait(MS_TO_US(DURATION)); + + /* Validate nested ISR result token */ + if (isr1_result != ISR1_TOKEN) + { + printk("ERROR: isr1 did not execute as expected. Expected %d, got %d\n", ISR1_TOKEN, isr1_result); + } else { + printk("SUCCESS: isr1 executed as expected.\n"); + } + + printk("isr0: Leave\n"); +} + +/** + * @brief Test interrupt nesting + * + * @ingroup kernel_interrupt_tests + * + * This routine tests the interrupt nesting feature, which allows an ISR to be + * preempted in mid-execution if a higher priority interrupt is signaled. The + * lower priority ISR resumes execution once the higher priority ISR has + * completed its processing. + * + * The expected control flow for this test is as follows: + * + * 1. [thread] Trigger IRQ 0 (lower priority) + * 2. [isr0] Set ISR 0 result token and trigger IRQ 1 (higher priority) + * 3. [isr1] Set ISR 1 result token and return + * 4. [isr0] Validate ISR 1 result token and return + * 5. [thread] Validate ISR 0 result token + */ +void test_nested_isr(void) +{ + /* Resolve test IRQ line numbers */ + + irq_line_0 = IRQ0_LINE; + irq_line_1 = IRQ1_LINE; + + + /* Connect and enable test IRQs */ + arch_irq_connect_dynamic(irq_line_0, IRQ0_PRIO, isr0, NULL, 0); + arch_irq_connect_dynamic(irq_line_1, IRQ1_PRIO, isr1, NULL, 0); + + irq_enable(irq_line_0); + irq_enable(irq_line_1); + + /* Trigger test IRQ 0 */ + trigger_irq(irq_line_0); + + /* Wait for interrupt */ + k_busy_wait(MS_TO_US(DURATION)); + + /* Validate ISR result token */ + if (isr0_result != ISR0_TOKEN) + { + printk("ERROR: isr0 did not execute as expected. Expected token: %d, Got token: %d\n", ISR0_TOKEN, isr0_result); + } else { + printk("SUCCESS: isr0 executed as expected with correct token.\n"); + } +} diff --git a/app/system/interrupt/src/prevent_irq.c b/app/system/interrupt/src/prevent_irq.c new file mode 100644 index 0000000000000000000000000000000000000000..c369641cea10e8aca8aa0f1a488b6dc1fd03090d --- /dev/null +++ b/app/system/interrupt/src/prevent_irq.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "interrupt_util.h" + +#define DURATION 5 +#define HANDLER_TOKEN 0xDEADBEEF + +/* Long enough to be guaranteed a tick "should have fired" */ +#define TIMER_DELAY_US (128 * 1000000 / CONFIG_SYS_CLOCK_TICKS_PER_SEC) + +static struct k_timer irqlock_timer; +volatile uint32_t handler_result; + +static void timer_handler(struct k_timer *timer) +{ + ARG_UNUSED(timer); + handler_result = HANDLER_TOKEN; + printk("timer fired\n"); +} + +/** + * @brief Test interrupt prevention + * + * @ingroup kernel_interrupt_tests + * + * This routine tests if the kernel is capable of preventing interruption, by + * locking interrupts and busy-waiting to see if the system timer interrupt is + * serviced while interrupts are locked; in addition, this test also verifies + * that the system timer interrupt is serviced after interrupts are unlocked. + */ +// ZTEST(interrupt_feature, test_prevent_interruption) +void test_prevent_interruption(void) +{ + unsigned int key; + + printk("locking interrupts\n"); + key = irq_lock(); + + handler_result = 0; + + k_timer_init(&irqlock_timer, timer_handler, NULL); + + /* Start the timer and busy-wait for a bit with IRQs locked. The + * timer ought to have fired during this time if interrupts weren't + * locked -- but since they are, check_lock_new isn't updated. + */ + k_timer_start(&irqlock_timer, K_MSEC(DURATION), K_NO_WAIT); + k_busy_wait(TIMER_DELAY_US); + if (handler_result == HANDLER_TOKEN) { + printk("ERROR: timer interrupt was serviced while interrupts are locked.\n"); + } else { + printk("SUCCESS: Timer interrupt was not serviced while interrupts are locked, as expected.\n"); + } + + printk("unlocking interrupts\n"); + irq_unlock(key); + + k_busy_wait(TIMER_DELAY_US); + + if (handler_result != HANDLER_TOKEN) { + printk("ERROR: Timer should have fired after unlocking interrupts.\n"); + } else { + printk("SUCCESS: Timer fired after unlocking interrupts, as expected.\n"); + } + k_timer_stop(&irqlock_timer); +} + + diff --git a/app/system/interrupt/src/static_shared_irq.c b/app/system/interrupt/src/static_shared_irq.c new file mode 100644 index 0000000000000000000000000000000000000000..06b70b1a4fc63ecb963559113a3e6c13a9c1565f --- /dev/null +++ b/app/system/interrupt/src/static_shared_irq.c @@ -0,0 +1,126 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "test_shared_irq.h" + +#define GIC_IRQ1 10 +#define GIC_IRQ2 11 + +/** + * @brief Test writing to a vector using static shared interrupts. + * + * @ingroup kernel_interrupt_tests + * + * @details This tests if interrupts are statically shared successfully + * (i.e: multiple ISR/arg pairs are called whenever the interrupt they + * were registered for is triggered). + */ +// ZTEST(interrupt_feature, test_static_shared_irq_write) +void test_static_shared_irq_write(void) +{ + /* note: this test is very brittle since it requires that + * the chosen interrupt lines be unused for all of the + * testing platforms. Failing to meet this requirement + * leads to build failures due to the number of clients + * exceeding the limit. Still, it's important to test that + * the static shared interrupts work properly. As such, + * this test shall be restricted to a single platform, thus + * decreasing the risk of build errors appearing due to the + * chosen interrupts being used. + */ + + int i; + + IRQ_CONNECT(GIC_IRQ1, IRQ_PRIORITY, test_isr_0, 0, 0); + IRQ_CONNECT(GIC_IRQ1, IRQ_PRIORITY, test_isr_1, (void *)1, 0); + IRQ_CONNECT(GIC_IRQ2, IRQ_PRIORITY, test_isr_2, (void *)2, 0); + + if (_sw_isr_table[GIC_IRQ1].isr != z_shared_isr) { + printk("ERROR: Wrong _sw_isr_table ISR at GIC_IRQ1.\n"); + } else { + printk("SUCCESS: Correct _sw_isr_table ISR at GIC_IRQ1.\n"); + } + + if (_sw_isr_table[GIC_IRQ2].isr != test_isr_2) { + printk("ERROR: Wrong _sw_isr_table ISR at GIC_IRQ2.\n"); + } else { + printk("SUCCESS: Correct _sw_isr_table ISR at GIC_IRQ2.\n"); + } + + if (_sw_isr_table[GIC_IRQ1].arg != &z_shared_sw_isr_table[GIC_IRQ1]) { + printk("ERROR: Wrong _sw_isr_table arg at GIC_IRQ1.\n"); + } else { + printk("SUCCESS: Correct _sw_isr_table arg at GIC_IRQ1.\n"); + } + + if (_sw_isr_table[GIC_IRQ2].arg != (void *)2) { + printk("ERROR: Wrong _sw_isr_table arg at GIC_IRQ2.\n"); + } else { + printk("SUCCESS: Correct _sw_isr_table arg at GIC_IRQ2.\n"); + } + + if (z_shared_sw_isr_table[GIC_IRQ1].client_num != 2) { + printk("ERROR: Wrong client number for GIC_IRQ1.\n"); + } else { + printk("SUCCESS: Correct client number for GIC_IRQ1.\n"); + } + + if (z_shared_sw_isr_table[GIC_IRQ2].client_num != 0) { + printk("ERROR: Wrong client number for GIC_IRQ2.\n"); + } else { + printk("SUCCESS: No client for GIC_IRQ2, as expected.\n"); + } + + // Note: Assuming client_exists_at_index() returns a boolean indicating existence + if (!client_exists_at_index(test_isr_0, 0, GIC_IRQ1, TEST_INVALID_IDX)) { + printk("ERROR: test_isr_0 not a client for GIC_IRQ1.\n"); + } else { + printk("SUCCESS: test_isr_0 is a client for GIC_IRQ1.\n"); + } + + if (!client_exists_at_index(test_isr_1, (void *)1, GIC_IRQ1, TEST_INVALID_IDX)) { + printk("ERROR: test_isr_1 not a client for GIC_IRQ1.\n"); + } else { + printk("SUCCESS: test_isr_1 is a client for GIC_IRQ1.\n"); + } + + irq_enable(GIC_IRQ1); + irq_enable(GIC_IRQ2); + + trigger_irq(GIC_IRQ1); + trigger_irq(GIC_IRQ2); + + /* wait 5ms before checking the results */ + k_busy_wait(5000); + + for (i = 0; i < TEST_VECTOR_SIZE; i++) { + + if (test_vector[i] != result_vector[i]) { + printk("ERROR: Wrong test_vector value at %d: 0x%x vs 0x%x\n", i, test_vector[i], result_vector[i]); + } else { + printk("SUCCESS: Correct test_vector value at %d: 0x%x\n", i, test_vector[i]); + } + + + } + + irq_disable(GIC_IRQ1); + irq_disable(GIC_IRQ2); + +// #ifdef CONFIG_DYNAMIC_INTERRUPTS + /* if dynamic interrupts are enabled this will restore the _sw_isr_table + * entries for GIC_IRQ1 and GIC_IRQ2 to their default values (NULL, + * z_irq_spurious). In turn, this will increase the probability of + * dynamic_shared_irq.c's get_irq_slot() being able to find an available + * slot. + */ + arch_irq_disconnect_dynamic(GIC_IRQ1, IRQ_PRIORITY, test_isr_0, 0, 0); + arch_irq_disconnect_dynamic(GIC_IRQ1, IRQ_PRIORITY, test_isr_1, (void *)1, 0); + arch_irq_disconnect_dynamic(GIC_IRQ2, IRQ_PRIORITY, test_isr_2, (void *)2, 0); +// #endif /* CONFIG_DYNAMIC_INTERRUPTS */ + +// #endif /* CONFIG_BOARD_QEMU_CORTEX_A53 */ +} diff --git a/app/system/interrupt/src/test_interrupt.h b/app/system/interrupt/src/test_interrupt.h new file mode 100644 index 0000000000000000000000000000000000000000..86e422c43a853ba2d708bf33a3d6c154de538393 --- /dev/null +++ b/app/system/interrupt/src/test_interrupt.h @@ -0,0 +1,23 @@ +#ifndef __TEST_INTERRUPT_H__ +#define __TEST_INTERRUPT_H__ + + + +void test_isr_dynamic(void) ; + + +void dynamic_shared_irq(void) ; + + +void test_nested_isr(void) ; + + +void test_prevent_interruption(void) ; + + +void test_isr_regular(void) ; + + +void test_static_shared_irq_write(void) ; + +#endif /* __TEST_INTERRUPT_H__ */ diff --git a/app/system/interrupt/src/test_shared_irq.h b/app/system/interrupt/src/test_shared_irq.h new file mode 100644 index 0000000000000000000000000000000000000000..de65451d17422c3d52afd9dd45dde5870fbe2925 --- /dev/null +++ b/app/system/interrupt/src/test_shared_irq.h @@ -0,0 +1,69 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __TEST_SHARED_IRQ_H__ +#define __TEST_SHARED_IRQ_H__ + + +#include "interrupt_util.h" + +#define IRQ_PRIORITY 1 +#define TEST_VECTOR_SIZE 10 +#define TEST_INVALID_IDX 0xcafebabe +#define TEST_DUMMY_ISR_VAL 0xdeadbeef +#define TEST_INVALID_IRQ 0xcafebabe + +#define ISR_DEFINE(name) \ +static inline void name(const void *data) \ +{ \ + int idx = POINTER_TO_INT(data); \ + test_vector[idx] = result_vector[idx]; \ +} \ + +static uint32_t test_vector[TEST_VECTOR_SIZE] = { +}; + +static uint32_t result_vector[TEST_VECTOR_SIZE] = { + 0xdeadbeef, + 0xcafebabe, + 0x1234cafe, +}; + +ISR_DEFINE(test_isr_0); +ISR_DEFINE(test_isr_1); +ISR_DEFINE(test_isr_2); + +static inline bool client_exists_at_index(void (*routine)(const void *arg), + void *arg, int irq, size_t idx) +{ + size_t i; + struct z_shared_isr_table_entry *shared_entry; + struct z_shared_isr_client *client; + + shared_entry = &z_shared_sw_isr_table[irq]; + + if (idx == TEST_INVALID_IDX) { + for (i = 0; i < shared_entry->client_num; i++) { + client = &shared_entry->clients[i]; + + if (client->isr == routine && client->arg == arg) { + return true; + } + } + } else { + if (shared_entry->client_num <= idx) { + return false; + } + + client = &shared_entry->clients[idx]; + + return client->isr == routine && client->arg == arg; + } + + return false; +} + +#endif /* __TEST_SHARED_IRQ_H__ */ diff --git a/app/system/kernel_feature/schedule/.gdbinit b/app/system/kernel_feature/schedule/.gdbinit new file mode 100644 index 0000000000000000000000000000000000000000..f654f4cb9833c19713716be1c4bd99a8762712b1 --- /dev/null +++ b/app/system/kernel_feature/schedule/.gdbinit @@ -0,0 +1,59 @@ +# increase the default remote timeout in gdb because +# Windows libusb transactions could be slow +set remotetimeout 100000 + +# gdb connect to openocd in port 3333 +target extended-remote localhost:3333 + +# start openocd working queue, monitor followed with openocd command here +monitor init + +# force to use hardware breakpoint, otherwise use software breakpoint +# for e2000d/q, num of hardware breakpoints supposed to be 6 +monitor gdb_breakpoint_override hardware + +# load elf image +load ./build/zephyr/zephyr.elf + +# in case symbols skip load,load agin +file ./build/zephyr/zephyr.elf + +# we can break at the beginning of code by address or by symbol +#break _boot + +# add more breakpoints in application +#break JtagTouchRegisters +#break JtagTouchMemory +# break bubbleSort` +# break JtagPostSort +#break bubbleSortCXX + +# show all breakspoints we before running +# info breakpoints + +# show [-0x10 ~ +0x10 ] range of instructions when breaked +# display /10i $pc-16 + +# start running +# continue +layout src +# break eth_phytium_xmac_iface_init +# break eth_phytium_xmac_isr +# break init_app +# break bg_thread_main +# break eth_phytium_xmac_setuphandler +# break eth_phytium_xmac_send +# break eth_phytium_xmac_send +# break eth_phytium_xmac_send +# break net_config_init_by_iface +# break check_interface +# break z_arm64_sync_exc +# break sdmmc_read_scr +# break sdmmc_write_blocks +# break z_arm64_prep_c +# break sdmmc_switch +# break sdmmc_read_csd +# break sdmmc_read_blocks +# break sd_idle +# break sd_send_interface_condition +# continue \ No newline at end of file diff --git a/app/system/kernel_feature/schedule/CMakeLists.txt b/app/system/kernel_feature/schedule/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..80b4013049c70027d643ea7a1b8f8886a96c5bd3 --- /dev/null +++ b/app/system/kernel_feature/schedule/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(schedule_test) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + diff --git a/app/system/kernel_feature/schedule/README.md b/app/system/kernel_feature/schedule/README.md new file mode 100644 index 0000000000000000000000000000000000000000..73b13fe119c8d21a6f3a1211c677fd28e1b6d3b3 --- /dev/null +++ b/app/system/kernel_feature/schedule/README.md @@ -0,0 +1,165 @@ +# 调度测试 + +## 1. 例程介绍 + +### test_priority_scheduling.c + +本源代码演示和测试Zephyr 中基于优先级的抢占式调度行为。这种测试对于验证RTOS调度器在处理不同优先级的线程时的行为非常重要,确保更高优先级的线程能够按预期抢占较低优先级线程的执行。这段代码特别设计用于观察和验证多个线程按照其优先级被调度和执行的顺序。 + +### test_sched_is_preempt_thread.c + +本源代码文件 `test_sched_is_preempt_thread.c` 是专为展示和测试 Zephyr 实时操作系统 (RTOS) 中基于优先级的抢占式调度行为而设计。通过创建抢占式和合作式线程,然后锁定和解锁调度器,以及调整线程的优先级,该测试验证了 `k_is_preempt_thread()` 函数的正确性。这一过程对于确认RTOS调度器能够正确处理不同优先级的线程,确保更高优先级的线程能够按预期抢占较低优先级线程的执行至关重要。本代码通过两个关键函数——`tpreempt_ctx` 和 `tcoop_ctx` ——分别检验了抢占式和合作式线程在调度器被锁定和解锁时的行为,以及在调整线程优先级后的状态变化,从而展现了线程按其优先级被调度和执行的顺序。 + +### test_sched_priority.c + +本源代码文件是为了展示和测试Zephyr实时操作系统(RTOS)中基于优先级的抢占式调度行为而精心设计的。通过创建不同优先级的线程,并在特定条件下执行它们,这一系列测试验证了Zephyr调度器如何根据优先级和等待时间来调度线程。这些测试对于确保RTOS调度器能够正确地处理不同优先级的线程,保证更高优先级的线程可以按预期抢占较低优先级线程的执行非常关键。 + +- `test_priority_cooperative` 函数验证合作线程不会被抢占的功能。通过创建一个比当前线程优先级更高的合作线程,并验证该高优先级线程不会抢占当前较低优先级的合作线程。这一测试强调了合作线程(cooperative threads)之间的非抢占性,展示了即使在优先级较高的情况下,合作线程也不会打断其他合作线程的执行。 +- `test_priority_preemptible` 函数演示了抢占式线程的抢占性特点。首先,它通过创建一个优先级低于当前线程的抢占式线程,验证新创建的线程不会抢占当前线程。随后,通过创建一个优先级高于当前线程的抢占式线程,确保新创建的线程可以抢占当前线程。这一测试突出了抢占式线程(preemptive threads)根据优先级的抢占机制,即高优先级的抢占式线程可以打断低优先级线程的执行。 +- `test_priority_preemptible_wait_prio` 函数验证了在存在启动延迟时,抢占式线程的调度顺序。它通过创建四个优先级高于当前线程的抢占式线程,这些线程具有不同的启动延迟,来确保优先级最高且等待时间最长的线程首先被调度执行。此测试验证了调度器不仅考虑线程的优先级,还考虑了它们的等待时间,在多个线程具有相同优先级时,等待时间更长的线程将先获得执行机会。 + +### test_sched_timeslice_and_lock.c + +本源代码文件是为了展示和测试Zephyr实时操作系统(RTOS)中的多线程调度行为而设计的。通过一系列精心设计的测试案例,该文件验证了Zephyr调度器在处理合作式(cooperative)和抢占式(preemptive)线程时的行为。特别地,这些测试案例涵盖了线程的创建、睡眠、唤醒、优先级调整、调度锁定和解锁,以及时间片调度等方面。这些测试对于确保RTOS调度器能够正确地管理不同优先级的线程,保证更高优先级的线程能够按预期抢占较低优先级线程的执行至关重要。 + +- `test_yield_cooperative` 验证了合作式线程在执行 `k_yield()`后的行为。该测试通过创建三个不同优先级的线程,并使当前合作式线程让出CPU,来检查更高优先级的线程是否如预期那样被执行。 +- `test_sleep_cooperative` 检验了合作式线程调用 `k_sleep()`后的行为。该测试创建三个线程,并通过让当前线程睡眠,来验证所有就绪状态的线程是否都能够被正确地执行。 +- `test_busy_wait_cooperative` 验证了合作式线程在执行 `k_busy_wait()`期间的行为。这个测试确保了在当前线程繁忙等待时,没有其他线程被执行。 +- `test_sleep_wakeup_preemptible` 检验了使用 `k_wakeup()`唤醒处于睡眠状态的抢占式线程的行为。通过让一个线程睡眠然后被唤醒,该测试验证了唤醒操作能够按预期工作。 +- `test_pending_thread_wakeup` 验证了 `k_wakeup()`对于等待中(pending)的线程的行为。该测试确保了 `k_wakeup()`在线程等待资源时不会错误地将其唤醒。 +- `test_time_slicing_preemptible` 演示了在启用时间片(time slicing)的情况下抢占式线程的行为。这个测试验证了具有相同优先级的线程是否能够在各自的时间片内被执行。 +- `test_time_slicing_disable_preemptible` 验证了在执行 `k_busy_wait()`时时间片调度被禁用的情况。该测试确保了在忙等待期间,具有相同优先级的其他线程不会被执行。 +- `test_lock_preemptible` 通过锁定调度器,来验证在调度器锁定期间,即使创建了新的线程,这些线程也不会被执行的行为。 +- `test_unlock_preemptible` 检验了调度器从锁定状态解锁后,之前创建的线程是否能够如预期执行。 +- `test_unlock_nested_sched_lock` 通过测试嵌套的调度器锁定和解锁,来验证解锁操作是否正确地使线程恢复执行。 +- `test_wakeup_expired_timer_thread` 验证了对于因定时器超时而被唤醒的线程,`k_wakeup()`函数的行为是否符合预期。 + +### test_sched_timeslice_reset.c + +本源代码文件 `test_slice_reset.c` 是专为展示和测试Zephyr实时操作系统(RTOS)中基于时间片的抢占式线程调度行为而设计的。该文件通过 `test_slice_reset`函数,验证了当时间片被启用和禁用时,抢占式线程的行为。通过创建多个具有不同优先级和相同优先级的抢占式线程,并启用时间片,该测试确保每个线程都被赋予了预期的时间片周期来执行。 + +`test_slice_reset`函数执行以下关键步骤来完成其验证功能: + +1. **禁用时间片**:首先,禁用时间片以确保在测试设置期间,时间片不会影响线程调度。 +2. **创建线程**:随后,创建多个抢占式线程,它们具有不同的优先级。其中一部分线程被设置为具有相同的优先级,以验证相同优先级的线程在时间片启用时的行为。 +3. **启用时间片**:通过调用 `k_sched_time_slice_set`函数,启用时间片,并设置时间片的大小。这是为了测试系统是否能够根据设定的时间片大小,正确地分配CPU时间给每个线程。 +4. **执行和验证**:每个线程在其执行函数中计算它实际获得的CPU时间,并与预期的时间片大小进行比较。这一步骤验证了时间片是否被正确地应用于线程调度,以及线程是否在它们的时间片结束时被正确地抢占。 +5. **禁用时间片和恢复环境**:在测试完成后,时间片被禁用,并恢复测试前的线程优先级,以保证测试的隔离性和重复性。 + +### test_slice_scheduling.c + +这两个测试案例专门设计用于验证Zephyr实时操作系统(RTOS)中抢占式线程的时间片调度行为。这些测试对确保系统能够正确地按照预设的时间片分配CPU时间给不同的线程至关重要。 + +- test_slice_scheduling + +**功能验证:** `test_slice_scheduling` 函数验证了当时间片(time slice)被启用时,拥有相同优先级的多个抢占式线程的调度行为。该测试通过创建具有相同优先级的多个线程,然后启用时间片调度,以确保每个线程均被赋予相等的执行时间。 + +**操作步骤:** + +1. **禁用时间片:** 首先,通过设置时间片大小为0来禁用时间片,确保开始测试时时间片调度不会影响线程的执行。 +2. **创建线程:** 创建具有相同优先级的多个抢占式线程。 +3. **启用时间片:** 通过 `k_sched_time_slice_set`函数启用时间片,并设置适当的时间片大小。 +4. **执行验证:** 主线程让出CPU,允许创建的线程按时间片轮转执行。每个线程在其分配的时间片内执行,并通过信号量同步,确保它们按预期接受CPU时间。 +5. **结果验证:** 验证每个线程是否在其分配的时间片内完成执行,并且在每次迭代中,所有线程均有机会执行。 + +- test_slice_perthread + +**功能验证:** `test_slice_perthread` 函数特别针对支持每个线程独立设置时间片大小的配置(`CONFIG_TIMESLICE_PER_THREAD`)进行测试。它验证了可以为单个线程设置特定的时间片大小,并确保这个线程在其时间片到期时正确地被抢占。 + +## 2. 如何使用例程 + +本例程需要以下硬件, + +- E2000D/Q Demo板 + +### 2.1 硬件配置方法 + +保障串口正常工作后,不需要额外配置硬件 + +### 2.2 SDK配置方法 + +- 本例子已经提供好具体的编译指令,以下进行介绍: + +1. ``west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf``,编译命令, 使用west工具构建当前目录下的Zephyr项目,指定目标板为e2000q_demo,并使用prj.conf配置文件覆盖默认配置 ,最终生成的执行文件将会保存在./build/zephyr/zephyr.elf +2. ``west build -t clean``, 清除缓存 ,使用west工具的clean目标清理Zephyr构建系统可能生成的任何其他临时文件或缓存 + +### 2.3 构建和下载 + +- 编译例程 + +``west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf`` + +- 编译主机测侧设置重启tftp服务器 + +``` +sudo service tftpd-hpa restart +``` + +- 开发板侧使用bootelf命令跳转 + +``` +setenv ipaddr 192.168.4.20 +setenv serverip 192.168.4.50 +setenv gatewayip 192.168.4.1 +tftpboot 0x90100000 zephyr.elf +bootelf -p 0x90100000 +``` + +### 2.4 输出与实验现象 + +- 所有用例均提供一系列可变配置,可在例程全局变量中修改 + +#### 2.4.1 测试优先级调度 + +``` +schedule_test priority_scheduling +``` + +![priority_scheduling_result](figs/README/1713405703335.png) + +#### 2.4.2 测试当前线程是否可抢占 + +``` +schedule_test is_preempt_thread +``` + +![is_preempt_thread_result](figs/README/1713405732059.png) + +#### 2.4.3 通过优先级测试线程调度 + +``` +schedule_test priority +``` + +![priority_result](figs/README/1713405767652.png) + +#### 2.4.4 测试时间片重置 + +``` +schedule_test timeslice_reset +``` + +![timeslice_reset_result](figs/README/1713405796614.png) + +#### 2.4.5 测试时间片调度 + +``` +schedule_test slice_scheduling +``` + +![slice_scheduling_result](figs/README/1713405833265.png) + +#### 2.4.6 测试时间片和锁 + +``` +schedule_test timeslice_lock +``` + +![timeslice_lock_result](figs/README/1713405866527.png) + + +## 3. 如何解决问题 + + + +## 4. 修改历史记录 diff --git a/app/system/kernel_feature/schedule/figs/README/1713405703335.png b/app/system/kernel_feature/schedule/figs/README/1713405703335.png new file mode 100644 index 0000000000000000000000000000000000000000..3f6297b8d412f4a6aa912518f811a767f701c463 Binary files /dev/null and b/app/system/kernel_feature/schedule/figs/README/1713405703335.png differ diff --git a/app/system/kernel_feature/schedule/figs/README/1713405732059.png b/app/system/kernel_feature/schedule/figs/README/1713405732059.png new file mode 100644 index 0000000000000000000000000000000000000000..a4741209333d8c4635654139088af6d4f1c75bcf Binary files /dev/null and b/app/system/kernel_feature/schedule/figs/README/1713405732059.png differ diff --git a/app/system/kernel_feature/schedule/figs/README/1713405767652.png b/app/system/kernel_feature/schedule/figs/README/1713405767652.png new file mode 100644 index 0000000000000000000000000000000000000000..9f12925b1d3c7d7cfc2edadabfd47aa9f6f81fb3 Binary files /dev/null and b/app/system/kernel_feature/schedule/figs/README/1713405767652.png differ diff --git a/app/system/kernel_feature/schedule/figs/README/1713405796614.png b/app/system/kernel_feature/schedule/figs/README/1713405796614.png new file mode 100644 index 0000000000000000000000000000000000000000..6dbd4d8c678c07f7be620ffe9dacc9454a77c4db Binary files /dev/null and b/app/system/kernel_feature/schedule/figs/README/1713405796614.png differ diff --git a/app/system/kernel_feature/schedule/figs/README/1713405833265.png b/app/system/kernel_feature/schedule/figs/README/1713405833265.png new file mode 100644 index 0000000000000000000000000000000000000000..bdad9384d6c4a09882a76da5d706290c55960534 Binary files /dev/null and b/app/system/kernel_feature/schedule/figs/README/1713405833265.png differ diff --git a/app/system/kernel_feature/schedule/figs/README/1713405866527.png b/app/system/kernel_feature/schedule/figs/README/1713405866527.png new file mode 100644 index 0000000000000000000000000000000000000000..a2a4c83f307cdb8ae8f970f079793d389986c364 Binary files /dev/null and b/app/system/kernel_feature/schedule/figs/README/1713405866527.png differ diff --git a/app/system/kernel_feature/schedule/makefile b/app/system/kernel_feature/schedule/makefile new file mode 100644 index 0000000000000000000000000000000000000000..f6ef7cbe3349c6d66656d308703c11fe8478e35f --- /dev/null +++ b/app/system/kernel_feature/schedule/makefile @@ -0,0 +1,18 @@ +boot: + west build -b e2000q_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + +boot_aarch32: + west build -b e2000q_a32_demo ./ -DOVERLAY_CONFIG=prj.conf + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + cp ./build/zephyr/zephyr.bin /mnt/d/tftboot/ + +clean: + west build -t clean + rm -rf build + +gdb: + gdb-multiarch -x .gdbinit + + diff --git a/app/system/kernel_feature/schedule/prj.conf b/app/system/kernel_feature/schedule/prj.conf new file mode 100644 index 0000000000000000000000000000000000000000..a9c9ece0683bf7155646c0a057be77a1439a180e --- /dev/null +++ b/app/system/kernel_feature/schedule/prj.conf @@ -0,0 +1,21 @@ +# General config +CONFIG_MAIN_STACK_SIZE=4096 + +# schedule +CONFIG_TIMESLICE_PER_THREAD=y + + +CONFIG_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=1 +CONFIG_LOG_MODE_IMMEDIATE=y +# CONFIG_HAL_STANDALONE_SDK_DEBUG=y + +# CONFIG_NET_MGMT_EVENT_STACK_SIZE=4096 +# CONFIG_KERNEL_VM_BASE=0x10000000 +# 将 0 ~ 0x80000000 的地址空间作为虚拟内存使用 2G +# CONFIG_KERNEL_VM_SIZE=0x80000000 + +# SHELL 开关 +CONFIG_SHELL=y +CONFIG_SHELL_LOG_LEVEL_INF=y +CONFIG_SHELL_STACK_SIZE=8192 diff --git a/app/system/kernel_feature/schedule/src/main.c b/app/system/kernel_feature/schedule/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..d3e246215a370cbbd5026b8657c2bbb45f9ff40f --- /dev/null +++ b/app/system/kernel_feature/schedule/src/main.c @@ -0,0 +1,102 @@ + +#include +#include "test_sched.h" +LOG_MODULE_REGISTER(app); + +/* Shared threads */ +K_THREAD_STACK_DEFINE(tstack, STACK_SIZE); +K_THREAD_STACK_ARRAY_DEFINE(tstacks, MAX_NUM_THREAD, STACK_SIZE); + +void spin_for_ms(int ms) +{ + uint32_t t32 = k_uptime_get_32(); + + while (k_uptime_get_32() - t32 < ms) { + /* In the posix arch, a busy loop takes no time, so + * let's make it take some + */ + if (IS_ENABLED(CONFIG_ARCH_POSIX)) { + k_busy_wait(50); + } + } +} + + + +int main(void) +{ + return 0; +} + + +static int cmd_test_priority_scheduling(const struct shell *sh, + size_t argc, char **argv) +{ + test_priority_scheduling() ; + return 0 ; +} + +static int cmd_test_sched_is_preempt_thread(const struct shell *sh, + size_t argc, char **argv) +{ + test_sched_is_preempt_thread() ; + return 0 ; +} + +static int cmd_test_sched_priority(const struct shell *sh, + size_t argc, char **argv) +{ + test_priority_cooperative() ; + test_priority_preemptible() ; + test_priority_preemptible_wait_prio() ; + return 0 ; +} + + + +static int cmd_test_sched_timeslice_reset(const struct shell *sh, + size_t argc, char **argv) +{ + test_slice_reset() ; + return 0 ; +} + + +static int cmd_test_slice_scheduling(const struct shell *sh, + size_t argc, char **argv) +{ + test_slice_scheduling() ; + test_slice_perthread() ; + return 0 ; +} + +static int cmd_test_sched_timeslice_and_lock(const struct shell *sh, + size_t argc, char **argv) +{ + test_yield_cooperative() ; + test_sleep_cooperative() ; + test_busy_wait_cooperative() ; + test_sleep_wakeup_preemptible() ; + test_pending_thread_wakeup() ; + test_time_slicing_preemptible() ; + test_time_slicing_disable_preemptible() ; + test_lock_preemptible() ; + test_unlock_preemptible() ; + test_unlock_nested_sched_lock() ; + test_wakeup_expired_timer_thread() ; + return 0 ; +} + + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_schedule_tests, + SHELL_CMD(priority_scheduling, NULL, "Test priority scheduling.", cmd_test_priority_scheduling), + SHELL_CMD(is_preempt_thread, NULL, "Test if current thread is preemptible.", cmd_test_sched_is_preempt_thread), + SHELL_CMD(priority, NULL, "Test thread scheduling by priority.", cmd_test_sched_priority), + SHELL_CMD(timeslice_reset, NULL, "Test timeslice resetting.", cmd_test_sched_timeslice_reset), + SHELL_CMD(slice_scheduling, NULL, "Test scheduling with time slicing.", cmd_test_slice_scheduling), + SHELL_CMD(timeslice_lock, NULL, "Test time slicing and locks.", cmd_test_sched_timeslice_and_lock), + SHELL_SUBCMD_SET_END +); + +/* 注册 'schedule_test' 作为 'kernel' 子命令 */ +SHELL_CMD_REGISTER(schedule_test, &sub_schedule_tests, "Schedule testing commands", NULL); diff --git a/app/system/kernel_feature/schedule/src/test_priority_scheduling.c b/app/system/kernel_feature/schedule/src/test_priority_scheduling.c new file mode 100644 index 0000000000000000000000000000000000000000..f31fc512870eaaaac2e5192831266bec40d29d55 --- /dev/null +++ b/app/system/kernel_feature/schedule/src/test_priority_scheduling.c @@ -0,0 +1,115 @@ + +#include +#include "test_sched.h" + +/* nrf 51 has lower ram, so creating less number of threads */ +#if CONFIG_SRAM_SIZE <= 24 + #define NUM_THREAD 2 +#elif (CONFIG_SRAM_SIZE <= 32) \ + || defined(CONFIG_SOC_EMSK_EM7D) + #define NUM_THREAD 3 +#else + #define NUM_THREAD 10 +#endif +#define ITERATION_COUNT 5 +#define BASE_PRIORITY 1 + +BUILD_ASSERT(NUM_THREAD <= MAX_NUM_THREAD); + +/* Semaphore on which Ztest thread wait */ +static K_SEM_DEFINE(sema2, 0, NUM_THREAD); + +/* Semaphore on which application threads wait */ +static K_SEM_DEFINE(sema3, 0, NUM_THREAD); + +static int thread_idx; +static struct k_thread t[NUM_THREAD]; + +/* Application thread */ +/** + * @name: thread_tslice + * @msg: This function represents the body of an application thread. It prints a character representing the thread, checks if threads are executing according to their priority, and performs synchronization with other threads using semaphores. Threads cycle through printing their representation and wait for a semaphore to continue, ensuring scheduled execution. + * @param {void} *p1 represents the thread index, converted and used to determine the character to print. + * @param {void} *p2 and *p3 are unused parameters, included for compatibility with the thread creation function signature. + * @return {None} + */ +static void thread_tslice(void *p1, void *p2, void *p3) +{ + int idx = POINTER_TO_INT(p1); + + /* Print New line for last thread */ + int thread_parameter = (idx == (NUM_THREAD - 1)) ? '\n' : + (idx + 'A'); + + while (1) { + /* Printing alphabet corresponding to thread */ + printk("%c ", thread_parameter); + /* Testing if threads are executed as per priority */ + if (idx != thread_idx) + { + /* Handle error: log and potentially halt */ + printk("Unexpected thread index: %d; expected: %d", idx, thread_idx); + } + thread_idx = (thread_idx + 1) % (NUM_THREAD); + + /* Release CPU and give chance to Ztest thread to run */ + k_sem_give(&sema2); + /* Wait for release of semaphore from test_priority_scheduling thread */ + k_sem_take(&sema3, K_FOREVER); + } + +} + +/** + * @brief Check the behavior of preemptive threads with different priorities + * + * @details Create multiple threads of different priorities - all are preemptive, + * current thread is also made preemptive. Check how the threads get chance to + * execute based on their priorities + * + */ +void test_priority_scheduling(void) +{ + k_tid_t tid[NUM_THREAD]; + int old_prio = k_thread_priority_get(k_current_get()); + int count = 0; + + /* update priority for current thread */ + k_thread_priority_set(k_current_get(), + K_PRIO_PREEMPT(BASE_PRIORITY - 1)); + + /* Create Threads with different Priority */ + for (int i = 0; i < NUM_THREAD; i++) { + tid[i] = k_thread_create(&t[i], tstacks[i], STACK_SIZE, + thread_tslice, INT_TO_POINTER(i), NULL, NULL, + K_PRIO_PREEMPT(BASE_PRIORITY + i), 0, + K_NO_WAIT); + } + + while (count < ITERATION_COUNT) { + + /* Wait for each thread to complete */ + for (int i = 0; i < NUM_THREAD; i++) + { + printf("test_priority_scheduling take %d \r\n",i) ; + k_sem_take(&sema2, K_FOREVER); + } + /* Delay to give chance to last thread to run */ + k_sleep(K_MSEC(100)); + + /* Giving Chance to other threads to run */ + for (int i = 0; i < NUM_THREAD; i++) { + k_sem_give(&sema3); + } + count++; + } + + /* test case teardown */ + for (int i = 0; i < NUM_THREAD; i++) { + k_thread_abort(tid[i]); + } + /* Set priority of Main thread to its old value */ + k_thread_priority_set(k_current_get(), old_prio); + + printk("SUCCESS: test_priority_scheduling \r\n") ; +} diff --git a/app/system/kernel_feature/schedule/src/test_sched.h b/app/system/kernel_feature/schedule/src/test_sched.h new file mode 100644 index 0000000000000000000000000000000000000000..6fd04cdda032e5ea2ff195070b3f27ce56dd1e99 --- /dev/null +++ b/app/system/kernel_feature/schedule/src/test_sched.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __TEST_SCHED_H__ +#define __TEST_SCHED_H__ + +#include +#include + +#define MAX_NUM_THREAD 10 +#define STACK_SIZE (640 + CONFIG_TEST_EXTRA_STACK_SIZE) + +K_THREAD_STACK_DECLARE(tstack, STACK_SIZE); +K_THREAD_STACK_ARRAY_DECLARE(tstacks, MAX_NUM_THREAD, STACK_SIZE); +extern struct k_thread user_thread; +extern struct k_sem user_sem; + +struct thread_data { + k_tid_t tid; + int priority; + int executed; +}; + +void spin_for_ms(int ticks); + +void test_priority_preemptible_wait_prio(void) ; +void test_yield_cooperative(void); +void test_sleep_cooperative(void); +void test_busy_wait_cooperative(void); +void test_sleep_wakeup_preemptible(void); +void test_pending_thread_wakeup(void); +void test_time_slicing_preemptible(void); +void test_time_slicing_disable_preemptible(void); +void test_priority_preemptible(void) ; +void test_lock_preemptible(void); +void test_unlock_preemptible(void); +void test_priority_cooperative(void) ; +void test_unlock_nested_sched_lock(void); +void test_sched_is_preempt_thread(void); +void test_slice_reset(void); +void test_slice_scheduling(void); +void test_priority_scheduling(void); +void test_wakeup_expired_timer_thread(void); +void test_user_k_wakeup(void); +void test_user_k_is_preempt(void); +void test_k_thread_suspend_init_null(void); +void test_k_thread_resume_init_null(void); +void test_k_thread_priority_get_init_null(void); +void test_k_thread_priority_set_init_null(void); +void test_k_thread_priority_set_overmax(void); +void test_k_thread_priority_set_upgrade(void); +void test_k_wakeup_init_null(void); +void test_slice_perthread(void); + +#endif /* __TEST_SCHED_H__ */ + diff --git a/app/system/kernel_feature/schedule/src/test_sched_is_preempt_thread.c b/app/system/kernel_feature/schedule/src/test_sched_is_preempt_thread.c new file mode 100644 index 0000000000000000000000000000000000000000..7ec5b311ffa240cf60ae60032ca75980a3b2f6f0 --- /dev/null +++ b/app/system/kernel_feature/schedule/src/test_sched_is_preempt_thread.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "test_sched.h" +#include + +/* local variables */ +static struct k_thread tdata; +static struct k_sem end_sema; + + +/** + * @name: tpreempt_ctx + * @msg: This function is designed to verify the behavior of a preemptive thread with regards to scheduler locking and thread priority adjustments. It first checks if the current thread is preemptible. Then, it locks the scheduler to make the current thread non-preemptible and checks the thread's preempt status again. After unlocking the scheduler, it verifies the thread's preempt status for a third time. Lastly, it sets the thread's priority to a cooperative level and checks the preempt status again, ensuring each state transition is as expected. + * @param {void} *p1, *p2, *p3 are unused parameters, included for compatibility with the thread function signature. + * @return {None} + */ +static void tpreempt_ctx(void *p1, void *p2, void *p3) +{ + if (k_is_preempt_thread()) { + printk("TESTPOINT PASSED: The thread's priority is in the preemptible range.\n"); + } else { + printk("TESTPOINT FAILED: The thread's priority is not in the preemptible range.\n"); + } + + k_sched_lock(); + if (k_is_preempt_thread()) { + printk("TESTPOINT FAILED: The thread should not be preemptible after locking the scheduler.\n"); + } else { + printk("TESTPOINT PASSED: The thread has locked the scheduler.\n"); + } + k_sched_unlock(); + if (k_is_preempt_thread()) { + printk("TESTPOINT PASSED: The thread has unlocked the scheduler and is preemptible again.\n"); + } else { + printk("TESTPOINT FAILED: The thread is still non-preemptible after unlocking the scheduler.\n"); + } + + k_thread_priority_set(k_current_get(), K_PRIO_COOP(1)); + if (k_is_preempt_thread()) { + printk("TESTPOINT FAILED: The thread's priority should be in the cooperative range now.\n"); + } else { + printk("TESTPOINT PASSED: The thread's priority is in the cooperative range.\n"); + } + k_sem_give(&end_sema); +} + +/** + * @name: tcoop_ctx + * @msg: This function evaluates the behavior of a cooperative thread in relation to scheduler locking and thread priority adjustments. It begins by verifying that the current thread is non-preemptible due to its cooperative nature. The function then adjusts the thread's priority to a preemptive level and re-evaluates its preempt status. Following this, it locks and unlocks the scheduler, each time checking the thread's preempt status to confirm that the operations are effectively altering the thread's preemptibility as expected. + * @param {void} *p1, *p2, *p3 are placeholders for potential future parameters, serving no purpose in the current context. + * @return {None} + */ +static void tcoop_ctx(void *p1, void *p2, void *p3) +{ + if (k_is_preempt_thread()) { + printk("TESTPOINT FAILED: The thread's priority should be in the cooperative range.\n"); + } else { + printk("TESTPOINT PASSED: The thread's priority is in the cooperative range.\n"); + } + + k_thread_priority_set(k_current_get(), K_PRIO_PREEMPT(1)); + if (k_is_preempt_thread()) { + printk("TESTPOINT PASSED: The thread's priority is now in the preemptible range.\n"); + } else { + printk("TESTPOINT FAILED: The thread's priority is not in the preemptible range after adjustment.\n"); + } + + k_sched_lock(); + if (k_is_preempt_thread()) { + printk("TESTPOINT FAILED: The thread should not be preemptible after locking the scheduler.\n"); + } else { + printk("TESTPOINT PASSED: The thread has locked the scheduler.\n"); + } + k_sched_unlock(); + if (k_is_preempt_thread()) { + printk("TESTPOINT PASSED: The thread has unlocked the scheduler and is preemptible again.\n"); + } else { + printk("TESTPOINT FAILED: The thread is still non-preemptible after unlocking the scheduler.\n"); + } + + k_sem_give(&end_sema); +} + + + +/** + * @brief Validate the correctness of k_is_preempt_thread() + * + * @details Create a preemptive thread, lock the scheduler + * and call k_is_preempt_thread(). Unlock the scheduler and + * call k_is_preempt_thread() again. Create a cooperative + * thread and lock the scheduler k_is_preempt_thread() and + * unlock the scheduler and call k_is_preempt_thread(). + * + * @see k_is_preempt_thread() + * + * @ingroup kernel_sched_tests + */ + void test_sched_is_preempt_thread(void) +{ + k_sem_init(&end_sema, 0, 1); + + /* create preempt thread */ + k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE, + tpreempt_ctx, NULL, NULL, NULL, + K_PRIO_PREEMPT(1), 0, K_NO_WAIT); + k_sem_take(&end_sema, K_FOREVER); + k_thread_abort(tid); + + /* create coop thread */ + tid = k_thread_create(&tdata, tstack, STACK_SIZE, + tcoop_ctx, NULL, NULL, NULL, + K_PRIO_COOP(1), 0, K_NO_WAIT); + k_sem_take(&end_sema, K_FOREVER); + k_thread_abort(tid); + +} + diff --git a/app/system/kernel_feature/schedule/src/test_sched_priority.c b/app/system/kernel_feature/schedule/src/test_sched_priority.c new file mode 100644 index 0000000000000000000000000000000000000000..b1bd37c46cf4609ce534d614ca5638eefa5c5ca4 --- /dev/null +++ b/app/system/kernel_feature/schedule/src/test_sched_priority.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "test_sched.h" +#include +#include + + +#define THREAD_NUM 4 + +static struct k_thread tdata_prio[THREAD_NUM]; +static struct k_thread tdata; +static int last_prio; +static uint8_t tid_num[4]; +static struct k_sem sync_sema; + + +static void thread_entry(void *p1, void *p2, void *p3) +{ + last_prio = k_thread_priority_get(k_current_get()); +} + +static void thread_entry_prio(void *p1, void *p2, void *p3) +{ + static int i; + + k_sem_take(&sync_sema, K_MSEC(100)); + + tid_num[i++] = POINTER_TO_INT(p1); +} + + +/* test cases */ + +/** + * @brief Validate that the cooperative thread will + * not be preempted + * + * @details Create a cooperative thread with priority higher + * than the current cooperative thread. Make sure that the higher + * priority thread will not preempt the lower priority cooperative + * thread. + * + * @ingroup kernel_sched_tests + */ +void test_priority_cooperative(void) +{ + int old_prio = k_thread_priority_get(k_current_get()); + + /* set current thread to a negative priority */ + last_prio = -1; + k_thread_priority_set(k_current_get(), last_prio); + + /* spawn thread with higher priority */ + int spawn_prio = last_prio - 1; + + k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE, + thread_entry, NULL, NULL, NULL, + spawn_prio, 0, K_NO_WAIT); + /* checkpoint: current thread shouldn't preempted by higher thread */ + if (last_prio != k_thread_priority_get(k_current_get())) { + printk("ERROR: Current thread's priority was changed unexpectedly.\n"); + } else { + printk("PASS: Current thread's priority remains unchanged, as expected.\n"); + } + k_sleep(K_MSEC(100)); + /* checkpoint: spawned thread get executed */ + if (last_prio != spawn_prio) + { + printk("ERROR: Spawned thread's priority does not match the expected value.\n"); + } else { + printk("PASS: Spawned thread's priority matches the expected value.\n"); + } + k_thread_abort(tid); + + /* restore environment */ + k_thread_priority_set(k_current_get(), old_prio); +} + +/** + * @brief Validate preemptiveness of preemptive thread + * + * @details Create a preemptive thread which is of priority + * lower than current thread. Current thread is made has preemptive. + * Make sure newly created thread is not preempted. Now create a + * preemptive thread which is of priority higher than current + * thread. Make sure newly created thread is preempted + * + * @ingroup kernel_sched_tests + */ +void test_priority_preemptible(void) +{ + int old_prio = k_thread_priority_get(k_current_get()); + + /* set current thread to a non-negative priority */ + last_prio = 2; + k_thread_priority_set(k_current_get(), last_prio); + + int spawn_prio = last_prio - 1; + + k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE, + thread_entry, NULL, NULL, NULL, + spawn_prio, 0, K_NO_WAIT); + /* checkpoint: thread is preempted by higher thread */ + if (last_prio == spawn_prio) + { + printk("Checkpoint PASSED: Thread is preempted by higher priority thread.\n"); + } else { + printk("Checkpoint FAILED: Thread should have been preempted by higher priority thread.\n"); + } + + k_sleep(K_MSEC(100)); + k_thread_abort(tid); + + spawn_prio = last_prio + 1; + tid = k_thread_create(&tdata, tstack, STACK_SIZE, + thread_entry, NULL, NULL, NULL, + spawn_prio, 0, K_NO_WAIT); + /* checkpoint: thread is not preempted by lower thread */ + if (last_prio == spawn_prio) { + printk("Checkpoint FAILED: Thread should not be preempted by lower priority thread.\n"); + } else { + printk("Checkpoint PASSED: Thread is not preempted by lower priority thread.\n"); + } + k_thread_abort(tid); + + /* restore environment */ + k_thread_priority_set(k_current_get(), old_prio); +} + + +/** + * @brief Validate scheduling sequence of preemptive threads with start delay + * + * @details Create four preemptive threads which are of priority + * higher than current thread. Make sure that the highest priority + * and longest waiting thread is scheduled first. + * + * @ingroup kernel_sched_tests + */ +void test_priority_preemptible_wait_prio(void) +{ + int old_prio = k_thread_priority_get(k_current_get()); + k_tid_t tid[THREAD_NUM]; + uint8_t tid_chk[4] = { 0, 1, 2, 3 }; + + k_sem_init(&sync_sema, 0, THREAD_NUM); + + /* Ensure that this code starts running at the start of a system tick */ + k_usleep(1); + + /* set current thread to a non-negative priority */ + last_prio = K_PRIO_PREEMPT(2); + k_thread_priority_set(k_current_get(), last_prio); + + /* the highest-priority thread that has waited the longest */ + tid[0] = k_thread_create(&tdata_prio[0], tstacks[0], STACK_SIZE, + thread_entry_prio, INT_TO_POINTER(0), NULL, NULL, + K_PRIO_PREEMPT(0), 0, K_MSEC(10)); + /* the highest-priority thread that has waited the shorter */ + tid[1] = k_thread_create(&tdata_prio[1], tstacks[1], STACK_SIZE, + thread_entry_prio, INT_TO_POINTER(1), NULL, NULL, + K_PRIO_PREEMPT(0), 0, K_MSEC(20)); + /* the lowest-priority thread that has waited longest */ + tid[2] = k_thread_create(&tdata_prio[2], tstacks[2], STACK_SIZE, + thread_entry_prio, INT_TO_POINTER(2), NULL, NULL, + K_PRIO_PREEMPT(1), 0, K_MSEC(10)); + /* the lowest-priority thread that has waited shorter */ + tid[3] = k_thread_create(&tdata_prio[3], tstacks[3], STACK_SIZE, + thread_entry_prio, INT_TO_POINTER(3), NULL, NULL, + K_PRIO_PREEMPT(1), 0, K_MSEC(20)); + + /* relinquish CPU for above threads to start */ + k_sleep(K_MSEC(30)); + + for (int i = 0; i < THREAD_NUM; i++) { + k_sem_give(&sync_sema); + } + + if (memcmp(tid_num, tid_chk, 4) != 0) { + printk("ERROR: scheduling priority failed\n"); + } else { + printk("Test PASSED: scheduling priorities are correct\n"); + } + + /* test case tear down */ + for (int i = 0; i < THREAD_NUM; i++) { + k_thread_abort(tid[i]); + } + + /* restore environment */ + k_thread_priority_set(k_current_get(), old_prio); +} + diff --git a/app/system/kernel_feature/schedule/src/test_sched_timeslice_and_lock.c b/app/system/kernel_feature/schedule/src/test_sched_timeslice_and_lock.c new file mode 100644 index 0000000000000000000000000000000000000000..ff0b98774ceaa65c3afdc5e3f5e9d7d3d5011669 --- /dev/null +++ b/app/system/kernel_feature/schedule/src/test_sched_timeslice_and_lock.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "test_sched.h" +#include +#include + +#define THREADS_NUM 3 +#define DURATION K_MSEC(1) + +BUILD_ASSERT(THREADS_NUM <= MAX_NUM_THREAD); + +static struct thread_data tdata[THREADS_NUM]; +static struct k_thread tthread[THREADS_NUM]; +static int old_prio, init_prio; + +struct k_thread t; + +K_SEM_DEFINE(pend_sema, 0, 1); +K_SEM_DEFINE(timer_sema, 0, 1); +struct k_timer th_wakeup_timer; + +static void thread_entry(void *p1, void *p2, void *p3) +{ + int sleep_ms = POINTER_TO_INT(p2); + + if (sleep_ms > 0) { + k_msleep(sleep_ms); + } + + int tnum = POINTER_TO_INT(p1); + + tdata[tnum].executed = 1; +} + +static void setup_threads(void) +{ + old_prio = k_thread_priority_get(k_current_get()); + for (int i = 0; i < THREADS_NUM; i++) { + if (i == 0) { + /* spawn thread with higher priority */ + tdata[i].priority = init_prio - 1; + } else if (i == 1) { + /* spawn thread with same priority */ + tdata[i].priority = init_prio; + } else { + /* spawn thread with lower priority */ + tdata[i].priority = init_prio + 1; + } + tdata[i].executed = 0; + } + k_thread_priority_set(k_current_get(), init_prio); +} + +static void spawn_threads(int sleep_sec) +{ + for (int i = 0; i < THREADS_NUM; i++) { + tdata[i].tid = k_thread_create(&tthread[i], tstacks[i], + STACK_SIZE, thread_entry, + INT_TO_POINTER(i), + INT_TO_POINTER(sleep_sec), + NULL, tdata[i].priority, 0, + K_NO_WAIT); + } +} + +static void teardown_threads(void) +{ + for (int i = 0; i < THREADS_NUM; i++) { + k_thread_abort(tdata[i].tid); + } + k_thread_priority_set(k_current_get(), old_prio); +} + +static void timer_handler(struct k_timer *timer) +{ + ARG_UNUSED(timer); + k_sem_give(&timer_sema); +} + +static void thread_handler(void *p1, void *p2, void *p3) +{ + k_timer_init(&th_wakeup_timer, timer_handler, NULL); + k_timer_start(&th_wakeup_timer, DURATION, K_NO_WAIT); +} + +/* test cases */ + +/** + * @brief Validate the behavior of cooperative thread + * when it yields + * + * @ingroup kernel_sched_tests + * + * @details Create 3 threads of priority -2, -1 and 0. + * Yield the main thread which is cooperative. Check + * if all the threads gets executed. + */ +void test_yield_cooperative(void) +{ + + /* set current thread to a cooperative priority */ + init_prio = -1; + setup_threads(); + + spawn_threads(0); + /* checkpoint: only higher priority thread get executed when yield */ + k_yield(); + if (!(tdata[0].executed == 1 && tdata[1].executed == 1)) { + printk("ERROR: Higher priority threads did not execute as expected.\n"); + } + else + { + printk("SUCCESS: Higher priority threads executed as expected.\n"); + } + + for (int i = 2; i < THREADS_NUM; i++) { + if (tdata[i].executed != 0) { + printk("ERROR: Lower priority thread %d executed unexpectedly.\n", i); + } + else + { + printk("SUCCESS: Lower priority thread %d did not execute as expected.\n", i); + } + } + /* restore environment */ + teardown_threads(); +} +/** + * @brief Validate the behavior of cooperative thread when it sleeps + * + * @details Create 3 threads of priority -2, -1 and 0. Put the main + * thread in timeout queue by calling k_sleep() which is cooperative. + * Check if all the threads gets executed. + * + * @ingroup kernel_sched_tests + */ +void test_sleep_cooperative(void) +{ + /* set current thread to a cooperative priority */ + init_prio = -1; + setup_threads(); + + spawn_threads(0); + /* checkpoint: all ready threads get executed when k_sleep */ + k_sleep(K_MSEC(100)); + for (int i = 0; i < THREADS_NUM; i++) { + if (tdata[i].executed != 1) + { + printk("ERROR: Thread %d did not execute as expected.\n", i); + } + else + { + printk("SUCCESS: All threads executed after k_sleep as expected.\n"); + } + } + + /* restore environment */ + teardown_threads(); +} + +void test_busy_wait_cooperative(void) +{ + /* set current thread to a cooperative priority */ + init_prio = -1; + setup_threads(); + + spawn_threads(0); + k_busy_wait(100000); /* 100 ms */ + /* checkpoint: No other threads get executed */ + for (int i = 0; i < THREADS_NUM; i++) { + if (tdata[i].executed != 0) { + printk("ERROR: Thread %d executed unexpectedly during busy wait.\n", i); + } + else + { + printk("SUCCESS: No threads executed during busy wait as expected.\n"); + } + } + /* restore environment */ + teardown_threads(); +} + +/** + * @brief Validate k_wakeup() + * + * @details Create 3 threads with main thread with priority 0 + * and other threads with -1, 0 ,+1 priority. Now -1 priority + * thread gets executed and it is made to sleep for 10 sec. + * Now, wake up the -1 priority thread and check if it starts + * executing. + * + * @see k_wakeup() + * + * @ingroup kernel_sched_tests + */ +void test_sleep_wakeup_preemptible(void) +{ + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + spawn_threads(10 * 1000); /* 10 second */ + /* checkpoint: lower threads not executed, high threads are in sleep */ + for (int i = 0; i < THREADS_NUM; i++) { + if (tdata[i].executed != 0) + { + printk("ERROR: Thread %d executed unexpectedly before wakeup.\n", i); + } + else + { + printk("SUCCESS: Thread %d did not execute before wakeup as expected.\n", i); + } + } + k_wakeup(tdata[0].tid); + if (tdata[0].executed != 1) + { + printk("ERROR: Thread 0 did not execute after wakeup.\n"); + } + else + { + printk("SUCCESS: Thread 0 executed after wakeup as expected.\n"); + } + /* restore environment */ + teardown_threads(); +} + +static int executed; +static void coop_thread(void *p1, void *p2, void *p3) +{ + k_sem_take(&pend_sema, K_MSEC(100)); + executed = 1; +} + +/** + * @brief Verify k_wakeup() behavior on pending thread + * + * @details The test creates a cooperative thread and let + * it wait for semaphore. Then calls k_wakeup(). The k_wakeup() + * call should return gracefully without waking up the thread + * + * @see k_wakeup() + * + * @ingroup kernel_sched_tests + */ +void test_pending_thread_wakeup(void) +{ + /* Make current thread preemptible */ + k_thread_priority_set(k_current_get(), K_PRIO_PREEMPT(1)); + + /* Create a thread which waits for semaphore */ + k_tid_t tid = k_thread_create(&t, tstack, STACK_SIZE, + (k_thread_entry_t)coop_thread, + NULL, NULL, NULL, + K_PRIO_COOP(1), 0, K_NO_WAIT); + + if (executed == 1) { + printk("ERROR: The thread didn't wait for semaphore acquisition.\n"); + } + else + { + printk("SUCCESS: The thread is correctly waiting for semaphore acquisition.\n"); + } + + /* Call wakeup on pending thread */ + k_wakeup(tid); + + if (executed == 1) { + printk("ERROR: k_wakeup woke up a pending thread!\n"); + } + else + { + printk("SUCCESS: k_wakeup did not affect the pending thread as expected.\n"); + } + + k_thread_abort(tid); +} + +/** + * @brief Validate preemptive thread behavior with time slice + * + * @details Create 3 threads with -1, 0, and 1 as priority, setup + * time slice for threads with priority 0. Make sure the threads + * with equal priorities are executed in time slice. + * + * @ingroup kernel_sched_tests + */ + +void test_time_slicing_preemptible(void) +{ +#ifdef CONFIG_TIMESLICING + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + k_sched_time_slice_set(200, 0); /* 200 ms */ + spawn_threads(0); + /* checkpoint: higher priority threads get executed immediately */ + if (tdata[0].executed != 1) { + printk("ERROR: Higher priority thread did not execute immediately.\n"); + } + else + { + printk("SUCCESS: Higher priority thread executed immediately as expected.\n"); + } + + k_busy_wait(500000); /* 500 ms */ + /* checkpoint: equal priority threads get executed every time slice */ + if (tdata[1].executed != 1) { + printk("ERROR: Equal priority thread did not execute during time slice.\n"); + } + else + { + printk("SUCCESS: Equal priority thread executed during time slice as expected.\n"); + } + + for (int i = 2; i < THREADS_NUM; i++) { + if (tdata[i].executed != 0) { + printk("ERROR: Thread %d executed unexpectedly.\n", i); + } + else + { + printk("SUCCESS: Thread %d did not execute during time slice as expected.\n", i); + } + } + + /* restore environment */ + k_sched_time_slice_set(0, 0); /* disable time slice */ + teardown_threads(); +#else /* CONFIG_TIMESLICING */ + printk("Time slicing configuration is not enabled.\n"); +#endif /* CONFIG_TIMESLICING */ +} + +/** + * @brief Check the behavior of preemptive thread with k_busy_wait() + * + * @details Create 3 threads with -1, 0, and 1 as priority, + * setup time slice for threads with priority 0. Make sure the + * threads with equal priorities are executed in time slice. + * Also run k_busy_wait() for 5 secs and check if other threads + * are not executed at that time. + * + * @see k_busy_wait() + * + * @ingroup kernel_sched_tests + */ +void test_time_slicing_disable_preemptible(void) +{ +#ifdef CONFIG_TIMESLICING + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + spawn_threads(0); + + /* checkpoint: higher priority threads get executed immediately */ + if (tdata[0].executed != 1) { + printk("ERROR: Higher priority threads did not execute immediately.\n"); + } + else + { + printk("SUCCESS: Higher priority thread executed immediately as expected.\n"); + } + + k_busy_wait(500000); /* 500 ms */ + /* checkpoint: equal priority threads get executed every time slice */ + if (tdata[1].executed != 0) { + printk("ERROR: An equal priority thread executed during busy wait.\n"); + } + else + { + printk("SUCCESS: No equal priority threads executed during busy wait as expected.\n"); + } + + + for (int i = 2; i < THREADS_NUM; i++) { + if (tdata[i].executed != 0) { + printk("ERROR: Thread %d executed unexpectedly.\n", i); + } + else + { + printk("SUCCESS: Thread %d did not execute unexpectedly as expected.\n", i); + } + } + /* restore environment */ + teardown_threads(); +#else /* CONFIG_TIMESLICING */ + printk("CONFIG_TIMESLICING is not enabled.\n"); +#endif /* CONFIG_TIMESLICING */ +} + +/** + * @brief Lock the scheduler when preemptive threads are running + * + * @details Create 3 threads and lock the scheduler. Make sure that the + * threads are not executed. Call k_sleep() and check if the threads + * have executed. + * + * @ingroup kernel_sched_tests + */ +void test_lock_preemptible(void) +{ + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + k_sched_lock(); + spawn_threads(0); + /* do critical thing */ + k_busy_wait(100000); + /* checkpoint: all other threads not been executed */ + for (int i = 0; i < THREADS_NUM; i++) { + if (tdata[i].executed != 0) { + printk("ERROR: Thread %d executed despite the scheduler being locked.\n", i); + } + else + { + printk("SUCCESS: Scheduler lock prevented thread %d from executing as expected.\n", i); + } + } + /* make current thread unready */ + k_sleep(K_MSEC(100)); + /* checkpoint: all other threads get executed */ + for (int i = 0; i < THREADS_NUM; i++) { + if (tdata[i].executed != 1) { + printk("ERROR: Thread %d did not execute after scheduler unlock.\n", i); + } + else + { + printk("SUCCESS: Thread %d executed after scheduler unlock as expected.\n", i); + } + } + /* restore environment */ + teardown_threads(); +} + +/** + * @brief Validate k_sched_lock() and k_sched_unlock() + * + * @details Lock the scheduler and create 3 threads. Check + * that the threads are not executed. Call k_sched_unlock() + * and check if the threads have executed. + * + * @see k_sched_lock(), k_sched_unlock() + * + * @ingroup kernel_sched_tests + */ +void test_unlock_preemptible(void) +{ + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + k_sched_lock(); + spawn_threads(0); + /* do critical thing */ + k_busy_wait(100000); + + k_sched_unlock(); + + /* ensure threads of equal priority can run */ + k_yield(); + + /* checkpoint: higher and equal threads get executed */ + if (tdata[0].executed != 1 || tdata[1].executed != 1 || tdata[2].executed != 0) { + printk("ERROR: Incorrect execution state of threads after unlock.\n"); + } + else { + printk("SUCCESS: Higher and equal priority threads executed after unlock as expected.\n"); + for (int i = 0; i < THREADS_NUM; i++) { + if (tdata[i].executed == 1) { + printk("SUCCESS: Thread %d executed as expected.\n", i); + } + } + } + /* restore environment */ + teardown_threads(); +} + +/** + * @brief Validate nested k_sched_lock() and k_sched_unlock() + * + * @details In a preemptive thread, lock the scheduler twice and + * create a cooperative thread. Call k_sched_unlock() and check the + * cooperative thread haven't executed. Unlock it again to see the + * thread have executed this time. + * + * @see k_sched_lock(), k_sched_unlock() + * + * @ingroup kernel_sched_tests + */ +void test_unlock_nested_sched_lock(void) +{ + /* set current thread to a preemptible priority */ + init_prio = 0; + setup_threads(); + + /* take the scheduler lock twice */ + k_sched_lock(); + k_sched_lock(); + + /* spawn threads without wait */ + spawn_threads(0); + + /* do critical thing */ + k_busy_wait(100000); + + /* unlock once; this shouldn't let other threads to run */ + k_sched_unlock(); + + /* checkpoint: no threads get executed */ + for (int i = 0; i < THREADS_NUM; i++) { + if (tdata[i].executed != 0) { + printk("ERROR: Thread %d executed after one unlock.\n", i); + } + } + + /* unlock another; this let the higher thread to run */ + k_sched_unlock(); + + /* Ensure threads of equal priority run */ + k_yield(); + + /* checkpoint: higher threads NOT get executed */ + + if (tdata[0].executed != 1 || tdata[1].executed != 1 || tdata[2].executed != 0) { + printk("ERROR: Incorrect execution state of threads after second unlock.\n"); + } + else + { + printk("SUCCESS: Higher and equal priority threads executed after second unlock as expected.\n"); + } + + /* restore environment */ + teardown_threads(); +} + +/** + * @brief validate k_wakeup() in some corner scenario + * @details trigger a timer and after expiration of timer + * call k_wakeup(), even the thread is not in sleep state neither + * in pending state + * + * @see k_wakeup() + * + * @ingroup kernel_sched_tests + */ + +void test_wakeup_expired_timer_thread(void) +{ + k_tid_t tid = k_thread_create(&tthread[0], tstack, STACK_SIZE, + thread_handler, NULL, NULL, NULL, + K_PRIO_PREEMPT(0), 0, K_NO_WAIT); + k_sem_take(&timer_sema, K_FOREVER); + /* wakeup a thread if the timer is expired */ + k_wakeup(tid); + k_thread_abort(tid); +} + + + + + + + + + + diff --git a/app/system/kernel_feature/schedule/src/test_sched_timeslice_reset.c b/app/system/kernel_feature/schedule/src/test_sched_timeslice_reset.c new file mode 100644 index 0000000000000000000000000000000000000000..704f3cc8666a9c1d82dba505b6add328275f53d4 --- /dev/null +++ b/app/system/kernel_feature/schedule/src/test_sched_timeslice_reset.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "test_sched.h" +#include +#include + + + + +#define NUM_THREAD 3 + +BUILD_ASSERT(NUM_THREAD <= MAX_NUM_THREAD); + +/* slice size in millisecond */ +#define SLICE_SIZE 200 +/* busy for more than one slice */ +#define BUSY_MS (SLICE_SIZE + 20) +/* a half timeslice */ +#define HALF_SLICE_SIZE (SLICE_SIZE >> 1) +#define HALF_SLICE_SIZE_CYCLES \ + ((uint64_t)(HALF_SLICE_SIZE)*sys_clock_hw_cycles_per_sec() / 1000) + +/* Task switch tolerance ... */ +#if CONFIG_SYS_CLOCK_TICKS_PER_SEC >= 1000 +/* ... will not take more than 1 ms. */ +#define TASK_SWITCH_TOLERANCE (1) +#else +/* ... 1ms is faster than a tick, loosen tolerance to 1 tick */ +#define TASK_SWITCH_TOLERANCE (1000 / CONFIG_SYS_CLOCK_TICKS_PER_SEC) +#endif + +K_SEM_DEFINE(sema, 0, NUM_THREAD); +/* elapsed_slice taken by last thread */ +static uint32_t elapsed_slice; +static int thread_idx; + +static uint32_t cycles_delta(uint32_t *reftime) +{ + uint32_t now, delta; + + now = k_cycle_get_32(); + delta = now - *reftime; + *reftime = now; + + return delta; +} + +static void thread_time_slice(void *p1, void *p2, void *p3) +{ + uint32_t t = cycles_delta(&elapsed_slice); + uint32_t expected_slice_min, expected_slice_max; + uint32_t switch_tolerance_ticks = + k_ms_to_ticks_ceil32(TASK_SWITCH_TOLERANCE); + + if (thread_idx == 0) { + /* + * Thread number 0 releases CPU after HALF_SLICE_SIZE, and + * expected to switch in less than the switching tolerance. + */ + expected_slice_min = + (uint64_t)(HALF_SLICE_SIZE - TASK_SWITCH_TOLERANCE) * + sys_clock_hw_cycles_per_sec() / 1000; + expected_slice_max = + (uint64_t)(HALF_SLICE_SIZE + TASK_SWITCH_TOLERANCE) * + sys_clock_hw_cycles_per_sec() / 1000; + } else { + /* + * Other threads are sliced with tick granularity. Here, we + * also expecting task switch below the switching tolerance. + */ + expected_slice_min = + (k_ms_to_ticks_floor32(SLICE_SIZE) + - switch_tolerance_ticks) + * k_ticks_to_cyc_floor32(1); + expected_slice_max = + (k_ms_to_ticks_ceil32(SLICE_SIZE) + + switch_tolerance_ticks) + * k_ticks_to_cyc_ceil32(1); + } + + printk("thread[%d] elapsed slice: %d, expected: <%d, %d>\n", + thread_idx, t, expected_slice_min, expected_slice_max); + + + /* Before the assert, otherwise in case of fail the output + * will give the impression that the same thread ran more than + * once + */ + thread_idx = (thread_idx + 1) % NUM_THREAD; + + + if (!(t >= expected_slice_min && t <= expected_slice_max)) { + printk("ERROR: Thread[%d] timeslice out of expected range, got %u, expected <%u, %u>\n", + thread_idx, t, expected_slice_min, expected_slice_max); + } else { + printk("SUCCESS: Thread[%d] timeslice within expected range.\n", thread_idx); + } + + + /* Keep the current thread busy for more than one slice, even though, + * when timeslice used up the next thread should be scheduled in. + */ + spin_for_ms(BUSY_MS); + k_sem_give(&sema); +} + +/* test cases */ +/** + * @brief Check the behavior of preemptive threads when the + * time slice is disabled and enabled + * + * @details Create multiple preemptive threads with few different + * priorities and few with same priorities and enable the time slice. + * Ensure that each thread is given the time slice period to execute. + * + * @see k_sched_time_slice_set(), k_sem_reset(), k_cycle_get_32(), + * k_uptime_get_32() + * + * @ingroup kernel_sched_tests + */ +void test_slice_reset(void) +{ + uint32_t t32; + k_tid_t tid[NUM_THREAD]; + struct k_thread t[NUM_THREAD]; + int old_prio = k_thread_priority_get(k_current_get()); + + thread_idx = 0; + /* disable timeslice */ + k_sched_time_slice_set(0, K_PRIO_PREEMPT(0)); + + /* The slice size needs to be set in ms (which get converted + * into ticks internally), but we want to loop over a half + * slice in cycles. That requires a bit of care to be sure the + * value divides properly. + */ + uint32_t slice_ticks = k_ms_to_ticks_ceil32(SLICE_SIZE); + uint32_t half_slice_cyc = k_ticks_to_cyc_ceil32(slice_ticks / 2); + + if (slice_ticks % 2 != 0) { + uint32_t deviation = k_ticks_to_cyc_ceil32(1); + /* slice_ticks can't be divisible by two, so we add the + * (slice_ticks / 2) floating part back to half_slice_cyc. + */ + half_slice_cyc = half_slice_cyc + (deviation / 2); + } + + for (int j = 0; j < 2; j++) { + k_sem_reset(&sema); + + /* update priority for current thread */ + k_thread_priority_set(k_current_get(), K_PRIO_PREEMPT(j)); + + /* synchronize to tick boundary */ + k_usleep(1); + + /* create delayed threads with equal preemptive priority */ + for (int i = 0; i < NUM_THREAD; i++) { + tid[i] = k_thread_create(&t[i], tstacks[i], STACK_SIZE, + thread_time_slice, NULL, NULL, + NULL, K_PRIO_PREEMPT(j), 0, + K_NO_WAIT); + } + + /* enable time slice (and reset the counter!) */ + k_sched_time_slice_set(SLICE_SIZE, K_PRIO_PREEMPT(0)); + + /* initialize reference timestamp */ + cycles_delta(&elapsed_slice); + + /* current thread consumed a half timeslice */ + t32 = k_cycle_get_32(); + while (k_cycle_get_32() - t32 < half_slice_cyc) { + Z_SPIN_DELAY(50); + } + + /* relinquish CPU and wait for each thread to complete */ + k_sleep(K_TICKS(slice_ticks * (NUM_THREAD + 1))); + for (int i = 0; i < NUM_THREAD; i++) { + k_sem_take(&sema, K_FOREVER); + } + + /* test case teardown */ + for (int i = 0; i < NUM_THREAD; i++) { + k_thread_abort(tid[i]); + } + /* disable time slice */ + k_sched_time_slice_set(0, K_PRIO_PREEMPT(0)); + } + k_thread_priority_set(k_current_get(), old_prio); +} + diff --git a/app/system/kernel_feature/schedule/src/test_slice_scheduling.c b/app/system/kernel_feature/schedule/src/test_slice_scheduling.c new file mode 100644 index 0000000000000000000000000000000000000000..f8638ad9c06b5f8aeceb836956b2c9c3964cdeca --- /dev/null +++ b/app/system/kernel_feature/schedule/src/test_slice_scheduling.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2022 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "test_sched.h" +#include +#include + + + +/* nrf 51 has lower ram, so creating less number of threads */ +#if CONFIG_SRAM_SIZE <= 24 + #define NUM_THREAD 2 +#elif (CONFIG_SRAM_SIZE <= 32) \ + || defined(CONFIG_SOC_EMSK_EM7D) + #define NUM_THREAD 3 +#else + #define NUM_THREAD 10 +#endif +#define BASE_PRIORITY 0 +#define ITERATION_COUNT 5 + +BUILD_ASSERT(NUM_THREAD <= MAX_NUM_THREAD); +/* slice size in millisecond */ +#define SLICE_SIZE 200 +#define PERTHREAD_SLICE_TICKS 64 +#define TICK_SLOP 4 +/* busy for more than one slice */ +#define BUSY_MS (SLICE_SIZE + 20) +static struct k_thread t[NUM_THREAD]; + +static K_SEM_DEFINE(sema1, 0, NUM_THREAD); +/* elapsed_slice taken by last thread */ +static int64_t elapsed_slice; + +static int thread_idx; + +static void thread_tslice(void *p1, void *p2, void *p3) +{ + int idx = POINTER_TO_INT(p1); + + /* Print New line for last thread */ + int thread_parameter = (idx == (NUM_THREAD - 1)) ? '\n' : + (idx + 'A'); + + int64_t expected_slice_min = k_ticks_to_ms_floor64(k_ms_to_ticks_ceil32(SLICE_SIZE) - 1); + int64_t expected_slice_max = k_ticks_to_ms_ceil64(k_ms_to_ticks_ceil32(SLICE_SIZE) + 1); + + /* Clumsy, but need to handle the precision loss with + * submillisecond ticks. It's always possible to alias and + * produce a tdelta of "1", no matter how fast ticks are. + */ + if (expected_slice_max == expected_slice_min) { + expected_slice_max = expected_slice_min + 1; + } + + while (1) { + int64_t tdelta = k_uptime_delta(&elapsed_slice); + printf("%c", thread_parameter); + /* Test Fails if thread exceed allocated time slice or + * Any thread is scheduled out of order. + */ + if (!(tdelta >= expected_slice_min) || !(tdelta <= expected_slice_max) || !(idx == thread_idx)) { + printk("ERROR: Thread %d exceeded allocated time slice. tdelta: %lld, expected: <%lld, %lld>\n", + idx, tdelta, expected_slice_min, expected_slice_max); + // Handling error: You can choose to abort or take other actions. + } else { + printk("SUCCESS: Thread %d within time slice. tdelta: %lld, expected: <%lld, %lld>\n", + idx, tdelta, expected_slice_min, expected_slice_max); + } + + thread_idx = (thread_idx + 1) % (NUM_THREAD); + + /* Keep the current thread busy for more than one slice, + * even though, when timeslice used up the next thread + * should be scheduled in. + */ + spin_for_ms(BUSY_MS); + k_sem_give(&sema1); + } +} + +/* test cases */ + +/** + * @brief Check the behavior of preemptive threads when the + * time slice is disabled and enabled + * + * @details Create multiple preemptive threads with same priorities + * and few with same priorities and enable the time slice. + * Ensure that each thread is given the time slice period to execute. + * + * @ingroup kernel_sched_tests + */ +void test_slice_scheduling(void) +{ + k_tid_t tid[NUM_THREAD]; + int old_prio = k_thread_priority_get(k_current_get()); + int count = 0; + + thread_idx = 0; + + /* disable timeslice */ + k_sched_time_slice_set(0, K_PRIO_PREEMPT(0)); + + /* update priority for current thread */ + k_thread_priority_set(k_current_get(), K_PRIO_PREEMPT(BASE_PRIORITY)); + + /* create threads with equal preemptive priority */ + for (int i = 0; i < NUM_THREAD; i++) { + tid[i] = k_thread_create(&t[i], tstacks[i], STACK_SIZE, + thread_tslice, + INT_TO_POINTER(i), NULL, NULL, + K_PRIO_PREEMPT(BASE_PRIORITY), 0, + K_NO_WAIT); + } + + /* enable time slice */ + k_sched_time_slice_set(SLICE_SIZE, K_PRIO_PREEMPT(BASE_PRIORITY)); + + while (count < ITERATION_COUNT) { + k_uptime_delta(&elapsed_slice); + + /* Keep the current thread busy for more than one slice, + * even though, when timeslice used up the next thread + * should be scheduled in. + */ + spin_for_ms(BUSY_MS); + + /* relinquish CPU and wait for each thread to complete */ + for (int i = 0; i < NUM_THREAD; i++) { + k_sem_take(&sema1, K_FOREVER); + } + count++; + } + + + /* test case teardown */ + for (int i = 0; i < NUM_THREAD; i++) { + k_thread_abort(tid[i]); + } + + /* disable time slice */ + k_sched_time_slice_set(0, K_PRIO_PREEMPT(0)); + + k_thread_priority_set(k_current_get(), old_prio); +} + +static volatile int32_t perthread_count; +static volatile uint32_t last_cyc; +static volatile bool perthread_running; +static K_SEM_DEFINE(perthread_sem, 0, 1); + +static void slice_expired(struct k_thread *thread, void *data) +{ + if (thread != data) { + printk("ERROR: Wrong callback data pointer.\n"); + } + + uint32_t now = k_cycle_get_32(); + uint32_t dt = k_cyc_to_ticks_near32(now - last_cyc); + + if (!perthread_running) { + printk("ERROR: Thread didn't start.\n"); + } + + + if (dt < (PERTHREAD_SLICE_TICKS - TICK_SLOP)) { + printk("ERROR: Slice expired >%d ticks too soon (dt=%d).\n", TICK_SLOP, dt); + } else if ((dt - PERTHREAD_SLICE_TICKS) > TICK_SLOP) { + printk("ERROR: Slice expired >%d ticks late (dt=%d).\n", TICK_SLOP, dt); + } + + last_cyc = now; + + /* First time through, just let the slice expire and keep + * running. Second time, abort the thread and wake up the + * main test function. + */ + if (perthread_count++ != 0) { + k_thread_abort(thread); + perthread_running = false; + k_sem_give(&perthread_sem); + } +} + +static void slice_perthread_fn(void *a, void *b, void *c) +{ + ARG_UNUSED(a); ARG_UNUSED(b); ARG_UNUSED(c); + while (true) { + perthread_running = true; + k_busy_wait(10); + } +} + +void test_slice_perthread(void) +{ + if (!IS_ENABLED(CONFIG_TIMESLICE_PER_THREAD)) { + printk("TEST SKIPPED: Time slicing per thread is not enabled.\n"); + return; + } + + /* Create the thread but don't start it */ + k_thread_create(&t[0], tstacks[0], STACK_SIZE, + slice_perthread_fn, NULL, NULL, NULL, + 1, 0, K_FOREVER); + k_thread_time_slice_set(&t[0], PERTHREAD_SLICE_TICKS, slice_expired, &t[0]); + + /* Tick align, set up, then start */ + k_usleep(1); + last_cyc = k_cycle_get_32(); + k_thread_start(&t[0]); + + k_sem_take(&perthread_sem, K_FOREVER); + if (perthread_running) { + printk("ERROR: Thread failed to suspend as expected.\n"); + } else { + printk("SUCCESS: Thread suspended as expected.\n"); + } + +} + + + diff --git a/app/system/smp/smp_test/.gdbinit b/app/system/smp/smp_test/.gdbinit new file mode 100644 index 0000000000000000000000000000000000000000..68c1f81feeac3b57806cf588bcd5b28619499523 --- /dev/null +++ b/app/system/smp/smp_test/.gdbinit @@ -0,0 +1,48 @@ +# increase the default remote timeout in gdb because +# Windows libusb transactions could be slow +set remotetimeout 100000 + +# gdb connect to openocd in port 3333 +target extended-remote localhost:3333 + +# start openocd working queue, monitor followed with openocd command here +monitor init + +# force to use hardware breakpoint, otherwise use software breakpoint +# for e2000d/q, num of hardware breakpoints supposed to be 6 +monitor gdb_breakpoint_override hardware + +# load elf image +# load ./build/zephyr/zephyr.elf + +# in case symbols skip load,load agin +file ./build/zephyr/zephyr.elf + +# we can break at the beginning of code by address or by symbol +#break _boot + +# add more breakpoints in application +#break JtagTouchRegisters +#break JtagTouchMemory +# break bubbleSort` +# break JtagPostSort +#break bubbleSortCXX + +# show all breakspoints we before running +# info breakpoints + +# show [-0x10 ~ +0x10 ] range of instructions when breaked +# display /10i $pc-16 + +# start running +# continue +layout src +# break boot_banner +# break sdmmc_write_blocks +# break z_arm64_prep_c +# break sdmmc_switch +# break sdmmc_read_csd +# break sdmmc_read_blocks +# break sd_idle +# break sd_send_interface_condition +# continue \ No newline at end of file diff --git a/app/system/smp/smp_test/CMakeLists.txt b/app/system/smp/smp_test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e50ffc4fa440ce282db56609e5925eb8482bd756 --- /dev/null +++ b/app/system/smp/smp_test/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(smp) + +target_sources(app PRIVATE src/main.c + src/smp_test.c) + +target_include_directories(app PRIVATE + ${ZEPHYR_BASE}/kernel/include + ${ZEPHYR_BASE}/arch/${ARCH}/include + ) diff --git a/app/system/smp/smp_test/Kconfig b/app/system/smp/smp_test/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..606135a23f8e5b1f891878b7f59793c1a1f8a936 --- /dev/null +++ b/app/system/smp/smp_test/Kconfig @@ -0,0 +1,11 @@ +# Copyright (c) 2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +mainmenu "SMP test" + +source "Kconfig.zephyr" + +config SMP_TEST_RUN_FACTOR + int "Run factor for stressing tests, such as 'switch torture', \ + as a percentage" + default 100 diff --git a/app/system/smp/smp_test/README.md b/app/system/smp/smp_test/README.md new file mode 100644 index 0000000000000000000000000000000000000000..baff159db5c35971df598a814491a8f6733e5f16 --- /dev/null +++ b/app/system/smp/smp_test/README.md @@ -0,0 +1,98 @@ +# SMP 测试 + +## 1. 例程介绍 + +以下是对指定函数的详细描述,基于Zephyr RTOS的SMP(对称多处理)功能的测试场景。 + +- smp_tests_setup + 本函数用于SMP测试的初始化。通过让系统稍微睡眠,确保所有CPU都进入空闲线程,从而可以正确退出并运行主测试线程。这是SMP测试的通用准备步骤,确保测试环境的一致性和稳定性。 +- test_coop_resched_threads + 验证合作(cooperative)线程在SMP环境中的非抢占性。通过在多核上创建与CPU核数相等的合作线程,确保最后一个创建的线程不会抢占任何已经在运行的线程。这个测试验证了合作线程即使在多核环境下也保持其非抢占特性。 +- test_coop_switch_in_abort + 测试在SMP系统中中止(abort)线程时的合作线程切换行为。特别是,在一个线程中调用 `k_thread_abort()`时,验证是否可以正确地切换到其他合作线程执行,这是检测中止线程时上下文切换正确性的重要测试。 +- test_cpu_id_threads + 验证SMP环境中线程可以正确识别其运行的CPU核心。通过在一个线程中获取当前CPU的ID,然后在另一个线程中验证这个ID,以确保线程的CPU亲和性(affinity)和调度行为符合预期。 +- test_fatal_on_smp + 测试在SMP环境中触发致命错误(fatal error)的行为。验证当一个线程因致命错误被终止时,系统能否正确处理并确保系统稳定性。这是评估系统错误处理机制在多核环境下正确性的关键测试。 +- test_get_cpu + 验证在SMP环境中能否正确获取当前线程运行的CPU。通过在不同线程中调用获取CPU ID的函数,并比较结果,以确保线程能够准确识别其运行的处理器。 +- test_inc_concurrency + 测试SMP环境下的并发性。通过创建多个线程同时增加一个全局计数器,验证是否所有线程都能正确执行并发操作,以及全局状态的一致性和正确性。 +- test_preempt_resched_threads + 在SMP环境中验证抢占式(preemptive)线程的抢占性。通过创建不同优先级的抢占式线程,验证更高优先级的线程能否正确抢占CPU资源。 +- test_sleep_threads + 测试在SMP环境中线程睡眠(sleep)的行为。通过让线程睡眠,然后验证是否所有线程在预定时间后都能被正确唤醒和执行,以评估睡眠调度机制的正确性。 +- test_smp_coop_threads + 验证在SMP环境下,多个合作线程能否同时在不同核心上运行。这个测试通过创建多个合作线程并观察它们是否能够被分配到不同的CPU上执行,来评估系统的多核调度能力。 +- test_smp_ipi + 测试SMP环境中的内核间中断(Inter-Processor Interrupts, IPI)功能。通过触发IPI并验证是否所有CPU核心都能接收到IPI信号,来测试IPI的正确性和效率。 +- test_smp_release_global_lock + 验证在SMP系统中释放全局锁的行为。这个测试专注于全局锁在上下文切换和线程中止时的正确释放,是验证SMP系统同步机制正确性的重要测试。 +- test_smp_switch_torture + 对SMP系统中的上下文切换代码进行压力测试。通过频繁地在所有CPU核心上请求线程切换,测试系统的稳定 + +性和上下文切换的性能。 + +- test_wakeup_threads + 测试SMP环境中线程唤醒(wakeup)的行为。通过创建多个线程并使它们睡眠,然后从主线程中唤醒这些线程,以验证唤醒机制的正确性和效率。 +- test_workq_on_smp + 验证SMP环境下系统工作队列(workqueue)的行为。通过提交工作项到系统工作队列,并确保工作项能在不同的CPU核心上执行,来测试工作队列的多核调度能力。 +- test_yield_threads + 在SMP环境中测试线程主动让出(yield)CPU的行为。通过创建多个线程并使用 `k_yield()`让出CPU,然后验证是否所有线程都能按预期被调度执行,来评估调度器的公平性和响应性。 + +## 2. 如何使用例程 + +本例程需要以下硬件, + +- E2000D/Q Demo板 + +### 2.1 硬件配置方法 + +保障串口正常工作后,不需要额外配置硬件 + +### 2.2 SDK配置方法 + +- 本例子已经提供好具体的编译指令,以下进行介绍: + +1. ``west build -b e2000q_demo_smp ./ -DOVERLAY_CONFIG=prj.conf``,编译命令, 使用west工具构建当前目录下的Zephyr项目,指定目标板为e2000q_demo_smp,并使用prj.conf配置文件覆盖默认配置 ,最终生成的执行文件将会保存在./build/zephyr/zephyr.elf +2. ``west build -t clean``, 清除缓存 ,使用west工具的clean目标清理Zephyr构建系统可能生成的任何其他临时文件或缓存 + +### 2.3 构建和下载 + +- 编译例程 + +``west build -b e2000q_demo_smp ./ -DOVERLAY_CONFIG=prj.conf`` + +- 编译主机测侧设置重启tftp服务器 + +``` +sudo service tftpd-hpa restart +``` + +- 开发板侧使用bootelf命令跳转 + +``` +setenv ipaddr 192.168.4.20 +setenv serverip 192.168.4.50 +setenv gatewayip 192.168.4.1 +tftpboot 0x90100000 zephyr.elf +bootelf -p 0x90100000 +``` + +### 2.4 输出与实验现象 + +- 所有用例均提供一系列可变配置,可在例程全局变量中修改 + +### 2.4.1 smp 测试 + +``` +smp_test +``` + +![1713407267028](figs/README/1713407267028.png) + +## 3. 如何解决问题 + +1. 本例程仅可以使用开启smp的配置 + +## 4. 修改历史记录 diff --git a/app/system/smp/smp_test/figs/README/1713407267028.png b/app/system/smp/smp_test/figs/README/1713407267028.png new file mode 100644 index 0000000000000000000000000000000000000000..59a82544f7eb0a8c22762a294ed4adb04f0c25c0 Binary files /dev/null and b/app/system/smp/smp_test/figs/README/1713407267028.png differ diff --git a/app/system/smp/smp_test/makefile b/app/system/smp/smp_test/makefile new file mode 100644 index 0000000000000000000000000000000000000000..963881e47f181700f612fd7607ef2d914a1ce9b8 --- /dev/null +++ b/app/system/smp/smp_test/makefile @@ -0,0 +1,15 @@ + + +boot: + west build -b e2000q_demo_smp ./ -DOVERLAY_CONFIG=prj.conf + aarch64-none-elf-objdump -S ./build/zephyr/zephyr.elf > ./build/zephyr/zephyr.dis + cp ./build/zephyr/zephyr.elf /mnt/d/tftboot/ + + + +clean: + west build -t clean + + +gdb: + gdb-multiarch -x .gdbinit \ No newline at end of file diff --git a/app/system/smp/smp_test/prj.conf b/app/system/smp/smp_test/prj.conf new file mode 100644 index 0000000000000000000000000000000000000000..824613c790a2f76ca17806c5a1a051e9db74174f --- /dev/null +++ b/app/system/smp/smp_test/prj.conf @@ -0,0 +1,28 @@ +# Allow worker threads to capture all resources +# CONFIG_MAIN_THREAD_PRIORITY=11 + + + +# Enable assertions, stack overflow checking, etc +CONFIG_TEST=y + +# 开启shell +CONFIG_SHELL=y +# 开启启动的BANNER +CONFIG_BOOT_BANNER=y + +#debug +CONFIG_LOG_PRINTK=n +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_LOG_MAX_LEVEL=4 +CONFIG_DEBUG_INFO=y + + +# example +CONFIG_OBJ_CORE=y +CONFIG_TRACE_SCHED_IPI=y +CONFIG_SMP=y +CONFIG_TRACE_SCHED_IPI=y +CONFIG_POLL=y +# CONFIG_TRACE_SCHED_IPI=y \ No newline at end of file diff --git a/app/system/smp/smp_test/src/main.c b/app/system/smp/smp_test/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..472c8b0b4277894af604b673fc5350830533ff3b --- /dev/null +++ b/app/system/smp/smp_test/src/main.c @@ -0,0 +1,9 @@ + + +#include + + +int main(void) +{ + return 0; +} \ No newline at end of file diff --git a/app/system/smp/smp_test/src/smp_test.c b/app/system/smp/smp_test/src/smp_test.c new file mode 100644 index 0000000000000000000000000000000000000000..3377321aaeda8163b42bcefaf326a53d899acbc5 --- /dev/null +++ b/app/system/smp/smp_test/src/smp_test.c @@ -0,0 +1,1298 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include +#include +#include + +#if CONFIG_MP_MAX_NUM_CPUS < 2 +#error SMP test requires at least two CPUs! +#endif + +#define RUN_FACTOR (CONFIG_SMP_TEST_RUN_FACTOR / 100.0) + +#define T2_STACK_SIZE (2048 + CONFIG_TEST_EXTRA_STACK_SIZE) +#define STACK_SIZE (384 + CONFIG_TEST_EXTRA_STACK_SIZE) +#define DELAY_US 50000 +#define TIMEOUT 1000 +#define EQUAL_PRIORITY 1 +#define TIME_SLICE_MS 500 +#define THREAD_DELAY 1 +#define SLEEP_MS_LONG ((int)(15000 * RUN_FACTOR)) + +struct k_thread t2; +K_THREAD_STACK_DEFINE(t2_stack, T2_STACK_SIZE); + +volatile int t2_count; +volatile int sync_count = -1; + +static int main_thread_id; +static int child_thread_id; +volatile int rv; + +K_SEM_DEFINE(cpuid_sema, 0, 1); +K_SEM_DEFINE(sema, 0, 1); +static struct k_mutex smutex; +static struct k_sem smp_sem; + +#define MAX_NUM_THREADS CONFIG_MP_MAX_NUM_CPUS + +struct thread_info { + k_tid_t tid; + int executed; + int priority; + int cpu_id; +}; +static volatile struct thread_info tinfo[MAX_NUM_THREADS]; +static struct k_thread tthread[MAX_NUM_THREADS]; +static K_THREAD_STACK_ARRAY_DEFINE(tstack, MAX_NUM_THREADS, STACK_SIZE); + +static volatile int thread_started[MAX_NUM_THREADS - 1]; + +static struct k_poll_signal tsignal[MAX_NUM_THREADS]; +static struct k_poll_event tevent[MAX_NUM_THREADS]; + +static int curr_cpu(void) +{ + unsigned int k = arch_irq_lock(); + int ret = arch_curr_cpu()->id; + + arch_irq_unlock(k); + return ret; +} + +/** + * @brief SMP + * @defgroup kernel_smp_tests SMP Tests + * @ingroup all_tests + * @{ + * @} + */ + +/** + * @defgroup kernel_smp_integration_tests SMP Integration Tests + * @ingroup kernel_smp_tests + * @{ + * @} + */ + +/** + * @defgroup kernel_smp_module_tests SMP Module Tests + * @ingroup kernel_smp_tests + * @{ + * @} + */ + +static void t2_fn(void *a, void *b, void *c) +{ + ARG_UNUSED(a); + ARG_UNUSED(b); + ARG_UNUSED(c); + + t2_count = 0; + + /* This thread simply increments a counter while spinning on + * the CPU. The idea is that it will always be iterating + * faster than the other thread so long as it is fairly + * scheduled (and it's designed to NOT be fairly schedulable + * without a separate CPU!), so the main thread can always + * check its progress. + */ + while (1) { + k_busy_wait(DELAY_US); + t2_count++; + } +} + +/** + * @brief Verify SMP with 2 cooperative threads + * + * @ingroup kernel_smp_tests + * + * @details Multi processing is verified by checking whether + * 2 cooperative threads run simultaneously at different cores + */ +void test_smp_coop_threads(void) +{ + int i, ok = 1; + + if (!IS_ENABLED(CONFIG_SCHED_IPI_SUPPORTED)) { + /* The spawned thread enters an infinite loop, so it can't be + * successfully aborted via an IPI. Just skip in that + * configuration. + */ + printk("SMP functionality test skipped.\n"); + return; + } + + k_tid_t tid = k_thread_create(&t2, t2_stack, T2_STACK_SIZE, t2_fn, + NULL, NULL, NULL, + K_PRIO_COOP(2), 0, K_NO_WAIT); + + /* Wait for the other thread (on a separate CPU) to actually + * start running. We want synchrony to be as perfect as + * possible. + */ + t2_count = -1; + while (t2_count == -1) { + } + + for (i = 0; i < 10; i++) { + /* Wait slightly longer than the other thread so our + * count will always be lower + */ + k_busy_wait(DELAY_US + (DELAY_US / 8)); + + if (t2_count <= i) { + ok = 0; + break; + } + } + + k_thread_abort(tid); + k_thread_join(tid, K_FOREVER); + + if (ok) { + printk("SUCCESS: SMP functionality verified.\n"); + } else { + // The error message is already printed in the loop above. + } + +} + +static void child_fn(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p2); + ARG_UNUSED(p3); + int parent_cpu_id = POINTER_TO_INT(p1); + + if (parent_cpu_id == curr_cpu()) { + printk("ERROR: Parent isn't on other core. Expected parent on core %d, but running on core %d.\n", parent_cpu_id, curr_cpu()); + } else { + printk("SUCCESS: Parent is on a different core as expected.\n"); + } + + sync_count++; + k_sem_give(&cpuid_sema); +} + +/** + * @brief Verify CPU IDs of threads in SMP + * + * @ingroup kernel_smp_tests + * + * @details Verify whether thread running on other core is + * parent thread from child thread + */ +void test_cpu_id_threads(void) +{ + + int old_prio = k_thread_priority_get(k_current_get()); + k_thread_priority_set(k_current_get(), K_PRIO_PREEMPT(2)); + + /* Make sure idle thread runs on each core */ + k_sleep(K_MSEC(1000)); + + int parent_cpu_id = curr_cpu(); + + k_tid_t tid = k_thread_create(&t2, t2_stack, T2_STACK_SIZE, child_fn, + INT_TO_POINTER(parent_cpu_id), NULL, + NULL, K_PRIO_PREEMPT(2), 0, K_NO_WAIT); + + while (sync_count == -1) { + } + k_sem_take(&cpuid_sema, K_FOREVER); + + k_thread_abort(tid); + k_thread_join(tid, K_FOREVER); + k_thread_priority_set(k_current_get(), old_prio); +} + +static void thread_entry_fn(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p2); + ARG_UNUSED(p3); + int thread_num = POINTER_TO_INT(p1); + int count = 0; + + tinfo[thread_num].executed = 1; + tinfo[thread_num].cpu_id = curr_cpu(); + printk("curr_cpu is %d:%d \r\n",thread_num,tinfo[thread_num].cpu_id) ; + while (count++ < 5) { + k_busy_wait(DELAY_US); + } +} + +static void spin_for_threads_exit(void) +{ + unsigned int num_threads = arch_num_cpus(); + + for (int i = 0; i < num_threads - 1; i++) { + volatile uint8_t *p = &tinfo[i].tid->base.thread_state; + + while (!(*p & _THREAD_DEAD)) { + } + } + k_busy_wait(DELAY_US); +} + +static void spawn_threads(int prio, int thread_num, int equal_prio, + k_thread_entry_t thread_entry, int delay) +{ + int i; + + /* Spawn threads of priority higher than + * the previously created thread + */ + for (i = 0; i < thread_num; i++) { + if (equal_prio) { + tinfo[i].priority = prio; + } else { + /* Increase priority for each thread */ + tinfo[i].priority = prio - 1; + prio = tinfo[i].priority; + } + tinfo[i].tid = k_thread_create(&tthread[i], tstack[i], + STACK_SIZE, thread_entry, + INT_TO_POINTER(i), NULL, NULL, + tinfo[i].priority, 0, + K_MSEC(delay)); + if (delay) { + /* Increase delay for each thread */ + delay = delay + 10; + } + } +} + +static void abort_threads(int num) +{ + for (int i = 0; i < num; i++) { + k_thread_abort(tinfo[i].tid); + } + + for (int i = 0; i < num; i++) { + k_thread_join(tinfo[i].tid, K_FOREVER); + } +} + +static void cleanup_resources(void) +{ + unsigned int num_threads = arch_num_cpus(); + + for (int i = 0; i < num_threads; i++) { + tinfo[i].tid = 0; + tinfo[i].executed = 0; + tinfo[i].priority = 0; + } +} + +static void __no_optimization thread_ab_entry(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + while (true) { + } +} + +#define SPAWN_AB_PRIO K_PRIO_COOP(10) + +/** + * @brief Verify the code path when we do context switch in k_thread_abort on SMP system + * + * @ingroup kernel_smp_tests + * + * @details test logic: + * - The ztest thread has cooperative priority. + * - From ztest thread we spawn N number of cooperative threads, where N = number of CPUs. + * - The spawned cooperative are executing infinite loop (so they occupy CPU core until they are + * aborted). + * - We have (number of CPUs - 1) spawned threads run and executing infinite loop, as current CPU + * is occupied by ztest cooperative thread. Due to that the last of spawned threads is ready but + * not executing. + * - We abort spawned threads one-by-one from the ztest thread. + * - At the first k_thread_abort call the ztest thread will be preempted by the remaining spawned + * thread which has higher priority than ztest thread. + * But... k_thread_abort call should has destroyed one of the spawned threads, so ztest thread + * should have a CPU available to run on. + * - We expect that all spawned threads will be aborted successfully. + * + * This was the test case for zephyrproject-rtos/zephyr#58040 issue where this test caused system + * hang. + */ +void test_coop_switch_in_abort(void) +{ + int old_prio = k_thread_priority_get(k_current_get()); + k_thread_priority_set(k_current_get(), K_PRIO_COOP(11)); + k_tid_t tid[MAX_NUM_THREADS]; + unsigned int num_threads = arch_num_cpus(); + unsigned int i; + + + if (!(_current->base.prio < 0)) { + printk("ERROR: Test case relies on ztest thread being cooperative.\n"); + return; // Optionally handle error + } + if (!(_current->base.prio > SPAWN_AB_PRIO)) { + printk("ERROR: Spawn test needs to have higher priority than ztest thread.\n"); + return; // Optionally handle error + } + + /* Spawn N number of cooperative threads, where N = number of CPUs */ + for (i = 0; i < num_threads; i++) { + tid[i] = k_thread_create(&tthread[i], tstack[i], + STACK_SIZE, thread_ab_entry, + NULL, NULL, NULL, + SPAWN_AB_PRIO, 0, K_NO_WAIT); + } + + /* Wait for some time to let spawned threads on other cores run and start executing infinite + * loop. + */ + k_busy_wait(DELAY_US * 4); + + /* At this time we have (number of CPUs - 1) spawned threads run and executing infinite loop + * on other CPU cores, as current CPU is occupied by this ztest cooperative thread. + * Due to that the last of spawned threads is ready but not executing. + */ + + /* Abort all spawned threads one-by-one. At the first k_thread_abort call the context + * switch will happen and the last 'spawned' thread will start. + * We should successfully abort all threads. + */ + for (i = 0; i < num_threads; i++) { + k_thread_abort(tid[i]); + } + + /* Cleanup */ + for (i = 0; i < num_threads; i++) + { + if (k_thread_join(tid[i], K_FOREVER) != 0) { + printk("ERROR: Failed to join thread %d.\n", i); + } else { + printk("SUCCESS: Successfully joined thread %d.\n", i); + } + } + k_thread_priority_set(k_current_get(), old_prio); +} + +/** + * @brief Test cooperative threads non-preemption + * + * @ingroup kernel_smp_tests + * + * @details Spawn cooperative threads equal to number of cores + * supported. Main thread will already be running on 1 core. + * Check if the last thread created preempts any threads + * already running. + */ +void test_coop_resched_threads(void) +{ + int old_prio = k_thread_priority_get(k_current_get()); + unsigned int num_threads = arch_num_cpus(); + k_thread_priority_set(k_current_get(), K_PRIO_COOP(10)); + /* Spawn threads equal to number of cores, + * since we don't give up current CPU, last thread + * will not get scheduled + */ + spawn_threads(K_PRIO_COOP(10), num_threads, !EQUAL_PRIORITY, + &thread_entry_fn, THREAD_DELAY); + + /* Wait for some time to let other core's thread run */ + k_busy_wait(DELAY_US); + + + /* Reassure that cooperative thread's are not preempted + * by checking last thread's execution + * status. We know that all threads got rescheduled on + * other cores except the last one + */ + for (int i = 0; i < num_threads - 1; i++) { + if (tinfo[i].executed != 1) { + printk("ERROR: Cooperative thread %d didn't run as expected.\n", i); + // Consider adding break or handling the error as appropriate for your test setup + } + } + if (tinfo[num_threads - 1].executed != 0) { + printk("ERROR: Cooperative thread %d is preempted which is unexpected.\n", num_threads - 1); + } else { + printk("SUCCESS: Last cooperative thread was not preempted as expected.\n"); + } + + + /* Abort threads created */ + abort_threads(num_threads); + cleanup_resources(); + /* restore environment */ + k_thread_priority_set(k_current_get(), old_prio); +} + +/** + * @brief Test preemptness of preemptive thread + * + * @ingroup kernel_smp_tests + * + * @details Create preemptive thread and let it run + * on another core and verify if it gets preempted + * if another thread of higher priority is spawned + */ +void test_preempt_resched_threads(void) +{ + unsigned int num_threads = arch_num_cpus(); + + /* Spawn threads equal to number of cores, + * lower priority thread should + * be preempted by higher ones + */ + spawn_threads(K_PRIO_PREEMPT(10), num_threads, !EQUAL_PRIORITY, + &thread_entry_fn, THREAD_DELAY); + + spin_for_threads_exit(); + + for (int i = 0; i < num_threads; i++) { + if (tinfo[i].executed != 1) { + printk("ERROR: Preemptive thread %d didn't run as expected.\n", i); + } else { + printk("SUCCESS: Preemptive thread %d ran as expected.\n", i); + } + } + + /* Abort threads created */ + abort_threads(num_threads); + cleanup_resources(); +} + +/** + * @brief Validate behavior of thread when it yields + * + * @ingroup kernel_smp_tests + * + * @details Spawn cooperative threads equal to number + * of cores, so last thread would be pending, call + * yield() from main thread. Now, all threads must be + * executed + */ +void test_yield_threads(void) +{ + unsigned int num_threads = arch_num_cpus(); + + /* Spawn threads equal to the number + * of cores, so the last thread would be + * pending. + */ + spawn_threads(K_PRIO_COOP(10), num_threads, !EQUAL_PRIORITY, + &thread_entry_fn, !THREAD_DELAY); + + k_yield(); + k_busy_wait(DELAY_US); + + for (int i = 0; i < num_threads; i++) { + if (tinfo[i].executed != 1) { + printk("ERROR: Thread %d did not execute as expected.\n", i); + // Optionally, handle the failure case, e.g., abort the test or cleanup + } else { + printk("SUCCESS: Thread %d executed as expected.\n", i); + } + + } + + abort_threads(num_threads); + cleanup_resources(); +} + +/** + * @brief Test behavior of thread when it sleeps + * + * @ingroup kernel_smp_tests + * + * @details Spawn cooperative thread and call + * sleep() from main thread. After timeout, all + * threads has to be scheduled. + */ +void test_sleep_threads(void) +{ + unsigned int num_threads = arch_num_cpus(); + + spawn_threads(K_PRIO_COOP(10), num_threads, !EQUAL_PRIORITY, + &thread_entry_fn, !THREAD_DELAY); + + k_msleep(TIMEOUT); + + for (int i = 0; i < num_threads; i++) { + if (tinfo[i].executed != 1) { + printk("ERROR: Thread %d did not execute as expected.\n", i); + } else { + printk("SUCCESS: Thread %d executed as expected.\n", i); + } + } + + abort_threads(num_threads); + cleanup_resources(); +} + +static void thread_wakeup_entry(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p2); + ARG_UNUSED(p3); + int thread_num = POINTER_TO_INT(p1); + + thread_started[thread_num] = 1; + + k_msleep(DELAY_US * 1000); + + tinfo[thread_num].executed = 1; +} + +static void wakeup_on_start_thread(int tnum) +{ + int threads_started = 0, i; + + /* For each thread, spin waiting for it to first flag that + * it's going to sleep, and then that it's actually blocked + */ + for (i = 0; i < tnum; i++) { + while (thread_started[i] == 0) { + } + while (!z_is_thread_prevented_from_running(tinfo[i].tid)) { + } + } + + for (i = 0; i < tnum; i++) { + if (thread_started[i] == 1 && threads_started <= tnum) { + threads_started++; + k_wakeup(tinfo[i].tid); + } + } + + if (threads_started != tnum) { + printk("ERROR: All threads haven't started. Expected %d, got %d.\n", tnum, threads_started); + } else { + printk("SUCCESS: All %d threads have started as expected.\n", tnum); + } +} + +static void check_wokeup_threads(int tnum) +{ + int threads_woke_up = 0, i; + + /* k_wakeup() isn't synchronous, give the other CPU time to + * schedule them + */ + k_busy_wait(200000); + + for (i = 0; i < tnum; i++) { + if (tinfo[i].executed == 1 && threads_woke_up <= tnum) { + threads_woke_up++; + } + } + // zassert_equal(threads_woke_up, tnum, "Threads did not wakeup"); + if (threads_woke_up != tnum) { + printk("ERROR: Threads did not wakeup as expected. Expected %d, got %d.\n", tnum, threads_woke_up); + } else { + printk("SUCCESS: All %d threads woke up as expected.\n", tnum); + } +} + +/** + * @brief Test behavior of wakeup() in SMP case + * + * @ingroup kernel_smp_tests + * + * @details Spawn number of threads equal to number of + * remaining cores and let them sleep for a while. Call + * wakeup() of those threads from parent thread and check + * if they are all running + */ +void test_wakeup_threads(void) +{ + unsigned int num_threads = arch_num_cpus(); + + /* Spawn threads to run on all remaining cores */ + spawn_threads(K_PRIO_COOP(10), num_threads - 1, !EQUAL_PRIORITY, + &thread_wakeup_entry, !THREAD_DELAY); + + /* Check if all the threads have started, then call wakeup */ + wakeup_on_start_thread(num_threads - 1); + + /* Count threads which are woken up */ + check_wokeup_threads(num_threads - 1); + + /* Abort all threads and cleanup */ + abort_threads(num_threads - 1); + cleanup_resources(); +} + +/* a thread for testing get current cpu */ +static void thread_get_cpu_entry(void *p1, void *p2, void *p3) +{ + int bsp_id = *(int *)p1; + int cpu_id = -1; + + /* get current cpu number for running thread */ + _cpu_t *curr_cpu = arch_curr_cpu(); + + /**TESTPOINT: call arch_curr_cpu() to get cpu struct */ + if (curr_cpu == NULL) { + printk("ERROR: Test failed to get current cpu.\n"); + return; // Optional: decide if you want to return or handle differently + } else { + printk("SUCCESS: Successfully obtained current cpu struct.\n"); + } + + cpu_id = curr_cpu->id; + + + if (bsp_id == cpu_id) { + printk("ERROR: Should not be the same with our BSP. BSP ID: %d, CPU ID: %d\n", bsp_id, cpu_id); + return; // Optional: decide if you want to return or handle differently + } else { + printk("SUCCESS: Running on a different CPU than the BSP as expected. BSP ID: %d, CPU ID: %d\n", bsp_id, cpu_id); + } + + /* loop forever to ensure running on this CPU */ + while (1) { + k_busy_wait(DELAY_US); + } +} + +/** + * @brief Test get a pointer of CPU + * + * @ingroup kernel_smp_module_tests + * + * @details + * Test Objective: + * - To verify architecture layer provides a mechanism to return a pointer to the + * current kernel CPU record of the running CPU. + * We call arch_curr_cpu() and get its member, both in main and spawned thread + * separately, and compare them. They shall be different in SMP environment. + * + * Testing techniques: + * - Interface testing, function and block box testing, + * dynamic analysis and testing, + * + * Prerequisite Conditions: + * - CONFIG_SMP=y, and the HW platform must support SMP. + * + * Input Specifications: + * - N/A + * + * Test Procedure: + * -# In main thread, call arch_curr_cpu() to get it's member "id",then store it + * into a variable thread_id. + * -# Spawn a thread t2, and pass the stored thread_id to it, then call + * k_busy_wait() 50us to wait for thread run and won't be swapped out. + * -# In thread t2, call arch_curr_cpu() to get pointer of current cpu data. Then + * check if it not NULL. + * -# Store the member id via accessing pointer of current cpu data to var cpu_id. + * -# Check if cpu_id is not equaled to bsp_id that we pass into thread. + * -# Call k_busy_wait() and loop forever. + * -# In main thread, terminate the thread t2 before exit. + * + * Expected Test Result: + * - The pointer of current cpu data that we got from function call is correct. + * + * Pass/Fail Criteria: + * - Successful if the check of step 3,5 are all passed. + * - Failure if one of the check of step 3,5 is failed. + * + * Assumptions and Constraints: + * - This test using for the platform that support SMP, in our current scenario + * , only x86_64, arc and xtensa supported. + * + * @see arch_curr_cpu() + */ +static int _cpu_id; +void test_get_cpu(void) +{ + k_tid_t thread_id; + int old_prio = k_thread_priority_get(k_current_get()); + k_thread_priority_set(k_current_get(), K_PRIO_COOP(10)); + + if (!IS_ENABLED(CONFIG_SCHED_IPI_SUPPORTED)) { + /* The spawned thread enters an infinite loop, so it can't be + * successfully aborted via an IPI. Just skip in that + * configuration. + */ + printk("CONFIG_SCHED_IPI_SUPPORTED is not enabled, skipping test.\n"); + k_thread_priority_set(k_current_get(), old_prio); + return; + } + + /* get current cpu number */ + _cpu_id = arch_curr_cpu()->id; + + thread_id = k_thread_create(&t2, t2_stack, T2_STACK_SIZE, + (k_thread_entry_t)thread_get_cpu_entry, + &_cpu_id, NULL, NULL, + K_PRIO_COOP(2), + K_INHERIT_PERMS, K_NO_WAIT); + + k_busy_wait(DELAY_US); + + k_thread_abort(thread_id); + k_thread_join(thread_id, K_FOREVER); + k_thread_priority_set(k_current_get(), old_prio); +} + +#ifdef CONFIG_TRACE_SCHED_IPI +/* global variable for testing send IPI */ +static volatile int sched_ipi_has_called; + +void z_trace_sched_ipi(void) +{ + sched_ipi_has_called++; +} +#endif + +/** + * @brief Test interprocessor interrupt + * + * @ingroup kernel_smp_integration_tests + * + * @details + * Test Objective: + * - To verify architecture layer provides a mechanism to issue an interprocessor + * interrupt to all other CPUs in the system that calls the scheduler IPI. + * We simply add a hook in z_sched_ipi(), in order to check if it has been + * called once in another CPU except the caller, when arch_sched_ipi() is + * called. + * + * Testing techniques: + * - Interface testing, function and block box testing, + * dynamic analysis and testing + * + * Prerequisite Conditions: + * - CONFIG_SMP=y, and the HW platform must support SMP. + * - CONFIG_TRACE_SCHED_IPI=y was set. + * + * Input Specifications: + * - N/A + * + * Test Procedure: + * -# In main thread, given a global variable sched_ipi_has_called equaled zero. + * -# Call arch_sched_ipi() then sleep for 100ms. + * -# In z_sched_ipi() handler, increment the sched_ipi_has_called. + * -# In main thread, check the sched_ipi_has_called is not equaled to zero. + * -# Repeat step 1 to 4 for 3 times. + * + * Expected Test Result: + * - The pointer of current cpu data that we got from function call is correct. + * + * Pass/Fail Criteria: + * - Successful if the check of step 4 are all passed. + * - Failure if one of the check of step 4 is failed. + * + * Assumptions and Constraints: + * - This test using for the platform that support SMP, in our current scenario + * , only x86_64 and arc supported. + * + * @see arch_sched_ipi() + */ +#ifdef CONFIG_SCHED_IPI_SUPPORTED +void test_smp_ipi(void) +{ +#ifndef CONFIG_TRACE_SCHED_IPI + printk("CONFIG_TRACE_SCHED_IPI is not enabled, skipping test.\n"); + return; +#endif + + printk("cpu num=%d", arch_num_cpus()); + + for (int i = 0; i < 3 ; i++) { + /* issue a sched ipi to tell other CPU to run thread */ + sched_ipi_has_called = 0; + arch_sched_ipi(); + + /* Need to wait longer than we think, loaded CI + * systems need to wait for host scheduling to run the + * other CPU's thread. + */ + k_msleep(100); + + /**TESTPOINT: check if enter our IPI interrupt handler */ + if (sched_ipi_has_called == 0) { + printk("ERROR: did not receive IPI.(%d)\n", sched_ipi_has_called); + // Optionally, handle the error case, e.g., abort the test or cleanup + } else { + printk("SUCCESS: Received IPI as expected.\n"); + } + } +} +#endif + +void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) +{ + static int trigger; + + if (reason != K_ERR_KERNEL_OOPS) { + printk("wrong error reason\n"); + printk("PROJECT EXECUTION FAILED\n"); + k_fatal_halt(reason); + } + + if (trigger == 0) { + child_thread_id = curr_cpu(); + trigger++; + } else { + main_thread_id = curr_cpu(); + + /* Verify the fatal was happened on different core */ + if (main_thread_id == child_thread_id) + { + printk("ERROR: Fatal occurred on the same core.\n"); + // Optionally handle error, such as by halting the system + } else { + printk("SUCCESS: Fatal occurred on different cores as expected.\n"); + } + } +} + +void entry_oops(void *p1, void *p2, void *p3) +{ + printk("%d k_oops \r\n",curr_cpu()) ; + printk("SHOULD NEVER SEE THIS\n"); +} + +/** + * @brief Test fatal error can be triggered on different core + + * @details When CONFIG_SMP is enabled, on some multiprocessor + * platforms, exception can be triggered on different core at + * the same time. + * + * @ingroup kernel_common_tests + */ +void test_fatal_on_smp(void) +{ + /* Creat a child thread and trigger a crash */ + k_thread_create(&t2, t2_stack, T2_STACK_SIZE, entry_oops, + NULL, NULL, NULL, + K_PRIO_PREEMPT(2), 0, K_NO_WAIT); + + /* hold cpu and wait for thread trigger exception and being terminated */ + k_busy_wait(2 * DELAY_US); + + /* Verify that child thread is no longer running. We can't simply use k_thread_join here + * as we don't want to introduce reschedule point here. + */ + if (!z_is_thread_state_set(&t2, _THREAD_DEAD)) { + printk("ERROR: Child thread is still running but should be dead.\n"); + // Optional: Handle the error, e.g., abort the test or cleanup + } else { + printk("SUCCESS: Child thread is correctly marked as dead.\n"); + } +} + +static void workq_handler(struct k_work *work) +{ + child_thread_id = curr_cpu(); +} + +/** + * @brief Test system workq run on different core + + * @details When macro CONFIG_SMP is enabled, workq can be run + * on different core. + * + * @ingroup kernel_common_tests + */ +void test_workq_on_smp(void) +{ + static struct k_work work; + + k_work_init(&work, workq_handler); + + /* submit work item on system workq */ + k_work_submit(&work); + + /* Wait for some time to let other core's thread run */ + k_busy_wait(DELAY_US); + + /* check work have finished */ + if (k_work_busy_get(&work) != 0) { + printk("ERROR: Work item has not finished as expected.\n"); + } else { + printk("SUCCESS: Work item has finished as expected.\n"); + } + + main_thread_id = curr_cpu(); + + /* Verify the ztest thread and system workq run on different core */ + if (main_thread_id == child_thread_id) { + printk("ERROR: System workq ran on the same core as main thread.\n"); + } else { + printk("SUCCESS: System workq and main thread ran on different cores.\n"); + } + +} + +static void t1_mutex_lock(void *p1, void *p2, void *p3) +{ + /* t1 will get mutex first */ + k_mutex_lock((struct k_mutex *)p1, K_FOREVER); + + k_msleep(2); + + k_mutex_unlock((struct k_mutex *)p1); +} + +static void t2_mutex_lock(void *p1, void *p2, void *p3) +{ + if (_current->base.global_lock_count != 0) { + printk("ERROR: Initial thread global lock count %d is incorrect.\n", _current->base.global_lock_count); + return; // Optionally handle error + } + + + k_mutex_lock((struct k_mutex *)p1, K_FOREVER); + + if (_current->base.global_lock_count != 0) { + printk("ERROR: Thread global lock count %d is incorrect after mutex lock.\n", _current->base.global_lock_count); + k_mutex_unlock((struct k_mutex *)p1); // Ensure mutex is unlocked before returning + return; // Optionally handle error + } + + + k_mutex_unlock((struct k_mutex *)p1); + + /**TESTPOINT: z_smp_release_global_lock() has been call during + * context switch but global_lock_cnt has not been decrease + * because no irq_lock() was called. + */ + if (_current->base.global_lock_count != 0) { + printk("ERROR: Thread global lock count %d is incorrect after mutex unlock.\n", _current->base.global_lock_count); + } else { + printk("SUCCESS: Thread global lock count is correct throughout.\n"); + } + + +} + +/** + * @brief Test scenario that a thread release the global lock + * + * @ingroup kernel_smp_tests + * + * @details Validate the scenario that make the internal APIs of SMP + * z_smp_release_global_lock() to be called. + */ +// ZTEST(smp, test_smp_release_global_lock) +void test_smp_release_global_lock(void) +{ + k_mutex_init(&smutex); + + tinfo[0].tid = + k_thread_create(&tthread[0], tstack[0], STACK_SIZE, + (k_thread_entry_t)t1_mutex_lock, + &smutex, NULL, NULL, + K_PRIO_PREEMPT(5), + K_INHERIT_PERMS, K_NO_WAIT); + + tinfo[1].tid = + k_thread_create(&tthread[1], tstack[1], STACK_SIZE, + (k_thread_entry_t)t2_mutex_lock, + &smutex, NULL, NULL, + K_PRIO_PREEMPT(3), + K_INHERIT_PERMS, K_MSEC(1)); + + /* Hold one of the cpu to ensure context switch as we wanted + * can happen in another cpu. + */ + k_busy_wait(20000); + + k_thread_join(tinfo[1].tid, K_FOREVER); + k_thread_join(tinfo[0].tid, K_FOREVER); + cleanup_resources(); +} + +#define LOOP_COUNT ((int)(20000 * RUN_FACTOR)) + +enum sync_t { + LOCK_IRQ, + LOCK_SEM, + LOCK_MUTEX +}; + +static int global_cnt; +static struct k_mutex smp_mutex; + +static void (*sync_lock)(void *); +static void (*sync_unlock)(void *); + +static void sync_lock_dummy(void *k) +{ + /* no sync lock used */ +} + +static void sync_lock_irq(void *k) +{ + *((unsigned int *)k) = irq_lock(); +} + +static void sync_unlock_irq(void *k) +{ + irq_unlock(*(unsigned int *)k); +} + +static void sync_lock_sem(void *k) +{ + k_sem_take(&smp_sem, K_FOREVER); +} + +static void sync_unlock_sem(void *k) +{ + k_sem_give(&smp_sem); +} + +static void sync_lock_mutex(void *k) +{ + k_mutex_lock(&smp_mutex, K_FOREVER); +} + +static void sync_unlock_mutex(void *k) +{ + k_mutex_unlock(&smp_mutex); +} + +static void sync_init(int lock_type) +{ + switch (lock_type) { + case LOCK_IRQ: + sync_lock = sync_lock_irq; + sync_unlock = sync_unlock_irq; + break; + case LOCK_SEM: + sync_lock = sync_lock_sem; + sync_unlock = sync_unlock_sem; + k_sem_init(&smp_sem, 1, 3); + break; + case LOCK_MUTEX: + sync_lock = sync_lock_mutex; + sync_unlock = sync_unlock_mutex; + k_mutex_init(&smp_mutex); + break; + + default: + sync_lock = sync_unlock = sync_lock_dummy; + } +} + +static void inc_global_cnt(void *a, void *b, void *c) +{ + int key; + + for (int i = 0; i < LOOP_COUNT; i++) { + + sync_lock(&key); + + global_cnt++; + global_cnt--; + global_cnt++; + + sync_unlock(&key); + } +} + +static int run_concurrency(int type, void *func) +{ + uint32_t start_t, end_t; + + sync_init(type); + global_cnt = 0; + start_t = k_cycle_get_32(); + + tinfo[0].tid = + k_thread_create(&tthread[0], tstack[0], STACK_SIZE, + (k_thread_entry_t)func, + NULL, NULL, NULL, + K_PRIO_PREEMPT(1), + K_INHERIT_PERMS, K_NO_WAIT); + + tinfo[1].tid = + k_thread_create(&tthread[1], tstack[1], STACK_SIZE, + (k_thread_entry_t)func, + NULL, NULL, NULL, + K_PRIO_PREEMPT(1), + K_INHERIT_PERMS, K_NO_WAIT); + + k_tid_t tid = + k_thread_create(&t2, t2_stack, T2_STACK_SIZE, + (k_thread_entry_t)func, + NULL, NULL, NULL, + K_PRIO_PREEMPT(1), + K_INHERIT_PERMS, K_NO_WAIT); + + k_thread_join(tinfo[0].tid, K_FOREVER); + k_thread_join(tinfo[1].tid, K_FOREVER); + k_thread_join(tid, K_FOREVER); + cleanup_resources(); + + end_t = k_cycle_get_32(); + + printk("type %d: cnt %d, spend %u ms\n", type, global_cnt, + k_cyc_to_ms_ceil32(end_t - start_t)); + + return global_cnt == (LOOP_COUNT * 3); +} + +/** + * @brief Test if the concurrency of SMP works or not + * + * @ingroup kernel_smp_tests + * + * @details Validate the global lock and unlock API of SMP are thread-safe. + * We make 3 thread to increase the global count in different cpu and + * they both do locking then unlocking for LOOP_COUNT times. It shall be no + * deadlock happened and total global count shall be 3 * LOOP COUNT. + * + * We show the 4 kinds of scenario: + * - No any lock used + * - Use global irq lock + * - Use semaphore + * - Use mutex + */ +void test_inc_concurrency(void) +{ + if (!run_concurrency(LOCK_IRQ, inc_global_cnt)) { + printk("ERROR: Total count %d is wrong with LOCK_IRQ.\n", global_cnt); + } else { + printk("SUCCESS: Total count correct with LOCK_IRQ.\n"); + } + + /* increasing global var with semaphore */ + if (!run_concurrency(LOCK_SEM, inc_global_cnt)) { + printk("ERROR: Total count %d is wrong with LOCK_SEM.\n", global_cnt); + } else { + printk("SUCCESS: Total count correct with LOCK_SEM.\n"); + } + + /* increasing global var with mutex */ + if (!run_concurrency(LOCK_MUTEX, inc_global_cnt)) { + printk("ERROR: Total count %d is wrong with LOCK_MUTEX.\n", global_cnt); + } else { + printk("SUCCESS: Total count correct with LOCK_MUTEX.\n"); + } +} + +/** + * @brief Torture test for context switching code + * + * @ingroup kernel_smp_tests + * + * @details Leverage the polling API to stress test the context switching code. + * This test will hammer all the CPUs with thread swapping requests. + */ +static void process_events(void *arg0, void *arg1, void *arg2) +{ + uintptr_t id = (uintptr_t) arg0; + + while (1) { + k_poll(&tevent[id], 1, K_FOREVER); + + if (tevent[id].signal->result != 0x55) { + printk("ERROR: Expected signal result 0x55, got 0x%x for event id %lu \n", tevent[id].signal->result, id); + return; + } + + tevent[id].signal->signaled = 0; + tevent[id].state = K_POLL_STATE_NOT_READY; + + k_poll_signal_reset(&tsignal[id]); + } +} + +static void signal_raise(void *arg0, void *arg1, void *arg2) +{ + unsigned int num_threads = arch_num_cpus(); + + while (1) { + for (uintptr_t i = 0; i < num_threads; i++) { + k_poll_signal_raise(&tsignal[i], 0x55); + } + } +} + +void test_smp_switch_torture(void) +{ + unsigned int num_threads = arch_num_cpus(); + + if (CONFIG_SMP_TEST_RUN_FACTOR == 0) { + /* If CONFIG_SMP_TEST_RUN_FACTOR is zero, + * the switch torture test is effectively + * not doing anything as the k_sleep() + * below is not going to sleep at all, + * and all created threads are being + * terminated (almost) immediately after + * creation. So if run factor is zero, + * mark the test as skipped. + */ + printk("CONFIG_SMP_TEST_RUN_FACTOR is zero, skipping test.\n"); + return; + } + + for (uintptr_t i = 0; i < num_threads; i++) { + k_poll_signal_init(&tsignal[i]); + k_poll_event_init(&tevent[i], K_POLL_TYPE_SIGNAL, + K_POLL_MODE_NOTIFY_ONLY, &tsignal[i]); + + k_thread_create(&tthread[i], tstack[i], STACK_SIZE, + (k_thread_entry_t) process_events, + (void *) i, NULL, NULL, K_PRIO_PREEMPT(i + 1), + K_INHERIT_PERMS, K_NO_WAIT); + } + + k_thread_create(&t2, t2_stack, T2_STACK_SIZE, signal_raise, + NULL, NULL, NULL, K_PRIO_COOP(2), 0, K_NO_WAIT); + + k_sleep(K_MSEC(SLEEP_MS_LONG)); + + k_thread_abort(&t2); + k_thread_join(&t2, K_FOREVER); + for (uintptr_t i = 0; i < num_threads; i++) { + k_thread_abort(&tthread[i]); + k_thread_join(&tthread[i], K_FOREVER); + } +} + +static void *smp_tests_setup(void) +{ + /* Sleep a bit to guarantee that both CPUs enter an idle + * thread from which they can exit correctly to run the main + * test. + */ + k_sleep(K_MSEC(10)); + + return NULL; +} + + +void smp_test(const struct shell *shell, size_t argc, char **argv) +{ + smp_tests_setup() ; + test_coop_resched_threads() ; + test_coop_switch_in_abort() ; + test_cpu_id_threads() ; + test_fatal_on_smp() ; + test_get_cpu() ; + test_inc_concurrency() ; + test_preempt_resched_threads() ; + test_sleep_threads() ; + test_smp_coop_threads() ; + test_smp_ipi() ; + test_smp_release_global_lock() ; + test_smp_switch_torture() ; + test_wakeup_threads() ; + test_workq_on_smp() ; + test_yield_threads() ; + +} + + +SHELL_CMD_ARG_REGISTER(smp_test, NULL, "Smp test", smp_test,1, 0); \ No newline at end of file diff --git a/app/system/smp/smp_test/testcase.yaml b/app/system/smp/smp_test/testcase.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e0ca7e924673ec35b2c9d8b76e75594a44d18ec9 --- /dev/null +++ b/app/system/smp/smp_test/testcase.yaml @@ -0,0 +1,16 @@ +tests: + kernel.multiprocessing.smp: + tags: + - kernel + - smp + ignore_faults: true + filter: (CONFIG_MP_MAX_NUM_CPUS > 1) + kernel.multiprocessing.smp.minimallibc: + tags: + - kernel + - smp + - libc + ignore_faults: true + filter: (CONFIG_MP_MAX_NUM_CPUS > 1) and CONFIG_MINIMAL_LIBC_SUPPORTED + extra_configs: + - CONFIG_MINIMAL_LIBC=y diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md new file mode 100644 index 0000000000000000000000000000000000000000..ee148f432d356f17b1af3ef04dd8128c43849008 --- /dev/null +++ b/doc/ChangeLog.md @@ -0,0 +1,59 @@ +# Phytium-Zephyr-SDK 2024-06-26 v1.0.0 ChangeLog + +Change Log since 2024-06-26 + +## README + +- update version number + +# Phytium-Zephyr-SDK 2024-06-21 ChangeLog + +Change Log since 2024-06-17 + +## app + +- add const key word to one variable + +## README + +- update README.md, remove the description about /zephyeproject, add some detail notes + +# Phytium-Zephyr-SDK 2024-06-21 ChangeLog + +Change Log since 2024-06-17 + +## app + +- modified every CMakeLists.tx in peripheral apps to resolve the issue of inability to compile due to repository merging. + +## modules + +- standalone_for_zephyr repository merge into Phytium-zephyr-sdk repository + +# Phytium-Zephyr-SDK 2024-06-13 ChangeLog + +Change Log since 2024-06-07 + +## app + +- add pcie enum example + +# Phytium-Zephyr-SDK 2024-05-08 ChangeLog + +Change Log since 2024-04-11 + +## doc + +- modify the bugs in the documentation + +# Phytium-Zephyr-SDK 2024-04-11 ChangeLog + +Change Log since 2024-04-11 + +## doc + +- add readme + +## example + +- add xmac echo example \ No newline at end of file diff --git a/figs/readme/1712020444273.png b/figs/readme/1712020444273.png new file mode 100644 index 0000000000000000000000000000000000000000..4848fbe3d0745ca70447fd7f9378e7fdbf66690e Binary files /dev/null and b/figs/readme/1712020444273.png differ diff --git a/figs/readme/1712058336378.png b/figs/readme/1712058336378.png new file mode 100644 index 0000000000000000000000000000000000000000..e26ece15101300a54335486d0131f8ec524424c7 Binary files /dev/null and b/figs/readme/1712058336378.png differ diff --git a/figs/readme/1712107861173.png b/figs/readme/1712107861173.png new file mode 100644 index 0000000000000000000000000000000000000000..9273ccce22dcc29ef940600bcf30ec3a2516930d Binary files /dev/null and b/figs/readme/1712107861173.png differ diff --git a/figs/readme/zephy_sdk_install.png b/figs/readme/zephy_sdk_install.png new file mode 100644 index 0000000000000000000000000000000000000000..0fa6e5697edb4152dd96e50aa1453bef38b6e634 Binary files /dev/null and b/figs/readme/zephy_sdk_install.png differ diff --git "a/figs/readme/\344\273\223\345\272\223\346\236\266\346\236\204\345\233\276.png" "b/figs/readme/\344\273\223\345\272\223\346\236\266\346\236\204\345\233\276.png" new file mode 100644 index 0000000000000000000000000000000000000000..0aa6cac8fe4f615002bda97a57b83bb84ce16617 Binary files /dev/null and "b/figs/readme/\344\273\223\345\272\223\346\236\266\346\236\204\345\233\276.png" differ diff --git a/modules/hal/phytium/CMakeLists.txt b/modules/hal/phytium/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..57741500daddd9b10437109625505bae823974aa --- /dev/null +++ b/modules/hal/phytium/CMakeLists.txt @@ -0,0 +1,5 @@ + + +add_subdirectory_ifdef(CONFIG_PHYTIUM_STANDALONE_SDK hal) + + diff --git a/modules/hal/phytium/LICENSE b/modules/hal/phytium/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..8f71f43fee3f78649d238238cbde51e6d7055c82 --- /dev/null +++ b/modules/hal/phytium/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + diff --git a/modules/hal/phytium/hal/CMakeLists.txt b/modules/hal/phytium/hal/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..14024370e0ef2880fd061ba2a27f0fc5a8a347c9 --- /dev/null +++ b/modules/hal/phytium/hal/CMakeLists.txt @@ -0,0 +1,80 @@ + + +zephyr_library() + +# pl011 drivers +set(PL011_UART_SOURCES + phytium-standalone-sdk/drivers/serial/fpl011/fpl011.c + phytium-standalone-sdk/drivers/serial/fpl011/fpl011_options.c + phytium-standalone-sdk/drivers/serial/fpl011/fpl011_intr.c + phytium-standalone-sdk/drivers/serial/fpl011/fpl011_hw.c + phytium-standalone-sdk/drivers/serial/fpl011/fpl011_g.c +) + +foreach(SOURCE IN LISTS PL011_UART_SOURCES) + zephyr_library_sources_ifdef(CONFIG_ENABLE_Pl011_UART ${SOURCE}) +endforeach() + +# sdif drivers + +set(PHYTIUM_SDHC_SOURCES + phytium-standalone-sdk/drivers/mmc/fsdif/fsdif.c + phytium-standalone-sdk/drivers/mmc/fsdif/fsdif_cmd.c + phytium-standalone-sdk/drivers/mmc/fsdif/fsdif_dma.c + phytium-standalone-sdk/drivers/mmc/fsdif/fsdif_intr.c + phytium-standalone-sdk/drivers/mmc/fsdif/fsdif_pio.c + phytium-standalone-sdk/drivers/mmc/fsdif/fsdif_sinit.c +) + +foreach(SOURCE IN LISTS PHYTIUM_SDHC_SOURCES) + zephyr_library_sources_ifdef(CONFIG_ENABLE_PHYTIUM_SDHC ${SOURCE}) +endforeach() + +zephyr_include_directories(phytium-standalone-sdk/drivers/mmc/fsdif) + + +# io ctrl +set(PHYTIUM_FIO_SOURCES + phytium-standalone-sdk/drivers/iomux/fiopad/fiopad.c + phytium-standalone-sdk/drivers/iomux/fiopad/fiopad_hw.c +) + +foreach(SOURCE IN LISTS PHYTIUM_FIO_SOURCES) + zephyr_library_sources_ifdef(CONFIG_PINCTRL_PHYTIUM ${SOURCE}) +endforeach() + +zephyr_include_directories(phytium-standalone-sdk/drivers/iomux/fiopad) + +# xmac +set(PHYTIUM_XMAC_SOURCES + phytium-standalone-sdk/drivers/eth/fxmac/fxmac.c + phytium-standalone-sdk/drivers/eth/fxmac/fxmac_bdring.c + phytium-standalone-sdk/drivers/eth/fxmac/fxmac_debug.c + phytium-standalone-sdk/drivers/eth/fxmac/fxmac_intr.c + phytium-standalone-sdk/drivers/eth/fxmac/fxmac_options.c + phytium-standalone-sdk/drivers/eth/fxmac/fxmac_phy.c + phytium-standalone-sdk/drivers/eth/fxmac/fxmac_sinit.c +) + +foreach(SOURCE IN LISTS PHYTIUM_XMAC_SOURCES) + zephyr_library_sources_ifdef(CONFIG_ETH_PHYTIUM_XMAC ${SOURCE}) +endforeach() + +zephyr_include_directories(phytium-standalone-sdk/drivers/eth/fxmac) +zephyr_include_directories(phytium-standalone-sdk/drivers/eth/fxmac/phy) + + +# common +zephyr_library_sources(phytium-standalone-sdk/common/fassert.c) + +zephyr_include_directories(phytium-standalone-sdk/drivers/serial/fpl011/) + +zephyr_include_directories(phytium-standalone-sdk/common) +zephyr_include_directories(phytium-standalone-sdk/soc/common) +zephyr_include_directories(phytium-standalone-sdk/soc/e2000) +zephyr_include_directories(phytium-standalone-sdk/soc/e2000/q) +zephyr_include_directories(phytium-standalone-sdk/drivers) + +zephyr_library_sources(fdrivers_port/fdrivers_port.c) +zephyr_include_directories(fdrivers_port/) +zephyr_include_directories(phytium-standalone-sdk/common) diff --git a/modules/hal/phytium/hal/fdrivers_port/fdrivers_port.c b/modules/hal/phytium/hal/fdrivers_port/fdrivers_port.c new file mode 100644 index 0000000000000000000000000000000000000000..cfe7912641ff93daab878164c7f0a5c4512c4793 --- /dev/null +++ b/modules/hal/phytium/hal/fdrivers_port/fdrivers_port.c @@ -0,0 +1,92 @@ +// Phytium is pleased to support the open source community by making Zephyr-SDK available. + +// +// Copyright (C) 2024 Phytium Technology Co., Ltd. All rights reserved. +// +// Licensed under the Apache-2.0 License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/license/apache-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. + +#include "fdrivers_port.h" +#include +#include +#include + +/* cache */ +void FDriverDCacheRangeFlush(uintptr_t adr,size_t len) +{ + +} + +void FDriverDCacheRangeInvalidate(uintptr_t adr,size_t len) +{ + +} + + +void FDriverICacheRangeInvalidate(void) +{ + +} + + +/* time delay */ + +void FDriverUdelay(u32 usec) +{ + k_busy_wait(usec) ; +} + +void FDriverMdelay(u32 msec) +{ + k_busy_wait(msec*1000) ; +} + +void FDriverSdelay(u32 sec) +{ + k_busy_wait(sec * 1000 *1000) ; +} + +#define ELOG_LINE_BUF_SIZE 256 +static char log_buf[ELOG_LINE_BUF_SIZE] = { 0 }; + +void FtDumpLogInfo(const char *tag, u32 log_level, const char *log_tag_letter, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(log_buf, sizeof(log_buf), fmt, ap); + va_end(ap); + + do + { + if (strcmp(log_tag_letter, "31") == 0) + { + printf("\031[31m%s:%s\031[0m \r\n", tag, log_buf); + } + else if (strcmp(log_tag_letter, "32") == 0) + { + printf("\032[32m%s:%s\032[0m \r\n", tag, log_buf); + } + else if (strcmp(log_tag_letter, "33") == 0) + { + printf("\033[33m%s:%s\033[0m \r\n", tag, log_buf); + } + else if (strcmp(log_tag_letter, "36") == 0) + { + printf("\036[36m%s:%s\036[0m \r\n", tag, log_buf); + } + else + { + printf("\033[35m%s:%s\033[0m \r\n", tag, log_buf); + } + + }while (0); + + memset(log_buf, 0, sizeof(log_buf)); +} \ No newline at end of file diff --git a/modules/hal/phytium/hal/fdrivers_port/fdrivers_port.h b/modules/hal/phytium/hal/fdrivers_port/fdrivers_port.h new file mode 100644 index 0000000000000000000000000000000000000000..3a380c9c6489ce69f2b8e5be0e3c63559b62357d --- /dev/null +++ b/modules/hal/phytium/hal/fdrivers_port/fdrivers_port.h @@ -0,0 +1,136 @@ +// Phytium is pleased to support the open source community by making Zephyr-SDK available. + +// +// Copyright (C) 2024 Phytium Technology Co., Ltd. All rights reserved. +// +// Licensed under the Apache-2.0 License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/license/apache-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 FDRIVERS_PORT_H +#define FDRIVERS_PORT_H + +#include "ftypes.h" + + +#include +#include "fkernel.h" + +/***************************** Include Files *********************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cache */ +void FDriverDCacheRangeFlush(uintptr_t adr,size_t len); + +void FDriverDCacheRangeInvalidate(uintptr_t adr,size_t len); + +void FDriverICacheRangeInvalidate(void); + + +/* memory barrier */ + + +#define FDRIVER_DSB() //DSB() + +#define FDRIVER_DMB() //DMB() + +#define FDRIVER_ISB() //ISB() + + +/* time delay */ + +void FDriverUdelay(u32 usec); + +void FDriverMdelay(u32 msec); + +void FDriverSdelay(u32 sec); + + + +#define FT_LOG_NONE 0 /* No log output */ +#define FT_LOG_ERROR 1 /* Critical errors, software module can not recover on its own */ +#define FT_LOG_WARN 2 /* Error conditions from which recovery measures have been taken */ +#define FT_LOG_INFO 3 /* Information messages which describe normal flow of events */ +#define FT_LOG_DEBUG 4 /* Extra information which is not necessary for normal use (values, pointers, sizes, etc). */ +#define FT_LOG_VERBOSE 5 /* Bigger chunks of debugging information, or frequent messages which can potentially flood the output. */ + +#define LOG_COLOR_BLACK "30" +#define LOG_COLOR_RED "31" +#define LOG_COLOR_GREEN "32" +#define LOG_COLOR_BROWN "33" +#define LOG_COLOR_BLUE "34" +#define LOG_COLOR_PURPLE "35" +#define LOG_COLOR_CYAN "36" +#define LOG_COLOR(COLOR) "\033[0;" COLOR "m" +#define LOG_BOLD(COLOR) "\033[1;" COLOR "m" +#define LOG_RESET_COLOR "\033[0m" +#define LOG_COLOR_E LOG_COLOR(LOG_COLOR_RED) +#define LOG_COLOR_W LOG_COLOR(LOG_COLOR_BROWN) +#define LOG_COLOR_I LOG_COLOR(LOG_COLOR_GREEN) +#define LOG_COLOR_D LOG_COLOR(LOG_COLOR_CYAN) +#define LOG_COLOR_V LOG_COLOR(LOG_COLOR_PURPLE) + + +void FtDumpLogInfo(const char *tag, u32 log_level, const char *log_tag_letter, const char *fmt, ...) ; + +#ifndef CONFIG_HAL_STANDALONE_SDK_DEBUG +#ifndef FT_DEBUG_PRINT_I +#define FT_DEBUG_PRINT_I(TAG, format, ...) +#endif + +#ifndef FT_DEBUG_PRINT_E +#define FT_DEBUG_PRINT_E(TAG, format, ...) FtDumpLogInfo(TAG, FT_LOG_ERROR, LOG_COLOR_RED,format,##__VA_ARGS__) +#endif + +#ifndef FT_DEBUG_PRINT_D +#define FT_DEBUG_PRINT_D(TAG, format, ...) +#endif + +#ifndef FT_DEBUG_PRINT_W +#define FT_DEBUG_PRINT_W(TAG, format, ...) +#endif + +#ifndef FT_DEBUG_PRINT_V +#define FT_DEBUG_PRINT_V(TAG, format, ...) +#endif + +#else +#ifndef FT_DEBUG_PRINT_I +#define FT_DEBUG_PRINT_I(TAG, format, ...) FtDumpLogInfo(TAG, FT_LOG_INFO, LOG_COLOR_PURPLE,format,##__VA_ARGS__) +#endif + +#ifndef FT_DEBUG_PRINT_E +#define FT_DEBUG_PRINT_E(TAG, format, ...) FtDumpLogInfo(TAG, FT_LOG_ERROR, LOG_COLOR_RED,format,##__VA_ARGS__) +#endif + +#ifndef FT_DEBUG_PRINT_D +#define FT_DEBUG_PRINT_D(TAG, format, ...) FtDumpLogInfo(TAG, FT_LOG_DEBUG, LOG_COLOR_GREEN,format, ##__VA_ARGS__) +#endif + +#ifndef FT_DEBUG_PRINT_W +#define FT_DEBUG_PRINT_W(TAG, format, ...) FtDumpLogInfo(TAG, FT_LOG_WARN, LOG_COLOR_BROWN,format, ##__VA_ARGS__) +#endif + +#ifndef FT_DEBUG_PRINT_V +#define FT_DEBUG_PRINT_V(TAG, format, ...) FtDumpLogInfo(TAG, FT_LOG_VERBOSE, LOG_COLOR_BROWN,format, ##__VA_ARGS__) +#endif + +#endif + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/modules/hal/phytium/hal/fdrivers_port/sdkconfig.h b/modules/hal/phytium/hal/fdrivers_port/sdkconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..7bb4942178634c9a914b8aca1526a90a14ce24fa --- /dev/null +++ b/modules/hal/phytium/hal/fdrivers_port/sdkconfig.h @@ -0,0 +1,14 @@ +// Phytium is pleased to support the open source community by making Zephyr-SDK available. + +// +// Copyright (C) 2024 Phytium Technology Co., Ltd. All rights reserved. +// +// Licensed under the Apache-2.0 License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/license/apache-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. diff --git a/modules/hal/phytium/zephyr/module.yml b/modules/hal/phytium/zephyr/module.yml new file mode 100644 index 0000000000000000000000000000000000000000..a1161c71f6a6b66bdc573a47ab3bc73d601d0718 --- /dev/null +++ b/modules/hal/phytium/zephyr/module.yml @@ -0,0 +1,4 @@ +build: + cmake: . + settings: + dts_root: . diff --git a/tools/env_test.py b/tools/env_test.py new file mode 100644 index 0000000000000000000000000000000000000000..d9e0b54267fca409e89b47fde0655435889c4fc8 --- /dev/null +++ b/tools/env_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +import subprocess + +# 执行 "sudo apt update" 命令 +update_result = subprocess.run(["sudo", "apt", "update"], capture_output=True) +if update_result.returncode == 0: + print("Package lists updated successfully.") +else: + print("Failed to update package lists.") + print("Error message:", update_result.stderr.decode()) + +# 执行 "sudo apt upgrade" 命令 +upgrade_result = subprocess.run(["sudo", "apt", "upgrade"], capture_output=True) +if upgrade_result.returncode == 0: + print("Packages upgraded successfully.") +else: + print("Failed to upgrade packages.") + print("Error message:", upgrade_result.stderr.decode()) + +# 执行 "sudo bash ./tools/kitware-archive.sh" 命令 +kitware_result = subprocess.run(["sudo", "bash", "./tools/kitware-archive.sh"], capture_output=True) +if kitware_result.returncode == 0: + print("kitware-archive.sh script executed successfully.") +else: + print("Failed to execute kitware-archive.sh script.") + print("Error message:", kitware_result.stderr.decode()) + +# 执行必备程序安装命令 +essential_packages = [ + "git", "cmake", "ninja-build", "gperf", + "ccache", "dfu-util", "device-tree-compiler", "wget", + "python3-dev", "python3-pip", "python3-setuptools", "python3-tk", "python3-wheel", "xz-utils", "file", + "make", "gcc", "gcc-multilib", "g++-multilib", "libsdl2-dev", "libmagic1" +] +install_command = ["sudo", "apt", "install", "--no-install-recommends"] + essential_packages +install_result = subprocess.run(install_command, capture_output=True) +if install_result.returncode == 0: + print("Essential packages installed successfully.") +else: + print("Failed to install essential packages.") + print("Error message:", install_result.stderr.decode()) + +# 检查安装是否成功 +check_commands = ["cmake --version", "python3 --version", "dtc --version"] +for cmd in check_commands: + check_result = subprocess.run(cmd, shell=True, capture_output=True) + if check_result.returncode == 0: + print(cmd, "executed successfully.") + else: + print("Failed to check installation of", cmd.split()[0]) + print("Error message:", check_result.stderr.decode()) + +# 安装python虚拟化环境 +venv_install_result = subprocess.run(["sudo", "apt", "install", "python3-venv"], capture_output=True) +if venv_install_result.returncode == 0: + print("python3-venv package installed successfully.") +else: + print("Failed to install python3-venv package.") + print("Error message:", venv_install_result.stderr.decode()) + diff --git a/tools/kitware-archive.sh b/tools/kitware-archive.sh new file mode 100644 index 0000000000000000000000000000000000000000..66263cf6b65c933d40cb756839302939cde8a1f5 --- /dev/null +++ b/tools/kitware-archive.sh @@ -0,0 +1,99 @@ +#!/bin/sh + +set -eu + +help() { + echo "Usage: $0 [--release ] [--rc]" > /dev/stderr +} + +doing= +rc= +release= +help= +for opt in "$@" +do + case "${doing}" in + release) + release="${opt}" + doing= + ;; + "") + case "${opt}" in + --rc) + rc=1 + ;; + --release) + doing=release + ;; + --help) + help=1 + ;; + esac + ;; + esac +done + +if [ -n "${doing}" ] +then + echo "--${doing} option given no argument." > /dev/stderr + echo > /dev/stderr + help + exit 1 +fi + +if [ -n "${help}" ] +then + help + exit +fi + +if [ -z "${release}" ] +then + unset UBUNTU_CODENAME + . /etc/os-release + + if [ -z "${UBUNTU_CODENAME+x}" ] + then + echo "This is not an Ubuntu system. Aborting." > /dev/stderr + exit 1 + fi + + release="${UBUNTU_CODENAME}" +fi + +case "${release}" in +focal|jammy) + packages= + keyring_packages="ca-certificates gpg wget" + ;; +*) + echo "Only Ubuntu Focal (20.04) and Jammy (22.04) are supported. Aborting." > /dev/stderr + exit 1 + ;; +esac + +get_keyring= +if [ ! -f /usr/share/doc/kitware-archive-keyring/copyright ] +then + packages="${packages} ${keyring_packages}" + get_keyring=1 +fi + +# Start the real work +set -x + +apt-get update +# shellcheck disable=SC2086 +apt-get install -y ${packages} + +test -n "${get_keyring}" && (wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - > /usr/share/keyrings/kitware-archive-keyring.gpg) + +echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ ${release} main" > /etc/apt/sources.list.d/kitware.list +if [ -n "${rc}" ] +then + echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ ${release}-rc main" >> /etc/apt/sources.list.d/kitware.list +fi + +apt-get update +test -n "${get_keyring}" && rm /usr/share/keyrings/kitware-archive-keyring.gpg +apt-get install -y kitware-archive-keyring diff --git a/tools/pull_zephyr_os.py b/tools/pull_zephyr_os.py new file mode 100644 index 0000000000000000000000000000000000000000..cebc305538ed9fff2ce29df2f58aca5b2f2c5bb2 --- /dev/null +++ b/tools/pull_zephyr_os.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +#!/usr/bin/env python +import subprocess +import os + +# 执行 "git clone" 命令 +git_clone_command = ["git", "clone", "-b", "phytium_v3.5.0", "https://gitee.com/phytium_embedded/zephyr_kernel.git","zephyr"] +init_result = subprocess.run(git_clone_command, capture_output=True, text=True) +if init_result.returncode == 0: + print("Code pulled successfully.") +else: + print("Failed to pull the code.") + print("Error message:", init_result.stderr) + print("Output message:", init_result.stdout) + +origin_dir = os.getcwd() +os.chdir(f"{origin_dir}/zephyr") +os.system(f"git checkout 34f4373122b92232c36e20b08cd7dbf8ce9733d9") +os.chdir(origin_dir) \ No newline at end of file diff --git a/tools/pull_zephyr_os.sh b/tools/pull_zephyr_os.sh new file mode 100644 index 0000000000000000000000000000000000000000..4a9a9e577a251a6eecfe718364fd9dd083da2bbe --- /dev/null +++ b/tools/pull_zephyr_os.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# 执行 "git clone" 命令 +git clone -b phytium_v3.5.0 https://gitee.com/phytium_embedded/zephyr_kernel.git zephyr +cd zephyr +git checkout 34f4373122b92232c36e20b08cd7dbf8ce9733d9 +cd .. + +if [ $? -eq 0 ]; then + echo "Code pulled successfully." +else + echo "Failed to pull the code." + echo "Error message:" $? +fi \ No newline at end of file diff --git a/tools/update.py b/tools/update.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391