-
Notifications
You must be signed in to change notification settings - Fork 93
/
Copy pathJTAGulator.spin
2902 lines (2304 loc) · 117 KB
/
JTAGulator.spin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{{
+-------------------------------------------------+
| JTAGulator |
| |
| Author: Joe Grand |
| Copyright (c) 2013-2023 Grand Idea Studio, Inc. |
| Web: http://www.grandideastudio.com |
| |
| Distributed under a Creative Commons |
| Attribution 3.0 United States license |
| http://creativecommons.org/licenses/by/3.0/us/ |
+-------------------------------------------------+
Program Description:
The JTAGulator is a hardware tool that assists in identifying on-chip
debug/programming interfaces from test points, vias, component pads,
and/or connectors on a target device.
Refer to the project page for more details:
http://www.jtagulator.com
Each interface object contains the low-level routines and operational details
for that particular on-chip debugging interface. This keeps the main JTAGulator
object a bit cleaner.
Command listing is available in the DAT section at the end of this file.
}}
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000 ' 5 MHz clock * 16x PLL = 80 MHz system clock speed
_stack = 256 ' Ensure we have this minimum stack space available
' Serial terminal
' Control characters
LF = 10 ' LF: Line Feed
CR = 13 ' CR: Carriage Return
CAN = 24 ' CAN: Cancel (Ctrl-X)
QUOTE = 34 ' Quotation mark
CON
' UI
MAX_LEN_CMD = 12 ' Maximum length of command string buffer
' Target voltage
VTARGET_IO_MIN = 14 ' Minimum target I/O voltage (VADJ) (for example, xy = x.yV)
VTARGET_IO_MAX = 33 ' Maximum target I/O voltage
' JTAG
NUM_RTCK_ITERATIONS = 10 ' Number of times to check for RTCK correlation from TCK
' UART/Asynchronous Serial
MAX_LEN_UART_USER = 34 ' Maximum length of user input string buffer (accounts for hexadecimal input of 16 bytes, \x00112233445566778899AABBCCDDEEFF)
MAX_LEN_UART_TX = 16 ' Maximum number of bytes to transmit to target (based on user input string)
MAX_LEN_UART_RX = 16 ' Maximum number of bytes to receive from target
UART_SCAN_DELAY = 20 ' Time to receive a byte from the target (ms)
UART_PULSE_DELAY = 50 ' Time for pulse width detection cog to measure pulse (ms)
UART_PULSE_COUNT = 32 ' Number of samples to receive during pulse width detection
UART_PULSE_ARRAY_L = 8 ' Range within array of captured pulses to determine minimum width (must be within UART_PULSE_COUNT)
UART_PULSE_ARRAY_H = 15
' Menu
MENU_MAIN = 0 ' Main/Top
MENU_JTAG = 1 ' JTAG
MENU_UART = 2 ' UART
MENU_GPIO = 3 ' General Purpose I/O
MENU_SWD = 4 ' Serial Wire Debug (SWD)
' EEPROM
eepromAddress = $8000 ' Starting address within EEPROM for system/user data storage
MODE_NORMAL = 0 ' JTAGulator main mode
MODE_SUMP = 1 ' Logic analyzer (OLS/SUMP)
MODE_OCD = 2 ' OpenOCD interface
EEPROM_MODE_OFFSET = 0
EEPROM_VTARGET_OFFSET = 4
EEPROM_TDI_OFFSET = 8
EEPROM_TDO_OFFSET = 12
EEPROM_TCK_OFFSET = 16
EEPROM_TMS_OFFSET = 20
VAR ' Globally accessible variables
byte vCmd[MAX_LEN_CMD + 1] ' Buffer for command input string + \0
long vBuf[sump#MAX_SAMPLE_PERIODS] ' Buffer for stack/data transfer (shared across objects)
long vTargetIO ' Target I/O voltage
long vMode ' JTAGulator operating mode (determined on start-up)
long jTDI ' JTAG pins (must stay in this order)
long jTDO
long jTCK
long jTMS
long jTRST
long jPinsKnown ' Parameter for BYPASS_Scan
long jIgnoreReg ' Parameter for OPCODE_Discovery
long jIR ' Parameters for EXTEST_Scan
long jDRFill
long jLoopPause
long uTXD ' UART pins (as seen from the target) (must stay in this order)
long uRXD
long uBaud
byte uSTR[MAX_LEN_UART_TX + 1] ' User input string buffer for UART_Scan + \0
byte uHex ' Is user input string ASCII (0) or hex (number of bytes)
long uPrintable
long uPinsKnown
long uWaitDelay ' Time to wait before checking for a response from the target (ms)
long uBaudIgnore ' Parameter for UART_Scan_TXD
long uLocalEcho ' Parameter for UART_Passthrough
long gWriteValue ' Parameter for Write_IO_Pins
long swdClk ' SWD pins (must stay in this order)
long swdIo
long swdPinsKnown ' Are above pins valid?
long swdFrequency
long chStart ' Channel range for the current scan (specified by the user)
long chEnd
long pinsLow ' Bring channels LOW before each permutation attempt (used in scan methods)
long pinsLowDelay
long pinsHighDelay
long idMenu ' Menu ID of currently active menu
OBJ
g : "JTAGulatorCon" ' JTAGulator global constants
u : "JTAGulatorUtil" ' JTAGulator general purpose utilities
pst : "PropSerial" ' Serial communication for user interface (modified version of built-in Parallax Serial Terminal)
str : "jm_strings" ' String manipulation methods (JonnyMac)
rr : "RealRandom" ' Random number generation (Chip Gracey, https://github.com/parallaxinc/propeller/tree/master/libraries/community/p1/All/Real%20Random)
pulse : "PulseWidth" ' Measure pulse width on specified input pin
sort : "sort_dec" ' Sorting algorithms (Brandon Nimon, https://github.com/parallaxinc/propeller/tree/master/libraries/community/p1/All/Sorting%20Algorithms%20in%20SPIN%20or%20PASM)
eeprom : "Basic_I2C_Driver" ' I2C protocol for boot EEPROM communication (Michael Green, https://github.com/parallaxinc/propeller/tree/master/libraries/community/p1/All/Basic%20I2C%20Driver)
uart : "JDCogSerial" ' UART/Asynchronous Serial communication engine (Carl Jacobs, https://github.com/parallaxinc/propeller/tree/master/libraries/community/p1/All/JDCogSerial)
pt_in : "jm_rxserial" ' UART/Asynchronous Serial receive driver for passthrough (JonnyMac, https://forums.parallax.com/discussion/114492/prop-baudrates)
pt_out : "jm_txserial" ' UART/Asynchronous Serial transmit driver for passthrough (JonnyMac, https://forums.parallax.com/discussion/114492/prop-baudrates)
jtag : "PropJTAG" ' JTAG/IEEE 1149.1 low-level methods
swd : "PropSWD" ' ARM SWD (Serial Wire Debug) low-level functions (Adam Green, https://github.com/adamgreen)
sump : "PropSUMP" ' OLS/SUMP protocol for logic analyzer mode
ocd : "PropOCD" ' OpenOCD binary protocol
PUB main | cmd
System_Init ' Initialize system/hardware
JTAG_Init ' Initialize JTAG-specific items
UART_Init ' Initialize UART-specific items
GPIO_Init ' Initialize GPIO-specific items
SWD_Init ' Initialize SWD-specific items
Do_Mode ' Read EEPROM to determine/select operating mode
' Start command receive/process cycle
repeat
u.TXSDisable ' Disable level shifter outputs (high-impedance)
u.LEDGreen ' Set status indicator to show that we're ready
Display_Command_Prompt ' Display command prompt
pst.StrInMax(@vCmd, MAX_LEN_CMD) ' Wait here to receive a carriage return terminated string or one of MAX_LEN_CMD bytes (the result is null terminated)
u.LEDRed ' Set status indicator to show that we're processing a command
if (strsize(@vCmd) == 1) ' Only single character commands are supported...
cmd := vCmd[0]
case idMenu
MENU_MAIN: ' Main/Top
Do_Main_Menu(cmd)
MENU_JTAG: ' JTAG
Do_JTAG_Menu(cmd)
MENU_UART: ' UART/Asynchronous Serial
Do_UART_Menu(cmd)
MENU_GPIO: ' General Purpose I/O
Do_GPIO_Menu(cmd)
MENU_SWD: ' Serial Wire Debug
Do_SWD_Menu(cmd)
other:
idMenu := MENU_MAIN
Do_Main_Menu(cmd)
else
Display_Invalid_Command
PRI Do_Mode | ackbit ' Read EEPROM to determine/select operating mode
' JTAGulator's EEPROM (64KB) is larger than required by the Propeller, so there is 32KB of additional,
' unused area available for data storage. Values will not get overwritten when JTAGulator firmware is
' re-loaded into the EEPROM.
ackbit := 0
ackbit += readLong(eepromAddress + EEPROM_MODE_OFFSET, @vMode)
ackbit += readLong(eepromAddress + EEPROM_VTARGET_OFFSET, @vTargetIO)
if ackbit ' If there's an error with the EEPROM
pst.Str(@ErrEEPROMNotResponding)
vMode := MODE_NORMAL
if (vMode <> MODE_NORMAL) and (vMode <> MODE_SUMP) and (vMode <> MODE_OCD)
vMode := MODE_NORMAL
if (vTargetIO < VTARGET_IO_MIN) or (vTargetIO > VTARGET_IO_MAX)
vMode := MODE_NORMAL
' Select operating mode
case vMode
MODE_SUMP: ' Logic analyzer (OLS/SUMP)
pst.Stop ' Stop serial communications (this will be restarted from within the sump object)
DACOutput(VoltageTable[vTargetIO - VTARGET_IO_MIN]) ' Set target I/O voltage
GPIO_Logic(0) ' Start logic analyzer mode
idMenu := MENU_GPIO ' Set to previously active menu upon return
MODE_OCD: ' OpenOCD interface
pst.Stop ' Stop serial communications (this will be restarted from within the ocd object)
DACOutput(VoltageTable[vTargetIO - VTARGET_IO_MIN]) ' Set target I/O voltage
JTAG_OpenOCD(0) ' Start OpenOCD mode
idMenu := MENU_JTAG ' Set to previously active menu upon return
MODE_NORMAL: ' JTAGulator main mode
Set_Config_Defaults ' Set configuration globals to default values
u.LEDYellow
pst.CharIn ' Wait until the user presses a key before getting started
pst.Str(@InitHeader) ' Display header
CON {{ MENU METHODS }}
PRI Do_Main_Menu(cmd)
case cmd
"J", "j": ' Switch to JTAG submenu
idMenu := MENU_JTAG
"U", "u": ' Switch to UART submenu
idMenu := MENU_UART
"G", "g": ' Switch to GPIO submenu
idMenu := MENU_GPIO
"S", "s": ' Switch to SWD submenu
idMenu := MENU_SWD
"A", "a": ' Scan all supported protocols
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
Read_IO_Pins ' GPIO: Read all channels (input, one shot)
pst.Str(@MsgSpacer)
IDCODE_Scan(1) ' JTAG: Combined Scan
pst.Str(@MsgSpacer)
IDCODE_Scan(0) ' JTAG: IDCODE Scan
pst.Str(@MsgSpacer)
BYPASS_Scan ' JTAG: BYPASS Scan
pst.Str(@MsgSpacer)
RTCK_Scan ' JTAG: Identify RTCK (Adaptive Clocking)
pst.Str(@MsgSpacer)
SWD_IDCODE_Scan ' SWD: Identify SWD pinout (IDCODE Scan)
pst.Str(@MsgSpacer)
UART_Scan_TXD ' UART: Identify UART pinout (TXD only, continuous automatic baud rate detection)
pst.Str(@MsgSpacer)
UART_Scan ' UART: Identify UART pinout
"V", "v": ' Set target I/O voltage
Set_Target_IO_Voltage
"I", "i": ' Display JTAGulator version information
pst.Str(@VersionInfo)
"H", "h": ' Display list of available commands
Display_Menu_Text
other:
Display_Invalid_Command
PRI Do_JTAG_Menu(cmd)
case cmd
"J", "j": ' Identify JTAG pinout
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
IDCODE_Scan(1) ' Combined IDCODE Scan and BYPASS Scan
"I", "i": ' Identify JTAG pinout (IDCODE Scan)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
IDCODE_Scan(0)
"B", "b": ' Identify JTAG pinout (BYPASS Scan)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
BYPASS_Scan
"R", "r": ' Identify RTCK (Adaptive Clocking)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
RTCK_Scan
"D", "d": ' Get JTAG Device IDs (Pinout already known)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
IDCODE_Known
"T", "t": ' Test BYPASS (TDI to TDO) (Pinout already known)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
BYPASS_Known
"Y", "y": ' Instruction/Data Register discovery (Pinout already known, requires single device in the chain)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
OPCODE_Discovery
"P", "p": ' Pin Mapper (EXTEST Scan) (Pinout already known, requires single device in the chain)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
EXTEST_Scan
"O", "o": ' OpenOCD interface (Pinout already known)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
JTAG_OpenOCD(1)
other:
Do_Shared_Menu(cmd)
PRI Do_UART_Menu(cmd)
case cmd
"U", "u": ' Identify UART pinout
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
UART_Scan
"T", "t": ' Identify UART pinout (TXD only, continuous automatic baud rate detection)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
UART_Scan_TXD
"P", "p": ' UART passthrough
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
UART_Passthrough
other:
Do_Shared_Menu(cmd)
PRI Do_GPIO_Menu(cmd)
case cmd
"R", "r": ' Read all channels (input, one shot)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
Read_IO_Pins
"C", "c": ' Read all channels (input, continuous)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
Monitor_IO_Pins
"W", "w": ' Write all channels (output)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
Write_IO_Pins
"L", "l": ' Logic analyzer (OLS/SUMP)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
GPIO_Logic(1)
other:
Do_Shared_Menu(cmd)
PRI Do_SWD_Menu(cmd)
case cmd
"I", "i": ' Identify SWD pinout (IDCODE Scan)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
SWD_IDCODE_Scan
"D", "d": ' Get SWD Device ID (Pinout already known)
if (vTargetIO == -1)
pst.Str(@ErrTargetIOVoltage)
else
SWD_IDCODE_Known
other:
Do_Shared_Menu(cmd)
PRI Do_Shared_Menu(cmd)
case cmd
"V", "v": ' Set target I/O voltage
Set_Target_IO_Voltage
"H", "h": ' Display list of available commands
Display_Menu_Text
"M", "m": ' Return to main menu
idMenu := MENU_MAIN
other:
Display_Invalid_Command
PRI Display_Menu_Text
case idMenu
MENU_MAIN:
pst.Str(@MenuMain)
MENU_JTAG:
pst.Str(@MenuJTAG)
MENU_UART:
pst.Str(@MenuUART)
MENU_GPIO:
pst.Str(@MenuGPIO)
MENU_SWD:
pst.Str(@MenuSWD)
if (idMenu <> MENU_MAIN)
pst.Str(@MenuShared)
PRI Display_Command_Prompt
pst.Str(String(CR, LF, LF))
case idMenu
MENU_MAIN: ' Main/Top, don't display any prefix/header
MENU_JTAG: ' JTAG
pst.Str(String("JTAG"))
MENU_UART: ' UART
pst.Str(String("UART"))
MENU_GPIO: ' General Purpose I/O
pst.Str(String("GPIO"))
MENU_SWD: ' Serial Wire Debug
pst.Str(String("SWD"))
other:
idMenu := MENU_MAIN
pst.Str(String("> "))
PRI Display_Invalid_Command
pst.Str(String(CR, LF, "? Press 'H' for available commands."))
CON {{ JTAG METHODS }}
PRI JTAG_Init
rr.start ' Start RealRandom cog (used during BYPASS Scan and Test BYPASS)
' Set default parameters
' BYPASS_Scan, RTCK_Scan
jPinsKnown := 0
' OPCODE_Discovery
jIgnoreReg := 1
' EXTEST_Scan
jIR := $00
jDRFill := 0
jLoopPause := 0
PRI IDCODE_Scan(type) | value, value_new, ctr, num, id[32 {jtag#MAX_DEVICES_LEN}], i, match, data_in, data_out, xtdi, xtdo, xtck, xtms, err ' Identify JTAG pinout (IDCODE Scan or Combined Scan)
if (type == 0) ' IDCODE Scan only
if (Get_Channels(3) == -1) ' Get the channel range to use
return
Display_Permutations((chEnd - chStart + 1), 3) ' TDO, TCK, TMS
else ' Combined IDCODE Scan and BYPASS Scan (aka JTAG Scan)
if (Get_Channels(4) == -1) ' Get the channel range to use
return
Display_Permutations((chEnd - chStart + 1), 4) ' TDI, TDO, TCK, TMS
if (Get_Settings == -1) ' Get configurable scan settings
return
if (type == 0)
err := @ErrIDCODEAborted
else
err := @ErrJTAGAborted
if (Wait_For_Space(err) == -1)
return
longfill(@id, 0, jtag#MAX_DEVICES_LEN) ' Clear IDCODE buffer
pst.Str(@MsgJTAGulating)
u.TXSEnable ' Enable level shifter outputs
jTDI := g#PROP_SDA ' TDI isn't used when we're just shifting data from the DR. Set TDI to a temporary pin so it doesn't interfere with enumeration.
' We assume the IDCODE is the default DR after reset
' Pin enumeration logic based on JTAGenum (http://deadhacker.com/2010/02/03/jtag-enumeration/)
num := 0 ' Counter of possible pinouts
ctr := 0
match := 0
xtdi := xtdo := xtck := xtms := 0
repeat jTDO from chStart to chEnd ' For every possible pin permutation (except TDI and TRST)...
repeat jTCK from chStart to chEnd
if (jTCK == jTDO)
next
repeat jTMS from chStart to chEnd
if (jTMS == jTCK) or (jTMS == jTDO)
next
if (pst.RxEmpty == 0) ' Abort scan if any key is pressed
if (type == 0)
JTAG_Scan_Cleanup(num, 0, xtdo, xtck, xtms) ' TDI isn't used during an IDCODE Scan
pst.Str(@ErrIDCODEAborted)
else
JTAG_Scan_Cleanup(num, xtdi, xtdo, xtck, xtms)
pst.Str(@ErrJTAGAborted)
pst.RxFlush
return
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH (in case there is a signal on the target that needs to be held HIGH, like TRST# or SRST#)
if (pinsLow == 1) ' Pulse channels LOW if requested by the user
u.Set_Pins_Low(chStart, chEnd) ' Set current channel range to output LOW
u.Pause(pinsLowDelay) ' Delay to stay asserted
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH
u.Pause(pinsHighDelay) ' Delay after deassertion before proceeding
jtag.Config(jTDI, jTDO, jTCK, jTMS) ' Configure JTAG
jtag.Get_Device_IDs(1, @value) ' Try to get a single Device ID (if it exists) by reading the DR
if (value <> -1) and (value & 1) ' Ignore if received Device ID is 0xFFFFFFFF or if bit 0 != 1
if (type == 0) ' IDCODE Scan
Display_JTAG_Pins ' Display current JTAG pinout
num += 1 ' Increment counter
xtdo := jTDO ' Keep track of most recent detection results
xtck := jTCK
xtms := jTMS
jPinsKnown := 1 ' Enable known pins flag
' Since we might not know how many devices are in the chain, try the maximum allowable number and verify the results afterwards
jtag.Get_Device_IDs(jtag#MAX_DEVICES_LEN, @id) ' We assume the IDCODE is the default DR after reset
repeat i from 0 to (jtag#MAX_DEVICES_LEN-1) ' For each device in the chain...
Display_Device_ID(id[i], i + 1, 0) ' Display Device ID of current device (without details)
else ' Combined IDCODE Scan and BYPASS Scan
' Now try to determine TDI by doing a BYPASS Test
repeat jTDI from chStart to chEnd ' For every remaining channel...
if (jTDI == jTMS) or (jTDI == jTCK) or (jTDI == jTDO)
next
if (pst.RxEmpty == 0) ' Abort scan if any key is pressed
JTAG_Scan_Cleanup(num, xtdi, xtdo, xtck, xtms)
pst.Str(@ErrJTAGAborted)
pst.RxFlush
return
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH (in case there is a signal on the target that needs to be held HIGH, like TRST# or SRST#)
if (pinsLow == 1) ' Pulse channels LOW if requested by the user
u.Set_Pins_Low(chStart, chEnd) ' Set current channel range to output LOW
u.Pause(pinsLowDelay) ' Delay to stay asserted
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH
u.Pause(pinsHighDelay) ' Delay after deassertion before proceeding
jtag.Config(jTDI, jTDO, jTCK, jTMS) ' Re-configure JTAG
value := jtag.Detect_Devices ' Get number of devices in the chain (if any)
' Run a BYPASS test to ensure TDO is actually passing TDI
data_in := rr.random ' Get 32-bit random number to use as the BYPASS pattern
data_out := jtag.Bypass_Test(value, data_in) ' Run the BYPASS instruction
if (data_in == data_out) ' If match, then we've found a JTAG interface on this current pinout
Display_JTAG_Pins ' Display current JTAG pinout
num += 1 ' Increment counter
xtdi := jTDI ' Keep track of most recent detection results
xtdo := jTDO
xtck := jTCK
xtms := jTMS
jPinsKnown := 1 ' Enable known pins flag
match := 1 ' Set flag to enable subsequent TRST# search
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH (in case there is a signal on the target that needs to be held HIGH, like TRST# or SRST#)
if (pinsLow == 1) ' Pulse channels LOW if requested by the user
u.Set_Pins_Low(chStart, chEnd) ' Set current channel range to output LOW
u.Pause(pinsLowDelay) ' Delay to stay asserted
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH
u.Pause(pinsHighDelay) ' Delay after deassertion before proceeding
jtag.Config(jTDI, jTDO, jTCK, jTMS) ' Re-configure JTAG
jtag.Get_Device_IDs(value, @id) ' We assume the IDCODE is the default DR after reset
repeat i from 0 to (value-1) ' For each device in the chain...
Display_Device_ID(id[i], i + 1, 0) ' Display Device ID of current device (without details)
quit ' Break out of the search for TDI and continue...
else
match := 0
' Progress indicator
++ctr
if (pinsLow == 0)
Display_Progress(ctr, 100, 1)
else
Display_Progress(ctr, 1, 1)
if (type == 0) or (type == 1 and match <> 0)
' Now try to determine if the TRST# pin is being used on the target
repeat jTRST from chStart to chEnd ' For every remaining channel...
if (jTRST == jTMS) or (jTRST == jTCK) or (jTRST == jTDO) or (jTRST == jTDI)
next
if (pst.RxEmpty == 0) ' Abort scan if any key is pressed
if (type == 0)
JTAG_Scan_Cleanup(num, 0, xtdo, xtck, xtms) ' TDI isn't used during an IDCODE Scan
pst.Str(@ErrIDCODEAborted)
else
JTAG_Scan_Cleanup(num, xtdi, xtdo, xtck, xtms)
pst.Str(@ErrJTAGAborted)
pst.RxFlush
return
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH (in case there is a signal on the target that needs to be held HIGH, like TRST# or SRST#)
if (pinsLow == 1) ' Pulse channels LOW if requested by the user
u.Set_Pins_Low(chStart, chEnd) ' Set current channel range to output LOW
u.Pause(pinsLowDelay) ' Delay to stay asserted
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH
u.Pause(pinsHighDelay) ' Delay after deassertion before proceeding
jtag.Config(jTDI, jTDO, jTCK, jTMS) ' Re-configure JTAG
dira[jTRST] := 1 ' Set current pin to output
outa[jTRST] := 0 ' Output LOW
u.Pause(100) ' Give target time to react
jtag.Get_Device_IDs(1, @value_new) ' Try to get a single Device ID again by reading the DR
if (value_new <> id[0]) ' If the new value doesn't match what we already have, then the current pin may be a reset line.
pst.Str(String("TRST#: ")) ' Display the pin number
pst.Dec(jTRST)
pst.Str(String(CR, LF))
' Progress indicator
++ctr
if (pinsLow == 0)
Display_Progress(ctr, 100, 0)
else
Display_Progress(ctr, 1, 0)
pst.Str(String(CR, LF))
' Progress indicator
++ctr
if (pinsLow == 0)
Display_Progress(ctr, 100, 1)
else
Display_Progress(ctr, 1, 1)
if (num == 0)
pst.Str(@ErrNoDeviceFound)
jPinsKnown := 0
if (type == 0)
JTAG_Scan_Cleanup(num, 0, xtdo, xtck, xtms) ' TDI isn't used during an IDCODE Scan
pst.Str(String(CR, LF, "IDCODE"))
else
JTAG_Scan_Cleanup(num, xtdi, xtdo, xtck, xtms)
pst.Str(String(CR, LF, "JTAG combined"))
pst.Str(@MsgScanComplete)
PRI BYPASS_Scan | value, value_new, ctr, num, data_in, data_out, xtdi, xtdo, xtck, xtms, tdiStart, tdiEnd, tdoStart, tdoEnd, tckStart, tckEnd, tmsStart, tmsEnd ' Identify JTAG pinout (BYPASS Scan)
num := 4 ' Number of pins needed to locate (TDI, TDO, TCK, TMS)
if (Get_Channels(num) == -1) ' Get the channel range to use
return
tdiStart := tdoStart := tmsStart := tckStart := chStart ' Set default start and end channels
tdiEnd := tdoEnd := tmsEnd := tckEnd := chEnd
if (Get_Pins_Known(0) == -1) ' Ask if any pins are known
return
if (jPinsKnown == 1)
pst.Str(@MsgUnknownPin)
if (Set_JTAG_Partial == -1)
return ' Abort if error
' If the user has entered a known pin, set it as both start and end to make it static during the scan
if (jTDI <> -2)
tdiStart := tdiEnd := jTDI
num -= 1
else
jTDI := 0 ' Reset pin
if (jTDO <> -2)
tdoStart := tdoEnd := jTDO
num -= 1
else
jTDO := 0
if (jTMS <> -2)
tmsStart := tmsEnd := jTMS
num -= 1
else
jTMS := 0
if (jTCK <> -2)
tckStart := tckEnd := jTCK
num -= 1
else
jTCK := 0
Display_Permutations((chEnd - chStart + 1) - (4 - num), num) ' Calculate number of permutations, accounting for any known channels
if (Get_Settings == -1) ' Get configurable scan settings
return
if (Wait_For_Space(@ErrBYPASSAborted) == -1)
return
pst.Str(@MsgJTAGulating)
u.TXSEnable ' Enable level shifter outputs
num := 0 ' Counter of possible pinouts
ctr := 0
xtdi := xtdo := xtck := xtms := 0
repeat jTDI from tdiStart to tdiEnd ' For every possible pin permutation (except TRST#)...
repeat jTDO from tdoStart to tdoEnd
if (jTDO == jTDI) ' Ensure each pin number is unique
next
repeat jTCK from tckStart to tckEnd
if (jTCK == jTDO) or (jTCK == jTDI)
next
repeat jTMS from tmsStart to tmsEnd
if (jTMS == jTCK) or (jTMS == jTDO) or (jTMS == jTDI)
next
if (pst.RxEmpty == 0) ' Abort scan if any key is pressed
JTAG_Scan_Cleanup(num, xtdi, xtdo, xtck, xtms)
pst.Str(@ErrBYPASSAborted)
pst.RxFlush
return
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH (in case there is a signal on the target that needs to be held HIGH, like TRST# or SRST#)
if (pinsLow == 1) ' Pulse channels LOW if requested by the user
u.Set_Pins_Low(chStart, chEnd) ' Set current channel range to output LOW
u.Pause(pinsLowDelay) ' Delay to stay asserted
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH
u.Pause(pinsHighDelay) ' Delay after deassertion before proceeding
jtag.Config(jTDI, jTDO, jTCK, jTMS) ' Configure JTAG
value := jtag.Detect_Devices
' Run a BYPASS test to ensure TDO is actually passing TDI
data_in := rr.random ' Get 32-bit random number to use as the BYPASS pattern
data_out := jtag.Bypass_Test(value, data_in) ' Run the BYPASS instruction
if (data_in == data_out) ' If match, then continue with this current pinout
Display_JTAG_Pins ' Display pinout
num += 1 ' Increment counter
xtdi := jTDI ' Keep track of most recent detection results
xtdo := jTDO
xtck := jTCK
xtms := jTMS
jPinsKnown := 1 ' Enable known pins flag
' Now try to determine if the TRST# pin is being used on the target
repeat jTRST from chStart to chEnd ' For every remaining channel...
if (jTRST == jTMS) or (jTRST == jTCK) or (jTRST == jTDO) or (jTRST == jTDI)
next
if (pst.RxEmpty == 0) ' Abort scan if any key is pressed
JTAG_Scan_Cleanup(num, xtdi, xtdo, xtck, xtms)
pst.Str(@ErrBYPASSAborted)
pst.RxFlush
return
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH (in case there is a signal on the target that needs to be held HIGH, like TRST# or SRST#)
if (pinsLow == 1) ' Pulse channels LOW if requested by the user
u.Set_Pins_Low(chStart, chEnd) ' Set current channel range to output LOW
u.Pause(pinsLowDelay) ' Delay to stay asserted
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH
u.Pause(pinsHighDelay) ' Delay after deassertion before proceeding
jtag.Config(jTDI, jTDO, jTCK, jTMS) ' Re-configure JTAG
dira[jTRST] := 1 ' Set current pin to output
outa[jTRST] := 0 ' Output LOW
u.Pause(100) ' Give target time to react
value_new := jtag.Detect_Devices
if (value_new <> value) and (value_new =< jtag#MAX_DEVICES_LEN) ' If the new value doesn't match what we already have, then the current pin may be a reset line.
pst.Str(String("TRST#: ")) ' Display the pin number
pst.Dec(jTRST)
pst.Str(String(CR, LF))
' Progress indicator
++ctr
if (pinsLow == 0)
Display_Progress(ctr, 10, 0)
else
Display_Progress(ctr, 1, 0)
pst.Str(@MsgDevicesDetected)
pst.Dec(value)
pst.Str(String(CR, LF))
' Progress indicator
++ctr
if (pinsLow == 0)
Display_Progress(ctr, 10, 1)
else
Display_Progress(ctr, 1, 1)
if (num == 0)
pst.Str(@ErrNoDeviceFound)
jPinsKnown := 0
JTAG_Scan_Cleanup(num, xtdi, xtdo, xtck, xtms)
pst.Str(String(CR, LF, "BYPASS"))
pst.Str(@MsgScanComplete)
PRI RTCK_Scan : err | ctr, num, known, matches, xtck, xrtck, tckStart, tckEnd ' Identify RTCK (Adaptive Clocking)
num := 2 ' Number of pins needed to locate (TCK, RTCK)
if (Get_Channels(num) == -1) ' Get the channel range to use
return
tckStart := chStart ' Set default start and end channels
tckEnd := chEnd
known := jPinsKnown
pst.Str(String(CR, LF, "Is TCK already known? ["))
if (known == 0)
pst.Str(String("y/N]: "))
else
pst.Str(String("Y/n]: "))
pst.StrInMax(@vCmd, MAX_LEN_CMD) ' Wait here to receive a carriage return terminated string or one of MAX_LEN_CMD bytes (the result is null terminated)
if (strsize(@vCmd) =< 1) ' We're only looking for a single character (or NULL, which will have a string size of 0)
case vCmd[0] ' Check the first character of the input string
0: ' The user only entered a CR, so keep the same value and pass through.
"N", "n":
known := 0 ' Disable flag
"Y", "y": ' If the user wants to use a partial pinout
known := 1 ' Enable flag
other: ' Any other key causes an error
pst.Str(@ErrOutOfRange)
return
else
pst.Str(@ErrOutOfRange)
return
if (known == 1)
pst.Str(String(CR, LF, "Enter X if pin is unknown."))
pst.Str(@MsgEnterTCKPin)
pst.Dec(jTCK) ' Display current value
pst.Str(String("]: "))
xtck := Get_Pin ' Get new value from user
if (xtck == -1) ' If carriage return was pressed...
xtck := jTCK ' Keep current setting
if (xtck < -2) or (xtck > chEnd) ' If entered value is out of range, abort
pst.Str(@ErrOutOfRange)
return -1
if (xtck <> -2)
tckStart := tckEnd := xtck
num -= 1
Display_Permutations((chEnd - chStart + 1) - (2 - num), num) ' Calculate number of permutations, accounting for any known channels
if (Get_Settings == -1) ' Get configurable scan settings
return
if (Wait_For_Space(@ErrRTCKAborted) == -1)
return
pst.Str(@MsgJTAGulating)
u.TXSEnable ' Enable level shifter outputs
num := 0 ' Counter of possibly good pinouts
ctr := 0 ' Counter of total loop iterations
repeat xtck from tckStart to tckEnd ' For every possible pin permutation
repeat xrtck from chStart to chEnd
if (xtck == xrtck)
next
if (pst.RxEmpty == 0) ' Abort scan if any key is pressed
pst.Str(@ErrRTCKAborted)
pst.RxFlush
return
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH (in case there is a signal on the target that needs to be held HIGH, like TRST# or SRST#)
if (pinsLow == 1) ' Pulse channels LOW if requested by the user
u.Set_Pins_Low(chStart, chEnd) ' Set current channel range to output LOW
u.Pause(pinsLowDelay) ' Delay to stay asserted
u.Set_Pins_High(chStart, chEnd) ' Set current channel range to output HIGH
u.Pause(pinsHighDelay) ' Delay after deassertion before proceeding
{{
_____//_____
TCK (from JTAGulator to target): ______/ \__________
| |
|<---->| _____//_____
RTCK (from target to JTAGulator): ___|______|/ \__________
^ Delay time varies with target
}}
matches := 0
dira[xrtck] := 0 ' Set current pin as input
dira[xtck] := 1 ' Set current pin as output
outa[xtck] := 0
repeat NUM_RTCK_ITERATIONS
!outa[xtck]
u.Pause(10) ' Delay for target to propagate signal from TCK to RTCK (if it exists)
if (outa[xtck] == ina[xrtck]) 'Check if RTCK mirrors TCK
++matches
if (matches == NUM_RTCK_ITERATIONS) ' Valid candidates should match 100% of the time
++num
pst.Str(String(CR, LF, "TCK: "))
pst.Dec(xtck)
pst.Str(String(CR, LF, "RTCK: "))
pst.Dec(xrtck)
pst.Str(String(CR, LF))
' Progress indicator
++ctr
if (pinsLow == 0)
Display_Progress(ctr, 10, 1)
else
Display_Progress(ctr, 1, 1)
if (num == 0)
pst.Str(@ErrNoDeviceFound)
pst.Str(String(CR, LF, "RTCK"))
pst.Str(@MsgScanComplete)
PRI IDCODE_Known | id[32 {jtag#MAX_DEVICES_LEN}], i, xtdi ' Get JTAG Device IDs (Pinout already known)
xtdi := jTDI ' Save current value, if it exists
if (Set_JTAG(0) == -1) ' Ask user for the known JTAG pinout
return ' Abort if error
longfill(@id, 0, jtag#MAX_DEVICES_LEN) ' Clear IDCODE buffer
u.TXSEnable ' Enable level shifter outputs
u.Set_Pins_High(0, g#MAX_CHAN-1) ' In case there is a signal on the target that needs to be held HIGH, like TRST# or SRST#
jtag.Config(jTDI, jTDO, jTCK, jTMS) ' Configure JTAG
' Since we might not know how many devices are in the chain, try the maximum allowable number and verify the results afterwards
jtag.Get_Device_IDs(jtag#MAX_DEVICES_LEN, @id) ' We assume the IDCODE is the default DR after reset
repeat i from 0 to (jtag#MAX_DEVICES_LEN-1) ' For each device in the chain...
Display_Device_ID(id[i], i + 1, 1) ' Display Device ID of current device (with details)
if (i == 0)
pst.Str(@ErrNoDeviceFound)
jPinsKnown := 0