0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

opensbi下的riscv64裸机系列编程1(串口输出)

嵌入式IoT 来源:嵌入式IoT 作者:嵌入式IoT 2020-12-31 10:56 次阅读

opensbi下的riscv64裸机系列编程1(串口输出)

  • 1.说明

  • 2.opensbi的编译

  • 3.基本环境的准备

    • 3.1 准备qemu

    • 3.2 准备交叉编译工具链

  • 4.工程完善

  • 5.封装的sbi接口

  • 6.程序运行

  • 7.printf函数的实现

  • 8.小结

1.说明

前面的文章中已经提到了opensbi的作用不仅仅是一个引导作用,还提供了M模式转换到S模式的实现,同时在S-Mode下的内核可以通过这一层访问一些M-Mode的服务。

本文会从最小系统角度出发,利用opensbi的M-Mode的服务在控制台上输出Hello

2.opensbi的编译

opensbi提供了三种引导启动模式

  • FW_PAYLOAD
  • FW_JUMP
  • FW_DYNAMIC

那么这三种模式有什么区别呢?

FW_PAYLOAD

这种模式会直接将Opensbi固件与uboot等绑定在一起。

可以说这种模式是需要bootloader的。

FW_JUMP

这种模式会直接跳转到bootloader去执行。

这个对于qemu的启动模式来说十分的有用。

FW_DYNAMIC

这种模式跳转的时候会传递动态的参数

这里是通过寄存器a2传递了fw_dynamic_info结构体信息

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

为了简化模型,目前只通过FW_JUMP方式进行跳转。

下载opensbi的代码

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

进行编译

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

注意FW_JUMP_ADDR=0x80200000是指定的跳转地址。当然可以指定固件跳转到其他的地址。

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

3.基本环境的准备

3.1 准备qemu

可以到官网下载最新的qemu

https://www.qemu.org

解压后进行安装与编译。

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

3.2 准备交叉编译工具链

可以到官网上下载对应的交叉编译工具链

https://www.sifive.com/software

准备交叉编译工具链

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

4.工程完善

相关的实验代码已经放到仓库

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

工程的目录结构如下:

.
├──build.sh##编译脚本
├──entry.s##入口函数
├──fw_bin##可执行的固件脚本
│├──fw_jump.elf##opensbi
│├──hello.elf##编译完成的固件
│└──run.sh##直接运行的脚本
├──link.ld##链接文件
├──main.c##主函数
├──readme.md
└──sbi.h##sbi调用api

首先是编译脚本

build.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文件,并通过link.ld文件进行链接。

link.ld

链接脚本规定了程序的布局

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{ }包含的结构中。

其中*代表通配符,而.则表示当前的地址。当链接脚本需要使用的时候,可将其通过-T进行参数的传递。

entry.s

该文件描述了执行的入口函数。

.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

最关键的是两点:

  • 设置函数堆地址
  • 跳转到main函数
stack_top:
.space4096*4
.globalstack_top

将栈顶设置,通过call跳转到c语言的main函数。

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){}
}

这个程序会调用opensbi的函数,此时可以在S-Mode访问M-Mode的串口输出服务。

5.封装的sbi接口

可以通过下面的官方文档来了解其使用。

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

在进行M-Mode服务访问的时候,采用了ECALL进行系统调用。

在系统调用过程中,ecall会使用a0与a7寄存器。其中a7寄存器保留的是系统的调用号,而a0寄存器则保存系统的调用参数。返回值则会保存在a0寄存器中。

需要注意的是在RISCV的设计上,S模式不直接控制时钟中断和软件中断,而是使用ecall指令请求M模式设置定时器或在代理处理器中断。

所以opensbi在提供M-Mode服务的时候,到目前为止,opensbi提供的sbi服务接口有如下的表示:

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的实现:

#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;
})

根据上述的解释,ecall采用的是内嵌汇编函数。

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

这个内嵌汇编的展开形式如上面所示,a0a1a2表示传递的参数,a7表示系统调用号。

而根据内嵌汇编的语法,有着如下的格式

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

对于C语言来说,其函数的调用规则是处理器规定的,而编译器可以按照这种规则进行翻译代码。riscv的函数调用规则可以按照下面的文档进行操作。

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

而对于main函数中的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)

可以看到通过ecall只传递一个参数。

6.程序运行

fw_bin文件夹下输入./run.sh就可以运行看到效果了。

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

而这条操作的代码如下:

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

对应的machine是sifive_u。bios是fw_jump.elf

7.printf函数的实现

对于printf函数的使用很容易,但是深入了解其实现机制,发现并不简单,因为可变参数的特性使得其变得复杂起来。

实验代码如下:

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

看一个glibc中的prinf的实现机制。

#include
#include
#include

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

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

returndone;
}

对于上述的定义

intprintf(constchar*format,...)

format表示固定的参数,...表示可变的参数。

主要的实现过程利用三个函数进行

va_start(p,format)//将指针p移到第一个变量参数
var=va_arg(p,变量类型)//已知变量的情况下,移到下个参数变量
va_end(p)//结束参数使用等价于p=NULL

这里为了实现方便,我直接使用开源的tinyprintf

https://github.com/cjlano/tinyprintf

移植的过程也很容易,在main.c文件中作如下的实现:

#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进行串口输出了。

结果如下:

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

8.小结

第一阶段实现了opensbi的启动流程,同时通过系统调用访问串口输出。已经实现了S-Mode下访问M-Mode的初步计划,并且通过串口进行基本的输出过程。随着工程的不断增加,后续会增加makefile工程组织,riscv下的中断处理、以及定时器中断的实现,下篇文章主要介绍这些。

责任编辑:xj

原文标题:opensbi下的riscv64裸机系列编程1(串口输出)

文章出处:【微信公众号:嵌入式IoT】欢迎添加关注!文章转载请注明出处。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    88

    文章

    3576

    浏览量

    93545
  • 串口
    +关注

    关注

    14

    文章

    1540

    浏览量

    76101
  • RISC
    +关注

    关注

    6

    文章

    461

    浏览量

    83638

原文标题:opensbi下的riscv64裸机系列编程1(串口输出)

文章出处:【微信号:Embeded_IoT,微信公众号:嵌入式IoT】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

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

    如何一键生成支持riscv64的Debian rootfs?
    发表于 12-28 07:21

    请问哪里可以下载可以在d1上运行的debian/riscv64的img镜像

    请问哪里可以下载可以在d1上运行的debian/riscv64的img镜像?非常感谢!
    发表于 12-28 07:47

    d1哪吒开发板的启动流程分析

    志D1芯片的启动流程最底层的分析来看,和其他全志产品线的芯片的启动流程基本类似,主要需要理解的是fel模式对SRAM,DDR等操作,这样在做裸机开发的时候,才能将程序下载进去。有了这些理解,在做
    发表于 08-16 08:16

    全志D1开发板RISCV64开箱评测

    的生态建设远远没有arm强大,但是也在开源思想的引领,开始逐步走向大众的视野。 这块哪吒 RISCV64的板子,从主要的核的特性上来看,与目前市面上可见的riscv开发板相比,特性主要有以下几点:
    发表于 08-16 07:05

    TOP系列编程器软件

    TOP系列编程器软件适用于TOP全系列编程器。(TOP853 TOP2003 TOP2004 TOP2005 TOP2048)
    发表于 12-27 16:27 1633次下载

    UNICON触摸屏HU系列编程软件0723

    支持UNICON触摸屏HU系列编程软件 安装步骤:
    发表于 09-24 11:18 86次下载
    UNICON触摸屏HU<b class='flag-5'>系列编程</b>软件0723

    FX1SFX1NFX2NFX2NC系列编程手册

    三菱FX1SFX1NFX2NFX2NC系列编程手册
    发表于 12-13 22:43 4次下载

    riscv64裸机编程实践与分析

    riscv64 裸机编程实践与分析 1.概述 2.最小工程的构成 3. 链接脚本 4.可执行的程序源代码分析 5.编译与运行 5.1 编译 5.2 运行 5.3 调试 6.总结
    的头像 发表于 12-31 10:54 4427次阅读
    <b class='flag-5'>riscv64</b><b class='flag-5'>裸机</b><b class='flag-5'>编程</b>实践与分析

    opensbiriscv64裸机编程:中断与异常

    opensbiriscv64裸机编程2(中断与异常) 1.本文说明 2.
    的头像 发表于 01-07 10:30 2639次阅读

    riscv64上运行完整Linux的流程

    搭建qemu RISC-V运行Linux环境 1.本文概述 2.工具介绍 2.1 riscv-gnu-toolchain 2.2 spike 2.3 RISC-V Porxy Kernel 2.4
    的头像 发表于 05-23 15:01 7279次阅读
    <b class='flag-5'>riscv64</b>上运行完整Linux的流程

    全志D1哪吒 RISCV64开发板上手评测

    全志D1开发板(哪吒 RISCV64)开箱评测 1.概述 2.开箱体验 3.资料情况 3.1 上手情况 3.2 芯片文档 4.总体感受 1.概述作为主打RISC-V架构芯片的国产开发板
    的头像 发表于 05-27 17:56 9690次阅读
    全志D<b class='flag-5'>1</b>哪吒 <b class='flag-5'>RISCV64</b>开发板上手评测

    ARM Cortex-R系列编程手册资源下载

    ARM Cortex-R系列编程手册资源下载
    发表于 08-23 16:16 13次下载

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

    支持串口并口和USB(FT245B)PIC-Pgm 1.9.3.1全系列编程软件
    发表于 12-20 16:34 15次下载

    RT-Smart riscv64汇编注释

    以rt-smart在全志D1上的代码为例,主要注释了rt-smart在riscv64上的系统初始化和异常处理的代码仓库地址https://gitee.com/rtthread/rt-thread
    的头像 发表于 02-08 21:40 1092次阅读

    RT-Smart riscv64汇编注释

    以rt-smart在全志D1上的代码为例,主要注释了rt-smart在riscv64上的系统初始化和异常处理的代码
    的头像 发表于 10-12 17:26 569次阅读
    RT-Smart <b class='flag-5'>riscv64</b>汇编注释