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

Changing frequency doesn't change simulation speed #201

Closed
gergoerdi opened this issue Feb 17, 2017 · 4 comments
Closed

Changing frequency doesn't change simulation speed #201

gergoerdi opened this issue Feb 17, 2017 · 4 comments

Comments

@gergoerdi
Copy link

I have the following test program:

#include <avr/io.h>
#include <util/delay.h>

int main ()
{
    DDRB |= _BV(DDB5);

    for (;;)
    {
        PORTB ^= _BV(PB5);
        _delay_ms(2000);
    }
}

I compile it with

avr-gcc -c -g -O3 -w -std=c++11 -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -I.. -mmcu=atmega328p -DF_CPU=16000000L blinkPB5.c -o _build/blinkPB5.c.o -MMD -MF _build/blinkPB5.c.m
avr-gcc -Os -Wl,--gc-sections -mmcu=atmega328p -o _build/image.elf _build/blinkPB5.c.o

This results in an image.elf file that I then load into the following program that uses SimAVR:

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include "sim_avr.h"
#include "avr_ioport.h"
#include "sim_elf.h"

static void * avr_run_thread(void * closure)
{
    avr_t *avr = (avr_t*)closure;

    for (;;) {
        avr_run(avr);
    }
    return NULL;
}

void led_changed_hook(struct avr_irq_t * irq, uint32_t value, void * param)
{
    printf("led_changed_hook %d %d\n", irq->irq, value);
}

int main(int argc, char *argv[])
{
    elf_firmware_t f;
    elf_read_firmware("image.elf", &f);
    f.frequency = 16e6;

    printf("f.frequency = %d\n", (int)f.frequency);
    const char *mmcu = "atmega328p";
    avr_t * avr = avr_make_mcu_by_name(mmcu);
    if (!avr) {
        fprintf(stderr, "%s: AVR '%s' not known\n", argv[0], mmcu);
        exit(1);
    }
    avr_init(avr);
    avr_load_firmware(avr, &f);

    avr_irq_register_notify(
        avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 5),
        led_changed_hook,
        NULL);

    pthread_t run;
    pthread_create(&run, NULL, avr_run_thread, avr);

    for (;;) {}
}

Problems:

  • The message triggered by PB5 is printed to the terminal at about 4x the expected speed of once per 2 seconds
  • Changing f.frequency doesn't change the speed at which the message is printed
@buserror
Copy link
Owner

It's not supposed to. simavr doesn't /try/ to run at simulated speed /unless/ you use a timer and sleep() the CPU. Otherwise, it has no way of really 'slowing down' the instruction flow compared to the host.

if you use a timer interrupt, and sleep() the CPU in your main(), then simavr knows it can try to sync itself to that speed and will 'sleep' the simulation accordingly. It's not perfect, and is not 'smooth' (the avr code will STILL run at full speed when not in sleep() mode; but it allows rather good approximation.

@buserror
Copy link
Owner

node that your _delay_ms(1000) is not /really/ a delay at all, it's a spinloop, simavr has no idea it's spinlooping for no reasons.

@gergoerdi
Copy link
Author

OK, so I've changed my AVR code to use a timer interrupt instead:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int main ()
{
    DDRB |= _BV(DDB5);

    TCCR1A = 0;
    TCCR1B = 0;
    TCNT1  = 0;
    TIMSK1 |= (1 << OCIE1A);

    sei();

    /* Set TIMER1 to 1Hz */
    TCCR1B |= (1 << WGM12);
    OCR1A   = 15624;
    TCCR1B |= ((1 << CS12) | (1 << CS10));

    for (;;);
}

ISR(TIMER1_COMPA_vect){
    PORTB ^= _BV(PB5);
}

Then I changed the main loop to sleep instead of busy-waiting:

#include <avr/power.h>
#include <avr/sleep.h>

    set_sleep_mode(SLEEP_MODE_IDLE);
    sleep_enable();
    for (;;)
    {
        sleep_mode();
    }

And now I'm getting nice 1Hz blinking.

Thanks!

@buserror
Copy link
Owner

Well done, it's actually a nice test case, would you care to convert it to a wiki page? That methos is also vastly better way of using the AVR anyway, as it'll save power when sleeping.

You can also use the asynchronous clock of the avr by using an external 32.7k crystal and use even less power as you will be able to use the deep sleep mode as well. I've made projects using picoamps using that system!

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

No branches or pull requests

2 participants