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