jtag_vpi: added an option to stop simulation on exit
[openocd.git] / src / jtag / drivers / jtag_vpi.c
1 /*
2 * JTAG to VPI driver
3 *
4 * Copyright (C) 2013 Franck Jullien, <elec4fun@gmail.com>
5 *
6 * See file CREDITS for list of people who contributed to this
7 * project.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <jtag/interface.h>
28 #ifdef HAVE_ARPA_INET_H
29 #include <arpa/inet.h>
30 #endif
31
32 #ifndef _WIN32
33 #include <netinet/tcp.h>
34 #endif
35
36 #include <string.h>
37
38 #define NO_TAP_SHIFT 0
39 #define TAP_SHIFT 1
40
41 #define SERVER_ADDRESS "127.0.0.1"
42 #define SERVER_PORT 5555
43
44 #define XFERT_MAX_SIZE 512
45
46 #define CMD_RESET 0
47 #define CMD_TMS_SEQ 1
48 #define CMD_SCAN_CHAIN 2
49 #define CMD_SCAN_CHAIN_FLIP_TMS 3
50 #define CMD_STOP_SIMU 4
51
52 /* jtag_vpi server port and address to connect to */
53 static int server_port = SERVER_PORT;
54 static char *server_address;
55
56 /* Send CMD_STOP_SIMU to server when OpenOCD exits? */
57 static bool stop_sim_on_exit;
58
59 static int sockfd;
60 static struct sockaddr_in serv_addr;
61
62 /* One jtag_vpi "packet" as sent over a TCP channel. */
63 struct vpi_cmd {
64 union {
65 uint32_t cmd;
66 unsigned char cmd_buf[4];
67 };
68 unsigned char buffer_out[XFERT_MAX_SIZE];
69 unsigned char buffer_in[XFERT_MAX_SIZE];
70 union {
71 uint32_t length;
72 unsigned char length_buf[4];
73 };
74 union {
75 uint32_t nb_bits;
76 unsigned char nb_bits_buf[4];
77 };
78 };
79
80 static char *jtag_vpi_cmd_to_str(int cmd_num)
81 {
82 switch (cmd_num) {
83 case CMD_RESET:
84 return "CMD_RESET";
85 case CMD_TMS_SEQ:
86 return "CMD_TMS_SEQ";
87 case CMD_SCAN_CHAIN:
88 return "CMD_SCAN_CHAIN";
89 case CMD_SCAN_CHAIN_FLIP_TMS:
90 return "CMD_SCAN_CHAIN_FLIP_TMS";
91 case CMD_STOP_SIMU:
92 return "CMD_STOP_SIMU";
93 default:
94 return "<unknown>";
95 }
96 }
97
98 static int jtag_vpi_send_cmd(struct vpi_cmd *vpi)
99 {
100 int retval;
101
102 /* Optional low-level JTAG debug */
103 if (LOG_LEVEL_IS(LOG_LVL_DEBUG_IO)) {
104 if (vpi->nb_bits > 0) {
105 /* command with a non-empty data payload */
106 char *char_buf = buf_to_str(vpi->buffer_out,
107 (vpi->nb_bits > DEBUG_JTAG_IOZ)
108 ? DEBUG_JTAG_IOZ
109 : vpi->nb_bits,
110 16);
111 LOG_DEBUG_IO("sending JTAG VPI cmd: cmd=%s, "
112 "length=%" PRIu32 ", "
113 "nb_bits=%" PRIu32 ", "
114 "buf_out=0x%s%s",
115 jtag_vpi_cmd_to_str(vpi->cmd),
116 vpi->length,
117 vpi->nb_bits,
118 char_buf,
119 (vpi->nb_bits > DEBUG_JTAG_IOZ) ? "(...)" : "");
120 free(char_buf);
121 } else {
122 /* command without data payload */
123 LOG_DEBUG_IO("sending JTAG VPI cmd: cmd=%s, "
124 "length=%" PRIu32 ", "
125 "nb_bits=%" PRIu32,
126 jtag_vpi_cmd_to_str(vpi->cmd),
127 vpi->length,
128 vpi->nb_bits);
129 }
130 }
131
132 /* Use little endian when transmitting/receiving jtag_vpi cmds.
133 The choice of little endian goes against usual networking conventions
134 but is intentional to remain compatible with most older OpenOCD builds
135 (i.e. builds on little-endian platforms). */
136 h_u32_to_le(vpi->cmd_buf, vpi->cmd);
137 h_u32_to_le(vpi->length_buf, vpi->length);
138 h_u32_to_le(vpi->nb_bits_buf, vpi->nb_bits);
139
140 retry_write:
141 retval = write_socket(sockfd, vpi, sizeof(struct vpi_cmd));
142
143 if (retval < 0) {
144 /* Account for the case when socket write is interrupted. */
145 #ifdef _WIN32
146 int wsa_err = WSAGetLastError();
147 if (wsa_err == WSAEINTR)
148 goto retry_write;
149 #else
150 if (errno == EINTR)
151 goto retry_write;
152 #endif
153 /* Otherwise this is an error using the socket, most likely fatal
154 for the connection. B*/
155 log_socket_error("jtag_vpi xmit");
156 /* TODO: Clean way how adapter drivers can report fatal errors
157 to upper layers of OpenOCD and let it perform an orderly shutdown? */
158 exit(-1);
159 } else if (retval < (int)sizeof(struct vpi_cmd)) {
160 /* This means we could not send all data, which is most likely fatal
161 for the jtag_vpi connection (the underlying TCP connection likely not
162 usable anymore) */
163 LOG_ERROR("Could not send all data through jtag_vpi connection.");
164 exit(-1);
165 }
166
167 /* Otherwise the packet has been sent successfully. */
168 return ERROR_OK;
169 }
170
171 static int jtag_vpi_receive_cmd(struct vpi_cmd *vpi)
172 {
173 unsigned bytes_buffered = 0;
174 while (bytes_buffered < sizeof(struct vpi_cmd)) {
175 int bytes_to_receive = sizeof(struct vpi_cmd) - bytes_buffered;
176 int retval = read_socket(sockfd, ((char *)vpi) + bytes_buffered, bytes_to_receive);
177 if (retval < 0) {
178 #ifdef _WIN32
179 int wsa_err = WSAGetLastError();
180 if (wsa_err == WSAEINTR) {
181 /* socket read interrupted by WSACancelBlockingCall() */
182 continue;
183 }
184 #else
185 if (errno == EINTR) {
186 /* socket read interrupted by a signal */
187 continue;
188 }
189 #endif
190 /* Otherwise, this is an error when accessing the socket. */
191 log_socket_error("jtag_vpi recv");
192 exit(-1);
193 } else if (retval == 0) {
194 /* Connection closed by the other side */
195 LOG_ERROR("Connection prematurely closed by jtag_vpi server.");
196 exit(-1);
197 }
198 /* Otherwise, we have successfully received some data */
199 bytes_buffered += retval;
200 }
201
202 /* Use little endian when transmitting/receiving jtag_vpi cmds. */
203 vpi->cmd = le_to_h_u32(vpi->cmd_buf);
204 vpi->length = le_to_h_u32(vpi->length_buf);
205 vpi->nb_bits = le_to_h_u32(vpi->nb_bits_buf);
206
207 return ERROR_OK;
208 }
209
210 /**
211 * jtag_vpi_reset - ask to reset the JTAG device
212 * @trst: 1 if TRST is to be asserted
213 * @srst: 1 if SRST is to be asserted
214 */
215 static int jtag_vpi_reset(int trst, int srst)
216 {
217 struct vpi_cmd vpi;
218 memset(&vpi, 0, sizeof(struct vpi_cmd));
219
220 vpi.cmd = CMD_RESET;
221 vpi.length = 0;
222 return jtag_vpi_send_cmd(&vpi);
223 }
224
225 /**
226 * jtag_vpi_tms_seq - ask a TMS sequence transition to JTAG
227 * @bits: TMS bits to be written (bit0, bit1 .. bitN)
228 * @nb_bits: number of TMS bits (between 1 and 8)
229 *
230 * Write a serie of TMS transitions, where each transition consists in :
231 * - writing out TCK=0, TMS=<new_state>, TDI=<???>
232 * - writing out TCK=1, TMS=<new_state>, TDI=<???> which triggers the transition
233 * The function ensures that at the end of the sequence, the clock (TCK) is put
234 * low.
235 */
236 static int jtag_vpi_tms_seq(const uint8_t *bits, int nb_bits)
237 {
238 struct vpi_cmd vpi;
239 int nb_bytes;
240
241 memset(&vpi, 0, sizeof(struct vpi_cmd));
242 nb_bytes = DIV_ROUND_UP(nb_bits, 8);
243
244 vpi.cmd = CMD_TMS_SEQ;
245 memcpy(vpi.buffer_out, bits, nb_bytes);
246 vpi.length = nb_bytes;
247 vpi.nb_bits = nb_bits;
248
249 return jtag_vpi_send_cmd(&vpi);
250 }
251
252 /**
253 * jtag_vpi_path_move - ask a TMS sequence transition to JTAG
254 * @cmd: path transition
255 *
256 * Write a serie of TMS transitions, where each transition consists in :
257 * - writing out TCK=0, TMS=<new_state>, TDI=<???>
258 * - writing out TCK=1, TMS=<new_state>, TDI=<???> which triggers the transition
259 * The function ensures that at the end of the sequence, the clock (TCK) is put
260 * low.
261 */
262
263 static int jtag_vpi_path_move(struct pathmove_command *cmd)
264 {
265 uint8_t trans[DIV_ROUND_UP(cmd->num_states, 8)];
266
267 memset(trans, 0, DIV_ROUND_UP(cmd->num_states, 8));
268
269 for (int i = 0; i < cmd->num_states; i++) {
270 if (tap_state_transition(tap_get_state(), true) == cmd->path[i])
271 buf_set_u32(trans, i, 1, 1);
272 tap_set_state(cmd->path[i]);
273 }
274
275 return jtag_vpi_tms_seq(trans, cmd->num_states);
276 }
277
278 /**
279 * jtag_vpi_tms - ask a tms command
280 * @cmd: tms command
281 */
282 static int jtag_vpi_tms(struct tms_command *cmd)
283 {
284 return jtag_vpi_tms_seq(cmd->bits, cmd->num_bits);
285 }
286
287 static int jtag_vpi_state_move(tap_state_t state)
288 {
289 if (tap_get_state() == state)
290 return ERROR_OK;
291
292 uint8_t tms_scan = tap_get_tms_path(tap_get_state(), state);
293 int tms_len = tap_get_tms_path_len(tap_get_state(), state);
294
295 int retval = jtag_vpi_tms_seq(&tms_scan, tms_len);
296 if (retval != ERROR_OK)
297 return retval;
298
299 tap_set_state(state);
300
301 return ERROR_OK;
302 }
303
304 static int jtag_vpi_queue_tdi_xfer(uint8_t *bits, int nb_bits, int tap_shift)
305 {
306 struct vpi_cmd vpi;
307 int nb_bytes = DIV_ROUND_UP(nb_bits, 8);
308
309 memset(&vpi, 0, sizeof(struct vpi_cmd));
310
311 vpi.cmd = tap_shift ? CMD_SCAN_CHAIN_FLIP_TMS : CMD_SCAN_CHAIN;
312
313 if (bits)
314 memcpy(vpi.buffer_out, bits, nb_bytes);
315 else
316 memset(vpi.buffer_out, 0xff, nb_bytes);
317
318 vpi.length = nb_bytes;
319 vpi.nb_bits = nb_bits;
320
321 int retval = jtag_vpi_send_cmd(&vpi);
322 if (retval != ERROR_OK)
323 return retval;
324
325 retval = jtag_vpi_receive_cmd(&vpi);
326 if (retval != ERROR_OK)
327 return retval;
328
329 /* Optional low-level JTAG debug */
330 if (LOG_LEVEL_IS(LOG_LVL_DEBUG_IO)) {
331 char *char_buf = buf_to_str(vpi.buffer_in,
332 (nb_bits > DEBUG_JTAG_IOZ) ? DEBUG_JTAG_IOZ : nb_bits,
333 16);
334 LOG_DEBUG_IO("recvd JTAG VPI data: nb_bits=%d, buf_in=0x%s%s",
335 nb_bits, char_buf, (nb_bits > DEBUG_JTAG_IOZ) ? "(...)" : "");
336 free(char_buf);
337 }
338
339 if (bits)
340 memcpy(bits, vpi.buffer_in, nb_bytes);
341
342 return ERROR_OK;
343 }
344
345 /**
346 * jtag_vpi_queue_tdi - short description
347 * @bits: bits to be queued on TDI (or NULL if 0 are to be queued)
348 * @nb_bits: number of bits
349 */
350 static int jtag_vpi_queue_tdi(uint8_t *bits, int nb_bits, int tap_shift)
351 {
352 int nb_xfer = DIV_ROUND_UP(nb_bits, XFERT_MAX_SIZE * 8);
353 int retval;
354
355 while (nb_xfer) {
356 if (nb_xfer == 1) {
357 retval = jtag_vpi_queue_tdi_xfer(bits, nb_bits, tap_shift);
358 if (retval != ERROR_OK)
359 return retval;
360 } else {
361 retval = jtag_vpi_queue_tdi_xfer(bits, XFERT_MAX_SIZE * 8, NO_TAP_SHIFT);
362 if (retval != ERROR_OK)
363 return retval;
364 nb_bits -= XFERT_MAX_SIZE * 8;
365 if (bits)
366 bits += XFERT_MAX_SIZE;
367 }
368
369 nb_xfer--;
370 }
371
372 return ERROR_OK;
373 }
374
375 /**
376 * jtag_vpi_clock_tms - clock a TMS transition
377 * @tms: the TMS to be sent
378 *
379 * Triggers a TMS transition (ie. one JTAG TAP state move).
380 */
381 static int jtag_vpi_clock_tms(int tms)
382 {
383 const uint8_t tms_0 = 0;
384 const uint8_t tms_1 = 1;
385
386 return jtag_vpi_tms_seq(tms ? &tms_1 : &tms_0, 1);
387 }
388
389 /**
390 * jtag_vpi_scan - launches a DR-scan or IR-scan
391 * @cmd: the command to launch
392 *
393 * Launch a JTAG IR-scan or DR-scan
394 *
395 * Returns ERROR_OK if OK, ERROR_xxx if a read/write error occured.
396 */
397 static int jtag_vpi_scan(struct scan_command *cmd)
398 {
399 int scan_bits;
400 uint8_t *buf = NULL;
401 int retval = ERROR_OK;
402
403 scan_bits = jtag_build_buffer(cmd, &buf);
404
405 if (cmd->ir_scan) {
406 retval = jtag_vpi_state_move(TAP_IRSHIFT);
407 if (retval != ERROR_OK)
408 return retval;
409 } else {
410 retval = jtag_vpi_state_move(TAP_DRSHIFT);
411 if (retval != ERROR_OK)
412 return retval;
413 }
414
415 if (cmd->end_state == TAP_DRSHIFT) {
416 retval = jtag_vpi_queue_tdi(buf, scan_bits, NO_TAP_SHIFT);
417 if (retval != ERROR_OK)
418 return retval;
419 } else {
420 retval = jtag_vpi_queue_tdi(buf, scan_bits, TAP_SHIFT);
421 if (retval != ERROR_OK)
422 return retval;
423 }
424
425 if (cmd->end_state != TAP_DRSHIFT) {
426 /*
427 * As our JTAG is in an unstable state (IREXIT1 or DREXIT1), move it
428 * forward to a stable IRPAUSE or DRPAUSE.
429 */
430 retval = jtag_vpi_clock_tms(0);
431 if (retval != ERROR_OK)
432 return retval;
433
434 if (cmd->ir_scan)
435 tap_set_state(TAP_IRPAUSE);
436 else
437 tap_set_state(TAP_DRPAUSE);
438 }
439
440 retval = jtag_read_buffer(buf, cmd);
441 if (retval != ERROR_OK)
442 return retval;
443
444 if (buf)
445 free(buf);
446
447 if (cmd->end_state != TAP_DRSHIFT) {
448 retval = jtag_vpi_state_move(cmd->end_state);
449 if (retval != ERROR_OK)
450 return retval;
451 }
452
453 return ERROR_OK;
454 }
455
456 static int jtag_vpi_runtest(int cycles, tap_state_t state)
457 {
458 int retval;
459
460 retval = jtag_vpi_state_move(TAP_IDLE);
461 if (retval != ERROR_OK)
462 return retval;
463
464 retval = jtag_vpi_queue_tdi(NULL, cycles, NO_TAP_SHIFT);
465 if (retval != ERROR_OK)
466 return retval;
467
468 return jtag_vpi_state_move(state);
469 }
470
471 static int jtag_vpi_stableclocks(int cycles)
472 {
473 uint8_t tms_bits[4];
474 int cycles_remain = cycles;
475 int nb_bits;
476 int retval;
477 const int CYCLES_ONE_BATCH = sizeof(tms_bits) * 8;
478
479 assert(cycles >= 0);
480
481 /* use TMS=1 in TAP RESET state, TMS=0 in all other stable states */
482 memset(&tms_bits, (tap_get_state() == TAP_RESET) ? 0xff : 0x00, sizeof(tms_bits));
483
484 /* send the TMS bits */
485 while (cycles_remain > 0) {
486 nb_bits = (cycles_remain < CYCLES_ONE_BATCH) ? cycles_remain : CYCLES_ONE_BATCH;
487 retval = jtag_vpi_tms_seq(tms_bits, nb_bits);
488 if (retval != ERROR_OK)
489 return retval;
490 cycles_remain -= nb_bits;
491 }
492
493 return ERROR_OK;
494 }
495
496 static int jtag_vpi_execute_queue(void)
497 {
498 struct jtag_command *cmd;
499 int retval = ERROR_OK;
500
501 for (cmd = jtag_command_queue; retval == ERROR_OK && cmd != NULL;
502 cmd = cmd->next) {
503 switch (cmd->type) {
504 case JTAG_RESET:
505 retval = jtag_vpi_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst);
506 break;
507 case JTAG_RUNTEST:
508 retval = jtag_vpi_runtest(cmd->cmd.runtest->num_cycles,
509 cmd->cmd.runtest->end_state);
510 break;
511 case JTAG_STABLECLOCKS:
512 retval = jtag_vpi_stableclocks(cmd->cmd.stableclocks->num_cycles);
513 break;
514 case JTAG_TLR_RESET:
515 retval = jtag_vpi_state_move(cmd->cmd.statemove->end_state);
516 break;
517 case JTAG_PATHMOVE:
518 retval = jtag_vpi_path_move(cmd->cmd.pathmove);
519 break;
520 case JTAG_TMS:
521 retval = jtag_vpi_tms(cmd->cmd.tms);
522 break;
523 case JTAG_SLEEP:
524 jtag_sleep(cmd->cmd.sleep->us);
525 break;
526 case JTAG_SCAN:
527 retval = jtag_vpi_scan(cmd->cmd.scan);
528 break;
529 default:
530 LOG_ERROR("BUG: unknown JTAG command type 0x%X",
531 cmd->type);
532 retval = ERROR_FAIL;
533 break;
534 }
535 }
536
537 return retval;
538 }
539
540 static int jtag_vpi_init(void)
541 {
542 int flag = 1;
543
544 sockfd = socket(AF_INET, SOCK_STREAM, 0);
545 if (sockfd < 0) {
546 LOG_ERROR("Could not create socket");
547 return ERROR_FAIL;
548 }
549
550 memset(&serv_addr, 0, sizeof(serv_addr));
551
552 serv_addr.sin_family = AF_INET;
553 serv_addr.sin_port = htons(server_port);
554
555 if (!server_address)
556 server_address = strdup(SERVER_ADDRESS);
557
558 serv_addr.sin_addr.s_addr = inet_addr(server_address);
559
560 if (serv_addr.sin_addr.s_addr == INADDR_NONE) {
561 LOG_ERROR("inet_addr error occured");
562 return ERROR_FAIL;
563 }
564
565 if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
566 close(sockfd);
567 LOG_ERROR("Can't connect to %s : %u", server_address, server_port);
568 return ERROR_COMMAND_CLOSE_CONNECTION;
569 }
570
571 if (serv_addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
572 /* This increases performance drematically for local
573 * connections, which is the most likely arrangement
574 * for a VPI connection. */
575 setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
576 }
577
578 LOG_INFO("Connection to %s : %u succeed", server_address, server_port);
579
580 return ERROR_OK;
581 }
582
583 static int jtag_vpi_stop_simulation(void)
584 {
585 struct vpi_cmd cmd;
586 memset(&cmd, 0, sizeof(struct vpi_cmd));
587 cmd.length = 0;
588 cmd.nb_bits = 0;
589 cmd.cmd = CMD_STOP_SIMU;
590 return jtag_vpi_send_cmd(&cmd);
591 }
592
593 static int jtag_vpi_quit(void)
594 {
595 if (stop_sim_on_exit) {
596 if (jtag_vpi_stop_simulation() != ERROR_OK)
597 LOG_WARNING("jtag_vpi: failed to send \"stop simulation\" command");
598 }
599 if (close_socket(sockfd) != 0) {
600 LOG_WARNING("jtag_vpi: could not close jtag_vpi client socket");
601 log_socket_error("jtag_vpi");
602 }
603 free(server_address);
604 return ERROR_OK;
605 }
606
607 COMMAND_HANDLER(jtag_vpi_set_port)
608 {
609 if (CMD_ARGC == 0)
610 LOG_WARNING("You need to set a port number");
611 else
612 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], server_port);
613
614 LOG_INFO("Set server port to %u", server_port);
615
616 return ERROR_OK;
617 }
618
619 COMMAND_HANDLER(jtag_vpi_set_address)
620 {
621 free(server_address);
622
623 if (CMD_ARGC == 0) {
624 LOG_WARNING("You need to set an address");
625 server_address = strdup(SERVER_ADDRESS);
626 } else
627 server_address = strdup(CMD_ARGV[0]);
628
629 LOG_INFO("Set server address to %s", server_address);
630
631 return ERROR_OK;
632 }
633
634 COMMAND_HANDLER(jtag_vpi_stop_sim_on_exit_handler)
635 {
636 if (CMD_ARGC != 1) {
637 LOG_ERROR("jtag_vpi_stop_sim_on_exit expects 1 argument (on|off)");
638 return ERROR_COMMAND_SYNTAX_ERROR;
639 } else {
640 COMMAND_PARSE_ON_OFF(CMD_ARGV[0], stop_sim_on_exit);
641 }
642 return ERROR_OK;
643 }
644
645 static const struct command_registration jtag_vpi_command_handlers[] = {
646 {
647 .name = "jtag_vpi_set_port",
648 .handler = &jtag_vpi_set_port,
649 .mode = COMMAND_CONFIG,
650 .help = "set the port of the VPI server",
651 .usage = "tcp_port_num",
652 },
653 {
654 .name = "jtag_vpi_set_address",
655 .handler = &jtag_vpi_set_address,
656 .mode = COMMAND_CONFIG,
657 .help = "set the address of the VPI server",
658 .usage = "ipv4_addr",
659 },
660 {
661 .name = "jtag_vpi_stop_sim_on_exit",
662 .handler = &jtag_vpi_stop_sim_on_exit_handler,
663 .mode = COMMAND_CONFIG,
664 .help = "Configure if simulation stop command shall be sent "
665 "before OpenOCD exits (default: off)",
666 .usage = "<on|off>",
667 },
668 COMMAND_REGISTRATION_DONE
669 };
670
671 static struct jtag_interface jtag_vpi_interface = {
672 .supported = DEBUG_CAP_TMS_SEQ,
673 .execute_queue = jtag_vpi_execute_queue,
674 };
675
676 struct adapter_driver jtag_vpi_adapter_driver = {
677 .name = "jtag_vpi",
678 .transports = jtag_only,
679 .commands = jtag_vpi_command_handlers,
680
681 .init = jtag_vpi_init,
682 .quit = jtag_vpi_quit,
683
684 .jtag_ops = &jtag_vpi_interface,
685 };

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)