CNRV

为推广RISC-V尽些薄力

View the Project on GitHub

RISC-V 双周简报 (2018-06-08)

要点新闻:

RV新闻

RISC-V Day 2018 Shanghai研讨会议程公布

RISC-V Day Shanghai

RISC-V Day 2018 Shanghai将会是本年度唯一由基金会在中国大陆举办的研讨会,会议的议程已经公布。(URL: https://tmt.knect365.com/risc-v-day-shanghai/agenda/1)

这次为期一天的研讨会汇集了一共15个演讲。上午的议程主要集中展示商业和开源的RISC-V CPU IP,分别来自AndesTech、SiFive、Syntacore、Codasip和来自国内的蜂鸟;而下午的议程则讨论处理器架构研究、处理器安全和的生态系统等话题。相比技术话题,或许能够认识更多业内的朋友对参会者来说更有价值。

早鸟票将于6月17日截止。我们同时也发起了非官方的支持学生参会的资助计划,参见下一篇报道。

社区活动

RISC-V Day 2018 Shanghai学生参会资助计划

RISC-V Day 2018 Shanghai将在6月30日在复旦大学光华楼吴文政报告厅举办,RISC-V正被国内越来越多的高校学生所接触和学习。相比x86、ARM和MIPS,我们相信因为RISC-V标准本身的开放,以及建立在这种开放之上的大量开源项目,能够让学术研究更加方便和高效。

我们在此发起一个非官方的面向学生的参会资助活动,尽我们所能来帮助学生参加这次会议,给更多对RISC-V感兴趣的同学一个深入了解RISC-V的机会。

资助计划详细内容如下

在校学生需要使用教育邮箱发送资助申请信到邮箱 xfguo@xfguo.org

学生需要首先简单介绍自己,尤其是学术经历和项目经历,之后我们很想了解你为什么对RISC-V感兴趣,并且愿意付出时间来参加这次会议。如果有接触过RISC-V甚至是做过任何相关项目,那么我们非常想听听你的相关经历和故事。(没有也没关系,这不会成为决定我们是否资助的关键因素)

请务必注明提供以下信息:

务必提供以下格式的邮件标题: “我想参加RISC-V Day Shanghai - 姓名 - 微信号“

第一波申请将于6月15日晚12点截止(因为6月17日是早鸟票结束的时间)

我们将会综合评定以后决定资助谁和资助多少费用,被资助的同学将会至少得到注册费用(20美元)的资助,如果资金充裕,对于外地同学我们也可能会提供部分路费和住宿费用的补贴。

一些说明:

在此我先替你们感谢提供资助的大佬们!


技术讨论

使用RVC指令导致32比特RV指令跨缓存块边界

如果使用RVC指令,所有指令将按16比特对齐,而非32比特。那么,就有可能出现一条32比特的正常指令横跨两个缓存块。 对于深流水线的高性能处理器来说这并不是一个问题,因为指令缓存会预先读取缓存块,补偿缓存缺失导致的指令读取延时。 但是对于嵌入式处理器来说,这就有可能造成一部分的性能下降。

于是,Sean提出,我们是否可以让编译器在发生32比特指令未对齐时自动插入一条RVC的NOP指令呢?

问题的答案看起来并不简单。RISC-V的RVC指令是和32比特指令一一对应的,因而链接器在目标文件链接时也能使用RVC替换。 可是链接时才知道那些指令被替换成RVC指令已经太迟了。链接器只能通过RVC替换来减小代码尺寸,不能增加代码尺寸。 如果要插入NOP来确保32比特指令的32比特对齐,则需要关闭链接时的RVC优化,这样会导致代码密度降低,和嵌入式处理器的一般要求相违背。

同时,也有人很细心的指出,这个问题在引入48比特指令时一定会出现。另外,MIPS最近提出的nanoMIPS指令集,通过同时使用16/32/48比特指令来压缩32比特指令获得了较好的压缩效果。 可见只是一味地减少指令长度能获得最佳的代码密度。

编译器的自动参数选择

一般来说我们认为给编译器更高的优化参数会得到更高的执行性能。但是在当前复杂的操作系统和处理器的情况下,事情并不那么简单。 为了解决最优编译器参数自动选取问题,GCC的开发者们开发出了一套能自动选取编译器参数的选取引擎(CK: Collective Knowledge): 一个用Python编写的,兼容的、可移植和扩展的开源计算机系统研究框架。 现在该框架已经支持GCC和LLVM。

中断用例

Liviu Ionescu 写了一篇文档详细讲述最常用的处理中断的情形,包括用于外设和上下文切换,并根据当前的 RISC-V 规范举了例子。

对于当前 RISC-V POSIX ABI,Liviu Ionescu 列举了处理中断的流程的例子:

  1. 首先处理最高优先级的中断:
    • 进入中断处理程序
    • 保存 16 个通用寄存器和 20 个浮点寄存器的内容到栈里
    • 调用 C/C++ 函数然后返回
    • 从栈里恢复 16 个通用寄存器和 20 个浮点寄存器的内容
    • 退出中断处理程序
  2. 如果还有其他优先级比较低或者一样的中断,每个中断都得重复一遍上面的步骤(N 个中断执行 N 次)
  3. 如果需要切换上下文,还得:
    • 进入裸的(naked)中断程序
    • 保存 32 个通用寄存器和 32 个浮点寄存器
    • 保存栈指针到当前的线程控制块
    • 选择下一个线程去执行
    • 恢复 32 个通用寄存器和 32 个浮点寄存器
    • 退出裸的(naked)中断程序

频繁地保存大量寄存器使得中断处理程序变得很慢,Liviu Ionescu 的建议是:

  1. 保留用于存放浮点寄存器的空间,但先不要马上保存这些寄存器的值,直到第一条浮点指令将被执行
  2. 把 ABI 规定由调用函数的程序(caller)保存的寄存器的值存到栈中
  3. 调用用于处理高优先级的中断程序
  4. 当在中断模式中如果需要,还会去调用其他中断程序去处理更低或者相同的中断
  5. 调用切换上下文(context_switch)处理程序(也许优先级最低)
    • 保存剩下的通用寄存器(ABI 规定由被调用的函数(callee)负责保存的)
    • 保存栈指针到当前的线程控制块
    • 选择下一个线程去执行
    • 从新的线程控制块恢复栈指针
    • 恢复剩下的通用寄存器(ABI 规定由被调用的函数(callee)负责保存的)
    • 返回
  6. 从栈中恢复 ABI 规定由调用函数的程序(caller)保存的寄存器的值
  7. 从新的线程上下文中的中断里返回

Links:

伪指令 加载地址(la) 与 加载本地地址(lla) (la vs lla)

Michael Clark 试图弄清如何处理重定向相关的伪指令,但是没有在指令集文档中找到相关内容的说明:

LA         载入地址
LLA        载入本地地址
LA.TLS.GD  载入地址, 本线程,全局动态加载(thread-local, global-dynamic)
LA.TLS.IE  载入地址, 本线程,执行时初始化 (thread-local, initial-exec)
LI         载入立即数
* 注:global-dynamic 和 initial-exec 是 GCC 线程私有变量类型请参阅相关文档

所以他提出问题:是不是任何字大小偏移量的重定向(word size relocation)都可能有两个入口,一个由高20位确定,另一个由低12位确定?

Michael Clark提供了部分伪代码的扩展的工作成果,但还未完善,可能最终需要反编译一些目标文件来弄清楚。

Stefan O’Rear: 支持通过反编译获得信息并记录下来,提醒是不是忽略了 TAILCALL

Nicolás Ojeda Bär : 提示 RISC-V 标准的第21章有相应的伪代码范例,虽然不够详实,但也对他编写 OCaml 本地编译器的后端起到了帮助。

Michael Clark已经通读了这些伪代码,但是他现在对于那些还没有在标准中列出的伪代码比较感兴趣。这些还没记录在案的伪代码似乎都和线程本地的存储和寻址有关(LA, LLA)。他维护了一个元数据和约束表用于伪代码的转换,但是还没有发现一个方法来表达以上这些伪代码的元数据。这个 REPO 里面的重定向部分并不准确,可以看作是他的工作笔记:https://github.com/michaeljclark/riscv-meta/blob/master/pseudos

Sebastian Huber : lla 伪代码由 GCC 提出,但还没有被写入 RISC-V 指令集手册第一卷 : 用户级指令集体系结构 2.2版中。

Tommy Murphy 表示可能应该提交这个遗漏到 RISC-V 指令集手册的 REPO 中。

Jim Wilson 表示汇编器文档可能可以用上一些 Micheal 的成果,但是很难找到志愿者做这些工作。lla 已经被提及过,lla 实际上是 载入本地地址的意思,它可以扩展为 auipc/addi,此时 auipc 获取地址的高位,而 addi 则获取地址的低位。 la 是载入地址的意思,它可以在 PIC 模式下,扩展为 auipc/l[wd],在非 PIC 模式下和 lla 一样扩展为 auipc/addi。

Link: sw-dev上的讨论

栈填充方向问题 (RFC: RISC-V microcontroller profile – About the direction of filling stack)

Iztok Jeras 在 RFC: RISC-V microcontroller profile 的thread上提出: 他注意到,在RISC-V上栈的填充和读取方向都是自顶向下的,这样在那些访问连续内存地址更高效的系统中可能会影响其性能。在大多数复杂系统中(通过内存突发访问的模式)访问连续内存地址会比随机的访问有更好的性能。

一般来说,地址默认都是增长的而不是减小。

在大型系统中,数据缓存通过提供随机的内存访问隐藏了这种硬件优化,而嵌入式系统则可能没有这样的机制。

这个帖子中他解释了 SRAM 的流水线式访问如何加速嵌入式系统的运行。

他谦虚地表示虽然自己可能对栈桢理解并不透彻,但是如果栈桢一旦确定就会被广泛使用。当然这可能也涉及到后向兼容的问题。

所以他提出问题:现在没有可能反转栈的访问顺序?

Jim Wilson 表示:人们应使用正式的栈回退信息而不是从头到尾地假定指令的顺序。所以这似乎并不太可能破坏任何关键性的东西。由于最近较忙无瑕他顾,遂建议在 github 上开一个 issue 备案。如果 Iztok 想自己尝试一下,可以看看 gcc/config/riscv/riscv.c. 中的 ‘riscv_for_each_saved_reg’ 函数。 建议为这个新主题开一个新的thread。

Iztok Jeras 按 Jim 的建议开了一个riscv-gcc 的 issues

Jacob Lifshay 提出 C 扩展是为自顶向下的生长的栈设计的,因为推栈和出战指令相对栈指针有一个 ‘zero-extended’ 偏移,遂无法访问小于栈指针的地址。Jim Wilson 认为 Jacob 可能误会了 Iztok 的意图, Iztok 并不是向改变栈的生长方向,他只是想是否可以改变寄存器的推栈顺序,比如从

sw ra,60(sp)
sw t0,56(sp)
sw t1,52(sp)
sw t2,48(sp)

变成

sw t2,48(sp)
sw t1,52(sp)
sw t0,56(sp)
sw ra,60(sp)

Link: isa-dev 上的讨论

安全点评

构建可信链条对隐私有需求的个人设备以及高安全性的云环境一直是重要的议题,传统Root of Trust构建基本上由verifiedboot以及measuredboot完成(参见hardenedboot),随着Intel SGX以及ARM平台TEE的推广,虽然当前云环境中主要的需求attestation并不需要secure enclave来实现,但这并不阻碍secure enclave被业界越来越关注。

早在2016年,MIT的研究人员就在Sanctum项目中尝试使用RISC-V实现Intel SGX类似的功能基础PoC最新版本的Sanctum使用Rocket开放核实现了PUF,attestation以及verifiedboot相关的构建信任链条的核心功能。

同时,MIT和UC Berkeley合作开发了另外一个叫Keystone的项目,Keystone在Sanctum的基础上使用了PMP(类似PaX UDEREF)以增强本身的安全性,不论是Sanctum还是Keystone都是开放的设计和实现,也就意味着任何人都可以去审计后门和漏洞,这一点和Intel SGX的复杂且封闭的设计和实现完全不同。

暴走事件

六月

七月

十月

十二月

招聘简讯

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


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


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

CNRV微信公众号


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