經過上篇「Day 14: 讓百萬人都驚呆的Debug Transport Module~~(上)」的基本介紹後,
今天來看幾個實作的方式,看看如何得到IDCODE和對Debug Module作基本的讀寫!!
請參考以下程式碼
static int idcode_scan(struct target *target)
{
struct scan_field field;
uint8_t in_value[4];
jtag_add_ir_scan(target->tap, &select_idcode, TAP_IDLE); ///譯註: 基本上就是把0x01 IDCODE敲進去DTM中!!
field.num_bits = 32;
field.out_value = NULL;
field.in_value = in_value;
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE); ///譯註: 然後從DR中把值撈回來!
int retval = jtag_execute_queue();
if (retval != ERROR_OK) {
LOG_ERROR("failed jtag scan: %d", retval);
return retval;
}
/* Always return to dbus. */
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
uint32_t in = buf_get_u32(.fieldin_value, 0, 32);
LOG_DEBUG("IDCODE: 0x0 -> 0x%x", in);
return in;
}
在OpenOCD中提供一個簡單的APIs用來控制JTAG的運作,如這個實作中所用到的jtag_add_ir_scan
!
原型如下:
void jtag_add_ir_scan(struct jtag_tap *active,
struct scan_field *in_fields,
tap_state_t state)
簡單說明一下參數的意義:
** 忘記JTAG的操作可以參考這邊: 小蘿蔔工作室 Little Robot Studio - JTAG **
(*註1): 所要傳輸的資料
uint8_t ir_idcode[1] = {0x1};
struct scan_field select_idcode = {
.in_value = NULL,
.out_value = ir_idcode
};
還記得在「Day 08: RISC-V Debug Module (上篇): Overview & Target Status Control」中,我們提到的幾個用來針對DMI Access操作(Read/Write)的實作嗎!?
讓我們在這邊再複習一下!
主要分成以下三個部分:
不過呢!? 上層對於Debug Module的操作(Read/Write)我們已經提到過!
今天讓我們更深入研究一下底層(DMI Scan),DMI是如何被傳輸的!
以下程式碼請參考src/target/riscv/riscv-013.c
/**
* exec: If this is set, assume the scan results in an execution, so more
* run-test/idle cycles may be required.
*/
static dmi_status_t dmi_scan(struct target *target, uint16_t *address_in,
uint64_t *data_in, dmi_op_t op, uint16_t address_out, uint64_t data_out,
bool exec)
{
riscv013_info_t *info = get_info(target);
uint8_t in[8] = {0};
uint8_t out[8];
struct scan_field field = {
.num_bits = info->abits + DTM_DMI_OP_LENGTH + DTM_DMI_DATA_LENGTH,
.out_value = out,
.in_value = in
};
assert(info->abits != 0);
///譯註: 0x11 $dmi的格式由address+data+op組成 (*註1)
buf_set_u64(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, op);
buf_set_u64(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out);
buf_set_u64(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out);
/* Assume dbus is already selected. */
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
int idle_count = info->dmi_busy_delay;
if (exec)
idle_count += info->ac_busy_delay;
if (idle_count) {
///譯註: 加上Delay (*註2)
jtag_add_runtest(idle_count, TAP_IDLE);
}
int retval = jtag_execute_queue();
if (retval != ERROR_OK) {
LOG_ERROR("dmi_scan failed jtag scan");
return DMI_STATUS_FAILED;
}
if (data_in) {
*data_in = buf_get_u64(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
}
if (address_in) {
*address_in = buf_get_u32(in, DTM_DMI_ADDRESS_OFFSET, info->abits);
}
dump_field(&field);
return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
}
(*註1) 可以參考「Day 14: 讓百萬人都驚呆的Debug Transport Module~~(上)」中的"# 2.3 0x11 dmi: Debug Module Interface Access"
(*註2) 可以參考「Day 14: 讓百萬人都驚呆的Debug Transport Module~~(上)」中的"# 2.2 0x10 dtmsc: DTM Control and Status"中idle欄位的建議值來設定,不過OpenOCD這邊用方式比較特別一些,
當上次DMI操作發生Busy時,他內部會有一個"線性方程式"會不斷地增加這個參考值,
然後下次遇到相關DMI的操作時,就直接拿個這算好的參考值來用!
請參考以下實作的方式(src/target/riscv/riscv-013.c)
static void increase_dmi_busy_delay(struct target *target)
{
riscv013_info_t *info = get_info(target);
info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1;
LOG_INFO("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
info->dtmcontrol_idle, info->dmi_busy_delay,
info->ac_busy_delay);
dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
}
和
static void increase_ac_busy_delay(struct target *target)
{
riscv013_info_t *info = get_info(target);
info->ac_busy_delay += info->ac_busy_delay / 10 + 1;
LOG_INFO("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
info->dtmcontrol_idle, info->dmi_busy_delay,
info->ac_busy_delay);
}
終於來到作後啦,相信冰雪聰明的讀者看到前面IDCODE和DMI的兩個範例後,
應該知道該如何實作這部分了吧XD!?
一樣先看程式碼(改寫自src/target/riscv/riscv-013.c)
static uint32_t dtmcs_scan(struct target *target, uint32_t out)
{
struct scan_field field;
uint8_t in_value[4];
uint8_t out_value[4];
buf_set_u32(out_value, 0, 32, out);
///譯註: 基本上就是把0x10 dtmcs敲進去DTM中 (*註1)!!
jtag_add_ir_scan(target->tap, &select_dtmcs, TAP_IDLE);
field.num_bits = 32;
field.out_value = out_value;
field.in_value = in_value;
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
/* Always return to dmi. */
select_dmi(target);
int retval = jtag_execute_queue();
if (retval != ERROR_OK) {
LOG_ERROR("failed jtag scan: %d", retval);
return retval;
}
uint32_t in = buf_get_u32(field.in_value, 0, 32);
LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in);
return in;
}
(*註1): 所要傳輸的資料如下:
#define DTMCS 0x10
uint8_t ir_dtmcs[1] = {DTMCS};
struct scan_field select_dtmcs = {
.in_value = NULL,
.out_value = ir_dtmcs
};
以上這幾天的文章中,終於將RISC-V底層的架構從上端Debug Module、中間Trigger Module到最後底層Debug Transport Module都給仔細地講完一遍~~然後鐵人賽就結束了 慘
沒啦,才剛過一半而已(Day 15),明天開始將進入到全新的領域--FTDI Based Adapter,
講述從PC端到Target端中間的Debug Trasnport Hardware的相關知識及應用 (大概吧!