forked from antonblanchard/microwatt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gpio.vhdl
166 lines (147 loc) · 6.81 KB
/
gpio.vhdl
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
-- GPIO module for microwatt
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.wishbone_types.all;
entity gpio is
generic (
NGPIO : integer := 32
);
port (
clk : in std_ulogic;
rst : in std_ulogic;
-- Wishbone
wb_in : in wb_io_master_out;
wb_out : out wb_io_slave_out;
-- GPIO lines
gpio_in : in std_ulogic_vector(NGPIO - 1 downto 0);
gpio_out : out std_ulogic_vector(NGPIO - 1 downto 0);
-- 1 = output, 0 = input
gpio_dir : out std_ulogic_vector(NGPIO - 1 downto 0);
-- Interrupt
intr : out std_ulogic
);
end entity gpio;
architecture behaviour of gpio is
constant GPIO_REG_BITS : positive := 5;
-- Register addresses, matching addr downto 2, so 4 bytes per reg
constant GPIO_REG_DATA_OUT : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00000";
constant GPIO_REG_DATA_IN : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00001";
constant GPIO_REG_DIR : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00010";
constant GPIO_REG_DATA_SET : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00100";
constant GPIO_REG_DATA_CLR : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00101";
constant GPIO_REG_INT_EN : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01000";
constant GPIO_REG_INT_STAT : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01001";
-- write 1 to clear
constant GPIO_REG_INT_CLR : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01100";
-- edge 0, level 1
constant GPIO_REG_INT_TYPE : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01101";
-- for edge: trigger on either edge = 1
constant GPIO_REG_INT_BOTH_EDGE : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01110";
-- for edge: rising 0, falling 1
-- for level: high 0, low 1
constant GPIO_REG_INT_LEVEL : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01111";
-- Current output value and direction
signal reg_data : std_ulogic_vector(NGPIO - 1 downto 0);
signal reg_dirn : std_ulogic_vector(NGPIO - 1 downto 0);
signal reg_in0 : std_ulogic_vector(NGPIO - 1 downto 0);
signal reg_in1 : std_ulogic_vector(NGPIO - 1 downto 0);
signal reg_in2 : std_ulogic_vector(NGPIO - 1 downto 0);
signal reg_intr_en : std_ulogic_vector(NGPIO - 1 downto 0);
signal reg_intr_hit : std_ulogic_vector(NGPIO - 1 downto 0);
signal reg_intr_type : std_ulogic_vector(NGPIO - 1 downto 0);
signal reg_intr_level : std_ulogic_vector(NGPIO - 1 downto 0);
signal reg_intr_both : std_ulogic_vector(NGPIO - 1 downto 0);
signal wb_rsp : wb_io_slave_out;
signal reg_out : std_ulogic_vector(NGPIO - 1 downto 0);
constant ZEROS : std_ulogic_vector(NGPIO-1 downto 0) := (others => '0');
begin
intr <= '0' when reg_intr_hit = ZEROS else '1';
gpio_out <= reg_data;
gpio_dir <= reg_dirn;
-- Wishbone response
wb_rsp.ack <= wb_in.cyc and wb_in.stb;
with wb_in.adr(GPIO_REG_BITS - 1 downto 0) select reg_out <=
reg_data when GPIO_REG_DATA_OUT,
reg_in1 when GPIO_REG_DATA_IN,
reg_dirn when GPIO_REG_DIR,
reg_intr_en when GPIO_REG_INT_EN,
reg_intr_hit when GPIO_REG_INT_STAT,
reg_intr_type when GPIO_REG_INT_TYPE,
reg_intr_both when GPIO_REG_INT_BOTH_EDGE,
reg_intr_level when GPIO_REG_INT_LEVEL,
(others => '0') when others;
wb_rsp.dat(wb_rsp.dat'left downto NGPIO) <= (others => '0');
wb_rsp.dat(NGPIO - 1 downto 0) <= reg_out;
wb_rsp.stall <= '0';
regs_rw: process(clk)
variable trig : std_logic_vector(0 to 2);
variable change : std_logic;
variable intr_hit : boolean;
begin
if rising_edge(clk) then
wb_out <= wb_rsp;
for i in NGPIO - 1 downto 0 loop
-- interrupt triggers. reg_in1 is current value
if reg_intr_type(i) = '0' then
-- edge
change := '0' when (reg_in1(i) = reg_in2(i)) else '1';
trig := change & reg_intr_both(i) & reg_intr_level(i);
case trig is
-- both
when "110" | "111" => intr_hit := true;
-- rising
when "100" => intr_hit := reg_in1(i) = '1';
-- falling
when "101" => intr_hit := reg_in1(i) = '0';
when others => intr_hit := false;
end case;
else
-- level
intr_hit := reg_in1(i) = not reg_intr_level(i);
end if;
reg_intr_hit(i) <= '1' when intr_hit and reg_intr_en(i) = '1';
end loop;
-- previous value for interrupt edge detection
reg_in2 <= reg_in1;
-- 2 flip flops to cross from async input to sys clock domain
reg_in1 <= reg_in0;
reg_in0 <= gpio_in;
if rst = '1' then
reg_data <= (others => '0');
reg_dirn <= (others => '0');
reg_intr_en <= (others => '0');
reg_intr_hit <= (others => '0');
reg_intr_type <= (others => '0');
reg_intr_both <= (others => '0');
reg_intr_level <= (others => '0');
wb_out.ack <= '0';
else
if wb_in.cyc = '1' and wb_in.stb = '1' and wb_in.we = '1' then
case wb_in.adr(GPIO_REG_BITS - 1 downto 0) is
when GPIO_REG_DATA_OUT =>
reg_data <= wb_in.dat(NGPIO - 1 downto 0);
when GPIO_REG_DIR =>
reg_dirn <= wb_in.dat(NGPIO - 1 downto 0);
when GPIO_REG_DATA_SET =>
reg_data <= reg_data or wb_in.dat(NGPIO - 1 downto 0);
when GPIO_REG_DATA_CLR =>
reg_data <= reg_data and not wb_in.dat(NGPIO - 1 downto 0);
when GPIO_REG_INT_EN =>
reg_intr_en <= wb_in.dat(NGPIO - 1 downto 0);
when GPIO_REG_INT_CLR =>
reg_intr_hit <= reg_intr_hit and not wb_in.dat(NGPIO - 1 downto 0);
when GPIO_REG_INT_TYPE =>
reg_intr_type <= wb_in.dat(NGPIO - 1 downto 0);
when GPIO_REG_INT_BOTH_EDGE =>
reg_intr_both <= wb_in.dat(NGPIO - 1 downto 0);
when GPIO_REG_INT_LEVEL =>
reg_intr_level <= wb_in.dat(NGPIO - 1 downto 0);
when others =>
end case;
end if;
end if;
end if;
end process;
end architecture behaviour;