diff --git a/day2/vhdl/.gitignore b/day2/vhdl/.gitignore new file mode 100644 index 0000000..6d765d9 --- /dev/null +++ b/day2/vhdl/.gitignore @@ -0,0 +1,4 @@ +workdir/ +*.o +*.ghw +sim diff --git a/day2/vhdl/parser.vhd b/day2/vhdl/parser.vhd new file mode 100644 index 0000000..36392f9 --- /dev/null +++ b/day2/vhdl/parser.vhd @@ -0,0 +1,86 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity parser is + port ( + clk : in std_logic; + reset : in std_logic; + is_record : in std_logic; + is_data : out std_logic; + + char : in character; + + num1, num2 : out natural range 0 to 99; + letter : out character + ); +end entity; + +architecture behaviour of parser is + type state_t is (S_NUM1, S_NUM2, S_LETTER, S_COLON, S_END_SPACE, S_DATA); + signal state : state_t := S_NUM1; + + subtype digit is natural range 0 to 9; + type multiples_lookup_t is array(digit) of natural range 0 to 90; + constant TEN_MULTIPLES : multiples_lookup_t := (0, 10, 20, 30, 40, 50, 60, 70, 80, 90); + + -- most significant digit of number + signal prev_digit : digit := 0; + signal current_digit : digit; + signal complete_num : natural range 0 to 99; + + function char_to_digit(input : in character) return digit is + begin + if input >= '0' and input <= '9' then + return character'pos(input) - character'pos('0'); + else + return 0; + end if; + end function; +begin + current_digit <= char_to_digit(char); + complete_num <= TEN_MULTIPLES(prev_digit) + current_digit; + + process(clk) + begin + if rising_edge(clk) then + if reset then + prev_digit <= 0; + state <= S_NUM1; + else + prev_digit <= 0; + + case state is + when S_NUM1 => + if is_record then + if char = '-' then + state <= S_NUM2; + else + num1 <= complete_num; + prev_digit <= current_digit; + end if; + end if; + when S_NUM2 => + if char = ' ' then + state <= S_LETTER; + else + num2 <= complete_num; + prev_digit <= current_digit; + end if; + when S_LETTER => + letter <= char; + state <= S_COLON; + when S_COLON => + state <= S_END_SPACE; + when S_END_SPACE => + state <= S_DATA; + when S_DATA => + if not is_record then + state <= S_NUM1; + end if; + end case; + end if; + end if; + end process; + + is_data <= '1' when state = S_DATA else '0'; +end architecture; diff --git a/day2/vhdl/run.sh b/day2/vhdl/run.sh new file mode 100755 index 0000000..db3d140 --- /dev/null +++ b/day2/vhdl/run.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh + +set -eu + +mkdir -p workdir + +ghdl analyze --std=08 --workdir=workdir parser.vhd verifier.vhd top.vhd sim.vhd +ghdl elab-run --std=08 --workdir=workdir sim -gSTEP=1 -gFILENAME="../input.txt" +ghdl elab-run --std=08 --workdir=workdir sim -gSTEP=2 -gFILENAME="../input.txt" diff --git a/day2/vhdl/sim.gtkw b/day2/vhdl/sim.gtkw new file mode 100644 index 0000000..a19d999 --- /dev/null +++ b/day2/vhdl/sim.gtkw @@ -0,0 +1,47 @@ +[*] +[*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI +[*] Wed Dec 2 10:02:23 2020 +[*] +[dumpfile] "/home/xiretza/dev/aoc2020/day2/sim.ghw" +[dumpfile_mtime] "Wed Dec 2 10:01:08 2020" +[dumpfile_size] 410436 +[savefile] "/home/xiretza/dev/aoc2020/day2/sim.gtkw" +[timestart] 410945000000 +[size] 1600 853 +[pos] -1 -1 +*-27.864407 226600000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] top. +[treeopen] top.sim. +[treeopen] top.sim.top. +[sst_width] 221 +[signals_width] 260 +[sst_expanded] 1 +[sst_vpaned_height] 233 +@28 +top.sim.clk +top.sim.reset +top.sim.top.parser_inst.is_record +@420 +top.sim.top.char +@200 +- +@28 +top.sim.top.parser_inst.is_data +@420 +top.sim.top.num1 +top.sim.top.num2 +top.sim.top.letter +@200 +- +@420 +top.sim.top.verifier_inst.count +@28 +top.sim.top.verifier_inst.verified +@200 +- +@24 +#{top.sim.num_verified[11:0]} top.sim.num_verified[11] top.sim.num_verified[10] top.sim.num_verified[9] top.sim.num_verified[8] top.sim.num_verified[7] top.sim.num_verified[6] top.sim.num_verified[5] top.sim.num_verified[4] top.sim.num_verified[3] top.sim.num_verified[2] top.sim.num_verified[1] top.sim.num_verified[0] +@29 +top.sim.top.record_ended +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/day2/vhdl/sim.vhd b/day2/vhdl/sim.vhd new file mode 100644 index 0000000..3b4299b --- /dev/null +++ b/day2/vhdl/sim.vhd @@ -0,0 +1,88 @@ +library ieee; +use ieee.std_logic_1164.all, + ieee.numeric_std.all; + +use std.textio.all; + +entity sim is + generic ( + FILENAME : string := "input.txt"; + COUNTER_WIDTH : positive := 12; + STEP : natural range 1 to 2 + ); +end entity; + +architecture a of sim is + file file_handle : text open read_mode is FILENAME; + signal char_in : character; + signal clk, reset, is_record : std_logic; + signal num_verified : unsigned(COUNTER_WIDTH-1 downto 0); + + procedure print(s: string) is + variable l : line; + begin + write(l, s); + writeline(output, l); + end procedure; +begin + process + variable current_line : line; + variable current_char : character; + variable good : boolean; + + procedure cycle_clock is + begin + wait for 10 ns; + clk <= '0'; + wait for 10 ns; + clk <= '1'; + wait for 0 ns; + end procedure; + begin + clk <= '0'; + is_record <= '0'; + char_in <= NUL; + + reset <= '1'; + cycle_clock; + reset <= '0'; + cycle_clock; + + lines_loop: loop + exit lines_loop when endfile(file_handle); + readline(file_handle, current_line); + + is_record <= '1'; + + chars_loop: loop + read(current_line, current_char, good); + exit chars_loop when not good; + + char_in <= current_char; + cycle_clock; + end loop; + + is_record <= '0'; + cycle_clock; + end loop; + + cycle_clock; + + print(to_string(to_integer(num_verified))); + + wait; + end process; + + top: entity work.top + generic map ( + COUNTER_WIDTH => COUNTER_WIDTH, + STEP => STEP + ) + port map ( + clk => clk, + reset => reset, + char => char_in, + is_record => is_record, + num_verified => num_verified + ); +end architecture; diff --git a/day2/vhdl/top.vhd b/day2/vhdl/top.vhd new file mode 100644 index 0000000..06edc38 --- /dev/null +++ b/day2/vhdl/top.vhd @@ -0,0 +1,84 @@ +library ieee; +use ieee.std_logic_1164.all, + ieee.numeric_std.all; + +entity top is + generic ( + COUNTER_WIDTH : positive; + STEP : natural range 1 to 2 + ); + port ( + clk : in std_logic; + reset : in std_logic; + char : in character; + is_record : in std_logic; + num_verified : out unsigned(COUNTER_WIDTH-1 downto 0) + ); +end entity; + +architecture behaviour of top is + signal is_data : std_logic; + signal num1, num2 : natural range 0 to 99; + signal letter : character; + + signal prev_is_record : std_logic; + signal record_ended : std_logic; + + signal verified : std_logic; +begin + record_ended <= prev_is_record and not is_record; + + parser_inst: entity work.parser + port map ( + clk => clk, + reset => reset, + is_record => is_record, + is_data => is_data, + char => char, + + num1 => num1, + num2 => num2, + letter => letter + ); + + generate_verifier: if step = 1 generate + verifier_inst: entity work.verifier(step1) + port map ( + clk => clk, + reset => reset or record_ended, + is_data => is_data, + num1 => num1, + num2 => num2, + letter => letter, + char => char, + verified => verified + ); + elsif step = 2 generate + verifier_inst: entity work.verifier(step2) + port map ( + clk => clk, + reset => reset or record_ended, + is_data => is_data, + num1 => num1, + num2 => num2, + letter => letter, + char => char, + verified => verified + ); + else generate + assert false report "Bad value for ""step""" severity failure; + end generate; + + process(clk) + begin + if rising_edge(clk) then + prev_is_record <= is_record; + if reset then + prev_is_record <= '0'; + num_verified <= (others => '0'); + elsif record_ended and verified then + num_verified <= num_verified + 1; + end if; + end if; + end process; +end architecture; diff --git a/day2/vhdl/verifier.vhd b/day2/vhdl/verifier.vhd new file mode 100644 index 0000000..e4351d9 --- /dev/null +++ b/day2/vhdl/verifier.vhd @@ -0,0 +1,58 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity verifier is + port ( + clk : in std_logic; + reset : in std_logic; + is_data : in std_logic; + + num1, num2 : in natural range 0 to 99; + letter : in character; + + char : in character; + + verified : out std_logic + ); +end entity; + +architecture step1 of verifier is + signal count : natural range 0 to 99; +begin + process(clk) + begin + if rising_edge(clk) then + if reset then + count <= 0; + elsif is_data then + if char = letter then + count <= count + 1; + end if; + end if; + end if; + end process; + + verified <= '1' when num1 <= count and count <= num2 else '0'; +end architecture; + +architecture step2 of verifier is + signal count : natural range 1 to 99; + signal parity : std_logic; +begin + process(clk) + begin + if rising_edge(clk) then + if reset then + count <= 1; + parity <= '0'; + elsif is_data then + count <= count + 1; + if (count = num1 or count = num2) and char = letter then + parity <= not parity; + end if; + end if; + end if; + end process; + + verified <= parity; +end architecture;