Implement the BIN and IPR bits for attinyX5 and change the ADC sample timing
authorga <ga@oldell.fish>
Thu, 21 Jan 2021 15:30:19 +0000 (15:30 +0000)
committerga <ga@oldell.fish>
Thu, 21 Jan 2021 15:30:19 +0000 (15:30 +0000)
to roughly match the datasheet.  Add a test.  Those MCUs have no UART, so
test.c and test.h have changes to use an alternative method.

simavr/cores/sim_tinyx5.h
simavr/sim/avr_adc.c
simavr/sim/avr_adc.h
tests/attiny85_adc_test.c [new file with mode: 0644]
tests/test_attiny85_adc_test.c [new file with mode: 0644]
tests/tests.c
tests/tests.h

index ca7ef25..52f4bc7 100644 (file)
@@ -135,7 +135,7 @@ const struct mcu_t SIM_CORENAME = {
                },
                
                .bin = AVR_IO_REGBIT(ADCSRB, BIN),
-               .ipr = AVR_IO_REGBIT(ADCSRA, IPR),
+               .ipr = AVR_IO_REGBIT(ADCSRB, IPR),
 
                .muxmode = {
                        [0] = AVR_ADC_SINGLE(0), [1] = AVR_ADC_SINGLE(1),
index 51ab1ca..611cdd5 100644 (file)
@@ -34,30 +34,38 @@ avr_adc_int_raise(
                // if the interrupts are not used, still raised the UDRE and TXC flag
                avr_raise_interrupt(avr, &p->adc);
                avr_regbit_clear(avr, p->adsc);
-               p->first = 0;
-               p->read_status = 0;
                if( p->adts_mode == avr_adts_free_running )
                        avr_raise_irq(p->io.irq + ADC_IRQ_IN_TRIGGER, 1);
+                if (!p->read_status) {
+                    /* Update I/O registers. */
+
+                    avr->data[p->r_adcl] = p->result & 0xff;
+                    avr->data[p->r_adch] = p->result >> 8;
+                }
        }
        return 0;
 }
 
-static uint8_t
-avr_adc_read_l(
-               struct avr_t * avr, avr_io_addr_t addr, void * param)
+static avr_cycle_count_t
+avr_adc_convert(struct avr_t * avr, avr_cycle_count_t when, void * param)
 {
-       avr_adc_t * p = (avr_adc_t *)param;
-       if (p->read_status)     // conversion already done
-               return avr_core_watch_read(avr, addr);
+        avr_adc_t *p = (avr_adc_t *)param;
+
+        p->first = 0; // Converter initialised
+
+        /* Ask the calling program for inputs. */
+
+       avr_adc_mux_t mux = p->muxmode[p->current_muxi];
+        union {
+                avr_adc_mux_t mux;
+                uint32_t v;
+        } e = { .mux = mux };
+        avr_raise_irq(p->io.irq + ADC_IRQ_OUT_TRIGGER, e.v);
 
-       uint8_t refi = avr_regbit_get_array(avr, p->ref, ARRAY_SIZE(p->ref));
-       uint16_t ref = p->ref_values[refi];
-       uint8_t muxi = avr_regbit_get_array(avr, p->mux, ARRAY_SIZE(p->mux));
-       avr_adc_mux_t mux = p->muxmode[muxi];
        // optional shift left/right
-       uint8_t shift = avr_regbit_get(avr, p->adlar) ? 6 : 0; // shift LEFT
+       uint8_t shift = p->current_extras.adjust ? 6 : 0; // shift LEFT
 
-       uint32_t reg = 0;
+       int32_t reg = 0, clipped = 0;
        switch (mux.kind) {
                case ADC_MUX_SINGLE:
                        reg = p->adc_values[mux.src];
@@ -81,7 +89,10 @@ avr_adc_read_l(
                                reg = avr->vcc / 4;
                        break;
        }
-       uint32_t vref = 3300;
+
+       int32_t vref = 3300;
+       uint16_t ref = p->ref_values[p->current_refi];
+
        switch (ref) {
                case ADC_VREF_VCC:
                        if (!avr->vcc)
@@ -107,18 +118,44 @@ avr_adc_read_l(
 //     printf("ADCL %d:%3d:%3d read %4d vref %d:%d=%d\n",
 //                     mux.kind, mux.diff, mux.src,
 //                     reg, refi, ref, vref);
-       reg = (reg * 0x3ff) / vref;     // scale to 10 bits ADC
-//     printf("ADC to 10 bits 0x%x %d\n", reg, reg);
-       if (reg > 0x3ff) {
-               AVR_LOG(avr, LOG_WARNING, "ADC: channel %d clipped %u/%u VREF %d\n", mux.kind, reg, 0x3ff, vref);
-               reg = 0x3ff;
+
+        if (mux.kind == ADC_MUX_DIFF) {
+                if (p->current_extras.negate)
+                        reg = -reg;
+                if (p->current_extras.bipolar) {
+                        reg = (reg * (int32_t)0x1ff) / vref; // scale to 9 bits
+                        if (reg > (int32_t)0x1ff) {
+                                clipped = 0x1ff;
+                        } else if (reg < -(int32_t)0x1ff) {
+                                clipped = 0x200;
+                        }
+                } else {
+                        reg = (reg * (int32_t)0x3ff) / vref; // scale to 10 bit
+                        if (reg < 0 || reg > (int32_t)0x3ff)
+                                clipped = 0x1ff;
+                }
+        } else {
+            reg = (reg * (int32_t)0x3ff) / vref;       // scale to 10 bits
+            if (reg < 0 || reg > (int32_t)0x3ff)
+                clipped = 0x3ff;
+        }
+//     printf("ADC to 9/10 bits 0x%x %d\n", reg, reg);
+       if (clipped) {
+               AVR_LOG(avr, LOG_WARNING,
+                        "ADC: channel %d clipped %u/%u VREF %d\n",
+                        p->current_muxi, reg, clipped, vref);
+               reg = clipped;
        }
+        reg &= 0x3ff;
        reg <<= shift;
-//     printf("ADC to 10 bits %x shifted %d\n", reg, shift);
-       avr->data[p->r_adcl] = reg;
-       avr->data[p->r_adch] = reg >> 8;
-       p->read_status = 1;
-       return avr_core_watch_read(avr, addr);
+//     printf("ADC to 9/10 bits %x shifted %d\n", reg, shift);
+        p->result = reg;
+
+        /* Schedule the interrupt in 11 ADC cycles. */
+
+        avr_cycle_timer_register(avr, p->current_prescale * 11,
+                                 avr_adc_int_raise, p);
+        return 0;
 }
 
 /*
@@ -127,25 +164,26 @@ avr_adc_read_l(
  * Consequently, if the result is left adjusted and no more than 8-bit
  * precision is required, it is sufficient to read ADCH.
  * Otherwise, ADCL must be read first, then ADCH."
- * So here if the H is read before the L, we still call the L to update the
- * register value.
  */
+
+static uint8_t
+avr_adc_read_l(
+               struct avr_t * avr, avr_io_addr_t addr, void * param)
+{
+       avr_adc_t * p = (avr_adc_t *)param;
+
+       p->read_status = 1; // Set the update interlock.
+       return avr_core_watch_read(avr, addr);
+}
+
 static uint8_t
 avr_adc_read_h(
                struct avr_t * avr, avr_io_addr_t addr, void * param)
 {
        avr_adc_t * p = (avr_adc_t *)param;
-       // no "break" here on purpose
-       switch (p->read_status) {
-               case 0:
-                       avr_adc_read_l(avr, p->r_adcl, param);
-                       FALLTHROUGH
-               case 1:
-                       p->read_status = 2;
-                       FALLTHROUGH
-               default:
-                       return avr_core_watch_read(avr, addr);
-       }
+
+        p->read_status = 0; // Clear the update interlock.
+        return avr_core_watch_read(avr, addr);
 }
 
 static void
@@ -211,47 +249,60 @@ avr_adc_write_adcsra(
        avr_adc_t * p = (avr_adc_t *)param;
        uint8_t adsc = avr_regbit_get(avr, p->adsc);
        uint8_t aden = avr_regbit_get(avr, p->aden);
+       uint8_t new_aden;
 
        avr->data[p->adsc.reg] = v;
+        new_aden = avr_regbit_get(avr, p->aden);
 
        // can't write zero to adsc
        if (adsc && !avr_regbit_get(avr, p->adsc)) {
                avr_regbit_set(avr, p->adsc);
                v = avr->data[p->adsc.reg];
        }
-       if (!aden && avr_regbit_get(avr, p->aden)) {
+       if (!aden && new_aden) {
                // first conversion
                p->first = 1;
                AVR_LOG(avr, LOG_TRACE, "ADC: Start AREF %d AVCC %d\n", avr->aref, avr->avcc);
        }
        if (aden && !avr_regbit_get(avr, p->aden)) {
                // stop ADC
+
+                avr_cycle_timer_cancel(avr, avr_adc_convert, p);
                avr_cycle_timer_cancel(avr, avr_adc_int_raise, p);
                avr_regbit_clear(avr, p->adsc);
                v = avr->data[p->adsc.reg];     // Peter Ross pross@xvid.org
        }
-       if (!adsc && avr_regbit_get(avr, p->adsc)) {
+       if (new_aden && !adsc && avr_regbit_get(avr, p->adsc)) {
                // start one!
-               uint8_t muxi = avr_regbit_get_array(avr, p->mux, ARRAY_SIZE(p->mux));
-               union {
-                       avr_adc_mux_t mux;
-                       uint32_t v;
-               } e = { .mux = p->muxmode[muxi] };
-               avr_raise_irq(p->io.irq + ADC_IRQ_OUT_TRIGGER, e.v);
+
+                /* Copy mux, prescaler and ADSRB settings, as they may change
+                 * before conversion.
+                 */
+
+               p->current_muxi = avr_regbit_get_array(avr, p->mux,
+                                                       ARRAY_SIZE(p->mux));
+                p->current_refi = avr_regbit_get_array(avr, p->ref,
+                                                       ARRAY_SIZE(p->ref));
 
                // clock prescaler are just a bit shift.. and 0 means 1
-               uint32_t div = avr_regbit_get_array(avr, p->adps, ARRAY_SIZE(p->adps));
+
+               uint32_t div = avr_regbit_get_array(avr, p->adps,
+                                                    ARRAY_SIZE(p->adps));
                if (!div) div++;
 
-               div = avr->frequency >> div;
                if (p->first)
-                       AVR_LOG(avr, LOG_TRACE, "ADC: starting at %uKHz\n", div / 13 / 100);
-               div /= p->first ? 25 : 13;      // first cycle is longer
-
-               avr_cycle_timer_register(avr,
-                               avr_hz_to_cycles(avr, div),
-                               avr_adc_int_raise, p);
-       }
+                       AVR_LOG(avr, LOG_TRACE, "ADC: starting at %uKHz\n",
+                                (avr->frequency >> div) / 13 / 100);
+               div = (1 << div);
+               div *= (p->first ? 14 : 2);     // first conversion is longer
+                p->current_prescale = div;
+                avr_cycle_timer_register(avr, div, avr_adc_convert, p);
+                p->current_extras.bipolar =
+                        p->bin.reg && avr_regbit_get(avr, p->bin);
+                p->current_extras.negate =
+                        p->ipr.reg && avr_regbit_get(avr, p->ipr);
+                p->current_extras.adjust = avr_regbit_get(avr, p->adlar);
+        }
        avr_core_watch_write(avr, addr, v);
        avr_adc_configure_trigger(avr, addr, v, param);
 }
index f2e6eda..1bfee9e 100644 (file)
@@ -131,14 +131,31 @@ typedef struct avr_adc_t {
        // use ADIF and ADIE bits
        avr_int_vector_t adc;
 
-       /*
+       avr_adc_mux_t   muxmode[64];    // maximum 6 bits of mux modes
+
+        /*
         * runtime bits
         */
-       avr_adc_mux_t   muxmode[64];// maximum 6 bits of mux modes
+
        uint16_t                adc_values[16]; // current values on the ADCs
        uint16_t                temp;           // temp sensor reading
        uint8_t                 first;
        uint8_t                 read_status;    // marked one when adcl is read
+
+        /* Conversion parameters saved at start (ADSC is set). */
+
+        uint8_t                 current_muxi;
+        uint8_t                 current_refi;
+        uint8_t                 current_prescale;
+        struct {
+                unsigned int bipolar : 1;      // BIN bit.
+                unsigned int negate : 1;       // IPR bit.
+                unsigned int adjust : 1;       // ADLAR bit.
+        }                       current_extras;
+
+        /* Buffered conversion result. */
+
+        uint16_t                result;
 } avr_adc_t;
 
 void avr_adc_init(avr_t * avr, avr_adc_t * port);
diff --git a/tests/attiny85_adc_test.c b/tests/attiny85_adc_test.c
new file mode 100644 (file)
index 0000000..044ddc4
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+       attiny85_adc_test.c
+
+       Copyright 2021 Giles Atkinson
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <avr/io.h>
+#include <stdio.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+#include <util/delay.h>
+#include <avr/cpufunc.h>
+#include "avr_mcu_section.h"
+
+AVR_MCU(F_CPU, "attiny85");
+AVR_MCU_VOLTAGES(5000, 5000, 3000) // VCC, AVCC, VREF - millivolts.
+
+/* No UART in tiny85, so simply write to unimplemented register ISIDR. */
+
+static int uart_putchar(char c, FILE *stream) {
+  if (c == '\n')
+    uart_putchar('\r', stream);
+  USIDR = c;
+  return 0;
+}
+
+static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
+                                         _FDEV_SETUP_WRITE);
+
+/* Table of values for ADMUX and ADSRB. */
+
+static struct params {
+    uint8_t mux, srb;
+} params[] = {
+    {0x90, 0 },    // 2.56V ref, input ADC0
+    {0x81, 0 },    // 1.10V ref, input ADC1
+    {0x82, 0 },    // 1.10V ref, input ADC2, overflow
+    {0x83, 0 },    // 1.10V ref, input ADC3, zero
+
+    {0x88, 0},     // 1.10V ref, ADC0/ADC0 differential
+    {0xa1, 0},     // 1.10V ref, ADC1, left adjusted
+    {0x96, 0},     // 2.56V ref, ADC2/ADC3 differential. signed
+    {0x97, 0x80},  // 2.56V ref, ADC2/ADC3 differential, signed, X20
+
+    {0x8B, 0},     // 1.10V ref, ADC0/ADC1 differential X20
+    {0x81, 0xa0},  // 1.10V ref, input ADC1, BIN and IPR on.
+    {0x96, 0x80},  // 2.56V ref, ADC2/ADC3 differential, signed
+    {0x96, 0x20},  // 2.56V ref, ADC2/ADC3 differential, IPR
+
+    {0x9a, 0x80},  // 2.56V ref, ADC0/ADC1 differential, signed +ve overflow
+    {0x9a, 0x80},  // 2.56V ref, ADC0/ADC1 differential, signed, positive
+    {0x86, 0x80},  // 1.10V ref, ADC2/ADC3 differential, signed, -ve overflow
+    {0x13, 0 },    // 3.00 V external ref, input ADC3
+};
+
+#define NUM_SUBTESTS (sizeof params / sizeof params[0])
+
+static int index_i, index_o;
+static uint16_t int_results[NUM_SUBTESTS + 2];
+
+ISR(ADC_vect)
+{
+    /* Write the next ADCMUX/ADSRB settings. */
+
+    if (index_i >= NUM_SUBTESTS) {
+        ADCSRA &= ~(1 << ADATE);
+        return;
+    }
+    ADMUX = params[index_i].mux;
+    ADCSRB = params[index_i].srb;
+    index_i++;
+}
+
+int main(void)
+{
+    int i;
+
+    stdout = &mystdout;
+    printf("ADC");
+
+    /* Turn on the ADC. */
+
+    ADCSRA = (1 << ADEN) + 5;      // Enable, clock scale = 32
+
+    /* Do conversions. */
+
+    for (i = 0; i < NUM_SUBTESTS; ++i) {
+        ADMUX = params[i].mux;
+        ADCSRB = params[i].srb;
+        ADCSRA = (1 << ADEN) + (1 << ADSC) + 5;
+        while ((ADCSRA & (1 << ADIF)) == 0)
+            ;
+        printf(" %d", ADC);
+    }
+    uart_putchar('\n', stdout);
+
+    /* Do it again with interrupts. printf() is too slow to send the
+     * results in real time, even with maximum pre-scaler ratio.
+     */
+
+    sei();
+    ADCSRA = (1 << ADEN) + (1 << ADSC) + (1 << ADATE) + (1 << ADIE) + 4;
+
+    while (index_o < NUM_SUBTESTS + 2) {
+        sleep_cpu();
+        int_results[index_o++] = ADC;
+    }
+    for (i = 0; i < NUM_SUBTESTS + 2; ++i)
+        printf(" %d", int_results[i]);
+    cli();
+    sleep_cpu();
+}
diff --git a/tests/test_attiny85_adc_test.c b/tests/test_attiny85_adc_test.c
new file mode 100644 (file)
index 0000000..60b073c
--- /dev/null
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "tests.h"
+#include "avr_adc.h"
+
+/* Start of the ADC's IRQ list. */
+
+static avr_irq_t *base_irq;
+
+/* Table of voltages to apply to each input in turn. */
+
+static uint32_t volts[] = {
+                           1283, // 2.56V Ref. just over half full scale
+                           990,  // 1.1 V ref 0.9 FS
+                           1200, //       over-voltage
+                           0,    //       zero
+
+                           1040, //       ADC0/ADC0 differential
+                           386,  //       ADC1, left adjust
+                           100,  // 2.56V ADC2/ADC3 differential
+                           210,  // 2.56V ADC2/ADC3 differential, signed X20
+
+                           400,  // 1.10V ref, ADC0/ADC1 differential X20
+                           2,    // 1.10V ref, ADC1
+                           100,  // 2.56V ref, ADC2/ADC3 differential, signed
+                           2000, // 2.56V ref, ADC2/3 diff, signed, IPR
+
+                           3000, // 2.56V ref, ADC0/1 diff, signed, overflow
+                           2215, // 2.56V ref, ADC0/1 diff, signed
+                           700,  // 1.10V ref, ADC2/3 diff, signed -ve overflow
+                           2222, // AREF (3V)  ADC3
+};
+
+static unsigned int index;
+
+/* Callback for A-D conversion sampling. */
+
+static void conversion(struct avr_irq_t *irq, uint32_t value, void *param)
+{
+    int i;
+    union {
+        avr_adc_mux_t request;
+        uint32_t      v;
+    }   u = { .v = value };
+
+    if (index >= ARRAY_SIZE(volts)) {
+        /* This happens when starting with interrupts.
+         * Do the sub-tests again after repeating the last one twice,
+         * once for the initial conversion and once again for the conversion
+         * that has already started at the time of the first interrupt.
+         * That agrees with the data sheet.
+         */
+
+        if (index == ARRAY_SIZE(volts))
+            index++;
+        else
+            index = 0;
+        return;
+    }
+
+    i = index & 3;
+    if (i != u.request.src &&
+        !(u.request.src + 1 == i && u.request.diff == i)) {
+        /* Requested input not expected. */
+
+        fail("Simulator requested input %d, but %d expected, index %d\n",
+             u.request.src, i, index);
+    }
+
+    avr_raise_irq(base_irq + i, volts[index]);
+    index++;
+}
+
+int main(int argc, char **argv) {
+       static const char *expected = "ADC 512 920 1023 0"
+                                           " 0 22912 39 585"
+                                           " 260 1 1003 759"
+                                           " 511 156 512 757\r\n"
+                                           " 757 757"
+                                           " 512 920 1023 0"
+                                           " 0 22912 39 585"
+                                           " 260 1 1003 759"
+                                           " 511 156 512 757";
+       avr_t             *avr;
+
+       tests_init(argc, argv);
+        avr = tests_init_avr("attiny85_adc_test.axf");
+
+        /* Request callback when a value is sampled for conversion. */
+
+        base_irq = avr_io_getirq(avr, AVR_IOCTL_ADC_GETIRQ, 0);
+        avr_irq_register_notify(base_irq + ADC_IRQ_OUT_TRIGGER,
+                                conversion, NULL);
+
+        /* Run program and check results. */
+
+        tests_assert_register_receive_avr(avr, 100000, expected,
+                                          (avr_io_addr_t)0x2f /* &USIDR */);
+       tests_success();
+       return 0;
+}
index 8161eba..7f83657 100644 (file)
@@ -153,8 +153,8 @@ struct output_buffer {
        int maxlen;
 };
 
-/* static void buf_output_cb(avr_t *avr, avr_io_addr_t addr, uint8_t v, */
-/*                       void *param) { */
+/* Callback for receiving data via an IRQ. */
+
 static void buf_output_cb(struct avr_irq_t *irq, uint32_t value, void *param) {
        struct output_buffer *buf = param;
        if (!buf)
@@ -171,6 +171,16 @@ static void buf_output_cb(struct avr_irq_t *irq, uint32_t value, void *param) {
        buf->str[buf->currlen] = 0;
 }
 
+/* Callback for receiving data directly from a register,
+ * after calling avr_register_io_write().
+ */
+
+static void reg_output_cb(struct avr_t *avr, avr_io_addr_t addr,
+                          uint8_t v, void *param)
+{
+    buf_output_cb(NULL, v, param);
+}
+
 static void init_output_buffer(struct output_buffer *buf) {
        buf->str = malloc(128);
        buf->str[0] = 0;
@@ -179,6 +189,25 @@ static void init_output_buffer(struct output_buffer *buf) {
        buf->maxlen = 4096;
 }
 
+static void tests_assert_xxxx_receive_avr(avr_t                *avr,
+                                          unsigned long         run_usec,
+                                          struct output_buffer *buf,
+                                          const char           *expected)
+{
+       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. "
+                            "Output is correct and complete.", run_usec);
+               }
+               _fail(NULL, 0, "Simulation did not finish within %lu simulated usec. "
+                    "Output so far: \"%s\"", run_usec, buf->str);
+       }
+       if (strcmp(buf->str, expected) != 0)
+               _fail(NULL, 0, "Outputs differ: expected \"%s\", got \"%s\"", expected, buf->str);
+}
+
 void tests_assert_uart_receive_avr(avr_t *avr,
                               unsigned long run_usec,
                               const char *expected,
@@ -186,19 +215,10 @@ void tests_assert_uart_receive_avr(avr_t *avr,
        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);
+       avr_irq_register_notify(
+                avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ(uart),
+                              UART_IRQ_OUTPUT), buf_output_cb, &buf);
+        tests_assert_xxxx_receive_avr(avr, run_usec, &buf, expected);
 }
 
 void tests_assert_uart_receive(const char *elfname,
@@ -213,6 +233,28 @@ void tests_assert_uart_receive(const char *elfname,
                               uart);
 }
 
+void tests_assert_register_receive_avr(avr_t         *avr,
+                                       unsigned long  run_usec,
+                                       const char    *expected,
+                                       avr_io_addr_t  reg_addr)
+{
+       struct output_buffer buf;
+
+       init_output_buffer(&buf);
+        avr_register_io_write(avr, reg_addr, reg_output_cb, &buf);
+       tests_assert_xxxx_receive_avr(avr, run_usec, &buf, expected);
+}
+
+void tests_assert_register_receive(const char    *elfname,
+                                   unsigned long  run_usec,
+                                   const char    *expected,
+                                   avr_io_addr_t  reg_addr)
+{
+       avr_t *avr = tests_init_avr(elfname);
+
+        tests_assert_register_receive_avr(avr, run_usec, expected, reg_addr);
+}
+
 void tests_assert_cycles_at_least(unsigned long n) {
        if (tests_cycle_count < n)
                _fail(NULL, 0, "Program ran for too few cycles (%"
index 1496b93..e378f83 100644 (file)
@@ -32,6 +32,14 @@ void tests_assert_uart_receive_avr(avr_t *avr,
                               unsigned long run_usec,
                               const char *expected,
                               char uart);                                 
+void tests_assert_register_receive(const char    *elfname,
+                                   unsigned long  run_usec,
+                                   const char    *expected,
+                                   avr_io_addr_t  reg_addr);
+void tests_assert_register_receive_avr(avr_t         *avr,
+                                       unsigned long  run_usec,
+                                       const char    *expected,
+                                       avr_io_addr_t  reg_addr);
 
 void tests_assert_cycles_at_least(unsigned long n);
 void tests_assert_cycles_at_most(unsigned long n);