关注RISC-V和Chisel以及开源IC和EDA在中国的发展
要点新闻:
本年度最后两场官方举办的RISC-V研讨会议程均已出炉,有机会参加的请不要错过。
华米最新的发布会上,其创始人在发布了新款手表的同时,还发布了其基于RISC-V的可穿戴领域的AI芯片,黄山一号(MHS001)。
作为SiFive的最近一轮投资人之一,这颗芯片采用了SiFive的嵌入式RISC-V CPU,并且集成了NN加速模块,用于加速面向健康检测的神经网络处理任务。
Link: AI全面覆盖!华米科技发布全球可穿戴领域第一颗人工智能芯片
嘉楠科技最近发布了其最新的端测AI芯片Kendryte K210,这颗芯片采用了基于rocket-chip的双核RV64GCC RISC-V CPU,内置8M字节的内存,应该没有MMU,可以运行RTOS,辅以多种自研AI加速器。
这颗芯片面向端测,提供机器视觉与语音识别能力。
Link: 官网 https://kendryte.com/
Keystone: Open-source Secure Hardware Enclave 是 MIT 和 UC Berkeley 在2018年初联合启动的一个开源计划,准备建立一个支持硬件隔离运行空间(hardware enclave)的可信执行环境(Trusted Execution Environment, TEE)。已经隔离的运行空间非常重要,基于它的安全计算将能保护私有数据在共有平台上的安全运行,阻止第三方程序甚至操作系统对安全计算的窥探(confidentiality)和篡改(integrity)。在enclave方案中,当前已经有商业实现,比如Intel的SGX和ARM的TrustZone,但由于其封闭性生态并没有大规模应用,在安全风险方面也存在无法有效审计的问题,早在 2016 年,MIT 的研究人员在 Sanctum 项目中尝试使用 RISC-V 实现 Intel SGX 类似的功能基础PoC,最新版本的Sanctum使用Rocket开放核实现了PUF,attestation以及verifiedboot相关的构建信任链条的核心功能,Keystone作为Sanctum的后续版本在Sanctum的基础上使用了PMP以增强monitor本身的安全性,同时也有计划简化用户空间的开发库。
Keystone的目标:
soberl
在sw-dev
邮件列表表示在基于Sv48编译的时候碰到了Linking问题(用的是release 20180629 @ https://www.sifive.com/products/tools/ )源码如下
// b.c:
int x[0x40000000]; // 4GB
int y;
// main.c:
extern int x[0x40000000];
extern int y;
__attribute__((noinline)) void fooA(long a, long b) {
asm volatile(""::"r"(a), "r"(b)); // to notify gcc that a/b are depended
}
int main() {
fooA((long)x, y); // to try both address access and load on an address
return 0;
}
用编译命令riscv64-unknown-elf-gcc main.c b.c -mcmodel=medany -save-temps -Os
会得到如下错误信息
main.o: In function `main':
main.c:(.text.startup+0x0): relocation truncated to fit: R_RISCV_PCREL_HI20 against symbol `y' defined in COMMON section in b.o
对main.o
反编译后看到lla a5,y
转成了auipc
,但是得到的汇编比较奇怪
0: 00000797 auipc a5,0x0
4: 00078793 mv a5,a5
如果用-fPIC
或者-fPIE
选项(地址无关码,见[1]和[2]),并且加上-nostdlib
的话Linking可以通过,但是此时编译器给出的是la a5,y
,并且变成了auipc
和ld
100b0: 00001797 auipc a5,0x1
100b4: 0587b783 ld a5,88(a5) # 11108 <_GLOBAL_OFFSET_TABLE_+0x10>
对此Andrew认为是预料之中的,如果需要用到超过2GiB的静态数据,必须得用PIC(包括对library代码)。因为PIC会影响到代码大小和性能,在给大量静态数据分配内存的时候,建议最好使用malloc
或者mmap
。
soberl
随后贴出了另外一个例子(上例用了很大的数组其实是为了说明问题方便),实际的Linker Script是这样的,数据的物理地址从4GiB开始,并且Mbare模式下物理地址和虚拟地址相等。
MEMORY {
code : ORIGIN = 0x00000000, LENGTH = 0x00010000
data : ORIGIN = 0x100000000, LENGTH = 0x00010000
}
这样的情况下,上例中的b.c
哪怕是用“正常”大小的数组
// b.c
int x[0x10];
int y;
编译使用命令riscv64-unknown-elf-gcc main.c b.c -mcmodel=medany -save-temps -Os -march=rv64imafd -nostdlib -T test.ld
,还是会有同样的报错
main.o: In function `main':
main.c:(.text.startup+0x0): relocation truncated to fit: R_RISCV_PCREL_HI20 against symbol `y' defined in COMMON section in b.o
这样是不是意味着,code和data的地址偏移必须都得放在2GiB的范围之内?
Andrew Waterman
表示的确如此,并且这个其实部分来自于RISC-V的基本设计理念。因为实现大跨度的相对PC寻址(或者甚至是绝对寻址)都需要比较多的指令,另外这种需求也被认为不是很多,所以目前RISC-V没有做“大”code的模型。支持大code最终还是会做的,目前为了达到目的,可以把code和data相对放的靠近一些。使用PIC并不是最好的替代办法, 因为对静态变量的处理有可能和预期不一致(编译器会生成相对PC寻址,而不是用全局位移表GOT, Global Offset Table)。
Yes, that’s right. Part of this is fundamental to RISC-V. It takes a bunch of instructions to implement PC-relative addressing (or even absolute addressing) with larger offsets. Because of this, and the perceived lack of need, we haven’t yet made a “large” code model.
We should do that eventually, but in the mean time, the best workaround is to put the code and the data closer together somehow. PIC is not a great workaround, because it won’t handle static variables the way you want (the compiler will use PC-relative addressing for these, rather than GOT-indirect addressing).
Links
Andreas Dachsberger 希望可以在 Arty 板上使用外置 DDR3,问题是:
Bruce Hoult 认为 SiFive 提供的 FPGA bitstream 是用于产品评估,而非 hacking。Freedom-e-sdk 并未提供任何 CPU 核或者与其他硬件交互有关的内容。建议如果要支持 DDR ,最好从完全开源的核心入手,比如 rocket-chip 或者 picorv32.
详细讨论细节见:RISC-V SW Dev
SiFive最近开源了Freedom U540芯片,也就是HiFive Unleashed上搭载的处理器芯片的Bootloader。
SiFive将此项目开源的目的之一是希望通过开源帮助用户构建可靠的可信任的Bootloader,这一改过去其他厂商闭源Bootloader并且一定程度上掩盖了问题的作风。
SiFive还发起了一项小的竞赛,第一个构建出和U540一模一样的ROM的PR发起者将会得到一块Unleashed的开发板.
Links:
Edmond Cote 在邮件列表里问到,除了像下面的例子用 for 循环的方式逐个赋值之外,是否有更好的方式?
val storeTableWalkerRequest: Vec[Bool] = Wire(Vec(NumStorePorts, Bool()))
val loadTableWalkerRequest: Vec[Bool] = Wire(Vec(NumLoadPorts, Bool()))
// Concatenate all request vectors
val tableWalkerRequests: Vec[Bool] = Wire(Vec(NumStorePorts + NumLoadPorts, Bool()))
for (i <- 0 until NumStorePorts) {
tableWalkerRequests(i) := storeTableWalkerRequest(i)
}
for (i <- NumStorePorts until NumStorePorts + NumLoadPorts) {
tableWalkerRequests(i) := loadTableWalkerRequest(i - NumStorePorts)
}
Steve Burns 给出了解决方法:
VecInit(IndexedSeq((storeTableWalkerRequest ++ loadTableWalkerRequest):_*) )
小编对此的理解是, storeTableWalkerRequest
和 loadTableWalkerRequest
是 Chisel 的 Vec
类型,它继承自 Scala 的 IndexedSeq
类型,可以用 ++
符号把两个 Vec
合并,然后为了把合并后的 Vec
变成 IndexedSeq
类型,需要用 IndexedSeq()
进行转换。IndexedSeq()
接受多个参数而不是一个集合,所以需要用 :_*
把一个集合参数变成多个参数,最后在用 VecInit()
变成 Vec
类型。
不过小编疑惑的是,既然 Chisel 的 Vec
类型继承自 Scala 的 IndexedSeq
类型,可以用 ++
符号合并,这样应该就算是合并两个 Vec
了,何必像上面那么麻烦呢?
Chisel最近开始支持从文件载入十六进制或者二进制的内容到Memeory中用于仿真。这一功能和verilog的$readmem
实现类似的功能。
此功能将一定程度的方便开发者,以下是简单的示例。
import chisel3.util.experimental.loadMemoryFromFile
val memory = Mem(memoryDepth, memoryType)
loadMemoryFromFile(memory, "/workspace/workdir/mem1.txt")
Links:
CNRV提供为行业公司提供公益性质的一句话的招聘信息发布,若有任何体系结构、IC设计、软件开发的招聘信息,欢迎联系我们!
整理编集: 宋威、黄柏玮、汪平、林容威、傅炜、巍巍、郭雄飞、黄玮、李健
特别感谢: Shawn
欢迎关注微信公众号CNRV,接收最新最时尚的RISC-V讯息!
本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行许可。