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.
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);
}l1-blink-registers.inoBars: share of CPU time// Lesson 1, register version: the same blink, written
// directly to the port registers.
// Pin 13 is PORTB bit 5 (PB5) on the UNO R3.
void setup() {
DDRB |= (1 << DDB5); // pin 13 is an output
}
void loop() {
PORTB |= (1 << PORTB5); // pin 13 HIGH
delay(1000);
PORTB &= ~(1 << PORTB5); // pin 13 LOW
delay(1000);
}l2-read-arduino.inoBars: share of CPU time// Lesson 2, Arduino version: read the knob on A0 and
// print the reading.
void setup() {
Serial.begin(115200);
}
void loop() {
int reading = analogRead(A0); // 0 to 1023 on the UNO R3
Serial.println(reading);
delay(20);
}l3-fade-arduino.inoBars: share of CPU time// Lesson 3, Arduino version: the knob on A0 sets how
// bright the LED on pin 9 is.
void setup() {
pinMode(9, OUTPUT);
}
void loop() {
int reading = analogRead(A0); // 0 to 1023
analogWrite(9, reading / 4); // 0 to 255
}sim-chaser.inoBars: share of CPU time// Simulator: eight LEDs on pins 2 to 9, lit one at a time,
// back and forth.
const int FIRST = 2;
const int LAST = 9;
void setup() {
for (int pin = FIRST; pin <= LAST; pin++) {
pinMode(pin, OUTPUT);
}
digitalWrite(FIRST, HIGH);
}
void loop() {
for (int pin = FIRST; pin < LAST; pin++) {
step(pin, pin + 1);
}
for (int pin = LAST; pin > FIRST; pin--) {
step(pin, pin - 1);
}
}
// Moves the lit LED from one pin to the next.
void step(int from, int to) {
digitalWrite(from, LOW);
digitalWrite(to, HIGH);
delay(70);
}sim-breathe.inoBars: share of CPU time// Simulator: the LED on pin 9 breathes. Its brightness rises
// and falls smoothly, set by PWM.
void setup() {
pinMode(9, OUTPUT);
}
void loop() {
for (int i = 0; i < 256; i++) {
analogWrite(9, curve(i));
delay(8);
}
}
// A triangle wave, squared: slow near dark, quick near full.
int curve(int i) {
int t = i < 128 ? i : 255 - i; // 0 up to 127, back to 0
return (t * t) >> 6; // 0 to 252
}sim-bare.inoBars: share of CPU time// Simulator: the bare chip. This main() replaces the Arduino
// core's, so nothing is set up: every register keeps its reset
// value until the register console writes to it.
int main(void) {
for (;;) {
}
}Keeping up –
Loading the simulator…
Logic analyzer the last 4 seconds of real time, at any speed
The TX lane is drawn from each byte the USART sends (8N1, at the rate set in UBRR0): the simulator reports bytes, not the pin’s level.
Registers
| Register | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | Value |
|---|---|---|---|---|---|---|---|---|---|
| DDRB | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0x00 |
| PORTB | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0x00 |
| PINB | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0x00 |
| TCCR0B | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0x00 |
| TCNT0 | 0x00 | ||||||||
| TIMSK0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0x00 |
- PC
- 0x0000
- SP
- 0x08FF
- SREG
- ········
- Cycles
- 0
- Interrupts
- –
Instruction tape newest first
Serial monitor · 115200 baud
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.
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
- avr8js: an ATmega328P simulator in JavaScript: the CPU, ports, timers, ADC and USART the bench runs
- GNU Binutils: addr2line: addresses to file names and line numbers, --inlines
- ArduinoCore-avr 1.8.8, wiring.c: the TIMER0_OVF interrupt, 64 × 256 clock cycles per overflow, keeps millis()
- Microchip ATmega328P datasheet DS40002061A: phase-correct PWM frequency (p135); Timer1 modes (p141)