bitsy_lang/sim/ext/
ram.rs

1use super::*;
2
3/// A same-cycle random-access memory.
4/// Has 64KiB of memory.
5/// Addressed by a 16-bit address.
6/// Reads and writes an 8-bit word at a time.
7#[derive(Debug)]
8pub struct Ram {
9    mem: [u8; 1 << 16],
10    read_addr: u16,
11    write_enable: bool,
12    write_addr: u16,
13    write_data: u8,
14}
15
16impl Ram {
17    pub fn new() -> Ram {
18        let mem = [0; 1 << 16];
19        Ram {
20            mem,
21            read_addr: 0,
22            write_enable: false,
23            write_addr: 0,
24            write_data: 0,
25        }
26    }
27
28    pub fn load_from_file<P: AsRef<std::path::Path>>(&mut self, path: P) -> anyhow::Result<()> {
29        let data = std::fs::read(&path)?;
30        let len = data.len().min(1<<16);
31        for i in 0..len {
32            self.mem[i] = data[i];
33        }
34        Ok(())
35    }
36
37    fn read(&self) -> Value {
38        Value::Word(8, self.mem[self.read_addr as usize] as u64)
39    }
40
41    pub fn render(&self) -> String {
42        //dbg!(self);
43        let mem = if let Some(index) = self.mem.iter().position(|&x| x == 0) {
44            &self.mem[..index+1]
45        } else {
46            &self.mem
47        };
48        format!("RAM: {:?}", String::from_utf8_lossy(mem))
49    }
50}
51
52impl ExtInstance for Ram {
53    fn incoming_ports(&self) -> Vec<PortName> {
54        vec![
55            "read_addr".to_string(),
56            "write_enable".to_string(),
57            "write_addr".to_string(),
58            "write_data".to_string(),
59        ]
60    }
61    fn outgoing_ports(&self) -> Vec<PortName> { vec!["read_data".to_string()] }
62
63    fn update(&mut self, port: &PortName, value: Value) -> Vec<(PortName, Value)>  {
64        if value.is_x() {
65            return vec![("read_data".to_string(), self.read())];
66        }
67        if port == "read_addr" {
68            if let Value::Word(16, addr) = value {
69                self.read_addr = addr as u16;
70                return vec![("read_data".to_string(), self.read())];
71            } else {
72                panic!("Ram must receive a Word<16> on read_addr. Received {value:?}")
73            }
74        } else if port == "write_enable" {
75            match value {
76                Value::Word(1, 0) => self.write_enable = false,
77                Value::Word(1, 1) => self.write_enable = true,
78                _ => panic!(),
79            }
80        } else if port == "write_addr" {
81            match value {
82                Value::Word(16, v) => self.write_addr = v.try_into().unwrap(),
83                _ => panic!(),
84            }
85        } else if port == "write_data" {
86            match value {
87                Value::Word(8, v) => self.write_data = v.try_into().unwrap(),
88                _ => panic!("write_data value must be Word<8>: {value:?}"),
89            }
90        } else {
91            panic!("Ram may only recieve data on read_data: received data on {port} {value:?}")
92        }
93        vec![]
94    }
95
96    fn reset(&mut self) -> Vec<(PortName, Value)> {
97        /*
98        for (i, ch) in "Hello, World!\0".bytes().enumerate() {
99            self.mem[i] = ch;
100        }
101
102        vec![("read_data".to_string(), self.read())]
103        */
104        vec![]
105    }
106
107    fn clock(&mut self) -> Vec<(PortName, Value)> {
108//        println!("Ram was clocked: {}", self.render());
109        if self.write_enable {
110            println!("Writing to RAM: 0x{:04x} <= {:02x}", self.write_addr, self.write_data);
111            self.mem[self.write_addr as usize] = self.write_data;
112            let read_data = Value::Word(8, self.mem[self.read_addr as usize].into());
113            //println!("Ram wrote {read_data:?} at address 0x{:x}", self.write_addr);
114            vec![("read_data".to_string(), read_data)]
115        } else {
116            vec![]
117        }
118    }
119}