aboutsummaryrefslogtreecommitdiff
path: root/src/note-synth.vhd
blob: 72a1cd1c287ff3fc92f918ebdac227274948506e (plain)
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
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.math_real.all;

entity note_synth is port(
	CLK: in std_logic; -- system clock
	RESET: in std_logic; -- async reset
	NOTE_IDX: in std_logic_vector(3 downto 0); -- note index
	NOTE_WRONG: in std_logic; -- note wrong
	NOTE_PLAY: in std_logic; -- output audio
	PWM_OUT: out std_logic); -- audio signal level
end note_synth;

architecture Behavioral of note_synth is
	function clk_for_freq(n : real) return natural is
		constant SYSFREQ : real := 100000000.0;
	begin
		return natural(integer(round(SYSFREQ / n)));
	end function;

	constant CLK_FOR_ERROR : natural := clk_for_freq(150.0);
	constant CLK_FOR_E4 : natural := clk_for_freq(329.6);
	constant CLK_FOR_F4 : natural := clk_for_freq(349.2);
	constant CLK_FOR_G4 : natural := clk_for_freq(391.9);
	constant CLK_FOR_A4 : natural := clk_for_freq(440.0);
	constant CLK_FOR_B4 : natural := clk_for_freq(493.8);
	constant CLK_FOR_C5 : natural := clk_for_freq(523.2);
	constant CLK_FOR_D5 : natural := clk_for_freq(587.3);
	constant CLK_FOR_E5 : natural := clk_for_freq(659.2);
	constant CLK_FOR_F5 : natural := clk_for_freq(698.4);
	signal MAX_CLK : natural := 1;
	signal PWM_OUT_TEMP : std_logic := '0';
begin
	MAX_CLK <= CLK_FOR_ERROR when NOTE_WRONG = '1' else
	           CLK_FOR_E4 when NOTE_IDX = x"0" else
						 CLK_FOR_F4 when NOTE_IDX = x"1" else
	           CLK_FOR_G4 when NOTE_IDX = x"2" else
	           CLK_FOR_A4 when NOTE_IDX = x"3" else
	           CLK_FOR_B4 when NOTE_IDX = x"4" else
	           CLK_FOR_C5 when NOTE_IDX = x"5" else
	           CLK_FOR_D5 when NOTE_IDX = x"6" else
	           CLK_FOR_E5 when NOTE_IDX = x"7" else
	           CLK_FOR_F5;

	PWM_OUT <= PWM_OUT_TEMP and NOTE_PLAY;
	process(CLK, RESET)
		variable CLK_COUNTER : integer := 0;
	begin
		if RESET = '1' then
			CLK_COUNTER := 0;
		elsif rising_edge(CLK) then
			CLK_COUNTER := CLK_COUNTER + 1;
			if CLK_COUNTER >= MAX_CLK then
				CLK_COUNTER := 0;

				if PWM_OUT_TEMP = '0' then PWM_OUT_TEMP <= '1'; else PWM_OUT_TEMP <= '0'; end if; -- toggle output
			end if;
		end if;
	end process;
end Behavioral;