perilus

RISC-V processor built with Chisel
git clone https://git.bradleygannon.com/perilus.git
Log | Files | Refs | Submodules | README

perilus.rs (4948B)


      1 use bounded_integer::BoundedU32;
      2 use std::{ffi::CString, io, path::PathBuf, slice};
      3 
      4 unsafe extern "C" {
      5     fn perilus_init();
      6     fn perilus_eval();
      7     fn perilus_drop();
      8     fn perilus_increment_time();
      9     fn perilus_set_clock(new_clock: u32);
     10     fn perilus_get_pc() -> u32;
     11     fn perilus_set_pc(new_pc: u32);
     12     fn perilus_set_reset(new_reset: u32);
     13     fn perilus_get_control_unit_state() -> u32;
     14     fn perilus_get_register_file() -> *const u32;
     15     fn perilus_set_register(reg: u32, value: u32);
     16     fn perilus_get_memory() -> *const u32;
     17     fn perilus_set_memory(address: u32, value: u32);
     18     unsafe fn start_vcd(filename: *const std::ffi::c_char);
     19     unsafe fn stop_vcd();
     20 }
     21 
     22 pub type Register = BoundedU32<0, 31>;
     23 pub type MemoryIndex = BoundedU32<0, { Perilus::MEMORY_SIZE_WORDS as u32 }>;
     24 
     25 pub struct Perilus;
     26 
     27 impl Perilus {
     28     pub const NUM_REGISTERS: usize = 32;
     29     pub const MEMORY_SIZE_WORDS: usize = 1_048_576;
     30     pub const MEMORY_SIZE_BYTES: usize = Self::MEMORY_SIZE_WORDS * 4;
     31 
     32     pub fn init() -> Perilus {
     33         unsafe { perilus_init() };
     34         Perilus
     35     }
     36     pub fn pulse_clock(&self) {
     37         unsafe {
     38             perilus_set_clock(0);
     39             perilus_eval();
     40             perilus_increment_time();
     41             perilus_set_clock(1);
     42             perilus_eval();
     43             perilus_increment_time();
     44         }
     45     }
     46     pub fn reset(&self) {
     47         unsafe {
     48             perilus_set_reset(1);
     49             self.pulse_clock();
     50             perilus_set_reset(0);
     51         }
     52     }
     53     pub fn get_pc(&self) -> u32 {
     54         unsafe { perilus_get_pc() }
     55     }
     56     pub fn set_pc(&self, new_pc: u32) {
     57         unsafe { perilus_set_pc(new_pc) };
     58     }
     59     pub fn get_control_unit_state(&self) -> u32 {
     60         unsafe { perilus_get_control_unit_state() }
     61     }
     62     pub fn get_registers(&self) -> &'static [u32] {
     63         unsafe { slice::from_raw_parts(perilus_get_register_file(), Perilus::NUM_REGISTERS) }
     64     }
     65     pub fn set_register(&self, register: Register, value: u32) {
     66         unsafe { perilus_set_register(register.get(), value) };
     67     }
     68     pub fn get_memory(&self) -> &'static [u32] {
     69         unsafe { slice::from_raw_parts(perilus_get_memory(), Perilus::MEMORY_SIZE_WORDS) }
     70     }
     71     pub fn set_memory(&self, address: u32, value: u32) {
     72         unsafe { perilus_set_memory(address, value) };
     73     }
     74     pub fn get_memory_byte(&self, address_bytes: u32) -> u8 {
     75         let word = self.get_memory()[(address_bytes / 4) as usize];
     76         (word >> ((address_bytes % 4) * 8)) as u8
     77     }
     78     pub fn set_memory_byte(&self, address_bytes: u32, value: u8) {
     79         let address_words = address_bytes / 4;
     80         let shift = (address_bytes % 4) * 8;
     81         let current = self.get_memory()[address_words as usize];
     82         let mask = !(0xffu32 << shift);
     83         self.set_memory(address_words, (current & mask) | ((value as u32) << shift));
     84     }
     85     pub fn memory_bytes(&self) -> Vec<u8> {
     86         self.get_memory()
     87             .iter()
     88             .flat_map(|word| word.to_le_bytes())
     89             .collect()
     90     }
     91     pub fn load_bytes(&self, bytes: &[u8]) -> io::Result<()> {
     92         if bytes.len() > Self::MEMORY_SIZE_BYTES {
     93             return Err(io::Error::new(
     94                 io::ErrorKind::InvalidInput,
     95                 "Provided file is too large to fit in simulated memory",
     96             ));
     97         }
     98         for (address_bytes, value) in bytes.iter().enumerate() {
     99             self.set_memory_byte(address_bytes as u32, *value);
    100         }
    101         Ok(())
    102     }
    103     pub fn load_file(&self, file: PathBuf) -> io::Result<()> {
    104         self.load_bytes(&std::fs::read(file)?)
    105     }
    106     pub fn save_file(&self, file: PathBuf) -> io::Result<()> {
    107         std::fs::write(file, self.memory_bytes())
    108     }
    109     pub fn start_trace(&self, file: PathBuf) -> io::Result<()> {
    110         // Ensure the parent directory exists (VCD file will be created by Verilator)
    111         if let Some(parent) = file.parent() {
    112             std::fs::create_dir_all(parent)?;
    113         }
    114         let c_string = CString::new(file.to_string_lossy().as_bytes())
    115             .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
    116         unsafe {
    117             start_vcd(c_string.as_ptr());
    118         }
    119         Ok(())
    120     }
    121     pub fn stop_trace(&self) {
    122         unsafe {
    123             stop_vcd();
    124         }
    125     }
    126 }
    127 
    128 impl Drop for Perilus {
    129     fn drop(&mut self) {
    130         unsafe { perilus_drop() };
    131     }
    132 }
    133 
    134 #[cfg(test)]
    135 mod tests {
    136     use super::*;
    137 
    138     #[test]
    139     fn memory_roundtrip() {
    140         let perilus = Perilus::init();
    141         let data = [0xde, 0xad, 0xbe, 0xef, 0x01, 0x02, 0x03];
    142         perilus.load_bytes(&data).unwrap();
    143         assert_eq!(&perilus.memory_bytes()[..data.len()], &data);
    144 
    145         perilus.set_memory_byte(0, 0xaa);
    146         assert_eq!(perilus.get_memory_byte(0), 0xaa);
    147 
    148         let oversized = vec![0u8; Perilus::MEMORY_SIZE_BYTES + 1];
    149         assert!(perilus.load_bytes(&oversized).is_err());
    150     }
    151 }