經過上篇程式碼塞好、塞滿後~~!
今天還是要繼續這個主題下去,主要剖析RISC-V在Target Status Control相關的程式碼!
首先,先來看一下進入點的部分,
參考以下內容(src/target/riscv/riscv.c):
int riscv_openocd_halt(struct target *target)
{
RISCV_INFO(r);
LOG_DEBUG("halting all harts");
int out = riscv_halt_all_harts(target); ///譯註: 讓所有的Hart進入Halt
if (out != ERROR_OK) {
LOG_ERROR("Unable to halt all harts");
return out;
}
register_cache_invalidate(target->reg_cache);
if (riscv_rtos_enabled(target)) {
target->rtos->current_threadid = r->rtos_hartid + 1;
target->rtos->current_thread = r->rtos_hartid + 1;
}
target->state = TARGET_HALTED; ///譯註: 設定目前的狀態為TARGET_HALTED
target->debug_reason = DBG_REASON_DBGRQ;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return out;
}
這部分比較簡單,主要就是呼叫riscv_halt_all_harts()
讓所有的Hart進入Halt的狀態!
所以接下來就是來看一下riscv_halt_all_harts()的內容(src/target/riscv/riscv.c):
int riscv_halt_all_harts(struct target *target)
{
for (int i = 0; i < riscv_count_harts(target); ++i) {
if (!riscv_hart_enabled(target, i))
continue;
riscv_halt_one_hart(target, i); ///譯註: 針對單一個Hart設定進入Halt的狀態
}
return ERROR_OK;
}
這也很簡單,就是針對每一個Hart,呼叫riscv_halt_one_hart()
,讓該Hart進入Halt,
所以一樣,這邊再來看一下riscv_halt_one_hart()的內容!
int riscv_halt_one_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
LOG_DEBUG("halting hart %d", hartid);
riscv_set_current_hartid(target, hartid);
///譯註: 判斷該Hart是否已經在Halt的狀態
if (riscv_is_halted(target)) {
LOG_DEBUG(" hart %d requested halt, but was already halted", hartid);
return ERROR_OK;
}
return r->halt_current_hart(target);
}
一樣,這邊先簡單的判斷一下,假如該Hart已經在Halt的階段的話,
那就可以直接回傳ERROR_OK,不用再繼續往下處理!
最後,終於來到核心的處理部分,讓我們看一下halt_current_hart()是如何處理!
static int riscv013_halt_current_hart(struct target *target)
{
RISCV_INFO(r);
LOG_DEBUG("halting hart %d", r->current_hartid);
if (riscv_is_halted(target))
LOG_ERROR("Hart %d is already halted!", r->current_hartid);
/* Issue the halt command, and then wait for the current hart to halt. */
uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 1); ///譯註: 將haltreq拉成1,讓Target進入Halt
dmi_write(target, DMI_DMCONTROL, dmcontrol);
///譯註: 做個檢查!
for (size_t i = 0; i < 256; ++i)
if (riscv_is_halted(target))
break;
if (!riscv_is_halted(target)) {
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
dmcontrol = dmi_read(target, DMI_DMCONTROL);
LOG_ERROR("unable to halt hart %d", r->current_hartid);
LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
LOG_ERROR(" dmstatus =0x%08x", dmstatus);
return ERROR_FAIL;
}
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 0);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
return ERROR_OK;
}
有沒有覺得這個有點熟悉啊!?
我們在「Day 10: RISC-V Debug Module (下篇)-Debug Module Registers」中的"# 2.1 0x10 dmcontrol: Debug Module Control"有提到過,如果需要讓該Hart進入Halt的階段,就是將$dmcontrol中的haltreq設定成1!
當然,設定完之後還要確認一下Hart是否有真得停下來,為了避免陷入無窮等待之中,
這邊預設會讀256次:
for (size_t i = 0; i < 256; ++i)
if (riscv_is_halted(target))
break;
如果還是真的無法停下來,那就要進行錯誤處理(幾本上就是把狀態印出來到Log中):
if (!riscv_is_halted(target)) {
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
dmcontrol = dmi_read(target, DMI_DMCONTROL);
LOG_ERROR("unable to halt hart %d", r->current_hartid);
LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
LOG_ERROR(" dmstatus =0x%08x", dmstatus);
return ERROR_FAIL;
}
最後,記得把haltreq放掉:
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 0);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
以上就是針對Halt Target的處理!
既然有Halt了,那當然就有Resume,一樣先來看一下進入點的部分,
參考以下內容(src/target/riscv/riscv.c):
int riscv_openocd_resume(
struct target *target,
int current,
target_addr_t address,
int handle_breakpoints,
int debug_execution
) {
LOG_DEBUG("resuming all harts");
///譯註: Resume可以在不同的地方開始,這邊先把要Resume的位置寫入$PC中
if (!current)
riscv_set_register(target, GDB_REGNO_PC, address);
int out = riscv_resume_all_harts(target); ///譯註: 讓所有的Hart進入Resume
if (out != ERROR_OK) {
LOG_ERROR("unable to resume all harts");
return out;
}
register_cache_invalidate(target->reg_cache);
target->state = TARGET_RUNNING;
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
return out;
}
跟Halt的方式類似,一樣是呼叫riscv_resume_all_harts()
,
讓所有的Hart進入Resume的狀態!
接下來就是要來研究一下riscv_resume_all_harts()的內容(src/target/riscv/riscv.c):
int riscv_resume_all_harts(struct target *target)
{
for (int i = 0; i < riscv_count_harts(target); ++i) {
if (!riscv_hart_enabled(target, i))
continue;
riscv_resume_one_hart(target, i); ///譯註: 針對單一個Hart設定進入Resume的狀態
}
riscv_invalidate_register_cache(target);
return ERROR_OK;
}
這也很簡單,就是針對每一個Hart,呼叫riscv_resume_one_hart()
,讓該Hart進入Resume,
所以這邊再來看一下riscv_resume_one_hart()的內容,請參考(src/target/riscv/riscv.c):
int riscv_resume_one_hart(struct target *target, int hartid)
{
RISCV_INFO(r);
LOG_DEBUG("resuming hart %d", hartid);
riscv_set_current_hartid(target, hartid);
///譯註: 判斷該Hart是否已經Resume
if (!riscv_is_halted(target)) {
LOG_DEBUG(" hart %d requested resume, but was already resumed", hartid);
return ERROR_OK;
}
r->on_resume(target);
return r->resume_current_hart(target);
}
一樣,這邊先簡單的判斷一下,假如該Hart已經Resume的話,
那就可以直接回傳ERROR_OK,不用再繼續往下處理,
這邊呼叫到兩個核心的處理on_resume()
和resume_current_hart()
首先是on_resume()
的部分,請參考(src/target/riscv/riscv-013.c):
static void riscv013_on_resume(struct target *target)
{
return riscv013_on_step_or_resume(target, false);
}
static void riscv013_on_step_or_resume(struct target *target, bool step)
{
///譯註: 執行Program Buffer
struct riscv_program program;
riscv_program_init(&program, target);
riscv_program_fence_i(&program);
if (riscv_program_exec(&program, target) != ERROR_OK)
LOG_ERROR("Unable to execute fence.i");
/* We want to twiddle some bits in the debug CSR so debugging works. */
///譯註: 針對$dcsr做處理
uint64_t dcsr = riscv_get_register(target, GDB_REGNO_DCSR);
dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1);
riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
}
可以發現到on_resume()
主要是在Resume之前,做一些必要的設定!
首先是利用Program Buffer執行fence.i
這個指令,將Instruction和Data做同步的處理,詳細資料可以參閱「The RISC-V Instruction Set Manual Volume I: User-Level ISA」的說明!
再來就是,在「Day 11: RISC-V Debug Introduction」中有提到過的$dcsr (忘記可以翻一下"# 5.1 0x7b0 dcsr: Debug Control and Status"),主要控制Hart在Debug Mode下的行為!
這邊主要有三點:
dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1);
分別是在M(Machine) Mode、S(Supervisor) Mode、U(User) Mode中,
如果收到"ebreak"指令的話,皆會回到Debug Mode!
最後,我們來看一下resume_current_hart()
的處理,請參考(src/target/riscv/riscv-013.c):
static int riscv013_resume_current_hart(struct target *target)
{
return riscv013_step_or_resume_current_hart(target, false);
}
static int riscv013_step_or_resume_current_hart(struct target *target, bool step)
{
RISCV_INFO(r);
LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step);
if (!riscv_is_halted(target)) {
LOG_ERROR("Hart %d is not halted!", r->current_hartid);
return ERROR_FAIL;
}
struct riscv_program program;
riscv_program_init(&program, target);
riscv_program_fence_i(&program);
if (riscv_program_exec(&program, target) != ERROR_OK)
return ERROR_FAIL;
/* Issue the resume command, and then wait for the current hart to resume. */
uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 1); ///譯註: 將resumereq拉成1,讓Target進入Resume
dmi_write(target, DMI_DMCONTROL, dmcontrol);
///譯註: 一樣做個檢查!
for (size_t i = 0; i < 256; ++i) {
usleep(10);
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
if (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0)
continue;
if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0)
continue;
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
return ERROR_OK;
}
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
dmcontrol = dmi_read(target, DMI_DMCONTROL);
LOG_ERROR("unable to resume hart %d", r->current_hartid);
LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
LOG_ERROR(" dmstatus =0x%08x", dmstatus);
if (step) {
LOG_ERROR(" was stepping, halting");
riscv013_halt_current_hart(target);
return ERROR_OK;
}
return ERROR_FAIL;
}
主要分成三個部分,首先是將resumereq拉起來成1,
忘記的話可以參考「Day 10: RISC-V Debug Module (下篇)-Debug Module Registers」中的"# 2.1 0x10 dmcontrol: Debug Module Control"!
再過來一樣,做個檢查
for (size_t i = 0; i < 256; ++i) {
usleep(10);
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
if (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0)
continue;
if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0)
continue;
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
return ERROR_OK;
}
看看Hart有沒有Resume成功!
最後就是假如沒有Resume成功的話,就進行錯誤處理!
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
dmcontrol = dmi_read(target, DMI_DMCONTROL);
LOG_ERROR("unable to resume hart %d", r->current_hartid);
LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
LOG_ERROR(" dmstatus =0x%08x", dmstatus);
以上就是針對Resume Target的部份!
其實Step就是在Resume完後,CPU執行一道指令後就立刻進入Debug Mode中,
因此在實作中,可以共同使用大部分的處理流程!
一樣,先看進入點,參考以下內容(src/target/riscv/riscv.c):
int riscv_openocd_step(
struct target *target,
int current,
target_addr_t address,
int handle_breakpoints
) {
LOG_DEBUG("stepping rtos hart");
///譯註: 同Resuem,Step可以在不同的地方開始,
/// 這邊先把要Step的位置寫入$PC中
if (!current)
riscv_set_register(target, GDB_REGNO_PC, address);
int out = riscv_step_rtos_hart(target); ///譯註: 讓該Hart進行Step
if (out != ERROR_OK) {
LOG_ERROR("unable to step rtos hart");
return out;
}
register_cache_invalidate(target->reg_cache);
target->state = TARGET_RUNNING; ///譯註: 先將狀態設成RUNNING
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
target->state = TARGET_HALTED; ///譯註: 然後再次進入HALTED的狀態
target->debug_reason = DBG_REASON_SINGLESTEP;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return out;
}
主要就是呼叫riscv_step_rtos_hart()
!
再過來研究一下riscv_step_rtos_hart()的內容,請參考(src/target/riscv/riscv.c):
int riscv_step_rtos_hart(struct target *target)
{
RISCV_INFO(r);
int hartid = r->current_hartid;
if (riscv_rtos_enabled(target)) {
hartid = r->rtos_hartid;
if (hartid == -1) {
LOG_USER("GDB has asked me to step \"any\" thread, so I'm stepping hart 0.");
hartid = 0;
}
}
riscv_set_current_hartid(target, hartid);
LOG_DEBUG("stepping hart %d", hartid);
///譯註: 判斷該Hart是否已經在Halt的狀態
if (!riscv_is_halted(target)) {
LOG_ERROR("Hart isn't halted before single step!");
return ERROR_FAIL;
}
riscv_invalidate_register_cache(target);
r->on_step(target);
if (r->step_current_hart(target) != ERROR_OK)
return ERROR_FAIL;
riscv_invalidate_register_cache(target);
r->on_halt(target);
if (!riscv_is_halted(target)) {
LOG_ERROR("Hart was not halted after single step!");
return ERROR_FAIL;
}
return ERROR_OK;
}
由於在Resume(Running)的狀態中,就不需要進行Step,
因此會先判斷一下這種狀況:
if (!riscv_is_halted(target)) {
LOG_ERROR("Hart isn't halted before single step!");
return ERROR_FAIL;
}
最後呼叫到兩個核心的處理on_step()
和step_current_hart()
來處理Step的流程
一樣,先來看看on_step()處理的過程,請參考(src/target/riscv/riscv-013.c):
static void riscv013_on_resume(struct target *target)
{
return riscv013_on_step_or_resume(target, false);
}
static void riscv013_on_step_or_resume(struct target *target, bool step)
{
struct riscv_program program;
riscv_program_init(&program, target);
riscv_program_fence_i(&program);
if (riscv_program_exec(&program, target) != ERROR_OK)
LOG_ERROR("Unable to execute fence.i");
/* We want to twiddle some bits in the debug CSR so debugging works. */
uint64_t dcsr = riscv_get_register(target, GDB_REGNO_DCSR);
dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1);
dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1);
riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
}
等等等~~~ 根本就跟on_resume()
一樣!!!
主要差異就是在
dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
在step的過程中,$dcsr的step會被設定成1,
忘記的話可以參考一下「Day 11: RISC-V Debug Introduction」中的"# 5.1 0x7b0 dcsr: Debug Control and Status"
最後,step_current_hart()處理的過程,跟上面Resume中類似,
請參考(src/target/riscv/riscv-013.c):
static int riscv013_step_current_hart(struct target *target)
{
return riscv013_step_or_resume_current_hart(target, true);
}
static int riscv013_step_or_resume_current_hart(struct target *target, bool step)
{
RISCV_INFO(r);
LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step);
if (!riscv_is_halted(target)) {
LOG_ERROR("Hart %d is not halted!", r->current_hartid);
return ERROR_FAIL;
}
struct riscv_program program;
riscv_program_init(&program, target);
riscv_program_fence_i(&program);
if (riscv_program_exec(&program, target) != ERROR_OK)
return ERROR_FAIL;
/* Issue the resume command, and then wait for the current hart to resume. */
uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 1);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
for (size_t i = 0; i < 256; ++i) {
usleep(10);
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
if (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0)
continue;
if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0)
continue;
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0);
dmi_write(target, DMI_DMCONTROL, dmcontrol);
return ERROR_OK;
}
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
dmcontrol = dmi_read(target, DMI_DMCONTROL);
LOG_ERROR("unable to resume hart %d", r->current_hartid);
LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
LOG_ERROR(" dmstatus =0x%08x", dmstatus);
if (step) {
LOG_ERROR(" was stepping, halting");
riscv013_halt_current_hart(target);
return ERROR_OK;
}
return ERROR_FAIL;
}
主要差異再多做一些檢查:
if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0)
continue;
在Resume後看看有沒有重新進入Halted!
如果沒有的話,一樣,還是有錯誤處理:
if (step) {
LOG_ERROR(" was stepping, halting");
riscv013_halt_current_hart(target);
return ERROR_OK;
}
以上就是針對Step Target的部份!
最後是Reset的部分啦!!
在OpenOCD中,Reset的流程主要分成兩步驟:
首先是進入點的部份,兩個一起看,請參考(src/target/riscv/riscv.c):
static int riscv_assert_reset(struct target *target)
{
struct target_type *tt = get_target_type(target);
return tt->assert_reset(target);
}
static int riscv_deassert_reset(struct target *target)
{
LOG_DEBUG("RISCV DEASSERT RESET");
struct target_type *tt = get_target_type(target);
return tt->deassert_reset(target);
}
就是簡單的呼叫0.13中的assert_reset()
和deassert_reset()
來處理!
這部分內容在「Day 08: RISC-V Debug Module (上篇): Overview & Target Status Control」中有稍微提到過,
這邊再剖析的更仔細些!!
一樣是先看一下實作的部分,請參考(src/target/riscv/riscv-013.c):
static int assert_reset(struct target *target)
{
RISCV_INFO(r);
select_dmi(target);
uint32_t control_base = set_field(0, DMI_DMCONTROL_DMACTIVE, 1);
if (target->rtos) {
....中間省略!
} else {
/* Reset just this hart. */
uint32_t control = set_field(control_base, DMI_DMCONTROL_HARTSEL,
r->current_hartid);
control = set_field(control, DMI_DMCONTROL_HALTREQ,
target->reset_halt ? 1 : 0);
control = set_field(control, DMI_DMCONTROL_HARTRESET, 1); ///譯註: 這邊試這先拉起hartreset看看~!
dmi_write(target, DMI_DMCONTROL, control);
/* Read back to check if hartreset is supported. */
uint32_t rb = dmi_read(target, DMI_DMCONTROL);
if (!get_field(rb, DMI_DMCONTROL_HARTRESET)) {
/* Use ndmreset instead. That will reset the entire device, but
* that's probably what OpenOCD wants anyway. */
control = set_field(control, DMI_DMCONTROL_HARTRESET, 0);
///譯註: 改用ndmreset重試!
control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
dmi_write(target, DMI_DMCONTROL, control);
}
}
///譯註: 將目前Target的狀態設成TARGET_RESET
target->state = TARGET_RESET;
return ERROR_OK;
}
在Debug System中,有兩個方式讓Target可以做Reset
在這個實作當中,會先試著用hartreset來做然後重新讀回$dmcontrol中的hartreset!
看看是否可以寫入,如果不行寫入,表示硬體不支援這種方式,
那就改用ndmreset的方式來進行!
當然,假如是reset halt
這種情況的時候,記得把haltreq拉起來:
control = set_field(control, DMI_DMCONTROL_HALTREQ,
target->reset_halt ? 1 : 0);
在把Reset拉起來後,當然要把拉放回去,所以就需要進行Deassert Reset!
一樣,先看實作的部分!
static int deassert_reset(struct target *target)
{
RISCV_INFO(r);
RISCV013_INFO(info);
select_dmi(target);
LOG_DEBUG("%d", r->current_hartid);
/* Clear the reset, but make sure haltreq is still set */
uint32_t control = 0;
control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); ///譯註: Reset歸回原位,但是如果有用到haltreq的話,不能先放到!
control = set_field(control, DMI_DMCONTROL_HARTSEL, r->current_hartid);
control = set_field(control, DMI_DMCONTROL_DMACTIVE, 1);
dmi_write(target, DMI_DMCONTROL, control);
uint32_t dmstatus;
int dmi_busy_delay = info->dmi_busy_delay;
time_t start = time(NULL);
if (target->reset_halt) {
LOG_DEBUG("Waiting for hart to be halted.");
do {
dmstatus = dmi_read(target, DMI_DMSTATUS);
if (time(NULL) - start > riscv_reset_timeout_sec) {
LOG_ERROR("Hart didn't halt coming out of reset in %ds; "
"dmstatus=0x%x; "
"Increase the timeout with riscv set_reset_timeout_sec.",
riscv_reset_timeout_sec, dmstatus);
return ERROR_FAIL;
}
target->state = TARGET_HALTED;
} while (get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0);
control = set_field(control, DMI_DMCONTROL_HALTREQ, 0);
dmi_write(target, DMI_DMCONTROL, control);
} else {
LOG_DEBUG("Waiting for hart to be running.");
do {
dmstatus = dmi_read(target, DMI_DMSTATUS);
if (get_field(dmstatus, DMI_DMSTATUS_ANYHALTED) ||
get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL)) {
LOG_ERROR("Unexpected hart status during reset. dmstatus=0x%x",
dmstatus);
return ERROR_FAIL;
}
if (time(NULL) - start > riscv_reset_timeout_sec) {
LOG_ERROR("Hart didn't run coming out of reset in %ds; "
"dmstatus=0x%x; "
"Increase the timeout with riscv set_reset_timeout_sec.",
riscv_reset_timeout_sec, dmstatus);
return ERROR_FAIL;
}
} while (get_field(dmstatus, DMI_DMSTATUS_ALLRUNNING) == 0);
target->state = TARGET_RUNNING;
}
info->dmi_busy_delay = dmi_busy_delay;
return ERROR_OK;
}
分成三部分來解釋!
首先,在Reset完後,先把Reset歸回原位,但是同時,
如果有用到haltreq的話(reset halt
),則不能在此時放掉haltreq(至少要等到確認後才行)!
/* Clear the reset, but make sure haltreq is still set */
uint32_t control = 0;
control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
control = set_field(control, DMI_DMCONTROL_HARTSEL, r->current_hartid);
control = set_field(control, DMI_DMCONTROL_DMACTIVE, 1);
dmi_write(target, DMI_DMCONTROL, control);
接著分成兩部分來處理,首先是reset halt
的時候:
LOG_DEBUG("Waiting for hart to be halted.");
do {
dmstatus = dmi_read(target, DMI_DMSTATUS);
if (time(NULL) - start > riscv_reset_timeout_sec) {
LOG_ERROR("Hart didn't halt coming out of reset in %ds; "
"dmstatus=0x%x; "
"Increase the timeout with riscv set_reset_timeout_sec.",
riscv_reset_timeout_sec, dmstatus);
return ERROR_FAIL;
}
target->state = TARGET_HALTED; ///譯註: 將Target的設定成TARGET_RUNNING
} while (get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0);
///譯註: 放掉haltreq
control = set_field(control, DMI_DMCONTROL_HALTREQ, 0);
dmi_write(target, DMI_DMCONTROL, control);
為了確認所有Target都正確停下來,因此讀取$dmstatus中的allhalted這個欄位!
確認停下來後,就可以把haltreq歸位:
control = set_field(control, DMI_DMCONTROL_HALTREQ, 0);
dmi_write(target, DMI_DMCONTROL, control);
再來是針對reset run
的這個部份:
LOG_DEBUG("Waiting for hart to be running.");
do {
dmstatus = dmi_read(target, DMI_DMSTATUS);
if (get_field(dmstatus, DMI_DMSTATUS_ANYHALTED) ||
get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL)) {
LOG_ERROR("Unexpected hart status during reset. dmstatus=0x%x",
dmstatus);
return ERROR_FAIL;
}
if (time(NULL) - start > riscv_reset_timeout_sec) {
LOG_ERROR("Hart didn't run coming out of reset in %ds; "
"dmstatus=0x%x; "
"Increase the timeout with riscv set_reset_timeout_sec.",
riscv_reset_timeout_sec, dmstatus);
return ERROR_FAIL;
}
} while (get_field(dmstatus, DMI_DMSTATUS_ALLRUNNING) == 0);
target->state = TARGET_RUNNING; ///譯註: 將Target的設定成TARGET_RUNNING
一樣,為了確認所有Target都正確運行中,因此讀取$dmstatus中的allrunning這個欄位!
確認都Target都正在賣力地跑後,就可以將Target的狀態設定成TARGET_RUNNING
假如發現$dmstatus中的anyhalted或是anyunavail的時候,就表示Reset已經造成Target出現問題,因此要進行相關處理(打印Log XD!?):
if (get_field(dmstatus, DMI_DMSTATUS_ANYHALTED) ||
get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL)) {
LOG_ERROR("Unexpected hart status during reset. dmstatus=0x%x",
dmstatus);
return ERROR_FAIL;
}
以上就是針對Reset Target的處理!
今天的程式碼又是塞好塞滿!
簡單的介紹了Target相關的處理: Halt、Resume+Step、Reset!
感覺打一篇的時間越花越多~~~!