【简介】OpenOCD 由jtag操作到parport driver流程
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流程的更多相关文章
- 移植UE4的模型操作到Unity中
最近在Unity上要写一个东东,功能差不多就是在Unity编辑器上的旋转,移动这些,在手机上也能比较容易操作最好,原来用Axiom3D写过一个类似的,有许多位置并不好用,刚好在研究UE4的源码,在模型 ...
- Python 控制流代码混淆简介,加大别人分析你代码逻辑和流程难度
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 王平 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自 ...
- OpenOCD安装与使用(JTAG调试)
本文介绍openocd开源软件的安装以及搭配JTAG对Xilinx u500VC707devkit的调试 PC OS: Ubuntu20.04 LTS Target ARCH: riscv64 JTA ...
- OpenOCD Debug Adapter Configuration
Correctly installing OpenOCD includes making your operating system give OpenOCD access to debug adap ...
- 高通平台 lcd driver 调试小结
一.概述 1.1 简介 本文档主要包括LCD模块的驱动流程分析.Framebuffer相关知识.Gralloc等相关内容,以及LCD调试的一些经验和相关bug的分析和讲解. 1.2 开发环境 And ...
- web框架开发-Django模型层(1)之ORM简介和单表操作
ORM简介 不需要使用pymysql的硬编码方式,在py文件中写sql语句,提供更简便,更上层的接口,数据迁移方便(有转换的引擎,方便迁移到不同的数据库平台)…(很多优点),缺点,因为多了转换环节,效 ...
- odl v2 driver
networking-odl项目的目的/用途就是sync odl和neutron的资源数据库和状态 v1中对于每个neutron的资源操作都相应的调用odl restfu api来同步odl,但问题有 ...
- Linux下编译内核配置选项简介
Code maturity level options代码成熟度选项 Prompt for development and/or incomplete code/drivers 显示尚在开发中或尚未完 ...
- driver匹配元素定位用法大全
# -*- coding:utf-8 -*- from selenium import webdriver from selenium.webdriver.common.by import By fr ...
随机推荐
- log4j.properties总结
一.以自己的log4j.properties为例: # 配置根Logger,格式:log4j.rootLogger = [ level ] , appenderName1, appenderName2 ...
- JDBC连接到数据库查询打印数据
通过一天的视频学习,认识了jdbc的连接原理前来小结: 游标读取数据库表的行一次读取一个,getXxx()方法读取表的列一个数据 next()方法可以让游标下移 可以把数据库的表看做是一个类,每条记录 ...
- 【51nod1462】树据结构
Source and Judge 51nod1462 Analysis 请先思考后再展开 dffxtz师兄出的题 做法一:暴力树剖+分块,时间复杂度为 $O(nlognsqrt n)$ 做法二:利用矩 ...
- Spring Boot 2.x基础教程:使用 Thymeleaf开发Web页面
通过本系列教程的前几章内容(API开发.数据访问).我们已经具备完成一个涵盖数据存储.提供HTTP接口的完整后端服务了.依托这些技能,我们已经可以配合前端开发人员,一起来完成一些前后端分离的Web项目 ...
- 谈谈Vue的递归组件
2月最后一天,而且还四年一遇,然而本月居然一篇博客没写,有点说不过去.所以,今天就来谈谈Vue的递归组件.我们先来看一个例子: See the Pen 递归组件 by imgss (@imgss) o ...
- sql--测试商品的重要度,是否需要及时补货
表1:商品表 表2:商品售卖表 需求:算出商品的平均点击率.平均销售.商品受欢迎度 1.使用inner join查出每件商品的点击率和销售额度 ) as selas from test a left ...
- 前端每日实战:146# 视频演示如何用纯 CSS 创作一个脉动 loader
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/wYvGwr 可交互视频 此视频是可 ...
- 利用ajax 引入静态页公共的头部与底部
利用ajax引入公共的头部与底部或者多个页面需要用到的重复的组件,对于新入门的前端来说是很实用的方法,自己也是新手菜鸟一枚,折腾了好久,实现的方法有很多种,这是我个人觉得比较简单方便的 首先得把公用的 ...
- DNA sequence HDU - 1560
DNA sequence Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...
- 钉钉小程序不用canvas在后端绘图前端用image标签获取图片的实践
公司的需求要用电子员工卡代替用了N久的工作证,在各种场合刷二维码来代替刷卡.在钉钉小程序里实现.感觉这回又要躺坑里了. 钉钉小程序第一次做.我这个自封的GDI+大神才不要想用钉钉jsapi的方式用ca ...