From 71c7f543c5b29112ba7598a4f59370d5f94e4bdb Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 17 Jul 2016 15:44:30 -0700 Subject: [PATCH] JTAG: Add TAP state management. Not all that happy with the implementation, but it's doing the job for now. --- firmware/common/jtag_tap.cpp | 266 +++++++++++++++++++++++++++++++++++ firmware/common/jtag_tap.hpp | 155 ++++++++++++++++++++ 2 files changed, 421 insertions(+) create mode 100644 firmware/common/jtag_tap.cpp create mode 100644 firmware/common/jtag_tap.hpp diff --git a/firmware/common/jtag_tap.cpp b/firmware/common/jtag_tap.cpp new file mode 100644 index 00000000..57541b85 --- /dev/null +++ b/firmware/common/jtag_tap.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "jtag_tap.hpp" + +#include "utility.hpp" + +#include + +namespace jtag { +namespace tap { + +size_t bits_t::length() const { + return count; +} + +bits_t::operator bool() const { + return (count > 0); +} + +bool bits_t::operator[](const size_t index) const { + if( p && (index < count) ) { + const auto n = bytes() * 8 - index - 1; + const auto byte = n >> 3; + const auto bit = (n ^ 7) & 7; + return (p[byte] >> bit) & 1; + } else { + return default_value; + } +} + +size_t bits_t::bytes() const { + return (count + 7) >> 3; +} + +#if JTAG_TAP_DEBUG +static char nibble_to_hex_char(const uint8_t v) { + if( v < 0xa ) { + return 0x30 + v; + } else { + return 0x57 + v; + } +} + +std::string to_string(const bits_t& bits) { + std::string s { std::to_string(bits.length()) + "/0x" }; + for(size_t i=0; i> 4) & 0xf); + s += nibble_to_hex_char((bits.p[i] >> 0) & 0xf); + } + return s; +} +#endif + +namespace { + +using route_t = uint16_t; + +struct entry_t { + state_t tms[2]; + route_t route; +}; +static_assert(sizeof(entry_t) == 4, "Unexpected size of entry_t"); + +using table_t = std::array; +static_assert(sizeof(table_t) == (16 * 4), "Unexpected size of table_t"); + +const table_t table { { + { state_t::run_test_idle, state_t::test_logic_reset, 0b0000000000000001 }, // test_logic_reset test_logic_reset => 1, others => 0 + { state_t::run_test_idle, state_t::select_dr_scan, 0b1111111111111101 }, // run_test_idle run_test_idle => 0, others => 1 + { state_t::capture_dr, state_t::select_ir_scan, 0b1111111000000001 }, // select_dr_scan run_test_idle..update_dr => 0, others => 1 + { state_t::shift_dr, state_t::exit1_dr, 0b1111111111101111 }, // capture_dr shift_dr => 0, others => 1 + { state_t::shift_dr, state_t::exit1_dr, 0b1111111111101111 }, // shift_dr shift_dr => 0, others => 1 + { state_t::pause_dr, state_t::update_dr, 0b1111111100111111 }, // exit1_dr pause_dr, exit2_dr => 0, others => 1 + { state_t::pause_dr, state_t::exit2_dr, 0b1111111110111111 }, // pause_dr pause_dr => 0, others => 1 + { state_t::shift_dr, state_t::update_dr, 0b1111111111101111 }, // exit2_dr shift_dr => 0, others => 1 + { state_t::run_test_idle, state_t::select_dr_scan, 0b1111111111111101 }, // update_dr run_test_idle => 0, others => 1 + { state_t::capture_ir, state_t::test_logic_reset, 0b0000000000000001 }, // select_ir_scan test_logic_reset => 1, others => 0 + { state_t::shift_ir, state_t::exit1_ir, 0b1111011111111111 }, // capture_ir shift_ir => 0, others => 1 + { state_t::shift_ir, state_t::exit1_ir, 0b1111011111111111 }, // shift_ir shift_ir => 0, others => 1 + { state_t::pause_ir, state_t::update_ir, 0b1001111111111111 }, // exit1_ir pause_ir, exit2_ir => 0, others => 1 + { state_t::pause_ir, state_t::exit2_ir, 0b1101111111111111 }, // pause_ir pause_ir => 0, others => 1 + { state_t::shift_ir, state_t::update_ir, 0b1111011111111111 }, // exit2_ir shift_ir => 0, others => 1 + { state_t::run_test_idle, state_t::select_dr_scan, 0b1111111111111101 }, // update_ir run_test_idle => 0, others => 1 +} }; + +const std::array state_name { { + "test_logic_reset", + "run_test_idle", + "select_dr_scan", + "capture_dr", + "shift_dr", + "exit1_dr", + "pause_dr", + "exit2_dr", + "update_dr", + "select_ir_scan", + "capture_ir", + "shift_ir", + "exit1_ir", + "pause_ir", + "exit2_ir", + "update_ir", +} }; + +const std::array state_long_name { { + "Test-Logic-Reset", + "Run-Test/Idle", + "Select-DR-Scan", + "Capture-DR", + "Shift-DR", + "Exit1-DR", + "Pause-DR", + "Exit2-DR", + "Update-DR", + "Select-IR-Scan", + "Capture-IR", + "Shift-IR", + "Exit1-IR", + "Pause-IR", + "Exit2-IR", + "Update-IR", +} }; + +const entry_t& entry(const state_t state) { + return table[toUType(state)]; +} + +} /* namespace */ + +const char* c_str(const state_t state) { + return state_name[toUType(state)]; +} + +/* TAPState **************************************************************/ + +state_t TAPState::state() const { + return _state; +} + +void TAPState::advance(const bool tms) { + _state = entry(_state).tms[tms]; +} + +bool TAPState::advance_toward(const state_t desired_state) const { + return (entry(_state).route >> toUType(desired_state)) & 1; +} + +/* TAPMachine ************************************************************/ + +void TAPMachine::set_run_test(const uint32_t value) { + _run_test = value; +} + +void TAPMachine::set_repeat(const uint8_t value) { + _repeat = value; +} + +void TAPMachine::set_end_ir(const state_t state) { + _end_ir = state; +} + +void TAPMachine::set_end_dr(const state_t state) { + _end_dr = state; +} + +bool TAPMachine::shift_ir(const bits_t& tdi_value, const bits_t& tdo_expected, const bits_t& tdo_mask) { + return shift_data(tdi_value, tdo_expected, tdo_mask, state_t::shift_ir, _end_ir, _run_test); +} + +bool TAPMachine::shift_dr(const bits_t& tdi_value, const bits_t& tdo_expected, const bits_t& tdo_mask) { + return shift_data(tdi_value, tdo_expected, tdo_mask, state_t::shift_dr, _end_dr, _run_test); +} + +void TAPMachine::state(const state_t state) { + if( state == state_t::test_logic_reset ) { + for(int i=0; i<5; i++) { + clock(1); + } + } else { + advance_to_state(state); + } +} + +void TAPMachine::wait(const state_t wait_state, const state_t end_state, const uint32_t wait_time) { + advance_to_state(wait_state); + delay_us(wait_time); + advance_to_state(end_state); +} + +bool TAPMachine::clock(const bool tms, const bool tdi) { + tap.advance(tms); + return target.clock(tms, tdi); +} + +void TAPMachine::advance_to_state(const state_t desired_state) { + while( tap.state() != desired_state ) { + const auto tms = tap.advance_toward(desired_state); + clock(tms); + } +} + +void TAPMachine::delay_us(const uint32_t microseconds) { + target.delay(microseconds); +} + +void TAPMachine::shift_start(const state_t state) { + advance_to_state(state); +} + +bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms) { + if( tdo_expected.length() != tdo_mask.length() ) { + return false; + } + if( tdo_expected && (tdi.length() != tdo_expected.length()) ) { + return false; + } + + auto tdo_error = false; + for(uint32_t i=0; i +#include + +namespace jtag { +namespace tap { + +class bits_t { +public: + constexpr bits_t( + ) : p { nullptr }, + count { 0 } + { + } + + constexpr bits_t( + const size_t count, + const bool default_value = true + ) : p { nullptr }, + count { count }, + default_value { default_value } + { + } + + constexpr bits_t( + const uint8_t* const p, + const size_t count + ) : p { p }, + count { count } + { + } + + size_t length() const; + + explicit operator bool() const; + + bool operator[](const size_t index) const; + +private: + const uint8_t* p { nullptr }; + size_t count { 0 }; + bool default_value { false }; + + size_t bytes() const; +}; + +enum class state_t : uint8_t { + /* Ordinal values are important for "routes" values, and to match XSVF definitions. */ + test_logic_reset = 0, + run_test_idle = 1, + select_dr_scan = 2, + capture_dr = 3, + shift_dr = 4, + exit1_dr = 5, + pause_dr = 6, + exit2_dr = 7, + update_dr = 8, + select_ir_scan = 9, + capture_ir = 10, + shift_ir = 11, + exit1_ir = 12, + pause_ir = 13, + exit2_ir = 14, + update_ir = 15, +}; + +class TAPState { +public: + constexpr TAPState( + ) : _state { state_t::test_logic_reset } + { + } + + state_t state() const; + void advance(const bool tms); + bool advance_toward(const state_t desired_state) const; + +private: + state_t _state; +}; + +class TAPMachine { +public: + constexpr TAPMachine( + jtag::Target& target + ) : target { target } + { + } + + void set_run_test(const uint32_t value); + void set_repeat(const uint8_t value); + void set_end_ir(const state_t state); + void set_end_dr(const state_t state); + + bool shift(const bits_t& tdi, const bool end_tms) { + return shift(tdi, {}, {}, end_tms); + } + + bool shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms); + + bool shift_ir(const bits_t& tdi_value, const bits_t& tdo_expected = {}, const bits_t& tdo_mask = {}); + bool shift_dr(const bits_t& tdi_value, const bits_t& tdo_expected = {}, const bits_t& tdo_mask = {}); + + void state(const state_t state); + + void wait(const state_t wait_state, const state_t end_state, const uint32_t wait_time); + +private: + jtag::Target& target; + TAPState tap { }; + + uint32_t _run_test { 0 }; + uint8_t _repeat { 0 }; + state_t _end_ir { }; + state_t _end_dr { }; + + bool clock(const bool tms, const bool tdi=false); + void advance_to_state(const state_t desired_state); + void delay_us(const uint32_t microseconds); + + void shift_start(const state_t state); + void shift_end(const state_t end_state, const uint32_t end_delay); + + bool shift_data(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const state_t state, const state_t end_state, const uint32_t end_delay); +}; + +} /* namespace tap */ +} /* namespace jtag */ + +#endif/*__JTAG_TAP_H__*/