-
Notifications
You must be signed in to change notification settings - Fork 338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
target/riscv: make sure target is halted when reset_halt is set #1214
base: riscv
Are you sure you want to change the base?
Conversation
control = 0; | ||
control = set_field(control, DM_DMCONTROL_DMACTIVE, 1); | ||
control = set_dmcontrol_hartsel(control, info->index); | ||
result = dm_write(target, DM_DMCONTROL, control); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not this write be conditional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to original code, HALTREQ needs to be cleared no matter reset_haltreq is set or not. According to the spec, HALTREQ set to 1 or 0 has no effect for a halted hart, so no need to be conditional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point is that the original code had only 1 write to DM_DMCONTROL . Now it has two. What is the point of the second write if there was no haltreq?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it seems there's an issue related to GD32V which needs HALTREQ to be cleared after reset, otherwise "reset" command does not behave as expected. So I cleared the HALTREQ after reset is acknowledged. For more details about code changes related to the issue I mentioned, please refer to 7d2ea18
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
t seems there's an issue related to GD32V which needs HALTREQ to be cleared after reset, otherwise "reset" command does not behave as expected.
I agree with @aap-sc:
In case target->reset_halt == false
, there is no HATLREQ set, and therefore no reason to clear it. The clearing of HATLREQ (second write to dmcontrol) should, IMO, only occur when target->reset_halt == true
.
@jhqian Is that correct understanding, or am I missing something?
@@ -2924,22 +2924,50 @@ static int deassert_reset(struct target *target) | |||
riscv_scan_set_delay(&info->learned_delays, RISCV_DELAY_BASE, | |||
orig_base_delay); | |||
|
|||
/* Ack reset and clear DM_DMCONTROL_HALTREQ if previously set */ | |||
/* Ack reset with DM_DMCONTROL_HALTREQ for those MCUs which need a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that the original comment should stay as is, and the phrase about
period of time to be halted after reset is released
should be moved to the wait loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm okay to move "wait for a period of time" into while, but to keep "clear DM_DMCONTROL_HALTREQ" as is perhaps not aligned with current code, as that bit will be cleared later. how about change the line of 2927 as following:
/* Act reset with DM_DMCONTROL_HALTREQ based on target->reset_haltreq */
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jhqian I am sorry, the wording of the comment is a little unclear. Did you mean to say this?
Please, make sure that all comments and descriptions are very clear and understandable. This helps all code readers, incl. reviewers.
/* Acknowledge the reset by writing dmcontrol.ackhavereset <- 1, and at the same time,
* keep the dmcontrol.haltreq unchanged. This is required for certain processors that
* take longer time to halt after leaving the reset. */
(Anyway, the code will probably change due to other review suggestions, and this specific comment will probably become irrelevant.)
as the file has been diverged from upstream, I made similar changes accordingly in upstream: https://review.openocd.org/c/openocd/+/8725 |
- some MCU will need certain period of time to be halted after ndmreset is issued, so in deassert_reset, it needs to make sure MCU is halted before clearing DM_DMCONTROL_HALTREQ. Change-Id: I6d7ef7b9b33aff65cb996968fb28cd62e3e1fb16 Signed-off-by: Ryan QIAN <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jhqian, thank you for the patch.
I have a couple of concerns:
-
AFAIU, this is a workaround. Please see [3.2. Reset Control] (RISC-V Debug Spec. Version 1.0.0-rc4, Revised 2024-12-05):
When a hart comes out of reset and haltreq or hasresethaltreq are set, the hart will immediately enter Debug Mode (halted state).
However, this exact change is classifyed as a bugfix in the spec (listed under [1.2.1.1. Bugfixes from 0.13 to 1.0]).
Though looking at the comments this is more of a clarification:
Clarify that harts halt out of reset if haltreq=1 riscv/riscv-debug-spec#419 (comment) -
The second loop seems excessive. Please see the comment I left.
-
If introducing the second loop is the only option, have you considered addressing the issue using
reset-deassert-post/pre
event handlers? -
@tom-van, I'd suggest first reviewing the code here since IMHO the reset procedure is much simpler in the fork.
get_field(dmstatus, DM_DMSTATUS_ALLHALTED) ? "true" : "false"); | ||
return ERROR_TIMEOUT_REACHED; | ||
} | ||
} while (!get_field(dmstatus, DM_DMSTATUS_ALLHALTED)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get the need for the second write of dmcontrol
.
Halt request should have been already generated.
Why can't this condition be merged with the condition of the first loop?
Moreover, I'm a bit concerned. What is being reported in dmstatus
so that the first loop exits? Is the selected hart "running"? If so, I believe at least a warning should be issued, so that user understands that reset halt
executes an unspecified amount of instructions before actually halting (which is, AFAIK, not the case on other RISC-V HW).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
although halt request was set, but there're HWs which need a period of time to be aware of that request after ndmreset is deasserted by ACKHAVERESET. So the second loop is to make sure selected harts are actually halted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I seem to have the same opinion as @en-sc:
Instead of creating another do-while loop, could the existing loop above be updated this way?
const riscv_reg_t expected_state =
target->reset_halt ? DM_DMSTATUS_ALLHALTED : DM_DMSTATUS_ALLRUNNING;
/* Keep waiting until both these conditions become true:
* - there is no hart hart that is marked as unavailable
* - all harts get to the expected state (halted/running)
*/
do {
/* ... */
} while (get_field(dmstatus, DM_DMSTATUS_ANYUNAVAIL) || !get_field(dmstatus, expected_state));
Regarding 3, I did try that, since I'm not sure how I can tell if "current" reset is a "reset run" or "reset halt", I can only "halt" on each reset-deassert. I believe it degrades the functionality. |
Acked.
The function Note that a pluggable event So to keep other RISC-V targets intact and not to diverge from other targets we should leave HALTREQ set on
Exactly. I have the same feeling.
Doesn't seem possible as the problem probably begins when |
The issueI believe the issue is actually with the current condition of the first loop.
How to addressI'd suggest dropping the check on |
@tom-van, so we should not actually wait to come out of reset here but instead report |
On a side note, does anyone have access to a GD32VF103 board so that the change can be tested on it? |
Sounds promising.
I would be very careful with using TARGET_UNKNOWN in this case. |
I have one. |
@@ -2924,22 +2924,50 @@ static int deassert_reset(struct target *target) | |||
riscv_scan_set_delay(&info->learned_delays, RISCV_DELAY_BASE, | |||
orig_base_delay); | |||
|
|||
/* Ack reset and clear DM_DMCONTROL_HALTREQ if previously set */ | |||
/* Ack reset with DM_DMCONTROL_HALTREQ for those MCUs which need a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jhqian I am sorry, the wording of the comment is a little unclear. Did you mean to say this?
Please, make sure that all comments and descriptions are very clear and understandable. This helps all code readers, incl. reviewers.
/* Acknowledge the reset by writing dmcontrol.ackhavereset <- 1, and at the same time,
* keep the dmcontrol.haltreq unchanged. This is required for certain processors that
* take longer time to halt after leaving the reset. */
(Anyway, the code will probably change due to other review suggestions, and this specific comment will probably become irrelevant.)
get_field(dmstatus, DM_DMSTATUS_ALLHALTED) ? "true" : "false"); | ||
return ERROR_TIMEOUT_REACHED; | ||
} | ||
} while (!get_field(dmstatus, DM_DMSTATUS_ALLHALTED)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I seem to have the same opinion as @en-sc:
Instead of creating another do-while loop, could the existing loop above be updated this way?
const riscv_reg_t expected_state =
target->reset_halt ? DM_DMSTATUS_ALLHALTED : DM_DMSTATUS_ALLRUNNING;
/* Keep waiting until both these conditions become true:
* - there is no hart hart that is marked as unavailable
* - all harts get to the expected state (halted/running)
*/
do {
/* ... */
} while (get_field(dmstatus, DM_DMSTATUS_ANYUNAVAIL) || !get_field(dmstatus, expected_state));
control = 0; | ||
control = set_field(control, DM_DMCONTROL_DMACTIVE, 1); | ||
control = set_dmcontrol_hartsel(control, info->index); | ||
result = dm_write(target, DM_DMCONTROL, control); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
t seems there's an issue related to GD32V which needs HALTREQ to be cleared after reset, otherwise "reset" command does not behave as expected.
I agree with @aap-sc:
In case target->reset_halt == false
, there is no HATLREQ set, and therefore no reason to clear it. The clearing of HATLREQ (second write to dmcontrol) should, IMO, only occur when target->reset_halt == true
.
@jhqian Is that correct understanding, or am I missing something?
Change-Id: I6d7ef7b9b33aff65cb996968fb28cd62e3e1fb16