Skip to content

Instantly share code, notes, and snippets.

@jevinskie
Last active February 8, 2022 00:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jevinskie/811a4fd6deba7fff7280f6908ae37437 to your computer and use it in GitHub Desktop.
Save jevinskie/811a4fd6deba7fff7280f6908ae37437 to your computer and use it in GitHub Desktop.
migen Altera JTAG
# JTAG TAP FSM -------------------------------------------------------------------------------------
class JTAGTAPFSM(Module):
def __init__(self, tms: Signal, tck: Signal, use_ieee_encoding=False, expose_signals=True):
self.submodules.fsm = fsm = ClockDomainsRenamer("jtag")(FSM())
# Debug counter
# self.tck_cnt = tck_cnt = Signal(16)
# self.sync.jtag += tck_cnt.eq(tck_cnt + 1)
fsm.act('test_logic_reset',
If(~tms, NextState('run_test_idle'))
)
fsm.act('run_test_idle',
If( tms, NextState('select_dr_scan'))
)
# DR
fsm.act('select_dr_scan',
If(~tms, NextState('capture_dr') ).Else(NextState('select_ir_scan'))
)
fsm.act('capture_dr',
If(~tms, NextState('shift_dr') ).Else(NextState('exit1_dr'))
)
fsm.act('shift_dr',
If( tms, NextState('exit1_dr'))
)
fsm.act('exit1_dr',
If(~tms, NextState('pause_dr') ).Else(NextState('update_dr'))
)
fsm.act('pause_dr',
If( tms, NextState('exit2_dr'))
)
fsm.act('exit2_dr',
If( tms, NextState('update_dr') ).Else(NextState('shift_dr'))
)
fsm.act('update_dr',
If( tms, NextState('select_dr_scan')).Else(NextState('run_test_idle'))
)
# IR
fsm.act('select_ir_scan',
If(~tms, NextState('capture_ir') ).Else(NextState('test_logic_reset'))
)
fsm.act('capture_ir',
If(~tms, NextState('shift_ir') ).Else(NextState('exit1_ir'))
)
fsm.act('shift_ir',
If( tms, NextState('exit1_ir'))
)
fsm.act('exit1_ir',
If(~tms, NextState('pause_ir') ).Else(NextState('update_ir'))
)
fsm.act('pause_ir',
If( tms, NextState('exit2_ir'))
)
fsm.act('exit2_ir',
If( tms, NextState('update_ir') ).Else(NextState('shift_ir'))
)
fsm.act('update_ir',
If( tms, NextState('select_dr_scan')).Else(NextState('run_test_idle'))
)
if use_ieee_encoding:
# 11491.1-2013 Table 6-3 "State assignments for example TAP controller" page 36 pdf page 58
self.fsm.encoding = {
'exit2_dr': 0,
'exit1_dr': 1,
'shift_dr': 2,
'pause_dr': 3,
'select_ir_scan': 4,
'update_dr': 5,
'capture_dr': 6,
'select_dr_scan': 7,
'exit2_ir': 8,
'exit1_ir': 9,
'shift_ir': 0xA,
'pause_ir': 0xB,
'run_test_idle': 0xC,
'update_ir': 0xD,
'capture_ir': 0xE,
'test_logic_reset': 0xF,
}
if expose_signals:
for state_name in fsm.actions:
reset_val = 0
if state_name == 'test_logic_reset':
reset_val = 1
sig = fsm.ongoing(state_name, reset=reset_val)
SHOUTING_NAME = state_name.upper()
hcs_name = SHOUTING_NAME
hcs = Signal(name=hcs_name)
setattr(self, hcs_name, hcs)
self.comb += hcs.eq(sig)
class AlteraJTAG(Module):
def __init__(self, primitive: str, reserved_pads: Record, chain=1):
self.reset = reset = Signal()
self.capture = capture = Signal()
self.shift = shift = Signal()
self.update = update = Signal()
#
self.runtest = runtest = Signal()
self.drck = drck = Signal()
self.sel = sel = Signal()
self.tck = tck = Signal()
self.tms = tms = Signal()
self.tdi = tdi = Signal()
self.tdo = tdo = Signal()
self.altera_reserved_tck = rtck = Signal()
self.altera_reserved_tms = rtms = Signal()
self.altera_reserved_tdi = rtdi = Signal()
self.altera_reserved_tdo = rtdo = Signal()
# inputs
# self.tdoutap = tdoutap = Signal() # fails synth on max10
self.tdouser = tdouser = Signal()
# self.tmscore = tmscore = Signal()
# self.tckcore = tckcore = Signal()
# self.tdicore = tdicore = Signal()
# self.corectl = corectl = Signal()
# self.ntdopinena = ntdopinena = Signal()
# outputs
self.tmsutap = tmsutap = Signal()
self.tckutap = tckutap = Signal()
self.tdiutap = tdiutap = Signal()
# self.tdocore = tdocore = Signal()
assert chain == 1
# # #
self.clock_domains.cd_jtag_inv = cd_jtag_inv = ClockDomain("jtag_inv")
self.comb += ClockSignal("jtag_inv").eq(~ClockSignal("jtag"))
self.comb += ResetSignal('jtag_inv').eq(ResetSignal("jtag"))
self.submodules.tap_fsm = JTAGTAPFSM(tms, tck)
self.sync.jtag_inv += reset.eq(self.tap_fsm.TEST_LOGIC_RESET)
self.sync.jtag_inv += capture.eq(self.tap_fsm.CAPTURE_DR)
self.specials += Instance(primitive,
o_shiftuser = shift,
o_updateuser = update,
#
o_runidleuser = runtest,
o_clkdruser = drck,
o_usr1user = sel,
# etc?
# i_tdoutap = tdoutap, # fails synth on max10
i_tdouser = tdouser,
# i_tmscore = tmscore, # nope on cyclone 10 lp
# i_tckcore = tckcore, # nope on cyclone 10 lp
# i_tdicore = tdicore, # nope on cyclone 10 lp
# i_corectl = corectl,
# i_ntdopinena = ntdopinena,
o_tmsutap = tmsutap,
o_tckutap = tckutap,
o_tdiutap = tdiutap,
# o_tdocore = tdocore, # nope on cyclone 10 lp
# reserved pins
i_tms = rtms,
i_tck = rtck,
i_tdi = rtdi,
o_tdo = rtdo,
)
self.comb += [
rtms.eq(reserved_pads.altera_reserved_tms),
rtck.eq(reserved_pads.altera_reserved_tck),
rtdi.eq(reserved_pads.altera_reserved_tdi),
reserved_pads.altera_reserved_tdo.eq(rtdo),
]
self.comb += [
tck.eq(tckutap),
tms.eq(tmsutap),
tdi.eq(tdiutap),
]
self.sync.jtag_inv += tdouser.eq(tdo)
class MAX10JTAG(AlteraJTAG):
def __init__(self, reserved_pads: Record, *args, **kwargs):
AlteraJTAG.__init__(self, "fiftyfivenm_jtag", reserved_pads, *args, **kwargs)
class Cyclone10LPJTAG(AlteraJTAG):
def __init__(self, reserved_pads: Record, *args, **kwargs):
AlteraJTAG.__init__(self, "cyclone10lp_jtag", reserved_pads, *args, **kwargs)
@lapnd
Copy link

lapnd commented Dec 24, 2021

Can you please give an few hint on how to use it?
I would like to use it together with LiteX

@tilk
Copy link

tilk commented Jan 18, 2022

This seems to be the thing missing to implement proper JTAG support for Intel FPGAs in LiteX.

@jevinskie
Copy link
Author

@tilk it is, I've been using it for months without issue.

@lapnd I've finally made my fork of LiteX public, try looking through it and my litex-boards. I'll try to clean it up this week.

https://github.com/jevinskie/litex

@lapnd
Copy link

lapnd commented Jan 26, 2022

Thank you @jevinskie

@tilk
Copy link

tilk commented Jan 26, 2022

Thanks! I'll take a look at it later. This needs to be merged.

@jevinskie
Copy link
Author

I’m working on this updated topic branch to get it merged. Right now it seems to be broken from the copy-pasta from the September base so it might need some fixes still (or it might be user error…):

jevinskie/litex@8e1c68f

Here is an example of adding support to an Altera board:
jevinskie/litex-boards@dd5106e

@jevinskie
Copy link
Author

@vietthanh85
Copy link

@jevinskie
Thanks for your work. It would be very helpful.
Unfortunately, I don't have DECA board. So I modified for my DE2_115.
Basically, I add following code to litex/litex/soc/cores/jtag.py

class CycloneIVEJTAG(AlteraJTAG):
    def __init__(self, reserved_pads, *args, **kwargs):
        AlteraJTAG.__init__(self, "cycloneive_jtag", reserved_pads, *args, **kwargs)

It was built and loaded successfully.

$ ./terasic_de2_115.py --csr-csv csr.csv --with-jtagbone --build --load

If I run litex_server with uart port, I can debug successuflly

$ sudo litex_server --uart --uart-port=/dev/ttyUSB0

(I referred https://github.com/timvideos/litex-buildenv/wiki/Debugging to debug)
However, If I run litex_server with virtual jtag, it was failed.

$ litex_server --jtag --jtag-config ../prog/openocd_de2_115_blaster.cfg
$ ./src/openocd -c 'interface dummy' \
              -c 'adapter_khz 1' \
              -c 'jtag newtap lx cpu -irlen 4' \
              -c 'target create lx.cpu0 vexriscv -endian little -chain-position lx.cpu -dbgbase 0xF00F0000' \
              -c 'vexriscv cpuConfigFile cpu0.yaml' \
              -c 'vexriscv networkProtocol etherbone' \
              -c 'init' \
              -c 'reset halt
Open On-Chip Debugger 0.11.0+dev-02577-g3eee6eb04-dirty (2022-02-08-06:16)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
DEPRECATED! use 'adapter driver' not 'interface'
Info : only one transport option; autoselect 'jtag'
DEPRECATED! use 'adapter speed' not 'adapter_khz'
adapter speed: 1 kHz

4027514880
Info : clock speed 1 kHz
Info : TAP lx.cpu does not have valid IDCODE (idcode=0x0)
Info : TAP auto0.tap does not have valid IDCODE (idcode=0x80000000)
Info : TAP auto1.tap does not have valid IDCODE (idcode=0xc0000000)
Info : TAP auto2.tap does not have valid IDCODE (idcode=0xe0000000)
Info : TAP auto3.tap does not have valid IDCODE (idcode=0xf0000000)
Info : TAP auto4.tap does not have valid IDCODE (idcode=0xf8000000)
Info : TAP auto5.tap does not have valid IDCODE (idcode=0xfc000000)
Info : TAP auto6.tap does not have valid IDCODE (idcode=0xfe000000)
Info : TAP auto7.tap does not have valid IDCODE (idcode=0xff000000)
Info : TAP auto8.tap does not have valid IDCODE (idcode=0xff800000)
Info : TAP auto9.tap does not have valid IDCODE (idcode=0xffc00000)
Info : TAP auto10.tap does not have valid IDCODE (idcode=0xffe00000)
Info : TAP auto11.tap does not have valid IDCODE (idcode=0xfff00000)
Info : TAP auto12.tap does not have valid IDCODE (idcode=0xfff80000)
Info : TAP auto13.tap does not have valid IDCODE (idcode=0xfffc0000)
Info : TAP auto14.tap does not have valid IDCODE (idcode=0xfffe0000)
Info : TAP auto15.tap does not have valid IDCODE (idcode=0xffff0000)
Info : TAP auto16.tap does not have valid IDCODE (idcode=0xffff8000)
Info : TAP auto17.tap does not have valid IDCODE (idcode=0xffffc000)
Info : TAP auto18.tap does not have valid IDCODE (idcode=0xffffe000)
Info : TAP auto19.tap does not have valid IDCODE (idcode=0xfffff000)
Warn : Unexpected idcode after end of chain: 21 0xfffff800
Error: double-check your JTAG setup (interface, speed, ...)
Error: Trying to use configured scan chain anyway...
Error: lx.cpu: IR capture error; saw 0x0f not 0x01
Warn : Bypassing JTAG setup events due to errors

It was stuck here and gdb server did not start, so I cannot debug it.

Could you show me an example how to debug vexriscv by using virtual jtag and openocd?
Any help would be very appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment