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

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

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

opensbi下的riscv64裸機(jī)系列編程1(串口輸出)

嵌入式IoT ? 來(lái)源:嵌入式IoT ? 作者:嵌入式IoT ? 2020-12-31 10:56 ? 次閱讀

opensbi下的riscv64裸機(jī)系列編程1(串口輸出)

  • 1.說(shuō)明

  • 2.opensbi的編譯

  • 3.基本環(huán)境的準(zhǔn)備

    • 3.1 準(zhǔn)備qemu

    • 3.2 準(zhǔn)備交叉編譯工具鏈

  • 4.工程完善

  • 5.封裝的sbi接口

  • 6.程序運(yùn)行

  • 7.printf函數(shù)的實(shí)現(xiàn)

  • 8.小結(jié)

1.說(shuō)明

前面的文章中已經(jīng)提到了opensbi的作用不僅僅是一個(gè)引導(dǎo)作用,還提供了M模式轉(zhuǎn)換到S模式的實(shí)現(xiàn),同時(shí)在S-Mode下的內(nèi)核可以通過(guò)這一層訪問一些M-Mode的服務(wù)。

本文會(huì)從最小系統(tǒng)角度出發(fā),利用opensbi的M-Mode的服務(wù)在控制臺(tái)上輸出Hello。

2.opensbi的編譯

opensbi提供了三種引導(dǎo)啟動(dòng)模式

  • FW_PAYLOAD
  • FW_JUMP
  • FW_DYNAMIC

那么這三種模式有什么區(qū)別呢?

FW_PAYLOAD

這種模式會(huì)直接將Opensbi固件與uboot等綁定在一起。

可以說(shuō)這種模式是需要bootloader的。

FW_JUMP

這種模式會(huì)直接跳轉(zhuǎn)到bootloader去執(zhí)行。

這個(gè)對(duì)于qemu的啟動(dòng)模式來(lái)說(shuō)十分的有用。

FW_DYNAMIC

這種模式跳轉(zhuǎn)的時(shí)候會(huì)傳遞動(dòng)態(tài)的參數(shù)

這里是通過(guò)寄存器a2傳遞了fw_dynamic_info結(jié)構(gòu)體信息。

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

為了簡(jiǎn)化模型,目前只通過(guò)FW_JUMP方式進(jìn)行跳轉(zhuǎn)。

下載opensbi的代碼

gitclonehttps://github.com/riscv/opensbi.git

進(jìn)行編譯

exportCROSS_COMPILE=riscv64-unknown-elf-
makePLATFORM=genericclean
makePLATFORM=genericFW_JUMP_ADDR=0x80200000

注意FW_JUMP_ADDR=0x80200000是指定的跳轉(zhuǎn)地址。當(dāng)然可以指定固件跳轉(zhuǎn)到其他的地址。

生成fw_jump.elf位于platform/generic/firmware/fw_jump.elf。

3.基本環(huán)境的準(zhǔn)備

3.1 準(zhǔn)備qemu

可以到官網(wǎng)下載最新的qemu

https://www.qemu.org

解壓后進(jìn)行安裝與編譯。

tarxvfqemu-5.2.0.tar.xz
./configure--target-list=riscv64-softmmu
make
sudomakeinstall

3.2 準(zhǔn)備交叉編譯工具鏈

可以到官網(wǎng)上下載對(duì)應(yīng)的交叉編譯工具鏈

https://www.sifive.com/software

準(zhǔn)備交叉編譯工具鏈

exportPATH=$PATH:/opt/riscv64-unknown-elf-gcc-8.3.0-2020.04.0-x86_64-linux-ubuntu14/bin/

4.工程完善

相關(guān)的實(shí)驗(yàn)代碼已經(jīng)放到倉(cāng)庫(kù)

https://github.com/bigmagic123/riscv64_opensbi_baremetal/tree/master/01_startup

工程的目錄結(jié)構(gòu)如下:

.
├──build.sh##編譯腳本
├──entry.s##入口函數(shù)
├──fw_bin##可執(zhí)行的固件腳本
│├──fw_jump.elf##opensbi
│├──hello.elf##編譯完成的固件
│└──run.sh##直接運(yùn)行的腳本
├──link.ld##鏈接文件
├──main.c##主函數(shù)
├──readme.md
└──sbi.h##sbi調(diào)用api

首先是編譯腳本

build.sh

目前為了簡(jiǎn)化工程,暫時(shí)沒有使用makefile文件。

riscv64-unknown-elf-gcc-nostdlib-centry.s-oentry.o
riscv64-unknown-elf-gcc-nostdlib-cmain.c-omain.o
riscv64-unknown-elf-ld-ofw_bin/hello.elf-Tlink.ldentry.omain.o

編譯了entry.smain.c文件,并通過(guò)link.ld文件進(jìn)行鏈接。

link.ld

鏈接腳本規(guī)定了程序的布局

OUTPUT_ARCH("riscv")
OUTPUT_FORMAT("elf64-littleriscv")
ENTRY(_start)
SECTIONS
{
/*text:testcodesection*/
.=0x80200000;
start=.;

.text:{
stext=.;
*(.text.entry)
*(.text.text.*)
.=ALIGN(4K);
etext=.;
}

.data:{
sdata=.;
*(.data.data.*)
edata=.;
}

.bss:{
sbss=.;
*(.bss.bss.*)
ebss=.;
}
PROVIDE(end=.);
}

整體的鏈接腳本寫在SECTION{ }包含的結(jié)構(gòu)中。

其中*代表通配符,而.則表示當(dāng)前的地址。當(dāng)鏈接腳本需要使用的時(shí)候,可將其通過(guò)-T進(jìn)行參數(shù)的傳遞。

entry.s

該文件描述了執(zhí)行的入口函數(shù)。

.section.text.entry
.globl_start
_start:
/*setupstack*/
lasp,stack_top#setupstackpointer
callmain
halt:jhalt#entertheinfiniteloop

loop:
jloop

.section.bss.stack
.align12
.globalstack_top
stack_top:
.space4096*4
.globalstack_top

最關(guān)鍵的是兩點(diǎn):

  • 設(shè)置函數(shù)堆地址
  • 跳轉(zhuǎn)到main函數(shù)
stack_top:
.space4096*4
.globalstack_top

將棧頂設(shè)置,通過(guò)call跳轉(zhuǎn)到c語(yǔ)言的main函數(shù)。

main.c

#include"sbi.h"
voidmain()
{
SBI_PUTCHAR('H');
SBI_PUTCHAR('e');
SBI_PUTCHAR('l');
SBI_PUTCHAR('l');
SBI_PUTCHAR('o');
SBI_PUTCHAR('
');
while(1){}
}

這個(gè)程序會(huì)調(diào)用opensbi的函數(shù),此時(shí)可以在S-Mode訪問M-Mode的串口輸出服務(wù)。

5.封裝的sbi接口

可以通過(guò)下面的官方文檔來(lái)了解其使用。

https://github.com/riscv/riscv-sbi-doc/blob/master/riscv-sbi.adoc

在進(jìn)行M-Mode服務(wù)訪問的時(shí)候,采用了ECALL進(jìn)行系統(tǒng)調(diào)用。

在系統(tǒng)調(diào)用過(guò)程中,ecall會(huì)使用a0與a7寄存器。其中a7寄存器保留的是系統(tǒng)的調(diào)用號(hào),而a0寄存器則保存系統(tǒng)的調(diào)用參數(shù)。返回值則會(huì)保存在a0寄存器中。

需要注意的是在RISCV的設(shè)計(jì)上,S模式不直接控制時(shí)鐘中斷和軟件中斷,而是使用ecall指令請(qǐng)求M模式設(shè)置定時(shí)器或在代理處理器中斷。

所以opensbi在提供M-Mode服務(wù)的時(shí)候,到目前為止,opensbi提供的sbi服務(wù)接口有如下的表示:

Function Name FID EID Replacement EID
sbi_set_timer 0 0x00 0x54494D45
sbi_console_putchar 0 0x01 N/A
sbi_console_getchar 0 0x02 N/A
sbi_clear_ipi 0 0x03 N/A
sbi_send_ipi 0 0x04 0x735049
sbi_remote_fence_i 0 0x05 0x52464E43
sbi_remote_sfence_vma 0 0x06 0x52464E43
sbi_remote_sfence_vma_asid 0 0x07 0x52464E43
sbi_shutdown 0 0x08 0x53525354
RESERVED 0x09-0x0F

這里只使用了sbi_console_putchar接口。

接著看看具體的ecall的實(shí)現(xiàn):

#defineSBI_ECALL(__num,__a0,__a1,__a2)
({
registerunsignedlonga0asm("a0")=(unsignedlong)(__a0);
registerunsignedlonga1asm("a1")=(unsignedlong)(__a1);
registerunsignedlonga2asm("a2")=(unsignedlong)(__a2);
registerunsignedlonga7asm("a7")=(unsignedlong)(__num);
asmvolatile("ecall"
:"+r"(a0)
:"r"(a1),"r"(a2),"r"(a7)
:"memory");
a0;
})

根據(jù)上述的解釋,ecall采用的是內(nèi)嵌匯編函數(shù)。

ecall
iia0,101
lia1,0
lia2,0
lia7,1

這個(gè)內(nèi)嵌匯編的展開形式如上面所示,a0、a1、a2表示傳遞的參數(shù),a7表示系統(tǒng)調(diào)用號(hào)。

而根據(jù)內(nèi)嵌匯編的語(yǔ)法,有著如下的格式

asm(assemblertemplate
:/*outputoperands*/
:/*inputoperands*/
:/*clobberedregisterslist*/
);

對(duì)于C語(yǔ)言來(lái)說(shuō),其函數(shù)的調(diào)用規(guī)則是處理器規(guī)定的,而編譯器可以按照這種規(guī)則進(jìn)行翻譯代碼。riscv的函數(shù)調(diào)用規(guī)則可以按照下面的文檔進(jìn)行操作。

https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf

而對(duì)于main函數(shù)中的SBI_PUTCHAR其展開為

#defineSBI_CONSOLE_PUTCHAR1
#defineSBI_PUTCHAR(__a0)SBI_ECALL_1(SBI_CONSOLE_PUTCHAR,__a0)
#defineSBI_ECALL_1(__num,__a0)SBI_ECALL(__num,__a0,0,0)

可以看到通過(guò)ecall只傳遞一個(gè)參數(shù)。

6.程序運(yùn)行

fw_bin文件夾下輸入./run.sh就可以運(yùn)行看到效果了。

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

而這條操作的代碼如下:

qemu-system-riscv64-Msifive_u-biosfw_jump.elf-kernelhello.elf-nographic

對(duì)應(yīng)的machine是sifive_u。bios是fw_jump.elf。

7.printf函數(shù)的實(shí)現(xiàn)

對(duì)于printf函數(shù)的使用很容易,但是深入了解其實(shí)現(xiàn)機(jī)制,發(fā)現(xiàn)并不簡(jiǎn)單,因?yàn)榭勺儏?shù)的特性使得其變得復(fù)雜起來(lái)。

實(shí)驗(yàn)代碼如下:

https://github.com/bigmagic123/riscv64_opensbi_baremetal/tree/master/02_printf

看一個(gè)glibc中的prinf的實(shí)現(xiàn)機(jī)制。

#include
#include
#include

/*WriteformattedoutputtostdoutfromtheformatstringFORMAT.*/
/*VARARGS1*/
intprintf(constchar*format,...)
{
va_listarg;
intdone;

va_start(arg,format);
done=vprintf(format,arg);
va_end(arg);

returndone;
}

對(duì)于上述的定義

intprintf(constchar*format,...)

format表示固定的參數(shù),...表示可變的參數(shù)。

主要的實(shí)現(xiàn)過(guò)程利用三個(gè)函數(shù)進(jìn)行

va_start(p,format)//將指針p移到第一個(gè)變量參數(shù)
var=va_arg(p,變量類型)//已知變量的情況下,移到下個(gè)參數(shù)變量
va_end(p)//結(jié)束參數(shù)使用等價(jià)于p=NULL

這里為了實(shí)現(xiàn)方便,我直接使用開源的tinyprintf。

https://github.com/cjlano/tinyprintf

移植的過(guò)程也很容易,在main.c文件中作如下的實(shí)現(xiàn):

#include"sbi.h"
#include"tinyprintf.h"
#defineUNUSED(x)(void)(x)
staticvoidstdout_putc(void*unused,char*ch)
{
SBI_PUTCHAR(ch);
}
voidmain()
{
init_printf(0,stdout_putc);

tfp_printf("helloworld
");
while(1){}
}

只需要移植init_printf接口就可以使用tfp_printf進(jìn)行串口輸出了。

結(jié)果如下:

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

8.小結(jié)

第一階段實(shí)現(xiàn)了opensbi的啟動(dòng)流程,同時(shí)通過(guò)系統(tǒng)調(diào)用訪問串口輸出。已經(jīng)實(shí)現(xiàn)了S-Mode下訪問M-Mode的初步計(jì)劃,并且通過(guò)串口進(jìn)行基本的輸出過(guò)程。隨著工程的不斷增加,后續(xù)會(huì)增加makefile工程組織,riscv下的中斷處理、以及定時(shí)器中斷的實(shí)現(xiàn),下篇文章主要介紹這些。

責(zé)任編輯:xj

原文標(biāo)題:opensbi下的riscv64裸機(jī)系列編程1(串口輸出)

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


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

    關(guān)注

    88

    文章

    3565

    瀏覽量

    93536
  • 串口
    +關(guān)注

    關(guān)注

    14

    文章

    1540

    瀏覽量

    76062
  • RISC
    +關(guān)注

    關(guān)注

    6

    文章

    461

    瀏覽量

    83637

原文標(biāo)題:opensbi下的riscv64裸機(jī)系列編程1(串口輸出)

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

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    如何一鍵生成支持riscv64的Debian rootfs?

    如何一鍵生成支持riscv64的Debian rootfs?
    發(fā)表于 12-28 07:21

    請(qǐng)問哪里可以下載可以在d1上運(yùn)行的debian/riscv64的img鏡像

    請(qǐng)問哪里可以下載可以在d1上運(yùn)行的debian/riscv64的img鏡像?非常感謝!
    發(fā)表于 12-28 07:47

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

    志D1芯片的啟動(dòng)流程最底層的分析來(lái)看,和其他全志產(chǎn)品線的芯片的啟動(dòng)流程基本類似,主要需要理解的是fel模式對(duì)SRAM,DDR等操作,這樣在做裸機(jī)開發(fā)的時(shí)候,才能將程序下載進(jìn)去。有了這些理解,在做
    發(fā)表于 08-16 08:16

    全志D1開發(fā)板RISCV64開箱評(píng)測(cè)

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

    TOP系列編程器軟件

    TOP系列編程器軟件適用于TOP全系列編程器。(TOP853?TOP2003?TOP2004?TOP2005?TOP2048)
    發(fā)表于 12-27 16:27 ?1633次下載

    UNICON觸摸屏HU系列編程軟件0723

    支持UNICON觸摸屏HU系列編程軟件 安裝步驟:
    發(fā)表于 09-24 11:18 ?86次下載
    UNICON觸摸屏HU<b class='flag-5'>系列編程</b>軟件0723

    FX1SFX1NFX2NFX2NC系列編程手冊(cè)

    三菱FX1SFX1NFX2NFX2NC系列編程手冊(cè)
    發(fā)表于 12-13 22:43 ?4次下載

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

    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é)
    的頭像 發(fā)表于 12-31 10:54 ?4424次閱讀
    <b class='flag-5'>riscv64</b><b class='flag-5'>裸機(jī)</b><b class='flag-5'>編程</b>實(shí)踐與分析

    opensbiriscv64裸機(jī)編程:中斷與異常

    opensbiriscv64裸機(jī)編程2(中斷與異常) 1.本文說(shuō)明 2.
    的頭像 發(fā)表于 01-07 10:30 ?2638次閱讀

    riscv64上運(yùn)行完整Linux的流程

    搭建qemu RISC-V運(yùn)行Linux環(huán)境 1.本文概述 2.工具介紹 2.1 riscv-gnu-toolchain 2.2 spike 2.3 RISC-V Porxy Kernel 2.4
    的頭像 發(fā)表于 05-23 15:01 ?7258次閱讀
    <b class='flag-5'>riscv64</b>上運(yùn)行完整Linux的流程

    全志D1哪吒 RISCV64開發(fā)板上手評(píng)測(cè)

    全志D1開發(fā)板(哪吒 RISCV64)開箱評(píng)測(cè) 1.概述 2.開箱體驗(yàn) 3.資料情況 3.1 上手情況 3.2 芯片文檔 4.總體感受 1.概述作為主打RISC-V架構(gòu)芯片的國(guó)產(chǎn)開發(fā)板
    的頭像 發(fā)表于 05-27 17:56 ?9678次閱讀
    全志D<b class='flag-5'>1</b>哪吒 <b class='flag-5'>RISCV64</b>開發(fā)板上手評(píng)測(cè)

    ARM Cortex-R系列編程手冊(cè)資源下載

    ARM Cortex-R系列編程手冊(cè)資源下載
    發(fā)表于 08-23 16:16 ?13次下載

    支持串口并口和USB(FT245B)PIC-Pgm 1.9.3.1全系列編程軟件

    支持串口并口和USB(FT245B)PIC-Pgm 1.9.3.1全系列編程軟件
    發(fā)表于 12-20 16:34 ?9次下載

    RT-Smart riscv64匯編注釋

    以rt-smart在全志D1上的代碼為例,主要注釋了rt-smart在riscv64上的系統(tǒng)初始化和異常處理的代碼倉(cāng)庫(kù)地址https://gitee.com/rtthread/rt-thread
    的頭像 發(fā)表于 02-08 21:40 ?1088次閱讀

    RT-Smart riscv64匯編注釋

    以rt-smart在全志D1上的代碼為例,主要注釋了rt-smart在riscv64上的系統(tǒng)初始化和異常處理的代碼
    的頭像 發(fā)表于 10-12 17:26 ?567次閱讀
    RT-Smart <b class='flag-5'>riscv64</b>匯編注釋