summaryrefslogtreecommitdiff
path: root/FreeRTOS/Source/portable/ThirdParty/GCC/ATmega/readme.md
blob: 4afb4fe159e3424e9b4cdd19f1a10994c1d9c2a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<h2>ATmegaxxxx</h2>

__Port for generalised Microchip ATmega architecture__

<h3>Description</h3>

This port provides a basis for supporting all modern ATmega devices using either the Enhanced Watchdog Timer, or Timer0 (an 8-bit Timer generally available across the whole range).

This initial commit contains the information required to build with System Tick being generated by either the:
- Watchdog Timer, or
- Timer0 - an 8-bit Timer, or
- TimerN - a 16-bit Timer which will be configured by the user.

Further commits can add support for 16-bit Timers available on many relevant devices. The availability of these 16-bit Timers is somewhat device specific, and these complex and highly configurable Timers are often used to generate phase correct PWM timing (for example) and they would be wasted as a simple System Tick.

The port also provides support for the 3 byte program counter devices __ATmega2560__ and __ATmega2561__. Specific to these two devices the `EIND` register need to be preserved during a context switch. Also, due to a limitation in GCC, the scheduler needs to reside in the lower 128kB of flash for both of these devices. This is achieved by adding the `.lowtext` section attribute to the function prototype.

To build generic Microchip (AVR) ATmega support the similarities across the family must be considered, and differences respected. Some comments on the strategy follow.

<h3>System Tick</h3>

The Microchip (AVR) ATmega family has limited Timer and Pin capabilities, and is designed to be used in physical applications, controlling hardware with PWM and recognising level and edge voltage changes. It does this mainly through the use of 16-bit Timers (for generating phase correct PWM by up/down counting), and Pins attached to Interrupts. The 8-bit Timers are also attached to Pins, and they can be used for more simple timing tasks, requiring only a single counting direction.

The Timers not attached to Pins (and therefore not impacting the application of the device) are some 16-bit Timers (very device dependent, eg Timer3 on 1284p), The RTC Timer, and the Watch Dog Timer.

The Watch Dog Timer is configured identically across most of the ATmega devices. It comes in two variants. 1. Old style (eg ATmega32) which does not have an Interrupt capability, and hence on these old devices cannot be used as the System Tick. and 2. New style enhanced WDT, which can generate an Interrupt, and is available on every relevant device.

Using the Watch Dog Timer (WDT) to generate the System Tick does not impact its use as a watch dog. It can be configured to generate a System Tick interrupt, and then one period later to Reset the device if the interrupt is not serviced.

Configuration and usage of the WDT is covered in `<avr/wdt.h>` which was revised in avr-libc 2.0.0.

Two additional WDT functions are provided in `port.c`, which extend avr-libc functions to enable the WDT Interrupt without enabling Reset `wdt_interrupt_enable()`, and to enable both the Interrupt and the Reset `wdt_interrupt_reset_enable()`.

<h3>3 Byte PC Devices</h3>

The ATtiny, ATmega, ATxmega families can optionally support both 3 byte PC and 3 byte RAM addresses. However, focusing on just the ATmega family only two devices have a large Flash requiring them to use 3 byte PC. These are the __ATmega2560__ and __ATmega2561__. This PR provides support for these two devices in two ways.

 - providing `portSAVE_CONTEXT()` and `portRESTORE_CONTEXT` saving both the __RAMPZ__ and __EIND__ registers.
 - providing a `portTASK_FUNCTION_PROTO()` with the linker attribute `.lowtext` which is used to ensure that the scheduler and relevant functions remain in the lower 128kB of Flash.

For devices which can support __XRAM__ and have the __RAMPZ__ register, this register is also preserved during the context switch.

<h3>Interrupt Nesting</h3>

The ATmega family does not support interrupt nesting, having only one interrupt priority. This means that when the Scheduler is running, interrupts are normally disabled.

When a very time critical process is running, based on microsecond timing generated by one of the Timers, it is important to re-enable interrupts as early as possible in processing a Yield. Fortunately, this is supported through the use of the `NO_BLOCK` decorator when defining the Interrupt Service Routine.

The `NO_BLOCK` decorator will enable the global interrupt early in the handling of an ISR (in this case for the Scheduler), and enable interrupts to be nested. Using this method, I've been able to successfully implement an [Audio Synthesiser](https://feilipu.me/2015/06/02/goldilocks-analogue-synthesizer/) with less than 83 microseconds for each cycle, whilst still running the Scheduler to handle display and input.

Using `NO_BLOCK` is optional, and should only be done if a critical Timer should interrupt the Scheduler.

<h3>Heap Management</h3>

Most users of FreeRTOS will choose to manage their own heap using one of the pre-allocated heap management algorithms, but for those that choose to use `heap_3.c`, the wrappered `malloc()` method, there is an issue that needs to be addressed.

The avr-libc library assumes that the stack will always be above the heap, and does a check for this when responding to a `malloc()` request. This is not the case when Tasks are running, as their stack is located in the early allocated heap address ranges which will be below free heap memory, and so the `malloc()` request will fail even though heap space is available.

To avoid this issue causing `pvPort_Malloc()` to failing, the user needs to issue this tuning statement BEFORE they use the heap, or use the `xTaskCreate()` API.

```c
if( __malloc_heap_end == 0 )
    __malloc_heap_end = (char *)(RAMEND - __malloc_margin);
```
Unfortunately in the repository there is nowhere sensible to include this statement as it should be included early in the `main()` function.

For devices which can support __XRAM__ the user will need to tune the location of stack and heap according to their own requirements.

<h3>Supported Devices</h3>

ATmega devices with __ENHANCED WDT__ Interrupt capability - will use WDT.

 - ATmega8U2/16U2/32U2 -> 2kB RAM
 - ATmega16U4/32U4 - Arduino Leonardo -> 2.5kB RAM
 - ATmega48PB/88PB/168PB/328PB - Arduino Uno -> 2kB RAM
 - ATmega164PA/324PA/644PA/1284P - Goldilocks -> __16kB RAM__
 - ATmega324PB -> 2kB RAM
 - ATmega640/1280/2560/1281/2561 - Arduino Mega -> __8kB RAM + XRAM__

ATmega devices without enhanced __WDT__ Interrupt capability - will use a 8-bit or 16-bit Timer.

 - ATmega8A/16A/32A/64A/128A -> 4kB RAM
 - ATmega165A/165PA/325A/325PA/3250A/3250PA/645A/645P/6450A/6450P -> 4kB RAM
 - ATmega169A/169PA/329A/329PA/3290A/3290PA/649A/649P/6490A/6490P -> 4kB RAM
 - ATmega808/809/1608/1609/3208/3209/4808/4809 - megaAVR 0-Series -> 6kB RAM