aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Stirling <opensource@mikestirling.co.uk>2011-07-29 21:14:17 +0100
committerMike Stirling <opensource@mikestirling.co.uk>2011-07-29 21:14:17 +0100
commitabb341bccf06bca03f56d2bc7612b2f81721b454 (patch)
tree5724ccb6d2321e7c58140779f81207aea0311bec
parent3ea54eb8b11e87333ad809c7bbfd9537c2fc8250 (diff)
downloadfpga-bbc-abb341bccf06bca03f56d2bc7612b2f81721b454.tar.gz
fpga-bbc-abb341bccf06bca03f56d2bc7612b2f81721b454.tar.bz2
fpga-bbc-abb341bccf06bca03f56d2bc7612b2f81721b454.zip
Work in progress. Added PS/2 keyboard interface, hardware debugger. Interrupt test output to 6522 (remove later). Fixed error in pin assignments that blocked access to Flash chip select. Added reading sideways ROMs from external Flash (all but BASIC masked off for now).
-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;