RISC-V on Rust 从零开始(9) - 实作memory model

CPU指令可以分成两大类,一是操作CPU内部暂存器的算术逻辑指令,一是存取记忆体,也就是所谓的load/store指令。要模拟算术逻辑指令,用先前所定义的Core资料结构即可,因为内部已经包含了暂存器,而load/store指令则需要与core外部的元件沟通,常见的有l1 cache、l2 cache、bus等,目前为了简化实作,先统一由一个memory model来代表core外部的系统。memory model很单纯,就是一块可以读写的记忆体,以64位元系统为例,位址空间就是0 ~ 2**64-1这么大。

memory model中使用HashMap来存放资料,此处引入block的概念,将整个记忆体空间以32 bytes为单位进行分割,HashMap的key为block base address,value为长度32的u8阵列,当CPU存取某个address时,memory model会先查看此address所属的block是否存在,是的话直接从HashMap中取得资料,否则创建新的block加入HashMap。使用HashMap的好处是可以动态的增加记忆体空间,效能方面虽然比array或vector略差,但可以有很好的弹性。

首先实作写1 byte的逻辑:

pub fn write_byte(&mut self, addr: AddressType, value: u8) {    let block_base = addr & (!(0x1f as AddressType));    let block_offset = addr - block_base;    if !self.data.contains_key(&block_base) {        self.data.insert(block_base, [0; 32]); // 插入新block    }    let block = self.data.get_mut(&block_base).unwrap();    block[block_offset as usize] = value;}pub fn read_byte(&mut self, addr: AddressType) -> u8 {    let block_base = addr & (!(0x1f as AddressType));    let block_offset = addr - block_base;    if !self.data.contains_key(&block_base) {        self.data.insert(block_base, [0; 32]);    }    let block = self.data.get(&block_base).unwrap();    block[block_offset as usize]}

由于存取memory的长度不是固定的,因此需要实作一个比较general的interface:

fn access_memory(&mut self, payload: &mut Payload) {    match payload.op {        MemoryOperation::READ => {            for i in 0..payload.data.len() {                payload.data[i] = self.read_byte(payload.addr + i as AddressType);            }        }        MemoryOperation::WRITE => {            for i in 0..payload.data.len() {                self.write_byte(payload.addr + i as AddressType, payload.data[i]);            }        }        MemoryOperation::INVALID => panic!("Invalid mem op"),    }}

此处的payload类似网路的封包,实际上硬体也是利用类似的方式互相沟通。

接着core就可以呼叫access_memory来读写记忆体:

fn write_memory(&mut self, address: AddressType, data: &[u8]) {    let mut payload = Payload {        addr: address,        data: data.to_vec(),        op: MemoryOperation::WRITE,    };    self.mem_if.as_mut().unwrap().access_memory(&mut payload);}fn read_memory(&mut self, address: AddressType, data: &mut [u8]) {    let mut payload = Payload {        addr: address,        data: data.to_vec(),        op: MemoryOperation::READ,    };    self.mem_if.as_mut().unwrap().access_memory(&mut payload);    for i in 0..data.len() {        data[i] = payload.data[i];    }}

有了memory model之后,就可以来实作load/store指令了。完整程式码可以参考:rv-sim


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章