From 3a0ffabf359300dddc0c9486947c356943c8a9a4 Mon Sep 17 00:00:00 2001 From: Gyorgy Szombathelyi Date: Sun, 5 Jan 2020 20:35:00 +0100 Subject: [PATCH] Add Z80CTC to common --- common/IO/Z80CTC/ctc_controler.vhd | 120 +++++++++++++++++++ common/IO/Z80CTC/ctc_counter.vhd | 151 +++++++++++++++++++++++ common/IO/Z80CTC/z80ctc.qip | 4 + common/IO/Z80CTC/z80ctc_top.vhd | 185 +++++++++++++++++++++++++++++ 4 files changed, 460 insertions(+) create mode 100644 common/IO/Z80CTC/ctc_controler.vhd create mode 100644 common/IO/Z80CTC/ctc_counter.vhd create mode 100644 common/IO/Z80CTC/z80ctc.qip create mode 100644 common/IO/Z80CTC/z80ctc_top.vhd diff --git a/common/IO/Z80CTC/ctc_controler.vhd b/common/IO/Z80CTC/ctc_controler.vhd new file mode 100644 index 00000000..d7aa41ba --- /dev/null +++ b/common/IO/Z80CTC/ctc_controler.vhd @@ -0,0 +1,120 @@ +--------------------------------------------------------------------------------- +-- Z80-CTC controler by Dar (darfpga@aol.fr) (19/10/2019) +-- http://darfpga.blogspot.fr +--------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; + +entity ctc_controler is +port( + clock : in std_logic; + clock_ena : in std_logic; + reset : in std_logic; + + d_in : in std_logic_vector( 7 downto 0); + load_data : in std_logic; + int_ack : in std_logic; + int_end : in std_logic; -- RETI detected + + int_pulse_0 : in std_logic; + int_pulse_1 : in std_logic; + int_pulse_2 : in std_logic; + int_pulse_3 : in std_logic; + + d_out : out std_logic_vector( 7 downto 0); + int_n : out std_logic +); +end ctc_controler; + +architecture struct of ctc_controler is + + signal int_vector : std_logic_vector(4 downto 0); + + signal wait_for_time_constant : std_logic; + signal load_data_r : std_logic; -- make sure load_data toggles to get one new data + + signal int_reg_0 : std_logic; + signal int_reg_1 : std_logic; + signal int_reg_2 : std_logic; + signal int_reg_3 : std_logic; + + signal int_in_service : std_logic_vector(3 downto 0); + + signal int_ack_r : std_logic; + signal int_end_r : std_logic; + +begin + +int_n <= '0' when (int_reg_0 or int_reg_1 or int_reg_2 or int_reg_3) = '1' else '1'; + +d_out <= int_vector & "000" when int_reg_0 = '1' else + int_vector & "010" when int_reg_1 = '1' else + int_vector & "100" when int_reg_2 = '1' else + int_vector & "110" when int_reg_3 = '1' else (others => '0'); + +process (reset, clock) +begin + + if reset = '1' then -- hardware and software reset + wait_for_time_constant <= '0'; + int_reg_0 <= '0'; + int_reg_1 <= '0'; + int_reg_2 <= '0'; + int_reg_3 <= '0'; + int_in_service <= (others => '0'); + load_data_r <= '0'; + int_vector <= (others => '0'); + else + if rising_edge(clock) then + if clock_ena = '1' then + + load_data_r <= load_data; + int_ack_r <= int_ack; + int_end_r <= int_end; + + if load_data = '1' and load_data_r = '0' then + + if wait_for_time_constant = '1' then + wait_for_time_constant <= '0'; + else + if d_in(0) = '1' then -- check if its a control world + wait_for_time_constant <= d_in(2); +-- if d_in(1) = '1' then -- software reset +-- wait_for_time_constant <= '0'; +-- end if; + else -- its an interrupt vector + int_vector <= d_in(7 downto 3); + end if; + end if; + + end if; + + if int_pulse_0 = '1' and int_in_service(0) = '0' then int_reg_0 <= '1'; end if; + if int_pulse_1 = '1' and int_in_service(1 downto 0) = "00" then int_reg_1 <= '1'; end if; + if int_pulse_2 = '1' and int_in_service(2 downto 0) = "000" then int_reg_2 <= '1'; end if; + if int_pulse_3 = '1' and int_in_service(3 downto 0) = "0000" then int_reg_3 <= '1'; end if; + + if int_ack_r = '0' and int_ack = '1' then + if int_reg_0 = '1' then int_reg_0 <= '0'; int_in_service(0) <= '1'; + elsif int_reg_1 = '1' then int_reg_1 <= '0'; int_in_service(1) <= '1'; + elsif int_reg_2 = '1' then int_reg_2 <= '0'; int_in_service(2) <= '1'; + elsif int_reg_3 = '1' then int_reg_3 <= '0'; int_in_service(3) <= '1'; + end if; + end if; + + if int_end_r = '0' and int_end = '1' then + if int_in_service(0) = '1' then int_in_service(0) <= '0'; + elsif int_in_service(1) = '1' then int_in_service(1) <= '0'; + elsif int_in_service(2) = '1' then int_in_service(2) <= '0'; + elsif int_in_service(3) = '1' then int_in_service(3) <= '0'; + end if; + end if; + + end if; + end if; + end if; +end process; + +end struct; diff --git a/common/IO/Z80CTC/ctc_counter.vhd b/common/IO/Z80CTC/ctc_counter.vhd new file mode 100644 index 00000000..58409ca2 --- /dev/null +++ b/common/IO/Z80CTC/ctc_counter.vhd @@ -0,0 +1,151 @@ +--------------------------------------------------------------------------------- +-- Z80-CTC counter by Dar (darfpga@aol.fr) (19/10/2019) +-- http://darfpga.blogspot.fr +--------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; + +entity ctc_counter is +port( + clock : in std_logic; + clock_ena : in std_logic; + reset : in std_logic; + + d_in : in std_logic_vector( 7 downto 0); + load_data : in std_logic; + + clk_trg : in std_logic; + + d_out : out std_logic_vector(7 downto 0); + zc_to : out std_logic; + int_pulse : out std_logic + + ); +end ctc_counter; + +architecture struct of ctc_counter is + + signal control_word : std_logic_vector(7 downto 0); + signal wait_for_time_constant : std_logic; + signal time_constant_loaded : std_logic; + signal restart_on_next_clock : std_logic; + signal restart_on_next_trigger : std_logic; + + signal prescale_max : std_logic_vector(7 downto 0); + signal prescale_in : std_logic_vector(7 downto 0) := (others => '0'); + signal count_max : std_logic_vector(7 downto 0); + signal count_in : std_logic_vector(7 downto 0) := (others => '0'); + signal zc_to_in : std_logic; + signal clk_trg_in : std_logic; + signal clk_trg_r : std_logic; + signal trigger : std_logic; + signal count_ena : std_logic; + signal load_data_r : std_logic; -- make sure load_data toggles to get one new data + +begin + +prescale_max <= + (others => '0') when control_word(6) = '1' else -- counter mode (prescale max = 0) + X"0F" when control_word(6 downto 5) = "00" else -- timer mode prescale 16 + X"FF"; -- timer mode prescale 256 + +clk_trg_in <= clk_trg xor control_word(4); +trigger <= '1' when clk_trg_in = '0' and clk_trg_r = '1' else '0'; + +d_out <= count_in(7 downto 0); + +zc_to <= zc_to_in; +int_pulse <= zc_to_in when control_word(7) = '1' else '0'; + +process (reset, clock) +begin + + if reset = '1' then -- hardware reset + count_ena <= '0'; + wait_for_time_constant <= '0'; + time_constant_loaded <= '0'; + restart_on_next_clock <= '0'; + restart_on_next_trigger <= '0'; + count_in <= (others=> '0'); + zc_to_in <= '0'; + clk_trg_r <= '0'; + else + if rising_edge(clock) then + if clock_ena = '1' then + + clk_trg_r <= clk_trg_in; + load_data_r <= load_data; + + if (restart_on_next_trigger = '1' and trigger = '1') or (restart_on_next_clock = '1') then + restart_on_next_clock <= '0'; + restart_on_next_trigger <= '0'; + count_ena <= '1'; + count_in <= count_max; + prescale_in <= prescale_max; + end if; + + if load_data = '1' and load_data_r = '0' then + + if wait_for_time_constant = '1' then + wait_for_time_constant <= '0'; + time_constant_loaded <= '1'; + count_max <= d_in; + + if control_word(6) = '0' and count_ena = '0' then -- in timer mode, if count was stooped + if control_word(3) = '0' then -- auto start when time_constant loaded + restart_on_next_clock <= '1'; + else -- wait for trigger to start + restart_on_next_trigger <= '1'; + end if; + end if; + if control_word(6) = '1' then -- in trigger mode reload the counter immediately, + -- otherwise the first period will undefined + prescale_in <= (others => '0'); + count_in <= d_in; + end if; + else -- not waiting for time constant + + if d_in(0) = '1' then -- check if its a control world + control_word <= d_in; + wait_for_time_constant <= d_in(2); + restart_on_next_clock <= '0'; + restart_on_next_trigger <= '0'; + + if d_in(1) = '1' then -- software reset + count_ena <= '0'; + time_constant_loaded <= '0'; + zc_to_in <= '0'; +-- zc_to_in_r <= '0'; + clk_trg_r <= clk_trg; + end if; + end if; + + end if; + + end if; -- end load data + + -- counter + zc_to_in <= '0'; + if ((control_word(6) = '1' and trigger = '1' ) or + (control_word(6) = '0' and count_ena = '1') ) and time_constant_loaded = '1' then + if prescale_in = 0 then + prescale_in <= prescale_max; + if count_in = 1 then + zc_to_in <= '1'; + count_in <= count_max; + else + count_in <= count_in - '1'; + end if; + else + prescale_in <= prescale_in - '1'; + end if; + end if; + + end if; + end if; + end if; +end process; + +end struct; diff --git a/common/IO/Z80CTC/z80ctc.qip b/common/IO/Z80CTC/z80ctc.qip new file mode 100644 index 00000000..6a3ed4fd --- /dev/null +++ b/common/IO/Z80CTC/z80ctc.qip @@ -0,0 +1,4 @@ +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) z80ctc_top.vhd ] +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) ctc_controler.vhd ] +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) ctc_counter.vhd ] +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) z80ctc_top.vhd ] diff --git a/common/IO/Z80CTC/z80ctc_top.vhd b/common/IO/Z80CTC/z80ctc_top.vhd new file mode 100644 index 00000000..b9efaca9 --- /dev/null +++ b/common/IO/Z80CTC/z80ctc_top.vhd @@ -0,0 +1,185 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; + +-- Z80-CTC (MK3882) top-level +entity z80ctc_top is +port( + clock : in std_logic; + clock_ena : in std_logic; + reset : in std_logic; + + din : in std_logic_vector(7 downto 0); + dout : out std_logic_vector(7 downto 0); + cpu_din : in std_logic_vector(7 downto 0); -- mirror the input to the cpu, for RETI detection + + ce_n : in std_logic; + cs : in std_logic_vector(1 downto 0); + m1_n : in std_logic; + iorq_n : in std_logic; + rd_n : in std_logic; + int_n : out std_logic; + + trg0 : in std_logic; + to0 : out std_logic; + + trg1 : in std_logic; + to1 : out std_logic; + + trg2 : in std_logic; + to2 : out std_logic; + + trg3 : in std_logic + ); +end z80ctc_top; + +architecture struct of z80ctc_top is + + signal cpu_int_ack_n : std_logic; + + signal ctc_controler_we : std_logic; + signal ctc_controler_do : std_logic_vector(7 downto 0); + signal ctc_int_ack : std_logic; + signal ctc_int_ack_phase : std_logic_vector(1 downto 0); + + signal ctc_counter_0_we : std_logic; + signal ctc_counter_0_do : std_logic_vector(7 downto 0); + signal ctc_counter_0_int : std_logic; + + signal ctc_counter_1_we : std_logic; + signal ctc_counter_1_do : std_logic_vector(7 downto 0); + signal ctc_counter_1_int : std_logic; + + signal ctc_counter_2_we : std_logic; + signal ctc_counter_2_do : std_logic_vector(7 downto 0); + signal ctc_counter_2_int : std_logic; + + signal ctc_counter_3_we : std_logic; + signal ctc_counter_3_do : std_logic_vector(7 downto 0); + signal ctc_counter_3_int : std_logic; + +begin + +process (clock, reset) +begin + if reset = '1' then + ctc_int_ack_phase <= "00"; + elsif rising_edge(clock) then + -- decode ED4D (reti) + if clock_ena = '1' and rd_n = '0' and m1_n = '0' then + case ctc_int_ack_phase is + when "00" => if cpu_din = x"ED" then ctc_int_ack_phase <= "01"; end if; + when "01" => if cpu_din = x"4D" then ctc_int_ack_phase <= "11"; elsif cpu_din /= x"ED" then ctc_int_ack_phase <= "00"; end if; + when "11" => if cpu_din = x"ED" then ctc_int_ack_phase <= "01"; elsif cpu_din /= x"4D" then ctc_int_ack_phase <= "00"; end if; + when others => ctc_int_ack_phase <= "00"; + end case; + end if; + end if; +end process; + +ctc_int_ack <= '1' when ctc_int_ack_phase = "11" else '0'; +cpu_int_ack_n <= iorq_n or m1_n; + +ctc_controler_we <= '1' when ce_n = '0' and iorq_n = '0' and m1_n = '1' and rd_n = '1' and cs = "00" else '0'; +ctc_counter_0_we <= '1' when ce_n = '0' and iorq_n = '0' and m1_n = '1' and rd_n = '1' and cs = "00" else '0'; +ctc_counter_1_we <= '1' when ce_n = '0' and iorq_n = '0' and m1_n = '1' and rd_n = '1' and cs = "01" else '0'; +ctc_counter_2_we <= '1' when ce_n = '0' and iorq_n = '0' and m1_n = '1' and rd_n = '1' and cs = "10" else '0'; +ctc_counter_3_we <= '1' when ce_n = '0' and iorq_n = '0' and m1_n = '1' and rd_n = '1' and cs = "11" else '0'; + +dout <= ctc_controler_do when cpu_int_ack_n = '0' else + ctc_counter_0_do when iorq_n = '0' and m1_n = '1' and rd_n = '0' and cs = "00" else + ctc_counter_1_do when iorq_n = '0' and m1_n = '1' and rd_n = '0' and cs = "01" else + ctc_counter_2_do when iorq_n = '0' and m1_n = '1' and rd_n = '0' and cs = "10" else + ctc_counter_3_do when iorq_n = '0' and m1_n = '1' and rd_n = '0' and cs = "11" else + x"FF"; + +-- CTC interrupt controler Z80-CTC (MK3882) +ctc_controler : entity work.ctc_controler +port map( + clock => clock, + clock_ena => clock_ena, + reset => reset, + + d_in => din, + load_data => ctc_controler_we, + int_ack => cpu_int_ack_n, + int_end => ctc_int_ack, + + int_pulse_0 => ctc_counter_0_int, + int_pulse_1 => ctc_counter_1_int, + int_pulse_2 => ctc_counter_2_int, + int_pulse_3 => ctc_counter_3_int, + + d_out => ctc_controler_do, + int_n => int_n +); + +ctc_counter_0 : entity work.ctc_counter +port map( + clock => clock, + clock_ena => clock_ena, + reset => reset, + + d_in => din, + load_data => ctc_counter_0_we, + + clk_trg => trg0, + + d_out => ctc_counter_0_do, + zc_to => to0, + int_pulse => ctc_counter_0_int + +); + +ctc_counter_1 : entity work.ctc_counter +port map( + clock => clock, + clock_ena => clock_ena, + reset => reset, + + d_in => din, + load_data => ctc_counter_1_we, + + clk_trg => trg1, + + d_out => ctc_counter_1_do, + zc_to => to1, + int_pulse => ctc_counter_1_int + +); + +ctc_counter_2 : entity work.ctc_counter +port map( + clock => clock, + clock_ena => clock_ena, + reset => reset, + + d_in => din, + load_data => ctc_counter_2_we, + + clk_trg => trg2, + + d_out => ctc_counter_2_do, + zc_to => to2, + int_pulse => ctc_counter_2_int + +); + +ctc_counter_3 : entity work.ctc_counter +port map( + clock => clock, + clock_ena => clock_ena, + reset => reset, + + d_in => din, + load_data => ctc_counter_3_we, + + clk_trg => trg3, + + d_out => ctc_counter_3_do, + zc_to => open, + int_pulse => ctc_counter_3_int + +); +end struct;