0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

riscv64裸機(jī)編程實踐與分析

嵌入式IoT ? 來源:嵌入式IoT ? 作者:嵌入式IoT ? 2020-12-31 10:54 ? 次閱讀

riscv64 裸機(jī)編程實踐與分析

  • 1.概述

  • 2.最小工程的構(gòu)成

  • 3. 鏈接腳本

  • 4.可執(zhí)行的程序源代碼分析

  • 5.編譯與運行

    • 5.1 編譯

    • 5.2 運行

    • 5.3 調(diào)試

  • 6.總結(jié)

1.概述

任何芯片在啟動之前都需要有一段匯編代碼,從這段匯編代碼上就可以體現(xiàn)一些架構(gòu)設(shè)計的特點。往往做嵌入式底層開發(fā)都需要關(guān)注這段匯編代碼的含義,這樣在使用的時候才能全面的了解啟動時做了什么事情,在后續(xù)的程序中遇到問題也能復(fù)盤推演。

本文就針對riscv64的最開始的啟動部分代碼進(jìn)行分析,從最小的一個裸機(jī)代碼開始分析,徹底的弄清楚riscv啟動的流程。

本次使用的環(huán)境是riscv64 qemu,而編譯器是通過下面的地址進(jìn)行下載

https://www.sifive.com/software

2.最小工程的構(gòu)成

一個最小的工程包含兩個東西:鏈接腳本以及源代碼。

源代碼就是可以讓cpu執(zhí)行的代碼,通過交叉編譯工具鏈編譯生成可執(zhí)行的二進(jìn)制程序。

鏈接腳本文件則可以告訴程序的布局,比如代碼段,函數(shù)的入口等等。有了這兩個文件將編譯出來的程序loader到板子上運行即可。

3. 鏈接腳本

下面看一下hello.ld文件。

OUTPUT_ARCH("riscv")
OUTPUT_FORMAT("elf64-littleriscv")
ENTRY(_start)
SECTIONS
{
/*text:testcodesection*/
.=0x80000000;
.text:{*(.text)}
/*data:Initializeddatasegment*/
.gnu_build_id:{*(.note.gnu.build-id)}
.data:{*(.data)}
.rodata:{*(.rodata)}
.sdata:{*(.sdata)}
.debug:{*(.debug)}
.+=0x8000;
stack_top=.;

/*Endofuninitalizeddatasegement*/
_end=.;
}

對于鏈接腳本(linker script),往往都是規(guī)定如何把輸入的文件按照特定的地址放到內(nèi)存中。

其中就上面的腳本而言:

OUTPUT_ARCH("riscv"):表示輸入文件的架構(gòu)是riscv。

OUTPUT_FORMAT("elf64-littleriscv"):表示elf64小端。一般arm,riscv,x86都是小端,小端是比較主流的。

ENTRY( _start ):表示函數(shù)入口是_start。

然后開始進(jìn)行代碼段的布局,起始地址開始處為0x80000000。然后依次放代碼段、數(shù)據(jù)段、只讀數(shù)據(jù)段、全局?jǐn)?shù)據(jù)段,debug段等等。

這里需要注意:

.+=0x8000;
stack_top=.;

這里說明,棧頂預(yù)留了0x8000個字節(jié)空間作為程序的??臻g,因為棧是向上增長的,所以這里預(yù)留了一些??臻g。

通過反匯編來查看生成程序的布局情況

#riscv64-unknown-elf-objdump-dhello

hello:fileformatelf64-littleriscv


Disassemblyofsection.text:

0000000080000000<_start>:
80000000:f14022f3csrrt0,mhartid
80000004:00029c63bnezt0,8000001c
80000008:00008117auipcsp,0x8
8000000c:04410113addisp,sp,68#8000804c<_end>
80000010:00000517auipca0,0x0
80000014:03450513addia0,a0,52#80000044
80000018:008000efjalra,80000020

000000008000001c:
8000001c:0000006fj8000001c

0000000080000020:
80000020:100102b7luit0,0x10010
80000024:00054303lbut1,0(a0)
80000028:00030c63beqzt1,80000040
8000002c:0002a383lwt2,0(t0)#10010000
80000030:fe03cee3bltzt2,8000002c
80000034:0062a023swt1,0(t0)
80000038:00150513addia0,a0,1
8000003c:fe9ff06fj80000024
80000040:00008067ret

對于qemu來說,sifive_u的起始地址為0x80000000,將代碼段的入口放在此處。

4.可執(zhí)行的程序源代碼分析

前面已經(jīng)描述了鏈接腳本的布局,也就是給程序指定了執(zhí)行的地址,每個函數(shù)以及函數(shù)入口在什么地址都已經(jīng)規(guī)劃好了,那么具體的入口函數(shù)該如何寫呢?

看看hello.s編程代碼:

.align 2
.equ UART_BASE,         0x10010000
.equ UART_REG_TXFIFO,   0

.section .text
.globl _start

_start:
        csrr  t0, mhartid             # read hardware thread id (`hart` stands for `hardware thread`)
        bnez  t0, halt                   # run only on the first hardware thread (hartid == 0), halt all the other threads

        la    sp, stack_top           # setup stack pointer

        la    a0, msg                 # load address of `msg` to a0 argument register
        jal   puts                    # jump to `puts` subroutine, return address is stored in ra regster

halt:   j     halt                    # enter the infinite loop

puts:                                 # `puts` subroutine writes null-terminated string to UART (serial communication port)
                                      # input: a0 register specifies the starting address of a null-terminated string
                                      # clobbers: t0, t1, t2 temporary registers

        li    t0, UART_BASE           # t0 = UART_BASE
1:      lbu   t1, (a0)                # t1 = load unsigned byte from memory address specified by a0 register
        beqz  t1, 3f                  # break the loop, if loaded byte was null

                                      # wait until UART is ready
2:      lw    t2, UART_REG_TXFIFO(t0) # t2 = uart[UART_REG_TXFIFO]
        bltz  t2, 2b                  # t2 becomes positive once UART is ready for transmission
        sw    t1, UART_REG_TXFIFO(t0) # send byte, uart[UART_REG_TXFIFO] = t1

        addi  a0, a0, 1               # increment a0 address by 1 byte
        j     1b

3:      ret

.section .rodata
msg:
     .string "Hello.
"

根據(jù)匯編語言的規(guī)則

.align2

表示入口程序以2^2也就是4字節(jié)對齊。

.equUART_BASE,0x10010000
.equUART_REG_TXFIFO,0

定義了UART的寄存器的基地址。

接著主要從_start:開始分析。

csrrt0,mhartid#readhardwarethreadid(`hart`standsfor`hardwarethread`)
bnezt0,halt#runonlyonthefirsthardwarethread(hartid==0),haltalltheotherthreads

根據(jù)riscv的設(shè)計,如果一個部件包含一個獨立的取指單元,那么該部件被稱為核心(core)。

一個RiscV兼容的核心能夠通過多線程技術(shù)(或者說超線程技術(shù))支持多個RiscV兼容硬件線程(harts),harts這兒就是指硬件線程, hardware thread的意思。

ba4f8054-4ad0-11eb-8b86-12bb97331649.png


上面的就包含一個E51的核和4個U54的核。

而這段匯編就是將其他的核掛起,只運行hartid == 0的核。

緊接著

lasp,stack_top#setupstackpointer

這里將棧指針sp賦值,sp此時指向棧頂。

laa0,msg#loadaddressof`msg`toa0argumentregister
jalputs#jumpto`puts`subroutine,returnaddressisstoredinraregster

對于riscv 架構(gòu)來說,a0寄存器表示第一個參數(shù)賦值,接著跳轉(zhuǎn)到puts函數(shù)中。

此時傳遞過去的參數(shù)為a0,也就是

.section.rodata
msg:
.string"Hello.
"

指向一個只讀的字符串結(jié)構(gòu)的數(shù)據(jù)。

puts的實現(xiàn)

通過匯編來描述一個串口驅(qū)動程序的編寫是比較重要的。

puts:#`puts`subroutinewritesnull-terminatedstringtoUART(serialcommunicationport)
#input:a0registerspecifiesthestartingaddressofanull-terminatedstring
#clobbers:t0,t1,t2temporaryregisters

lit0,UART_BASE#t0=UART_BASE
1:lbut1,(a0)#t1=loadunsignedbytefrommemoryaddressspecifiedbya0register
beqzt1,3f#breaktheloop,ifloadedbytewasnull

#waituntilUARTisready
2:lwt2,UART_REG_TXFIFO(t0)#t2=uart[UART_REG_TXFIFO]
bltzt2,2b#t2becomespositiveonceUARTisreadyfortransmission
swt1,UART_REG_TXFIFO(t0)#sendbyte,uart[UART_REG_TXFIFO]=t1

addia0,a0,1#incrementa0addressby1byte
j1b

3:ret

首先剛才通過a0寄存器將參數(shù)傳遞過來,然后從1:開始,讀取字符串,beqz t1, 3f表示當(dāng)t1 == 0時,跳轉(zhuǎn)到3:之前。此時會跳出2:循環(huán)。

2:則是向串口FIFO送數(shù)的過程。

到這里一個字符串輸出就可以正常的執(zhí)行了。

5.編譯與運行

5.1 編譯

上述程序分析完成會,可以將其進(jìn)行編譯。

riscv64-unknown-elf-gcc-march=rv64g-mabi=lp64-static-mcmodel=medany-fvisibility=hidden-nostdlib-nostartfiles-Thello.ld-Isifive_uhello.s-ohello

上述編譯過程可以生成hello程序。

#readelf-hhello
ELFHeader:
Magic:7f454c46020101000000000000000000
Class:ELF64
Data:2'scomplement,littleendian
Version:1(current)
OS/ABI:UNIX-SystemV
ABIVersion:0
Type:EXEC(Executablefile)
Machine:RISC-V
Version:0x1
Entrypointaddress:0x80000000
Startofprogramheaders:64(bytesintofile)
Startofsectionheaders:4680(bytesintofile)
Flags:0x0
Sizeofthisheader:64(bytes)
Sizeofprogramheaders:56(bytes)
Numberofprogramheaders:1
Sizeofsectionheaders:64(bytes)
Numberofsectionheaders:7
Sectionheaderstringtableindex:6

可以分析一下gcc攜帶的參數(shù)。

-march:可以指定編譯出來的架構(gòu),比如rv32或者rv64等等。

-static:表示靜態(tài)編譯。

-mabi=lp64:數(shù)據(jù)模型和浮點參數(shù)傳遞規(guī)則

數(shù)據(jù)模型:

- int字長 long字長 指針字長
ilp32/ilp32f/ilp32d 32bits 32bits 32bits
lp64/lp64f/lp64d 32bits 64bits 64bits

浮點傳遞規(guī)則

- 需要浮點擴(kuò)展指令? float參數(shù) double參數(shù)
ilp32/lp64 不需要 通過整數(shù)寄存器(a0-a1)傳遞 通過整數(shù)寄存器(a0-a3)傳遞
ilp32f/lp64f 需要F擴(kuò)展 通過浮點寄存器(fa0-fa1)傳遞 通過整數(shù)寄存器(a0-a3)傳遞
ilp32d/lp64d 需要F擴(kuò)展和D擴(kuò)展 通過浮點寄存器(fa0-fa1)傳遞 通過浮點寄存器(fa0-fa1)傳遞

-mcmodel=medany:對于-mcmodel=medlow-mcmodel=medany

-mcmodel=medlow

使用 LUI 指令取符號地址的高20位。LUI 配合其它包含低12位立即數(shù)的指令后,可以訪問的地址空間是 -2GiB ~ 2GiB。

對于 RV64 而言,能訪問的就是 0x0000000000000000 ~ 0x000000007FFFFFFF,以及 0xFFFFFFFF800000000 ~ 0xFFFFFFFFFFFFFFFF 這兩個區(qū)域,前一個區(qū)域即 +2GiB 的地址空間,后一個區(qū)域即 -2GiB 的地址空間。其它地址空間就訪問不到了。

-mcmodel=medany

使用 AUIPC 指令取符號地址的高20位。AUIPC 配合其它包含低12位立即數(shù)的指令后,可以訪問當(dāng)前 PC 的前后2GiB (PC - 2GiB ~ PC + 2GiB)的地址空間。

對于RV64,取決于當(dāng)前 PC 值,能訪問到是 PC - 2GiB 到 PC + 2GiB 這個地址空間。假設(shè)當(dāng)前 PC 是 0x1000000000000000,那么能訪問的地址范圍是 0x0000000080000000 ~ 0x100000007FFFFFFF。假設(shè)當(dāng)前 PC 是 0xA000000000000000,那么能訪問的地址范圍是0x9000000080000000~0xA00000007FFFFFFF。

-fvisibility=hidden:動態(tài)庫部分需要對外顯示的函數(shù)接口顯示出來。

-nostdlib:不連接系統(tǒng)標(biāo)準(zhǔn)啟動文件和標(biāo)準(zhǔn)庫文件,只把指定的文件傳遞給連接器。

-nostartfiles:不帶main函數(shù)的入口程序。

-Thello.ld:加載鏈接地址。

5.2 運行

輸入下面的命令即可看到Hello.字符串輸出。

#qemu-system-riscv64-nographic-machinesifive_u-biosnone-kernelhello
Hello.

5.3 調(diào)試

調(diào)試過程比較只需在運行的后面加-s -S,即

qemu-system-riscv64-nographic-machinesifive_u-biosnone-kernelhello-s-S

另外再開一個終端輸入

riscv64-unknown-elf-gdbhello

接著輸入target remote localhost:1234即可。

通過b _start打斷點,并且通過si進(jìn)行單步跳轉(zhuǎn)可實現(xiàn)程序的單步運行。

6.總結(jié)

riscv64最小裸機(jī)程序的運行很好理解,主要梳理清楚其啟動地址與鏈接文件即可。還有就是注意gcc的編譯參數(shù),這些對于riscv的啟動來說也是非常關(guān)鍵的部分。

責(zé)任編輯:xj

原文標(biāo)題:riscv64 裸機(jī)編程實踐與分析

文章出處:【微信公眾號:嵌入式IoT】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3616

    瀏覽量

    93740
  • RISC
    +關(guān)注

    關(guān)注

    6

    文章

    462

    瀏覽量

    83735

原文標(biāo)題:riscv64 裸機(jī)編程實踐與分析

文章出處:【微信號:Embeded_IoT,微信公眾號:嵌入式IoT】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    【「RISC-V體系結(jié)構(gòu)編程實踐」閱讀體驗】-- SBI及NEMU環(huán)境

    基于《RISC-V體系結(jié)構(gòu)編程實踐(第二版)》這本書籍,官方文檔及網(wǎng)上資料繼續(xù)我的RISC-V旅程。 接前面的篇章,今天來看看RISCV-V的SBI、BenOS和MySBI及NEMU環(huán)境。 SBI
    發(fā)表于 11-26 09:37

    【「RISC-V體系結(jié)構(gòu)編程實踐」閱讀體驗】-- 前言與開篇

    發(fā)燒友論壇書籍評測活動中,看到有RISC-V相關(guān)的書籍在評測:《RISC-V體系結(jié)構(gòu)編程實踐(第二版)》,于是抱著僥幸的心理參加了,第一次參加這種書籍或開發(fā)板評測活動,沒想到居然中了,緣分真的挺奇妙
    發(fā)表于 11-23 15:43

    韓國裸機(jī)云服務(wù)器是什么?

    韓國裸機(jī)云服務(wù)器是一種結(jié)合了裸機(jī)服務(wù)器與云端技術(shù),提供多IP地址分配和高性能網(wǎng)絡(luò)服務(wù)的云計算解決方案。主機(jī)推薦小編為您整理發(fā)布韓國裸機(jī)云服務(wù)器的詳細(xì)解釋。
    的頭像 發(fā)表于 11-06 10:11 ?128次閱讀

    freertos和裸機(jī)有什么區(qū)別

    FreeRTOS 和裸機(jī)編程是兩種不同的嵌入式系統(tǒng)開發(fā)方法,它們在設(shè)計理念、資源使用、功能實現(xiàn)等方面有著顯著的差異。 1. 基本概念 1.1 FreeRTOS FreeRTOS 是一個小型的、可裁剪
    的頭像 發(fā)表于 09-02 14:13 ?1430次閱讀

    美國硅谷raksmart站群裸機(jī)服務(wù)器租用費用分析

    RAKsmart是一家提供數(shù)據(jù)中心服務(wù)的公司,其在美國硅谷擁有數(shù)據(jù)中心,并提供包括站群裸機(jī)服務(wù)器在內(nèi)的多種服務(wù)器租賃服務(wù)。站群服務(wù)器通常用于托管大量網(wǎng)站或應(yīng)用程序,因此對硬件性能和網(wǎng)絡(luò)穩(wěn)定性有著較高的要求。接下來我們將對RAKsmart硅谷站群裸機(jī)服務(wù)器的租用費用進(jìn)行
    的頭像 發(fā)表于 08-29 10:05 ?210次閱讀

    東京裸機(jī)云多IP服務(wù)器全面分析

    東京裸機(jī)云多IP服務(wù)器是一種提供多IP地址分配和高性能網(wǎng)絡(luò)服務(wù)的云計算解決方案,廣泛應(yīng)用于需要多IP管理和高穩(wěn)定性的網(wǎng)絡(luò)應(yīng)用。下面將從幾個方面具體介紹東京裸機(jī)云多IP服務(wù)器,rak部落為您整理發(fā)布東京裸機(jī)云多IP服務(wù)器的全面
    的頭像 發(fā)表于 07-22 09:49 ?334次閱讀

    在ubuntu 24.04下嘗試使用riscv64-linux-musleabi_for_x86_64-pc-linux-gnu工具鏈編譯cv1800大核出現(xiàn)報錯的原因?

    在ubuntu 24.04下嘗試使用riscv64-linux-musleabi_for_x86_64-pc-linux-gnu工具鏈編譯cv1800大核,結(jié)果出現(xiàn)如下報錯: /home
    發(fā)表于 07-16 08:20

    洛杉磯裸機(jī)云大寬帶服務(wù)器的特性和優(yōu)勢

    洛杉磯裸機(jī)云大寬帶服務(wù)器是結(jié)合了物理服務(wù)器性能和云服務(wù)靈活性的高性能計算服務(wù),為用戶提供高效、安全的計算和存儲能力。在了解如何使用洛杉磯裸機(jī)云大寬帶服務(wù)器之前,需要了解其基本特性和優(yōu)勢。以下是對洛杉磯裸機(jī)云大寬帶服務(wù)器的具體
    的頭像 發(fā)表于 07-08 10:11 ?251次閱讀

    使用msys2 mingw64編譯nuclei openocd源碼出錯的原因?

    :msys64homeAdministratorbuildnuclei-riscv-openocdbuild/../src/jtag/drivers/mpsse.c:358:(.text+0xc71): undefined reference
    發(fā)表于 05-29 07:52

    谷歌安卓系統(tǒng)即將取消對RISC-V架構(gòu)的支持

    負(fù)責(zé)安卓Linux核心分支開發(fā)的谷歌高級工程師向AOSP提交了一系列補(bǔ)丁,其中顯示“已去除ACK對riscv64的支持”。這些補(bǔ)丁詳細(xì)描述指出“對risc64 GKI內(nèi)核的支持已停止”。
    的頭像 發(fā)表于 04-30 15:40 ?1543次閱讀

    國產(chǎn)riscv芯片大匯總?

    請問有統(tǒng)計國產(chǎn)的riscv芯片的嗎?能匯總一下嗎?
    發(fā)表于 04-27 11:53

    RISCV soft JTAG調(diào)試_v1.2

    因為目前軟件的限制,RISCV的邏輯不能同時共用JTAG,所以如果想要同時去調(diào)試邏輯和RISCV的話,可以通過RISCV的soft Jtag來實現(xiàn)。soft Jtag就是通過GPIO來實現(xiàn)的軟件
    的頭像 發(fā)表于 04-23 08:38 ?1076次閱讀

    全志D1s開發(fā)板裸機(jī)開發(fā)之壞境搭建

    環(huán)境搭建 開發(fā)板介紹 張?zhí)祜w老師編寫的《RISC-V體系結(jié)構(gòu)編程實踐》,里面的源碼是基于 QEMU 模擬器的,可以認(rèn)為它是一款虛擬的開發(fā)板。如果需要在真實開發(fā)板上學(xué)習(xí),可以使用百問網(wǎng)
    發(fā)表于 03-06 13:54

    RISCV soft JTAG調(diào)試_v1.1

    因為目前軟件的限制,RISCV的邏輯不能同時共用JTAG,所以如果想要同時去調(diào)試邏輯和RISCV的話,可以通過RISCV的soft Jtag來實現(xiàn)。soft Jtag就是通過GPIO來實現(xiàn)的軟件
    的頭像 發(fā)表于 02-23 16:16 ?669次閱讀
    <b class='flag-5'>RISCV</b> soft JTAG調(diào)試_v1.1

    【昉·星光 2 高性能RISC-V單板計算機(jī)體驗】為 Ubuntu 安裝 Docker 及常用軟件

    : 獲取鏡像 通常來說,RISC-V 架構(gòu)的開發(fā)板不能使用基于其他架構(gòu)開發(fā)的鏡像,下面是一些基于 RISC-V 鏡像的合集:https://hub.docker.com/u/riscv64/ 安裝其他常用軟件 sudo apt install vim htop net-tools 根據(jù)所需即可。
    發(fā)表于 02-21 17:54