-
Notifications
You must be signed in to change notification settings - Fork 11
/
interrupt_controller.v
156 lines (141 loc) · 3.83 KB
/
interrupt_controller.v
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
`default_nettype none
`timescale 1ns / 1ps
module interrupt_controller (
input wire clock,
input wire reset,
input wire m1_n,
input wire iorq_n,
output wire int_n,
input wire [4:0] int_req,
output reg [4:0] int_ack,
output wire [7:0] jump_addr,
input wire [15:0] A,
input wire [7:0] Di,
output wire [7:0] Do,
input wire wr_n,
input wire rd_n,
input wire cs
);
//////////////////////////////////////
// Interrupt Registers
//
// IF - Interrupt Flag (FF0F)
// Bit 4: New Value on Selected Joypad Keyline(s) (rst 60)
// Bit 3: Serial I/O transfer end (rst 58)
// Bit 2: Timer Overflow (rst 50)
// Bit 1: LCD (see STAT) (rst 48)
// Bit 0: V-Blank (rst 40)
//
// IE - Interrupt Enable (FFFF)
// Bit 4: New Value on Selected Joypad Keyline(s)
// Bit 3: Serial I/O transfer end
// Bit 2: Timer Overflow
// Bit 1: LCDC (see STAT)
// Bit 0: V-Blank
//
// 0 <= disable
// 1 <= enable
//////////////////////////////////////
wire[7:0] IF;
reg[7:0] IE;
parameter POLL_STATE = 0;
parameter WAIT_STATE = 1;
parameter ACK_STATE = 2;
parameter CLEAR_STATE = 3;
parameter VBLANK_INT = 0;
parameter LCDC_INT = 1;
parameter TIMER_INT = 2;
parameter SERIAL_INT = 3;
parameter INPUT_INT = 4;
parameter VBLANK_JUMP = 8'hA0; // 8'h40;
parameter LCDC_JUMP = 8'hA2; // 8'h48;
parameter TIMER_JUMP = 8'hA4; // 8'h50;
parameter SERIAL_JUMP = 8'hA6; // 8'h58;
parameter INPUT_JUMP = 8'hA8; // 8'h60;
reg[1:0] state;
reg[2:0] interrupt;
reg[7:0] reg_out;
always @(posedge clock)
begin
if (reset)
begin
IE <= 8'h0;
state <= POLL_STATE;
end
else
begin
// Read / Write for registers
if (cs)
begin
if (!wr_n)
begin
case (A)
16'hFFFF: IE <= Di;
endcase
end
else if (!rd_n)
begin
case (A)
16'hFF0F: reg_out <= IF;
16'hFFFF: reg_out <= IE;
endcase
end
end
case (state)
POLL_STATE:
begin
if (IF[VBLANK_INT] && IE[VBLANK_INT])
begin
interrupt <= VBLANK_INT;
state <= WAIT_STATE;
end
else if (IF[LCDC_INT] && IE[LCDC_INT])
begin
interrupt <= LCDC_INT;
state <= WAIT_STATE;
end
else if (IF[TIMER_INT] && IE[TIMER_INT])
begin
interrupt <= TIMER_INT;
state <= WAIT_STATE;
end
else if (IF[SERIAL_INT] && IE[SERIAL_INT])
begin
interrupt <= SERIAL_INT;
state <= WAIT_STATE;
end
else if (IF[INPUT_INT] && IE[INPUT_INT])
begin
interrupt <= INPUT_INT;
state <= WAIT_STATE;
end
end
WAIT_STATE:
begin
if (!m1_n && !iorq_n)
state <= ACK_STATE;
end
ACK_STATE:
begin
int_ack[interrupt] <= 1'b1;
state <= CLEAR_STATE;
end
CLEAR_STATE:
begin
int_ack[interrupt] <= 1'b0;
if (m1_n || iorq_n)
state <= POLL_STATE;
end
endcase
end
end
assign IF = int_req; // this makes the value read only
assign Do = (cs) ? reg_out : 8'hFF;
assign int_n = (state == WAIT_STATE) ? 1'b0 : 1'b1; // active low
assign jump_addr =
(interrupt == VBLANK_INT) ? VBLANK_JUMP :
(interrupt == LCDC_INT) ? LCDC_JUMP :
(interrupt == TIMER_INT) ? TIMER_JUMP :
(interrupt == SERIAL_INT) ? SERIAL_JUMP :
(interrupt == INPUT_INT) ? INPUT_JUMP : 8'hZZ;
endmodule