JTAG Guide for AVR Microcontrollers
JTAG Guide for AVR Microcontrollers
The Joint Test Access Group (JTAG) standard, formally known as IEEE Std 1149.1, represents
one of the most significant and enduring technologies in the field of electronics testing and
embedded systems development. Initially conceived to address the challenges of testing
increasingly dense printed circuit board assemblies (PCBAs), its robust and extensible
architecture has allowed it to evolve into a ubiquitous interface for in-system programming
and on-chip debugging of microcontrollers and other complex integrated circuits.1 A thorough
understanding of its fundamental principles—the physical interface, the governing state
machine, and its core operational modes—is essential for any engineer working with modern
embedded hardware.
The JTAG standard emerged in the 1980s as a direct response to a growing crisis in
electronics manufacturing. The advent of surface-mount technology (SMT) and complex
integrated circuit packages, such as Ball Grid Arrays (BGAs), rendered traditional
"bed-of-nails" in-circuit testing methods impractical or impossible.3 These test fixtures
required physical access to every net on a PCBA, a requirement that could no longer be met
as components and traces became smaller and were placed on both sides of a board.
To solve this problem, a consortium of electronics companies formed the Joint Test Access
Group to develop a standardized method for testing board-level interconnects without
requiring direct physical probing. The result was a specification that was later formalized as
the IEEE 1149.1 "Standard Test Access Port and Boundary-Scan Architecture".1 The core
innovation was the concept of a "boundary-scan chain," a serial shift register built into the
periphery of each compliant IC, sitting between the core logic and the physical I/O pins.3 By
connecting these chains across multiple ICs on a board, a test system could gain virtual
access to the pins, allowing it to control their logic levels and observe their inputs to verify
connectivity and detect faults like shorts and opens.3
While boundary-scan testing remains a critical application, the true genius of the JTAG
standard lies in its architectural flexibility. The standard defines a generic serial
communication protocol and a state machine to access on-chip registers, but it does not
rigidly define the function of all possible registers.1 This extensibility allowed silicon vendors to
add their own custom, device-specific registers and logic accessible through the same
standard JTAG port. This evolution transformed JTAG from a pure manufacturing test tool into
a powerful, multi-purpose interface for developers. Its capabilities expanded to include
programming on-chip non-volatile memory (such as Flash and EEPROM) and, most
importantly for embedded development, providing a gateway to sophisticated on-chip debug
(OCD) modules.2 These OCD modules, which are proprietary to each chip manufacturer, use
the JTAG port as a transport layer to provide deep, real-time control over the CPU, enabling
functions like setting breakpoints, single-stepping through code, and inspecting memory and
registers.1
The physical connection between an external JTAG debugger (often called a probe or dongle)
and the target device is established through the Test Access Port (TAP). The IEEE 1149.1
standard defines a minimal set of signals required for this interface, ensuring interoperability
between compliant devices and tools.1
The JTAG TAP consists of four mandatory signals and one optional signal:
● TCK (Test Clock): This is the primary clock signal for JTAG operations. It is driven by the
JTAG debugger and synchronizes the operation of the TAP controller's state machine and
the shifting of data through the instruction and data registers. All JTAG state transitions
and data captures occur relative to the edges of TCK.1
● TMS (Test Mode Select): This is the main control signal used to navigate the JTAG state
machine. The debugger drives TMS, and its logic level is sampled by the target device on
the rising edge of TCK. The sequence of 1s and 0s on TMS dictates the path taken
through the 16 states of the TAP controller, thereby controlling all JTAG operations.1
● TDI (Test Data Input): This is the serial data input line to the target device. Data is
shifted into the selected on-chip register (either the Instruction Register or a Data
Register) from this pin, one bit at a time, on the rising edge of TCK.1
● TDO (Test Data Output): This is the serial data output line from the target device. Data
is shifted out of the selected on-chip register to this pin. The data on TDO typically
changes on the falling edge of TCK to ensure the debugger can reliably sample it on the
subsequent rising edge.1
● TRST* (Test Reset): This is an optional, active-low signal that provides an asynchronous
means of resetting the TAP controller to its initial state (Test-Logic-Reset). While optional,
its presence can simplify the process of synchronizing the debugger with the target's
JTAG logic. Many devices, including some AVRs, do not implement this pin and rely solely
on the TMS signal sequence to achieve a reset state.1
These pins on the target IC connect to an internal hardware block, sometimes referred to as
the Debug and Test Access Block (DTAB), which contains the TAP controller and the
associated registers.2 In a system with multiple JTAG-compliant devices, their TAPs can be
connected in a "daisy-chain" configuration. The TDO of the first device is connected to the
TDI of the second, and so on, forming a single, long shift register path. The TCK and TMS
signals are typically connected to all devices in parallel.2
The core of all JTAG operations is the TAP controller, a 16-state finite state machine (FSM)
defined by the IEEE 1149.1 standard. Every action performed via the JTAG port—from loading
an instruction to shifting data—is a direct result of navigating this state machine. The path
through the FSM is controlled exclusively by the value of the TMS signal on the rising edge of
each TCK pulse.1
The 16 states are organized into two main branches: one for accessing the Instruction
Register (IR) and one for accessing the currently selected Data Register (DR).
To perform any operation, the debugger must drive a precise sequence on the TMS line. For
example, to navigate from the Test-Logic-Reset state to the Shift-IR state to load a new
instruction, the debugger must apply the TMS sequence 0, 1, 1, 0, 0 on five consecutive rising
edges of TCK.1 This deterministic, state-based control is the foundation of JTAG's reliability
and functionality.
The JTAG architecture is built around a simple yet powerful concept: a single Instruction
Register (IR) controls which one of several Data Registers (DRs) is accessible at any given
time.1 The process involves two distinct phases:
1. Instruction Scan: The TAP controller is navigated to the Shift-IR state. The debugger
then shifts a specific binary opcode into the IR via the TDI pin. Once the entire opcode is
loaded, the controller is moved to the Update-IR state, which makes the new instruction
active.2
2. Data Scan: The newly loaded instruction configures the JTAG logic to select a specific
Data Register and place it in the serial path between TDI and TDO. The TAP controller is
then navigated to the Shift-DR state, allowing the debugger to shift data into or out of
this selected DR.2
The IEEE standard defines several mandatory instructions and their corresponding Data
Registers, while also allowing for manufacturer-specific additions. The most common
standard instructions include:
● BYPASS: This instruction selects a single-bit DR called the Bypass Register. Its purpose is
to effectively "bypass" the main data registers of a device in a daisy chain. This allows a
debugger to communicate with a specific device in the chain without having to shift data
through the potentially long boundary-scan registers of all other devices.2
● IDCODE: This instruction selects the 32-bit Device ID Register. When this DR is read, it
returns a unique identifier containing the manufacturer's code, the part number, and the
version of the silicon. This allows tools to automatically detect and verify the device they
are connected to.4
● EXTEST: This instruction selects the Boundary-Scan Register (BSR). It is used to test the
interconnects external to the IC. The BSR disconnects the core logic from the I/O pins,
allowing the debugger to drive values out onto the pins (via TDI) and capture the values
present on other pins (via TDO) to test PCB traces.3
The flexible IR/DR architecture has enabled JTAG to become a cornerstone of the embedded
development lifecycle, serving three primary functions:
1. Boundary-Scan Testing: In a manufacturing context, the EXTEST instruction is used to
verify the integrity of the PCBA. By controlling the I/O pins of JTAG-enabled devices,
automated test equipment can check for short circuits between traces, open circuits
(such as unsoldered pins), and missing pull-up/pull-down resistors. This greatly reduces
the need for expensive physical test fixtures and improves fault diagnostics.3
2. In-System Programming (ISP): Many microcontrollers, FPGAs, and CPLDs use the JTAG
port as a primary method for programming their internal non-volatile memories. The
manufacturer defines custom JTAG instructions that provide access to the memory
controllers, allowing a debugger to erase, program, and verify the device's firmware
directly on the board.3
3. On-Chip Debugging (OCD): This is the most valuable application for firmware
developers. Chip manufacturers extend the JTAG standard by implementing a complex,
device-specific debug module inside the MCU. The JTAG TAP serves as the
communication gateway to this module.1 By loading custom JTAG instructions, a
developer's IDE can halt the processor, execute instructions one at a time, inspect and
modify the contents of CPU registers and system memory, and set hardware or software
breakpoints. This provides deep insight into the real-time behavior of the code running
on the physical hardware, a capability that is indispensable for efficient debugging.4
The enduring relevance of the JTAG standard, therefore, stems not from a rigid,
all-encompassing set of functions, but from its architectural design as a generic and
extensible serial communication protocol. This framework has allowed silicon vendors to build
powerful, proprietary on-chip debug modules on top of a standardized transport layer. The
JTAG TAP acts as a standardized gateway to these device-specific features. This explains the
vast differences in debug capabilities between, for example, an ARM core and an AVR MCU,
despite both using a "JTAG" port. The underlying transport mechanism is the same, but the
proprietary logic they connect to is vastly different.
While the JTAG standard provides a universal framework, its specific implementation within
the Microchip AVR family of microcontrollers has unique characteristics, requirements, and
quirks. For developers working with these popular 8-bit MCUs, a detailed understanding of
which devices support JTAG, how to enable and manage the interface via fuse bits, and the
interplay between JTAG and standard I/O functionality is critical for successful hardware
design and debugging.
The JTAG interface is not universally available across the entire AVR portfolio. Its inclusion is
largely a feature of older and physically larger devices, where the pin count overhead of the
4-wire TAP was less of a design constraint. It is most commonly found on devices with 40 or
more pins.7
A critical detail for hardware designers is that the JTAG signals are multiplexed with
general-purpose I/O pins. The specific port used varies between devices. For instance, on the
widely used ATmega16 and ATmega32, the JTAG interface is mapped to PORTC, specifically
pins PC2 (TCK), PC3 (TMS), PC4 (TDO), and PC5 (TDI).6 However, on a device like the
ATmega162, the interface is shifted to pins PC4 through PC7.19 Failure to consult the
device-specific datasheet to identify the correct pins is a common source of design errors.
The following table provides a non-exhaustive list of representative AVR devices that support
the JTAG interface.
Perhaps the most crucial and often frustrating aspect of the AVR JTAG implementation is its
reliance on non-volatile fuse bits for activation. Unlike some modern architectures where the
debug port is always available, on an AVR, the JTAG interface can be completely disabled at
the hardware level. Two fuses are central to this system: JTAGEN and OCDEN.20
● JTAGEN (JTAG Enable): This is the master switch for the JTAG TAP. The JTAGEN fuse
must be programmed (which, for AVR fuses, means being set to a value of 0) to enable
the JTAG hardware and connect the TDI, TDO, TMS, and TCK signals to their respective
pins. By default, AVR microcontrollers are shipped from the factory with the JTAGEN fuse
programmed, making the interface available out of the box.19 However, developers often
unprogram (set to 1) this fuse to reclaim the four JTAG pins for use as standard GPIO,
especially in pin-constrained applications. This act renders the JTAG port completely
non-functional.19
● OCDEN (On-Chip Debug Enable): This fuse specifically enables the on-chip debug
system. While JTAGEN enables the physical port, OCDEN activates the internal logic that
allows for breakpoints, single-stepping, and other debug functions. For a full debugging
session to be possible, both JTAGEN and OCDEN must be programmed.20 A key
convenience feature of modern IDEs like Microchip Studio is that they typically handle the
OCDEN fuse automatically. When a debug session is initiated, the IDE will use the JTAG
interface to check the state of OCDEN and program it if it is not already enabled,
shielding the user from this manual step.19
Even when the JTAGEN fuse is programmed, developers may still need to use the JTAG pins
for general-purpose I/O at certain times in their application. To accommodate this without
constantly reprogramming fuses, AVRs provide a software control mechanism through the
JTD (JTAG Test Disabled) bit in the MCUCR (MCU Control Register).22
Setting the JTD bit to 1 disables the JTAG interface and allows the corresponding pins to
function as normal GPIO as defined by their DDR and PORT registers. This provides a dynamic
way to switch between debugging and I/O functionality. However, to prevent accidental
changes to this critical bit (for example, by a runaway pointer), the hardware requires a
specific timed sequence to modify it. To change the JTD bit, the application software must
write the desired value to the MCUCR register twice within four CPU clock cycles.25
Beyond the standard IEEE 1149.1 instructions, AVR microcontrollers implement a set of custom
instructions to control their proprietary on-chip debug (OCD) module and other core
functions. These instructions are loaded into the IR just like the standard ones. A key example
is the AVR_RESET instruction (opcode 0xC), which allows the JTAG debugger to force the
target MCU into a reset state and hold it there, or to release it to begin program execution.6
The OCD module itself is the "device-specific debug engine" that provides the powerful
debugging features developers rely on.1 It is a separate block of logic within the MCU that can
take control of the CPU. It contains its own set of internal registers, accessible via JTAG data
scans, that are used to configure breakpoints, control execution flow (run, halt, step), and
read out the state of the CPU's program counter and general-purpose registers.5 For example,
the OCD module contains Program Store Breakpoint registers (PSB0, PSB1) where the
debugger can write specific memory addresses. When the CPU's program counter matches a
value stored in an enabled breakpoint register, the OCD module will automatically halt the
CPU and notify the debugger.6 It is this tight integration between the JTAG TAP and the OCD
module that enables true source-level debugging on AVR hardware.
JTAG is a powerful and robust interface for on-chip debugging, but it is just one of several
methods available for programming and debugging AVR microcontrollers. Over the years, as
Microchip (formerly Atmel) developed new device families with different design
constraints—particularly lower pin counts and smaller packages—a variety of specialized
interfaces were introduced. This evolution has resulted in a fragmented landscape of options,
each with distinct advantages, limitations, and use cases. A strategic understanding of this
landscape is crucial for selecting the right tools and design approach for a given project.
JTAG (Joint Test Action Group)
● Physical Interface: A 4-wire interface (TCK, TMS, TDI, TDO) plus power and ground
connections. It typically uses a standard 10-pin header on the target board.7
● Core Functionality: Provides full, real-time on-chip debugging capabilities, including
setting hardware breakpoints, single-stepping through code, and reading/writing to all
memory spaces and registers. It also serves as a high-speed programming interface.5
● Advantages: JTAG is widely considered the most powerful and reliable debugging
interface for the AVRs that support it. Its programming speed is independent of the
target MCU's clock frequency, making it consistently fast.8 For developers who have
access to it, it is often the preferred choice because "it just works" without the complex
mode-switching of other interfaces.27
● Limitations: Its primary drawback is the high pin count, which consumes four valuable
I/O pins. Consequently, it is generally found only on older, larger devices (typically 40 pins
and up) and is absent on smaller ATmega and ATtiny parts.7 Its functionality is also
contingent on the
JTAGEN fuse being programmed.16
● Physical Interface: A 3-wire synchronous serial interface (MOSI, MISO, SCK) that
leverages the AVR's built-in Serial Peripheral Interface (SPI) hardware. It also requires
connections to the RESET pin, power, and ground, and typically uses a 6-pin or 10-pin
header.8
● Core Functionality: Strictly a programming interface. It allows for writing and reading
the Flash program memory, EEPROM, fuse bits, and lock bits.9
● Advantages: ISP is the most ubiquitous interface in the AVR world, supported by nearly
every device in the portfolio. The hardware programmers are extremely inexpensive and
widely available.7 The protocol is simple and well-understood by the community.8
● Limitations: The most significant limitation of ISP is its complete lack of debugging
capabilities. Development using only ISP relies on the crude and inefficient
"burn-and-run" method, where code is repeatedly compiled, uploaded, and tested by
observing external behavior (e.g., blinking LEDs or serial output).7 Furthermore, its
programming speed is directly proportional to the target's clock frequency, with the
maximum ISP clock being one-quarter of the system clock. This can lead to slow
programming times for devices running on a slow internal oscillator.8
debugWIRE
● Physical Interface: A 2-wire proprietary interface specific to the ATxmega family, using a
PDI_DATA pin and a PDI_CLK pin (which is shared with the /RESET pin).16
● Core Functionality: Provides both high-speed programming and full on-chip debugging
for ATxmega devices.9
● Advantages: PDI offers a good compromise between JTAG and debugWIRE. It uses
fewer pins than JTAG but is significantly faster and more robust than debugWIRE.
Critically, the PDI interface is always enabled by default and does not require a fuse to be
set, eliminating a major point of failure present in both JTAG and debugWIRE systems.16
● Limitations: Its use is restricted exclusively to the ATxmega family of microcontrollers.9
Decision Matrix
The following table summarizes the key characteristics of each interface to aid in technology
selection.
Successfully establishing a JTAG connection with an AVR microcontroller requires the correct
hardware tools, a precise physical wiring setup, and an awareness of critical power and
voltage considerations. Errors in this foundational stage are the most common source of
frustration for developers and can prevent any software-level debugging from even beginning.
A standardized connector pinout is used for the AVR JTAG interface to ensure compatibility
between target boards and debugging tools. The standard is a 2x5 (10-pin) header, which is
available in both a 100-mil (2.54mm) through-hole version and a more compact 50-mil
(1.27mm) surface-mount version.13
The standard pinout for this 10-pin header is detailed in the table below.
The practical consequence of this reversal is that a standard, straight-through 10-pin ribbon
cable will not work. Connecting such a cable between the Atmel-ICE and a standard JTAG
header on a target board will result in a complete mismatch of signals (e.g., TCK being
connected to GND). To solve this, Atmel-ICE kits are supplied with a special ribbon cable that
has one of the connectors physically assembled backwards, which corrects the pinout by the
time it reaches the target board.45 When using third-party cables or designing custom
adapter boards, engineers must be acutely aware of this reversal and ensure their connection
scheme compensates for it. This design choice, while likely made for commercial reasons to
encourage the use of official accessories, violates standard engineering conventions and
creates an unnecessary trap for the unwary.
The first step in any debugging process is to establish a verified physical connection between
the hardware components.
1. Driver Installation: Before connecting the Atmel-ICE debugger to the host PC for the
first time, it is essential that Microchip Studio is already installed. The IDE installation
package includes the necessary USB drivers. Connecting the hardware before installing
the software may cause Windows to fail to find the correct drivers.32
2. Hardware Connection: Connect the appropriate 10-pin ribbon cable to the "AVR" port
on the Atmel-ICE. Connect the other end of the cable to the 10-pin JTAG header on the
target AVR board. Finally, connect the Atmel-ICE to the PC via a USB cable and ensure
the target board is powered on.32
3. Firmware Upgrade: Upon the first connection within Microchip Studio, the IDE will
automatically check the firmware version on the Atmel-ICE. If the firmware is outdated,
the IDE will prompt the user to perform an automatic upgrade to ensure compatibility
with the latest version of the software and supported devices.32
4. Connection Verification: To confirm that the hardware is connected and
communicating correctly, open the "Device Programming" dialog from the Tools menu. In
this dialog:
○ Select "Atmel-ICE" as the Tool.
○ Select the specific target MCU (e.g., "ATmega2560") as the Device.
○ Select "JTAG" as the Interface.
○ Click the "Apply" button.
○ Click the "Read" button next to the "Device Signature" field.
If the connection is successful, the dialog will display the device's unique signature
and its target voltage, confirming that the physical link is operational.9
To enable debugging, the project must be compiled with the correct settings to include
symbolic information that links the compiled machine code back to the original source code.
● Select Debug Configuration: In the main toolbar of Microchip Studio, there is a
dropdown menu for build configurations. Ensure that "Debug" is selected, not "Release".
The Debug configuration typically disables or reduces compiler optimizations (-O0) and
adds full debug symbols (-g3), which are essential for a coherent step-by-step
debugging experience. The Release configuration, by contrast, uses high optimization
levels that can reorder or eliminate code, making it very difficult to debug.32
● Assign Debugger to Project: Open the project's properties (Project > [ProjectName]
Properties). Navigate to the "Tool" tab. Select "Atmel-ICE" as the debugger/programmer
for the project and ensure the "JTAG" interface is selected.47
With the hardware connected and the project configured, a debug session can begin.
● Start Debugging: The primary way to start a session is by selecting Debug > Start
Debugging and Break from the menu, or by pressing the Alt+F5 shortcut. This will
compile the project (if necessary), upload the code to the target MCU, and halt execution
at the beginning of the main() function.32
● Execution Control: A debug toolbar provides icons for controlling the program's
execution flow 48:
○ Continue (F5): Resumes program execution, which will run freely until it hits a
breakpoint or is manually paused.
○ Break All (Ctrl+Alt+Break): Halts program execution at its current location.
○ Step Into (F11): Executes the current line of code. If the line is a function call, the
debugger will enter that function, allowing it to be stepped through.
○ Step Over (F10): Executes the current line of code. If the line is a function call, the
debugger will execute the entire function and halt on the next line in the current
scope.
○ Step Out (Shift+F11): Executes the remainder of the current function and halts after
returning to the calling function.
● Breakpoints: Breakpoints are the most fundamental debugging tool. To set a breakpoint,
click in the grey margin to the left of a line of code or press F9. A red circle will appear,
indicating an active breakpoint. When the program is continued, it will execute at full
speed until the program counter reaches the address of the breakpoint, at which point it
will halt.32 Microchip Studio also supports
conditional breakpoints, which only trigger if a specified C expression is true (e.g., break
only when a loop counter i is equal to 8).49
State Inspection and Manipulation
The true power of on-chip debugging lies in the ability to inspect and even modify the state of
the microcontroller while it is halted.
● Variable Inspection: The easiest way to check a variable's value is to simply hover the
mouse cursor over its name in the source code editor; a tooltip will appear with its
current value. For more persistent monitoring, variables can be added to the "Watch"
window, or their values can be observed in the "Locals" and "Autos" windows, which
automatically track variables within the current scope.32
● I/O View: This is an exceptionally powerful and unique feature of Microchip Studio. The
I/O View window displays all of the MCU's peripheral registers (e.g., PORTA, TCCR1A,
UDR0) in an organized, bit-field-aware format. When a register's value changes during
execution, it is highlighted in red. More importantly, this view is interactive. A developer
can directly click on a checkbox corresponding to a bit in a PORT register to toggle it
from 0 to 1, and the physical pin on the MCU will change its state in real-time. This allows
for direct, low-level hardware manipulation without writing any code.48
● Memory View: For inspecting raw data, the Memory View window can be used to display
the contents of any memory region (Flash, SRAM, EEPROM) at a given address.48
The integrated nature of Microchip Studio provides a high level of abstraction that simplifies
the complex mechanics of JTAG debugging. This is most evident in its handling of the AVR's
fuse-based configuration. For example, when using the debugWIRE interface, the IDE
automatically detects if the DWEN fuse is disabled and presents a simple dialog box asking for
permission to enable it.32 Similarly, the
OCDEN fuse required for JTAG debugging is typically managed by the IDE without any
required user intervention.19 This contrasts sharply with command-line workflows, where the
developer is responsible for manually configuring these fuses. The value of an official IDE,
therefore, lies not just in its editor and compiler, but in its role as a "state manager" for the
target device, shielding the developer from low-level, error-prone configuration tasks.
To properly terminate a debugging session, select Debug > Stop Debugging or use the
corresponding toolbar button. This is particularly important for interfaces like debugWIRE, as
this command signals the IDE to perform necessary cleanup actions, such as disabling the
DWEN fuse to re-enable the ISP interface and normal Reset functionality.32 Abruptly ending a
session can leave the device in a state where it is no longer programmable via ISP.
PlatformIO maintains a database of configurations for hundreds of boards. When a user starts
a debug session, PlatformIO reads the [Link] file, identifies the board and the specified
debug_tool, and automatically launches the correct GDB server with the appropriate
command-line arguments. This seamless process works exceptionally well for modern
platforms like ARM Cortex-M (STM32, SAM) and ESP32.51
Despite the Atmel-ICE being listed as a supported tool in PlatformIO's documentation, this
support is currently limited to firmware uploading, not debugging. As of this writing, there is
no built-in, "zero-config" support for initiating a JTAG debugging session on an AVR
microcontroller using an Atmel-ICE directly via a simple debug_tool = atmel-ice entry in
[Link].54
The officially supported debugging methods for the atmelavr platform are simavr, which is a
software simulator, and avr-stub, which is a software GDB stub that must be compiled into the
user's application and communicates over a standard UART/serial port. Neither of these
methods utilizes the powerful hardware-based JTAG interface.12 This gap in native support
reflects a broader trend in the embedded ecosystem: as the industry shifts towards newer
architectures like ARM, the development effort from both commercial entities and the
open-source community naturally gravitates towards these more prevalent platforms. While
support for legacy architectures like AVR is maintained, it often does not receive the same
level of investment in "quality of life" features like seamless debugger integration.
To bridge this gap, developers can manually configure PlatformIO to use a third-party,
open-source tool called AVaRICE. AVaRICE acts as a GDB server that can communicate with
Atmel's JTAG-based debuggers (including the Atmel-ICE) and translate GDB's remote serial
protocol into the necessary JTAG commands.58 This advanced technique effectively builds a
custom debug toolchain within the PlatformIO framework.
The following steps outline this process for an ATmega1284P, but the principles apply to other
JTAG-enabled AVRs.
Ini, TOML
[env:pro_mini_xl]
platform = atmelavr
board = pro_mini_xl_1284p
framework = arduino
; Custom Upload Configuration
upload_protocol = custom
upload_port = --edbg
upload_flags =
--part
atmega1284p
--reset-srst
upload_command = C:\Users\\.platformio\packages\tool-avarice\bin\[Link] $UPLOAD_PORT
$UPLOAD_FLAGS --erase --program --file $SOURCE --verify
Ini, TOML
● build_type = debug & debug_build_flags: Ensures the code is compiled with full debug
symbols and no optimization.
● debug_tool = custom: Specifies that a custom debug server will be used.
● debug_port = localhost:4242: Defines the TCP port on which GDB will communicate. This
can be any unused port.
● debug_server: This is the command PlatformIO will execute to start the debug server. It
calls [Link] with flags to select the Atmel-ICE (--edbg), specify the target part, and
tells it to start listening for a GDB connection on port 4242.58
The communication between avr-gdb and AVaRICE occurs over a standard TCP network
socket. This means they do not need to run on the same machine; a developer could
theoretically debug a target board in a lab from a computer anywhere on the network.59
Before a debugging session can begin, the source code must be compiled with specific flags
to include the necessary debugging information.
1. Install Toolchain: Ensure a complete AVR command-line toolchain is installed, including
avr-gcc (the compiler), avr-gdb, and AVaRICE. On Windows, the WinAVR package was a
historical choice, though modern toolchains are available through other means. On Linux,
these tools can often be installed via a package manager.59
2. Compile with Debug Flags: The C/C++ source code must be compiled using avr-gcc
with flags that embed debug symbols into the final output file. The most important flags
are:
○ -g or -ggdb: This tells the compiler to include debugging information in the standard
format that GDB understands.
○ -O0: This flag disables all compiler optimizations. This is crucial for a predictable
debugging experience, as optimizations can reorder instructions, eliminate variables,
or inline functions, causing the execution flow to diverge significantly from the
source code.62
A typical command-line JTAG session involves running the server in one terminal and the
client in another.
The command-line workflow, while lacking the visual convenience of a GUI, exposes the
fundamental power and flexibility of the GDB ecosystem. Because every action is a text
command, the entire process is scriptable. A developer can create a .gdbinit file containing a
sequence of GDB commands that are executed automatically on startup.61 This enables
powerful automation for tasks like regression testing, where a script could load firmware, run
to a specific point, dump the contents of memory to a file for analysis, and then reset the
target, all without manual intervention. This level of automation is difficult or impossible to
achieve with a GUI-only approach, demonstrating that mastering the command line unlocks a
different and more powerful class of development and testing workflows.
Despite its power, using JTAG with AVR microcontrollers can be fraught with challenges,
particularly for those new to the ecosystem. The majority of problems stem not from faulty
debugger hardware, but from incorrect configuration of the target device, physical
connection errors, or power-related issues. A systematic, layered approach to diagnosis is the
key to resolving these issues efficiently.
The debugger connects initially but the session is unreliable, freezing, or failing unexpectedly.
● Symptom: A debug session starts successfully, but then suddenly terminates, the target
becomes unresponsive, or synchronization is lost.23
● Possible Causes & Solutions:
1. Incorrect MCU Clock Source: The device's clock fuse bits are configured for a
source that is not present or not working (e.g., set for an external high-speed crystal
when none is on the board). The CPU is not executing code, so it cannot respond to
the debugger.
■ Solution: Use ISP to read the clock fuses and ensure they are set to a reliable
source, such as the internal RC oscillator, for initial bring-up and debugging.63
2. Application Code Disables JTAG: The firmware running on the target contains a
bug that inadvertently writes to the MCUCSR register, setting the JTAGD bit and
disabling the JTAG interface mid-session.
■ Solution: This can be difficult to find. Try to hold the target in reset with the
debugger to regain control before the faulty code executes. Then, carefully audit
the application code for any writes to MCUCSR and correct the logic.23
3. Insufficient Power: The USB port powering the debugger cannot supply the
required current (typically up to 500mA), leading to unstable operation. This is
common when using passive USB hubs.
■ Solution: Connect the debugger directly to a host PC's USB port or use a
powered USB hub.23
A systematic, layered approach is the most effective way to troubleshoot JTAG issues. The
problems are rarely in the debugger itself but are almost always found in one of three areas:
device configuration (fuses), physical connectivity (wiring), or runtime environment (power
and application code). By diagnosing in that order—from fuses to wires to code—an engineer
can efficiently isolate and resolve nearly any JTAG problem.
"Target Voltage detected VTG (Vref) pin on JTAG Connect Pin 4 of the JTAG
was 0.0 Volts" header is not connected to header to the VCC rail on
the target's VCC. the target board.
JTAG pins do not function Software failed to set the Modify the code to use two
as GPIO after debugging. JTD bit in MCUCR due to a consecutive write-only
timing violation. operations to MCUCR to
disable the JTAG interface.
Debug session starts but 1. Incorrect clock source 1. Use ISP to set clock fuses
then freezes or fails. fuses. 2. Application code to a known good source
is disabling JTAG. 3. (e.g., internal RC oscillator).
Insufficient power to the 2. Hold target in reset and
debugger. audit code for writes to
MCUCSR. 3. Connect
debugger to a powered
USB hub or directly to the
PC.
Conclusion
The IEEE 1149.1 JTAG standard has proven to be a remarkably resilient and adaptable
technology. Born from the necessity of testing complex circuit boards, its extensible
architecture has allowed it to become an indispensable tool for the in-system programming
and on-chip debugging of microcontrollers, including the venerable AVR family. For the
embedded systems engineer, JTAG provides a powerful window into the real-time operation
of firmware on physical hardware, enabling a level of control and observability that is essential
for developing and troubleshooting complex systems.
However, the implementation of JTAG within the AVR ecosystem is a study in trade-offs. While
powerful, it is confined to older, larger MCUs and is governed by a fuse-based activation
system that prioritizes I/O pin flexibility over the robustness of the debug interface. This
design choice, coupled with the non-standard connector on modern tools like the Atmel-ICE,
creates a series of potential pitfalls that demand a higher level of system awareness from the
developer. The existence of multiple, often mutually exclusive, programming and debugging
interfaces—ISP, debugWIRE, PDI, and UPDI—further complicates the landscape, with each
representing a different point in the evolutionary balance between capability, pin count, and
operational complexity.
Successfully leveraging JTAG for AVR development requires a holistic approach. It begins with
a solid theoretical understanding of the JTAG state machine and protocol. It demands
meticulous attention to hardware detail, from selecting the right debugger and verifying
pinouts to ensuring proper power and signal integrity. Finally, it necessitates proficiency in a
chosen toolchain, whether it is the tightly integrated, user-friendly environment of Microchip
Studio or the flexible but more demanding custom configurations required for modern IDEs
like PlatformIO and command-line tools like avr-gdb. By mastering these three
domains—theory, hardware, and software—the developer can transform JTAG from a
potential source of frustration into its intended role: a powerful and efficient enabler of robust
embedded systems.
Works cited