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

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)