1. 定义 jtag_command_type

在 OpenOCD 中,JTag 命令在枚举 jtag_command_type 中定义,定义如下:

 /**
* The type of the @c jtag_command_container contained by a
* @c jtag_command_s structure.
*/
enum jtag_command_type {
JTAG_SCAN = , // 数据扫描,IR_SCAN 或 DR_SCAN
/* JTAG_TLR_RESET's non-minidriver implementation is a
* vestige from a statemove cmd. The statemove command
* is obsolete and replaced by pathmove.
*
* pathmove does not support reset as one of it's states,
* hence the need for an explicit statemove command.
*/
JTAG_TLR_RESET = , // TAP 状态机转到 RESET 状态
JTAG_RUNTEST = , // 状态机在 IDEL 态下进行自循环
JTAG_RESET = , // 进行 srst 或 trst
JTAG_PATHMOVE = , // 进行状态切换,手动控制状态机轮转
JTAG_SLEEP = , // sleep 一段时间
JTAG_STABLECLOCKS = , // 发送 N 次 TMS,TMS 的值取决于 TAP 当前状态
JTAG_TMS = , // 发送指定的 TMS
};

2. 使用 jtag_command_type 创建 cmd 序列

jtag_command_type 在 IR/DR 等命令创建时创建,在 execute_queue 中使用。例如,在创建 IR_SCAN 时,会构建 jtag_command 结构体,并赋 予cmd->type 为 JTAG_SCAN。

 /**
* see jtag_add_ir_scan()
*
*/
int interface_jtag_add_ir_scan(struct jtag_tap *active,
const struct scan_field *in_fields, tap_state_t state)
{
size_t num_taps = jtag_tap_count_enabled(); struct jtag_command *cmd = cmd_queue_alloc(sizeof(struct jtag_command));
struct scan_command *scan = cmd_queue_alloc(sizeof(struct scan_command));
struct scan_field *out_fields = cmd_queue_alloc(num_taps * sizeof(struct scan_field)); jtag_queue_command(cmd); cmd->type = JTAG_SCAN;
cmd->cmd.scan = scan;

scan->ir_scan = true;
scan->num_fields = num_taps; /* one field per device */
scan->fields = out_fields;
scan->end_state = state; struct scan_field *field = out_fields; /* keep track where we insert data */ /* loop over all enabled TAPs */ for (struct jtag_tap *tap = jtag_tap_next_enabled(NULL); tap != NULL; tap = jtag_tap_next_enabled(tap)) {
/* search the input field list for fields for the current TAP */ if (tap == active) {
/* if TAP is listed in input fields, copy the value */
tap->bypass = ; jtag_scan_field_clone(field, in_fields);
} else {
/* if a TAP isn't listed in input fields, set it to BYPASS */ tap->bypass = ; field->num_bits = tap->ir_length;
field->out_value = buf_set_ones(cmd_queue_alloc(DIV_ROUND_UP(tap->ir_length, )), tap->ir_length);
field->in_value = NULL; /* do not collect input for tap's in bypass */
} /* update device information */
buf_cpy(field->out_value, tap->cur_instr, tap->ir_length); field++;
}
/* paranoia: jtag_tap_count_enabled() and jtag_tap_next_enabled() not in sync */
assert(field == out_fields + num_taps); return ERROR_OK;
}

在上面的代码中,定义了本次操作的 cmd 是 JTAG_SCAN,操作数据在 scan 变量中进行定义。

jtag_queue_command 用于将本次新建的命令序列加入命令列表中,在执行jtag_execute_queue的时候执行。

上述代码中的 for 循环,遍历全部的 TAP,并将 bypass 的TAP的 IR 设置为全1,并把数据加入到操作的数据列表中。

与 interface_jtag_add_ir_scan 函数类似,能够构建cmd的函数如下:

 // cmd->type = JTAG_SCAN;
int interface_jtag_add_ir_scan(struct jtag_tap *active,
const struct scan_field *in_fields, tap_state_t state) // cmd->type = JTAG_SCAN;
int interface_jtag_add_dr_scan(struct jtag_tap *active, int in_num_fields,
const struct scan_field *in_fields, tap_state_t state) // cmd->type = JTAG_SCAN;
static int jtag_add_plain_scan(int num_bits, const uint8_t *out_bits,
uint8_t *in_bits, tap_state_t state, bool ir_scan) // cmd->type = JTAG_TLR_RESET;
int interface_jtag_add_tlr(void) // cmd->type = JTAG_TMS;
int interface_add_tms_seq(unsigned num_bits, const uint8_t *seq, enum tap_state state) // cmd->type = JTAG_PATHMOVE;
int interface_jtag_add_pathmove(int num_states, const tap_state_t *path) // cmd->type = JTAG_RUNTEST;
int interface_jtag_add_runtest(int num_cycles, tap_state_t state) // cmd->type = JTAG_STABLECLOCKS;
int interface_jtag_add_clocks(int num_cycles) // cmd->type = JTAG_RESET;
int interface_jtag_add_reset(int req_trst, int req_srst) // cmd->type = JTAG_SLEEP;
int interface_jtag_add_sleep(uint32_t us)

3. 根据 cmd->type 进行操作

在cmd类型定义完成后,在执行 jtag_execute_queue 时使用 cmd->type 来处理具体的工作。 以 parport 驱动为例,jtag_execute_queue 最终会调用到  bitbang_execute_queue,函数代码如下:

 int bitbang_execute_queue(void)
{
struct jtag_command *cmd = jtag_command_queue; /* currently processed command */
int scan_size;
enum scan_type type;
uint8_t *buffer;
int retval; if (!bitbang_interface) {
LOG_ERROR("BUG: Bitbang interface called, but not yet initialized");
exit(-);
} /* return ERROR_OK, unless a jtag_read_buffer returns a failed check
* that wasn't handled by a caller-provided error handler
*/
retval = ERROR_OK; if (bitbang_interface->blink) {
if (bitbang_interface->blink() != ERROR_OK)
return ERROR_FAIL;
} while (cmd) {
switch (cmd->type) {
case JTAG_RESET:
LOG_DEBUG_IO("reset trst: %i srst %i",
cmd->cmd.reset->trst,
cmd->cmd.reset->srst);
if ((cmd->cmd.reset->trst == ) ||
(cmd->cmd.reset->srst && (jtag_get_reset_config() & RESET_SRST_PULLS_TRST)))
tap_set_state(TAP_RESET);
if (bitbang_interface->reset(cmd->cmd.reset->trst,
cmd->cmd.reset->srst) != ERROR_OK)
return ERROR_FAIL;
break;
case JTAG_RUNTEST:
LOG_DEBUG_IO("runtest %i cycles, end in %s",
cmd->cmd.runtest->num_cycles,
tap_state_name(cmd->cmd.runtest->end_state));
bitbang_end_state(cmd->cmd.runtest->end_state);
if (bitbang_runtest(cmd->cmd.runtest->num_cycles) != ERROR_OK)
return ERROR_FAIL;
break; case JTAG_STABLECLOCKS:
/* this is only allowed while in a stable state. A check for a stable
* state was done in jtag_add_clocks()
*/
if (bitbang_stableclocks(cmd->cmd.stableclocks->num_cycles) != ERROR_OK)
return ERROR_FAIL;
break; case JTAG_TLR_RESET:
LOG_DEBUG_IO("statemove end in %s",
tap_state_name(cmd->cmd.statemove->end_state));
bitbang_end_state(cmd->cmd.statemove->end_state);
if (bitbang_state_move() != ERROR_OK)
return ERROR_FAIL;
break;
case JTAG_PATHMOVE:
LOG_DEBUG_IO("pathmove: %i states, end in %s",
cmd->cmd.pathmove->num_states,
tap_state_name(cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - ]));
if (bitbang_path_move(cmd->cmd.pathmove) != ERROR_OK)
return ERROR_FAIL;
break;
case JTAG_SCAN:
bitbang_end_state(cmd->cmd.scan->end_state);
scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer);
LOG_DEBUG_IO("%s scan %d bits; end in %s",
(cmd->cmd.scan->ir_scan) ? "IR" : "DR",
scan_size,
tap_state_name(cmd->cmd.scan->end_state));
type = jtag_scan_type(cmd->cmd.scan);
if (bitbang_scan(cmd->cmd.scan->ir_scan, type, buffer,
scan_size) != ERROR_OK)
return ERROR_FAIL;
if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK)
retval = ERROR_JTAG_QUEUE_FAILED;
if (buffer)
free(buffer);
break;
case JTAG_SLEEP:
LOG_DEBUG_IO("sleep %" PRIi32, cmd->cmd.sleep->us);
jtag_sleep(cmd->cmd.sleep->us);
break;
case JTAG_TMS:
retval = bitbang_execute_tms(cmd);
break;
default:
LOG_ERROR("BUG: unknown JTAG command type encountered");
exit(-);
}
cmd = cmd->next;
}
if (bitbang_interface->blink) {
if (bitbang_interface->blink() != ERROR_OK)
return ERROR_FAIL;
} return retval;
}

在 bitbang_execute_queue 函数中,通过 switch 语句对各种不同类型的 cmd->type 进行解析处理。

在 bitbang_execute_queue 中,变量 bitbang_interface 在 parport 驱动初始化时定义,定义如下:

 static struct bitbang_interface parport_bitbang = {
.read = &parport_read,
.write = &parport_write,
.reset = &parport_reset,
.blink = &parport_led,
};

4. JTAG_SCAN

接下来的内容将着重讲解 JTAG_SCAN 功能。JTAG_SCAN 是进行 IR_SCAN 和 DR_SCCAN 的 command,是上位机与被调试处理器进行数据交互的接口。在 bitbang_execute_queue 函数中可以看到,JTAG_SCAN 命令相关代码如下:

             case JTAG_SCAN:
bitbang_end_state(cmd->cmd.scan->end_state);
scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer);
LOG_DEBUG_IO("%s scan %d bits; end in %s",
(cmd->cmd.scan->ir_scan) ? "IR" : "DR",
scan_size,
tap_state_name(cmd->cmd.scan->end_state));
type = jtag_scan_type(cmd->cmd.scan);
if (bitbang_scan(cmd->cmd.scan->ir_scan, type, buffer,
scan_size) != ERROR_OK)
return ERROR_FAIL;
if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK)
retval = ERROR_JTAG_QUEUE_FAILED;
if (buffer)
free(buffer);
break;

在函数开始时,首先将状态机结束状态设置成 cmd 命令预设的状态,然后通过 jtag_build_buffer 函数拼接 scan 数据。

4.1 jtag_build_buffer

jtag_build_buffer 函数如下:

 int jtag_build_buffer(const struct scan_command *cmd, uint8_t **buffer)
{
int bit_count = ;
int i; bit_count = jtag_scan_size(cmd);
*buffer = calloc(, DIV_ROUND_UP(bit_count, ));

bit_count = ; LOG_DEBUG_IO("%s num_fields: %i",
cmd->ir_scan ? "IRSCAN" : "DRSCAN",
cmd->num_fields); for (i = ; i < cmd->num_fields; i++) {
if (cmd->fields[i].out_value) {
if (LOG_LEVEL_IS(LOG_LVL_DEBUG_IO)) {
char *char_buf = buf_to_str(cmd->fields[i].out_value,
(cmd->fields[i].num_bits > DEBUG_JTAG_IOZ)
? DEBUG_JTAG_IOZ
: cmd->fields[i].num_bits, ); LOG_DEBUG("fields[%i].out_value[%i]: 0x%s", i,
cmd->fields[i].num_bits, char_buf);
free(char_buf);
}
buf_set_buf(cmd->fields[i].out_value, , *buffer,
bit_count, cmd->fields[i].num_bits);
} else {
LOG_DEBUG_IO("fields[%i].out_value[%i]: NULL",
i, cmd->fields[i].num_bits);
} bit_count += cmd->fields[i].num_bits;
} /*LOG_DEBUG_IO("bit_count totalling: %i", bit_count); */ return bit_count;
}

在函数开始时,首先统计 scan 数据长度,构建数据 buffer。然后将 cmd 中相应的数据复制到 buffer 中,最后函数将拼接数据的总长度作为返回值返回。

在获取操作的数据后,接下来通过 bitbang_scan 函数实现数据交互,将发送的数据通过 TDI 发送出去,同时接收 TDO 的返回数据。

4.2 bitbang_scan

bitbang_scan 函数的定义如下:

 static int bitbang_scan(bool ir_scan, enum scan_type type, uint8_t *buffer,
unsigned scan_size)
{
tap_state_t saved_end_state = tap_get_end_state();
unsigned bit_cnt; if (!((!ir_scan &&
(tap_get_state() == TAP_DRSHIFT)) ||
(ir_scan && (tap_get_state() == TAP_IRSHIFT)))) {
if (ir_scan)
bitbang_end_state(TAP_IRSHIFT); // 状态机状态设置为 IR_SHIFT
else
bitbang_end_state(TAP_DRSHIFT); // 状态机状态设置为 DR_SHIFT

if (bitbang_state_move() != ERROR_OK) // 执行状态转换
return ERROR_FAIL;
bitbang_end_state(saved_end_state); // 恢复结束状态
} size_t buffered = ;
for (bit_cnt = ; bit_cnt < scan_size; bit_cnt++) {
int tms = (bit_cnt == scan_size-) ? : ;
int tdi;
int bytec = bit_cnt/;
int bcval = << (bit_cnt % ); /* if we're just reading the scan, but don't care about the output
* default to outputting 'low', this also makes valgrind traces more readable,
* as it removes the dependency on an uninitialised value
*/
tdi = ;
if ((type != SCAN_IN) && (buffer[bytec] & bcval)) // 如果需要输出数据,且数据为 1,则 tdi 置 1
tdi = ;

if (bitbang_interface->write(, tms, tdi) != ERROR_OK) // 发送 tck,tms,tdi,下降沿
return ERROR_FAIL; if (type != SCAN_OUT) {
if (bitbang_interface->buf_size) {
if (bitbang_interface->sample() != ERROR_OK)
return ERROR_FAIL;
buffered++;
} else {
switch (bitbang_interface->read()) { // 接收 TDO 数据
case BB_LOW:
buffer[bytec] &= ~bcval;
break;
case BB_HIGH:
buffer[bytec] |= bcval;
break;
default:
return ERROR_FAIL;
}
}
} if (bitbang_interface->write(, tms, tdi) != ERROR_OK) // 发送 tck,tms,tdi,上升沿
return ERROR_FAIL; if (type != SCAN_OUT && bitbang_interface->buf_size &&
(buffered == bitbang_interface->buf_size ||
bit_cnt == scan_size - )) { // 采用 sample 形式的数据读取
for (unsigned i = bit_cnt + - buffered; i <= bit_cnt; i++) {
switch (bitbang_interface->read_sample()) {
case BB_LOW:
buffer[i/] &= ~( << (i % ));
break;
case BB_HIGH:
buffer[i/] |= << (i % );
break;
default:
return ERROR_FAIL;
}
}
buffered = ;
}
} if (tap_get_state() != tap_get_end_state()) { // 进行状态切换,切换到 cmd 要求的结束状态
/* we *KNOW* the above loop transitioned out of
* the shift state, so we skip the first state
* and move directly to the end state.
*/
if (bitbang_state_move() != ERROR_OK) // 当前状态为 IR/DR SHIFT,所以要跳过当前状态,传参为 1
return ERROR_FAIL;
}
return ERROR_OK;
}

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 数据。代码如下:

 static bb_value_t parport_read(void)
{
int data = ; #if PARPORT_USE_PPDEV == 1
ioctl(device_handle, PPRSTATUS, &data); // 读取数据
#else
data = inb(statusport); // 读取数据
#endif if ((data ^ cable->INPUT_INVERT) & cable->TDO_MASK) // 分析读取到的 TDO 数据读取数据
return BB_HIGH;
else
return BB_LOW;
}

5.2 parport_write

parport_write 函数用于构建并口的输出数据,并执行数据输出。代码如下:

 static int parport_write(int tck, int tms, int tdi)
{
int i = wait_states + ; if (tck) // 设置 TCK 数据
dataport_value |= cable->TCK_MASK;
else
dataport_value &= ~cable->TCK_MASK; if (tms) // 设置 TMS 数据
dataport_value |= cable->TMS_MASK;
else
dataport_value &= ~cable->TMS_MASK; if (tdi) // 设置 TDI 数据
dataport_value |= cable->TDI_MASK;
else
dataport_value &= ~cable->TDI_MASK; while (i-- > )
parport_write_data(); // 输出数据

return ERROR_OK;
}

parport_write_data 函数为实际的数据输出函数,代码如下:

 static inline void parport_write_data(void)
{
uint8_t output;
output = dataport_value ^ cable->OUTPUT_INVERT; // 构建输出数据

#if PARPORT_USE_PPDEV == 1
ioctl(device_handle, PPWDATA, &output);
#else
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
outb(dataport, output); // 数据输出
#else
outb(output, dataport); // 数据输出
#endif
#endif
}

5.3 parport_reset

parport_reset 函数用于 trst 或 srst,代码如下:

 /* (1) assert or (0) deassert reset lines */
static int parport_reset(int trst, int srst)
{
LOG_DEBUG("trst: %i, srst: %i", trst, srst); if (trst == ) // 设置 trst
dataport_value |= cable->TRST_MASK;
else if (trst == )
dataport_value &= ~cable->TRST_MASK; if (srst == ) // 设置 srst
dataport_value |= cable->SRST_MASK;
else if (srst == )
dataport_value &= ~cable->SRST_MASK; parport_write_data(); // 发送数据

return ERROR_OK;
}

5.4 parport_led

parport_led 函数用于点亮和关闭调试器上的指示灯。代码如下:

 /* turn LED on parport adapter on (1) or off (0) */
static int parport_led(int on)
{
if (on) // 是否点亮指示灯
dataport_value |= cable->LED_MASK;
else
dataport_value &= ~cable->LED_MASK; parport_write_data(); // 发送数据

return ERROR_OK;
}
												

【简介】OpenOCD 由jtag操作到parport driver流程的更多相关文章

  1. 移植UE4的模型操作到Unity中

    最近在Unity上要写一个东东,功能差不多就是在Unity编辑器上的旋转,移动这些,在手机上也能比较容易操作最好,原来用Axiom3D写过一个类似的,有许多位置并不好用,刚好在研究UE4的源码,在模型 ...

  2. Python 控制流代码混淆简介,加大别人分析你代码逻辑和流程难度

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 王平 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自 ...

  3. OpenOCD安装与使用(JTAG调试)

    本文介绍openocd开源软件的安装以及搭配JTAG对Xilinx u500VC707devkit的调试 PC OS: Ubuntu20.04 LTS Target ARCH: riscv64 JTA ...

  4. OpenOCD Debug Adapter Configuration

    Correctly installing OpenOCD includes making your operating system give OpenOCD access to debug adap ...

  5. 高通平台 lcd driver 调试小结

    一.概述 1.1 简介 本文档主要包括LCD模块的驱动流程分析.Framebuffer相关知识.Gralloc等相关内容,以及LCD调试的一些经验和相关bug的分析和讲解. 1.2  开发环境 And ...

  6. web框架开发-Django模型层(1)之ORM简介和单表操作

    ORM简介 不需要使用pymysql的硬编码方式,在py文件中写sql语句,提供更简便,更上层的接口,数据迁移方便(有转换的引擎,方便迁移到不同的数据库平台)…(很多优点),缺点,因为多了转换环节,效 ...

  7. odl v2 driver

    networking-odl项目的目的/用途就是sync odl和neutron的资源数据库和状态 v1中对于每个neutron的资源操作都相应的调用odl restfu api来同步odl,但问题有 ...

  8. Linux下编译内核配置选项简介

    Code maturity level options代码成熟度选项 Prompt for development and/or incomplete code/drivers 显示尚在开发中或尚未完 ...

  9. driver匹配元素定位用法大全

    # -*- coding:utf-8 -*- from selenium import webdriver from selenium.webdriver.common.by import By fr ...

随机推荐

  1. 《软件自动化测试开发-Java和Python测试开发实用指南》出版了

    1.关于书中下载链接的问题:出现404,页面不存在 解答:大小写要区分,l和1不能弄错了 2.关于勘误,出现极个别漏子少字错字 解答:后续版本会改进,目前能理解就好了 ---------------- ...

  2. AQS总结

    前言 AQS(Abstract Queued Synchronizer)是JUC并发包中的核心基础组件,作者是大名鼎鼎的Doug Lea.通过AQS可以实现大部分的同步需求. 宏观架构 AQS包括一个 ...

  3. 码海拾遗:简述C++(一)

    C++是Bjarne Stroustrup博士于1982年,在C语言的基础上引入并扩充了面向对象的概念后发明的一种新的程序语言.就与C语言的渊源而言,C++可以说是C语言的超集,它兼容C的一切(可能是 ...

  4. Hexo和github搭建个人博客 - 朱晨

    GitHub账号 mac/pc 环境 12 node.jsgit 创建GitHub仓库 登陆GitHub,创建一个新的Respository Repository name叫做{username}.g ...

  5. Dangerous query method called with non-attribute argument(s)

    踩坑 query method. 问题描述 现有model issue,需要对issues进行排序,根据指定的ID集合来决定记录的位置,比如id包含在(4, 6, 9)中的纪录就排在前面,剩下的排在后 ...

  6. go语言指南之切片练习

    题目: 实现 Pic.它应当返回一个长度为 dy 的切片,其中每个元素是一个长度为 dx,元素类型为 uint8 的切片.当你运行此程序时,它会将每个整数解释为灰度值(好吧,其实是蓝度值)并显示它所对 ...

  7. 进阶之路 | 奇妙的Handler之旅

    前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 需要已经具备的知识: Handler的基本概念及使用 学习导图: 一.为什么要学习Handler? 在Andr ...

  8. czC#02

    1.out参数 out参数要求在方法的内部必须为其赋值 using System; using System.Text; namespace Demo { class Program { //返回一个 ...

  9. blender 2.8 [学习笔记-04] 编辑模式-网格拆分

    在编辑模式下

  10. 【猫狗数据集】谷歌colab之使用pytorch读取自己数据集(猫狗数据集)

    之前在:https://www.cnblogs.com/xiximayou/p/12398285.html创建好了数据集,将它上传到谷歌colab 在colab上的目录如下: 在utils中的rdat ...