From d68da33045fce1ff813cafc69e842c1ea5aed519 Mon Sep 17 00:00:00 2001
From: Markus Hitter <mah@jump-ing.de>
Date: Fri, 15 Nov 2013 22:44:07 +0100
Subject: Allow to store simulation parameters inside the ELF file.

For now this is a proof of concept. Storing parameters works fine,
reading them back, too, but instead of being used, they're simple
written to the console. For now the parameters are device type and
cpu frequency, but the concept can be extended easily to all other
command line parameters.

The proof is applied to the simple_serial example.

The point of this strategy is to avoid the neccessity to run
simulavr with more command line parameters than just the name of
the ELF file. At compilation time we know best for which device
we compile, after all.
---
 examples/simple_serial/Makefile.am |  1 +
 examples/simple_serial/main.c      | 33 ++++-----------
 src/avrerror.h                     |  4 ++
 src/avrreadelf.cpp                 | 36 +++++++++++++++++
 src/simulavr_info.h                | 83 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 131 insertions(+), 26 deletions(-)
 create mode 100644 src/simulavr_info.h

diff --git a/examples/simple_serial/Makefile.am b/examples/simple_serial/Makefile.am
index bebf7a7..01e7996 100644
--- a/examples/simple_serial/Makefile.am
+++ b/examples/simple_serial/Makefile.am
@@ -10,6 +10,7 @@ AVR_GCC = @AVR_GCC@
 AVR_CPU = atmega644
 AVR_FREQUENCY = 20000000
 AVR_CFLAGS = -g -O2 -mmcu=$(AVR_CPU) -DF_CPU=$(AVR_FREQUENCY)
+AVR_CFLAGS += -Wl,--section-start=.siminfo=0x600000
 AVR_OBJS = main.o serial.o
 SIMULAVR_ARGS = -d $(AVR_CPU) -f $(EXAMPLE).elf
 
diff --git a/examples/simple_serial/main.c b/examples/simple_serial/main.c
index de0b342..4fef1fb 100644
--- a/examples/simple_serial/main.c
+++ b/examples/simple_serial/main.c
@@ -16,33 +16,14 @@
 #include <avr/interrupt.h>
 
 /*
- * This is how simavr (our friendly competitor) tells the simulator
- * which hardware variant a firmware is compiled for and which
- * peripherals (UART, port pins, etc.) should be taken into account on
- * simulation. The system makes adding parameters at the command line
- * obsolete and is highly customizeable.
- *
- * These macros create custom sections in the ELF binary to transport
- * this information, the actual binary code is untouched. Accordingly
- * the very same ELF file can be uploaded to real hardware, too.
- *
- * To stop the linker from stripping this information, avr-gcc needs a few
- * extra parameters when linking the binary:
- *
- *   -Wl,--undefined=_mmcu,--section-start=.mmcu=0x910000
- *
+ * Here we tell SimulAVR for which device and which frequency we build this
+ * code. The nice thing is, this shows up in the ELF file, so it can be read
+ * by SimulAVR, but is ignored when converted to a ihex file for hardware
+ * upload. In both case, this doesn't influence the running AVR code at all.
  */
-// #include "avr_mcu_section.h"
-// #include "timer.h"
-// AVR_MCU(F_CPU, "atmega644");
-// AVR_MCU_VOLTAGES(5000, 5000, 5000);
-// AVR_MCU_SIMAVR_CONSOLE(&UDR0);
-// const struct avr_mmcu_vcd_trace_t _mytrace[]  _MMCU_ = {
-//   { AVR_MCU_VCD_SYMBOL("Serial"), .what = (void*)&UDR0, },
-//   { AVR_MCU_VCD_SYMBOL("PORTA"), .what = (void*)&PORTA, },
-//   { AVR_MCU_VCD_SYMBOL("XStep"), .mask = (1 << PINA2), .what = (void*)&PORTA, },
-//   { AVR_MCU_VCD_SYMBOL("DDRA"), .what = (void*)&DDRA, },
-// };
+#include "../../src/simulavr_info.h"
+SIMINFO_DEVICE("atmega644");
+SIMINFO_CPUFREQUENCY(F_CPU);
 
 
 // This is all we need:
diff --git a/src/avrerror.h b/src/avrerror.h
index 0a8d7b0..c75ceca 100644
--- a/src/avrerror.h
+++ b/src/avrerror.h
@@ -32,8 +32,12 @@
 #define ATTRIBUTE_NORETURN __declspec(noreturn)
 #define ATTRIBUTE_PRINTF(string_arg, first_arg)
 #elif defined(__GNUC__)
+#ifndef ATTRIBUTE_NORETURN
 #define ATTRIBUTE_NORETURN __attribute__((noreturn))
+#endif
+#ifndef ATTRIBUTE_PRINTF
 #define ATTRIBUTE_PRINTF(string_arg, first_arg) __attribute__ ((format (printf, string_arg, first_arg)))
+#endif
 #else
 #define ATTRIBUTE_NORETURN
 #define ATTRIBUTE_PRINTF(string_arg, first_arg)
diff --git a/src/avrreadelf.cpp b/src/avrreadelf.cpp
index 2828bdc..d6eed49 100644
--- a/src/avrreadelf.cpp
+++ b/src/avrreadelf.cpp
@@ -30,9 +30,11 @@
 #endif
 
 #include <string>
+#include <cstring>
 #include <map>
 
 #include "avrdevice_impl.h"
+#include "simulavr_info.h"
 
 #include "avrreadelf.h"
 
@@ -248,6 +250,40 @@ void ELFLoad(const AvrDevice * core) {
             free(tmp);
         }
 
+        if ( ! strcmp(sec->name, ".siminfo")) {
+          avr_warning(".siminfo section found!");
+          int size = sec->size;
+          unsigned char *data, *data_ptr, *data_end;
+
+          data = (unsigned char *)malloc(size);
+          if (data == NULL)
+            break;
+          bfd_get_section_contents(abfd, sec, data, 0, size);
+
+          data_ptr = data;
+          data_end = data + size;
+          while (data_ptr < data_end) {
+            char tag = *data_ptr++;
+            switch (tag) {
+              case SIMINFO_TAG_DEVICE:
+                avr_warning("device is %s", data_ptr);
+                while (*data_ptr != '\0')
+                  data_ptr++;
+                data_ptr++; // the '\0' its self
+                break;
+              case SIMINFO_TAG_CPUFREQUENCY:
+                avr_warning("frequency is %u", *(uint32_t *)data_ptr);
+                data_ptr += sizeof(uint32_t);
+                break;
+              default:
+                avr_warning("Unknown tag in ELF .siminfo section: %hu", tag);
+                data_ptr++;
+            }
+          }
+          free(data);
+        }
+
+
         sec = sec->next;
     }
 
diff --git a/src/simulavr_info.h b/src/simulavr_info.h
new file mode 100644
index 0000000..193f19a
--- /dev/null
+++ b/src/simulavr_info.h
@@ -0,0 +1,83 @@
+/*
+ *   $Id$
+ *
+ * This header provides macros to embed simulator setup information into
+ * a compiled ELF binary.
+ *
+ * Example:
+ *
+ * Add this somewhere at the root level of your AVR code:
+ *
+ *   #include "simulavr_info.h"
+ *   SIMINFO_DEVICE("atmega644");
+ *   SIMINFO_CPUFREQUENCY(F_CPU);
+ *
+ * Then link as usual, but add these linker flags to avr-gcc to prohibit
+ * the linker from removing the info sections at the link stage:
+ *
+ *   -Wl,--section-start=.siminfo=0x600000
+ *
+ * Having this done, running the ELF binary in the simulator will
+ * automatically inform simulavr for which AVR variant and CPU frequency
+ * the binary was built, making the corresponding command line parameters
+ * obsolete. In case you give both, in-binary and CLI parameters, CLI
+ * parameters take precedence.
+ *
+ * The really nice thing about this mechanism is, it doesn't alter the
+ * executed binary at all. You can upload and run this on real hardware,
+ * not a single byte of Flash memory or a single CPU cycle at runtime wasted.
+ */
+
+#ifndef __SIMULAVR_INFO_H__
+#define __SIMULAVR_INFO_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define LD_SECTION __attribute__((section(".siminfo")))
+
+enum {
+  SIMINFO_TAG_NOTAG = 0, // keep this unused as a protection against empty data
+  SIMINFO_TAG_DEVICE,
+  SIMINFO_TAG_CPUFREQUENCY
+};
+
+struct siminfo_long_t {
+  uint8_t tag;
+  uint32_t value;
+} __attribute__((__packed__));
+
+struct siminfo_string_t {
+  uint8_t tag;
+  char string[];
+} __attribute__((__packed__));
+
+
+/*
+ * This gives the device type, like "attiny45", "atmega128", "atmega644", etc.
+ */
+#define SIMINFO_DEVICE(name) \
+  const struct siminfo_string_t siminfo_device LD_SECTION = { \
+    SIMINFO_TAG_DEVICE, \
+    name \
+  }
+
+/*
+ * This gives the cpu frequency, like 8000000UL or 16000000UL.
+ */
+#define SIMINFO_CPUFREQUENCY(value) \
+  const struct siminfo_long_t siminfo_cpufrequency LD_SECTION = { \
+    SIMINFO_TAG_CPUFREQUENCY, \
+    value \
+  }
+
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __SIMULAVR_INFO_H__ */
-- 
1.8.3.2

