首页 > 其他分享 >QEMU专栏 - 使用 QEMU 调试 FreeRTOS示例

QEMU专栏 - 使用 QEMU 调试 FreeRTOS示例

时间:2024-06-24 09:34:03浏览次数:3  
标签:monitor FreeRTOS 示例 qemu UART0 build QEMU Monitor

写在最前

这几天一直在研究QEMU中多核ARM加载不同镜像的问题, 一直不得其解, 这部分后续可以分几个不分拆解下,看看为什么会出现这种问题. 今天先来看看如何使用QEMU来调试FreeRTOS的示例代码.

编译并运行 FreeRTOS 示例代码 (基础版本)

首先是下载代码, 这种只需要看最新代码情况下,我通常会使用--depth=1来减少下载的代码量; 并且需要下载子模块, 所以使用--recurse-submodules来下载子模块.

git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules --depth=1

然后进入到 FreeRTOS/Demo/CORTEX_MPS2_QEMU_IAR_GCC中, 在这个目录下,使用vscode打开. 我比较喜欢使用MSYS2 MinGW 64-bit 控制台, 但是vscodebash (MSYS2)打开后,默认不是在当前工作区的目录, 所以可以通过修改Preferences: Open User Settings (JSON)的方式打开用户设置settings.json, 然后找到"terminal.integrated.profiles.windows"这个配置, 然后添加如下配置:

  "bash mingw64": {
      "path": "C:\\msys64\\msys2_shell.cmd",
      "args": [
          "-defterm",
          "-here",
          "-no-start",
          "-mingw64"    
      ]
  },

由于这个目录下已经配置好了launch.jsontasks.json, 但是这里需要简单的调整一下, 让其在我们编译出的QEMU中运行, 打开tasks.json文件:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
          "label": "Build QEMU",
          "type": "shell",
          "command": "make --directory=${workspaceFolder}/build/gcc",
          "problemMatcher": {
            "base": "$gcc",
            "fileLocation": ["relative", "${workspaceFolder}/build/gcc"]
          },
          "group": {
              "kind": "build",
              "isDefault": true
          }
        },
        {
          "label": "Run QEMU",
          "type": "shell",
          "command": "echo 'QEMU RTOSdemo started'; qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -kernel ${workspaceFolder}/build/gcc/output/RTOSDemo.out -monitor none -nographic -serial stdio -s -S",
          "dependsOn": ["Build QEMU"],
          "isBackground": true,
          "problemMatcher": [
            {
              "pattern": [
                {
                  "regexp": ".",
                  "file": 1,
                  "location": 2,
                  "message": 3
                }
              ],
              "background": {
                "activeOnStart": true,
                "beginsPattern": ".",
                "endsPattern": "QEMU RTOSdemo started",
              }
            }
          ]
        }
    ]
}

我们需要将qemu-system-arm调整为我们编译出的QEMU的路径:

"command": "echo 'QEMU RTOSdemo started'; <Path>/build/qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -kernel ${workspaceFolder}/build/gcc/output/RTOSDemo.out -monitor none -nographic -serial stdio -s -S"

到这里, 我们就可以通过F5来运行我们的FreeRTOS示例代码了, 由于launch.json中配置了preLaunchTaskRun QEMU, 而Run QEMU又依赖于Build QEMU, 所以在运行时会先编译, 然后运行QEMU, 这里的参数更加复杂:

 <Path>/build/qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -kernel ${workspaceFolder}/build/gcc/output/RTOSDemo.out -monitor none -nographic -serial stdio -s -S

基础版中,我们只关注运行, 但是也需要简单的调试, 所以设置 -monitor none , -nographic , -serial stdio , -s , -S 这几个参数, 这里简单的解释一下:

  • -monitor none : 不显示监视器
  • -nographic : 不显示图形界面
  • -serial stdio : 将串口输出重定向到标准输出
  • -s : 启动GDB服务器
  • -S : 启动时暂停

我一般不喜欢贴图, 主要是因为贴图不方便修改, 但是这里还是需要简单的贴一下:
Debug

其实我在代码中,并未加入任何的断点, 但是启动后, 仍然停在了Reset_Handler中, 这是因为在launch.json中将stopAtEntry设置为了true, 这个参数的意思是在程序启动时, 是否停在入口处, 这里我们设置为true, 所以会停在Reset_Handler中, 如果设置为false, 则会直接运行程序, 这里大家可以自行测试.

关于断点相关的调试, 这里就不多赘述了, 至于GDB的内容, 后续需要的时候,再来详细的介绍.

进阶调试版本 (初次接触Monitor)

在这一个篇章中,我们可以初步了解下QEMU Monitor , QEMU Monitor是一个用于与QEMU交互的命令行工具, 现阶段我们主要通过QEMU Monitor查看QEMU的状态.

为此, 我们需要调整之前在tasks.jsonRun QEMU的命令, 这里有几种选择:

  1. 使用独立的QEMU Monitor窗口

为此, 首先,我们需要将-nographic去掉, 这样QEMU就会显示图形界面, 然后其次,我们要将-monitor none去掉, 改成-monitor vc , 改动后command如下:

"command": "echo 'QEMU RTOSdemo started'; <Path>/build/qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -kernel ${workspaceFolder}/build/gcc/output/RTOSDemo.out -monitor vc -serial stdio -s -S"

当然,这种效果和直接去掉-nographic以及-monitor none是一样的, 但是为了明确我们使用了QEMU Monitor, 我们这里使用了-monitor vc.

按照这种方式, 启动的QEMU Monitor窗口,不要关闭, 因为如果关闭, QEMU也会关闭, 那么调试就结束了.

  1. 使用QEMU Monitor命令行
    由于之前我们使用串口占用了标准输出, 那如果我们希望使用QEMU Monitor命令行, 那么我们需要将串口输出重定向到文件, 这里我们可以使用-serial file:output.txt来将串口输出重定向到文件, 这里我们可以将command改为:
"command": "echo 'QEMU RTOSdemo started'; <Path>/build/qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -kernel ${workspaceFolder}/build/gcc/output/RTOSDemo.out -monitor stdio -serial file:output.txt -s -S"

这两种配置方式各有利弊, 我们目前阶段都还好了.

接下来就是使用QEMU Monitor来查看代码中寄存器的值了, 运行F5后,开始调试,单步调试到main.c中的prvUARTInit中:

static void prvUARTInit( void )
{
    UART0_BAUDDIV = 16;
    UART0_CTRL = 1;
}

这个时候, 先不要着急进行下一步调试, 在这里先暂停下, 查看下UART0_BAUDDIV的定义:

 * required UART registers. */
#define UART0_ADDRESS                         ( 0x40004000UL )
#define UART0_DATA                            ( *( ( ( volatile uint32_t * ) ( UART0_ADDRESS + 0UL ) ) ) )
#define UART0_STATE                           ( *( ( ( volatile uint32_t * ) ( UART0_ADDRESS + 4UL ) ) ) )
#define UART0_CTRL                            ( *( ( ( volatile uint32_t * ) ( UART0_ADDRESS + 8UL ) ) ) )
#define UART0_BAUDDIV                         ( *( ( ( volatile uint32_t * ) ( UART0_ADDRESS + 16UL ) ) ) )

通过这个宏定义, 我们可以得知UART0_BAUDDIV的地址为0x40004010, 并且数据类型为uint32_t, 那么我们可以通过QEMU Monitor来查看这个地址的值, 在QEMU Monitor中输入xp /1xw 0x40004010 得到如下输出:

(qemu) xp /1xw 0x40004010
0000000040004010: 0x00000000

这里的xp表示eXamine Physical memory, /1xw表示/fmt, 其中:

  • w 代表了查看的数据size为word, word的大小为4个字节, 32bits.
  • x 代表了查看的数据的格式为hex, 即16进制.
  • /1 代表了查看的数据的个数为1, 即查看一个word的数据.

这里我们可以看到, UART0_BAUDDIV的值为0x00000000, 这是因为我们停在了prvUARTInit中, 还没有对UART0_BAUDDIV进行赋值, 所以这里的值为0x00000000, 这里我们可以继续单步调试, 然后再次查看UART0_BAUDDIV的值.

(qemu) xp /1xw 0x40004010
0000000040004010: 0x00000010

这里我们可以看到, UART0_BAUDDIV的值为0x00000010, 这是因为我们在prvUARTInit中对UART0_BAUDDIV进行了赋值, 这里的值为0x00000010, 后续的UART0_CTRL也可以通过这种方式来查看, 大家可以自行尝试.

在这里, 再稍微延伸一点, 为什么是0x40004010呢? 经常弄单片机的小伙伴肯定理解这是UART0的基地址, 而16 则是BAUDDIVUART0中的偏移量, 这个值是通过手册查找得到的, 这里就不多赘述了.

最后,我们通过一个简单的info mtree来查看内存的布局:

(qemu) info mtree
(qemu) info mtree
address-space: cpu-memory-0
  0000000000000000-ffffffffffffffff (prio 0, i/o): armv7m-container
...
      0000000040004000-0000000040004fff (prio 0, i/o): uart
...

address-space: bitband-source
address-space: bitband-source
address-space: memory
  0000000000000000-ffffffffffffffff (prio -1, i/o): system
...
    0000000040004000-0000000040004fff (prio 0, i/o): uart
...

从内存布局中, 我们也能看到UART的地址, 这里就不多赘述了.

写在最后/更新频次说明

这次我们简单介绍了一下FreeRTOS 中的一个示例, 并由此衍生出了一些小配置及monitor中的一些简单命令, 这里只是一个简单的入门, 后续还有很多内容需要去深入了解, 也希望大家能够多多尝试, 多多探索.

后续的更新频次会保证在一周最少一篇, 尽可能的保证质量, 但是由于个人能力有限, 也希望大家能够多多包涵, 如果有什么问题, 也欢迎大家提出, 我会尽力解答.

最后, 感谢大家的阅读, 也希望大家能够多多支持, 谢谢!

下期内容暂时未定, 但是应该和我最近遇到的问题有关, 也希望大家能够多多关注, 谢谢!

参考资料

标签:monitor,FreeRTOS,示例,qemu,UART0,build,QEMU,Monitor
From: https://blog.csdn.net/qq727755316/article/details/139788605

相关文章

  • 一个使用Python和假设的天气API来获取和展示天气数据的简单脚本示例
    要使用Python编写一个天气预测的脚本,我们通常需要依赖于现有的天气API来获取实时或历史天气数据,并且结合机器学习或统计模型来进行预测。然而,由于天气预测是一个复杂的任务,通常需要大量的计算资源和专业的气象知识,这里我们将简化这个过程,只展示如何使用Python和一个假设的天......
  • 基于Spring Boot的MyBatis整合示例:构建简单的用户管理系统
    目录1.创建一个新的SpringBoot项目2.添加必要的依赖3.配置数据库连接4.创建实体类5.创建Mapper接口6.创建MapperXML文件7.创建Service类8.创建Controller类9.启动应用程序10.测试在当今的软件开发中,SpringBoot和MyBatis作为两个流行的Java框架,常常被......
  • 538个代码示例!麻省理工教授的Python程序设计+人工智能案例实践
    Python简单易学,且提供了丰富的第三方库,可以用较少的代码完成较多的工作,使开发者能够专注于如何解决问题而只花较少的时间去考虑如何编程。此外,Python还具有免费开源、跨平台、面向对象、胶水语言等优点,在系统编程、图形界面开发、科学计算、Web开发、数据分析、人工智能等方面......
  • 常微分方程算法之编程示例一(欧拉法)
    目录一、研究问题二、C++代码三、计算结果一、研究问题    前面几节内容介绍了常微分方程有限差分格式的推导。为加强对本专栏知识的理解,从本节开始,我们补充一些具体算例及相应的编程。    欧拉法的原理及推导请参考:常微分方程算法之欧拉法(Euler)_欧拉......
  • golang openai GPT4o 示例代码
    packagemainimport( "context" "errors" "fmt" "io" "log" "os" "github.com/joho/godotenv" openai"github.com/sashabaranov/go-openai")funcmain(){ err:=godoten......
  • 微信H5分享示例
    在H5页面(通常指使用HTML5技术开发的网页)分享内容到微信好友或朋友圈,需要使用微信的JS-SDK来实现。引入JS-SDK:在H5页面的<head>标签中引入微信JS-SDK的脚本文件。配置JS-SDK:在页面加载完成后,调用wx.config方法来配置JS-SDK。这一步通常需要提供一系列的配置参数,如appId、tim......
  • Python梯度提升决策树的方法示例
    梯度提升决策树(GradientBoostingDecisionTree,简称GBDT)是一种基于集成学习的算法,它通过构建多个决策树模型,并将它们组合在一起来实现更好的预测性能。GBDT的核心思想是在每轮迭代中,根据当前模型的残差(真实值与预测值之差)来训练一个新的决策树,然后将这个新树添加到模型中,以不断减......
  • 【Unity动画系统】Amimator Controller的概念及其使用示例
    Unity的AnimatorController是动画系统中的一个核心组件,它负责管理和控制动画状态机(AnimationStateMachine)的行为。AnimatorController包含了动画状态、转换规则、以及用于控制动画流程的参数。AnimatorController的概念:动画状态(AnimationStates):代表单个动画剪辑(Animati......
  • Ant-Design-Vue动态表头并填充数据(含示例代码)
    关注我,持续分享逻辑思维&管理思维&面试题;可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;推荐专栏《10天学会使用asp.net编程AI大模型》,目前已完成所有内容。一顿烧烤不到的费用,让人能紧跟时代的浪潮。从普通网站,到公众号、小程序,再到AI大模型网站。干货满满。学成后可......
  • 063篇 - 案例研究与示例(Case Studies and Examples)
    大家好,我是元壤教育的张涛,一名知识博主,专注于生成式人工智能(AIGC)各领域的研究与实践。我喜欢用简单的方法,帮助大家轻松掌握AIGC应用技术。我的愿景是通过我的文章和教程,帮助1000万人学好AIGC,用好AIGC。在本章中,我们将探讨提示工程项目的实际案例研究和示例,展示基于提示的......