aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CII_Starter_pin_assignments.csv1
-rw-r--r--bbc_micro_de1.qsf6
-rw-r--r--bbc_micro_de1.vhd257
-rw-r--r--debugger.vhd252
-rw-r--r--keyboard.txt81
-rw-r--r--keyboard.vhd251
-rw-r--r--m6522.vhd26
-rw-r--r--m6522_tb.vhd25
-rw-r--r--ps2_intf.vhd153
9 files changed, 967 insertions, 85 deletions
diff --git a/CII_Starter_pin_assignments.csv b/CII_Starter_pin_assignments.csv
index 07e4fe5..c63a6de 100644
--- a/CII_Starter_pin_assignments.csv
+++ b/CII_Starter_pin_assignments.csv
@@ -408,6 +408,7 @@ From,To,Assignment Name,Value,Enabled
,FL_OE_N,Location,PIN_AA15,Yes
,FL_RST_N,Location,PIN_W14,Yes
,FL_WE_N,Location,PIN_Y14,Yes
+,FL_CE_N,Location,PIN_AB15,Yes
,SRAM_ADDR[0],Location,PIN_AA3,Yes
,SRAM_ADDR[1],Location,PIN_AB3,Yes
,SRAM_ADDR[2],Location,PIN_AA4,Yes
diff --git a/bbc_micro_de1.qsf b/bbc_micro_de1.qsf
index 4003236..99dc850 100644
--- a/bbc_micro_de1.qsf
+++ b/bbc_micro_de1.qsf
@@ -498,6 +498,10 @@ set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
set_global_assignment -name LL_ROOT_REGION ON -section_id "Root Region"
set_global_assignment -name LL_MEMBER_STATE LOCKED -section_id "Root Region"
+set_global_assignment -name MISC_FILE "U:/git_repos/fpga/bbc/bbc_micro_de1.dpf"
+set_location_assignment PIN_AB15 -to FL_CE_N
+set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "3.3-V LVTTL"
+set_global_assignment -name VHDL_FILE ps2_intf.vhd
set_global_assignment -name VHDL_FILE simple_uart.vhd
set_global_assignment -name VHDL_FILE m6522.vhd
set_global_assignment -name VHDL_FILE seg7.vhd
@@ -513,4 +517,6 @@ set_global_assignment -name VHDL_FILE bbc_micro_de1.vhd
set_global_assignment -name VHDL_FILE bbc_micro_de1_tb.vhd
set_global_assignment -name QIP_FILE ehbasic.qip
set_global_assignment -name VHDL_FILE m6522_tb.vhd
+set_global_assignment -name VHDL_FILE keyboard.vhd
+set_global_assignment -name VHDL_FILE debugger.vhd
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file
diff --git a/bbc_micro_de1.vhd b/bbc_micro_de1.vhd
index 0d29726..3e53add 100644
--- a/bbc_micro_de1.vhd
+++ b/bbc_micro_de1.vhd
@@ -85,9 +85,10 @@ port (
-- Flash
FL_ADDR : out std_logic_vector(21 downto 0);
FL_DQ : inout std_logic_vector(7 downto 0);
- FL_RST_N : in std_logic;
- FL_OE_N : in std_logic;
- FL_WE_N : in std_logic;
+ FL_RST_N : out std_logic;
+ FL_OE_N : out std_logic;
+ FL_WE_N : out std_logic;
+ FL_CE_N : out std_logic;
-- GPIO
GPIO_0 : inout std_logic_vector(35 downto 0);
@@ -111,17 +112,6 @@ component pll32 IS
);
end component;
------------------------
--- 7 segment display
------------------------
-
-component seg7 is
-port (
- D : in std_logic_vector(3 downto 0);
- Q : out std_logic_vector(6 downto 0)
-);
-end component;
-
------------------------------
-- Test UART for custom ROM
------------------------------
@@ -320,10 +310,86 @@ component M6522 is
I_P2_H : in std_logic; -- high for phase 2 clock ____----__
RESET_L : in std_logic;
ENA_4 : in std_logic; -- clk enable (4x system clock rate)
- CLK : in std_logic
+ CLK : in std_logic;
+ testout : out std_logic_vector(7 downto 0)
);
end component;
+-----------------------------
+-- PS/2 Keyboard Emulation
+-----------------------------
+
+component keyboard is
+port (
+ CLOCK : in std_logic;
+ nRESET : in std_logic;
+ CLKEN_1MHZ : in std_logic;
+
+ -- PS/2 interface
+ PS2_CLK : in std_logic;
+ PS2_DATA : in std_logic;
+
+ -- If 1 then column is incremented automatically at
+ -- 1 MHz rate
+ AUTOSCAN : in std_logic;
+
+ COLUMN : in std_logic_vector(3 downto 0);
+ ROW : in std_logic_vector(2 downto 0);
+
+ -- 1 when currently selected key is down (AUTOSCAN disabled)
+ KEYPRESS : out std_logic;
+ -- 1 when any key is down (except row 0)
+ INT : out std_logic;
+ -- BREAK key output - 1 when pressed
+ BREAK_OUT : out std_logic;
+
+ -- DIP switch inputs
+ DIP_SWITCH : in std_logic_vector(7 downto 0)
+ );
+end component;
+
+component debugger is
+generic (
+ -- Set this for a reasonable half flash duration relative to the
+ -- clock frequency
+ flash_divider : natural := 24
+ );
+port (
+ CLOCK : in std_logic;
+ nRESET : in std_logic;
+ -- CPU clock enable in
+ CLKEN_IN : in std_logic;
+ -- Gated clock enable back out to CPU
+ CLKEN_OUT : out std_logic;
+
+ -- CPU
+ A_CPU : in std_logic_vector(15 downto 0);
+ R_nW : in std_logic;
+ SYNC : in std_logic;
+
+ -- Controls
+ -- RUN or HALT CPU
+ RUN : in std_logic;
+ -- Push button to single-step in HALT mode
+ nSTEP : in std_logic;
+ -- Push button to cycle display mode
+ nMODE : in std_logic;
+ -- Push button to cycle display digit in edit mode
+ nDIGIT : in std_logic;
+ -- Push button to cycle digit value in edit mode
+ nSET : in std_logic;
+
+ -- Output to display
+ DIGIT3 : out std_logic_vector(6 downto 0);
+ DIGIT2 : out std_logic_vector(6 downto 0);
+ DIGIT1 : out std_logic_vector(6 downto 0);
+ DIGIT0 : out std_logic_vector(6 downto 0);
+
+ LED_BREAKPOINT : out std_logic;
+ LED_WATCHPOINT : out std_logic
+ );
+end component;
+
-------------
-- Signals
-------------
@@ -332,16 +398,17 @@ end component;
signal pll_reset : std_logic;
signal pll_locked : std_logic;
signal clock : std_logic;
+signal hard_reset_n : std_logic;
signal reset_n : std_logic;
-- Clock enable counter
-- CPU and video cycles are interleaved. The CPU runs at 2 MHz (every 16th
-- cycle) and the video subsystem is enabled on every odd cycle.
signal clken_counter : unsigned(4 downto 0);
-signal slomo_counter : unsigned(17 downto 0);
signal cpu_cycle : std_logic; -- Qualifies all 2 MHz cycles
signal cpu_cycle_mask : std_logic; -- Set to mask CPU cycles until 1 MHz cycle is complete
signal cpu_clken : std_logic; -- 2 MHz cycles in which the CPU is enabled
+signal cpu_debug_clken : std_logic; -- CPU clken return from hardware debugger
-- IO cycles are out of phase with the CPU
signal vid_clken : std_logic; -- 16 MHz video cycles
signal mhz4_clken : std_logic; -- Used by 6522
@@ -418,6 +485,7 @@ signal sys_via_cb2_oe_n : std_logic;
signal sys_via_pb_in : std_logic_vector(7 downto 0);
signal sys_via_pb_out : std_logic_vector(7 downto 0);
signal sys_via_pb_oe_n : std_logic_vector(7 downto 0);
+signal sys_via_testout : std_logic_vector(7 downto 0);
-- User VIA signals
signal user_via_do : std_logic_vector(7 downto 0);
@@ -439,6 +507,7 @@ signal user_via_cb2_oe_n : std_logic;
signal user_via_pb_in : std_logic_vector(7 downto 0);
signal user_via_pb_out : std_logic_vector(7 downto 0);
signal user_via_pb_oe_n : std_logic_vector(7 downto 0);
+signal user_via_testout : std_logic_vector(7 downto 0);
-- IC32 latch on System VIA
signal ic32 : std_logic_vector(7 downto 0);
@@ -450,6 +519,13 @@ signal disp_addr_offs : std_logic_vector(1 downto 0);
signal caps_lock_led_n : std_logic;
signal shift_lock_led_n : std_logic;
+-- Keyboard
+signal keyb_column : std_logic_vector(3 downto 0);
+signal keyb_row : std_logic_vector(2 downto 0);
+signal keyb_out : std_logic;
+signal keyb_int : std_logic;
+signal keyb_break : std_logic;
+
-- Memory enables
signal ram_enable : std_logic; -- 0x0000
signal rom_enable : std_logic; -- 0x8000 (BASIC/sideways ROMs)
@@ -471,6 +547,9 @@ signal adlc_enable : std_logic; -- 0xFEA0-FEBF (Econet)
signal adc_enable : std_logic; -- 0xFEC0-FEDF
signal tube_enable : std_logic; -- 0xFEE0-FEFF
+-- ROM select latch
+signal romsel : std_logic_vector(3 downto 0);
+
signal mhz1_enable : std_logic; -- Set for access to any 1 MHz peripheral
begin
@@ -484,15 +563,25 @@ begin
CLOCK_24(0),
clock,
pll_locked );
-
- -- Display of address bus
- addr3 : seg7 port map (cpu_a(15 downto 12), HEX3);
- addr2 : seg7 port map (cpu_a(11 downto 8), HEX2);
- addr1 : seg7 port map (cpu_a(7 downto 4), HEX1);
- addr0 : seg7 port map (cpu_a(3 downto 0), HEX0);
+
+ -- Hardware debugger block (single-step, breakpoints)
+ debug: debugger port map (
+ clock,
+ hard_reset_n,
+ cpu_clken,
+ cpu_debug_clken,
+ cpu_a(15 downto 0), cpu_r_nw, cpu_sync,
+ SW(8), -- RUN
+ KEY(3), -- STEP
+ KEY(2), -- MODE
+ KEY(1), -- DIGIT
+ KEY(0), -- SET
+ HEX3, HEX2, HEX1, HEX0,
+ LEDR(3), -- BREAKPOINT
+ LEDR(2) -- WATCHPOINT
+ );
-- Test UART
-
test_uart : simple_uart port map (
clock,
reset_n,
@@ -509,7 +598,7 @@ begin
cpu : T65 port map (
cpu_mode,
reset_n,
- cpu_clken,
+ cpu_debug_clken,
clock,
cpu_ready,
cpu_abort_n,
@@ -598,9 +687,9 @@ begin
sys_via_pb_out,
sys_via_pb_oe_n,
mhz1_clken,
- reset_n,
+ hard_reset_n, -- System VIA is reset by power on reset only
mhz4_clken,
- clock
+ clock, sys_via_testout
);
-- User VIA
@@ -632,12 +721,29 @@ begin
mhz1_clken,
reset_n,
mhz4_clken,
- clock
+ clock, user_via_testout
+ );
+
+ -- Keyboard
+ keyb : keyboard port map (
+ clock, hard_reset_n, mhz1_clken,
+ PS2_CLK, PS2_DAT,
+ keyb_enable_n,
+ keyb_column,
+ keyb_row,
+ keyb_out,
+ keyb_int,
+ keyb_break,
+ SW(7 downto 0)
);
-- Asynchronous reset
+ -- PLL is reset by external reset switch
pll_reset <= not SW(9);
- reset_n <= not (pll_reset or not pll_locked);
+ -- Keyboard and System VIA are reset by external reset switch or PLL being out of lock
+ hard_reset_n <= not (pll_reset or not pll_locked);
+ -- Rest of system is reset by all of the above plus the keyboard BREAK key
+ reset_n <= hard_reset_n and not keyb_break;
-- Clock enable generation - 32 MHz clock split into 32 cycles
-- CPU is on 0 and 16 (but can be masked by 1 MHz bus accesses)
@@ -647,21 +753,15 @@ begin
mhz4_clken <= clken_counter(0) and clken_counter(1) and clken_counter(2); -- 7/15/23/31
mhz2_clken <= mhz4_clken and clken_counter(3); -- 15/31
mhz1_clken <= mhz2_clken and clken_counter(4); -- 31
- cpu_cycle <=
- not (clken_counter(0) or clken_counter(1) or clken_counter(2) or clken_counter(3))
- when (SW(8) = '1' or slomo_counter = 0) else '0'; -- 0/16
+ cpu_cycle <= not (clken_counter(0) or clken_counter(1) or clken_counter(2) or clken_counter(3)); -- 0/16
cpu_clken <= cpu_cycle and not cpu_cycle_mask;
clk_gen: process(clock,reset_n)
begin
if reset_n = '0' then
clken_counter <= (others => '0');
- slomo_counter <= (others => '0');
elsif rising_edge(clock) then
clken_counter <= clken_counter + 1;
- if clken_counter = 0 then
- slomo_counter <= slomo_counter + 1;
- end if;
end if;
end process;
@@ -778,15 +878,31 @@ begin
-- CPU data bus mux and interrupts
cpu_di <=
+ SRAM_DQ(7 downto 0) when ram_enable = '1' else
+ FL_DQ when rom_enable = '1' else
mos_d when mos_enable = '1' else
- "11111111" when rom_enable = '1' else
crtc_do when crtc_enable = '1' else
+ "00000010" when acia_enable = '1' else
sys_via_do when sys_via_enable = '1' else
user_via_do when user_via_enable = '1' else
test_uart_do when io_fred = '1' else
- SRAM_DQ(7 downto 0);
- --cpu_irq_n <= sys_via_irq_n; -- and user_via_irq_n;
- cpu_irq_n <= '1';
+ (others => '0'); -- un-decoded locations are pulled down by RP1
+ cpu_irq_n <= sys_via_irq_n; -- and user_via_irq_n;
+ --cpu_irq_n <= '1';
+
+ -- ROMs are in external flash
+ FL_RST_N <= reset_n;
+ FL_CE_N <= '0';
+ FL_OE_N <= '0';
+ FL_WE_N <= '1';
+ FL_ADDR(13 downto 0) <= cpu_a(13 downto 0);
+ FL_ADDR(21 downto 16) <= (others => '0');
+ FL_ADDR(15 downto 14) <=
+ "00" when mos_enable = '1' else
+ "01" when rom_enable = '1' and romsel(1 downto 0) = "11" else -- BASIC
+ --"10" when rom_enable = '1' and romsel(1 downto 0) = "00" else
+ "11";
+
-- SRAM bus
SRAM_UB_N <= '1';
@@ -863,14 +979,14 @@ begin
-- VIDPROC
vidproc_invert_n <= '1';
- r_in <= '0';
+ r_in <= '0'; -- FIXME: From SAA5050
g_in <= '0';
b_in <= '0';
- GPIO_0(0) <= not (crtc_hsync xor crtc_vsync);
- GPIO_0(1) <= cpu_clken;
- GPIO_0(2) <= cpu_cycle_mask;
- GPIO_0(3) <= mhz1_enable;
+ GPIO_0(0) <= cpu_irq_n;
+ GPIO_0(1) <= keyb_out;
+ GPIO_0(2) <= keyb_enable_n;
+ GPIO_0(3) <= sys_via_testout(6); -- timer 1
-- CRTC drives video out (CSYNC on HSYNC output, VSYNC high)
VGA_HS <= not (crtc_hsync xor crtc_vsync);
@@ -880,13 +996,35 @@ begin
VGA_B <= b_out & b_out & b_out & b_out;
-- Connections to System VIA
+ -- ADC
+ sys_via_cb1_in <= '1'; -- /EOC
+ -- CRTC
sys_via_ca1_in <= crtc_vsync;
- sys_via_ca2_in <= '0'; -- Keyboard interrupt
- sys_via_cb1_in <= '0'; -- /EOC
sys_via_cb2_in <= crtc_lpstb;
+ -- Keyboard
+ sys_via_ca2_in <= keyb_int;
+ sys_via_pa_in(7) <= keyb_out;
+ keyb_column <= sys_via_pa_out(3 downto 0);
+ keyb_row <= sys_via_pa_out(6 downto 4);
+ -- Others (idle until missing bits implemented)
+ sys_via_pa_in(6 downto 0) <= (others => '0');
+ sys_via_pb_in(7 downto 4) <= (others => '1');
-- Connections to User VIA (user port is output on green LEDs)
- LEDG <= user_via_pb_out;
+ --LEDG <= user_via_pb_out;
+ LEDG <= sys_via_testout;
+
+ -- ROM select latch
+ process(clock,reset_n)
+ begin
+ if reset_n = '0' then
+ romsel <= (others => '0');
+ elsif rising_edge(clock) then
+ if romsel_enable = '1' and cpu_r_nw = '0' then
+ romsel <= cpu_do(3 downto 0);
+ end if;
+ end if;
+ end process;
-- IC32 latch
sound_enable_n <= ic32(0);
@@ -912,28 +1050,5 @@ begin
-- Keyboard LEDs
LEDR(0) <= not caps_lock_led_n;
LEDR(1) <= not shift_lock_led_n;
-
- -- Hack for jumper links
- process(sys_via_pa_out)
- begin
- sys_via_pa_in <= sys_via_pa_out;
-
- if sys_via_pa_out(6 downto 4) = "000" then
- case to_integer(unsigned(sys_via_pa_out(3 downto 0))) is
- when 2 => sys_via_pa_in(7) <= SW(7);
- when 3 => sys_via_pa_in(7) <= SW(6);
- when 4 => sys_via_pa_in(7) <= SW(5);
- when 5 => sys_via_pa_in(7) <= SW(4);
- when 6 => sys_via_pa_in(7) <= SW(3);
- when 7 => sys_via_pa_in(7) <= SW(2);
- when 8 => sys_via_pa_in(7) <= SW(1);
- when 9 => sys_via_pa_in(7) <= SW(0);
- when others => sys_via_pa_in(7) <= '0';
- end case;
- else
- -- 'W' output of keyboard IC2 is inverse data, so 0 is no key pressed
- sys_via_pa_in(7) <= '0';
- end if;
- end process;
-
+
end architecture;
diff --git a/debugger.vhd b/debugger.vhd
new file mode 100644
index 0000000..0a4219d
--- /dev/null
+++ b/debugger.vhd
@@ -0,0 +1,252 @@
+--
+-- General purpose hardware debugger
+--
+-- (C) 2011 Mike Stirling
+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+entity debugger is
+generic (
+ -- Set this for a reasonable half flash duration relative to the
+ -- clock frequency
+ flash_divider : natural := 24
+ );
+port (
+ CLOCK : in std_logic;
+ nRESET : in std_logic;
+ -- CPU clock enable in
+ CLKEN_IN : in std_logic;
+ -- Gated clock enable back out to CPU
+ CLKEN_OUT : out std_logic;
+
+ -- CPU
+ A_CPU : in std_logic_vector(15 downto 0);
+ R_nW : in std_logic;
+ SYNC : in std_logic;
+
+ -- Controls
+ -- RUN or HALT CPU
+ RUN : in std_logic;
+ -- Push button to single-step in HALT mode
+ nSTEP : in std_logic;
+ -- Push button to cycle display mode
+ nMODE : in std_logic;
+ -- Push button to cycle display digit in edit mode
+ nDIGIT : in std_logic;
+ -- Push button to cycle digit value in edit mode
+ nSET : in std_logic;
+
+ -- Output to display
+ DIGIT3 : out std_logic_vector(6 downto 0);
+ DIGIT2 : out std_logic_vector(6 downto 0);
+ DIGIT1 : out std_logic_vector(6 downto 0);
+ DIGIT0 : out std_logic_vector(6 downto 0);
+
+ LED_BREAKPOINT : out std_logic;
+ LED_WATCHPOINT : out std_logic
+ );
+end entity;
+
+architecture rtl of debugger is
+
+component seg7 is
+port (
+ D : in std_logic_vector(3 downto 0);
+ Q : out std_logic_vector(6 downto 0)
+);
+end component;
+
+-- Current display mode
+type mode_t is (modeAddress,modeBreak,modeWatch);
+signal mode : mode_t;
+-- Current edit digit
+signal digit : unsigned(1 downto 0);
+-- For flashing selected digit
+signal counter : unsigned(flash_divider-1 downto 0);
+signal flash : std_logic;
+-- Selected breakpoint address (stop on instruction fetch)
+signal breakpoint : std_logic_vector(15 downto 0);
+-- Selected watchpoint address (stop on write)
+signal watchpoint : std_logic_vector(15 downto 0);
+-- Address of last instruction fetch
+signal instr_addr : std_logic_vector(15 downto 0);
+-- Break flags
+signal halt : std_logic;
+-- Set when a request to resume has been received but before
+-- the CPU has run
+signal resuming : std_logic;
+
+-- Display interface
+signal a_display : std_logic_vector(15 downto 0);
+signal d3_display : std_logic_vector(6 downto 0);
+signal d2_display : std_logic_vector(6 downto 0);
+signal d1_display : std_logic_vector(6 downto 0);
+signal d0_display : std_logic_vector(6 downto 0);
+
+-- Registered button inputs
+signal r_step_n : std_logic;
+signal r_mode_n : std_logic;
+signal r_digit_n : std_logic;
+signal r_set_n : std_logic;
+
+begin
+ -- Mask CPU clock enable
+ CLKEN_OUT <= CLKEN_IN and not halt;
+
+ -- Route selected address to display
+ a_display <= instr_addr when mode = modeAddress else
+ breakpoint when mode = modeBreak else
+ watchpoint when mode = modeWatch else
+ (others => '0');
+
+ -- Generate display digits from binary
+ d3 : seg7 port map (a_display(15 downto 12),d3_display);
+ d2 : seg7 port map (a_display(11 downto 8),d2_display);
+ d1 : seg7 port map (a_display(7 downto 4),d1_display);
+ d0 : seg7 port map (a_display(3 downto 0),d0_display);
+
+ -- Flash selected digit in edit modes
+ DIGIT3 <= d3_display when (mode = modeAddress or flash = '1' or digit /= "11") else "1111111";
+ DIGIT2 <= d2_display when (mode = modeAddress or flash = '1' or digit /= "10") else "1111111";
+ DIGIT1 <= d1_display when (mode = modeAddress or flash = '1' or digit /= "01") else "1111111";
+ DIGIT0 <= d0_display when (mode = modeAddress or flash = '1' or digit /= "00") else "1111111";
+
+ -- Show mode on LEDs
+ LED_BREAKPOINT <= '1' when mode = modeBreak else '0';
+ LED_WATCHPOINT <= '1' when mode = modeWatch else '0';
+
+ -- Flash counter
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ counter <= (others => '0');
+ flash <= '0';
+ elsif rising_edge(CLOCK) then
+ counter <= counter + 1;
+ if counter = 0 then
+ flash <= not flash;
+ end if;
+ end if;
+ end process;
+
+ -- Register buttons, select input mode and digit
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ r_mode_n <= '1';
+ r_digit_n <= '1';
+ r_set_n <= '1';
+ mode <= modeAddress;
+ digit <= (others => '0');
+ elsif rising_edge(CLOCK) then
+ -- Register buttons
+ r_mode_n <= nMODE;
+ r_digit_n <= nDIGIT;
+ r_set_n <= nSET;
+
+ if r_mode_n = '1' and nMODE = '0' then
+ -- Increment mode
+ if mode = modeAddress then
+ mode <= modeBreak;
+ elsif mode = modeBreak then
+ mode <= modeWatch;
+ else
+ mode <= modeAddress;
+ end if;
+ end if;
+ if r_digit_n = '1' and nDIGIT = '0' then
+ -- Increment digit
+ digit <= digit + 1;
+ end if;
+ end if;
+ end process;
+
+ -- Set watchpoint address
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ watchpoint <= (others => '0');
+ elsif rising_edge(CLOCK) and mode = modeWatch then
+ if r_set_n = '1' and nSET = '0' then
+ -- Increment selected digit on each button press
+ case digit is
+ when "00" => watchpoint(3 downto 0) <= std_logic_vector(unsigned(watchpoint(3 downto 0)) + 1);
+ when "01" => watchpoint(7 downto 4) <= std_logic_vector(unsigned(watchpoint(7 downto 4)) + 1);
+ when "10" => watchpoint(11 downto 8) <= std_logic_vector(unsigned(watchpoint(11 downto 8)) + 1);
+ when "11" => watchpoint(15 downto 12) <= std_logic_vector(unsigned(watchpoint(15 downto 12)) + 1);
+ when others => null;
+ end case;
+ end if;
+ end if;
+ end process;
+
+ -- Set breakpoint address
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ breakpoint <= (others => '0');
+ elsif rising_edge(CLOCK) and mode = modeBreak then
+ if r_set_n = '1' and nSET = '0' then
+ -- Increment selected digit on each button press
+ case digit is
+ when "00" => breakpoint(3 downto 0) <= std_logic_vector(unsigned(breakpoint(3 downto 0)) + 1);
+ when "01" => breakpoint(7 downto 4) <= std_logic_vector(unsigned(breakpoint(7 downto 4)) + 1);
+ when "10" => breakpoint(11 downto 8) <= std_logic_vector(unsigned(breakpoint(11 downto 8)) + 1);
+ when "11" => breakpoint(15 downto 12) <= std_logic_vector(unsigned(breakpoint(15 downto 12)) + 1);
+
+ when others => null;
+ end case;
+ end if;
+ end if;
+ end process;
+
+ -- CPU control logic
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ r_step_n <= '1';
+ halt <= '0';
+ resuming <= '0';
+ instr_addr <= (others => '0');
+ elsif rising_edge(CLOCK) then
+ -- Register single-step button
+ r_step_n <= nSTEP;
+
+ if SYNC = '1' then
+ -- Latch address of instruction fetch
+ instr_addr <= A_CPU;
+ end if;
+
+ -- Check for halt conditions if we are not resuming from a previous halt
+ if resuming = '0' then
+ if RUN = '0' and SYNC = '1' then
+ -- If not in RUN mode then halt on any instruction fetch
+ -- (single-step)
+ halt <= '1';
+ end if;
+ if A_CPU = breakpoint and SYNC = '1' then
+ -- Halt CPU when instruction fetched from breakpoint address
+ halt <= '1';
+ end if;
+ if A_CPU = watchpoint and SYNC = '0' and R_nW = '0' then
+ -- Halt CPU when data write to watchpoint address
+ halt <= '1';
+ end if;
+ end if;
+
+ -- Resume or single step when user presses "STEP" button
+ if r_step_n = '1' and nSTEP = '0' then
+ resuming <= '1';
+ halt <= '0';
+ end if;
+
+ -- Once the CPU has run we can trigger a new halt
+ if CLKEN_IN = '1' then
+ resuming <= '0';
+ end if;
+ end if;
+ end process;
+end architecture;
diff --git a/keyboard.txt b/keyboard.txt
new file mode 100644
index 0000000..d63fa03
--- /dev/null
+++ b/keyboard.txt
@@ -0,0 +1,81 @@
+Column Row Key PC key Scancode
+0 0 SHIFT LEFT SHIFT, RIGHT SHIFT 12 + 59
+0 1 Q Q 15
+0 2 F0 F10 09
+0 3 1 (!) 1 16
+0 4 CAPS LOCK CAPS LOCK 58
+0 5 SHIFT LOCK LEFT ALT 11
+0 6 TAB TAB 0D
+0 7 ESCAPE ESCAPE 76
+1 0 CTRL LEFT CTRL, RIGHT CTRL 14 + E0,14
+1 1 3 (#) 3 26
+1 2 W W 1D
+1 3 2 (") 2 1E
+1 4 A A 1C
+1 5 S S 1B
+1 6 Z Z 1A
+1 7 F1 F1 05
+2 0 DIP 7
+2 1 4 ($) 4 25
+2 2 E E 24
+2 3 D D 23
+2 4 X X 22
+2 5 C C 21
+2 6 SPACE SPACE 29
+2 7 F2 F2 06
+3 0 DIP 6
+3 1 5 (%) 5 2E
+3 2 T T 2C
+3 3 R R 2D
+3 4 F F 2B
+3 5 G G 34
+3 6 V V 2A
+3 7 F3 F3 04
+4 0 DIP 5
+4 1 F4 F4 0C
+4 2 7 (') 7 3D
+4 3 6 (&) 6 36
+4 4 Y Y 35
+4 5 H H 33
+4 6 B B 32
+4 7 F5 F5 03
+5 0 DIP 4
+5 1 8 (() 8 3E
+5 2 I I 43
+5 3 U U 3C
+5 4 J J 3B
+5 5 N N 31
+5 6 M M 3A
+5 7 F6 F6 0B
+6 0 DIP 3
+6 1 F7 F7 83
+6 2 9 ()) 9 46
+6 3 O O 44
+6 4 K K 42
+6 5 L L 4B
+6 6 , (<) , 41
+6 7 F8 F8 0A
+7 0 DIP 2
+7 1 - (=) - 4E
+7 2 0 0 45
+7 3 P P 4D
+7 4 @ ` 0E
+7 5 ; (+) ; 4C
+7 6 . (>) . 49
+7 7 F9 F9 01
+8 0 DIP 1
+8 1 ^ (~) = 55
+8 2 _ (£) # 5D
+8 3 [ ({) [ 54
+8 4 : (*) ' 52
+8 5 ] (}) ] 5B
+8 6 / (?) / 4A
+8 7 \ (|) \ 61
+9 0 DIP 0
+9 1 LEFT LEFT E0,6B
+9 2 DOWN DOWN E0,72
+9 3 UP UP E0,75
+9 4 RETURN RETURN 5A
+9 5 DELETE BACKSPACE 66
+9 6 COPY END E0,69
+9 7 RIGHT RIGHT E0,74
diff --git a/keyboard.vhd b/keyboard.vhd
new file mode 100644
index 0000000..0558469
--- /dev/null
+++ b/keyboard.vhd
@@ -0,0 +1,251 @@
+-- BBC keyboard implementation with interface to PS/2
+--
+-- (C) 2011 Mike Stirling
+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+entity keyboard is
+port (
+ CLOCK : in std_logic;
+ nRESET : in std_logic;
+ CLKEN_1MHZ : in std_logic;
+
+ -- PS/2 interface
+ PS2_CLK : in std_logic;
+ PS2_DATA : in std_logic;
+
+ -- If 1 then column is incremented automatically at
+ -- 1 MHz rate
+ AUTOSCAN : in std_logic;
+
+ COLUMN : in std_logic_vector(3 downto 0);
+ ROW : in std_logic_vector(2 downto 0);
+
+ -- 1 when currently selected key is down (AUTOSCAN disabled)
+ KEYPRESS : out std_logic;
+ -- 1 when any key is down (except row 0)
+ INT : out std_logic;
+ -- BREAK key output - 1 when pressed
+ BREAK_OUT : out std_logic;
+
+ -- DIP switch inputs
+ DIP_SWITCH : in std_logic_vector(7 downto 0)
+ );
+end entity;
+
+architecture rtl of keyboard is
+
+-- PS/2 interface
+component ps2_intf is
+generic (filter_length : positive := 8);
+port(
+ CLK : in std_logic;
+ nRESET : in std_logic;
+
+ -- PS/2 interface (could be bi-dir)
+ PS2_CLK : in std_logic;
+ PS2_DATA : in std_logic;
+
+ -- Byte-wide data interface - only valid for one clock
+ -- so must be latched externally if required
+ DATA : out std_logic_vector(7 downto 0);
+ VALID : out std_logic;
+ ERROR : out std_logic
+ );
+end component;
+
+-- Interface to PS/2 block
+signal keyb_data : std_logic_vector(7 downto 0);
+signal keyb_valid : std_logic;
+signal keyb_error : std_logic;
+
+-- Internal signals
+type key_matrix is array(0 to 9) of std_logic_vector(7 downto 0);
+signal keys : key_matrix;
+signal col : unsigned(3 downto 0);
+signal release : std_logic;
+signal extended : std_logic;
+begin
+
+ ps2 : ps2_intf port map (
+ CLOCK, nRESET,
+ PS2_CLK, PS2_DATA,
+ keyb_data, keyb_valid, keyb_error
+ );
+
+ -- Column counts automatically when AUTOSCAN is enabled, otherwise
+ -- value is loaded from external input
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ col <= (others => '0');
+ elsif rising_edge(CLOCK) then
+ if AUTOSCAN = '0' then
+ -- If autoscan disabled then transfer current COLUMN to counter
+ -- immediately (don't wait for next 1 MHz cycle)
+ col <= unsigned(COLUMN);
+ elsif CLKEN_1MHZ = '1' then
+ -- Otherwise increment the counter once per 1 MHz tick
+ col <= col + 1;
+ end if;
+ end if;
+ end process;
+
+ -- Generate interrupt if any key in currently scanned column is pressed
+ -- (apart from in row 0). Output selected key status if autoscan disabled.
+ process(keys,col,ROW,AUTOSCAN)
+ variable k : std_logic_vector(7 downto 0);
+ begin
+ -- Shortcut to current key column
+ k := keys(to_integer(col));
+
+ -- Interrupt if any key pressed in rows 1 to 7.
+ INT <= k(7) or k(6) or k(5) or k(4) or k(3) or k(2) or k(1);
+
+ -- Determine which key is pressed
+ -- Inhibit output during auto-scan
+ if AUTOSCAN = '0' then
+ KEYPRESS <= k(to_integer(unsigned(ROW)));
+ else
+ KEYPRESS <= '0';
+ end if;
+ end process;
+
+ -- Decode PS/2 data
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ release <= '0';
+ extended <= '0';
+
+ BREAK_OUT <= '0';
+
+ keys(0) <= (others => '0');
+ keys(1) <= (others => '0');
+ keys(2) <= (others => '0');
+ keys(3) <= (others => '0');
+ keys(4) <= (others => '0');
+ keys(5) <= (others => '0');
+ keys(6) <= (others => '0');
+ keys(7) <= (others => '0');
+ keys(8) <= (others => '0');
+ keys(9) <= (others => '0');
+ elsif rising_edge(CLOCK) then
+ -- Copy DIP switches through to row 0
+ keys(2)(0) <= DIP_SWITCH(7);
+ keys(3)(0) <= DIP_SWITCH(6);
+ keys(4)(0) <= DIP_SWITCH(5);
+ keys(5)(0) <= DIP_SWITCH(4);
+ keys(6)(0) <= DIP_SWITCH(3);
+ keys(7)(0) <= DIP_SWITCH(2);
+ keys(8)(0) <= DIP_SWITCH(1);
+ keys(9)(0) <= DIP_SWITCH(0);
+
+ if keyb_valid = '1' then
+ -- Decode keyboard input
+ if keyb_data = X"e0" then
+ -- Extended key code follows
+ extended <= '1';
+ elsif keyb_data = X"f0" then
+ -- Release code follows
+ release <= '1';
+ else
+ -- Cancel extended/release flags for next time
+ release <= '0';
+ extended <= '0';
+
+ -- Decode scan codes
+ case keyb_data is
+ when X"12" => keys(0)(0) <= not release; -- Left SHIFT
+ when X"59" => keys(0)(0) <= not release; -- Right SHIFT
+ when X"15" => keys(0)(1) <= not release; -- Q
+ when X"09" => keys(0)(2) <= not release; -- F10 (F0)
+ when X"16" => keys(0)(3) <= not release; -- 1
+ when X"58" => keys(0)(4) <= not release; -- CAPS LOCK
+ when X"11" => keys(0)(5) <= not release; -- LEFT ALT (SHIFT LOCK)
+ when X"0D" => keys(0)(6) <= not release; -- TAB
+ when X"76" => keys(0)(7) <= not release; -- ESCAPE
+ when X"14" => keys(1)(0) <= not release; -- LEFT/RIGHT CTRL (CTRL)
+ when X"26" => keys(1)(1) <= not release; -- 3
+ when X"1D" => keys(1)(2) <= not release; -- W
+ when X"1E" => keys(1)(3) <= not release; -- 2
+ when X"1C" => keys(1)(4) <= not release; -- A
+ when X"1B" => keys(1)(5) <= not release; -- S
+ when X"1A" => keys(1)(6) <= not release; -- Z
+ when X"05" => keys(1)(7) <= not release; -- F1
+ when X"25" => keys(2)(1) <= not release; -- 4
+ when X"24" => keys(2)(2) <= not release; -- E
+ when X"23" => keys(2)(3) <= not release; -- D
+ when X"22" => keys(2)(4) <= not release; -- X
+ when X"21" => keys(2)(5) <= not release; -- C
+ when X"29" => keys(2)(6) <= not release; -- SPACE
+ when X"06" => keys(2)(7) <= not release; -- F2
+ when X"2E" => keys(3)(1) <= not release; -- 5
+ when X"2C" => keys(3)(2) <= not release; -- T
+ when X"2D" => keys(3)(3) <= not release; -- R
+ when X"2B" => keys(3)(4) <= not release; -- F
+ when X"34" => keys(3)(5) <= not release; -- G
+ when X"2A" => keys(3)(6) <= not release; -- V
+ when X"04" => keys(3)(7) <= not release; -- F3
+ when X"0C" => keys(4)(1) <= not release; -- F4
+ when X"3D" => keys(4)(2) <= not release; -- 7
+ when X"36" => keys(4)(3) <= not release; -- 6
+ when X"35" => keys(4)(4) <= not release; -- Y
+ when X"33" => keys(4)(5) <= not release; -- H
+ when X"32" => keys(4)(6) <= not release; -- B
+ when X"03" => keys(4)(7) <= not release; -- F5
+ when X"3E" => keys(5)(1) <= not release; -- 8
+ when X"43" => keys(5)(2) <= not release; -- I
+ when X"3C" => keys(5)(3) <= not release; -- U
+ when X"3B" => keys(5)(4) <= not release; -- J
+ when X"31" => keys(5)(5) <= not release; -- N
+ when X"3A" => keys(5)(6) <= not release; -- M
+ when X"0B" => keys(5)(7) <= not release; -- F6
+ when X"83" => keys(6)(1) <= not release; -- F7
+ when X"46" => keys(6)(2) <= not release; -- 9
+ when X"44" => keys(6)(3) <= not release; -- O
+ when X"42" => keys(6)(4) <= not release; -- K
+ when X"4B" => keys(6)(5) <= not release; -- L
+ when X"41" => keys(6)(6) <= not release; -- ,
+ when X"0A" => keys(6)(7) <= not release; -- F8
+ when X"4E" => keys(7)(1) <= not release; -- -
+ when X"45" => keys(7)(2) <= not release; -- 0
+ when X"4D" => keys(7)(3) <= not release; -- P
+ when X"0E" => keys(7)(4) <= not release; -- ` (@)
+ when X"4C" => keys(7)(5) <= not release; -- ;
+ when X"49" => keys(7)(6) <= not release; -- .
+ when X"01" => keys(7)(7) <= not release; -- F9
+ when X"55" => keys(8)(1) <= not release; -- = (^)
+ when X"5D" => keys(8)(2) <= not release; -- # (_)
+ when X"54" => keys(8)(3) <= not release; -- [
+ when X"52" => keys(8)(4) <= not release; -- '
+ when X"5B" => keys(8)(5) <= not release; -- ]
+ when X"4A" => keys(8)(6) <= not release; -- /
+ when X"61" => keys(8)(7) <= not release; -- \
+ when X"6B" => keys(9)(1) <= not release; -- LEFT
+ when X"72" => keys(9)(2) <= not release; -- DOWN
+ when X"75" => keys(9)(3) <= not release; -- UP
+ when X"5A" => keys(9)(4) <= not release; -- RETURN
+ when X"66" => keys(9)(5) <= not release; -- BACKSPACE (DELETE)
+ when X"69" => keys(9)(6) <= not release; -- END (COPY)
+ when X"74" => keys(9)(7) <= not release; -- RIGHT
+
+ -- F12 is used for the BREAK key, which in the real BBC asserts
+ -- reset. Here we pass this out to the top level which may
+ -- optionally OR it in to the system reset
+ when X"07" => BREAK_OUT <= not release; -- F12 (BREAK)
+
+ when others => null;
+ end case;
+
+ end if;
+ end if;
+ end if;
+ end process;
+
+end architecture;
+
+
diff --git a/m6522.vhd b/m6522.vhd
index f0b974a..a0dd241 100644
--- a/m6522.vhd
+++ b/m6522.vhd
@@ -90,7 +90,8 @@ entity M6522 is
I_P2_H : in std_logic; -- high for phase 2 clock ____----__
RESET_L : in std_logic;
ENA_4 : in std_logic; -- clk enable
- CLK : in std_logic
+ CLK : in std_logic;
+ testout : out std_logic_vector(7 downto 0)
);
end;
@@ -191,6 +192,8 @@ architecture RTL of M6522 is
signal final_irq : std_logic;
begin
+ testout <= r_ifr and "1" & r_ier;
+
p_phase : process
begin
-- internal clock phase
@@ -597,17 +600,14 @@ begin
--
-- Timer 1
--
- p_timer1_done : process
+ p_timer1_done : process(t1c,phase,r_acr)
variable done : boolean;
begin
- wait until rising_edge(CLK);
- if (ENA_4 = '1') then
done := (t1c = x"0000");
t1c_done <= done and (phase = "11");
if (phase = "11") then
t1_reload_counter <= done and (r_acr(6) = '1');
end if;
- end if;
end process;
p_timer1 : process
@@ -626,12 +626,15 @@ begin
elsif t1c_done then
t1c_active <= false;
end if;
+ if RESET_L = '0' then
+ t1c_active <= false;
+ end if;
t1_toggle <= '0';
if t1c_active and t1c_done then
t1_toggle <= '1';
t1_irq <= '1';
- elsif t1_w_reset_int or t1_r_reset_int or (clear_irq(6) = '1') then
+ elsif RESET_L = '0' or t1_w_reset_int or t1_r_reset_int or (clear_irq(6) = '1') then
t1_irq <= '0';
end if;
end if;
@@ -650,17 +653,14 @@ begin
end if;
end process;
- p_timer2_done : process
+ p_timer2_done : process(t2c,phase)
variable done : boolean;
begin
- wait until rising_edge(CLK);
- if (ENA_4 = '1') then
done := (t2c = x"0000");
t2c_done <= done and (phase = "11");
if (phase = "11") then
t2_reload_counter <= done;
end if;
- end if;
end process;
p_timer2 : process
@@ -693,11 +693,13 @@ begin
elsif t2c_done then
t2c_active <= false;
end if;
-
+ if RESET_L = '0' then
+ t2c_active <= false;
+ end if;
if t2c_active and t2c_done then
t2_irq <= '1';
- elsif t2_w_reset_int or t2_r_reset_int or (clear_irq(5) = '1') then
+ elsif RESET_L = '0' or t2_w_reset_int or t2_r_reset_int or (clear_irq(5) = '1') then
t2_irq <= '0';
end if;
end if;
diff --git a/m6522_tb.vhd b/m6522_tb.vhd
index 7c4b1fa..eb76db5 100644
--- a/m6522_tb.vhd
+++ b/m6522_tb.vhd
@@ -46,7 +46,8 @@ component M6522 is
I_P2_H : in std_logic; -- high for phase 2 clock ____----__
RESET_L : in std_logic;
ENA_4 : in std_logic; -- clk enable
- CLK : in std_logic
+ CLK : in std_logic;
+ testout : out std_logic_vector(7 downto 0)
);
end component;
@@ -80,6 +81,8 @@ signal n_reset : std_logic := '0';
signal clken : std_logic := '0';
signal clock : std_logic := '0';
+signal testout : std_logic_vector(7 downto 0);
+
begin
uut: m6522 port map (
@@ -90,7 +93,7 @@ begin
cb1_in, cb1_out, n_cb1_oe,
cb2_in, cb2_out, n_cb2_oe,
pb_in, pb_out, n_pb_oe,
- phase2, n_reset, clken, clock
+ phase2, n_reset, clken, clock, testout
);
clock <= not clock after 125 ns; -- 4x 1 MHz
@@ -317,6 +320,24 @@ begin
wait for 50 us;
reg_read("1000"); -- Should clear interrupt
+ -- Timer 2 test similar to BBC usage (speech interrupt)
+ -- PB6 high
+ pb_in(6) <= '1';
+ reg_write("1101","01111111"); -- Clear interrupts
+ reg_write("1110","01111111"); -- Disable all interrupts
+ reg_write("1011","00100000"); -- Timer 2 PB6 counter mode
+ reg_write("1000","00000001"); -- Start at 1
+ reg_write("1001","00000000");
+ reg_write("1110","10100000"); -- Enable timer 2 interrupt
+ wait for 5 us;
+ -- Generate falling edge
+ pb_in(6) <= '0';
+ wait for 5 us;
+ -- Clear interrupt
+ reg_write("1101","00100000");
+ -- Zero timer high byte
+ reg_write("1001","00000000");
+
wait;
end process;
diff --git a/ps2_intf.vhd b/ps2_intf.vhd
new file mode 100644
index 0000000..3cd15bf
--- /dev/null
+++ b/ps2_intf.vhd
@@ -0,0 +1,153 @@
+-- ZX Spectrum for Altera DE1
+--
+-- Copyright (c) 2009-2010 Mike Stirling
+--
+-- All rights reserved
+--
+-- Redistribution and use in source and synthezised forms, with or without
+-- modification, are permitted provided that the following conditions are met:
+--
+-- * Redistributions of source code must retain the above copyright notice,
+-- this list of conditions and the following disclaimer.
+--
+-- * Redistributions in synthesized form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+--
+-- * Neither the name of the author nor the names of other contributors may
+-- be used to endorse or promote products derived from this software without
+-- specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+-- POSSIBILITY OF SUCH DAMAGE.
+--
+
+-- PS/2 interface (input only)
+-- Based loosely on ps2_ctrl.vhd (c) ALSE. http://www.alse-fr.com
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+-- This is input-only for the time being
+entity ps2_intf is
+generic (filter_length : positive := 8);
+port(
+ CLK : in std_logic;
+ nRESET : in std_logic;
+
+ -- PS/2 interface (could be bi-dir)
+ PS2_CLK : in std_logic;
+ PS2_DATA : in std_logic;
+
+ -- Byte-wide data interface - only valid for one clock
+ -- so must be latched externally if required
+ DATA : out std_logic_vector(7 downto 0);
+ VALID : out std_logic;
+ ERROR : out std_logic
+ );
+end ps2_intf;
+
+architecture ps2_intf_arch of ps2_intf is
+subtype filter_t is std_logic_vector(filter_length-1 downto 0);
+signal clk_filter : filter_t;
+
+signal ps2_clk_in : std_logic;
+signal ps2_dat_in : std_logic;
+-- Goes high when a clock falling edge is detected
+signal clk_edge : std_logic;
+signal bit_count : unsigned (3 downto 0);
+signal shiftreg : std_logic_vector(8 downto 0);
+signal parity : std_logic;
+begin
+ -- Register input signals
+ process(nRESET,CLK)
+ begin
+ if nRESET = '0' then
+ ps2_clk_in <= '1';
+ ps2_dat_in <= '1';
+ clk_filter <= (others => '1');
+ clk_edge <= '0';
+ elsif rising_edge(CLK) then
+ -- Register inputs (and filter clock)
+ ps2_dat_in <= PS2_DATA;
+ clk_filter <= PS2_CLK & clk_filter(clk_filter'high downto 1);
+ clk_edge <= '0';
+
+ if clk_filter = filter_t'(others => '1') then
+ -- Filtered clock is high
+ ps2_clk_in <= '1';
+ elsif clk_filter = filter_t'(others => '0') then
+ -- Filter clock is low, check for edge
+ if ps2_clk_in = '1' then
+ clk_edge <= '1';
+ end if;
+ ps2_clk_in <= '0';
+ end if;
+ end if;
+ end process;
+
+ -- Shift in keyboard data
+ process(nRESET,CLK)
+ begin
+ if nRESET = '0' then
+ bit_count <= (others => '0');
+ shiftreg <= (others => '0');
+ parity <= '0';
+ DATA <= (others => '0');
+ VALID <= '0';
+ ERROR <= '0';
+ elsif rising_edge(CLK) then
+ -- Clear flags
+ VALID <= '0';
+ ERROR <= '0';
+
+ if clk_edge = '1' then
+ -- We have a new bit from the keyboard for processing
+ if bit_count = 0 then
+ -- Idle state, check for start bit (0) only and don't
+ -- start counting bits until we get it
+
+ parity <= '0';
+
+ if ps2_dat_in = '0' then
+ -- This is a start bit
+ bit_count <= bit_count + 1;
+ end if;
+ else
+ -- Running. 8-bit data comes in LSb first followed by
+ -- a single stop bit (1)
+ if bit_count < 10 then
+ -- Shift in data and parity (9 bits)
+ bit_count <= bit_count + 1;
+ shiftreg <= ps2_dat_in & shiftreg(shiftreg'high downto 1);
+ parity <= parity xor ps2_dat_in; -- Calculate parity
+ elsif ps2_dat_in = '1' then
+ -- Valid stop bit received
+ bit_count <= (others => '0'); -- back to idle
+ if parity = '1' then
+ -- Parity correct, submit data to host
+ DATA <= shiftreg(7 downto 0);
+ VALID <= '1';
+ else
+ -- Error
+ ERROR <= '1';
+ end if;
+ else
+ -- Invalid stop bit
+ bit_count <= (others => '0'); -- back to idle
+ ERROR <= '1';
+ end if;
+ end if;
+ end if;
+ end if;
+ end process;
+end ps2_intf_arch;