在Ubuntu上使用QEMU学习RISC-V程序(1)起步第一个程序

文章目录

    • 一、 引言
    • 二、 环境准备
    • 三、编写简单的RISC-V程序
    • 四、 编译步骤详解
    • 五、使用QEMU运行程序
    • 六、程序详解
    • 七、退出QEMU
    • 八、总结
    • 附录:QEMU中通过UTRA显示字符工作原理
      • 1、内存映射I/O原理
      • 2、add.s程序工作流程
      • 3、关键指令解析
      • 4、QEMU模拟的UART控制器
      • 5、为什么不需要初始化UART?
      • 6、字符如何显示到终端?
      • 7、扩展知识:处理UART状态
      • 总结

一、 引言

RISC-V作为一种开源指令集架构,近年来在嵌入式系统和高性能计算领域备受关注。借助QEMU模拟器,我们可以在Ubuntu主机上轻松测试和运行RISC-V程序,无需真实硬件。本文将详细介绍如何使用riscv64-unknown-elf-gcc工具链编译一个简单的RISC-V程序,并通过QEMU模拟器启动它。

二、 环境准备

首先需要在Ubuntu系统上安装必要的工具链和依赖:

# 安装RISC-V交叉编译工具链
sudo apt-get update
sudo apt-get install gcc-riscv64-unknown-elf binutils-riscv64-unknown-elf# 安装QEMU模拟器
sudo apt-get install qemu-system-riscv64# 验证安装结果
riscv64-unknown-elf-gcc --version
qemu-system-riscv64 --version

三、编写简单的RISC-V程序

下面是一个简单的RISC-V汇编程序,它将两个数相加并通过串口输出结果:

# add.s - 使用直接UART操作的版本.section .text.globl _start_start:# 设置栈指针la sp, stack_top# 初始化两个数li a0, 10li a1, 20# 相加add a2, a0, a1# 直接操作UART输出结果la a0, msgcall print_stringmv a0, a2call print_intla a0, newlinecall print_string# 无限循环
loop:j loop# 打印字符串函数
print_string:li t0, 0x10000000  # UART基地址
ps_loop:lb t1, 0(a0)       # 加载字符beqz t1, ps_done   # 如果是NULL,结束sb t1, 0(t0)       # 写入UART数据寄存器addi a0, a0, 1     # 指向下一个字符j ps_loop
ps_done:ret# 打印整数函数(简化版)
print_int:li t0, 0x10000000  # UART基地址li t1, 10          # 除数# 将数字转换为ASCII并输出# 此处为简化实现,实际需要更复杂的转换逻辑li t2, '0'add t2, t2, a0sb t2, 0(t0)ret.section .rodata
msg:.string "计算结果: "
newline:.string "\n".section .bss.align 3
stack:.space 4096
stack_top:

四、 编译步骤详解

接下来我们使用RISC-V交叉编译工具链编译这个程序:

# 1. 编译汇编代码为目标文件
riscv64-unknown-elf-as -march=rv64g -mabi=lp64 add.s -o add.o# 2. 创建链接脚本link.ld
cat > link.ld << EOF
ENTRY(_start)SECTIONS {.text 0x80000000 : {*(.text)}.data : {*(.data)}.bss : {*(.bss)}
}
EOF# 3. 链接目标文件
riscv64-unknown-elf-ld -T link.ld add.o -o add.elf# 4. 转换为二进制格式
riscv64-unknown-elf-objcopy -O binary add.elf add.bin# 5. 生成可执行文件
riscv64-unknown-elf-objcopy -O elf64-littleriscv add.elf add

五、使用QEMU运行程序

编译完成后,我们可以使用QEMU模拟器运行生成的RISC-V程序:

qemu-system-riscv64 \-machine virt \-cpu rv64 \-m 128M \-nographic \-bios none \-kernel add.elf \

如果一切正常,你将在终端看到以下输出:

计算结果: 3

六、程序详解

这个简单的RISC-V程序包含几个关键部分:

  1. 初始化部分:设置栈指针并初始化要相加的两个数
  2. 计算部分:执行加法运算
  3. 输出部分:通过系统调用将结果输出到UTRA
  4. 退出部分:调用exit系统调用结束程序

值得注意的是,我们使用了QEMU虚拟平台提供的系统调用接口来实现输出功能。在真实硬件上,可能需要通过操作UART寄存器来实现相同的功能。

七、退出QEMU

退出qemu-system-riscv64通常可以使用快捷键或通过监视器界面来操作,具体方法如下:

  • 使用快捷键:按下Ctrl + a,然后松开这两个键,再按下x,即可直接终止QEMU进程,回到shell界面。
  • 通过监视器界面:首先按下Ctrl + a,然后松开,再按下c,这将退出当前操作系统的shell界面,进入QEMU的监视器界面。接着在监视器界面中,输入q并按回车键,即可完全退出QEMU。

八、总结

通过本文的步骤,你已经学会了如何在Ubuntu上使用RISC-V交叉编译工具链编写、编译一个简单的汇编程序,并通过QEMU模拟器运行它。这为进一步开发更复杂的RISC-V应用程序奠定了基础。后续你可以尝试添加更复杂的功能,如C语言支持、设备驱动等。


附录:QEMU中通过UTRA显示字符工作原理

本附录是QEMU系统中,UTRA显示的工作原理。供理解上面add.s程序是如何输出的。

在嵌入式系统中,与外部设备(如屏幕、串口)通信通常通过**内存映射I/O(Memory-Mapped I/O)**实现。在RISC-V架构的QEMU模拟环境中,向特定内存地址写入数据实际上是向模拟的UART(通用异步收发传输器)控制器发送字符,最终显示在终端上。以下是详细的工作原理解析:

1、内存映射I/O原理

在计算机系统中,外设(如串口、硬盘)的控制寄存器被映射到特定的内存地址空间。CPU可以像访问内存一样访问这些地址,从而控制外设的行为。

在QEMU模拟的RISC-V virt 平台中:

  • UART基地址0x10000000
  • 向该地址写入一个字节数据,相当于通过串口发送一个字符
  • 读取该地址,则获取接收到的字符

2、add.s程序工作流程

以下是 add.s 程序的打印关键部分:

# 打印字符串函数
print_string:li t0, 0x10000000  # UART基地址
ps_loop:lb t1, 0(a0)       # 加载字符,a0是调用print_string函数的时候,输入字符串的地址beqz t1, ps_done   # 如果是NULL,结束sb t1, 0(t0)       # 写入UART数据寄存器addi a0, a0, 1     # 指向下一个字符j ps_loop
ps_done:

3、关键指令解析

  1. li(Load Immediate)

    li t0, 0x10000000
    
    • 将立即数(常量)0x10000000 加载到寄存器 t0
    • 相当于 t0 = 0x10000000;
  2. sb(Store Byte)

    sb t1, 0(t0)
    
    • 将寄存器 t1 的低8位(一个字节)存储到地址 t0 + 0
    • 相当于 *(uint8_t*)t0 = t1 & 0xFF;

4、QEMU模拟的UART控制器

QEMU的 virt 平台模拟了一个 16550兼容UART控制器,其简化结构如下:

偏移地址寄存器名称功能
0x00RBR/THR/DLL接收/发送缓冲区
0x01IER/DLM中断使能寄存器
0x02IIR/FCR中断标识/FIFO控制
0x03LCR线路控制寄存器

test.s 中,我们直接操作的是 THR(Transmitter Holding Register)

  • 当向 0x10000000 写入数据时,数据被放入发送缓冲区
  • UART控制器会自动将缓冲区中的数据转换为串行信号发送
  • QEMU捕获这些模拟的串行信号,并将其转换为终端输出

5、为什么不需要初始化UART?

在QEMU的 virt 平台中:

  • UART控制器默认已配置为 8数据位、无校验、1停止位(8N1)
  • 波特率设置为 115200bps
  • 这些默认配置适用于大多数简单应用,因此无需额外初始化

在真实硬件上,通常需要先配置LCR(线路控制寄存器)、IER(中断使能寄存器)等:

# 真实硬件上的UART初始化示例
li t0, 0x10000000     # UART基地址# 设置波特率为115200
li t1, 0x00           # 除数寄存器值(对于115200bps)
sw t1, 0(t0)          # DLL (除数锁存器低位)
sw t1, 1(t0)          # DLM (除数锁存器高位)# 配置为8N1模式
li t1, 0x03           # 8数据位, 1停止位, 无校验
sw t1, 3(t0)          # LCR (线路控制寄存器)

6、字符如何显示到终端?

整个数据流向如下:

  1. CPU执行 sb t1, 0(t0) 指令
  2. 数据被写入内存地址 0x10000000
  3. QEMU检测到对该地址的写操作
  4. QEMU模拟UART控制器的行为,将数据转换为字符
  5. QEMU将字符输出到宿主系统的终端

7、扩展知识:处理UART状态

在更复杂的应用中,需要检查UART状态以确保数据成功发送:

# 带状态检查的UART发送函数
uart_putc:li t0, 0x10000000      # UART基地址li t1, 0x10000005      # LSR (线路状态寄存器)地址wait_tx_ready:lb t2, 0(t1)           # 读取LSRandi t2, t2, 0x20      # 检查THRE位(bit 5)beqz t2, wait_tx_ready # 如果THRE=0,继续等待sb a0, 0(t0)           # 发送字符ret

总结

add.s 程序中,通过向 0x10000000 地址写入数据,实际上是利用了QEMU模拟的UART控制器的内存映射I/O特性。这种方式直接、高效,适用于简单的输出需求。在实际开发中,根据硬件平台的不同,可能需要更复杂的初始化和错误处理逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90304.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90304.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

R拟合 | 一个分布能看到三个峰,怎么拟合出这三个正态分布的参数? | 高斯混合模型 与 EM算法

1. 效果已知数据符合上图分布&#xff0c;怎么求下图的三个分布的参数mu, sigma&#xff0c;及每个分布的权重 lambda&#xff1f; 2. 代码: 高斯混合模型&#xff08;Gaussian Mixture Model&#xff0c;简称GMM&#xff09; library(mixtools) set.seed(123) # 确保结果可重复…

Excel自动分列开票工具推荐

软件介绍 本文介绍一款基于Excel VBA开发的自动分列开票工具&#xff0c;可高效处理客户对账单并生成符合要求的发票清单。 软件功能概述 该工具能够将客户对账单按照订单号自动拆分为独立文件&#xff0c;并生成可直接导入发票清单系统的标准化格式。 软件特点 这是一款体…

【自用】JavaSE--Stream流

概述获取Stream流集合的stream流集合名.stream( );collection集合List集合与Set集合都属于Collection集合&#xff0c;因此可以直接调用stream方法获取stream流&#xff0c;示例如下结果>map集合map集合存在键值对&#xff0c;因此无法使用该方法直接获取stream流&#xff0…

【Elasticsearch】快照与恢复功能详解

《Elasticsearch 集群》系列&#xff0c;共包含以下文章&#xff1a; 1️⃣ 冷热集群架构2️⃣ 合适的锅炒合适的菜&#xff1a;性能与成本平衡原理公式解析3️⃣ ILM&#xff08;Index Lifecycle Management&#xff09;策略详解4️⃣ Elasticsearch 跨机房部署5️⃣ 快照与恢…

技嘉z370主板开启vtx

技嘉z370vtx应该默认就是开启状态&#xff0c;虽然主板开启的vtx但是系统默认设置会导致vtx不能使用 1. 关闭hyper-V,Windows虚拟机监控程序平台,虚拟机平台 控制面板->程序->启用或关闭windows功能 2.以管理员身份运行CMD bcdedit /set hypervisorlaunchtype off 3.…

Springmvc的自动解管理

中央转发器&#xff08;DispatcherServlet&#xff09;控制器视图解析器静态资源访问消息转换器格式化静态资源管理一、中央转发器Xml无需配置<servlet><servlet-name>chapter2</servlet-name><servlet-class>org.springframework.web.servlet.Dispatc…

C#_定时器_解析

问题一:这里加lock是啥意思?它的原理是, 为什么可以锁住? private readonly Timer _timer;/// <summary>/// 构造函数中初始化定时器/// </summary>public FtpTransferService(){// 初始化定时器_timer new Timer(_intervalMinutes * 60 * 1000);_timer.Elapsed…

Trae开发uni-app+Vue3+TS项目飘红踩坑

前情 Trae IDE上线后我是第一时间去使用体验的&#xff0c;但是因为一直排队问题不得转战Cursor&#xff0c;等到Trae出付费模式的时候&#xff0c;我已经办了Cursor的会员&#xff0c;本来是想等会员过期了再转战Trae的&#xff0c;但是最近Cursor开始做妖了 网上有一堆怎么…

低代码中的统计模型是什么?有什么作用?

低代码开发平台中的统计模型是指通过可视化配置、拖拽操作或少量代码即可应用的数据分析工具&#xff0c;旨在帮助技术人员及非技术人员快速实现数据描述、趋势预测和业务决策。其核心价值在于降低数据分析门槛&#xff0c;使业务人员无需深入掌握统计原理或编程技能&#xff0…

Linux 下在线安装启动VNC

描述 Linux中的VNC就类似于Windows中的远程桌面系统 本文只记录在Cent OS 7的系统下在线安装VNC。 安装VNC 1、安装VNC yum install tigervnc-server2、配置VNC的密码 为用户设置 VNC 密码&#xff08;首次运行会提示输入&#xff0c;也可以提前输入&#xff09; vncpasswd密码…

支持OCR和AI解释的Web PDF阅读器:解决大文档阅读难题

支持OCR和AI解释的Web PDF阅读器&#xff1a;解决大文档阅读难题一、背景&#xff1a;为什么需要这个工具&#xff1f;问题场景解决方案二、技术原理&#xff1a;如何实现这些功能&#xff1f;1、核心技术组件2、工作流程3、关键点三、操作指南1、环境准备2、生成Html代码3、We…

研发过程都有哪些

产品规划与定义 (Product Planning & Definition) 在详细的需求调研之前&#xff0c;通常会进行市场分析、竞品分析、确立产品目标和核心价值。这个阶段决定了“我们要做什么”以及“为什么要做”。 系统设计与架构 (System & Architectural Design) 这是开发的“蓝图”…

旧物回收小程序系统开发——开启绿色生活新篇章

在当今社会&#xff0c;环保已经成为全球关注的焦点话题。随着人们生活水平的提高&#xff0c;消费能力不断增强&#xff0c;各类物品的更新换代速度日益加快&#xff0c;大量旧物被随意丢弃&#xff0c;不仅造成了资源的巨大浪费&#xff0c;还对环境产生了严重的污染。在这样…

UE5 UI 水平框

文章目录slot区分尺寸和对齐方式尺寸&#xff1a;自动模式尺寸&#xff1a;填充模式对齐常用设置所有按钮大小一致&#xff0c;不受文本影响靠右排列和unity的HorizontalLayout不太一样slot 以在水平框中放入带文字的按钮为例 UI如下布置 按钮的大小受slot的尺寸、对齐和内部…

【Golang】Go语言变量

Go语言变量 文章目录Go语言变量一、Go语言变量二、变量声明2.1、第一种声明方式2.2、第二种声明方式2.3、第三种声明方式2.4、多变量声明2.5、打印变量占用字节一、Go语言变量 变量来源于数学&#xff0c;是计算机语言中能存储计算结果或能表示值抽象的概念变量可以通过变量名…

Qt WebEngine Widgets的使用

一、Qt WebEngine基本概念Qt WebEngine中主要分为三个模块&#xff1a;Qt WebEngine Widgets模块&#xff0c;主要用于创建基于C Widgets部件的Web程序&#xff1b;Qt WebEngine模块用来创建基于Qt Quick的Web程序&#xff1b;Qt WebEngine Core模块用来与Chromeium交互。网页玄…

【C++】标准模板库(STL)—— 学习算法的利器

【C】标准模板库&#xff08;STL&#xff09;—— 学习算法的利器学习 STL 需要注意的几点及 STL 简介一、什么是 STL&#xff1f;二、学习 STL 前的先修知识三、STL 常见容器特点对比四、学习 STL 的关键注意点五、STL 学习路线建议六、总结七、下一章 vector容器快速上手学习…

YOLO算法演进综述:从YOLOv1到YOLOv13的技术突破与应用实践,一文掌握YOLO家族全部算法!

引言&#xff1a;介绍目标检测技术背景和YOLO算法的演进意义。YOLO算法发展历程&#xff1a;使用阶段划分方式系统梳理各代YOLO的技术演进&#xff0c;包含早期奠基、效率优化、注意力机制和高阶建模四个阶段。YOLOv13的核心技术创新&#xff1a;详细解析HyperACE机制、FullPAD…

快速将前端得依赖打为tar包(yarn.lock版本)并且推送至nexus私有依赖仓库(笔记)

第一步创建js文件 文件名为downloadNpmPackage.jsprocess.env.NODE_TLS_REJECT_UNAUTHORIZED "0";const fs require("fs"); const path require("path"); const request require("request");// 设置依赖目录 const downUrl "…

Unity VS Unreal Engine ,“电影像游戏的时代” 新手如何抉择引擎?(结)

Unity VS Unreal Engine &#xff0c;“电影像游戏的时代” 新手如何抉择引擎&#xff1f;(1)-CSDN博客 这是我的上一篇文章&#xff0c;如果你仍然困惑选择引擎的事情&#xff0c;我们不妨从别的方面看看 注意&#xff1a;我们可能使用"UE5"来表示Unreal Engine系…