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. 实战:Zabbix 3.4邮件报警配置

    环境:CentOS 7 + Zabbix 3.4 如果只是监测并不能满足需求,还需要通过邮件的方式对问题进行报警.接下来进行实战操作: 一.安装邮件发送工具 mailx 如果CentOS 有邮件服务, ...

  2. MyBatis like (模糊查询)

    select * from user where user_name like concat('%',#{userName},'%'); select * from user where user_n ...

  3. 初窥Android AudioFlinger

    Android  AudioFlinger 是Android音频系统的两大服务之一,另一个服务是AudioPolicyService,这两大服务都在系统启动时有MediaSever加载,加载的代码位于 ...

  4. Spring aop(1)--- 寻找切面和代理对象执行流程源码分析

    1.基于注解,首先我们是通过@EnableAspectJAutoProxy()这个注解开起AOP功能,这个注解会导入AspectJAutoProxyRegistrar组件从而将AnnotationAw ...

  5. Reids(4)——神奇的HyperLoglog解决统计问题

    一.HyperLogLog 简介 HyperLogLog 是最早由 Flajolet 及其同事在 2007 年提出的一种 估算基数的近似最优算法.但跟原版论文不同的是,好像很多书包括 Redis 作者 ...

  6. 如何使用@import导入实现了ImportBeanDefinitionRegistrar接口的类?

    如何使用@import导入实现了ImportBeanDefinitionRegistrar接口的类?   在程序开发的时候,我们经常会遇见一个名词“接口”这也是我们做开发人员工作中必不可少的一个技术, ...

  7. 7-7 jmu_python_是否是数 (10 分)

    本题要求从键盘输入一个字符串,判断该串是否属于整数.浮点数或者复数的表示 输入格式: 输入一个字符串 输出格式: 输出yes或no 输入样例: -299 输出样例: yes a = input() t ...

  8. Spring-Boot-应用可视化监控

    图文简介 快速开始 1.Spring Boot 应用暴露监控指标[版本 1.5.7.RELEASE] 首先,添加依赖如下依赖: <dependency> <groupId>or ...

  9. 关于IT培训机构的个人看法

    1.前言 缘分与巧合,最近接触比较多的培训机构出来的人,以及看过关于培训机构的文章和问答.虽然没在培训机构上过课,但是接触过很多培训机构出来的人,也看过一些培训机构的课程.关于培训机构,我也有自己的看 ...

  10. Python -Selenium的安装和调用

    安装selenium步骤: 1.安装pip(cmd命令行管理员方式): pip install pip 也可直接搜索pip,到官网下载安装 2.安装selenium(cmd命令行管理员方式): pip ...