------------------------------------------------------------------------ -- ES5702 KPC ---------------------------------------------------------- -- Ensoniq SQ80/EPS/VFX KPC ASIC aka ES5702-0001-01 aka S38BC010PS01 --- ------------------------------------------------------------------------ -- V1.0 2023-11-24 by Rainer Buchty, rainer@buchty.net -- case study, completely unverified -- solely based on schematics and SQ80 KPC code analysis -- passed ghdl -a syntax check ------------------------------------------------------------------------ library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; entity kpc is port( -- timer control clk, clr_run, -- master clock, CPU control f0, f1, f2, f3: -- key oscillators in std_logic; -- CPU interface as, e, g1, g2: -- address strobe, e clock, gate 1/2 (=A15/A14#) in std_logic; ad: -- multiplexed A/D bus inout std_logic_vector(7 downto 0) ); end entity; architecture rtl of kpc is -- bus control signal acc: std_logic; signal cpu_a: std_logic_vector(2 downto 0); -- counter control signal ready: std_logic; -- helper for AND(fXs="11") signal f0l, f1l, f2l, f3l: std_logic; -- last signal level of Fx trigger signal f0s, f1s, f2s, f3s: std_logic_vector(1 downto 0); -- counter state signal f0c, f1c, f2c, f3c: unsigned(7 downto 0); -- counter value (may be larger, but only top 8 bits are sent out) begin -------------------------------------------------------------------- -- bus control -------------------------------------------------------------------- -- latch address from muxed bus cpu_a <= ad(2 downto 0) when e='1' and as='1'; -- access condition / valid data acc <= '1' when e='1' and as='0' and g1='0' and g2='0' else '0'; -------------------------------------------------------------------- -- CPU (read) access -------------------------------------------------------------------- -- 0x00-03: F0-F3 -- 0x05: status, only ready/busy flag on bit 1 known -------------------------------------------------------------------- ad <= std_logic_vector(f0c) when acc='1' and cpu_a="000" -- 0x4000: F0 else std_logic_vector(f1c) when acc='1' and cpu_a="001" -- 0x4001: F1 else std_logic_vector(f2c) when acc='1' and cpu_a="010" -- 0x4002: F2 else std_logic_vector(f3c) when acc='1' and cpu_a="011" -- 0x4003: F3 else "000000" & ready & '0' when acc='1' and cpu_a="100" -- 0x4005: status else (others=>'Z'); -------------------------------------------------------------------- -- high-speed counters --------------------------------------------- -------------------------------------------------------------------- -- counters based on the assumption that external view = internal state -- might well be that counters are 1-2 bit larger internally, giving some slight filtering -------------------------------------------------------------------- -- ready signal: 1 when all counters finished ready <= '1' when f0s="11" and f1s="11" and f2s="11" and f3s="11" else '0'; PF0: process(clr_run,f0,f0c,f0l,f0s) begin if clr_run='1' then f0c <= (others=>'0'); -- clear counter f0l <= f0; -- latch F0 state f0s <= "00"; -- init counter state elsif clk'event and clk='1' then case f0s is when "00" => -- wait for first f0 transition as it's async to measuring f0c <= (others=>'0'); f0l <= f0; if f0l/=f0 then f0s <= "01"; end if; when "01" => -- measure first cycle half f0c <= f0c+1; f0l <= f0; if f0l/=f0 then f0s <= "10"; end if; when "10" => -- measure second cycle half f0c <= f0c+1; f0l <= f0; if f0l/=f0 then f0s <= "11"; end if; when others => -- done; null; end case; end if; end process; PF1: process(clr_run,f1,f1c,f1l,f1s) begin if clr_run='1' then f1c <= (others=>'0'); -- clear counter f1l <= f1; -- latch F1 state f1s <= "00"; -- init counter state elsif clk'event and clk='1' then case f1s is when "00" => -- wait for first f1 transition as it's async to measuring f1c <= (others=>'0'); f1l <= f1; if f1l/=f1 then f1s <= "01"; end if; when "01" => -- measure first cycle half f1c <= f1c+1; f1l <= f1; if f1l/=f1 then f1s <= "10"; end if; when "10" => -- measure second cycle half f1c <= f1c+1; f1l <= f1; if f1l/=f1 then f1s <= "11"; end if; when others => -- done, freeze null; end case; end if; end process; PF2: process(clr_run,f2,f2c,f2l,f2s) begin if clr_run='1' then f2c <= (others=>'0'); -- clear counter f2l <= f2; -- latch F2 state f2s <= "00"; -- init counter state elsif clk'event and clk='1' then case f2s is when "00" => -- wait for first f2 transition as it's async to measuring f2c <= (others=>'0'); f2l <= f2; if f2l/=f2 then f2s <= "01"; end if; when "01" => -- measure first cycle half f2c <= f2c+1; f2l <= f2; if f2l/=f2 then f2s <= "10"; end if; when "10" => -- measure second cycle half f2c <= f2c+1; f2l <= f2; if f2l/=f2 then f2s <= "11"; end if; when others => -- done, freeze null; end case; end if; end process; PF3: process(clr_run,f3,f3c,f3l,f3s) begin if clr_run='1' then f3c <= (others=>'0'); -- clear counter f3l <= f3; -- latch F3 state f3s <= "00"; -- init counter state elsif clk'event and clk='1' then case f3s is when "00" => -- wait for first f3 transition as it's async to measuring f3c <= (others=>'0'); f3l <= f3; if f3l/=f3 then f3s <= "01"; end if; when "01" => -- measure first cycle half f3c <= f3c+1; f3l <= f3; if f3l/=f3 then f3s <= "10"; end if; when "10" => -- measure second cycle half f3c <= f3c+1; f3l <= f3; if f3l/=f3 then f3s <= "11"; end if; when others => -- done, freeze null; end case; end if; end process; end architecture;