国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

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

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

riscv64裸機編程實踐與分析

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

掃碼添加小助手

加入工程師交流群

riscv64 裸機編程實踐與分析

  • 1.概述

  • 2.最小工程的構成

  • 3. 鏈接腳本

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

  • 5.編譯與運行

    • 5.1 編譯

    • 5.2 運行

    • 5.3 調試

  • 6.總結

1.概述

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

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

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

https://www.sifive.com/software

2.最小工程的構成

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

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

鏈接腳本文件則可以告訴程序的布局,比如代碼段,函數的入口等等。有了這兩個文件將編譯出來的程序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ī)定如何把輸入的文件按照特定的地址放到內存中。

其中就上面的腳本而言:

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

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

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

然后開始進行代碼段的布局,起始地址開始處為0x80000000。然后依次放代碼段、數據段、只讀數據段、全局數據段,debug段等等。

這里需要注意:

.+=0x8000;
stack_top=.;

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

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

#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í)行的程序源代碼分析

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

看看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.
"

根據匯編語言的規(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

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

一個RiscV兼容的核心能夠通過多線程技術(或者說超線程技術)支持多個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 架構來說,a0寄存器表示第一個參數賦值,接著跳轉到puts函數中。

此時傳遞過去的參數為a0,也就是

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

指向一個只讀的字符串結構的數據。

puts的實現

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

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寄存器將參數傳遞過來,然后從1:開始,讀取字符串,beqz t1, 3f表示當t1 == 0時,跳轉到3:之前。此時會跳出2:循環(huán)。

2:則是向串口FIFO送數的過程。

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

5.編譯與運行

5.1 編譯

上述程序分析完成會,可以將其進行編譯。

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攜帶的參數。

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

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

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

數據模型:

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

浮點傳遞規(guī)則

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

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

-mcmodel=medlow

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

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

-mcmodel=medany

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

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

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

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

-nostartfiles:不帶main函數的入口程序。

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

5.2 運行

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

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

5.3 調試

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

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

另外再開一個終端輸入

riscv64-unknown-elf-gdbhello

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

通過b _start打斷點,并且通過si進行單步跳轉可實現程序的單步運行。

6.總結

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

責任編輯:xj

原文標題:riscv64 裸機編程實踐與分析

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


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

    關注

    90

    文章

    3716

    瀏覽量

    97178
  • RISC
    +關注

    關注

    6

    文章

    485

    瀏覽量

    86598

原文標題:riscv64 裸機編程實踐與分析

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

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

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    如何在 VF2 上玩 Minecraft 的分步指南

    /libraries/org/lwjgl/lwjgl/3.3.1/cp ~/lwjgl3-riscv-existing/bin/libs/native/linux/riscv64/org/lwjgl
    發(fā)表于 02-27 07:45

    debian-202308 映像不支持 firmware-realtek 包 riscv64嗎?

    我發(fā)現在 riscv64 架構的 debian-202308 映像中缺少為帶有 Realtek 芯片的 USB 集線器以太網端口提供驅動程序的 firmware-realtek 包。我發(fā)現
    發(fā)表于 02-26 10:49

    【CIE全國RISC-V創(chuàng)新應用大賽】+MUSE Pi Pro RiscV UEFI固件開發(fā)

    /edk2-non-osi 1.4 RiscV64工具鏈的前綴定義 為RiscV64工具鏈指定前綴變量,參考如下命令: #define RISCV64 prefix export
    發(fā)表于 11-13 00:20

    riscv底層原理分析gd32vf103的中斷行為

    riscv底層原理分析gd32vf103的中斷行為 1.概述 2.中斷向量表初始化 3.詳細分析一下irq_entry 4.關于gd32vf103中斷編程模型的理解 1.概
    發(fā)表于 10-31 08:04

    全志D1開發(fā)板(哪吒 RISCV64)開箱評測

    riscv的生態(tài)建設遠遠沒有arm強大,但是也在開源思想的引領下,開始逐步走向大眾的視野。 這塊哪吒 RISCV64的板子,從主要的核的特性上來看,與目前市面上可見的riscv開發(fā)板相比,特性主要有以下幾點
    發(fā)表于 10-31 07:50

    riscv實現自定義指令并用qemu運行

    利用qemu模擬硬件平臺,實現特定指令解析,同時寫裸機代碼來測試該指令的運行情況。當然,如果實現的很好,是需要修改riscv的gcc的,讓自己的擴展指令加入。這里不做修改,后面會詳細描述細節(jié)。 自定義
    發(fā)表于 10-31 07:37

    d1哪吒開發(fā)板的啟動流程分析

    的可以研究很深的開發(fā)板。本文主要從研究D1啟動流程的角度出發(fā),探索一下D1的裸機開發(fā)實踐。對于研究D1的底層裸機開發(fā),首先需要知道可以玩那些東西,也可以對RISCV相關的軟件生態(tài)有比較
    發(fā)表于 10-29 06:44

    用哪吒D1開發(fā)板體驗riscv向量底層編程

    1.前言 2.機器模式處理器狀態(tài)寄存器(MSTATUS) 3.編譯選項支持V擴展 4.RISCV向量計算的原理 5.通過實例分析RISCV V擴展的運作機制 6.RVV使用體驗 1.前言
    發(fā)表于 10-29 06:21

    目前最新版的userapps如何安裝D1s環(huán)境?

    /get_toolchain.py和 smart-env.sh,按照最新代碼的Readme.md去安裝,在xmake f -a riscv64時hui’chu’xian’bao’cu會出現報錯,報錯信息如下
    發(fā)表于 09-25 07:33

    riscv virt64編譯后 ls無法運行怎么解決?

    用倉庫里的默認配置編譯qemu-virt64-riscv 生成后運行,顯示 [E/DBG] virtio-blk0 mount failed ls看不到文件夾 msh />ls No such directory
    發(fā)表于 09-22 06:38

    【VisionFive 2單板計算機試用體驗】RISC-V架構卡片計算機初體驗

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

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

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

    【M-K1HSE開發(fā)板免費體驗】超高性能與顏值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) 架構?

    在spacemit k1型板(bpi-f3)上編譯OpenVINO?,但失敗。 使用 riscv64 構建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