99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

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

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

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

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

嵌入式IoT ? 來源:嵌入式IoT ? 作者:嵌入式IoT ? 2020-12-31 10:54 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

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

  • 1.概述

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

  • 3. 鏈接腳本

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

  • 5.編譯與運(yùn)行

    • 5.1 編譯

    • 5.2 運(yùn)行

    • 5.3 調(diào)試

  • 6.總結(jié)

1.概述

任何芯片在啟動之前都需要有一段匯編代碼,從這段匯編代碼上就可以體現(xiàn)一些架構(gòu)設(shè)計的特點(diǎn)。往往做嵌入式底層開發(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到板子上運(yùn)行即可。

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àn)闂J窍蛏显鲩L的,所以這里預(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è)計,如果一個部件包含一個獨(dú)立的取指單元,那么該部件被稱為核心(core)。

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

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


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

而這段匯編就是將其他的核掛起,只運(yùn)行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的實(shí)現(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.編譯與運(yùn)行

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ù)模型和浮點(diǎn)參數(shù)傳遞規(guī)則

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

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

浮點(diǎn)傳遞規(guī)則

- 需要浮點(diǎn)擴(kuò)展指令? float參數(shù) double參數(shù)
ilp32/lp64 不需要 通過整數(shù)寄存器(a0-a1)傳遞 通過整數(shù)寄存器(a0-a3)傳遞
ilp32f/lp64f 需要F擴(kuò)展 通過浮點(diǎn)寄存器(fa0-fa1)傳遞 通過整數(shù)寄存器(a0-a3)傳遞
ilp32d/lp64d 需要F擴(kuò)展和D擴(kuò)展 通過浮點(diǎn)寄存器(fa0-fa1)傳遞 通過浮點(diǎn)寄存器(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 運(yùn)行

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

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

5.3 調(diào)試

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

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

另外再開一個終端輸入

riscv64-unknown-elf-gdbhello

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

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

6.總結(jié)

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

責(zé)任編輯:xj

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

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


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

    關(guān)注

    88

    文章

    3689

    瀏覽量

    95239
  • RISC
    +關(guān)注

    關(guān)注

    6

    文章

    481

    瀏覽量

    84972

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

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

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    【Milk-V Duo S 開發(fā)板免費(fèi)體驗(yàn)】2 - 安裝編譯環(huán)境并測試Hello world!

    ARM64 2. RISCV64 Which would you like: 我使用的是 RISCV 系統(tǒng),則選擇 2。 這個腳本會克隆GitHub - milkv-duo/host-tools,不過只有
    發(fā)表于 07-07 21:18

    【VisionFive 2單板計算機(jī)試用體驗(yàn)】RISC-V架構(gòu)卡片計算機(jī)初體驗(yàn)

    前言 說實(shí)話,板子收到一周了才發(fā)布第一篇試用報告有點(diǎn)過分,主要是之前沒接觸過RISCV64的板子,原先在ARM板上積累的經(jīng)驗(yàn)貌似在這邊不太行得通,再加上貌似國內(nèi)大廠和高校的源對于RISCV架構(gòu)的支持
    發(fā)表于 07-06 23:28

    【Milk-V Duo S 開發(fā)板免費(fèi)體驗(yàn)】應(yīng)用開發(fā)環(huán)境搭建

    選擇 2。 由于 DuoS 支持 RISCV 和 ARM 兩種架構(gòu),還需要按提示繼續(xù)選擇: Select Arch: 1. ARM64 2. RISCV64 Which would you like
    發(fā)表于 07-01 21:22

    【M-K1HSE開發(fā)板免費(fèi)體驗(yàn)】超高性能與顏值RISCV64位8核視美泰M-K1HSE開發(fā)板

    【超高性能與顏值RISCV64位8核視美泰M-K1HSE開發(fā)板】 https://www.bilibili.com/video/BV1dQKZzsERi/?share_source
    發(fā)表于 06-26 23:14

    請問Openvino是否支持 Risc-V (riscv64) 架構(gòu)?

    在spacemit k1型板(bpi-f3)上編譯OpenVINO?,但失敗。 使用 riscv64 構(gòu)建OpenVINO?并崩潰。
    發(fā)表于 06-24 07:26

    使用msys2 mingw64編譯nuclei openocd源碼出錯怎么解決?

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

    鐵電存儲器SF24C64對標(biāo)FM24C64性能、應(yīng)用和成本分析

    鐵電存儲器SF24C64對標(biāo)FM24C64性能、應(yīng)用和成本分析
    的頭像 發(fā)表于 03-03 10:25 ?722次閱讀
    鐵電存儲器SF24C<b class='flag-5'>64</b>對標(biāo)FM24C<b class='flag-5'>64</b>性能、應(yīng)用和成本<b class='flag-5'>分析</b>

    鐵電存儲器SF24C64對標(biāo)MB85RC64性能、應(yīng)用深度分析

    鐵電存儲器SF24C64對標(biāo)MB85RC64性能、應(yīng)用深度分析
    的頭像 發(fā)表于 02-25 09:40 ?460次閱讀
    鐵電存儲器SF24C<b class='flag-5'>64</b>對標(biāo)MB85RC<b class='flag-5'>64</b>性能、應(yīng)用深度<b class='flag-5'>分析</b>

    美國裸機(jī)云服務(wù)器是什么詳細(xì)介紹

    美國裸機(jī)云服務(wù)器是一種高性能的計算資源,在云計算領(lǐng)域逐漸受到企業(yè)和開發(fā)者的青睞。主機(jī)推薦小編為您整理發(fā)布美國裸機(jī)云服務(wù)器的詳細(xì)介紹,希望對您了解美國裸機(jī)云服務(wù)器是什么有幫助。
    的頭像 發(fā)表于 02-07 15:56 ?321次閱讀

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

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

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

    發(fā)燒友論壇書籍評測活動中,看到有RISC-V相關(guān)的書籍在評測:《RISC-V體系結(jié)構(gòu)編程實(shí)踐(第二版)》,于是抱著僥幸的心理參加了,第一次參加這種書籍或開發(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 ?355次閱讀

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

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

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

    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ù)器的租用費(fèi)用進(jìn)行
    的頭像 發(fā)表于 08-29 10:05 ?490次閱讀

    東京裸機(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 ?603次閱讀