diff options
author | Alvaro <alvieboy@alvie.com> | 2011-09-01 12:13:53 +0100 |
---|---|---|
committer | Alvaro <alvieboy@alvie.com> | 2011-09-01 12:13:53 +0100 |
commit | 91e13ae045ee76c25b8883013d386beab3cb8086 (patch) | |
tree | b09d846da68630a3424b291e81e401170a41caac /zpu/hdl/tap | |
parent | 3941564bb9cb1fa9faa9a3461074d06f2ef0dbe0 (diff) | |
download | zpu-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/README | 22 | ||||
-rw-r--r-- | zpu/hdl/tap/jtagx.vhd | 254 | ||||
-rw-r--r-- | zpu/hdl/tap/tap.vhd | 217 |
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; |