跳到主要内容

构建,运行,调试

ZStudio 中内置了兆松科技自研的 ZCC 编译器,ZEMU 仿真器以及 ZDB 调试器。本节将介绍如何使用 ZStudio 构建、运行和调试项目代码。

构建

ZStudio 不使用 make 或 cmake 等外部工具,而是由 ZStudio 中内置的项目文件(.zsproj)对项目进行管理。项目文件保存了对项目进行操作所需的所有信息和指令,包括构建配置、运行配置、调试配置以及性能分析配置。

本节内容使用 ZSolution 快速上手中的 Quick_Start 项目,用户可以遵循以下步骤构建项目:

  1. 设置当前活动项目

    用户确认当前想要构建的项目已设置为当前活动的项目,项目快捷控制中的按钮将基于当前激活项目进行构建、仿真和调试。

    设置当前活动项目
  2. 选择构建版本

    项目快捷控制的下拉框可以快捷切换 "Debug"、"Release"、"Min Size" 或 "Release with Debug information" 版本。

    • Debug(调试版):主要用于开发阶段,包含了丰富的调试信息。

    • Release(发布版):主要用于生产环境,不包含调试信息。

    • Min Size(最小尺寸版):点在于减小最终生成文件的大小。

    • Release with Debug Information(带调试信息的发布版):在发布版的基础上,保留了一些调试信息。

  3. 选择构建配置

    用户点击 gear-button(构建配置)按钮或右键项目节点在上下文菜单中选择"构建配置"进入设置窗口,修改详细构建参数。

    • Arch:用于指定编译器生成适用于哪个目标处理器架构的代码。例如"rvimac" 表示 支持 32 位整数运算、乘除法、原子操作和压缩指令的 RISC-V 架构。

      对于用户指定的 Arch,如果 ZStudio 没有在工具链中检测到可用的库,将会提醒用户在包管理器中下载。所有安装的 ZCC 软件包都将直接安装进工具链中并应用于全局。

      指定 Arch 并下载对应的库

      指定 Arch 并下载对应的库

    • ABI:用于指定 RISC-V 应用程序二进制接口,例如“ilp32”,表示在 32 位系统中,整数、长整型和指针数据类型均为 32 位。

    • CPU:用于启用特定的优化,相当于同时指定 Arch、ABI 和调度模型并且优先级最高。

    • 编译器选项允许用户指定编译器参数,例如优化级别和调试级别等。

    • 链接器选项允许用户禁用默认的库和起始文件,指定链接脚本等。

    • 汇编器选项允许用户指定汇编器参数。

    • NM/Readelf/Objdump/Objcopy/Size工具允许用户启用/禁用输出并控制输出内容。

    详细的工具链配置参考配置构建选项小节。

    配置构建选项
  4. 构建项目

    点击项目快捷控制build-button 按钮或菜单栏中的"Build"开始构建。

  5. 检查构建结果

    开始构建后,底部的构建控制台会自动输出构建日志信息;用户可通过构建控制台的下拉框过滤不同的输出信息。

  6. 检查生成文件

    构建完成后,在项目导航或文件导航 output 节点中检查是否生成了项目可执行文件或目标文件。

    检查构建结果
  7. 下载生成文件至本地

    ZStudio Cloud 支持将构建成功后生成的文件下载至本地。在**文件导航(File Navigator)**中右键 output 节点下的文件,选择"下载"并指定本地路径。

    下载输出文件至本地

配置构建选项

用户针对某个项目自定义构建选项,可以右键项目节点在上下文菜单中选择"构建配置"进入设置窗口,进入 工具设置(Tool Settings) 页面,选择合适的构建选项或添加自定义参数。

  • 编译器 > 优化

    用户可以在此处修改优化等级,优化选项以及指定项目使用的代码模型等。

    • 优化等级:此选项可以控制编译器对其生成的代码的优化程度。用户根据需要在-o0 至-os 中选择合适的优化等级以平衡程序性能和代码大小。

    • 代码模型:ZCC 编译器目前支持 medany, medlow 选项,在 RISC-V 架构中分别等效于 GCC 编译器的 medium, small 选项,参考下表。

      ZCC 编译器支持的 RV32 及 RV64 代码模型
      Code ModelZCCGCCtext/data/rodata
      RISC-V 架构medlowsmall整个 RV32 地址空间或 RV64 地址空间最低 2GiB 和最高 2GiB 的范围
      RISC-V 架构medanymedium整个 RV32 地址空间或 RV64 地址空间基于当前 PC ± 2GiB 的寻址范围
    • 循环展开:用户在下拉框中选择对应选项以控制传递给编译器的参数。传递 -funroll-loops 参数给编译器即完全展开循环;传递 -fno-unroll-loops 参数给编译器即关闭循环展开,如果选择"不传任何参数给编译器"则编译器默认进行部分循环展开。

    • 链接时优化:ZCC 编译器默认开启链接时优化,如果需要关闭链接时优化,需要给 ZCC 编译器传递 -fno-lto 参数,但其他工具链,例如 GCC 和 Clang 默认不开启链接时优化。需要使用 -flto 参数来启用链接时优化。因此用户可以通过下拉框手动开启( -flto )或关闭( -fno-lto )链接时优化或选择"使用编译器默认参数",即是否启用链接时优化由当前编译器的默认选项决定。

    • 移除未使用的函数段 (-ffunction-sections) 和数据段 (-fdata-sections):允许移除不被引用的函数段或数据段。当同时选择这两个选项时,编译器会将每个函数/数据项放置在输出文件的独立代码段中,然后将 --gc-sections 选项传递给链接器,以移除从未被引用的函数/数据项。如果 ZCC(或其他工具链)充当链接器使用,则使用 -wl, --gc-sections 选项。

      编译器优化选项
    • 其他优化参数:此选项可以用于手动添加优化参数或关闭针对特定优化级别开启的默认优化选项。

  • 编译器 > 调试

    用户可以在此处指定生成调试信息的详细程度。

    • 不显示:不生成或显示任何调试信息。
    • 最低级别:记录基本的调试信息,如关键错误或检查点。
    • 默认级别:记录更详细的信息,包括变量值或函数调用跟踪。
    • 最高级别:最高级别的详细信息,捕获大量信息以进行彻底的调试分析。
  • 链接器 > 常规

    用户可以在此处配置链接选项,例如禁用标准启动文件,禁用默认库文件,禁用系统启动文件和库文件,指定链接脚本等。

    链接脚本
    • 链接脚本 : 指定一个链接器脚本链接进项目,用户需要在此处明确指定一个链接器脚本。ZCC 的链接器不支持链接脚本里面的 DEFINED 宏,对于使用了 DEFINED 的 链接脚本,需要如下修改。关于 ZCC 对链接脚本的支持请参考 ZCC 使用手册

      -  __stack_size = DEFINED(__stack_size) ? __stack_size : 2K;
      + __stack_size = 2K;
  • 目标文件处理工具

    当启用目标文件处理工具(NM、Readelf、Objdump、Objcopy 和 Size 工具)时,在项目构建完成后,相关的输出结果将生成在项目目录树下的输出节点中。

运行

ZStudio 提供了多种运行项目的方式,用户需要为需要为不同的项目及其运行平台创建必要的配置。

连接开发板运行

ZStudio 支持使用 OpenOCD 连接开发板运行或调试项目,参考连接开发板调试以及示例项目 Hello_with_dsp_and_nn_sdk_0.4.0.

以仿真器运行

用户可参考以下步骤在仿真器上运行项目:

  1. 确认运行参数

    点击 gear-button(运行配置)按钮打开弹窗可修改详细参数。

    配置运行参数 1

    配置运行参数 2

    • 程序参数:在此处指定传给运行的程序需要的参数。

    • 连接目标:选择程序运行在仿真器上或在开发板上。

    • 选择开发板:运行在仿真器上的程序需要选择虚拟开发板。

      • 内置:需要在仿真器虚拟开发板下拉列表中选择内置虚拟开发板。
      • 自定义:需要在开发板配置文件中指定 .zvb 文件。
    • 仿真精度等级:

      • 快速功能仿真:运行速度更快,主要用于验证程序功能正确性,此模式只能得到少量性能数据。
      • 时钟精确仿真:运行速度较慢,但可以获取到程序运行的 Cycle 信息,也可以执行进一步性能分析(profiling)。
    • 仿真器参数:在此处指定传给仿真器需要的参数。例如:使用 --stat 选项查看程序运行后的分析数据或统计信息。

      添加仿真器参数

      添加参数后运行

  2. 点击项目快捷控制run-button 按钮或菜单栏中的"Run",ZStudio 根据当前活动项目调用 ZEMU 运行可执行文件,运行结果将自动输出到底部面板的运行控制台中。

    运行输出

调试

ZStudio 内置了兆松科技的 ZDB 调试器,用于帮助快速查找代码中的错误,修复项目。

连接开发板调试

连接开发板调试需要用户构建项目后将项目烧录到开发板设备上:

  1. 将开发板连接到当前 PC,确认计算机可以正确识别开发板设备。

    提示

    请遵循各厂商开发板用户手册并检查是否需要安装驱动。

  2. 新建一个串行端口连接

    在"视图"下拉菜单中选择"串口监控工具",点击 + 按钮配置串行端口连接参数,在串行端口下拉框中选择自动检测到的串行通讯端口,点击"确定"创建串口连接。

    创建串行端口连接

    状态灯变为绿色代表当前串行端口连接成功。开发板与当前 PC 断开连接将会导致串行端口连接失败,状态灯变为红色。

    串行端口连接成功

    查看下方表格了解串口监控工具提供的所有配置选项。

    串口监控工具配置选项
    设置项使用说明可用选项
    串行端口使用“串行端口”下拉列表选择端口自动检测连接到计算机的串行端口兼容设备
    波特率使用"波特率"下拉列表或输入自定义参数决定串口监控工具尝试与连接的设备通信的频率300、1200、2400、4800、9600、19200、38400、57600、74880、115200、230400、250000、自定义参数
    数据位使用"数据位"下拉列表选择用于串行端口连接的数据位数5, 6, 7, 8
    奇偶校验使用"奇偶校验"下拉列表选择用于串行端口连接的奇偶校验无、奇数、偶数、标记、空格
    停止位使用"停止位"下拉列表选择用于串行端口连接的停止位数1, 1, 5, 2
  3. 添加并启动 OpenOCD

    在"视图"下拉菜单中选择"OpenOCD 资源管理器",点击 + 按钮创建 OpenOCD 配置,用户需要指定 OpenOCD 可执行文件和配置文件在当前 PC 文件系统中的位置。

    添加 OpenOCD 配置
    提示

    请遵循各厂商开发板用户手册下载 OpenOCD 可执行文件和配置文件。

    右键已添加的 OpenOCD 选择开启或关闭当前 OpenOCD 的进程,状态灯变为绿色代表当前 OpenOCD 进程正在进行。

    成功启动 OpenOCD
    提示

    开发板电源关闭或与 PC 断开连接均可能导致 OpenOCD 与开发板的连接断开,此时状态灯为绿色仅代表当前 OpenOCD 进程正在进行,需检查后重启 OpenOCD。

  4. 指定运行和调试配置

    项目快捷控制点击 gear-button(调试配置)打开配置窗口。指定开发板为运行和调试的连接目标,选择与开发板匹配的 OpenOCD 配置。

    确认调试配置
  5. 添加断点,开启调试会话。调试器运行到它遇到第一个断点停止。点击 "Step Over" 并在串口监控工具中检查项目输出。

关于连接开发板调试的示例项目,请参考Hello_with_dsp_and_nn_sdk_0.4.0

以仿真器调试

用户可以遵循以下步骤在仿真器上调试项目:

  1. 将项目切换成 Debug 配置然后重新构建。
  2. 构建成功后点击"齿轮"按钮更改调试配置,调试器链接目标默认为 Simulator,可添加参数。
  3. 添加断点,点击 debug-button 按钮,开启调试会话。
提示

请检查 Windows Defender 防火墙是否允许 zemu.exe 通过防火墙。

调试开始之前工具栏为不可选中状态(灰色),开启调试会话后用户可以进行相关快捷操作。从左至右依次为:

调试工具栏
  • 继续/暂停(Continue) F5
  • 跨过(Step Over) F10:允许调试器单步执行程序而不会进入子函数,并在执行完当前行后直接跳到下一行。即使当前行包含了函数调用,Step Over 也会跳过函数的执行,直接到达当前函数的下一行指令。
  • 步入(Step Into) F11:允许调试器进入被调用函数的代码内部并逐步执行其中的语句。如果在函数中存在其他函数调用,可以继续使用 Step Into 命令来进一步深入函数调用链。
  • 跳出(Step Out) Shift+F11:允许调试器恢复运行直至函数返回。
  • 重启(Restart) Control+Shift+F5
  • 停止(Stop) Shift+F5

用户可以在调试工具栏下方中查看与调试相关的信息:

调试相关信息
  • 线程(THREADS):查看正在调试的代码中的线程。
  • 调用堆栈(CALL STACK):查看线程调用的函数,检查代码执行流程。
  • 寄存器(REGISTERS):查看寄存器信息。
  • 变量(VARIABLES):查看局部、全局变量信息。
  • 监视(WATCH):跟踪某个变量或复杂表达式的结果。
  • 断点(BREAKPOINTS):与断点相关的操作及信息。

调试开始后,状态栏会改变为橙色。用户点击"为当前活动打开配置"可以快速更改调试配置;点击"在开发板/仿真器上调试"快速更改调试目标。

调试状态栏

设置断点

用户可以设置一个或多个断点,当程序命中断点时,程序暂停,设置断点可以帮助用户查看变量的值,或者查看此段代码是否正常执行。

设置断点后,用户可以在调试视图中 BREAKPOINTS(断点)部分查看看所有断点,并进行删除、编辑、禁用或再次启用等操作,图示按钮从左至右依次为:

断点相关操作
  • 添加函数断点
  • 停用/激活所有断点
  • 删除所有断点

此外,用户也可以直接右键单击列表中某个断点,选择删除停用激活等操作。

行断点

行断点(Line Breakpoint)是调试过程中最常用的断点类型,用户在编辑器中单击代码行左侧的空白处,断点显示为一个红点。点击开始按钮进行调试,调试器运行到它遇到第一个断点停止:

设置行断点

异常断点

异常断点(Exception Breakpoint)是针对程序运行时异常情况的一种断点类型。ZStudio 能够捕获 C++ 的异常处理,当程序抛出异常( throw )或捕获异常( catch )时,调试器会中断程序的执行。异常断点可以帮助用户快速定位和解决程序中的异常问题,比如访问空指针、除以零等。以下图的整数除法函数为例:

异常断点
  1. 单击调试窗口左侧的 BREAKPOINTS(断点)部分展开视图。
  2. 勾选 C++ Catch 和 C++ Throw 异常处理。
  3. 开始调试。

当程序抛出异常时,C++ Throw 能够捕获这个异常事件。此处 division 函数抛出了一个异常。

C++ Throw

C++ Catch 用于捕获抛出的异常,当异常发生时,程序会跳转到 catch 语句所指定的处理逻辑,停止继续执行后面的代码。此处 division 函数捕获了一个异常。

C++ Catch

函数断点

当用户想调试程序中的任意一个函数但由于行数过多难以对其快速定位时,用户可以设置函数断点(Function Breakpoint),以下图的 C++ 代码为例,用户可以通过以下步骤使用函数断点。

点击“添加函数断点”按钮
  1. 单击调试窗口左侧的 BREAKPOINTS(断点)部分展开视图。
  2. 点击"添加函数断点"按钮打开函数断点设置对话框。
  3. 输入函数名,点击确定。

当程序运行到设定的函数断点时暂停执行。

设置函数断点

条件断点

用户可以指定条件设置条件断点(Conditional Breakpoint),根据条件和变量的值来检查程序执行情况,当调试器判断在断点上设置的条件满足时,暂停程序执行;否则恢复运行。用户可以设置两种类型的条件断点。

  • 表达式:当调试器判断在断点上设置的表达式满足时,暂停程序执行。
  • 命中次数:当调试器达到用户设定的命中次数时,暂停程序执行。如果用户预期程序中的循环在执行了一定次数后会发生错误,可以设置一个指定命中次数的条件断点。

以下图的 C 代码为例,用户可以通过以下步骤使用条件断点。

设置表达式条件断点
  1. 右键单击代码行左侧的空白处,选择"添加条件断点"。如果已经添加了行断点,也可右键单击该断点,选择"编辑断点"。

  2. 在设置窗口下拉框选择条件断点的类型为"表达式"或"命中次数",并根据选择类型设置条件。

    • 表达式:可以输入任何调试器认可的有效表达式,当表达式满足时调试器停止。

      条件为表达式
    • 命中次数:指定触发次数。当达到指定的命中次数时,程序停止。

      条件为命中次数
  3. Enter 键确认条件断点。

条件断点在编辑器中显示为中间有一个等号的红色断点标志。用户可以将鼠标悬停在条件断点上查看设置的条件。 当程序执行到条件断点处时,调试器会先检查该条件是否成立,表达式i==3成立时程序暂停执行。

当条件满足时程序停止执行

查看内存

当用户在调试时,随时可能需要查看进程的内存数据或观察数据的变化,例如在调试数据处理问题时,您可能需要查看运行中进程的原始内存。内存视图默认显示在右侧面板,点击侧栏图标即可展开视图,用户可以随时检查内存数据。

内存视图

用户可以通过以下步骤使用查看内存功能:

  1. 获取地址:用户可以在 REPL 窗口输入`p &variable_name获取需要查看的变量的地址。
  2. 跳转地址:将十六进制数地址输入到内存视图的地址栏中,点击"转到(GO)"字段跳转到指定地址;内存视图默认显示为一个 256 字节的区域,从所选地址开始,依次往后排序。可以向上或向下查看更多数据,也可以通过修改 长度 字段来设置获取的数据。

以下图的 C 代码为例,当用户需要检查数组 array 的值在内存中的变化时,可以在内存视图中查看相应地址。当用户单步执行代码时,内存视图会标出当前显示的内存区域中发生的更改。

检查内存

设置格式

如果用户需要更改内存视图数据显示格式,单击视图中右上角的"设置"按钮,例如用户可以将字节大小设为 8,每组字节数设为 4,每行的组数设为 1,从而更直观地观察连续内存的变化。改变格式并不会改变实际的内存数据只会改变查看内存数据方式。

更改内存视图数据显示格式

查看寄存器

ZStudio 支持在调试过程中,随时查看寄存器的值,用户可以展开调试视图的寄存器部分。以下面的 C 程序为例,用户可以通过以下步骤在调试过程中设置寄存器值达到预期的调试效果:

设置寄存器的值
  1. 启动调试器直到调试器达到断点停止。
  2. 在调试器中展开寄存器部分,并找到目标寄存器。
  3. 双击寄存器或右键选择"设置值",并在输入框中输入新的寄存器值,点击"确定"。

查看变量

所有局部、全局变量信息都显示在 VARIABLES 列表中。当调试器暂停时,将鼠标悬停在变量上即可查看相关信息,也可以在 VARIABLES 列表中查看变量的值,用户在进行单步调试时,可观察执行每一步时变量的变化。

检查变量

如果用户需要修改成程序中变量的值来测试程序在某些条件下的行为,或者是在运行时修改程序的当前行为,可以通过以下步骤设置变量值达到预期的调试效果:

  1. 展开调试器视图 VARIABLES 部分,双击目标变量或右键选择"设置值"。
  2. 在输入框中输入新的值,点击"确定"。

当调试器暂停时,用户可以在 VARIABLES 列表进行如下操作:

  • 复制值: 复制变量的值
  • 复制表达式: 复制变量的名字

监视变量或表达式

调试器的监视(watch)可以帮助程序员在调试过程中跟踪特定变量或内存区域的值和状态,以下图的当用户想跟踪某个变量或者表达式的结果,可以为该变量或表达式设置监听。

  1. 单击 WATCH 部分中的 + 按钮,打开对话框。
  2. 输入变量或表达式的名称,然后点击"确定"。

设置完后运行程序,当程序运行到设置断点的行时,程序会暂停在该处并显示变量的值。此时用户可以逐步执行程序并观察变量 sum 的值,以判断程序是否按照预期方式进行计算。

设置监视变量`sum`

**WATCH(监视)**部分中其他可以操作包括:

  • 全部折叠
  • 移除所有表达式

调试控制台

**调试控制台(Debug Console)**视图一般用于输出运行结果,调试信息。在菜单栏中选择 "视图" > "调试控制台" (或使用快捷键 Control+Shift+Y )来打开调试控制台。调试开始,ZStudio 会自动调出 Debug Console 面板并显示调试输出。

REPL

调试控制台中 REPL(Read-Eval-Print Loop)是一种读取 - 求值 -输出的交互式编程环境,在调试过程中,REPL 可以用于计算变量或者函数的返回值。点击调试视图右上角的 REPL-button 按钮打开 REPL 输入框。

下图的累加器函数,循环在执行过程中,在底部的 REPL 窗口输入 i+count,然后按 Enter 发送,Debug Console 会输出当前变量 i+count 的返回值。

Debug console REPL

在调试过程中,当 ZStudio 提供的常用调试功能无法满足一些高级调试的需求时,REPL 也可以用作输入命令的工具,允许用户直接输入底层的调试命令,REPL 支持几乎所有的底层调试命令。输入命令时,需要以**`**开头,否则会被 REPL 解释为查看变量值。一些常见的调试命令包括:

  • bt:打印函数调用堆栈。
  • p <expression>:打印表达式的值。

下图的累加函数,循环在执行过程中,在底部的 REPL 窗口输入 `p &i ,然后键入 Enter 发送,Debug Console 会输出当前变量的地址。

通过REPL使用命令与调试器进行交互
提示

必须处于正在运行的调试中才能使用调试控制台 REPL;在使用 REPL 输入命令时,需考虑调用函数或者改变变量值的潜在风险,避免对程序造成意外影响。

反汇编视图

ZStudio 在调试中提供了反汇编视图,用于显示程序的汇编指令及示 CPU 寄存器、内存地址和指令流等。用户可以通过以下步骤切换反汇编视图。

  1. 确认调试正在运行。
  2. 右键单击编辑器中的源码视图选择"打开反汇编视图(Open Disassembly View)"。
打开反汇编视图