Spike, the RISC-V ISA Simulator, implements a functional model of one or more RISC-V processors.
Spike is named after the golden spike used to celebrate the completion of the US transcontinental railway.

一些同学初接触RISC-V,总逃脱不了被Rocketchip, Chisel, Spike ISS俘虏的命运。今天我们就来说说Spike。历经千辛万苦clone repo,编译安装,并跟着说明文档写了一个hello world程序、然后编译、最后敲下spike pk hello 之后终于在终端上见到了朝思暮想的hello world字符。平复一下自己激动的心情之后你是否在想:RISC-V的目标代码为什么能在我的x86机子的linux系统的终端上打印字符串?这中间到底发生了什么?

简单来说,这就是一个仿真的过程,Spike作为一个指令集的模拟器,配合其他工具如pk、fesrv完成系统的模拟。Spike本身使用C++实现,属于ESL中Untimed model。Spike是个RISC-V指令集的仿真器,所以Spike只能吃RISC-V的目标代码。那目标代码是准备运行在什么机器上呢?RISC-V的生态碎片化很厉害,不能像x86系统那样有明确的spec,甚至无法和ARM相比:ARM指令集以及系统留给你发挥的余地并不多。指令集支持哪些部分这个问题倒不大,告诉Spike就行了。memory map怎么定义的?有没有MMU?是否是跑在linux上的?这个关系就很大了。简单地说,spike可以运行下面三种类型的代码:

  • bare metal
  • 使用newlib
  • 使用glibc

1. software stack

让我们先看看RISC-V software stack的定义(先不考虑用于支持virtualization的hypervisor模式):

对于bare metal来说,顾名思义,程序代码在RISC-V硬件上裸奔,直接用spike去仿真就可以,不去加载pk。这时候并不存在AEE,或者说不存在现成的AEE,如果需要ABI的功能需要你自己实现一个AEE。这个时候硬件上的代码怎么和外界打交道?比如输出点字符什么的(如打印在终端上或者记录在文件里)?很遗憾,无法直接做到。但是我们可以把GDB接上去看看程序的运行状态。而且需要注意的是编译代码的时候需要指定各个segment的地址,并把这些信息传递给spike,否则spike是无法知道你code里访问的memory到底多大、在哪里。

看来定义一个系统是非常有必要的了。pk就是做了这样一件事情,pk(proxy kernel)是个轻量级的内核,已经定义好了RAM的基地址,也绑定了一些基本的library (newlib),是software stack里AEE的位置。所以我们也无需自己再去准备link的script了。这个时候就不能使用bare metal的工具了,该使用riscv64-unknown-elf里的一套工具,看得出来,pk轻量级系统还是64位的(当然也有32位的,就看你当时编译pk工具指定哪个32位还是64位了)。pk提供了一些IO能力了,我们就有办法和外界交互了。只是这些IO system call并不是pk自己处理,而是把它们导到host OS去处理。这么说吧,做了模拟器该做的事,所以pk也是模拟器的一部分。甚至pk中包含了一个boot loader,很容易理解,毕竟不能让机器上来就运行我们写的application级别的程序,作为一个系统来讲,很多初始化的工作要做。另外既然要和host打交道,那么需要一个RISC-V target和host直接的桥梁,这既是fesvr(frontend server)的功能了。

glibc很容易理解,提供了OS的接口,这时候ABI访问的就是OS了,作为写application,和在x86上的linux上写没什么区别。这时候工具就要使用riscv64-unknown-linux-gnu这一套。当然IO相关功能也是由linux来提供,至于具体是怎么定义的,则是OS来操心的:OS需要知道你的机器spec的每一个细节,编译OS时候必须给出来的。

2. spike, pk, fesrv三基友

我们重点谈谈使用pk的spike仿真原理。为了简单起见,我们假定host是x86 linux。下面谈谈spike,pk,fesrv这三基友是个什么关系。它们之间简单的连接关系如下图。

Application的目标代码是RISC-V毫无疑问。pk作为RISC-V的轻量级操作系统,也应该是RISC-V目标代码。Spike在Host上运行,毫无疑问是host的目标代码。而fesrv调用host的系统服务,所以只能是host的目标代码(这里是x86 linux)。那假设RISC-V程序里的一个系统调用,怎么转成x86 linux上的系统调用呢?

咱们来看一个例子,写一个Hello world:

#include <stdio.h>int main()
{printf("Hello World!n");return 0;
}

我们可以看到里面用到了printf,表明我们用到了系统调用。我们使用riscv64-unknown-elf-gcc把它编译成RISC-V的目标文件hello_world。

2.1. device tree与boot loader

Spike还有一个输入,就是device tree,就是让Spike知道当前的target硬件的一些特性。可以通过下面的命令看一下当前使用的device(就是SoC系统)的一些配置:

$spike --dump-dts hello_world

得到下面的输出:

/dts-v1/;/ {#address-cells = <2>;#size-cells = <2>;compatible = "ucbbar,spike-bare-dev";model = "ucbbar,spike-bare";cpus {#address-cells = <1>;#size-cells = <0>;timebase-frequency = <10000000>;CPU0: cpu@0 {device_type = "cpu";reg = <0>;status = "okay";compatible = "riscv";riscv,isa = "rv64imafdc";mmu-type = "riscv,sv48";clock-frequency = <1000000000>;CPU0_intc: interrupt-controller {#interrupt-cells = <1>;interrupt-controller;compatible = "riscv,cpu-intc";};};};memory@80000000 {device_type = "memory";reg = <0x0 0x80000000 0x0 0x80000000>;};soc {#address-cells = <2>;#size-cells = <2>;compatible = "ucbbar,spike-bare-soc", "simple-bus";ranges;clint@2000000 {compatible = "riscv,clint0";interrupts-extended = <&CPU0_intc 3 &CPU0_intc 7 >;reg = <0x0 0x2000000 0x0 0xc0000>;};};htif {compatible = "ucb,htif0";};
};

这其实就是pk这个虚拟的RISC-V SoC的配置了,因为我们的hello world是通过riscv64-unknown-elf-gcc编译出来的,表明我们使用了pk支持的device。

上电后(reset后)开始执行0x80000000地址上的代码,其实就是boot loader了,这里是pk的代码(可以通过objdump得到):

0000000080000000 <_ftext>:
80000000: 1e80006f j 800001e8 <do_reset>00000000800001e8 <do_reset>:
800001e8: 00000093 li ra, 0
800001ec: 00000113 li sp, 0
800001f0: 00000193 li gp, 0
800001f4: 00000213 li tp, 0
...
800002a8: 00070463 beqz a4,800002b0 <do_reset + 0xc8>
800002ac: 6f60206f j 800029a2 <init_first_hart>
800002b0: 00800613 li a2,8
800002b4: 30461073 csrw mie, a2

具体细节不必赘述,最终会跳到我们的Application:hello_world的入口。

2.2. 黑魔法HTIF

辗转腾挪,printf的函数调用最终会传递给pk来处理,黑魔法来了,在pk.c中有这样的代码:

static size_t parse_args(arg_buf* args)
{long r = frontend_syscall(SYS_getmainvars, va2pa(args), sizeof(*args), 0, 0, 0, 0, 0);//......

在frontend.c中实现frontend_syscall:

long frontend_syscall(long n, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)
{static volatile uint64_t magic_mem[8];static spinlock_t lock = SPINLOCK_INIT;spinlock_lock(&lock);magic_mem[0] = n;magic_mem[1] = a0;magic_mem[2] = a1;magic_mem[3] = a2;magic_mem[4] = a3;magic_mem[5] = a4;magic_mem[6] = a5;magic_mem[7] = a6;htif_syscall((uintptr_t)magic_mem);long ret = magic_mem[0];spinlock_unlock(&lock);return ret;
}

关键部分是htif_syscall,我们又来到machine/htif.c里:

void htif_syscall(uintptr_t arg)
{do_tohost_fromhost(0, 0, arg);
}static void do_tohost_fromhost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
{spinlock_lock(&htif_lock);__set_tohost(dev, cmd, data);while (1) {uint64_t fh = fromhost;if (fh) {if (FROMHOST_DEV(fh) == dev && FROMHOST_CMD(fh) == cmd) {fromhost = 0;break;}__check_fromhost();}}spinlock_unlock(&htif_lock);
}static void __set_tohost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
{while (tohost)__check_fromhost();tohost = TOHOST_CMD(dev, cmd, data);
}

在htif.h里define了TOHOST_CMD:

# define TOHOST_CMD(dev, cmd, payload) ({ if ((dev) || (cmd)) __builtin_trap(); (payload); })
#endif

看来pk和fesrv之间是靠HTIF来传递信息。在fesrv的http://htif.cc里:

  //......std::map<std::string, uint64_t> symbols = load_elf(path.c_str(), &preload_aware_memif, &entry);if (symbols.count("tohost") && symbols.count("fromhost")) {tohost_addr = symbols["tohost"];fromhost_addr = symbols["fromhost"];} else {fprintf(stderr, "warning: tohost and fromhost symbols not in ELF; can't communicate with targetn");}
//......

看到这里,答案呼之欲出了:fesrv知道数据交换的内存地址,看来这个黑魔法就是fesrv基于risc-v仿真也是运行在host内存里的便利,直接通过内存数据读写来交换数据。至于如何去查询target是否需要系统服务,服务哪些target,这些在http://htif.cc里有具体实现,不再赘述。

基于matlab的pcm系统仿真_深入理解基于RISC-V ISS Spike的仿真系统:探索Spike,pk和fesrv...相关推荐

  1. 基于matlab的pcm设计实验报告,基于MATLAB的PCM系统仿真课程设计

    基于MATLAB的PCM系统仿真课程设计 通信专业课程设计一 课课 程程 设设 计(论计(论 文)文) 设计设计 论文论文 题目题目基于基于 MATLABMATLAB 的的 PCMPCM 系统仿真系统 ...

  2. 基于matlab的ofdm系统仿真及性能分析,基于MATLAB的OFDM系统性能仿真

    基于MATLAB的OFDM系统性能仿真(论文15000字) 摘要:本文介绍OFDM系统的基本原理与实现方法,以及移动多径环境下使用OFDM的优势,详细阐述了OFDM系统中符号间干扰ISI.载波间干扰I ...

  3. 基于matlab的通信系统仿真的本科论文,基于MATLAB的TDM通信系统仿真设计.docx

    摘要:当前,通信技术越来越多的影响着我们的生活.数字通信系统凭借其抗干扰能力强.传输差错可控的特性已然成为当代通信技术的主流.本课题根据时分多路复用的基本原理,利用MATLAB软件的图形用户界面来模拟 ...

  4. OFDM同步技术基于MATLAB仿真,基于Matlab的OFDM系统仿真

    中文摘要 交频分复用(OFDM, Orthogonal Frequency Division Multiplexing) 是一种特殊的多载波方案,它可以被看作一种调制技术,也可以被当作是一种复用技术。 ...

  5. 基于matlab的频率特性测试仪,基于MATLAB的频率特性测试仪_终稿

    基于MATLAB的频率特性测试仪_终稿 (15页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14.9 积分 基于MATLAB的低频频率特性测试虚拟仪器设 ...

  6. 基于 MATLAB 的 PCM 编码解码实现

    基于 MATLAB 的 PCM 编码解码实现_vlaser的小屋-CSDN博客_pcm编码matlab

  7. 混频通信的matlab仿真,基于MATLAB的扩频通信系统仿真研究—上海交通大学

    基于MATLAB 的扩频通信系统仿真研究 范伟 翟传润 战兴群 (上海交通大学电子信息与电气工程学院,200030,上海) 摘要 本文阐述了扩展频谱通信技术的理论基础和实现方法,利用MATLAB 提供 ...

  8. 基于matlab的mimo仿真,基于MATLAB的MIMO系统仿真与分析|Matlab代做

    核心提示:基于MATLAB的MIMO系统仿真与分析|Matlab代做... 近年来,人们对无线通信业务需求的爆炸式增长激励着研究工作者们在相关领域的各个层面不断寻求技术突破,期望以更完美的解决方案来满 ...

  9. 基于matlab的2psk功率,基于matlab的相移键控系统仿真.doc

    <基于matlab的相移键控系统仿真.doc>由会员分享,提供在线免费全文阅读可下载,此文档格式为doc,更多相关<基于matlab的相移键控系统仿真.doc>文档请在天天文库 ...

最新文章

  1. 任正非最新讲话:华为专家队伍怎么建?
  2. 从零开始的Python学习Episode 4——列表
  3. React开发(235):document.body.clientHeight
  4. 一文详解微服务架构的数据设计
  5. [Turn]C# 强制关闭当前程序进程(完全Kill掉不留痕迹)
  6. 图论 —— 弦图 —— LexBFS 算法
  7. qt 如何 指针 自动 释放内存_要是面试官再问你智能指针的问题,就拿这篇文章“盘他”!!!...
  8. 黑链 明链 暗链 简介
  9. Android - Md5加密 工具类
  10. MySql将一张表的数据copy到另一张表中
  11. 给俺老婆的一封信(太有才了!)
  12. 新高考改革之下,有孚网络助力教育信息化进程
  13. 蒙特梭利素材语言幼儿识字 补笔画 闪卡三段卡蒙氏教具
  14. 爬取新浪微博热搜排行
  15. HTML5期末大作业:仿唯品会购物网站设计——仿唯品会购物商城(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 商城网站设计
  16. 学习编程可以从事哪些行业
  17. 无线中DNS改为114.114.114.114真的能提高网速吗?如何提高网速?
  18. 第87篇ES之Elastica-php匹配多值字段及给Problem的elementName设中文分词ik
  19. ZCMU - 1601: 卡斯丁狗去挖矿
  20. 使用tkinter开发GUI程序4 -- tkinter常见控件的特征属性(第二部分)

热门文章

  1. 设置MySQL排序方式_设置MySQL设置字符集和排序方式
  2. 微信公众号怎么快速导出一个月的文章数据
  3. 使用struts2的 下载
  4. 浅谈Normalize.css
  5. websocket 学习--简单使用,nodejs搭建websocket服务器,到模拟股票,到实现聊天室
  6. python中属性是什么意思啊_python中的“对象属性”和一般属性是什么?
  7. jmeter连接mysql数据库驱动_十八、JMeter实战-JDBC连接MySQL数据库
  8. Spring注解——使用@ComponentScan自动扫描组件
  9. python使用loaddata_Python中LOADDATAINFILE语句导入数据(txt)进入MySQL的一些注意事项...
  10. pythonweb面试常见问题_python和web框架面试题目整理(3)