Simulate two AVR running simultaneously with correct timing?
kittennbfive opened this issue · 5 comments
Hi,
is there a way to simulate 2 AVR running "synchronously"? By this i mean that the time the code on each AVR is running is the same at any point in time.
Let me try to clarify with this simple example:
#include <avr/io.h>
#include <util/delay.h>
#define BAUD 9600
#include <util/setbaud.h>
void uart_putchar(char c)
{
while(!(UCSR0A&(1<<UDRE0)));
UDR0=c;
}
int main(void)
{
UBRR0=UBRR_VALUE;
UCSR0B|=(1<<TXEN0);
char c='a';
while(1)
{
uart_putchar(c++);
uart_putchar('\n');
_delay_ms(1000);
}
return 0;
}
avr-gcc -Wall -Wextra -O3 -std=c99 -mmcu=atmega328p -o avr_fast.elf -DF_CPU=18432000UL main.c
avr-gcc -Wall -Wextra -O3 -std=c99 -mmcu=atmega328p -o avr_slow.elf -DF_CPU=8192000UL main.c
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <err.h>
#include <signal.h>
#include "sim_avr.h"
#include "sim_elf.h"
volatile bool run=true;
static void sigint_handler(int sig)
{
(void)sig;
run=false;
}
int main(void)
{
signal(SIGINT, &sigint_handler);
avr_t *avr1, *avr2;
elf_firmware_t firmware1, firmware2;
if(elf_read_firmware("avr_fast.elf", &firmware1))
{
printf("elf_read_firmware avr_fast.elf failed\n");
return 1;
}
avr1 = avr_make_mcu_by_name("atmega1284p");
if (!avr1) {
printf("avr_make_mcu_by_name avr_fast failed\n");
return 1;
}
if(elf_read_firmware("avr_slow.elf", &firmware2))
{
printf("elf_read_firmware avr_slow.elf failed\n");
return 1;
}
avr2 = avr_make_mcu_by_name("atmega1284p");
if (!avr2) {
printf("avr_make_mcu_by_name avr_slow failed\n");
return 1;
}
avr_init(avr1);
avr_init(avr2);
avr1->frequency=18432000;
avr2->frequency=8192000;
avr_load_firmware(avr1, &firmware1);
avr_load_firmware(avr2, &firmware2);
int state1, state2;
do
{
state1=avr_run(avr1);
state2=avr_run(avr2);
} while(state1!=cpu_Done && state1!=cpu_Crashed && state2!=cpu_Done && state2!=cpu_Crashed && run);
avr_terminate(avr1);
avr_terminate(avr2);
return 0;
}
Notice that both AVR have different frequencies! As these frequencies are correctly passed to avr-gcc, _delay_ms(1000)
should exactly wait 1s on each AVR and the difference in execution speed of uart_putchar
should be negligible for a short period of time.
I get an (simulated) UART-output like
a.
a.
b.
c.
b.
d.
e.
c.
f.
g.
d.
h.
i.
j.
e.
k.
l.
f.
m.
I would like to get what i would get with 2 real AVR started at the same point in time, ie
a [from avr1]
a [from avr2]
b [from avr1]
b [from avr2]
...
Is there a way to achieve this? Is my request actually comprehensible?
I need this because i want to simulate a communication between both AVR and some simulated external device (code to be written).
Thank you!
The avr_run() function executes only one instruction per call, to simulate different frequencies you must call the functions with different frequencies. By modifying the code as below you can implement your idea.
int ratio=0;
do
{
state1=avr_run(avr1); //fast
if(ratio > 3)
{
ratio=0;
state2=avr_run(avr2);// slow 4x
}
ratio++;
} while(state1!=cpu_Done && state1!=cpu_Crashed && state2!=cpu_Done && state2!=cpu_Crashed && run);
The frequency passed to the avr object only serves to calculate the sleep time needed for the code to run approximately with real time (clock wall). After calling the avr_run() function several times, the time the instructions took to execute is calculated and sleep is added so that the time matches the chosen execution frequency. If the execution takes longer than specified, no sleep is added and the code runs slower than expected, this depends on the capacity of your computer's processor.
Oh, silly me... I apologize. :-( Of course it's one instruction per call, so with different frequencies... Thank you, i will use something like the code snippet you suggested. However you say one instruction per call so sometimes avr_run() will execute 2 or more AVR-clock cycles? In this case there will still be a small error accumulating over time if the two AVR don't run the same code?
(Also i will need something like a fractional divider if freq1/freq2 is not an integer, but i should be able to work this out.)
Usually the instructions are one or two cycles, you can use something like the code below to get time precision (This is what I use in my PICSimLab simulator, which uses simavr)
if (twostep){
twostep = 0; //NOP
}
else{
cycle_start = avr->cycle;
avr_run (avr);
if ((avr->cycle - cycle_start) > 1){
twostep = 1;
}
}
As it is not possible to split the instructions, a solution would be to vary the ratio between the frequencies. For example a ratio of 2.5 can be implemented using the sequence of ratios 2, 3, 2, 3,... While the fast one executes 2+3 = 5 times the slow one will executes 2 times .
@lcgamboa wrote:
a solution would be to vary the ratio between the frequencies [...]
If the frequency ratio is a rational number, a simple option is to count time in units of the greatest common divisor of the two clock periods. For example, for a ratio of 5/2:
const int avr1_time_step = 5;
const int avr2_time_step = 2;
if (avr1_time_step * avr1->cycle < avr2_time_step * avr2->cycle)
avr_run(avr1);
else
avr_run(avr2);
Thank you very much, the code-snippet from edgar-bonet works perfect. :-)
Issue solved, closing.