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. log4j.properties总结

    一.以自己的log4j.properties为例: # 配置根Logger,格式:log4j.rootLogger = [ level ] , appenderName1, appenderName2 ...

  2. JDBC连接到数据库查询打印数据

    通过一天的视频学习,认识了jdbc的连接原理前来小结: 游标读取数据库表的行一次读取一个,getXxx()方法读取表的列一个数据 next()方法可以让游标下移 可以把数据库的表看做是一个类,每条记录 ...

  3. 【51nod1462】树据结构

    Source and Judge 51nod1462 Analysis 请先思考后再展开 dffxtz师兄出的题 做法一:暴力树剖+分块,时间复杂度为 $O(nlognsqrt n)$ 做法二:利用矩 ...

  4. Spring Boot 2.x基础教程:使用 Thymeleaf开发Web页面

    通过本系列教程的前几章内容(API开发.数据访问).我们已经具备完成一个涵盖数据存储.提供HTTP接口的完整后端服务了.依托这些技能,我们已经可以配合前端开发人员,一起来完成一些前后端分离的Web项目 ...

  5. 谈谈Vue的递归组件

    2月最后一天,而且还四年一遇,然而本月居然一篇博客没写,有点说不过去.所以,今天就来谈谈Vue的递归组件.我们先来看一个例子: See the Pen 递归组件 by imgss (@imgss) o ...

  6. sql--测试商品的重要度,是否需要及时补货

    表1:商品表 表2:商品售卖表 需求:算出商品的平均点击率.平均销售.商品受欢迎度 1.使用inner join查出每件商品的点击率和销售额度 ) as selas from test a left ...

  7. 前端每日实战:146# 视频演示如何用纯 CSS 创作一个脉动 loader

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/wYvGwr 可交互视频 此视频是可 ...

  8. 利用ajax 引入静态页公共的头部与底部

    利用ajax引入公共的头部与底部或者多个页面需要用到的重复的组件,对于新入门的前端来说是很实用的方法,自己也是新手菜鸟一枚,折腾了好久,实现的方法有很多种,这是我个人觉得比较简单方便的 首先得把公用的 ...

  9. DNA sequence HDU - 1560

    DNA sequence Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tot ...

  10. 钉钉小程序不用canvas在后端绘图前端用image标签获取图片的实践

    公司的需求要用电子员工卡代替用了N久的工作证,在各种场合刷二维码来代替刷卡.在钉钉小程序里实现.感觉这回又要躺坑里了. 钉钉小程序第一次做.我这个自封的GDI+大神才不要想用钉钉jsapi的方式用ca ...