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


<<:  开源的WebPC让你使用浏览器远程控制 linux or windows

>>:  cv2播Video+声音 cv2+ffpyPlayer 【附码】

[Day3] 资讯安全的攻击与威胁-恶意程序及密码破解

今天研究的攻击威胁类型是恶意程序和密码破解。 恶意程序 恶意程序是攻击者设计用来破坏目标对象的电脑设...

Day30-终於完赛啦之用iOS本地推播来庆祝一下!!

很难得,有一件事情能持续以恒地坚持一个月(除了变胖这件事以外),最後的分享就来讲讲本地推播吧: 本地...

[Java]手把手带你实作PTT爬虫(2)-文章内容及储存

前言 上一篇教学实作了一个简单的爬虫并成功的爬到了 PTT 的文章列表 这次就继续将 PTT 文章内...

系统分析师的养成之路—案例分享(1)

上周我跟大家分享了系统分析师必须具备的「观察」、「商业思维」、「聆听」共3个软实力,但在讲述下一个主...

Day27 React-实作todoList(二)建立子元件

Header元件 第一个元件先从 Header.js 开始 Header要负责 显示标题 待办事项 ...