1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use core::ptr;

pub struct Fifo {
    buf_size: u32,
    tx_ptr: u32,
    rx_ptr: u32,
    buf: [u8; 1],
}

impl Fifo {
    pub fn reset(&mut self, size: isize) {
        let buf_size = (size - 4*3) as u32;
        unsafe {
            ptr::write_volatile(&mut self.buf_size, 0);
            ptr::write_volatile(&mut self.tx_ptr, 0);
            ptr::write_volatile(&mut self.rx_ptr, 0);
            ptr::write_volatile(&mut self.buf_size, buf_size);
        }
    }
    
    pub fn send_char(&mut self, c: u8) -> bool {
        unsafe {
            let rx_ptr = ptr::read_volatile(&self.rx_ptr);
            let tx_ptr = ptr::read_volatile(&self.tx_ptr as *const u32);
            let buf = &mut self.buf[0] as *mut u8;
            let next_ptr = if tx_ptr + 1 < self.buf_size {
                tx_ptr + 1
            } else {
                0
            };
            if next_ptr != rx_ptr {
                ptr::write_volatile(buf.offset(tx_ptr as isize), c);
                ptr::write_volatile(&mut self.tx_ptr, next_ptr);
                true
            } else {
                false
            }
        }
    }

    pub fn recv_char(&mut self) -> Option<u8> {
        unsafe {
            let tx_ptr = ptr::read_volatile(&self.tx_ptr as *const u32);
            let rx_ptr = ptr::read_volatile(&self.rx_ptr as *const u32);
            let buf = &self.buf[0] as *const u8;
            if tx_ptr != rx_ptr {
                let c = ptr::read_volatile(buf.offset(rx_ptr as isize));
                let next_ptr = if rx_ptr + 1 < self.buf_size {
                    rx_ptr + 1
                } else {
                    0
                };
                ptr::write_volatile(&mut self.rx_ptr as *mut u32, next_ptr);
                Some(c)
            } else {
                None
            }
        }
    }
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn fifo_test() {
        let mut mem :  [u8; 256] = [0; 256];
        let ptr = &mut mem as *mut u8;
        unsafe {
            let fifo = &mut *(ptr as *mut Fifo);
            fifo.buf_size = 3;
            fifo.tx_ptr = 0;
            fifo.rx_ptr = 0;
            assert_eq!(fifo.recv_char(), None);
            assert_eq!(fifo.send_char('a' as u8), true);
            assert_eq!(fifo.send_char('b' as u8), true);
            assert_eq!(fifo.send_char('c' as u8), false);
            assert_eq!(fifo.recv_char().unwrap(), 'a' as u8);
            assert_eq!(fifo.send_char('c' as u8), true);
            assert_eq!(fifo.send_char('d' as u8), false);
            assert_eq!(fifo.recv_char().unwrap(), 'b' as u8);
            assert_eq!(fifo.recv_char().unwrap(), 'c' as u8);
            assert_eq!(fifo.recv_char(), None);
        }
    }
}