-
Notifications
You must be signed in to change notification settings - Fork 7
/
effect_gen.vhd
229 lines (195 loc) · 8 KB
/
effect_gen.vhd
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
220
221
222
223
224
225
226
227
228
229
-- Effect program (8 effect slots):
-- n = # of steps (128 max)
-- Step 0...n-1 : freq (hz) and duration (msec) (9 bits each)
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
library work;
entity effect_gen is
generic(
-- Use 1e3 for simulation so that clks_per_msec becomes 1
g_clk_freq_in : integer := 25175000 -- 25.175 MHz
);
port (
-- Set the g_clk_freq_in generic appropriately
i_clock : in std_logic;
-- Active low reset
i_reset_n : in std_logic;
-- 0: Launch
-- 1: Player fire
-- 2: Enemy fire
-- 3: Enemy destroy
-- 4: Player destroy
i_effectSel : in std_logic_vector(2 downto 0);
-- Rising edge will trigger selected effect to play once, overriding any currently playing effect
i_effectTrig : in std_logic;
-- Square wave (50% DC) output for buzzer
o_buzzPin : out std_logic;
o_playing : out std_logic;
o_currEffect : out std_logic_vector(2 downto 0)
);
end entity effect_gen;
architecture rtl of effect_gen is
-- Constants
constant rom_depth : integer := 1024;
CONSTANT effect_size : integer := 128; -- Size of each effect "slot" in words
CONSTANT word_size : integer := 13; -- Word size of the ROM
CONSTANT clks_per_msec : integer := (g_clk_freq_in)/(1e3);
-- Components
component clock_div IS
GENERIC (n : NATURAL := 8);
PORT ( clock_in, reset : IN STD_LOGIC;
divisor : IN STD_LOGIC_VECTOR(n DOWNTO 0); -- divisor = 2*(max_cnt+1), size = n+1 bits
clock_out : OUT STD_LOGIC );
END component;
-- Types
TYPE state_type IS (S_INIT, S_IDLE, S_START, S_LOAD_N_PRE, S_LOAD_N, S_LOAD_FREQ_PRE,
S_LOAD_FREQ, S_LOAD_DUR_PRE, S_LOAD_DUR, S_WAIT_DUR, S_NEXT_STEP, S_COMP);
-- Signals
signal r_state : state_type;
signal r_romAddr : std_logic_vector(9 downto 0);
signal r_buzzDivisor : std_logic_vector(27 downto 0);
signal r_buzzDisable : std_logic;
signal r_effectTrig_d : std_logic := '0'; -- Registered trigger input
signal r_effectTrig_re : std_logic; -- Rising edge of trigger input
signal r_currEffect : std_logic_vector(2 downto 0);
signal w_romData : std_logic_vector(word_size-1 downto 0);
begin
-- Get rising edge of the trigger input
r_effectTrig_d <= i_effectTrig when rising_edge(i_clock); -- DFF
r_effectTrig_re <= not r_effectTrig_d and i_effectTrig; -- One-cycle strobe
process(i_clock, i_reset_n)
-- Variables
variable v_romAddr : integer range 0 to 1023;
variable v_numSteps : integer range 0 to 2**word_size - 1 := 0;
variable v_freq : integer range 0 to 2**word_size - 1 := 0;
variable v_duration_msec : integer range 0 to 2**word_size - 1 := 0;
variable v_clkCounter : integer range 0 to clks_per_msec := 0;
begin
if (i_reset_n = '0') then
r_state <= S_INIT;
r_buzzDisable <= '1';
elsif rising_edge(i_clock) then
case r_state is
when S_INIT =>
r_state <= S_IDLE;
r_buzzDisable <= '1';
when S_IDLE =>
-- Wait for trigger
if (r_effectTrig_re = '1') then
r_state <= S_START;
else
r_state <= S_IDLE;
end if;
when S_START =>
-- Set starting ROM addr
case i_effectSel is
when "000" =>
v_romAddr := 0*effect_size;
when "001" =>
v_romAddr := 1*effect_size;
when "010" =>
v_romAddr := 2*effect_size;
when "011" =>
v_romAddr := 3*effect_size;
when "100" =>
v_romAddr := 4*effect_size;
when "101" =>
v_romAddr := 5*effect_size;
when "110" =>
v_romAddr := 6*effect_size;
when "111" =>
v_romAddr := 7*effect_size;
when others =>
v_romAddr := 0*effect_size;
end case;
r_currEffect <= i_effectSel;
r_state <= S_LOAD_N_PRE;
-- One extra clock cycle is needed to latch in the ROM address
when S_LOAD_N_PRE =>
r_state <= S_LOAD_N;
when S_LOAD_N =>
v_numSteps := to_integer(unsigned(w_romData));
v_romAddr := v_romAddr + 1;
r_state <= S_LOAD_FREQ_PRE;
when S_LOAD_FREQ_PRE =>
r_state <= S_LOAD_FREQ;
when S_LOAD_FREQ =>
v_freq := to_integer(unsigned(w_romData));
-- Check for zero, this means no freq (a simple delay in the program)
if (v_freq = 0) then
r_buzzDivisor <= (others => '0');
r_buzzDisable <= '1';
else
r_buzzDivisor <= STD_LOGIC_VECTOR(TO_UNSIGNED(g_clk_freq_in / v_freq, r_buzzDivisor'LENGTH));
r_buzzDisable <= '0';
end if;
v_romAddr := v_romAddr + 1;
r_state <= S_LOAD_DUR_PRE;
when S_LOAD_DUR_PRE =>
r_state <= S_LOAD_DUR;
when S_LOAD_DUR =>
v_duration_msec := to_integer(unsigned(w_romData));
v_clkCounter := 0;
v_romAddr := v_romAddr + 1;
r_state <= S_WAIT_DUR;
when S_WAIT_DUR =>
-- Still waiting
if (v_duration_msec > 0) then
v_clkCounter := v_clkCounter + 1;
-- Count msec
if (v_clkCounter = clks_per_msec) then
v_clkCounter := 0;
v_duration_msec := v_duration_msec - 1;
end if;
r_state <= S_WAIT_DUR;
-- Duration complete
else
r_state <= S_NEXT_STEP;
end if;
when S_NEXT_STEP =>
v_numSteps := v_numSteps - 1; -- Decr step counter
if (v_numSteps = 0) then
r_state <= S_COMP;
else
r_state <= S_LOAD_FREQ_PRE;
end if;
-- Sequence complete
when S_COMP =>
r_state <= S_INIT;
when others =>
r_state <= S_INIT;
end case;
-- Override current sequence when new trigger is receieved
if (r_effectTrig_re = '1') then
r_state <= S_START;
end if;
end if;
-- Var to signal
r_romAddr <= STD_LOGIC_VECTOR(TO_UNSIGNED(v_romAddr, r_romAddr'LENGTH));
end process;
-- Outputs
o_currEffect <= r_currEffect;
o_playing <= '1' when (r_state /= S_IDLE) else '0';
-- Instantiation and port mapping
eff_mem: entity work.sync_ram_init generic map(
numElements => rom_depth,
dataWidth => word_size,
initFile => "../res/effect_mem.mif"
)
port map(
clkA => i_clock,
writeEnableA => '0',
addrA => r_romAddr,
dataOutA => w_romData,
dataInA => (others => '0')
);
U2 : clock_div generic map (
n => 27
) port map (
clock_in => i_clock,
reset => r_buzzDisable,
divisor => r_buzzDivisor,
clock_out => o_buzzPin
);
end rtl;