summaryrefslogtreecommitdiffstats
path: root/zpu/hdl/tap
diff options
context:
space:
mode:
authorAlvaro <alvieboy@alvie.com>2011-09-01 12:13:53 +0100
committerAlvaro <alvieboy@alvie.com>2011-09-01 12:13:53 +0100
commit91e13ae045ee76c25b8883013d386beab3cb8086 (patch)
treeb09d846da68630a3424b291e81e401170a41caac /zpu/hdl/tap
parent3941564bb9cb1fa9faa9a3461074d06f2ef0dbe0 (diff)
downloadzpu-91e13ae045ee76c25b8883013d386beab3cb8086.zip
zpu-91e13ae045ee76c25b8883013d386beab3cb8086.tar.gz
jtag: Apply Martin Strubel JTAG implementation for ZPUjtagdbg
The current JTAG debugging capable ZPU implementation (VHDL) consists of: - A generic, device independent JTAG module (tck, tms, tdi, tdo, trst) - A TAP module, defining JTAG instruction and data registers - A few control lines to/from the core (request, execute, acknowledge, ready) and: * An emulation instruction register * A data exchange register - An enhanced ZPU small core state machine
Diffstat (limited to 'zpu/hdl/tap')
-rw-r--r--zpu/hdl/tap/README22
-rw-r--r--zpu/hdl/tap/jtagx.vhd254
-rw-r--r--zpu/hdl/tap/tap.vhd217
3 files changed, 493 insertions, 0 deletions
diff --git a/zpu/hdl/tap/README b/zpu/hdl/tap/README
new file mode 100644
index 0000000..07a1234
--- /dev/null
+++ b/zpu/hdl/tap/README
@@ -0,0 +1,22 @@
+Test Access Port implementation for the ZPU (alpha)
+----------------------------------------------------------------------------
+08/2011 Martin Strubel <hackfin@section5.ch>
+
+Brief:
+- Implements JTAG interface (on FPGA custom pins) for the zealot small core.
+ Zealot medium (and others) under scrutiny.
+- Important: Clock synchronization between TCK and core clock domain has
+ to be taken care of by the parenting module, in particular for the
+ emuexec_i pin. That means: From the emuexec rising edge signal from the TAP
+ you have to create a one core clock cycle wide emuexec_i pulse.
+- If you want to swap the debug interface, just write a new tapxxx.vhd
+- The software debug interface may change, and it may be different for various
+ implementations of the ZPU.
+ Possible solution:
+ * Create and register various ZPU core IDs (IDCODE instruction)
+ * Take care of the ZPU variant in the zpu emulation library:
+ $ZPU/zpu/sw/emulation/
+ * Likewise, handle various debug interfaces (direct/indirect JTAG, etc.)
+ * Implement different debug targets in gdbproxy, callable like
+ 'gdbproxy zpu_<special_interface_name>', and just use different
+ libzpuemu configurations.
diff --git a/zpu/hdl/tap/jtagx.vhd b/zpu/hdl/tap/jtagx.vhd
new file mode 100644
index 0000000..89c2075
--- /dev/null
+++ b/zpu/hdl/tap/jtagx.vhd
@@ -0,0 +1,254 @@
+----------------------------------------------------------------------------
+-- Simple JTAG controller, enhanced version
+--
+-- $Id: jtag.vhdl 35 2008-09-05 23:31:00Z strubi $
+--
+-- (c) 2005, 2006, 2011
+-- Martin Strubel // <hackfin@section5.ch>
+----------------------------------------------------------------------------
+
+-- Functionality:
+--
+-- This module implements a JTAG controller with a instruction register (IR)
+-- and a data register (DR).
+-- Data is clocked into the IR register MSB first,
+-- into the DR register LSB first.
+--
+-- The reason for this inconsistent behaviour is, that this controller
+-- allows variable sizes of data registers, depending on the IR value.
+--
+-- (Actually, the Blackfin CPU JTAG controller does it the same odd way)
+--
+-- The IR and DR register size is specified in the parameters:
+--
+-- IRSIZE (default 4)
+-- DRSIZE (default 8)
+--
+-- All special functionality must be encoded outside this module, using
+-- the IR values.
+-- There is one exception: The Instruction "1111" is reserved for the
+-- IR_BYPASS mode. In this mode, the TDI bit is passed onto TDO with a delay
+-- of one bit, according to the JTAG standard.
+--
+-- The design is tested using the JTAG library coming with the ICEbear
+-- USB JTAG adapter.
+--
+
+library ieee;
+use ieee.std_logic_1164.all;
+use IEEE.std_logic_unsigned.all;
+use IEEE.numeric_std.all; -- TO_INTEGER
+
+library work;
+use work.jtag.all;
+
+entity JtagController is
+ generic (IRSIZE : natural := 4;
+ DRSIZE : natural := 8);
+ port (
+ tck, -- Tap Clock
+ trst, -- Tap Reset
+ tms, -- Tap mode select
+ tdi : in std_logic; -- Tap data in
+ tdo : out std_logic; -- Tap data out
+ state : out jtag_state_type; -- JTAG machine state
+-- Data register input:
+ dr_in : in std_logic_vector (DRSIZE-1 downto 0);
+-- Configureable DR size:
+ msbpos : in bitpos_type;
+-- Data register output:
+ dr_out : out std_logic_vector (DRSIZE-1 downto 0);
+-- Instruction register:
+ ir_out : out std_logic_vector (IRSIZE-1 downto 0)
+ );
+end JtagController;
+
+architecture behaviour of JtagController is
+
+-- The only fixed instruction: All ones. Reserved for bypassing.
+ constant IR_BYPASS : std_logic_vector (IRSIZE-1 downto 0) :=
+ (others => '1');
+
+ signal mystate : jtag_state_type := TEST_LOGIC_RESET;
+ signal next_state : jtag_state_type;
+
+ signal s_dr : std_logic_vector (DRSIZE-1 downto 0);
+ signal s_ir : std_logic_vector (IRSIZE-1 downto 0) :=
+ (others => '1');
+
+ signal msb : bitpos_type;
+
+ -- Disabled: Buffered register
+ -- signal ir : std_logic_vector (IRSIZE-1 downto 0);
+
+begin
+
+nextstate_decode:
+ process (mystate, tms)
+ begin
+ case mystate is
+ when CAPTURE_DR =>
+ if (tms = '1') then
+ next_state <= EXIT1_DR;
+ else
+ next_state <= SHIFT_DR;
+ end if;
+ when CAPTURE_IR =>
+ if (tms = '1') then
+ next_state <= EXIT1_IR;
+ else
+ next_state <= SHIFT_IR;
+ end if;
+ when EXIT1_DR =>
+ if (tms = '1') then
+ next_state <= UPDATE_DR;
+ else
+ next_state <= PAUSE_DR;
+ end if;
+ when EXIT1_IR =>
+ if (tms = '1') then
+ next_state <= UPDATE_IR;
+ else
+ next_state <= PAUSE_IR;
+ end if;
+ when EXIT2_DR =>
+ if (tms = '1') then
+ next_state <= UPDATE_DR;
+ else
+ next_state <= SHIFT_DR;
+ end if;
+ when EXIT2_IR =>
+ if (tms = '1') then
+ next_state <= UPDATE_IR;
+ else
+ next_state <= SHIFT_IR;
+ end if;
+ when PAUSE_DR =>
+ if (tms = '1') then
+ next_state <= EXIT2_DR;
+ else
+ next_state <= PAUSE_DR;
+ end if;
+ when PAUSE_IR =>
+ if (tms = '1') then
+ next_state <= EXIT2_IR;
+ else
+ next_state <= PAUSE_IR;
+ end if;
+ when RUN_TEST_IDLE =>
+ if (tms = '1') then
+ next_state <= SELECT_DR;
+ else
+ next_state <= RUN_TEST_IDLE;
+ end if;
+ when SELECT_DR =>
+ if (tms = '1') then
+ next_state <= SELECT_IR;
+ else
+ next_state <= CAPTURE_DR;
+ end if;
+ when SELECT_IR =>
+ if (tms = '1') then
+ next_state <= TEST_LOGIC_RESET;
+ else
+ next_state <= CAPTURE_IR;
+ end if;
+ when SHIFT_DR =>
+ if (tms = '1') then
+ next_state <= EXIT1_DR;
+ else
+ next_state <= SHIFT_DR;
+ end if;
+ when SHIFT_IR =>
+ if (tms = '1') then
+ next_state <= EXIT1_IR;
+ else
+ next_state <= SHIFT_IR;
+ end if;
+ when TEST_LOGIC_RESET =>
+ if (tms = '1') then
+ next_state <= TEST_LOGIC_RESET;
+ else
+ next_state <= RUN_TEST_IDLE;
+ end if;
+ when UPDATE_DR =>
+ if (tms = '1') then
+ next_state <= SELECT_DR;
+ else
+ next_state <= RUN_TEST_IDLE;
+ end if;
+ when UPDATE_IR =>
+ if (tms = '1') then
+ next_state <= SELECT_DR;
+ else
+ next_state <= RUN_TEST_IDLE;
+ end if;
+ when others =>
+ end case;
+ end process;
+
+-- When we're in BYPASS, use MSB 0
+ msb <= 0 when s_ir = IR_BYPASS else msbpos;
+
+tdo_encode:
+ process (mystate, s_ir, s_dr)
+ begin
+ case mystate is
+ when SHIFT_IR =>
+ tdo <= s_ir(0); -- Shift out LSB
+ when SHIFT_DR =>
+ tdo <= s_dr(msb); -- Take MSB
+ when others =>
+ tdo <= '1';
+ end case;
+ end process;
+
+state_advance:
+ process (tck, trst)
+ begin
+ if (trst = '0') then
+ mystate <= TEST_LOGIC_RESET;
+ elsif rising_edge(tck) then
+ mystate <= next_state; -- Advance to next state
+ end if;
+ end process;
+
+process_ir_dr:
+ process (tck)
+ begin
+ if rising_edge(tck) then
+-- takes effect when entering the concerning state
+ case next_state is
+ -- When resetting, go into BYPASS mode
+ when TEST_LOGIC_RESET =>
+ s_ir <= (others => '1');
+ s_dr <= (others => '0');
+ when others =>
+ end case;
+
+-- Mystate is the current state, process takes effect on rising TCK when IN
+-- the concerning state.
+ case mystate is
+ when SHIFT_IR =>
+ s_ir <= tdi & s_ir(IRSIZE-1 downto 1); -- Shift in from MSB
+ when SHIFT_DR =>
+ s_dr <= s_dr(DRSIZE-2 downto 0) & tdi; -- likewise from LSB
+ when CAPTURE_DR =>
+-- We could move this BYPASS check to a higher level module. But since
+-- it's a reserved command, we leave it in here.
+ if (s_ir /= IR_BYPASS) then
+ s_dr <= dr_in; -- Latch!
+ end if;
+ when others =>
+ end case;
+ end if;
+ end process;
+
+ -- always assign state to output
+ -- We assign nextstate which is valid on the rising_edge of tck
+ state <= next_state;
+
+ ir_out <= s_ir;
+ dr_out <= s_dr;
+
+end behaviour;
diff --git a/zpu/hdl/tap/tap.vhd b/zpu/hdl/tap/tap.vhd
new file mode 100644
index 0000000..41dc07b
--- /dev/null
+++ b/zpu/hdl/tap/tap.vhd
@@ -0,0 +1,217 @@
+-- Example Test Access Port (TAP) controller implementation
+-- (c) 2005-2011 Martin Strubel <hackfin@section5.ch>
+
+-- General behaviour summary:
+
+-- * JtagController entity decodes the TMS and TDI sequences
+-- * IR and DR are decoded by this TAP controller
+-- * Signals to a CPU core are generated by the TAP. See
+-- "Core emulation signals" below. These are mapped into a control register:
+-- emuctl [W]
+-- * Signals from a CPU core are mapped into a status register:
+-- emustat [R]
+
+-- Note: Fixed size IR register is clocked in LSB first,
+-- Variable size DR register is clocked in MSB first.
+
+-- The default EMUIR value on a state machine reset is set to
+-- INS_NOP, which should be defined properly by the parent module.
+-- This is necessary for the core to not run any spurious command when
+-- a breakpoint was hit and the JTAG state machine enters Run-Test-Idle.
+
+-- This TAP is supported by a generic software library as part of the
+-- ICEbear software suite.
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library work;
+use work.jtag.all;
+-- TAP register definitions (generated):
+use work.tap_registers.all;
+
+entity tap is
+ generic (EMUDAT_SIZE : natural := 32;
+ EMUIR_SIZE : natural := 8;
+ IDCODE : std_logic_vector(32-1 downto 0) := x"deadbeef";
+ INS_NOP : std_logic_vector(8-1 downto 0) := x"00");
+ port (
+ -- JTAG signals:
+ tck, trst, tms, tdi : in std_logic;
+ tdo : out std_logic;
+ -- Core <-> TAP signals:
+ core_reset : out std_logic; -- Reset core logic
+ emuexec : out std_logic; -- Execute opcode on rising edge
+ emurequest : out std_logic; -- Emulation request to core
+ emuack : in std_logic; -- Core has acknowledged EMULATION request
+ emurdy : in std_logic; -- Core ready to execute next instruction
+ pulse : in std_logic; -- Pulse event counter
+ -- PC of possibly running core. Allows to access PC without
+ -- entering emulation.
+ dbgpc : in std_logic_vector(EMUDAT_SIZE-1 downto 0); -- PC
+
+ -- Extra status bits, core dependent
+ exstat : in std_logic_vector(7 downto 0);
+
+ emudata_i : in std_logic_vector(EMUDAT_SIZE-1 downto 0);
+ emudata_o : out std_logic_vector(EMUDAT_SIZE-1 downto 0);
+ -- Not implemented in this version
+ -- emudat_wr : in std_logic;
+ -- emudat_rd : in std_logic;
+ emuir : out std_logic_vector(EMUIR_SIZE-1 downto 0)
+ );
+end tap;
+
+architecture behaviour of tap is
+
+ -- Use generated registers from tap_pkg.vhd
+
+ -- Note: all ones is always reserved for BYPASS
+ -- all zeros is normally reserved for EXTEST
+
+ signal jtag_state : jtag_state_type;
+
+ signal exec : std_logic := '0';
+
+ -- Emulation control/status registers:
+ signal emustat : std_logic_vector(16-1 downto 0);
+ signal emuctl : std_logic_vector(16-1 downto 0) := x"0000";
+
+ -- Core emulation signals:
+
+ signal dr_in : std_logic_vector(EMUDAT_SIZE-1 downto 0);
+ signal dr_out : std_logic_vector(EMUDAT_SIZE-1 downto 0);
+
+ signal count1 : unsigned(32-1 downto 0);
+ signal count1_reset : std_logic;
+ signal count2 : unsigned(16-1 downto 0);
+
+ signal ir : std_logic_vector(4-1 downto 0);
+
+ -- Position of MSB of data register when not in BYPASS
+ -- (defines length of DR register). See also jtag_config.vhd
+ signal msbpos : bitpos_type := 31;
+
+ -- Emulation auxiliaries:
+
+ -- Emulation data register for exchange between core and JTAG:
+ signal emudat_i : std_logic_vector(EMUDAT_SIZE-1 downto 0);
+ signal emudat_o : std_logic_vector(EMUDAT_SIZE-1 downto 0);
+-- These signals are just stubs. Not implemented in this version.
+ -- signal emudat_rxf : std_logic; -- Receive full
+ -- signal emudat_txe : std_logic := '0'; -- Transmit empty
+ -- signal emudat_ovr : std_logic := '0'; -- Overrun
+ -- signal emudat_unr : std_logic := '0'; -- Underrun
+
+begin
+i_jtag : JtagController
+ port map (
+ tck => tck,
+ tms => tms,
+ tdi => tdi,
+ tdo => tdo,
+ trst => trst,
+ state => jtag_state,
+ dr_out => dr_out,
+ msbpos => msbpos,
+ dr_in => dr_in,
+ ir_out => ir
+ );
+
+-- Select DR register according to supported IRs
+-- We sample this with tck to avoid gated clock issues
+
+select_dr:
+ process (tck)
+ begin
+ if rising_edge(tck) then
+ case ir is
+ when TAP_IDCODE =>
+ dr_in <= IDCODE;
+ msbpos <= 31;
+ when TAP_EMUDATA =>
+ dr_in <= emudata_i;
+ msbpos <= emudata_i'length - 1;
+ when TAP_EMUSTAT =>
+ dr_in(emustat'length-1 downto 0) <= emustat;
+ msbpos <= emustat'length - 1;
+ when TAP_DBGPC =>
+ dr_in <= dbgpc;
+ msbpos <= dbgpc'length - 1;
+ when TAP_COUNT1 =>
+ dr_in(count1'length-1 downto 0) <= std_logic_vector(count1);
+ msbpos <= count1'length - 1;
+ when TAP_COUNT2 =>
+ dr_in(count2'length-1 downto 0) <= std_logic_vector(count2);
+ msbpos <= count2'length - 1;
+ when others =>
+ dr_in <= (others => 'X');
+ msbpos <= 31;
+ end case;
+ end if;
+ end process;
+
+
+-- NOTE: action is being taken when ENTERING the concerning state
+-- on rising edge of tck.
+
+-- exec is the signal sent to the core. It is like an IRQ event, thus
+-- it must be properly treated as an exception (edge sensitive)
+-- inside the core logic.
+
+decode_scanchain_w:
+ process (tck)
+ begin
+ if rising_edge(tck) then
+ count1_reset <= '0';
+ exec <= '0';
+ case jtag_state is
+ when UPDATE_DR =>
+ if ir = TAP_EMUCTRL then
+ emuctl <= dr_out(15 downto 0);
+ elsif ir = TAP_EMUDATA then
+ emudat_o <= dr_out;
+ elsif ir = TAP_EMUIR then
+ emuir <= dr_out(EMUIR_SIZE-1 downto 0);
+ end if;
+ when TEST_LOGIC_RESET =>
+ count1_reset <= '1';
+ count2 <= (others => '0');
+ emuctl <= (others => '0');
+ emudat_o <= (others => '0');
+-- Important to reset the EMUIR to NOP for sane JTAG operation:
+ emuir <= INS_NOP;
+ when RUN_TEST_IDLE =>
+ count2 <= count2 + 1;
+ exec <= '1';
+ when others =>
+ end case;
+ end if;
+ end process;
+
+pulse_count:
+ process (count1_reset, pulse)
+ begin
+ if count1_reset = '1' then
+ count1 <= (others => '0');
+ elsif rising_edge(pulse) then
+ count1 <= count1 + 1;
+ end if;
+ end process;
+
+emuexec <= exec;
+emurequest <= emuctl(0);
+core_reset <= emuctl(15);
+
+-- Registers:
+emudata_o <= emudat_o;
+
+-- FIXME:
+-- These mappings should probably move to glue between TAP and core
+-- to be more generic.
+
+emustat <= exstat &
+ "0000" & "00" & emurdy & emuack;
+
+end behaviour;
OpenPOWER on IntegriCloud