- further work on ETB decoding (not yet functional)
[openocd.git] / src / target / etb.c
1 /***************************************************************************
2 * Copyright (C) 2007 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "arm7_9_common.h"
25 #include "etb.h"
26
27 #include "log.h"
28 #include "types.h"
29 #include "binarybuffer.h"
30 #include "target.h"
31 #include "register.h"
32 #include "jtag.h"
33
34 #include <stdlib.h>
35
36 char* etb_reg_list[] =
37 {
38 "ETB_identification",
39 "ETB_ram_depth",
40 "ETB_ram_width",
41 "ETB_status",
42 "ETB_ram_data",
43 "ETB_ram_read_pointer",
44 "ETB_ram_write_pointer",
45 "ETB_trigger_counter",
46 "ETB_control",
47 };
48
49 int etb_reg_arch_type = -1;
50
51 int etb_get_reg(reg_t *reg);
52 int etb_set_reg(reg_t *reg, u32 value);
53 int etb_set_reg_w_exec(reg_t *reg, u8 *buf);
54
55 int etb_write_reg(reg_t *reg, u32 value);
56 int etb_read_reg(reg_t *reg);
57
58 int handle_arm7_9_etb_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
59 int handle_arm7_9_etb_dump_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
60
61 char *etmv1_branch_reason_string[] =
62 {
63 "normal pc change", "tracing enabled", "restart after FIFO overflow",
64 "exit from debug state", "peridoic synchronization point",
65 "reserved", "reserved", "reserved"
66 };
67
68
69 int etb_set_instr(etb_t *etb, u32 new_instr)
70 {
71 jtag_device_t *device = jtag_get_device(etb->chain_pos);
72
73 if (buf_get_u32(device->cur_instr, 0, device->ir_length) != new_instr)
74 {
75 scan_field_t field;
76
77 field.device = etb->chain_pos;
78 field.num_bits = device->ir_length;
79 field.out_value = calloc(CEIL(field.num_bits, 8), 1);
80 buf_set_u32(field.out_value, 0, field.num_bits, new_instr);
81 field.out_mask = NULL;
82 field.in_value = NULL;
83 field.in_check_value = NULL;
84 field.in_check_mask = NULL;
85 field.in_handler = NULL;
86 field.in_handler_priv = NULL;
87
88 jtag_add_ir_scan(1, &field, -1, NULL);
89
90 free(field.out_value);
91 }
92
93 return ERROR_OK;
94 }
95
96 int etb_scann(etb_t *etb, u32 new_scan_chain)
97 {
98 if(etb->cur_scan_chain != new_scan_chain)
99 {
100 scan_field_t field;
101
102 field.device = etb->chain_pos;
103 field.num_bits = 5;
104 field.out_value = calloc(CEIL(field.num_bits, 8), 1);
105 buf_set_u32(field.out_value, 0, field.num_bits, new_scan_chain);
106 field.out_mask = NULL;
107 field.in_value = NULL;
108 field.in_check_value = NULL;
109 field.in_check_mask = NULL;
110 field.in_handler = NULL;
111 field.in_handler_priv = NULL;
112
113 /* select INTEST instruction */
114 etb_set_instr(etb, 0x2);
115 jtag_add_dr_scan(1, &field, -1, NULL);
116
117 etb->cur_scan_chain = new_scan_chain;
118
119 free(field.out_value);
120 }
121
122 return ERROR_OK;
123 }
124
125 reg_cache_t* etb_build_reg_cache(etb_t *etb)
126 {
127 reg_cache_t *reg_cache = malloc(sizeof(reg_cache_t));
128 reg_t *reg_list = NULL;
129 etb_reg_t *arch_info = NULL;
130 int num_regs = 9;
131 int i;
132
133 /* register a register arch-type for etm registers only once */
134 if (etb_reg_arch_type == -1)
135 etb_reg_arch_type = register_reg_arch_type(etb_get_reg, etb_set_reg_w_exec);
136
137 /* the actual registers are kept in two arrays */
138 reg_list = calloc(num_regs, sizeof(reg_t));
139 arch_info = calloc(num_regs, sizeof(etb_reg_t));
140
141 /* fill in values for the reg cache */
142 reg_cache->name = "etb registers";
143 reg_cache->next = NULL;
144 reg_cache->reg_list = reg_list;
145 reg_cache->num_regs = num_regs;
146
147 /* set up registers */
148 for (i = 0; i < num_regs; i++)
149 {
150 reg_list[i].name = etb_reg_list[i];
151 reg_list[i].size = 32;
152 reg_list[i].dirty = 0;
153 reg_list[i].valid = 0;
154 reg_list[i].bitfield_desc = NULL;
155 reg_list[i].num_bitfields = 0;
156 reg_list[i].value = calloc(1, 4);
157 reg_list[i].arch_info = &arch_info[i];
158 reg_list[i].arch_type = etb_reg_arch_type;
159 reg_list[i].size = 32;
160 arch_info[i].addr = i;
161 arch_info[i].etb = etb;
162 }
163
164 return reg_cache;
165 }
166
167 int etb_get_reg(reg_t *reg)
168 {
169 if (etb_read_reg(reg) != ERROR_OK)
170 {
171 ERROR("BUG: error scheduling etm register read");
172 exit(-1);
173 }
174
175 if (jtag_execute_queue() != ERROR_OK)
176 {
177 ERROR("register read failed");
178 }
179
180 return ERROR_OK;
181 }
182
183 int etb_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask)
184 {
185 etb_reg_t *etb_reg = reg->arch_info;
186 u8 reg_addr = etb_reg->addr & 0x7f;
187 scan_field_t fields[3];
188
189 DEBUG("%i", etb_reg->addr);
190
191 jtag_add_end_state(TAP_RTI);
192 etb_scann(etb_reg->etb, 0x0);
193 etb_set_instr(etb_reg->etb, 0xc);
194
195 fields[0].device = etb_reg->etb->chain_pos;
196 fields[0].num_bits = 32;
197 fields[0].out_value = reg->value;
198 fields[0].out_mask = NULL;
199 fields[0].in_value = NULL;
200 fields[0].in_check_value = NULL;
201 fields[0].in_check_mask = NULL;
202 fields[0].in_handler = NULL;
203 fields[0].in_handler_priv = NULL;
204
205 fields[1].device = etb_reg->etb->chain_pos;
206 fields[1].num_bits = 7;
207 fields[1].out_value = malloc(1);
208 buf_set_u32(fields[1].out_value, 0, 7, reg_addr);
209 fields[1].out_mask = NULL;
210 fields[1].in_value = NULL;
211 fields[1].in_check_value = NULL;
212 fields[1].in_check_mask = NULL;
213 fields[1].in_handler = NULL;
214 fields[1].in_handler_priv = NULL;
215
216 fields[2].device = etb_reg->etb->chain_pos;
217 fields[2].num_bits = 1;
218 fields[2].out_value = malloc(1);
219 buf_set_u32(fields[2].out_value, 0, 1, 0);
220 fields[2].out_mask = NULL;
221 fields[2].in_value = NULL;
222 fields[2].in_check_value = NULL;
223 fields[2].in_check_mask = NULL;
224 fields[2].in_handler = NULL;
225 fields[2].in_handler_priv = NULL;
226
227 jtag_add_dr_scan(3, fields, -1, NULL);
228
229 /* read the identification register in the second run, to make sure we
230 * don't read the ETB data register twice, skipping every second entry
231 */
232 buf_set_u32(fields[1].out_value, 0, 7, 0x0);
233 fields[0].in_value = reg->value;
234 fields[0].in_check_value = check_value;
235 fields[0].in_check_mask = check_mask;
236
237 jtag_add_dr_scan(3, fields, -1, NULL);
238
239 free(fields[1].out_value);
240 free(fields[2].out_value);
241
242 return ERROR_OK;
243 }
244
245 int etb_read_reg(reg_t *reg)
246 {
247 return etb_read_reg_w_check(reg, NULL, NULL);
248 }
249
250 int etb_set_reg(reg_t *reg, u32 value)
251 {
252 if (etb_write_reg(reg, value) != ERROR_OK)
253 {
254 ERROR("BUG: error scheduling etm register write");
255 exit(-1);
256 }
257
258 buf_set_u32(reg->value, 0, reg->size, value);
259 reg->valid = 1;
260 reg->dirty = 0;
261
262 return ERROR_OK;
263 }
264
265 int etb_set_reg_w_exec(reg_t *reg, u8 *buf)
266 {
267 etb_set_reg(reg, buf_get_u32(buf, 0, reg->size));
268
269 if (jtag_execute_queue() != ERROR_OK)
270 {
271 ERROR("register write failed");
272 exit(-1);
273 }
274 return ERROR_OK;
275 }
276
277 int etb_write_reg(reg_t *reg, u32 value)
278 {
279 etb_reg_t *etb_reg = reg->arch_info;
280 u8 reg_addr = etb_reg->addr & 0x7f;
281 scan_field_t fields[3];
282
283 DEBUG("%i: 0x%8.8x", etb_reg->addr, value);
284
285 jtag_add_end_state(TAP_RTI);
286 etb_scann(etb_reg->etb, 0x0);
287 etb_set_instr(etb_reg->etb, 0xc);
288
289 fields[0].device = etb_reg->etb->chain_pos;
290 fields[0].num_bits = 32;
291 fields[0].out_value = malloc(4);
292 buf_set_u32(fields[0].out_value, 0, 32, value);
293 fields[0].out_mask = NULL;
294 fields[0].in_value = NULL;
295 fields[0].in_check_value = NULL;
296 fields[0].in_check_mask = NULL;
297 fields[0].in_handler = NULL;
298 fields[0].in_handler_priv = NULL;
299
300 fields[1].device = etb_reg->etb->chain_pos;
301 fields[1].num_bits = 7;
302 fields[1].out_value = malloc(1);
303 buf_set_u32(fields[1].out_value, 0, 7, reg_addr);
304 fields[1].out_mask = NULL;
305 fields[1].in_value = NULL;
306 fields[1].in_check_value = NULL;
307 fields[1].in_check_mask = NULL;
308 fields[1].in_handler = NULL;
309 fields[1].in_handler_priv = NULL;
310
311 fields[2].device = etb_reg->etb->chain_pos;
312 fields[2].num_bits = 1;
313 fields[2].out_value = malloc(1);
314 buf_set_u32(fields[2].out_value, 0, 1, 1);
315 fields[2].out_mask = NULL;
316 fields[2].in_value = NULL;
317 fields[2].in_check_value = NULL;
318 fields[2].in_check_mask = NULL;
319 fields[2].in_handler = NULL;
320 fields[2].in_handler_priv = NULL;
321
322 jtag_add_dr_scan(3, fields, -1, NULL);
323
324 free(fields[0].out_value);
325 free(fields[1].out_value);
326 free(fields[2].out_value);
327
328 return ERROR_OK;
329 }
330
331 int etb_store_reg(reg_t *reg)
332 {
333 return etb_write_reg(reg, buf_get_u32(reg->value, 0, reg->size));
334 }
335
336 int etb_register_commands(struct command_context_s *cmd_ctx, command_t *arm7_9_cmd)
337 {
338 register_command(cmd_ctx, arm7_9_cmd, "etb", handle_arm7_9_etb_command, COMMAND_CONFIG, NULL);
339
340 register_command(cmd_ctx, arm7_9_cmd, "etb_dump", handle_arm7_9_etb_dump_command, COMMAND_EXEC, "dump current ETB content");
341
342 return ERROR_OK;
343 }
344
345 #define PIPESTAT(x) ((x) & 0x7)
346 #define TRACEPKT(x) (((x) & 0x7fff8) >> 3)
347 #define TRACESYNC(x) (((x) & 0x80000) >> 19)
348
349 int etmv1_next_packet(int trace_depth, u32 *trace_data, int frame, int *port_half, int apo, u8 *packet)
350 {
351 while (frame < trace_depth)
352 {
353 if (apo > 0)
354 {
355 if (TRACESYNC(trace_data[frame]))
356 apo--;
357 }
358 else
359 {
360 /* we're looking for a branch address, skip if TRACESYNC isn't set */
361 if ((apo == 0) && (!TRACESYNC(trace_data[frame])))
362 {
363 frame++;
364 continue;
365 }
366
367 /* TRACEPKT is valid if this isn't a TD nor a TRIGGER cycle */
368 if (((PIPESTAT(trace_data[frame]) != 0x7) && (PIPESTAT(trace_data[frame]) != 0x6))
369 && !((apo == 0) && (!TRACESYNC(trace_data[frame]))))
370 {
371 if (*port_half == 0)
372 {
373 *packet = TRACEPKT(trace_data[frame]) & 0xff;
374 *port_half = 1;
375 }
376 else
377 {
378 *packet = (TRACEPKT(trace_data[frame]) & 0xff00) >> 8;
379 *port_half = 0;
380 frame++;
381 }
382 return frame;
383 }
384 }
385 frame++;
386 }
387
388 /* we reached the end of the trace without finding the packet we're looking for
389 * tracing is finished
390 */
391 return -1;
392 }
393
394 int handle_arm7_9_etb_dump_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
395 {
396 int retval;
397 target_t *target = get_current_target(cmd_ctx);
398 armv4_5_common_t *armv4_5;
399 arm7_9_common_t *arm7_9;
400 int i, j, k;
401 int first_frame = 0;
402 int last_frame;
403 int addressbits_valid = 0;
404 u32 address = 0x0;
405 u32 *trace_data;
406 int port_half = 0;
407 int last_instruction = -1;
408 u8 branch_reason;
409 u8 packet;
410 char trace_output[256];
411 int trace_output_len;
412 u8 apo;
413
414 if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
415 {
416 command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
417 return ERROR_OK;
418 }
419
420 if (!arm7_9->etb)
421 {
422 command_print(cmd_ctx, "no ETB configured for current target");
423 return ERROR_OK;
424 }
425
426 if (!(arm7_9->etb->RAM_depth && arm7_9->etb->RAM_width))
427 {
428 /* identify ETB RAM depth and width */
429 etb_read_reg(&arm7_9->etb->reg_cache->reg_list[ETB_RAM_DEPTH]);
430 etb_read_reg(&arm7_9->etb->reg_cache->reg_list[ETB_RAM_WIDTH]);
431 jtag_execute_queue();
432
433 arm7_9->etb->RAM_depth = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_DEPTH].value, 0, 32);
434 arm7_9->etb->RAM_width = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_WIDTH].value, 0, 32);
435 }
436
437 trace_data = malloc(sizeof(u32) * arm7_9->etb->RAM_depth);
438
439 etb_read_reg(&arm7_9->etb->reg_cache->reg_list[ETB_STATUS]);
440 etb_read_reg(&arm7_9->etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER]);
441 jtag_execute_queue();
442
443 /* check if we overflowed, and adjust first and last frame of the trace accordingly */
444 if (buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_STATUS].value, 1, 1))
445 {
446 first_frame = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER].value, 0, 32);
447 }
448
449 last_frame = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_WRITE_POINTER].value, 0, 32) - 1;
450
451 etb_write_reg(&arm7_9->etb->reg_cache->reg_list[ETB_RAM_READ_POINTER], first_frame);
452
453 /* read trace data from ETB */
454 i = first_frame;
455 j = 0;
456 do {
457 etb_read_reg(&arm7_9->etb->reg_cache->reg_list[ETB_RAM_DATA]);
458 jtag_execute_queue();
459 trace_data[j++] = buf_get_u32(arm7_9->etb->reg_cache->reg_list[ETB_RAM_DATA].value, 0, 32);
460 i++;
461 } while ((i % arm7_9->etb->RAM_depth) != (first_frame % arm7_9->etb->RAM_depth));
462
463 for (i = 0, j = 0; i < arm7_9->etb->RAM_depth; i++)
464 {
465 int trigger = 0;
466
467 trace_output_len = 0;
468
469 /* catch trigger, actual PIPESTAT is encoded in TRACEPKT[2:0] */
470 if (PIPESTAT(trace_data[i]) == 0x6)
471 {
472 trigger = 1;
473 trace_data[i] &= ~0x7;
474 trace_data[i] |= TRACEPKT(trace_data[i]) & 0x7;
475 }
476
477 if (addressbits_valid == 32)
478 {
479 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
480 "%i: 0x%8.8x %s", i, address, (trigger) ? "(TRIGGER) " : "");
481 }
482 else if (addressbits_valid != 0)
483 {
484 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
485 "%i: 0x...%x %s", i, address, (trigger) ? "(TRIGGER) " : "");
486 }
487 else
488 {
489 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
490 "%i: 0xUNK %s", i, (trigger) ? "(TRIGGER) " : "");
491 }
492
493 switch (PIPESTAT(trace_data[i]))
494 {
495 case 0x0:
496 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
497 "IE");
498 break;
499 case 0x1:
500 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
501 "ID");
502 break;
503 case 0x2:
504 /* Instruction exectued - TRACEPKT might be valid, but belongs to another cycle */
505 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
506 "IN");
507 break;
508 case 0x3:
509 /* WAIT cycle - TRACEPKT is valid, but belongs to another cycle */
510 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
511 "WT");
512 break;
513 case 0x4:
514 /* following a branch two APO cycles are output on PIPESTAT[1:0]
515 * but another BE/BD could overwrite the current branch,
516 * or a trigger could cause the APO to be output on TRACEPKT[1:0]
517 */
518 if ((PIPESTAT(trace_data[i + 1]) == 0x4)
519 || (PIPESTAT(trace_data[i + 1]) == 0x5))
520 {
521 /* another branch occured, we ignore this one */
522 j = (j < i + 1) ? i + 1 : j;
523 break;
524 }
525 else if (PIPESTAT(trace_data[i + 1]) == 0x6)
526 {
527 apo = TRACEPKT(trace_data[i + 1]) & 0x3;
528 }
529 else
530 {
531 apo = PIPESTAT(trace_data[i + 1]) & 0x3;
532 }
533
534 if ((PIPESTAT(trace_data[i + 2]) == 0x4)
535 || (PIPESTAT(trace_data[i + 2]) == 0x5))
536 {
537 j = (j < i + 2) ? i + 1 : j;
538 i = i + 1;
539 break;
540 }
541 else if (PIPESTAT(trace_data[i + 2]) == 0x6)
542 {
543 apo |= (TRACEPKT(trace_data[i + 2]) & 0x3) << 2;
544 }
545 else
546 {
547 apo = (PIPESTAT(trace_data[i + 1]) & 0x3) << 2;
548 }
549
550 branch_reason = -1;
551 k = 0;
552 do
553 {
554 if ((j = etmv1_next_packet(arm7_9->etb->RAM_depth, trace_data, j, &port_half, apo, &packet)) != -1)
555 {
556 address &= ~(0x7f << (k * 7));
557 address |= (packet & 0x7f) << (k * 7);
558 }
559 else
560 {
561 break;
562 }
563 k++;
564 } while ((k < 5) && (packet & 0x80));
565
566 if (addressbits_valid < ((k * 7 > 32) ? 32 : k * 7))
567 addressbits_valid = (k * 7 > 32) ? 32 : k * 7;
568
569 if (k == 5)
570 {
571 branch_reason = (packet & 0x7) >> 4;
572 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
573 "BE 0x%x (/%i) (%s)", address, addressbits_valid, etmv1_branch_reason_string[branch_reason]);
574 }
575 else
576 {
577 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
578 "BE 0x%x (/%i)", address, addressbits_valid);
579 }
580
581 break;
582 case 0x5:
583 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
584 "BD");
585 break;
586 case 0x6:
587 /* We catch the trigger event before we get here */
588 ERROR("TR pipestat should have been caught earlier");
589 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
590 "--");
591 break;
592 case 0x7:
593 /* TRACE disabled - TRACEPKT = invalid */
594 trace_output_len += snprintf(trace_output + trace_output_len, 256 - trace_output_len,
595 "TD");
596 break;
597 }
598
599 /* PIPESTAT other than WT (b011) and TD (b111) mean we executed an instruction */
600 if ((PIPESTAT(trace_data[i]) & 0x3) != 0x3)
601 {
602 last_instruction = i;
603 address += 4;
604 }
605
606 /* The group of packets for a particular instruction cannot start on or before any
607 * previous functional PIPESTAT (IE, IN, ID, BE, or BD)
608 */
609 if (j < last_instruction)
610 {
611 j = last_instruction + 1;
612 }
613
614 /* restore trigger PIPESTAT to ensure TRACEPKT is ignored */
615 if (trigger == 1)
616 {
617 trace_data[i] &= ~0x7;
618 trace_data[i] |= 0x6;
619 }
620
621 command_print(cmd_ctx, "%s (raw: 0x%8.8x)", trace_output, trace_data[i]);
622 }
623
624 return ERROR_OK;
625 }

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)