333 lines
11 KiB
C
333 lines
11 KiB
C
|
/*
|
||
|
avr_mcu_section.h
|
||
|
|
||
|
Copyright 2008-2013 Michel Pollet <buserror@gmail.com>
|
||
|
|
||
|
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/>.
|
||
|
*/
|
||
|
|
||
|
#ifndef __AVR_MCU_SECTION_H__
|
||
|
#define __AVR_MCU_SECTION_H__
|
||
|
|
||
|
/*
|
||
|
* This header is used to pass "parameters" to the programmer or the simulator,
|
||
|
* it tags the ELF file with a section that contains parameters about the physical
|
||
|
* AVR this was compiled for, including the speed, model, and signature bytes.
|
||
|
*
|
||
|
* A programmer software can read this and verify fuses values for example, and a
|
||
|
* simulator can instantiate the proper "model" of AVR, the speed and so on without
|
||
|
* command line parameters.
|
||
|
*
|
||
|
* Example of use:
|
||
|
*
|
||
|
* #include "avr_mcu_section.h"
|
||
|
* AVR_MCU(F_CPU, "atmega88");
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <stdint.h>
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
#endif
|
||
|
|
||
|
enum {
|
||
|
AVR_MMCU_TAG = 0,
|
||
|
AVR_MMCU_TAG_NAME,
|
||
|
AVR_MMCU_TAG_FREQUENCY,
|
||
|
AVR_MMCU_TAG_VCC,
|
||
|
AVR_MMCU_TAG_AVCC,
|
||
|
AVR_MMCU_TAG_AREF,
|
||
|
AVR_MMCU_TAG_LFUSE,
|
||
|
AVR_MMCU_TAG_HFUSE,
|
||
|
AVR_MMCU_TAG_EFUSE,
|
||
|
AVR_MMCU_TAG_SIGNATURE,
|
||
|
AVR_MMCU_TAG_SIMAVR_COMMAND,
|
||
|
AVR_MMCU_TAG_SIMAVR_CONSOLE,
|
||
|
AVR_MMCU_TAG_VCD_FILENAME,
|
||
|
AVR_MMCU_TAG_VCD_PERIOD,
|
||
|
AVR_MMCU_TAG_VCD_TRACE,
|
||
|
AVR_MMCU_TAG_VCD_PORTPIN,
|
||
|
AVR_MMCU_TAG_VCD_IRQ,
|
||
|
AVR_MMCU_TAG_PORT_EXTERNAL_PULL,
|
||
|
};
|
||
|
|
||
|
enum {
|
||
|
SIMAVR_CMD_NONE = 0,
|
||
|
SIMAVR_CMD_VCD_START_TRACE,
|
||
|
SIMAVR_CMD_VCD_STOP_TRACE,
|
||
|
SIMAVR_CMD_UART_LOOPBACK,
|
||
|
};
|
||
|
|
||
|
#if __AVR__
|
||
|
/*
|
||
|
* WARNING. Due to newer GCC being stupid, they introduced a bug that
|
||
|
* prevents us introducing variable length strings in the declaration
|
||
|
* of structs. Worked for a million years, and no longer.
|
||
|
* So the new method declares the string as fixed size, and the parser
|
||
|
* is forced to skip the zeroes in padding. Dumbo.
|
||
|
*/
|
||
|
#define _MMCU_ __attribute__((section(".mmcu")))
|
||
|
struct avr_mmcu_long_t {
|
||
|
uint8_t tag;
|
||
|
uint8_t len;
|
||
|
uint32_t val;
|
||
|
} __attribute__((__packed__));
|
||
|
|
||
|
struct avr_mmcu_string_t {
|
||
|
uint8_t tag;
|
||
|
uint8_t len;
|
||
|
char string[64];
|
||
|
} __attribute__((__packed__));
|
||
|
|
||
|
struct avr_mmcu_addr_t {
|
||
|
uint8_t tag;
|
||
|
uint8_t len;
|
||
|
void * what;
|
||
|
} __attribute__((__packed__));
|
||
|
|
||
|
struct avr_mmcu_vcd_trace_t {
|
||
|
uint8_t tag;
|
||
|
uint8_t len;
|
||
|
uint8_t mask;
|
||
|
void * what;
|
||
|
char name[32];
|
||
|
} __attribute__((__packed__));
|
||
|
|
||
|
#define AVR_MCU_STRING(_tag, _str) \
|
||
|
const struct avr_mmcu_string_t _##_tag _MMCU_ = {\
|
||
|
.tag = _tag,\
|
||
|
.len = sizeof(struct avr_mmcu_string_t) - 2,\
|
||
|
.string = _str,\
|
||
|
}
|
||
|
/*
|
||
|
* This trick allows concatenation of tokens. We need a macro redirection
|
||
|
* for it to work.
|
||
|
* The goal is to make unique variable names (they don't matter anyway)
|
||
|
*/
|
||
|
#define DO_CONCAT2(_a, _b) _a##_b
|
||
|
#define DO_CONCAT(_a, _b) DO_CONCAT2(_a,_b)
|
||
|
|
||
|
#define AVR_MCU_LONG(_tag, _val) \
|
||
|
const struct avr_mmcu_long_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\
|
||
|
.tag = _tag,\
|
||
|
.len = sizeof(struct avr_mmcu_long_t) - 2,\
|
||
|
.val = _val,\
|
||
|
}
|
||
|
|
||
|
#define AVR_MCU_BYTE(_tag, _val) \
|
||
|
const uint8_t _##_tag _MMCU_ = { _tag, 1, _val }
|
||
|
|
||
|
/*!
|
||
|
* This Macro allows you to specify traces for the VCD file output
|
||
|
* engine. This specifies a default header, and let you fill in the
|
||
|
* relevant bits.
|
||
|
* Example:
|
||
|
* const struct avr_mmcu_vcd_trace_t _mytrace[] _MMCU_ = {
|
||
|
* { AVR_MCU_VCD_SYMBOL("UDR0"), .what = (void*)&UDR0, },
|
||
|
* { AVR_MCU_VCD_SYMBOL("UDRE0"), .mask = (1 << UDRE0), .what = (void*)&UCSR0A, },
|
||
|
* };
|
||
|
* This structure will automatically tell simavr to add a VCD trace
|
||
|
* for the UART register, and the UDRE0 bit, so you can trace exactly
|
||
|
* the timing of the changed using gtkwave.
|
||
|
*/
|
||
|
#define AVR_MCU_VCD_SYMBOL(_name) \
|
||
|
.tag = AVR_MMCU_TAG_VCD_TRACE, \
|
||
|
.len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\
|
||
|
.name = _name
|
||
|
|
||
|
/*!
|
||
|
* Specifies the name and wanted period (in usec) for a VCD file
|
||
|
* this is not mandatory for the VCD output to work, if this tag
|
||
|
* is not used, a VCD file will still be created with default values
|
||
|
*/
|
||
|
#define AVR_MCU_VCD_FILE(_name, _period) \
|
||
|
AVR_MCU_STRING(AVR_MMCU_TAG_VCD_FILENAME, _name);\
|
||
|
AVR_MCU_LONG(AVR_MMCU_TAG_VCD_PERIOD, _period)
|
||
|
|
||
|
/*!
|
||
|
* It is possible to send "commands" to simavr from the
|
||
|
* firmware itself. For this to work you need to specify
|
||
|
* an IO register that is to be used for a write-only
|
||
|
* bridge. A favourite is one of the usual "GPIO register"
|
||
|
* that most (all ?) AVR have.
|
||
|
* See definition of SIMAVR_CMD_* to see what commands can
|
||
|
* be used from your firmware.
|
||
|
*/
|
||
|
#define AVR_MCU_SIMAVR_COMMAND(_register) \
|
||
|
const struct avr_mmcu_addr_t _simavr_command_register _MMCU_ = {\
|
||
|
.tag = AVR_MMCU_TAG_SIMAVR_COMMAND,\
|
||
|
.len = sizeof(void *),\
|
||
|
.what = (void*)_register, \
|
||
|
}
|
||
|
/*!
|
||
|
* Similar to AVR_MCU_SIMAVR_COMMAND, The CONSOLE allows the AVR code
|
||
|
* to declare a register (typically a GPIO register, but any unused
|
||
|
* register can work...) that will allow printing on the host's console
|
||
|
* without using a UART to do debug.
|
||
|
*/
|
||
|
#define AVR_MCU_SIMAVR_CONSOLE(_register) \
|
||
|
const struct avr_mmcu_addr_t _simavr_console_register _MMCU_ = {\
|
||
|
.tag = AVR_MMCU_TAG_SIMAVR_CONSOLE,\
|
||
|
.len = sizeof(void *),\
|
||
|
.what = (void*)_register, \
|
||
|
}
|
||
|
/*!
|
||
|
* Allows the firmware to hint simavr as to wether there are external
|
||
|
* pullups/down on PORT pins. It helps if the firmware uses "open drain"
|
||
|
* pins by toggling the DDR pins to switch between an output state and
|
||
|
* a "default" state.
|
||
|
* The value passed here will be output on the PORT IRQ when the DDR
|
||
|
* pin is set to input again
|
||
|
*/
|
||
|
#define AVR_MCU_EXTERNAL_PORT_PULL(_port, _mask, _val) \
|
||
|
AVR_MCU_LONG(AVR_MMCU_TAG_PORT_EXTERNAL_PULL, \
|
||
|
(((unsigned long)((_port)&0xff) << 16) | \
|
||
|
((unsigned long)((_mask)&0xff) << 8) | \
|
||
|
((_val)&0xff)));
|
||
|
/*!
|
||
|
* Add this port/pin to the VCD file. The syntax uses the name of the
|
||
|
* port as a character, and not a pointer to a register.
|
||
|
* AVR_MCU_VCD_PORT_PIN('B', 5);
|
||
|
*/
|
||
|
#define AVR_MCU_VCD_PORT_PIN(_port, _pin, _name) \
|
||
|
const struct avr_mmcu_vcd_trace_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\
|
||
|
.tag = AVR_MMCU_TAG_VCD_PORTPIN, \
|
||
|
.len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\
|
||
|
.mask = _port, \
|
||
|
.what = (void*)_pin, \
|
||
|
.name = _name, \
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* These allows you to add a trace showing how long an IRQ vector is pending,
|
||
|
* and also how long it is running. You can specify the IRQ as a vector name
|
||
|
* straight from the firmware file, and it will be named properly in the trace
|
||
|
*/
|
||
|
|
||
|
#define AVR_MCU_VCD_IRQ_TRACE(_vect_number, __what, _trace_name) \
|
||
|
const struct avr_mmcu_vcd_trace_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\
|
||
|
.tag = AVR_MMCU_TAG_VCD_IRQ, \
|
||
|
.len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\
|
||
|
.mask = _vect_number, \
|
||
|
.what = (void*)__what, \
|
||
|
.name = _trace_name, \
|
||
|
};
|
||
|
#define AVR_MCU_VCD_IRQ(_irq_name) \
|
||
|
AVR_MCU_VCD_IRQ_TRACE(_irq_name##_vect_num, 1, #_irq_name)
|
||
|
#define AVR_MCU_VCD_IRQ_PENDING(_irq_name) \
|
||
|
AVR_MCU_VCD_IRQ_TRACE(_irq_name##_vect_num, 0, #_irq_name "_pend")
|
||
|
#define AVR_MCU_VCD_ALL_IRQ() \
|
||
|
AVR_MCU_VCD_IRQ_TRACE(0xff, 1, "IRQ")
|
||
|
#define AVR_MCU_VCD_ALL_IRQ_PENDING() \
|
||
|
AVR_MCU_VCD_IRQ_TRACE(0xff, 0, "IRQ_PENDING")
|
||
|
|
||
|
/*!
|
||
|
* This tag allows you to specify the voltages used by your board
|
||
|
* It is optional in most cases, but you will need it if you use
|
||
|
* ADC module's IRQs. Not specifying it in this case might lead
|
||
|
* to a divide-by-zero crash.
|
||
|
* The units are Volts*1000 (millivolts)
|
||
|
*/
|
||
|
#define AVR_MCU_VOLTAGES(_vcc, _avcc, _aref) \
|
||
|
AVR_MCU_LONG(AVR_MMCU_TAG_VCC, (_vcc));\
|
||
|
AVR_MCU_LONG(AVR_MMCU_TAG_AVCC, (_avcc));\
|
||
|
AVR_MCU_LONG(AVR_MMCU_TAG_AREF, (_aref));
|
||
|
|
||
|
/*!
|
||
|
* This the has to be used if you want to add other tags to the .mmcu section
|
||
|
* the _mmcu symbol is used as an anchor to make sure it stays linked in.
|
||
|
*/
|
||
|
#define AVR_MCU(_speed, _name) \
|
||
|
AVR_MCU_STRING(AVR_MMCU_TAG_NAME, _name);\
|
||
|
AVR_MCU_LONG(AVR_MMCU_TAG_FREQUENCY, _speed);\
|
||
|
const uint8_t _mmcu[2] _MMCU_ = { AVR_MMCU_TAG, 0 }
|
||
|
|
||
|
/*
|
||
|
* The following MAP macros where copied from
|
||
|
* https://github.com/swansontec/map-macro/blob/master/map.h
|
||
|
*
|
||
|
* The license header for that file is reproduced below:
|
||
|
*
|
||
|
* Copyright (C) 2012 William Swanson
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person
|
||
|
* obtaining a copy of this software and associated documentation
|
||
|
* files (the "Software"), to deal in the Software without
|
||
|
* restriction, including without limitation the rights to use, copy,
|
||
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||
|
* of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be
|
||
|
* included in all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
*
|
||
|
* Except as contained in this notice, the names of the authors or
|
||
|
* their institutions shall not be used in advertising or otherwise to
|
||
|
* promote the sale, use or other dealings in this Software without
|
||
|
* prior written authorization from the authors.
|
||
|
*/
|
||
|
|
||
|
#define _EVAL0(...) __VA_ARGS__
|
||
|
#define _EVAL1(...) _EVAL0 (_EVAL0 (_EVAL0 (__VA_ARGS__)))
|
||
|
#define _EVAL2(...) _EVAL1 (_EVAL1 (_EVAL1 (__VA_ARGS__)))
|
||
|
#define _EVAL3(...) _EVAL2 (_EVAL2 (_EVAL2 (__VA_ARGS__)))
|
||
|
#define _EVAL4(...) _EVAL3 (_EVAL3 (_EVAL3 (__VA_ARGS__)))
|
||
|
#define _EVAL(...) _EVAL4 (_EVAL4 (_EVAL4 (__VA_ARGS__)))
|
||
|
|
||
|
#define _MAP_END(...)
|
||
|
#define _MAP_OUT
|
||
|
|
||
|
#define _MAP_GET_END() 0, _MAP_END
|
||
|
#define _MAP_NEXT0(test, next, ...) next _MAP_OUT
|
||
|
#define _MAP_NEXT1(test, next) _MAP_NEXT0 (test, next, 0)
|
||
|
#define _MAP_NEXT(test, next) _MAP_NEXT1 (_MAP_GET_END test, next)
|
||
|
|
||
|
#define _MAP0(f, x, peek, ...) f(x) _MAP_NEXT (peek, _MAP1) (f, peek, __VA_ARGS__)
|
||
|
#define _MAP1(f, x, peek, ...) f(x) _MAP_NEXT (peek, _MAP0) (f, peek, __VA_ARGS__)
|
||
|
#define _MAP(f, ...) _EVAL (-MAP1 (f, __VA_ARGS__, (), 0))
|
||
|
|
||
|
/* End of original MAP macros. */
|
||
|
|
||
|
// Define MAP macros with one additional argument
|
||
|
#define _MAP0_1(f, a, x, peek, ...) f(a, x) _MAP_NEXT (peek, _MAP1_1) (f, a, peek, __VA_ARGS__)
|
||
|
#define _MAP1_1(f, a, x, peek, ...) f(a, x) _MAP_NEXT (peek, _MAP0_1) (f, a, peek, __VA_ARGS__)
|
||
|
#define _MAP_1(f, a, ...) _EVAL (_MAP1_1 (f, a, __VA_ARGS__, (), 0))
|
||
|
|
||
|
#define _SEND_SIMAVR_CMD_BYTE(reg, b) reg = b;
|
||
|
|
||
|
// A helper macro for sending multi-byte commands
|
||
|
#define SEND_SIMAVR_CMD(reg, ...) \
|
||
|
do { \
|
||
|
_MAP_1(_SEND_SIMAVR_CMD_BYTE, reg, __VA_ARGS__) \
|
||
|
} while(0)
|
||
|
|
||
|
#endif /* __AVR__ */
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
#endif
|