在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
英文地址,請注明出處:http://en.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系…