From 91e13ae045ee76c25b8883013d386beab3cb8086 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Thu, 1 Sep 2011 12:13:53 +0100 Subject: jtag: Apply Martin Strubel JTAG implementation for ZPU 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 --- zpu/hdl/tap/jtagx.vhd | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 zpu/hdl/tap/jtagx.vhd (limited to 'zpu/hdl/tap/jtagx.vhd') 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 // +---------------------------------------------------------------------------- + +-- 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; -- cgit v1.1