+all: make-simavr make-tests
-
-all: make-tests
+make-simavr:
make -C simavr && make -C examples
make-tests:
--- /dev/null
+test_atmega48_disabled_timer
+test_atmega48_enabled_timer
+test_atmega48_watchdog_test
+test_atmega644_adc_test
+test_atmega88_example
+test_atmega88_timer16
+test_atmega88_uart_echo
#
-# This makefile take each "at*" file, extracts it's part name
-# And compile it into an ELF binary.
-# It also disassemble it for debugging purposes.
+# This makefile takes each "at*" file, extracts it's part name
+# And compiles it into an ELF binary.
+# It also disassembles it for debugging purposes.
#
# Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
#
SHELL = /bin/bash
sources := $(wildcard at*.c)
+test_sources := $(wildcard test_*.c)
+simavr = ..
-all : ${sources:.c=.axf}
+IPATH += ${simavr}/include
+IPATH += ${simavr}/simavr/sim
+CFLAGS += -Wall
+TEST_SRC=${wildcard test_*.c}
+TESTS=${TEST_SRC:.c=}
+
+all: obj ${sources:.c=.axf} ${TESTS}
# if you want hex files
#all : ${sources:.c=.hex}
# if you need assembler output
include ../Makefile.common
-clean:
- rm -f *.hex *.o *.axf *.s
+# do not delete intermediate .o files after running `make run_tests'
+.SECONDARY:
+
+test_%: ${OBJ}/test_%.o ${OBJ}/tests.o ${simavr}/simavr/${OBJ}/libsimavr.so
+ gcc ${LFLAGS} -o $@ $^ ${LDFLAGS}
+
+run_tests: obj ${sources:.c=.axf} ${TESTS}
+ ./run_tests
+
+clean: clean-${OBJ}
+ rm -f ${TESTS} *.axf
ISR(TIMER0_COMPA_vect)
{
- TCCR0B = 0;
- TCNT0 = 0;
}
int main(void)
TIMSK0 |= (1 << OCIE0A); // Enable CTC interrupt
OCR0A = 0xAA; // CTC compare value
+ //TCCR0B |= (1 << CS00) | (1 << CS01); // Start timer: clk/64
+
sei(); // Enable global interrupts
// here the interupts are enabled, but the interupt
// vector should not be called
- while(1)
- sleep_mode();
+ sleep_mode();
+
+ // this should not be reached
+ cli();
+ sleep_mode();
}
--- /dev/null
+/*
+ * avrtest.c
+ *
+ * Created on: 4 Feb 2011
+ * Author: sliedes
+ * This is a very slightly modified version of atmega48_disabled_timer.c
+ * by jone.
+ */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+
+#include "avr_mcu_section.h"
+AVR_MCU(F_CPU, "atmega48");
+
+ISR(TIMER0_COMPA_vect)
+{
+}
+
+int main(void)
+{
+ // Set up timer0 - do not start yet
+ TCCR0A |= (1 << WGM01); // Configure timer 0 for CTC mode
+ TIMSK0 |= (1 << OCIE0A); // Enable CTC interrupt
+ OCR0A = 0xAA; // CTC compare value
+
+ TCCR0B |= (1 << CS00) | (1 << CS01); // Start timer: clk/64
+
+ sei(); // Enable global interrupts
+
+ // here the interupts are enabled, but the interupt
+ // vector should not be called
+ sleep_mode();
+
+ // this should not be reached
+ cli();
+ sleep_mode();
+}
uint32_t value EEMEM = 0xdeadbeef;
static int uart_putchar(char c, FILE *stream) {
- if (c == '\n')
- uart_putchar('\r', stream);
- loop_until_bit_is_set(UCSR0A, UDRE0);
- UDR0 = c;
- return 0;
+ if (c == '\n')
+ uart_putchar('\r', stream);
+ loop_until_bit_is_set(UCSR0A, UDRE0);
+ UDR0 = c;
+ return 0;
}
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
#include <avr/sleep.h>
/*
- * This demonstrate how to use the avr_mcu_section.h file
+ * This demonstrates how to use the avr_mcu_section.h file.
* The macro adds a section to the ELF file with useful
- * information for the simulator
+ * information for the simulator.
*/
#include "avr_mcu_section.h"
AVR_MCU(F_CPU, "atmega88");
// use CLK/8 prescale value, clear timer/counter on compareA match
// toggle OC2A pin too
TCCR2A = (1 << WGM21) | (1 << COM2A0);
- TCCR2B = (2 << CS20); // prescaler
- OCR2A = 63; // 64 hz
- TIMSK2 |= (1 << OCIE2A);
+ TCCR2B = (2 << CS20); // prescaler
+ OCR2A = 63; // 64 hz
+ TIMSK2 |= (1 << OCIE2A);
sei();
int count = 0;
- while (count++ < 100) {
- // we read TCNT1, which should contain some sort of incrementing value
- tcnt = TCNT1; // read it
- if (tcnt > 10000) {
- TCNT1 = 500; // reset it arbitrarily
- PORTB ^= 2; // mark it in the waveform file
- }
+ while (count++ < 100) {
+ // we read TCNT1, which should contain some sort of incrementing value
+ tcnt = TCNT1; // read it
+ if (tcnt > 10000) {
+ TCNT1 = 500; // reset it arbitrarily
+ PORTB ^= 2; // mark it in the waveform file
+ }
sleep_cpu(); // this will sleep until a new timer2 tick interrupt occurs
}
// sleeping with interrupt off is interpreted by simavr as "exit please"
AVR_MCU_SIMAVR_COMMAND(&GPIOR0);
static int uart_putchar(char c, FILE *stream) {
- if (c == '\r')
- uart_putchar('\r', stream);
- loop_until_bit_is_set(UCSR0A, UDRE0);
- UDR0 = c;
- return 0;
+ if (c == '\r')
+ uart_putchar('\r', stream);
+ loop_until_bit_is_set(UCSR0A, UDRE0);
+ UDR0 = c;
+ return 0;
}
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
--- /dev/null
+#!/bin/sh
+
+TESTS=`find . -maxdepth 1 -executable -name test_\*`
+
+num_failed=0
+num_run=0
+
+for test in $TESTS; do
+ num_run=$(($num_run+1))
+ if ! $test; then
+ echo "$test returned with exit value $?."
+ num_failed=$(($num_failed+1))
+ fi
+done
+
+echo "Tests run: $num_run Successes: $(($num_run-$num_failed)) Failures: $num_failed"
--- /dev/null
+#include "tests.h"
+
+int main(int argc, char **argv) {
+ tests_init(argc, argv);
+ switch(tests_init_and_run_test("atmega48_disabled_timer.axf", 100000000)) {
+ case LJR_CYCLE_TIMER:
+ // the cycle timer fired
+ break;
+ case LJR_SPECIAL_DEINIT:
+ // sleep with interrupts off or some other such reason
+ fail("AVR woke up from sleep while it shouldn't have (after %"
+ PRI_avr_cycle_count " cycles)", tests_cycle_count);
+ default:
+ fail("Error in test case: Should never reach this.");
+ }
+ tests_success();
+ return 0;
+}
--- /dev/null
+#include "tests.h"
+
+int main(int argc, char **argv) {
+ tests_init(argc, argv);
+ switch(tests_init_and_run_test("atmega48_enabled_timer.axf", 100000000)) {
+ case LJR_CYCLE_TIMER:
+ fail("AVR did not wake up to the enabled timer.");
+ case LJR_SPECIAL_DEINIT:
+ break;
+ default:
+ fail("Error in test case: Should never reach this.");
+ }
+ tests_success();
+ return 0;
+}
--- /dev/null
+#include "tests.h"
+
+int main(int argc, char **argv) {
+ tests_init(argc, argv);
+
+ static const char *expected =
+ "Watchdog is active\r\n"
+ "Waiting for Watchdog to kick\r\n"
+ "Watchdog kicked us!\r\n";
+
+ tests_assert_uart_receive("atmega48_watchdog_test.axf", 1000000,
+ expected, '0');
+ tests_success();
+ return 0;
+}
--- /dev/null
+#include "tests.h"
+
+int main(int argc, char **argv) {
+ tests_init(argc, argv);
+
+ static const char *expected =
+ "Read 8 ADC channels to test interrupts\r\n"
+ "All done. Now reading the 1.1V value in pooling mode\r\n"
+ "Read ADC value 0155 = 1098 mvolts -- ought to be 1098\r\n";
+ tests_assert_uart_receive("atmega644_adc_test.axf", 100000,
+ expected, '0');
+
+ tests_success();
+ return 0;
+}
--- /dev/null
+#include "tests.h"
+
+int main(int argc, char **argv) {
+ tests_init(argc, argv);
+
+ static const char *expected =
+ "Read from eeprom 0xdeadbeef -- should be 0xdeadbeef\r\n"
+ "Read from eeprom 0xcafef00d -- should be 0xcafef00d\r\n";
+ tests_assert_uart_receive("atmega88_example.axf", 100000,
+ expected, '0');
+
+ tests_success();
+ return 0;
+}
--- /dev/null
+#include "tests.h"
+
+int main(int argc, char **argv) {
+ tests_init(argc, argv);
+ enum tests_finish_reason reason =
+ tests_init_and_run_test("atmega88_timer16.axf", 10000000);
+ switch(reason) {
+ case LJR_CYCLE_TIMER:
+ fail("Test failed to finish properly; reason=%d, cycles=%"
+ PRI_avr_cycle_count, reason, tests_cycle_count);
+ break;
+ case LJR_SPECIAL_DEINIT:
+ break;
+ default:
+ fail("This should not be reached; reason=%d", reason);
+ }
+ tests_assert_cycles_between(12500000, 12500300);
+ tests_success();
+ return 0;
+}
--- /dev/null
+#include "tests.h"
+
+int main(int argc, char **argv) {
+ tests_init(argc, argv);
+
+ static const char *expected =
+ "Hey there, this should be received back\n"
+ "Received: Hey there, this should be received back\n";
+ tests_assert_uart_receive("atmega88_uart_echo.axf", 100000,
+ expected, '0');
+
+ tests_success();
+ return 0;
+}
--- /dev/null
+#include "tests.h"
+#include "sim_avr.h"
+#include "sim_elf.h"
+#include "sim_core.h"
+#include "avr_uart.h"
+#include <stdio.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+avr_cycle_count_t tests_cycle_count = 0;
+int tests_disable_stdout = 1;
+
+static char *test_name = "(uninitialized test)";
+static FILE *orig_stderr = NULL;
+static int finished = 0;
+
+static void atexit_handler(void) {
+ if (!finished)
+ _fail(NULL, 0, "Test exit without indicating success.");
+}
+
+void tests_success(void) {
+ if (orig_stderr)
+ stderr = orig_stderr;
+ fprintf(stderr, "OK: %s\n", test_name);
+ finished = 1;
+}
+
+void tests_init(int argc, char **argv) {
+ test_name = strdup(argv[0]);
+ atexit(atexit_handler);
+}
+
+static avr_cycle_count_t
+cycle_timer_longjmp_cb(struct avr_t *avr, avr_cycle_count_t when, void *param) {
+ jmp_buf *jmp = param;
+ longjmp(*jmp, LJR_CYCLE_TIMER);
+}
+
+static jmp_buf *special_deinit_jmpbuf = NULL;
+
+static void special_deinit_longjmp_cb(struct avr_t *avr) {
+ if (special_deinit_jmpbuf)
+ longjmp(*special_deinit_jmpbuf, LJR_SPECIAL_DEINIT);
+}
+
+static int my_avr_run(avr_t * avr)
+{
+ if (avr->state == cpu_Stopped)
+ return avr->state;
+
+ uint16_t new_pc = avr->pc;
+
+ if (avr->state == cpu_Running)
+ new_pc = avr_run_one(avr);
+
+ // if we just re-enabled the interrupts...
+ // double buffer the I flag, to detect that edge
+ if (avr->sreg[S_I] && !avr->i_shadow)
+ avr->pending_wait++;
+ avr->i_shadow = avr->sreg[S_I];
+
+ // run the cycle timers, get the suggested sleep time
+ // until the next timer is due
+ avr_cycle_count_t sleep = avr_cycle_timer_process(avr);
+
+ avr->pc = new_pc;
+
+ if (avr->state == cpu_Sleeping) {
+ if (!avr->sreg[S_I]) {
+ printf("simavr: sleeping with interrupts off, quitting gracefully\n");
+ avr_terminate(avr);
+ fail("Test case error: special_deinit() returned?");
+ exit(0);
+ }
+ /*
+ * try to sleep for as long as we can (?)
+ */
+ // uint32_t usec = avr_cycles_to_usec(avr, sleep);
+ // printf("sleep usec %d cycles %d\n", usec, sleep);
+ // usleep(usec);
+ avr->cycle += 1 + sleep;
+ }
+ // Interrupt servicing might change the PC too, during 'sleep'
+ if (avr->state == cpu_Running || avr->state == cpu_Sleeping)
+ avr_service_interrupts(avr);
+
+ // if we were stepping, use this state to inform remote gdb
+
+ return avr->state;
+}
+
+avr_t *tests_init_avr(const char *elfname) {
+ tests_cycle_count = 0;
+ if (tests_disable_stdout) {
+ orig_stderr = stderr;
+ fclose(stdout);
+ stderr = stdout;
+ }
+ elf_firmware_t fw;
+ if (elf_read_firmware(elfname, &fw))
+ fail("Failed to read ELF firmware \"%s\"", elfname);
+ avr_t *avr = avr_make_mcu_by_name(fw.mmcu);
+ if (!avr)
+ fail("Creating AVR failed.");
+ avr_init(avr);
+ avr_load_firmware(avr, &fw);
+ return avr;
+}
+
+int tests_run_test(avr_t *avr, unsigned long run_usec) {
+ if (!avr)
+ fail("Internal test error: avr == NULL in run_test()");
+ // register a cycle timer to fire after 100 seconds (simulation time);
+ // assert that the simulation has not finished before that.
+ jmp_buf jmp;
+ special_deinit_jmpbuf = &jmp;
+ avr->special_deinit = special_deinit_longjmp_cb;
+ avr_cycle_timer_register_usec(avr, run_usec,
+ cycle_timer_longjmp_cb, &jmp);
+ int reason = setjmp(jmp);
+ tests_cycle_count = avr->cycle;
+ if (reason == 0) {
+ // setjmp() returned directly, run avr
+ while (1)
+ my_avr_run(avr);
+ } else if (reason == 1) {
+ // returned from longjmp(); cycle timer fired
+ return reason;
+ } else if (reason == 2) {
+ // returned from special deinit, avr stopped
+ return reason;
+ }
+ fail("Error in test case: Should never reach this.");
+ return 0;
+}
+
+int tests_init_and_run_test(const char *elfname, unsigned long run_usec) {
+ avr_t *avr = tests_init_avr(elfname);
+ return tests_run_test(avr, run_usec);
+}
+
+struct output_buffer {
+ char *str;
+ int currlen;
+ int alloclen;
+ int maxlen;
+};
+
+/* static void buf_output_cb(avr_t *avr, avr_io_addr_t addr, uint8_t v, */
+/* void *param) { */
+static void buf_output_cb(struct avr_irq_t *irq, uint32_t value, void *param) {
+ struct output_buffer *buf = param;
+ if (!buf)
+ fail("Internal error: buf == NULL in buf_output_cb()");
+ if (buf->currlen > buf->alloclen-1)
+ fail("Internal error");
+ if (buf->alloclen == 0)
+ fail("Internal error");
+ if (buf->currlen == buf->alloclen-1) {
+ buf->alloclen *= 2;
+ buf->str = realloc(buf->str, buf->alloclen);
+ }
+ buf->str[buf->currlen++] = value;
+ buf->str[buf->currlen] = 0;
+}
+
+static void init_output_buffer(struct output_buffer *buf) {
+ buf->str = malloc(128);
+ buf->str[0] = 0;
+ buf->currlen = 0;
+ buf->alloclen = 128;
+ buf->maxlen = 4096;
+}
+
+void tests_assert_uart_receive(const char *elfname,
+ unsigned long run_usec,
+ const char *expected,
+ char uart) {
+ avr_t *avr = tests_init_avr(elfname);
+ struct output_buffer buf;
+ init_output_buffer(&buf);
+
+ avr_irq_register_notify(avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT),
+ buf_output_cb, &buf);
+ enum tests_finish_reason reason = tests_run_test(avr, run_usec);
+ if (reason == LJR_CYCLE_TIMER) {
+ if (strcmp(buf.str, expected) == 0) {
+ _fail(NULL, 0, "Simulation did not finish within %lu simulated usec. "
+ "UART output is correct and complete.", run_usec);
+ }
+ _fail(NULL, 0, "Simulation did not finish within %lu simulated usec. "
+ "UART output so far: \"%s\"", run_usec, buf.str);
+ }
+ if (strcmp(buf.str, expected) != 0)
+ _fail(NULL, 0, "UART outputs differ: expected \"%s\", got \"%s\"", expected, buf.str);
+}
+
+void tests_assert_cycles_at_least(unsigned long n) {
+ if (tests_cycle_count < n)
+ _fail(NULL, 0, "Program ran for too few cycles (%"
+ PRI_avr_cycle_count " < %lu)", tests_cycle_count, n);
+}
+
+void tests_assert_cycles_at_most(unsigned long n) {
+ if (tests_cycle_count > n)
+ _fail(NULL, 0, "Program ran for too many cycles (%"
+ PRI_avr_cycle_count " > %lu)", tests_cycle_count, n);
+}
+
+void tests_assert_cycles_between(unsigned long min, unsigned long max) {
+ tests_assert_cycles_at_least(min);
+ tests_assert_cycles_at_most(max);
+}
+
+void _fail(const char *filename, int linenum, const char *fmt, ...) {
+ if (orig_stderr)
+ stderr = orig_stderr;
+
+ if (filename)
+ fprintf(stderr, "%s:%d: ", filename, linenum);
+
+ fprintf(stderr, "Test ");
+ if (test_name)
+ fprintf(stderr, "%s ", test_name);
+ fprintf(stderr, "FAILED.\n");
+
+ if (filename)
+ fprintf(stderr, "%s:%d: ", filename, linenum);
+
+ va_list va;
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ putc('\n', stderr);
+
+ finished = 1;
+ _exit(1);
+}
--- /dev/null
+#ifndef __TESTS_H__
+#define __TESTS_H__
+
+#include "sim_avr.h"
+
+enum tests_finish_reason {
+ LJR_CYCLE_TIMER = 1,
+ LJR_SPECIAL_DEINIT = 2,
+ // LJR_SLEEP_WITH_INT_OFF - LJR_SPECIAL_DEINIT happens...
+};
+
+#define ATMEGA48_UDR0 0xc6
+#define ATMEGA88_UDR0 0xc6
+#define ATMEGA644_UDR0 0xc6
+
+#define fail(s, ...) _fail(__FILE__, __LINE__, s, ## __VA_ARGS__)
+
+void __attribute__ ((noreturn,format (printf, 3, 4)))
+_fail(const char *filename, int linenum, const char *fmt, ...);
+
+avr_t *tests_init_avr(const char *elfname);
+void tests_init(int argc, char **argv);
+void tests_success(void);
+
+int tests_run_test(avr_t *avr, unsigned long usec);
+int tests_init_and_run_test(const char *elfname, unsigned long run_usec);
+void tests_assert_uart_receive(const char *elfname,
+ unsigned long run_usec,
+ const char *expected, // what we should get
+ char uart);
+
+void tests_assert_cycles_at_least(unsigned long n);
+void tests_assert_cycles_at_most(unsigned long n);
+
+// the range is inclusive
+void tests_assert_cycles_between(unsigned long min, unsigned long max);
+
+extern avr_cycle_count_t tests_cycle_count;
+extern int tests_disable_stdout;
+
+#endif