aboutsummaryrefslogtreecommitdiffstats
path: root/debugger.vhd
diff options
context:
space:
mode:
Diffstat (limited to 'debugger.vhd')
-rw-r--r--debugger.vhd300
1 files changed, 300 insertions, 0 deletions
diff --git a/debugger.vhd b/debugger.vhd
new file mode 100644
index 0000000..1d78bd5
--- /dev/null
+++ b/debugger.vhd
@@ -0,0 +1,300 @@
+-- BBC Micro for Altera DE1
+--
+-- Copyright (c) 2011 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 agreement from the author.
+--
+-- * License is granted for non-commercial use only. A fee may not be charged
+-- for redistributions as source code or in synthesized/hardware form without
+-- specific prior written agreement from the author.
+--
+-- 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.
+--
+--
+-- 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 IRQ in
+ nIRQ_IN : in std_logic;
+ -- Gated IRQ back out to CPU (no interrupts when single stepping)
+ nIRQ_OUT : out std_logic;
+
+ -- CPU
+ A_CPU : in std_logic_vector(15 downto 0);
+ R_nW : in std_logic;
+ SYNC : in std_logic;
+
+ -- Aux bus input for display in hex
+ AUX_BUS : in std_logic_vector(15 downto 0);
+
+ -- 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,modeAux);
+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;
+ -- Mask interrupt
+ nIRQ_OUT <= nIRQ_IN or not RUN;
+
+ -- Route selected address to display
+ a_display <= instr_addr when mode = modeAddress else
+ breakpoint when mode = modeBreak else
+ watchpoint when mode = modeWatch else
+ AUX_BUS when mode = modeAux 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 mode = modeAux or flash = '1' or digit /= "11") else "1111111";
+ DIGIT2 <= d2_display when (mode = modeAddress or mode = modeAux or flash = '1' or digit /= "10") else "1111111";
+ DIGIT1 <= d1_display when (mode = modeAddress or mode = modeAux or flash = '1' or digit /= "01") else "1111111";
+ DIGIT0 <= d0_display when (mode = modeAddress or mode = modeAux or flash = '1' or digit /= "00") else "1111111";
+
+ -- Show mode on LEDs
+ LED_BREAKPOINT <= '1' when mode = modeBreak or mode = modeAux else '0';
+ LED_WATCHPOINT <= '1' when mode = modeWatch or mode = modeAux 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;
+ elsif mode = modeWatch then
+ mode <= modeAux;
+ 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 => '1');
+ 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 => '1');
+ 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;
+
+ -- Once the CPU has run we can trigger a new halt
+ if CLKEN_IN = '1' then
+ resuming <= '0';
+ end if;
+
+ 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;
+ end if;
+ end process;
+end architecture;