1. 定义 jtag_command_type
在 OpenOCD 中,JTag 命令在枚举 jtag_command_type 中定义,定义如下:
1 /** 2 * The type of the @c jtag_command_container contained by a 3 * @c jtag_command_s structure. 4 */ 5 enum jtag_command_type { 6 JTAG_SCAN = 1, // 数据扫描,IR_SCAN 或 DR_SCAN 7 /* JTAG_TLR_RESET's non-minidriver implementation is a 8 * vestige from a statemove cmd. The statemove command 9 * is obsolete and replaced by pathmove. 10 * 11 * pathmove does not support reset as one of it's states, 12 * hence the need for an explicit statemove command. 13 */ 14 JTAG_TLR_RESET = 2, // TAP 状态机转到 RESET 状态 15 JTAG_RUNTEST = 3, // 状态机在 IDEL 态下进行自循环 16 JTAG_RESET = 4, // 进行 srst 或 trst 17 JTAG_PATHMOVE = 6, // 进行状态切换,手动控制状态机轮转 18 JTAG_SLEEP = 7, // sleep 一段时间 19 JTAG_STABLECLOCKS = 8, // 发送 N 次 TMS,TMS 的值取决于 TAP 当前状态 20 JTAG_TMS = 9, // 发送指定的 TMS 21 };
2. 使用 jtag_command_type 创建 cmd 序列
jtag_command_type 在 IR/DR 等命令创建时创建,在 execute_queue 中使用。例如,在创建 IR_SCAN 时,会构建 jtag_command 结构体,并赋 予cmd->type 为 JTAG_SCAN。
1 /** 2 * see jtag_add_ir_scan() 3 * 4 */ 5 int interface_jtag_add_ir_scan(struct jtag_tap *active, 6 const struct scan_field *in_fields, tap_state_t state) 7 { 8 size_t num_taps = jtag_tap_count_enabled(); 9 10 struct jtag_command *cmd = cmd_queue_alloc(sizeof(struct jtag_command)); 11 struct scan_command *scan = cmd_queue_alloc(sizeof(struct scan_command)); 12 struct scan_field *out_fields = cmd_queue_alloc(num_taps * sizeof(struct scan_field)); 13 14 jtag_queue_command(cmd); 15 16 cmd->type = JTAG_SCAN; 17 cmd->cmd.scan = scan; 18 19 scan->ir_scan = true; 20 scan->num_fields = num_taps; /* one field per device */ 21 scan->fields = out_fields; 22 scan->end_state = state; 23 24 struct scan_field *field = out_fields; /* keep track where we insert data */ 25 26 /* loop over all enabled TAPs */ 27 28 for (struct jtag_tap *tap = jtag_tap_next_enabled(NULL); tap != NULL; tap = jtag_tap_next_enabled(tap)) { 29 /* search the input field list for fields for the current TAP */ 30 31 if (tap == active) { 32 /* if TAP is listed in input fields, copy the value */ 33 tap->bypass = 0; 34 35 jtag_scan_field_clone(field, in_fields); 36 } else { 37 /* if a TAP isn't listed in input fields, set it to BYPASS */ 38 39 tap->bypass = 1; 40 41 field->num_bits = tap->ir_length; 42 field->out_value = buf_set_ones(cmd_queue_alloc(DIV_ROUND_UP(tap->ir_length, 8)), tap->ir_length); 43 field->in_value = NULL; /* do not collect input for tap's in bypass */ 44 } 45 46 /* update device information */ 47 buf_cpy(field->out_value, tap->cur_instr, tap->ir_length); 48 49 field++; 50 } 51 /* paranoia: jtag_tap_count_enabled() and jtag_tap_next_enabled() not in sync */ 52 assert(field == out_fields + num_taps); 53 54 return ERROR_OK; 55 }
在上面的代码中,定义了本次操作的 cmd 是 JTAG_SCAN,操作数据在 scan 变量中进行定义。
jtag_queue_command 用于将本次新建的命令序列加入命令列表中,在执行jtag_execute_queue的时候执行。
上述代码中的 for 循环,遍历全部的 TAP,并将 bypass 的TAP的 IR 设置为全1,并把数据加入到操作的数据列表中。
与 interface_jtag_add_ir_scan 函数类似,能够构建cmd的函数如下:
1 // cmd->type = JTAG_SCAN; 2 int interface_jtag_add_ir_scan(struct jtag_tap *active, 3 const struct scan_field *in_fields, tap_state_t state) 4 5 // cmd->type = JTAG_SCAN; 6 int interface_jtag_add_dr_scan(struct jtag_tap *active, int in_num_fields, 7 const struct scan_field *in_fields, tap_state_t state) 8 9 // cmd->type = JTAG_SCAN; 10 static int jtag_add_plain_scan(int num_bits, const uint8_t *out_bits, 11 uint8_t *in_bits, tap_state_t state, bool ir_scan) 12 13 // cmd->type = JTAG_TLR_RESET; 14 int interface_jtag_add_tlr(void) 15 16 // cmd->type = JTAG_TMS; 17 int interface_add_tms_seq(unsigned num_bits, const uint8_t *seq, enum tap_state state) 18 19 // cmd->type = JTAG_PATHMOVE; 20 int interface_jtag_add_pathmove(int num_states, const tap_state_t *path) 21 22 // cmd->type = JTAG_RUNTEST; 23 int interface_jtag_add_runtest(int num_cycles, tap_state_t state) 24 25 // cmd->type = JTAG_STABLECLOCKS; 26 int interface_jtag_add_clocks(int num_cycles) 27 28 // cmd->type = JTAG_RESET; 29 int interface_jtag_add_reset(int req_trst, int req_srst) 30 31 // cmd->type = JTAG_SLEEP; 32 int interface_jtag_add_sleep(uint32_t us)
3. 根据 cmd->type 进行操作
在cmd类型定义完成后,在执行 jtag_execute_queue 时使用 cmd->type 来处理具体的工作。 以 parport 驱动为例,jtag_execute_queue 最终会调用到 bitbang_execute_queue,函数代码如下:
1 int bitbang_execute_queue(void) 2 { 3 struct jtag_command *cmd = jtag_command_queue; /* currently processed command */ 4 int scan_size; 5 enum scan_type type; 6 uint8_t *buffer; 7 int retval; 8 9 if (!bitbang_interface) { 10 LOG_ERROR("BUG: Bitbang interface called, but not yet initialized"); 11 exit(-1); 12 } 13 14 /* return ERROR_OK, unless a jtag_read_buffer returns a failed check 15 * that wasn't handled by a caller-provided error handler 16 */ 17 retval = ERROR_OK; 18 19 if (bitbang_interface->blink) { 20 if (bitbang_interface->blink(1) != ERROR_OK) 21 return ERROR_FAIL; 22 } 23 24 while (cmd) { 25 switch (cmd->type) { 26 case JTAG_RESET: 27 LOG_DEBUG_IO("reset trst: %i srst %i", 28 cmd->cmd.reset->trst, 29 cmd->cmd.reset->srst); 30 if ((cmd->cmd.reset->trst == 1) || 31 (cmd->cmd.reset->srst && (jtag_get_reset_config() & RESET_SRST_PULLS_TRST))) 32 tap_set_state(TAP_RESET); 33 if (bitbang_interface->reset(cmd->cmd.reset->trst, 34 cmd->cmd.reset->srst) != ERROR_OK) 35 return ERROR_FAIL; 36 break; 37 case JTAG_RUNTEST: 38 LOG_DEBUG_IO("runtest %i cycles, end in %s", 39 cmd->cmd.runtest->num_cycles, 40 tap_state_name(cmd->cmd.runtest->end_state)); 41 bitbang_end_state(cmd->cmd.runtest->end_state); 42 if (bitbang_runtest(cmd->cmd.runtest->num_cycles) != ERROR_OK) 43 return ERROR_FAIL; 44 break; 45 46 case JTAG_STABLECLOCKS: 47 /* this is only allowed while in a stable state. A check for a stable 48 * state was done in jtag_add_clocks() 49 */ 50 if (bitbang_stableclocks(cmd->cmd.stableclocks->num_cycles) != ERROR_OK) 51 return ERROR_FAIL; 52 break; 53 54 case JTAG_TLR_RESET: 55 LOG_DEBUG_IO("statemove end in %s", 56 tap_state_name(cmd->cmd.statemove->end_state)); 57 bitbang_end_state(cmd->cmd.statemove->end_state); 58 if (bitbang_state_move(0) != ERROR_OK) 59 return ERROR_FAIL; 60 break; 61 case JTAG_PATHMOVE: 62 LOG_DEBUG_IO("pathmove: %i states, end in %s", 63 cmd->cmd.pathmove->num_states, 64 tap_state_name(cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1])); 65 if (bitbang_path_move(cmd->cmd.pathmove) != ERROR_OK) 66 return ERROR_FAIL; 67 break; 68 case JTAG_SCAN: 69 bitbang_end_state(cmd->cmd.scan->end_state); 70 scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); 71 LOG_DEBUG_IO("%s scan %d bits; end in %s", 72 (cmd->cmd.scan->ir_scan) ? "IR" : "DR", 73 scan_size, 74 tap_state_name(cmd->cmd.scan->end_state)); 75 type = jtag_scan_type(cmd->cmd.scan); 76 if (bitbang_scan(cmd->cmd.scan->ir_scan, type, buffer, 77 scan_size) != ERROR_OK) 78 return ERROR_FAIL; 79 if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK) 80 retval = ERROR_JTAG_QUEUE_FAILED; 81 if (buffer) 82 free(buffer); 83 break; 84 case JTAG_SLEEP: 85 LOG_DEBUG_IO("sleep %" PRIi32, cmd->cmd.sleep->us); 86 jtag_sleep(cmd->cmd.sleep->us); 87 break; 88 case JTAG_TMS: 89 retval = bitbang_execute_tms(cmd); 90 break; 91 default: 92 LOG_ERROR("BUG: unknown JTAG command type encountered"); 93 exit(-1); 94 } 95 cmd = cmd->next; 96 } 97 if (bitbang_interface->blink) { 98 if (bitbang_interface->blink(0) != ERROR_OK) 99 return ERROR_FAIL; 100 } 101 102 return retval; 103 }
在 bitbang_execute_queue 函数中,通过 switch 语句对各种不同类型的 cmd->type 进行解析处理。
在 bitbang_execute_queue 中,变量 bitbang_interface 在 parport 驱动初始化时定义,定义如下:
1 static struct bitbang_interface parport_bitbang = { 2 .read = &parport_read, 3 .write = &parport_write, 4 .reset = &parport_reset, 5 .blink = &parport_led, 6 };
4. JTAG_SCAN
接下来的内容将着重讲解 JTAG_SCAN 功能。JTAG_SCAN 是进行 IR_SCAN 和 DR_SCCAN 的 command,是上位机与被调试处理器进行数据交互的接口。在 bitbang_execute_queue 函数中可以看到,JTAG_SCAN 命令相关代码如下:
1 case JTAG_SCAN: 2 bitbang_end_state(cmd->cmd.scan->end_state); 3 scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); 4 LOG_DEBUG_IO("%s scan %d bits; end in %s", 5 (cmd->cmd.scan->ir_scan) ? "IR" : "DR", 6 scan_size, 7 tap_state_name(cmd->cmd.scan->end_state)); 8 type = jtag_scan_type(cmd->cmd.scan); 9 if (bitbang_scan(cmd->cmd.scan->ir_scan, type, buffer, 10 scan_size) != ERROR_OK) 11 return ERROR_FAIL; 12 if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK) 13 retval = ERROR_JTAG_QUEUE_FAILED; 14 if (buffer) 15 free(buffer); 16 break;
在函数开始时,首先将状态机结束状态设置成 cmd 命令预设的状态,然后通过 jtag_build_buffer 函数拼接 scan 数据。
4.1 jtag_build_buffer
jtag_build_buffer 函数如下:
1 int jtag_build_buffer(const struct scan_command *cmd, uint8_t **buffer) 2 { 3 int bit_count = 0; 4 int i; 5 6 bit_count = jtag_scan_size(cmd); 7 *buffer = calloc(1, DIV_ROUND_UP(bit_count, 8)); 8 9 bit_count = 0; 10 11 LOG_DEBUG_IO("%s num_fields: %i", 12 cmd->ir_scan ? "IRSCAN" : "DRSCAN", 13 cmd->num_fields); 14 15 for (i = 0; i < cmd->num_fields; i++) { 16 if (cmd->fields[i].out_value) { 17 if (LOG_LEVEL_IS(LOG_LVL_DEBUG_IO)) { 18 char *char_buf = buf_to_str(cmd->fields[i].out_value, 19 (cmd->fields[i].num_bits > DEBUG_JTAG_IOZ) 20 ? DEBUG_JTAG_IOZ 21 : cmd->fields[i].num_bits, 16); 22 23 LOG_DEBUG("fields[%i].out_value[%i]: 0x%s", i, 24 cmd->fields[i].num_bits, char_buf); 25 free(char_buf); 26 } 27 buf_set_buf(cmd->fields[i].out_value, 0, *buffer, 28 bit_count, cmd->fields[i].num_bits); 29 } else { 30 LOG_DEBUG_IO("fields[%i].out_value[%i]: NULL", 31 i, cmd->fields[i].num_bits); 32 } 33 34 bit_count += cmd->fields[i].num_bits; 35 } 36 37 /*LOG_DEBUG_IO("bit_count totalling: %i", bit_count); */ 38 39 return bit_count; 40 }
在函数开始时,首先统计 scan 数据长度,构建数据 buffer。然后将 cmd 中相应的数据复制到 buffer 中,最后函数将拼接数据的总长度作为返回值返回。
在获取操作的数据后,接下来通过 bitbang_scan 函数实现数据交互,将发送的数据通过 TDI 发送出去,同时接收 TDO 的返回数据。
4.2 bitbang_scan
bitbang_scan 函数的定义如下:
1 static int bitbang_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, 2 unsigned scan_size) 3 { 4 tap_state_t saved_end_state = tap_get_end_state(); 5 unsigned bit_cnt; 6 7 if (!((!ir_scan && 8 (tap_get_state() == TAP_DRSHIFT)) || 9 (ir_scan && (tap_get_state() == TAP_IRSHIFT)))) { 10 if (ir_scan) 11 bitbang_end_state(TAP_IRSHIFT); // 状态机状态设置为 IR_SHIFT 12 else 13 bitbang_end_state(TAP_DRSHIFT); // 状态机状态设置为 DR_SHIFT 14 15 if (bitbang_state_move(0) != ERROR_OK) // 执行状态转换 16 return ERROR_FAIL; 17 bitbang_end_state(saved_end_state); // 恢复结束状态 18 } 19 20 size_t buffered = 0; 21 for (bit_cnt = 0; bit_cnt < scan_size; bit_cnt++) { 22 int tms = (bit_cnt == scan_size-1) ? 1 : 0; 23 int tdi; 24 int bytec = bit_cnt/8; 25 int bcval = 1 << (bit_cnt % 8); 26 27 /* if we're just reading the scan, but don't care about the output 28 * default to outputting 'low', this also makes valgrind traces more readable, 29 * as it removes the dependency on an uninitialised value 30 */ 31 tdi = 0; 32 if ((type != SCAN_IN) && (buffer[bytec] & bcval)) // 如果需要输出数据,且数据为 1,则 tdi 置 1 33 tdi = 1; 34 35 if (bitbang_interface->write(0, tms, tdi) != ERROR_OK) // 发送 tck,tms,tdi,下降沿 36 return ERROR_FAIL; 37 38 if (type != SCAN_OUT) { 39 if (bitbang_interface->buf_size) { 40 if (bitbang_interface->sample() != ERROR_OK) 41 return ERROR_FAIL; 42 buffered++; 43 } else { 44 switch (bitbang_interface->read()) { // 接收 TDO 数据 45 case BB_LOW: 46 buffer[bytec] &= ~bcval; 47 break; 48 case BB_HIGH: 49 buffer[bytec] |= bcval; 50 break; 51 default: 52 return ERROR_FAIL; 53 } 54 } 55 } 56 57 if (bitbang_interface->write(1, tms, tdi) != ERROR_OK) // 发送 tck,tms,tdi,上升沿 58 return ERROR_FAIL; 59 60 if (type != SCAN_OUT && bitbang_interface->buf_size && 61 (buffered == bitbang_interface->buf_size || 62 bit_cnt == scan_size - 1)) { // 采用 sample 形式的数据读取 63 for (unsigned i = bit_cnt + 1 - buffered; i <= bit_cnt; i++) { 64 switch (bitbang_interface->read_sample()) { 65 case BB_LOW: 66 buffer[i/8] &= ~(1 << (i % 8)); 67 break; 68 case BB_HIGH: 69 buffer[i/8] |= 1 << (i % 8); 70 break; 71 default: 72 return ERROR_FAIL; 73 } 74 } 75 buffered = 0; 76 } 77 } 78 79 if (tap_get_state() != tap_get_end_state()) { // 进行状态切换,切换到 cmd 要求的结束状态 80 /* we *KNOW* the above loop transitioned out of 81 * the shift state, so we skip the first state 82 * and move directly to the end state. 83 */ 84 if (bitbang_state_move(1) != ERROR_OK) // 当前状态为 IR/DR SHIFT,所以要跳过当前状态,传参为 1 85 return ERROR_FAIL; 86 } 87 return ERROR_OK; 88 }
5. bitbang_interface 解析
bitbang_interface 在 parport.c 中的定义为 parport_bitbang。结构体定义入下:
static struct bitbang_interface parport_bitbang = { .read = &parport_read, .write = &parport_write, .reset = &parport_reset, .blink = &parport_led, };
在结构体中,包含 read、write、reset 和 blink 函数。其中,read 操作用于读取 tdo;write 操作用于输出 tck、tms 和 tdi;reset操作用于进行 trst 或 srst 对 JTag 进行复位;blink 用于点亮或熄灭指示灯。
5.1 parport_read
parport_read 函数用于读取并口的输入数据,其中包含 TDO 数据。代码如下:
1 static bb_value_t parport_read(void) 2 { 3 int data = 0; 4 5 #if PARPORT_USE_PPDEV == 1 6 ioctl(device_handle, PPRSTATUS, &data); // 读取数据 7 #else 8 data = inb(statusport); // 读取数据 9 #endif 10 11 if ((data ^ cable->INPUT_INVERT) & cable->TDO_MASK) // 分析读取到的 TDO 数据读取数据 12 return BB_HIGH; 13 else 14 return BB_LOW; 15 }
5.2 parport_write
parport_write 函数用于构建并口的输出数据,并执行数据输出。代码如下:
1 static int parport_write(int tck, int tms, int tdi) 2 { 3 int i = wait_states + 1; 4 5 if (tck) // 设置 TCK 数据 6 dataport_value |= cable->TCK_MASK; 7 else 8 dataport_value &= ~cable->TCK_MASK; 9 10 if (tms) // 设置 TMS 数据 11 dataport_value |= cable->TMS_MASK; 12 else 13 dataport_value &= ~cable->TMS_MASK; 14 15 if (tdi) // 设置 TDI 数据 16 dataport_value |= cable->TDI_MASK; 17 else 18 dataport_value &= ~cable->TDI_MASK; 19 20 while (i-- > 0) 21 parport_write_data(); // 输出数据 22 23 return ERROR_OK; 24 }
parport_write_data 函数为实际的数据输出函数,代码如下:
1 static inline void parport_write_data(void) 2 { 3 uint8_t output; 4 output = dataport_value ^ cable->OUTPUT_INVERT; // 构建输出数据 5 6 #if PARPORT_USE_PPDEV == 1 7 ioctl(device_handle, PPWDATA, &output); 8 #else 9 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 10 outb(dataport, output); // 数据输出 11 #else 12 outb(output, dataport); // 数据输出 13 #endif 14 #endif 15 }
5.3 parport_reset
parport_reset 函数用于 trst 或 srst,代码如下:
1 /* (1) assert or (0) deassert reset lines */ 2 static int parport_reset(int trst, int srst) 3 { 4 LOG_DEBUG("trst: %i, srst: %i", trst, srst); 5 6 if (trst == 0) // 设置 trst 7 dataport_value |= cable->TRST_MASK; 8 else if (trst == 1) 9 dataport_value &= ~cable->TRST_MASK; 10 11 if (srst == 0) // 设置 srst 12 dataport_value |= cable->SRST_MASK; 13 else if (srst == 1) 14 dataport_value &= ~cable->SRST_MASK; 15 16 parport_write_data(); // 发送数据 17 18 return ERROR_OK; 19 }
5.4 parport_led
parport_led 函数用于点亮和关闭调试器上的指示灯。代码如下:
1 /* turn LED on parport adapter on (1) or off (0) */ 2 static int parport_led(int on) 3 { 4 if (on) // 是否点亮指示灯 5 dataport_value |= cable->LED_MASK; 6 else 7 dataport_value &= ~cable->LED_MASK; 8 9 parport_write_data(); // 发送数据 10 11 return ERROR_OK; 12 }