Simulator

The bench

A simulated UNO R3 running programs compiled for it. Watch the chip work through each line of the sketch, slow time down to single instructions, or write its registers yourself.

Program

Lesson 1: pin 13 on for a second, off for a second, with digitalWrite and delay.

l1-blink-arduino.inoBars: share of CPU time
// Lesson 1, Arduino version.
// Blinks the UNO R3's LED on pin 13: 1 s on, 1 s off.
void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  delay(1000);
}
Speed

Keeping up –

Loading the simulator…

Logic analyzer the last 4 seconds of real time, at any speed

Registers

Registerbit 7bit 6bit 5bit 4bit 3bit 2bit 1bit 0Value
DDRB000000000x00
PORTB000000000x00
PINB000000000x00
TCCR0B000000000x00
TCNT00x00
TIMSK0000000000x00
PC
0x0000
SP
0x08FF
SREG
········
Cycles
0
Interrupts
–

Instruction tape newest first

    Register console

    Write the chip’s registers yourself. Each line is applied to the simulated chip as a write; it is not compiled. It works best on the Bare chip, where no program writes over it.

    Ctrl + Enter runs it

    How it works

    Each program here was compiled for the UNO R3 by arduino-⁠cli when the site was built. The chip is avr8js, a simulator of the ATmega328P, running in a background thread so the page can draw while it runs1.

    The build also lists every machine instruction with the source line it came from, using avr-⁠objdump and avr-addr2line on each compiled file. Code the compiler moved into your sketch, such as analogRead inside loop, still counts as your line2. As the chip runs, each instruction is looked up to light its line, add its cycles to the bars, and fill the instruction tape.

    The interrupts lane marks each interrupt the chip takes. Most are TIMER0_OVF: Arduino’s startup code runs timer0 at 64 clock cycles a step, so it overflows every 256 steps, and its handler counts that time for millis() and delay()3. Serial output adds the USART’s own interrupts. The Bare chip turns no interrupts on, so its lane stays empty.

    At 1× a PWM output looks steadily dim, because each frame averages many periods. Timer1’s 8-⁠bit phase-⁠correct PWM runs at 490.2 Hz4, so at 1/1000 one period takes about two seconds on screen, and you can watch the LED switch.

    The register console is not a compiler. The page performs each write on the simulated chip’s memory at the simulated time you ask for, and the simulated ports, timers and ADC respond to it as they would to the same write from a program.

    Sources

    1. avr8js: an ATmega328P simulator in JavaScript: the CPU, ports, timers, ADC and USART the bench runs
    2. GNU Binutils: addr2line: addresses to file names and line numbers, --inlines
    3. ArduinoCore-⁠avr 1.8.8, wiring.c: the TIMER0_OVF interrupt, 64 × 256 clock cycles per overflow, keeps millis()
    4. Microchip ATmega328P datasheet DS40002061A: phase-⁠correct PWM frequency (p135); Timer1 modes (p141)