FFI bindings of Spike - Rust example


项目仓库:libspike-interfaces


叶剑豪

目录

  • FFI bindings of Spike
    • 名词解释
    • 需求分析
    • 做了一些的工作
  • Rust FFI bindings
    • 项目结构
    • 代码示例
    • rust 对 unsafe 进行进一步封装
  • 其它
    • 未来的工作

FFI bindings of Spike

名词解释


  • FFI(Foreign Function Interface): 外部函数接口
  • Spike: 一个RISC-V ISA的模拟器
  • t1: 使用Chisel硬件构造语言开发RISC-V V extension长矢量机硬件验证框架
  • nix: 一个函数式的包管理器

需求分析

  • 现有验证框架的不足
    • rtl和模拟框架之间的dpi接口不易维护
    • 验证框架中的spikeC++代码不易维护和扩展
    • online difftest不是必要的
    • 设计,开发和验证的紧耦合
  • 基于offline diffetst的验证框架可以带来很多其它的好处
    • 从流程上来说和online difftest相比更加灵活
    • 更简洁的仿真框架设计
    • 方便序列化

做了一些的工作

  • 开发了一套spikeC API
  • C通过C APIspike交互的example
  • Rust通过FFI绑定到spikeC API上的example
    • RustFFI支持很好
    • Rust的代码更易于维护和扩展
  • t1提交了我在Github上的第一个PR
    • 修复了t1README.mdnixbuild问题

遇到的难点

  • t1基于nix的项目管理
  • RustFFI接口
    • 大量的unsafe
    • 生命周期管理
    • 内存管理

libspike-interfaces

项目结构

.
├── examples
│  ├── elfloader              // C API example
│  └── rs_elfloader           // Rust FFI example
├── nix                       // nix build scripts
│  ├── examples
│  └── pkgs
├── resources                 // resources
└── src                       // spike C API implementation
    ├── CMakeLists.txt
    ├── spike_interfaces.cc
    ├── spike_interfaces.h
    └── spike_interfaces_c.h

代码示例

C-API

void spike_register_callback(ffi_callback callback);
spike_t* spike_new(const char* arch, const char* set, const char* lvl);
spike_processor_t* spike_get_proc(spike_t* spike);
void spike_destruct(spike_t* spike);
// ...

rust:

#[link(name = "spike_interfaces")]
extern "C" {
  pub fn spike_register_callback(callback: FfiCallback);
  fn spike_new(arch: *const c_char, set: *const c_char, lvl: *const c_char) -> *mut ();
  fn spike_get_proc(spike: *mut ()) -> *mut ();
  fn spike_destruct(spike: *mut ());
  // ...
}

rust 进行进一步封装

pub struct Spike {
  spike: *mut (),
}

impl Spike {
  pub fn new(arch: &str, set: &str, lvl: &str) -> Self {
    let arch = CString::new(arch).unwrap();
    let set = CString::new(set).unwrap();
    let lvl = CString::new(lvl).unwrap();
    let spike = unsafe { spike_new(arch.as_ptr(), set.as_ptr(), lvl.as_ptr()) };
    Spike { spike }
  }

  pub fn get_proc(&self) -> Processor {
    let processor = unsafe { spike_get_proc(self.spike as *mut ()) };
    Processor { processor }
  }
}

其它

未来的工作

  • 完善spikeC API并且贡献上游
    • 支持序列化与反序列化
    • 支持UB检测
    • 支持自定义指令的拓展接口
  • 完善RustFFI接口以及对应的example
  • 实现offline difftest的验证框架
  • spike C-API的更多使用场景
    • RISC-V的总线行为模型
    • 基于trace的性能分析模型
    • 协同仿真框架

谢谢