【简介】OpenOCD 由jtag操作到parport driver流程

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 }


原文地址:https://www.cnblogs.com/elta/p/12559827.html