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
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#![allow(unused)]
use std::cell::RefCell;
use RegT;
use bus::Bus;

const MAX_CONTROLLERS: usize = 16;

/// a single interrupt controller
#[derive(Clone,Copy)]
pub struct Controller {
    pub int_enabled: bool,
    pub int_requested: bool,
    pub int_pending: bool,
    pub int_vec: u8,
}

impl Controller {
    pub fn new() -> Controller {
        Controller {
            int_enabled: true,
            int_requested: false,
            int_pending: false,
            int_vec: 0,
        }
    }
    pub fn reset(&mut self) {
        self.int_enabled = true;
        self.int_requested = false;
        self.int_pending = false;
        self.int_vec = 0;
    }
}

/// interrupt controller daisychain
pub struct Daisychain {
    pub num_ctrl: usize,
    pub ctrl: [Controller; MAX_CONTROLLERS],
}

impl Daisychain {
    /// initialize a new daisychain
    pub fn new(num_controllers: usize) -> Daisychain {
        Daisychain {
            num_ctrl: num_controllers,
            ctrl: [Controller::new(); MAX_CONTROLLERS],
        }
    }

    /// reset interrupt controllers in daisychain
    pub fn reset(&mut self) {
        for ctrl in self.ctrl.iter_mut() {
            ctrl.reset();
        }
    }

    /// request an interrupt from an interrupt controller, called by bus
    pub fn irq(&mut self, bus: &Bus, ctrl_id: usize, vec: u8) {
        if self.ctrl[ctrl_id].int_enabled {
            {
                let ctrl = &mut self.ctrl[ctrl_id];
                assert!(!ctrl.int_pending);
                ctrl.int_enabled = false;
                ctrl.int_requested = true;
                ctrl.int_vec = vec;
            }
            bus.irq_cpu();

            // disable interrupt on downstream controllers
            for i in ctrl_id + 1..self.num_ctrl {
                self.ctrl[i].int_enabled = false;
            }
        }
    }

    /// CPU acknowledges interrupt request, return the interrupt vector
    pub fn irq_ack(&mut self) -> RegT {
        // find the interrupt controller which issued the request
        // and return it's interrupt vector.
        // downstream controller remain in interrupt-disabled
        // state until the CPU sends the RETI
        for ctrl in self.ctrl.iter_mut() {
            if ctrl.int_requested {
                ctrl.int_requested = false;
                ctrl.int_pending = true;
                return ctrl.int_vec as RegT;
            }
        }
        panic!("irq_ack() called without any interrupt pending!")
    }

    /// CPU executes a RETI, this enabled interrupts on downstream controllers
    pub fn irq_reti(&mut self) {
        let mut is_downstream = false;
        for ctrl in self.ctrl.iter_mut() {
            ctrl.int_enabled = true;
            if ctrl.int_pending {
                if is_downstream {
                    // interrupt-enable propagation stops at
                    // first downstream device where an interrupt
                    // is still pending
                    break;
                }
                ctrl.int_pending = false;
                is_downstream = true;
            }
        }
    }
}

// ------------------------------------------------------------------------------
#[cfg(test)]
mod test {
    use std::cell::RefCell;
    use super::*;
    use RegT;
    use Bus;
    use CPU;

    #[test]
    fn reset() {
        let mut daisy = Daisychain::new(8);
        assert!(8 == daisy.num_ctrl);
        for ctrl in daisy.ctrl.iter() {
            assert!(ctrl.int_enabled);
            assert!(!ctrl.int_requested);
            assert!(!ctrl.int_pending);
            assert!(0 == ctrl.int_vec);
        }
        daisy.ctrl[0].int_enabled = false;
        daisy.ctrl[1].int_requested = true;
        daisy.ctrl[2].int_pending = true;
        daisy.ctrl[3].int_vec = 0x10;
        daisy.reset();
        for ctrl in daisy.ctrl.iter() {
            assert!(ctrl.int_enabled);
            assert!(!ctrl.int_requested);
            assert!(!ctrl.int_pending);
            assert!(0 == ctrl.int_vec);
        }
    }

    const DEV0: usize = 0;
    const DEV1: usize = 1;
    const DEV2: usize = 2;
    const NUM_DEVS: usize = 3;

    struct State {
        pub irq_received: bool,
        pub irq_ctrl_id: usize,
        pub irq_vec: u8,
        pub irq_cpu_called: bool,
    }
    struct TestBus {
        pub state: RefCell<State>,
        pub daisy: RefCell<Daisychain>,
        pub cpu: RefCell<CPU>,
    }
    impl TestBus {
        pub fn new() -> TestBus {
            TestBus {
                state: RefCell::new(State {
                    irq_received: false,
                    irq_ctrl_id: 0xFF,
                    irq_vec: 0,
                    irq_cpu_called: false,
                }),
                daisy: RefCell::new(Daisychain::new(NUM_DEVS)),
                cpu: RefCell::new(CPU::new()),
            }
        }
    }

    impl Bus for TestBus {
        fn irq(&self, ctrl_id: usize, vec: u8) {
            let mut state = self.state.borrow_mut();
            state.irq_received = true;
            state.irq_ctrl_id = ctrl_id;
            state.irq_vec = vec;
        }
        fn irq_cpu(&self) {
            let mut state = self.state.borrow_mut();
            state.irq_cpu_called = true;
        }
    }

    #[test]
    fn irq_ack() {
        let bus = TestBus::new();
        let mut daisy = bus.daisy.borrow_mut();
        // test with interrupt disabled
        daisy.ctrl[DEV0].int_enabled = false;
        daisy.irq(&bus, DEV0, 0x10);
        {
            let dev0 = &daisy.ctrl[DEV0];
            let state = bus.state.borrow();
            assert!(!dev0.int_enabled);
            assert!(!dev0.int_requested);
            assert!(!dev0.int_pending);
            assert!(dev0.int_vec == 0x00);
            assert!(!state.irq_received);
        }
        // test with interrupt enabled
        daisy.ctrl[DEV0].int_enabled = true;
        daisy.irq(&bus, DEV0, 0x10);
        {
            let dev0 = &daisy.ctrl[DEV0];
            let dev1 = &daisy.ctrl[DEV1];
            let dev2 = &daisy.ctrl[DEV2];
            let state = bus.state.borrow();
            assert!(!dev0.int_enabled);
            assert!(dev0.int_requested);
            assert!(!dev0.int_pending);
            assert!(dev0.int_vec == 0x10);
            assert!(state.irq_cpu_called);
            assert!(!dev1.int_enabled);
            assert!(!dev2.int_enabled);
        }
    }
}