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

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)