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. 在dataframe添加1行(首行,或者尾部),且不覆盖

    如果直接用下面的代码添加第1行,则会覆盖掉原来的第1行. #指定位置增加一行: df.loc[0]={'a':1,'b':2} 正确方法: 新建一个同样的 dataframe, 然后合并两个dataf ...

  2. rpm报错warning: /var/tmp/rpm-tmp.1OZa8q: Header V3 DSA/SHA1 Signature, key ID 5072e1f5: NOKEY的解决

    参考链接:http://blog.51cto.com/zymin0823/1546537 报错: 解决:使用如下两个选项

  3. flutter 白板工具Twitter IconFacebook Icon

    flutter 白板工具 Categories: flutter 平常桌面上都放着一些草稿纸,因为经常要整理思路.画画草图啥的.这不是电子时代嘛,就觉得在手机.pad上也可以这样写写画画,我看了有很多 ...

  4. Python2.7错误处理FileNotFoundError报错NameError: name 'FileNotFoundError' is not defined

    错误信息如下: 原因是FileNotFoundError是python3.0中的写法,而Python2.7中应写为IOError.

  5. 关于Newtonsoft.Json引用报错

    自己运行的vs版本是2012,然后同事用了2017的,我把代码发给他后运行发现报以下错误: {未能加载文件或程序集"Newtonsoft.Json, Version=4.5.0.0, Cul ...

  6. P1678 烦恼的高考志愿

    P1678题库链接:https://www.luogu.org/problem/P1678 难度:普及- 算法标签:模拟,贪心,排序,二分查找 1.朴素模拟 O(m*n) 得分30 先将m个学校的录取 ...

  7. 完全依赖QML实现播放器

    前言 一直听闻QML无比强大好用,工作中需要扣一个同时播放视频的Demo,所以就趁这个机会研究了一下. 效果图和源码 源码仓库 主要设计 主页面QML import QtQuick 2.12 impo ...

  8. java8新特性——stream笔记

    stream对象 Stream IntStream LongStream DoubleStream 创建 常用的三种方式: 使用list对象: list.stream() − 为集合创建串行流. li ...

  9. 带你入门 CSS Grid 布局

    前言 三月中旬的时候,有一个对于 CSS 开发者来说很重要的消息,最新版的 Firefox 和 Chrome 已经正式支 CSS Grid 这一新特性啦.没错:我们现在就可以在最流行的两大浏览器上玩转 ...

  10. spring——AOP原理及源码(五)

    前情回顾: 在上一篇中,通过 wrapIfNecessary 方法,我们获取到了合适的增强器(日志方法)与业务类进行包装,最终返回了我们业务类的代理对象. 本篇我们将从业务方法的执行开始,看看增强器( ...