Skip to content
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

Terminate the simulation on exit() from firmware #380

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

edgar-bonet
Copy link

The firmware can end the simulation by sleeping with interrupts disabled. While this way of hanging the CPU makes a lot of sense, it is not the most intuitive.

The standard way to terminate a C program is by either calling exit() or returning from main(). It turns out the AVR C runtime supports stopping the firmware in both these standard ways. Returning from main() jumps to exit(), which disables interrupts and hangs the CPU in an infinite loop. The implementation is roughly like this:

; after the C runtime initializations:
.section .init9
    call main  ; run the firmware
    jmp exit   ; if main() returns, go to exit()

exit:
_exit:
    cli        ; disable interrupts
    rjmp .-2   ; hang the CPU

This pull request makes run_avr gracefully quit if the firmware executes the CPU-hanging instruction rjmp .-2 with interrupts disabled.

The actual condition used in the code is:

if (avr->state == cpu_Running && new_pc == avr->pc
    && avr->flash[new_pc] == 0xff && avr->flash[new_pc+1] == 0xcf
    && !avr->sreg[S_I])

Some of those tests may be redundant but, as I am not sure to understand all the subtleties of the simulation, I opted for defensive programming.

The feature has been tested with the following program:

#include <stdlib.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include "avr_mcu_section.h"

AVR_MCU(F_CPU, "atmega328p");
AVR_MCU_SIMAVR_CONSOLE(&GPIOR0);

int main(void)
{
    const char *s = "Exiting.\r\n";
    for (const char *t = s; *t; t++)
        GPIOR0 = *t;
#if defined(QUIT_BY_SLEEP)
    sleep_mode();
#elif defined(QUIT_BY_EXIT)
    exit(0);
#endif
}

When the firmware calls exit() or returns from main(), it ends up
running _exit(), which disables interrupts and hangs the CPU in an
infinite, empty loop. run_avr now terminates the simulation if the
CPU-hanging instruction (rjmp .-2) is executed with interrupts disabled.
@edgar-bonet
Copy link
Author

I checked that, with this pull request, abort() is also a valid way of terminating the simulation. According to the avr-libc documentation, abort() is implemented as:

cli();
_exit(1);

@kittennbfive
Copy link
Contributor

+1 for this PR, i wondered why cli();while(1); does not stop the simulation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants