Renode:第一个RISC-V裸机demo

在Renode上跑通了一个裸机(baremetal)demo,记录一下过程,为后面的进一步开发做铺垫。

该Demo来自国外一个大神的代码仓库:https://github.com/y2kbugger/baremetal-riscv-renode

有兴趣的同学可以自行下载。

1. 准备工作

1.1 Renode安装

由于我们使用Renode作为仿真平台,因此首先需要完成Renode的安装。以上代码仓库中包含了所有的工程组件,包括Renode源码、RISC-V工具链源码,以及编译所需组件的Makefile,但是实际使用中,可能会出错,所以我选择逐项安装。

Renode的安装在之前的文章里面已经介绍过,在本Demo中可以直接使用,安装方法见之前的博客:初识Renode_牧羊女-CSDN博客

1.2 RISC-V toolchain安装

要想在x86平台上编译RISC-V的binaries,首先需要安装交叉编译工具。针对该Demo的需要,我们只需要安装riscv32-unknown-elf-gcc套件即可,之前的文章我们也介绍了riscv32-unknown-elf-gcc的编译安装过程:编译riscv32-unknown-elf-gcc_牧羊女-CSDN博客

2. 硬件组成和软件代码

该Demo非常简单,堪称嵌入式界的“Hello World”,功能是让一个虚拟LED闪烁。该Demo使用的硬件及软件代码见下面描述。

2.1 硬件组成

Demo使用的硬件组成非常简单,包括一块很小的memory、一个RISC-V CPU、一个GPIO以及两个LED,如下面平台描述文件(vexriscv.repl)所示:

mem: Memory.MappedMemory @ sysbus 0x0
    size: 0x00040000

cpu: CPU.VexRiscv @ sysbus

gpio_out: GPIOPort.LiteX_GPIO @ sysbus 0x60000800
    type: Type.Out
    0 -> led0@0
    1 -> led1@0

led0 : Miscellaneous.LED @ gpio_out 0
led1 : Miscellaneous.LED @ gpio_out 1

关于平台描述文件的介绍,见前面的文章: Renode学习:平台描述文件REPL_牧羊女-CSDN博客

2.2 源代码

该Demo的软件代码(baremetal.s)为汇编语言,但也很简单,按照我有限的理解加了一点注释,如下:

#这段有点像C中的宏定义哈
.equ LED, 0x60000800
.equ DELAY_COUNT, 9000000
.equ LED_STATE_INITIAL, 0b00
.equ LED_STATE_TOGGLE_MASK, 0b01

.section .text     #定义文本段
.global _start     #定义_start为外部程序可访问的标签,类似于我们常用的main()函数
_start:
        li a5, LED                # 将上面LED表示的值加载到a5寄存器
        li a4, LED_STATE_INITIAL  # 同上,li为加载立即数指令
        li a6, LED_STATE_TOGGLE_MASK
        sw a4, 0x0(a5)      # store word,将寄存器一个word大小的值存入堆栈,0x0为offset
loop:
        li a0, DELAY_COUNT      # reset counter
delay_loop:
        addi a0, a0, -1         # count down
        bnez a0, delay_loop     # 条件转移指令,不为0时发生
toggle_led:
        lw a4, 0x0(a5)          # read in old led state
        xor a4, a4, a6          # toggle led state word
        sw a4, 0x0(a5)          # write new state
        jump loop, t0

3. 编译、运行

工程的运行需要三个步骤,分别是:

(1) 从源码baremetal.s中编译出目标二进制文件;

(2) 启动由以上REPL平台描述文件定义的硬件模拟环境;

(3) 将步骤(1)中编译出的elf文件加载到平台模拟器的内存中。

3.1 编译目标文件

目标文件的编译命令行如下:

riscv32-unknown-elf-gcc baremetal.s -ggdb -O0 -o image -ffreestanding -nostdlib

其中,

  • riscv32-unknown-elf-gcc为我们在1.2中编译安装的risc-v交叉编译器;
  • baremetal.s为软件源码,以上已有介绍;
  • -ggdb,使能GDB调试;
  • -o image,指定输出elf二进制文件的名称为“image”,可根据自己需要自由修改;
  • -ffreestanding,不使用main函数,不假定操作系统的存在;
  • -nostdlib,不依赖C标准库。

执行完以上编译命令后,会生成目标ELF文件image:

3.2 仿真脚本

Renode有一个非常方便的地方,就是可以通过一个RESC脚本将所有的步骤串联起来执行,而无需用户在命令行窗口中逐个输入命令,这就大大简化了我们的仿真操作步骤。

工程的仿真脚本(vexriscv.resc)如下:

:name: 1_blinky_VexRiscv
:description: Loader for the baremetal-riscv-renode project example 1 "blinky"

$name?="vexriscv-machine"

using sysbus
mach create $name
machine LoadPlatformDescription @vexriscv.repl

sysbus LoadELF @image

machine StartGdbServer 3333
logLevel -1 sysbus.gpio_out.led0
logLevel -1 sysbus.gpio_out.led1

start

关于脚本中命令的介绍,见之前的文章:Renode学习:Working with machines_牧羊女-CSDN博客 

3.3 运行

代码仓库中已经将运行命令也集成到了Makefile文件中,所以我们只需要执行make launch即可使demo运行起来。

Makefile文件如下:

image: baremetal.s
	riscv32-unknown-elf-gcc baremetal.s -ggdb -O0 -o image -ffreestanding -nostdlib

launch: image
	renode vexriscv.resc

debug:
	riscv32-unknown-elf-gdb -x gdbrc

clean:
	rm image

运行界面如下:

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
详细分析以下代码#include<iostream> #include<cstring> #include<cmath> using namespace std; int Node[50]; int number; int Calculate(char x)//计算偏移 { if(x>=97&&x<=122) return 0; switch(x) { case '!': case '^': case '(': case ')': return 1; case '|': case '-': return 2; case '<': return 3; default: return 1; } } int CalculateOut(string &Word,int n,int m)//输出||、->、<->,等等 { int i; if(m!=0) { for(i=1; i<m; i++)cout<<Word[n+i]; if(Word[i+n]!='\0') cout<<" "; } } int Input(string &Word)//输出数据 { int m,i,tag,q; number=0; for(i=0; Word[i]!='\0'; ) { q=i; if(Word[i]==' ') { i++; continue; } cout<<Word[i]; tag=Calculate(Word[i]); CalculateOut(Word,i,tag); i+=tag;//偏移 if(!tag)//数字计数 { Node[Word[q]-97]++; number++; if(Word[i+1]!='\0') cout<<" "; i++; } } cout<<endl; return number; } int Bin(int n,int m) { int bin[10000]; int a,b,i=0; do { a=n%2; n/=2; bin[i++]=a; } while(n>0); for(n=0; n<m-i; n++) { cout<<"0 "; } for(n=i-1; n>=0; n--) { cout<<bin[n]; if(n) cout<<" "; } } int Outnumber(int n) { int i,m=1; for(i=1; i<n; i++) { m=m*2+1; } for(i=m; i>=0; i--) { Bin(i,n); cout<<endl; } } void CoutNode() { int i,temp=0; for(i=0; i<50; i++) { if(temp==0&&Node[i]!=0) { cout.put(97+i); temp++; } else if(Node[i]!=0) { cout<<" "; cout.put(97+i); temp++; } } number=temp; cout<<endl; } int ReNode()//初始化 { for(int i=0; i<110; i++) Node[i]=0; } int Turn(char Map[400],string &Word) { int n,i; for(i=0,n=0;i<=strlen(Map);i++) { if(Map[i]!=' ') { Word[n]=Map[i]; n++; } } Word[n]='\0'; } int main() { int n,i; string Word; char Map[400]; while(gets(Map)!=NULL)//输入 { ReNode(); Turn(Map,Word); Input(Word); CoutNode(); Outnumber(number); } // Bin(2); }
06-09

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值