CNRV

关注RISC-V和Chisel以及开源IC和EDA在中国的发展

View the Project on GitHub

RISC-V 双周简报 (2018-09-16)

要点新闻:

RV新闻

RISC-V Day Tokyo和RISC-V Summit的议程出炉

本年度最后两场官方举办的RISC-V研讨会议程均已出炉,有机会参加的请不要错过。

华米发布其基于RISC-V的面向可穿戴领域的MCU芯片

华米最新的发布会上,其创始人在发布了新款手表的同时,还发布了其基于RISC-V的可穿戴领域的AI芯片,黄山一号(MHS001)。

作为SiFive的最近一轮投资人之一,这颗芯片采用了SiFive的嵌入式RISC-V CPU,并且集成了NN加速模块,用于加速面向健康检测的神经网络处理任务。

Link: AI全面覆盖!华米科技发布全球可穿戴领域第一颗人工智能芯片

嘉楠科技发布搭载RISC-V CPU的AI芯片

嘉楠科技最近发布了其最新的端测AI芯片Kendryte K210,这颗芯片采用了基于rocket-chip的双核RV64GCC RISC-V CPU,内置8M字节的内存,应该没有MMU,可以运行RTOS,辅以多种自研AI加速器。

这颗芯片面向端测,提供机器视觉与语音识别能力。

K210

Link: 官网 https://kendryte.com/

MIT 和 UC Berkeley 联合启动 Keystone 计划

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的目标:

  1. 可信链 (Chain of Trust)
    • 可信启动 (Secure boot)
    • 远程认证 (Remote attestation)
    • 安全密钥供给 (Secure key provisioning)
  2. 内存隔离 (Memory Isolation)
    • 内存的物理保护 (Physical memory protection)
    • 页表隔离 (Page table isolation)
  3. 抵御物理攻击 (Defense against Physical Attack)
    • 内存加密 (Memory encryption)
    • 内存地址总线加密 (Memory address bus encryption)
  4. 抵御测信道攻击 (Defense against Side-channel Attack)
    • 基于隔离的体系结构 (Isolated architecture)
  5. 形式化验证 (Formal Verification)
  6. 部署 (Deployment)
    • RISC-V QEMU 仿真
    • 基于FireSim的FPGA仿真 (FPGA-based deployment (FireSim))
    • 流片 (Tape out to chip)
  7. 安全供应链的管理 (Secure supply-chain management)

技术讨论

RISC-V还缺少large code model

soberlsw-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,并且变成了auipcld

   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

关于 DDR3 控制器的使用

Andreas Dachsberger 希望可以在 Arty 板上使用外置 DDR3,问题是:

  1. 在 freedom-e-sdk 中是否已有支持 DDR3 内存控制器?
  2. 若有,如何使用?

详细讨论细节见:RISC-V SW Dev

代码更新

SiFive开源Freedom U540 Bootloader

SiFive最近开源了Freedom U540芯片,也就是HiFive Unleashed上搭载的处理器芯片的Bootloader。

SiFive将此项目开源的目的之一是希望通过开源帮助用户构建可靠的可信任的Bootloader,这一改过去其他厂商闭源Bootloader并且一定程度上掩盖了问题的作风。

SiFive还发起了一项小的竞赛,第一个构建出和U540一模一样的ROM的PR发起者将会得到一块Unleashed的开发板.

Links:


Chisel 增刊

Chisel 合并 Vec 对象的方法

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):_*) )

小编对此的理解是, storeTableWalkerRequestloadTableWalkerRequest 是 Chisel 的 Vec 类型,它继承自 Scala 的 IndexedSeq 类型,可以用 ++ 符号把两个 Vec 合并,然后为了把合并后的 Vec 变成 IndexedSeq 类型,需要用 IndexedSeq() 进行转换。IndexedSeq() 接受多个参数而不是一个集合,所以需要用 :_* 把一个集合参数变成多个参数,最后在用 VecInit() 变成 Vec 类型。

不过小编疑惑的是,既然 Chisel 的 Vec 类型继承自 Scala 的 IndexedSeq 类型,可以用 ++ 符号合并,这样应该就算是合并两个 Vec 了,何必像上面那么麻烦呢?

Chisel实验性的支持将文件内容载入Memeory

Chisel最近开始支持从文件载入十六进制或者二进制的内容到Memeory中用于仿真。这一功能和verilog的$readmem实现类似的功能。

此功能将一定程度的方便开发者,以下是简单的示例。

import chisel3.util.experimental.loadMemoryFromFile

val memory = Mem(memoryDepth, memoryType)

loadMemoryFromFile(memory, "/workspace/workdir/mem1.txt")

Links:

暴走事件

2018年10月

2018年12月

招聘简讯

CNRV提供为行业公司提供公益性质的一句话的招聘信息发布,若有任何体系结构、IC设计、软件开发的招聘信息,欢迎联系我们!


整理编集: 宋威、黄柏玮、汪平、林容威、傅炜、巍巍、郭雄飞、黄玮、李健

特别感谢: Shawn


欢迎关注微信公众号CNRV,接收最新最时尚的RISC-V讯息!

CNRV微信公众号


知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行许可。