JTAG Debug Design for a processor
The JTAG interface is standardized, so there isn't much left to design. You need to implement the standard state machine that handles TMS, and handling for the various shift registers that can be connected to TDI and TDO.

Implementing a debug circuit generally involves accepting data that is shifted in through TDI, usually there is one register of fixed length, the IR, and one or more registers of varying length that can be connected to the DR path.

Generally, you want to implement at least the following commands in the IR:


BYPASS (all ones): Connect a one bit register with no function during DR.
BOUNDARY_SCAN (typically, all zeroes): Connect a register containing one bit for each I/O pin on the package to DR, load the current state during CAPTURE-DR, and write the new state during UPDATE-DR.


For processors with more intelligent debugging capabilities, you also want a third mode where the debug interface is connected to DR, and ideally a fourth mode where you can read out an ID code to identify the chip. You can also use additional IR values if that is helpful, but for a simple design I'd start with


BOUNDARY_SCAN (0b00)
IDCODE (0b01)
DEBUG (0b10)
BYPASS (0b11)


Preload the IR with the IDCODE instruction during CAPTURE-IR, that will make scanning the bus easier (for that to work, IDCODE needs to be all zero except for the LSB -- look at the ARM implementation for an example).

The DR for the DEBUG command is what you need to design. You have full freedom to build any mapping there that you want, and the ideal mapping depends on your processor architecture. Your debug interface can ignore the shifting, and just implement a synchronous parallel data bus -- every time you enter CAPTURE-DR state while the IR is DEBUG, a word is transferred from the outgoing FIFO, and every time you enter UPDATE-DR in DEBUG mode, a word is transferred to the incoming FIFO.

Generally, you want at least the following functionality:


Define code breakpoints
Define data breakpoints
Define breakpoint instructions (so you get infinite breakpoints by replacing instructions)
Read and alter CPU state
Copy register values to the debugger
Copy data from the debugger to registers
Copy memory data to debugger
Copy data from the debugger to memory
Execute arbitrary instructions
Test whether the CPU is currently running
Halt execution
Execute single instructions from the current state
Restart execution
Override whether interrupts are enabled (this makes your life a lot easier when you have a periodic timer but you want to debug something else)


Some of these may overlap -- again, looking at the way it is implemented on ARM is perhaps not the worst idea:


Writing memory can be achieved by writing to registers, then running a "store" instruction normally.
Writing registers can be achieved by connecting the data bus to the boundary scan interface and issuing a "load" instruction
Halting the CPU can be achieved by defining a breakpoint that will trigger on any instruction
The breakpoint instruction can be defined with a data breakpoint that triggers on a specific value during a data transfer that is tagged as an instruction fetch


If you have a pipelined design where instructions may need to be completed before the machine state has settled, use the RUN-TEST/IDLE state for that: generate a clock impulse everytime you pass through there, and have a way to find out from the DR in DEBUG mode whether the pipeline is empty. It is completely acceptable to require a debugger to pass through RUN-TEST/IDLE four times after giving a command to stop execution, that just becomes part of the interface.

In general it is preferable to keep the on-chip implementation as simple as possible, and handle all the complexity on the host side, for example, the ARM increments the PC with every instruction it executes, so the debug driver needs to keep track of how many instructions it issued, and correct the PC before restarting.