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

49cc6af7f59db9d8eb1028e78f835470.png
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模式):

83059ca53b1eb01a9c1885bb90f757e1.png

对于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这三基友是个什么关系。它们之间简单的连接关系如下图。

a21379df2efd4bd437ec727f9d3a0697.png

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里有具体实现,不再赘述。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/564483.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

think php 子查询,使用thinkPHP怎么实现一个子查询语句

使用thinkPHP怎么实现一个子查询语句发布时间&#xff1a;2021-01-30 13:31:08来源&#xff1a;亿速云阅读&#xff1a;85作者&#xff1a;Leah这篇文章给大家介绍使用thinkPHP怎么实现一个子查询语句&#xff0c;内容非常详细&#xff0c;感兴趣的小伙伴们可以参考借鉴&#x…

python的三个特性_Python3.9的7个特性

作者|PADHMA编译|VK来源|Analytics Vidhya介绍正如著名作家韦恩•W•戴尔所说&#xff0c;改变你看待事物的方式 你所看待的事物也会改变当Python的新版本问世时&#xff0c;许多人担心向后兼容性问题和其他问题。但是如果你喜欢Python&#xff0c;你一定会对新更新中发布的特性…

C++类的成员变量和成员函数

类可以看做是一种数据类型&#xff0c;它类似于普通的数据类型&#xff0c;但是又有别于普通的数据类型。类这种数据类型是一个包含成员变量和成员函数的集合。 类的成员变量和普通变量一样&#xff0c;也有数据类型和名称&#xff0c;占用固定长度的内存。但是&#xff0c;在…

单机安装oracle,单机安装oracle系统

一、安装oracle6.9操作系统1、硬盘划分为30G(以下分区皆强制为主分区)/boot 200Mswap 4G/ 剩余全部空间安装下一步的步骤就不说了勾选图形选项基本系统--》备份客户端&#xff0c;大系统性能&#xff0c;存储工具&#xff0c;安全工具&#xff0c;性能工具&#xff0c;目录客户…

redis timeout设置多少合适_热水器怎么调温度?一般热水器温度设置多少度比较合适?...

对于热水器的温度设置您知道怎么操作吗&#xff1f;一般情况下电热水器设置多少度比较合适呢&#xff1f;今天蜜罐蚁装修网小编给大家介绍下热水器如何调节温度&#xff0c;以及热水器调节温度在什么范围比较合适。下面请看小编以美的电热水器某个型号产品举例图文讲解热水器调…

C++类成员的访问权限以及类的封装

C通过 public、protected、private 三个关键字来控制成员变量和成员函数的访问权限&#xff0c;它们分别表示公有的、受保护的、私有的&#xff0c;被称为成员访问限定符。所谓访问权限&#xff0c;就是你能不能使用该类中的成员。 C 中的 public、private、protected 只能修饰…

oracle数据库12下载地址,Oracle 数据库和补丁下载地址 12.1.0.2 11.2.0.4 11.2.0.1

Oracle 数据库和补丁下载地址 12.1.0.2 11.2.0.4 11.2.0.1 AIX Linux Windows平台AIX 12.1.0.2 DATABASE DB 数据库软件介质下载地址&#xff1a;ftp://104.236.52.210/aix.ppc64_12102_database_1of2.zipftp://104.236.52.210/aix.ppc64_12102_database_2of2.zipAIX 11.2.0.4…

用swing设计一个打地鼠小游戏_这7个风靡欧美的英语小游戏,学会胜过刷100道题!...

精彩导读小编为大家搜罗了一些在国外家喻户晓的语言类小游戏。好的方法胜过刷上100道题&#xff0c;真正让孩子觉得好玩&#xff0c;教学才会事半功倍&#xff01;01Would You Rather...最近牛津大学的面试考题惊天地爆出了一题&#xff1a;你想要变成吸血鬼还是想要变成僵尸&a…

oracle数据库9i安装,Oracle 9i数据库服务器的安装和辅助软件安装教程

安装数据库服务器以Oracle 9i数据库服务器软件的安装过程为例&#xff0c;介绍数据库服务器的安装过程。14.3.1 安装数据库服务器系统环境数据库服务器安装之前&#xff0c;一般都需要检测系统安装环境&#xff0c;以避免系统不支持、内存不够、硬盘空间不足等情况发生。下面从…

python计算排队时间_Python(pdb)-排队执行命令

我正在实现一个“断点”系统以用于我的Python开发,该系统将允许我调用一个实质上调用pdb.set_trace()的函数.我想实现的某些功能要求我在set_trace上下文中时通过代码控制pdb.例&#xff1a;disableList []def breakpoint(nameNone):def d():disableList.append(name)#****#is…

oracle数据库各组件介绍,Oracle 数据库 组件相关说明【第一部分】

参考MOS文档:Information On Installed Database Components and Schemas (文档 ID 472937.1)Oracle 组件可以通过下面的SQL查看&#xff0c;这是我的一个生产库的组件情况&#xff0c;我当时是都部署了&#xff1a;SQL> col COMP_NAME for a50SQL> select comp_id,comp_…

oracle 触发器 行级,oracle的行级触发器使用

行级触发器&#xff1a;当触发器被触发时&#xff0c;要使用被插入、更新或删除的记录中的列值&#xff0c;有时要使用操作前、后列的值.:NEW 修饰符访问操作完成后列的值:OLD 修饰符访问操作完成前列的值例1: 建立一个触发器, 当职工表 emp 表被删除一条记录时&#xff0c;把被…

python include的功能_在Python的Config中增加Include功能

在python中配置文件分析我一般都用configparser。很好&#xff0c;符合我的一贯需求。文本格式、简单、内置。如:[db]hostlocalhostuserdlpasswd12345678[other]因为小程序较多&#xff0c;分别在不同地方&#xff0c;但是都有些共同的配置(如DB的配置)。如果能在配置文件中inc…

oracle的空闲等待事件,Oracle 常见的33个等待事件详解

一&#xff0e; 等待事件的相关知识&#xff1a;1.1 等待事件主要可以分为两类&#xff0c;即空闲(IDLE)等待事件和非空闲(NON-IDLE)等待事件。1). 空闲等待事件指ORACLE正等待某种工作&#xff0c;在诊断和优化数据库的时候&#xff0c;不用过多注意这部分事件。2). 非空闲等待…

python wechatpay微信支付回调_【微信支付】JSAPI支付开发者文档

XXE漏洞需要您在回调处理代码里面解析XML之前&#xff0c;加入禁用实体解析的代码&#xff0c;不同语言设置的内容不同&#xff0c;下面提供了几种主流开发语言的设置指引(您可以根据关键字找到xml解析组件采取对应方法升级)&#xff1a;【PHP】 解析XML代码前加入&#xff1a;…

如何查看oracle压力,oracle压力测试之orabm

数据库压力测试一、用orabm测试数据库CPUOrabm通过在用户指定的并发下运行指定数量的事务来测试系统性能.它主要测试数据库服务器的CPU性能和内存。orabm实际上是一堆sql的集合。orabm可以用在linux/windows/solaris下使用。orabm不一定需要运行在数据库所在服务器上&#xff0…

C++ static静态成员变量

在C中&#xff0c;我们可以使用静态成员变量来实现多个对象共享数据的目标。静态成员变量是一种特殊的成员变量&#xff0c;它被关键字static修饰&#xff0c;例如&#xff1a; class Student{ public:Student(char *name, int age, float score);void show(); public:static …

oracle河南城建学院实验报告,数据处理实验报告

化工与材料工程学院实验报告《试验设计与数据处理》上机实验报告学 生 学 号 学 生 姓 名 专 业 班 级 指 导 教 师 联合指导教师 完 成 日 期教 授 教 授 2010.5.27.第 1 章 实验一 Excel 基础及有关操作技巧1.1 实验目的熟悉 Excel 基础操作&#xff0c;掌握 Excel 有关操作技…

import lombok 报错_Lombok注解@Getter @Setter详解

1.为什么强大​项目中经常使用bean&#xff0c;entity等类&#xff0c;绝大部分数据类类中都需要get、set、toString、equals和hashCode方法&#xff0c;尽管IDE工具都会帮我们生成。但自动生成这些代码后&#xff0c;如果bean中的属性一旦有修改、删除或增加时&#xff0c;需要…

司铭宇老师:如何让企业销售培训效果落地

如何让企业销售培训效果落地 在企业销售培训中&#xff0c;我们经常听到一个词&#xff0c;那就是“落地”。所谓的“落地”&#xff0c;简单来说就是将培训中所学到的知识和技能转化为实际的工作行动&#xff0c;从而提高销售业绩。但是&#xff0c;如何才能让销售培训效果真…