Repository: nqtronix/fifofast
Branch: master
Commit: 196edda9d390
Files: 32
Total size: 129.7 KB
Directory structure:
gitextract_fki9r3a0/
├── .gitignore
├── LICENSE.md
├── README.md
├── fifofast.atsln
├── fifofast.componentinfo.xml
├── fifofast.cproj
├── fifofast.h
├── fifofast_demo.c
├── fifofast_demo.h
├── fifofast_test.c
├── fifofast_test.h
├── subrepos/
│ ├── readme (license info).txt
│ └── unittrace/
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── unittrace.atsln
│ ├── unittrace.c
│ ├── unittrace.componentinfo.xml
│ ├── unittrace.cproj
│ ├── unittrace.h
│ ├── unittrace_demo.c
│ └── utility/
│ └── macros/
│ └── com/
│ ├── com readme.txt
│ └── macro_type.h
└── utility/
└── macros/
├── com/
│ ├── com readme.txt
│ ├── macro_array.h
│ ├── macro_math.h
│ └── macro_type.h
├── mpl/
│ ├── macro_cat.h
│ ├── macro_narg.h
│ ├── macro_vfunc.h
│ └── mpl readme.txt
└── readme.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/Debug
/.vs
*.bak
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (c) 2018-2022 nqtronix (github.com/nqtronix)
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 OR COPYRIGHT HOLDERS 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.
================================================
FILE: README.md
================================================
<h1 align="center" style="font-weight: bold; margin-top: 20px; margin-bottom: 20px;">fifofast</h4>
<h3 align="center" style="font-weight: bold; margin-top: 20px; margin-bottom: 20px;">A fast, generic fifo for MCUs.</h4>
<p align="center">
<a href="#changelog"><img src="https://img.shields.io/github/release-pre/nqtronix/fifofast.svg" alt="release: NA"></a>
<a href="#about"><img src="https://img.shields.io/badge/language-C_(GCC_5.4.0)-blue.svg" alt="language: C GCC (5.4.0)"></a>
<a href="#about"><img src="https://img.shields.io/badge/platform-MCUs, AVR8-blue.svg" alt="platform: MCUs, AVR8"></a>
<a href="#about"><img src="https://img.shields.io/badge/status-active-brightgreen.svg" alt="status: active"></a>
<a href="https://github.com/nqtronix/fifofast/issues"><img src="https://img.shields.io/github/issues/nqtronix/fifofast.svg" alt="issues: NA"></a>
<a href="#license"><img src="https://img.shields.io/github/license/nqtronix/fifofast.svg" alt="license: NA"></a>
</p>
<p align="center">
<a href="#getting-started">Getting Started</a> •
<a href="#documentation">Documentation</a> •
<a href="#under-the-hood">Under the Hood</a> •
<a href="#support">Need Help?</a> •
<a href="#contribute">Contribute</a> •
<a href="#about">About</a> •
<a href="#credits">Credits</a>
</p>
## Introduction
First-In-First-Out (FIFO) buffers are one of the most used data structures, especially on micro controllers (MCUs) to handle data input/output in real time. Although there are countless of implementations, there wasn't a single one that is well optimized for entry level MCUs.
**fifofast** was specifically designed to consume as little CPU time and SRAM as possible, while providing more versatility and features than typical implementations. It is ideally suited to buffer serial data, ADC measurement results or arbitrary data shared between differnt time critical functions.
<br>
## Key Features
- **generic data:** fifofast supports **_any_** data type, even custom typedef'd ones
- **static memory:** no additional overhead through dynamic memory management
- **inline support:** speeds up execution, especially from ISRs (Interrupt Service Routines)
- **minimal RAM:** a typical fifo has only **3 _byte_** management overhead
- **easy to use:** all typical fifo functions are implemented (and they work like you expect)
- **supports debugging:** with the build-in debugger of Atmel Studio 7
- **well documented:** extensive use of comments within the source code
<br>
## Limititations
- **Fifo size:**<br>
The fifo size is limited to 2ⁿ elements to make use of the fast wrapping functionality. Other sizes will be automatically rounded up.
- **Element size:**<br>
Normal fifos can store elements of any size. An exception are point-able fifos, which have a maximum element size of 255 bytes.
- **Programm memory usage:**<br>
Each function-like macro or inline function pastes new code at its location. Compared to a regular function-based fifo the program memory usage (flash) is higher.
<br>
## Minimal Code Example
The declaration of a fifo is slightly different to support generic types, but they are accessed just like you'd expect. This is the minimum code example that can be compiled:
```c
#include "fifofast.h"
// declare a fifo with 16 elements of type 'int_8' with the name 'fifo_int8';
// for access in other .c files, move the declaration into a .h file and include it in each .c file
_fff_declare(int8_t, fifo_int8, 16);
// initialize the fifo; use this macro only in one .c file (even if accessed from different files)
_fff_init(fifo_int8);
int main(void)
{
// volatile prevents the compiler from optimizing the variable away
volatile int8_t tmp;
// write a value to the fifo
_fff_write(fifo_int8, -42);
// read back the value from the fifo
tmp = _fff_read(fifo_int8);
// 'tmp' contains now the value '-42'
while(1);
}
```
You find this a variation of this snippet and much more in [`fifofast_test.c`](fifofast_test.c).
<br>
## Getting Started
This section is written especially for everyone who is **not familiar** with the used tools. If you run into problems, please [ask for clarification](#get-help).<br>
### Step 1: Software and Tools
- [**Atmel Studio 7.0**][tool-atmel-studio-7-0] **(Build 1931) [free]**<br>
The installer contains all tools you need to open, edit, compile, simulate and flash this code. If you favor another development tool, feel free to use it instead. (But please understand that I can not provide any support).
- **An AVR8 ISP/JTAG programmer [optional]**<br>
To program AVR8 MCUs I use the [AVR Dragon][tool-avr-dragon]. It can be also used as a debugger and is available within Atmel Studio by default.
### Step 2: Download fifofast
- Clone this repository or hit [Download][git-download] and extract the .zip file.
### Step 3: Browse the project
- **Open the project in Atmel Studio:**<br>
Either double click `fifofast.atsln` or open Atmel Studio and select "File -> Open -> Project/Solution..."
- **Open any file of your interest:**<br>
Select the file in the top right window "Solution Explorer". If the window is not visible, open it by pressing `CTRL + ALT + L` or selecting "View -> Solution Explorer" from the menu.
### Step 4: Run the demo
- **Compile the demo code:**<br>
Press `F7` or select "Build -> Build Solution" from the menu
- **Run the demo code in the simulator:**<br>
Press `CTRL + F5` or select "Debug -> Start Debugging and Break" from the menu
- **Place breakpoints:**<br>
Left-Click on the lightgrey area left of the code to place or remove a breakpoint. Select lines with the comment "easy breakpoint".
- **View variable values:**<br>
When the code is paused, hover over any variable to display its value. Alternately you can "Right-Click -> Watch" to display the value in the "watch window".
- **Run Code:**<br>
Press `F5` to run to the next breakpoint or `F10` to execute one step.
### Step 5: Going further
- **Changing the target device:**<br>
Press `ALT + F7` and select "Device -> Change device..." to select your desired MCU.
- **Program a real device:**<br>
Connect your programmer, press `ALT + F7` and select "Tool". Choose your tool, your programming interface and wire up your MCU. Press `CTRL + ALT + F7` to flash the code to the MCU. Non-official programmers are not supported by Atmel Studio.
<br>
## Documentation
### API
To keep the documentation up-to-date with the least hassle, all configuration options, functions and their arguments are explained in a comment right in front of the declaration. See `fifofast.h` for more information. This section will be updated as soon as this project hits version 1.0.0.
Below you find information about the unusual functions/ macros in this implementation.
#### `_fff_peek()`
The macro `_fff_peek()` allows the user to access any element of the fifo _as if_ it was an array. The first element of the fifo can be accessed with `_fff_peek(fifo, 0)`, the following elements with an incremented index. See the illustration below:
```
array within fifo:
first element v
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ │ │ │ d │ a │ t │ a │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
_fff_peek(fifo, 0) ^ ^ ^ ^
_fff_peek(fifo, 1) ─────┘ │ │
_fff_peek(fifo, 2) ─────────┘ │
_fff_peek(fifo, 3) ─────────────┘
```
`_fff_peek()` is different from the typical `_fff_read()` and `_fff_write()` in multiple ways:
1. The macro can be used as a right side **or** left side argument to read from/ write to a specific location.
2. The data pointers are **not** changed. Reading or writing data will not remove/ add elements to/ from the fifo.
3. The current fifo level is **not** checked, allowing the user to access "empty space", too.
Thus `_fff_peek()` is especially useful for any algorithm that needs to modify present data with minimum overhead. This macro is only marginally slower than a regular array access and significantly faster than copying data to a temporary buffer
#### `_fff_rebase()`
If you receive strings through a serial interface you may want to use a fifo to store them temporally. Once completely received, you may want to use any of the build-in string functions on the stored data. This is not always directly possible. Consider the following case:
A fifo has received multiple chars, which might be stored in the internal array as shown below:
```
array within fifo:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ n │ g │ │ │ s │ t │ r │ i │
└───┴───┴───┴───┴───┴───┴───┴───┘
```
However, the string functions expect a continuous data array without the "wrap" in the middle. To solve this, 0.8.0 adds the macro `_fff_rebase()`. It re-arranges the array, so that the first element is stored in the first position of the array.
```
array within fifo, after _fff_rebase():
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ s │ t │ r │ i │ n │ g │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
```
To get a pointer to the first character, you can use `_fff_peek()`:
```c
char* first_char = &_fff_peek(fifo, 0);
```
**Note:** `_fff_rebase()` iterates across all elements in the array and the execution time increases linearly with depth and element size of the fifo. Use only when necessary.
<br>
### Configuration
fifofast is designed to work out-of-the-box the majority of all use cases. To increase flexibility, but retain the performance for simple applications, you can set configuration options\* in `fifofast.h` in the section _User Config_.
<sub>*As of 0.7.0 there is only one configuration option, but this is the place where other options would go.</sub>
<br>
### Pointable Fifos
Since release 0.6.0 you can create special fifos which can be referenced by a pointer. They are created very similar to normal fifos, but they have a `_p` as a suffix:
```c
// declare pointable fifo
_fff_declare_p(uint8_t, fifo_uint8p, 4);
//init pointable fifo
_fff_init_p(fifo_uint8p);
```
<br>
To access such a fifo you have two options:
1. Pass its pointer to one of the implemented functions OR
2. Use its identifier in a macro
Generic data in C can only be archived with pointers and type casts. This can be very confusing, so let me demonstrate it with examples:
```c
// 'fifo_uint8_p' has its own type depending on data type and depth, but its header looks like fff_proto_t,
// which is the only type excepted by the functions. Therefore you need to convert the pointer first:
uint8_t tmp_is_empty = fff_is_empty((fff_proto_t*)&fifo_uint8_p);
// alternatively you can create a new temporal pointer like this:
fff_proto_t* fifo_pointer = (fff_proto_t*)&fifo_uint8_p;
// to write you need to pass a pointer to the data location. NO type check can be performed!
uint8_t tmp_value = 42;
fff_write(fifo_pointer, &tmp_value);
// if you read data, you will only receive a void pointer. This needs to be cast into a pointer of the right
// type and then de-referenced:
uint8_t tmp_read = *(uint8_t*)fff_peek_read(fifo_pointer, 0); // returns '42'
```
Type conversions are often considered to be an _evil_ feature of C, as it hides all type mismatches. To reduce the chance of bug, **only use these inline functions where absolutly required!**
<br>
### FIFO Arrays
For some applications you may need multiple identical fifos which can be selected with an index.
To create a fifo array, declare its structure first:
```c
// declare an array (suffix _a) of fifos with 16 elements each.
_fff_declare_a(uint8_t, fifo_array, 16, 5);
```
Next initialize it and specify the amount of fifos you need:
```c
// initialize an array (suffix _a) of 5 fifos.
_fff_init_a(fifo_array, 5);
```
Now you can access each fifo like this:
```c
// write to the fifo at index 'fifo_nr' the value 'data'
_fff_write(fifo_array[fifo_nr], data);
```
<br>
### Aligned Data
Because **fifofast** supports any data type, it may also be used to store frames for a serial data transmission. It is often useful to access the data not only in binary (`raw`) format, but also as a struct (`header`):
```c
typedef struct
{
uint16_t control;
uint8_t length;
uint8_t id;
uint8_t data[];
} header_t;
typedef union __attribute__((aligned(2), packed))
{
uint8_t raw[32];
header_t header;
} frame_u;
```
In this case the header variable `control` is 2 bytes large and must be stored aligned
on most architectures. The union however is treated by default as a `uint8_t` array, so no alignment is enforced. To do this manually, GCC supports the [`__attribute__((aligned(n)))`][doc-gcc-alignment]. If a struct or union is declared like this, it will be correctly stored in the fifo.
To align any non-typedef'd data, you can declare a fifo like this:
```c
_fff_declare(uint8_t __attribute__((aligned(4))), fifo_uint8, 4);
```
<br>
## Under the Hood
To use **fifofast** you don't need to know its inner workings. This chapter is for those who seek to understand and learn.
### The Typical Implementation
To get the best performance most fifos are based on an array for data storage. New elements are always placed at the next free index. When the fifo is read, the element of the earliest written index is returned. Example:
```
empty array:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ │ │ │ │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
write 4 elements:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ a │ b │ c │ d │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
read 2 elements:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ │ │ c │ d │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
```
After the array was filled at least once, new data must be added to the very first location. This is called a [circular buffer (wiki)][wiki-circular-buffer]
```
after some time (example):
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ │ │ │ │ │ e │ f │ g │
└───┴───┴───┴───┴───┴───┴───┴───┘
write 1 element:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ h │ │ │ │ │ e │ f │ g │
└───┴───┴───┴───┴───┴───┴───┴───┘
```
To detect this overflow the straight forward approach is to use `if(index > array_size){index = 0;}`. This comparison has to be done for _every_ fifo access. Branches take typically more cycles than arithmetic instruction, especially if a [instruction pipeline (wiki)][wiki-pipelining] is used within the MCU.
<br>
### 2ⁿ Ring Buffer
If the array has a length of 2ⁿ elements, this `if` can be replaced with a faster logic instruction.
```c
// increment the write index by 1
index = ((index+1) & (array_size-1));
```
Let's take the example above and observe the bahavior:
```c
// start values
uint8_t array_size = 8;
uint8_t index = 5;
// first increment v equivilant binary v decimal result
index = ((5+1) & (8-1)); // == (0b0110 & 0b0111) == 0b110 == 6
index = ((6+1) & (8-1)); // == (0b0111 & 0b0111) == 0b111 == 7
index = ((7+1) & (8-1)); // == (0b1000 & 0b0111) == 0b000 == 0
index = ((0+1) & (8-1)); // == (0b0001 & 0b0111) == 0b001 == 1
```
As you can see, the logic operation works exactly as we want! Of course you don't have to deal with this detail, **fifofast** takes care of that for you. For more info look in the [resource](#resources) section.
<br>
### Generic Data
Support for generic data types is not a part of C so **fifofast** has to use a creative work-around with macros. The key are the `_fff_declare(...)` macros:
```c
#define _fff_declare(_type, _id, _depth) \
struct _FFF_NAME_STRUCT(_id) \
{ \
_FFF_GET_TYPE(_depth) read; \
_FFF_GET_TYPE(_depth) write; \
_FFF_GET_TYPE(_depth) level; \
_type data[_FFF_GET_ARRAYDEPTH16(_depth)]; \
} _id
```
Each macro "call" declares a new struct with an individual type and identifier. Therefore each of these struct can have members of a different size. However all structs have identical member identifiers, so if the `_id` known, a member can be accessed with `_id.member` like a normal struct. The compiler keeps track of all datatypes used within the structure and generates appropriate code.
<br>
## Support
### Get Help
**Something doesn't work as expected?** No worries! Just open up a new issue in the [GitHub issue tracker][git-issues]. Please provide all information to reproduce your problem. If you don't have a GitHub account (and can't be bothered to create one,) you can [contact](#contact) me directly.
### Contribute
**Spotted an error?** [Open an issue][git-issues] or submit a pull request.
There is no CONTRIBUTING.md yet, sorry. Contributions will inherit the [license](#license) of this project. If you have any questions, just ask.
<br>
## About
### Status
**This project is currently classified as <a href="https://github.com/nqtronix/git-template/blob/master/badges.md#project-status"><img src="https://img.shields.io/badge/status-active-brightgreen.svg" alt="status: active"></a> :**<br>
_The developers are working on new features, optimisations, documentation or another part of the project. The code will be kept in working condition by updating dependencies, fixing bugs and solving issues with a high priority._
The first version of this fifo was created about a year ago. Since then I've used the macros successfully for multiple projects and different MCU architectures (AVR8, SAMG and SAML). fifofast is activly used for upcoming projects and will receive additional features whenever I need them.
### Known Issues
- **Non-Atomic Glitches:**<br>
Accessing the same fifo from normal _and_ ISR code can cause glitches with some function combinations. To prevent this, put the normal code in an [atomic block][doc-gcc-atomic] if the fifo is also accessed in an ISR.
### Planned Features
- none (for now)
### Changelog
This project uses [**Semantic Versioning 2.0.0**][semver.org]. During initial development (0.x.x versions) any _major_ increase is substituted with a _minor_ increase (0.1.0->0.2.0 instead of 0.1.0->1.0.0).
The message of each commit contains detailed information about the changes made. The list below is a summary about all significant improvements.
- **0.8.0 (latest)**
- implemented `_fff_rebase()` to handle strings better
- **0.7.0**
- improved usage of struct member 'level'
- 'level' contains now real value, even if fifo is full
- demo-code compiles with 25% less flash usage
- less if-statements required, performance therefore increased
- **0.6.0**
- implemented pointable fifos, including tests
- **0.5.0**
- testing now automated with the brand new [unittrace][git-repo-unittrace].
- _finally_ polished up this readme :tada:
- **0.4.0**
- array with several fifos now possible
- aligned data example provided
- **0.3.0**
- complete _and successful_ test of all macros (this time for real)
- **0.2.0**
- ~~complete~~ test of all macros
- changed fifo struct to improve readability
- MIT License added to git
- **0.1.0**
- initial commit
### Contact
If you haven't done so already, please check out [Get Help](#get-help) for the fastest possible help on your issue. Alternatively you can find my public email address on my [profile][git-profile].
<br>
## Credits
### Projects Used
- [**git-template**][git-repo-git-template] - _A simple and clean git repository template._<br>
- [**unittrace**][git-repo-unittrace] - _A simple testing and debugging tool for MCUs inspired by MinUnit_<br>
Specifically written for this project, because testing became to annoying.
<br>
### Related Projects
- none (yet)
Want yours to be listed here, too? Create a merge request or [**get in touch**](#get-help).
<br>
### Additional Resources
- [**2ⁿ circular buffer explanation (German)**][article-2n-buffer] or [English (with google translate)][article-2n-buffer-eng]
- [**GCC: Atomically and Non-Atomically Executed Code Blocks**][doc-gcc-atomic]
- [**GCC: Specifying Attributes of Variables**][doc-gcc-alignment]
<br>
## License
This project is proudly licensed under the [MIT license][git-license].
The MIT license was chosen to give you the freedom to use this project in any way you want, while protecting all contributors from legal claims. Good code works, great code works for everyone. If this code has become a part of one of your projects, a link back to us would be highly appreciated. Thanks!
<!-- Links -->
[semver.org]:https://semver.org/
[git-readme]:README.md
[git-license]:LICENSE.md
[git-profile]:https://github.com/nqtronix
[git-issues]:https://github.com/nqtronix/fifofast/issues
[git-download]:https://github.com/nqtronix/fifofast/archive/master.zip
[git-repo-unittrace]:https://github.com/nqtronix/unittrace/
[git-repo-git-template]:https://github.com/nqtronix/git-template/
[doc-gcc-atomic]:https://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html
[doc-gcc-alignment]:https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Variable-Attributes.html
[article-2n-buffer]:https://www.mikrocontroller.net/articles/FIFO#2n-Ringpuffer_-_die_schnellste_L.C3.B6sung
[article-2n-buffer-eng]:https://translate.google.com/translate?sl=de&tl=en&js=y&prev=_t&hl=de&ie=UTF-8&u=https%3A%2F%2Fwww.mikrocontroller.net%2Farticles%2FFIFO%232n-Ringpuffer_-_die_schnellste_L.C3.B6sung&edit-text=&act=url
[wiki-pipelining]:https://en.wikipedia.org/wiki/Instruction_pipelining
[wiki-circular-buffer]:https://en.wikipedia.org/wiki/Circular_buffer
[tool-atmel-studio-7-0]:https://www.microchip.com/mplab/avr-support/atmel-studio-7
[tool-avr-dragon]:https://www.microchip.com/Developmenttools/ProductDetails/ATAVRDRAGON
================================================
FILE: fifofast.atsln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Atmel Studio Solution File, Format Version 11.00
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "fifofast", "fifofast.cproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|AVR = Debug|AVR
Release|AVR = Release|AVR
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Debug|AVR.ActiveCfg = Debug|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Debug|AVR.Build.0 = Debug|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.ActiveCfg = Release|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.Build.0 = Release|AVR
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
================================================
FILE: fifofast.componentinfo.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<Store xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="AtmelPackComponentManagement">
<ProjectComponents>
<ProjectComponent z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<CApiVersion></CApiVersion>
<CBundle></CBundle>
<CClass>Device</CClass>
<CGroup>Startup</CGroup>
<CSub></CSub>
<CVariant></CVariant>
<CVendor>Atmel</CVendor>
<CVersion>1.0.0</CVersion>
<DefaultRepoPath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs</DefaultRepoPath>
<DependentComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
<Description></Description>
<Files xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.0.90\include</AbsolutePath>
<Attribute></Attribute>
<Category>include</Category>
<Condition>C</Condition>
<FileContentHash i:nil="true" />
<FileVersion></FileVersion>
<Name>include</Name>
<SelectString></SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.0.90\include\avr\iom328p.h</AbsolutePath>
<Attribute></Attribute>
<Category>header</Category>
<Condition>C</Condition>
<FileContentHash>UMk4QUzkkuShabuoYtNl/Q==</FileContentHash>
<FileVersion></FileVersion>
<Name>include/avr/iom328p.h</Name>
<SelectString></SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.0.90\templates\main.c</AbsolutePath>
<Attribute>template</Attribute>
<Category>source</Category>
<Condition>C Exe</Condition>
<FileContentHash>GD1k8YYhulqRs6FD1B2Hog==</FileContentHash>
<FileVersion></FileVersion>
<Name>templates/main.c</Name>
<SelectString>Main file (.c)</SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.0.90\templates\main.cpp</AbsolutePath>
<Attribute>template</Attribute>
<Category>source</Category>
<Condition>C Exe</Condition>
<FileContentHash>YXFphlh0CtZJU+ebktABgQ==</FileContentHash>
<FileVersion></FileVersion>
<Name>templates/main.cpp</Name>
<SelectString>Main file (.cpp)</SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.0.90\gcc\dev\atmega328p</AbsolutePath>
<Attribute></Attribute>
<Category>libraryPrefix</Category>
<Condition>GCC</Condition>
<FileContentHash i:nil="true" />
<FileVersion></FileVersion>
<Name>gcc/dev/atmega328p</Name>
<SelectString></SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
</Files>
<PackName>ATmega_DFP</PackName>
<PackPath>C:/Program Files (x86)/Atmel/Studio/7.0/Packs/atmel/ATmega_DFP/1.0.90/Atmel.ATmega_DFP.pdsc</PackPath>
<PackVersion>1.0.90</PackVersion>
<PresentInProject>true</PresentInProject>
<ReferenceConditionId>ATmega328P</ReferenceConditionId>
<RteComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d4p1:string></d4p1:string>
</RteComponents>
<Status>Resolved</Status>
<VersionMode>Fixed</VersionMode>
<IsComponentInAtProject>true</IsComponentInAtProject>
</ProjectComponent>
</ProjectComponents>
</Store>
================================================
FILE: fifofast.cproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<ProjectVersion>7.0</ProjectVersion>
<ToolchainName>com.Atmel.AVRGCC8.C</ToolchainName>
<ProjectGuid>dce6c7e3-ee26-4d79-826b-08594b9ad897</ProjectGuid>
<avrdevice>ATmega328P</avrdevice>
<avrdeviceseries>none</avrdeviceseries>
<OutputType>Executable</OutputType>
<Language>C</Language>
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
<AssemblyName>fifofast</AssemblyName>
<Name>fifofast</Name>
<RootNamespace>fifofast</RootNamespace>
<ToolchainFlavour>Native</ToolchainFlavour>
<KeepTimersRunning>true</KeepTimersRunning>
<OverrideVtor>false</OverrideVtor>
<CacheFlash>true</CacheFlash>
<ProgFlashFromRam>true</ProgFlashFromRam>
<RamSnippetAddress>0x20000000</RamSnippetAddress>
<UncachedRange />
<preserveEEPROM>true</preserveEEPROM>
<OverrideVtorValue>exception_table</OverrideVtorValue>
<BootSegment>2</BootSegment>
<eraseonlaunchrule>0</eraseonlaunchrule>
<AsfFrameworkConfig>
<framework-data>
<options />
<configurations />
<files />
<documentation help="" />
<offline-documentation help="" />
<dependencies>
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.42.0" />
</dependencies>
</framework-data>
</AsfFrameworkConfig>
<avrtool>com.atmel.avrdbg.tool.simulator</avrtool>
<avrtoolserialnumber />
<avrdeviceexpectedsignature>0x1E950F</avrdeviceexpectedsignature>
<com_atmel_avrdbg_tool_simulator>
<ToolOptions xmlns="">
<InterfaceProperties>
</InterfaceProperties>
</ToolOptions>
<ToolType xmlns="">com.atmel.avrdbg.tool.simulator</ToolType>
<ToolNumber xmlns="">
</ToolNumber>
<ToolName xmlns="">Simulator</ToolName>
</com_atmel_avrdbg_tool_simulator>
<ResetRule>0</ResetRule>
<EraseKey />
<AAFDebugger>
<AAFDebugFiles xmlns="">
<DebugFile>
<path>\Debug\fifofast.lss</path>
<AAFSetting>
<Label>Lss Files</Label>
<Extention>.lss</Extention>
<Regex>^\s*(?<address>[a-f0-9]*):\s*.*$</Regex>
<DebugEnabled>true</DebugEnabled>
<RegexGroups>address</RegexGroups>
<DebuggerExpression>$pc</DebuggerExpression>
</AAFSetting>
</DebugFile>
</AAFDebugFiles>
</AAFDebugger>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<ToolchainSettings>
<AvrGcc>
<avrgcc.common.Device>-mmcu=atmega328p -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.0.90\gcc\dev\atmega328p"</avrgcc.common.Device>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>NDEBUG</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.0.90\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcc.linker.libraries.Libraries>
</AvrGcc>
</ToolchainSettings>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<ToolchainSettings>
<AvrGcc>
<avrgcc.common.Device>-mmcu=atmega328p -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.0.90\gcc\dev\atmega328p"</avrgcc.common.Device>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>DEBUG</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.0.90\include</Value>
<Value>../subrepos</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcc.linker.libraries.Libraries>
<avrgcc.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcc.assembler.debugging.DebugLevel>
</AvrGcc>
</ToolchainSettings>
</PropertyGroup>
<ItemGroup>
<Compile Include="fifofast.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="fifofast_demo.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="fifofast_demo.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="fifofast_test.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="fifofast_test.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="subrepos\unittrace\unittrace.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="subrepos\unittrace\unittrace.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="subrepos\unittrace\utility\macros\com\macro_type.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="utility\macros\com\macro_array.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="utility\macros\com\macro_math.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="utility\macros\com\macro_type.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="utility\macros\mpl\macro_cat.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="utility\macros\mpl\macro_narg.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="utility\macros\mpl\macro_vfunc.h">
<SubType>compile</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="subrepos" />
<Folder Include="subrepos\unittrace\" />
<Folder Include="subrepos\unittrace\utility\" />
<Folder Include="subrepos\unittrace\utility\macros\" />
<Folder Include="subrepos\unittrace\utility\macros\com\" />
<Folder Include="utility\" />
<Folder Include="utility\macros\" />
<Folder Include="utility\macros\com\" />
<Folder Include="utility\macros\mpl\" />
</ItemGroup>
<ItemGroup>
<None Include="LICENSE.md">
<SubType>compile</SubType>
</None>
<None Include="README.md">
<SubType>compile</SubType>
</None>
<None Include="subrepos\readme %28license info%29.txt">
<SubType>compile</SubType>
</None>
<None Include="subrepos\unittrace\LICENSE.md">
<SubType>compile</SubType>
</None>
<None Include="subrepos\unittrace\README.md">
<SubType>compile</SubType>
</None>
<None Include="subrepos\unittrace\utility\macros\com\com readme.txt">
<SubType>compile</SubType>
</None>
<None Include="utility\macros\com\com readme.txt">
<SubType>compile</SubType>
</None>
<None Include="utility\macros\mpl\mpl readme.txt">
<SubType>compile</SubType>
</None>
<None Include="utility\macros\readme.txt">
<SubType>compile</SubType>
</None>
</ItemGroup>
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
</Project>
================================================
FILE: fifofast.h
================================================
/*
* fifofast.h
*
* Created: 23.10.2017 06:45:20
* Author: Dennis aka nqtronix (github.com/nqtronix)
*
* Description:
* Most libraries for data structures are designed for dynamically allocating memory, which can't be
* efficiently used on small, low-cost, embedded devices.
* This library implements the common fifo data structure based on function-like macros, which
* allows the code to be generic for all data types and buffer sizes. All memory is allocated at
* compile time. As a side effect the execution time for most functions and RAM memory consumption
* is reduced, but the program size slightly increased.
*
* Some programs may need to access multiple fifos, e.g. to distribute measured ADC values to their
* corresponding handler functions. For theses applications you can create fifos, which can be safely
* passed by pointers. Although these fifos can be access by all function-like macros, they can't be
* as generic and are limited to user selectable depth 'FIFOFAST_MAX_DEPTH_POINTABLE'.
*
* If any fifo is accessed from ISRs make sure all access to the involved fifo from normal code is
* atomic! This is important to prevent glitches in the middle of an operation!
*/
#ifndef FIFOFAST_H_
#define FIFOFAST_H_
#include <stdint.h> // required for data types (uint8_t, uint16_t, ...)
#include <stddef.h> // required for "NULL"
#include <string.h> // required for memcopy
// include required macros
// by default, the project's macros will be used, they have to be located at:
// "utility/macros/..."
//
// If your project has no global macros, you can re-direct GCC to the included files:
// a) in Atmel Studio edit project properties (ALT+F7 OR "Project -> <project name> Properties...")
// b) Go to "Toolchain / AVR/GNU C Compiler / Directories"
// c) add the relative path to: "../src/subrepo/fifofast/" or wherever this code is located. DONE!
//
// YOU DO NOT need to change the include(s) below.
#include "utility/macros/mpl/macro_cat.h"
#include "utility/macros/com/macro_array.h"
#include "utility/macros/com/macro_math.h"
#include "utility/macros/com/macro_type.h"
//////////////////////////////////////////////////////////////////////////
// User Config
//////////////////////////////////////////////////////////////////////////
// defines the maximum depth of pointable fifos. The performance of fifofast drops with increased
// maximum depth in discrete steps, especially on 8bit MCUs. 32bit MCUs (such as ARM cortex) are
// mostly unaffected, except they require slightly more RAM. If the value is NOT a 2^n value, it
// will automatically be rounded up.
//
// depth | performance
// -----------------+-------------
// 4 <= x <= 128 | best
// x == 256 | ok
// 512 <= x | slow
#define FIFOFAST_MAX_DEPTH_POINTABLE 128
//////////////////////////////////////////////////////////////////////////
// General Info
//////////////////////////////////////////////////////////////////////////
// version numbering is based on "Semantic Versioning 2.0.0" (semver.org)
#define FIFOFAST_VERSION_MAJOR 0
#define FIFOFAST_VERSION_MINOR 8
#define FIFOFAST_VERSION_PATCH 0
#define FIFOFAST_VERSION_SUFFIX
#define FIFOFAST_VERSION_META
// For all development versions (0.x.x) the patch version is increased whenever a function was renamed
//////////////////////////////////////////////////////////////////////////
// Check requirements
//////////////////////////////////////////////////////////////////////////
#ifndef __GNUC__
#error fifofast.h requires "compound statments" and "typeof" offered by a GNU C/ GCC compiler!
#endif
#ifndef __OPTIMIZE__
#pragma message "fifofast.h is intended to be compiled with optimisation and will run VERY SLOWLY without!"
#endif
//////////////////////////////////////////////////////////////////////////
// internal macros (_FFF_*)
//////////////////////////////////////////////////////////////////////////
// rounds up given argument to next 2^n value. Used to deal with invalid user input.
#define ROUND_UP_2N(_arg) ((uint64_t)1<<(_log2(_arg-1)+1))
// returns the structure name matching to given ID without the keyword 'struct'
#define _FFF_NAME_STRUCT(_id) CAT(fff_, _id, _s)
// returns matching type for internal index values; fifo constrains are automatically applied
#define _FFF_GET_TYPE(_depth) _type_min(_limit_lo(_depth,4)-1)
#define _FFF_SIZEOF_DATA(_id) sizeof(((struct _FFF_NAME_STRUCT(_id)*)0)->data[0])
#define _FFF_SIZEOF_ARRAY(_id) _sizeof_array(((struct _FFF_NAME_STRUCT(_id)*)0)->data)
// returns the length of the data array; fifo constrains are automatically applied
#define _FFF_GET_ARRAYDEPTH(_depth) _limit(ROUND_UP_2N(_depth), 4, ((uint32_t)1<<31))
#define _FFF_GET_ARRAYDEPTH_P(_depth) _limit(ROUND_UP_2N(_depth), 4, ROUND_UP_2N(FIFOFAST_MAX_DEPTH_POINTABLE))
//////////////////////////////////////////////////////////////////////////
// Data Structures (for inline functions only)
//////////////////////////////////////////////////////////////////////////
// extra #defines prevent VAssitX from marking the type red (because it doesn't understand 'typeof')
#define FIFOFAST_INDEX_T _type_min(ROUND_UP_2N(FIFOFAST_MAX_DEPTH_POINTABLE)-1)
#define FIFOFAST_LEVEL_T _type_min(ROUND_UP_2N(FIFOFAST_MAX_DEPTH_POINTABLE))
typedef FIFOFAST_INDEX_T fff_index_t;
typedef FIFOFAST_LEVEL_T fff_level_t;
typedef struct
{
const fff_index_t data_size; // bytes per element in data array
const fff_index_t mask; // (max amount of elements in data array) - 1
fff_index_t read; // index from which to read next element
fff_index_t write; // index to which to write next element
fff_level_t level; // current amount of stored data. Is larger than 'mask', if full
uint8_t data[]; // data storage array
} fff_proto_t;
//////////////////////////////////////////////////////////////////////////
// Function Declarations (User)
//////////////////////////////////////////////////////////////////////////
// these functions behave as their corresponding macros, so please refer to their description
// for infos on usage.
static inline fff_index_t fff_mem_mask(fff_proto_t *fifo) __attribute__((__always_inline__));
static inline fff_level_t fff_mem_level(fff_proto_t *fifo) __attribute__((__always_inline__));
static inline fff_index_t fff_mem_free(fff_proto_t *fifo) __attribute__((__always_inline__));
static inline fff_index_t fff_data_size(fff_proto_t *fifo) __attribute__((__always_inline__));
static inline uint8_t fff_is_empty(fff_proto_t *fifo) __attribute__((__always_inline__));
static inline uint8_t fff_is_full(fff_proto_t *fifo) __attribute__((__always_inline__));
static inline void fff_reset(fff_proto_t *fifo) __attribute__((__always_inline__));
static inline void fff_remove(fff_proto_t *fifo, fff_level_t amount) __attribute__((__always_inline__));
static inline void fff_remove_lite(fff_proto_t *fifo, fff_level_t amount) __attribute__((__always_inline__));
static inline void fff_write(fff_proto_t *fifo, void *data) __attribute__((__always_inline__));
static inline void fff_write_lite(fff_proto_t *fifo, void *data) __attribute__((__always_inline__));
static inline void* fff_peek_read(fff_proto_t *fifo, fff_index_t idx) __attribute__((__always_inline__));
static inline void fff_peek_write(fff_proto_t *fifo, fff_index_t idx, void *data) __attribute__((__always_inline__));
//////////////////////////////////////////////////////////////////////////
// Function Declarations (Internal)
//////////////////////////////////////////////////////////////////////////
static inline fff_index_t fff_wrap(fff_proto_t *fifo, fff_index_t idx) __attribute__((__always_inline__));
static inline void* fff_data_p(fff_proto_t *fifo, fff_index_t idx) __attribute__((__always_inline__));
//////////////////////////////////////////////////////////////////////////
// user macros (_fff_*)
//////////////////////////////////////////////////////////////////////////
// all function-like macros are suitable for ANY fifo, independent of data type or size.
// declares semi-anonymous fifofast structure
// semi-anonymous means it appears anonymous for the user as it is derived from the '_id' whenever
// needed, but is not anonymous on compiler level. This has the additional benefit of VAssisX and
// debugging (inspecting variables during runtime with an external debugger) working as usual. The
// fifo shall only be access through macros or functions provided in this file.
//
// The variant _fff_declare_p(...) declares a structure which includes the size of each element and
// the maximum amount of elements and can thus be used by the inline functions. They still can be
// accessed with all function-like macros provided for some speed gain.
//
// The variant_fff_declare_pa(...) declares an array with structures as declared by _fff_declare_p(...).
//
//
// _id: C conform identifier
// _type: any C type except pointers and structs. To store pointers or structs use typedef first
// _depth: maximum amount of elements, which can be stored in the fifo. Only values of 2^n are
// possible. If another value is passed the next larger value will be automatically
// selected. The amount of additional RAM required increases in discrete steps:
// depth (elements) | RAM (bytes)
// ---------------------+-------------
// 4 <= x <= 128 | 3
// x == 256 | 4
// 512 <= x <= 32768 | 6
// x == 65536 | 8
// 131072 <= x | 12
#define _fff_declare(_type, _id, _depth) \
struct _FFF_NAME_STRUCT(_id) { \
_FFF_GET_TYPE(_depth) read; \
_FFF_GET_TYPE(_depth) write; \
_FFF_GET_TYPE(_depth+1) level; \
_type data[_FFF_GET_ARRAYDEPTH(_depth)]; \
} _id
#define _fff_declare_p(_type, _id, _depth) \
struct _FFF_NAME_STRUCT(_id) { \
const fff_index_t data_size; \
const fff_index_t mask; \
fff_index_t read; \
fff_index_t write; \
fff_level_t level; \
_type data[_FFF_GET_ARRAYDEPTH_P(_depth)]; \
} _id
// declares an array with '_size' fifos. '_size' can be any positive integer.
#define _fff_declare_a(_type, _id, _depth, _size) _fff_declare(_type, _id, _depth) [_size]
#define _fff_declare_pa(_type, _id, _depth, _size) _fff_declare_p(_type, , _depth) [_size]
// initializes the fifo with the name '<_id>'
// This initialization function is technically identical to the term "definition" in c, but to
// prevent confusion with "#define" it has been named '_fff_init()'. Since it is a definition it
// can be only called once. Use '_fff_reset()' to reset any fifo back to it's original state.
//
// The variants '_fff_init_p(_id)' and '_fff_init_pa(_id, _arraysize)' are intended for the
// respective declarations.
#define _fff_init(_id) \
struct _FFF_NAME_STRUCT(_id) _id = \
{ \
0, \
0, \
0, \
{} \
}
#define _fff_init_a(_id, _arraysize) \
struct _FFF_NAME_STRUCT(_id) _id [] = \
{[0 ... _arraysize-1] = { \
0, \
0, \
0, \
{} \
}}
#define _fff_init_p(_id) \
struct _FFF_NAME_STRUCT(_id) _id = \
{ \
_FFF_SIZEOF_DATA(_id), \
_FFF_SIZEOF_ARRAY(_id)-1, \
0, \
0, \
0, \
{} \
}
#define _fff_init_pa(_id, _arraysize) \
struct _FFF_NAME_STRUCT(_id) _id [] = \
{[0 ... _arraysize-1] = { \
_FFF_SIZEOF_DATA(_id), \
_FFF_SIZEOF_ARRAY(_id)-1, \
0, \
0, \
0, \
{} \
}}
// masks a given index value based on a given fifo
// This macro is used to simplify other marcos below; the end user will likely never need it
// _id: C conform identifier
// idx: the index value to mask. MUST be larger than -_sizeof_array(_id.data)
#define _fff_wrap(_id, idx) ((idx) & _fff_mem_mask(_id))
// returns the maximum amount of data elements which can be stored in the fifo
// The returned value is calculated at compile time and thus a constant. No atomic access is needed.
// NOTE: For a fifo of size 256 a "256" is returned and thus does not fit into uint8_t
// _id: C conform identifier
#define _fff_mem_depth(_id) (_sizeof_array(_id.data))
#define _fff_mem_mask(_id) (_sizeof_array(_id.data)-1)
// returns the amount of bytes per data element
// The returned value is calculated at compile time and thus a constant. No atomic access is needed.
// _id: C conform identifier
#define _fff_data_size(_id) (sizeof(_id.data[0]))
// returns !0 if empty
#define _fff_is_empty(_id) (_id.level == 0)
// returns !0 if full
#define _fff_is_full(_id) (_id.level > _fff_mem_mask(_id))
// returns the current fill level of the fifo (the amount of elements that can be read)
// _id: C conform identifier
#define _fff_mem_level(_id) (_id.level)
// returns the current free space of the fifo (the amount of elements that can be written)
// _id: C conform identifier
#define _fff_mem_free(_id) (_fff_mem_depth(_id) - _id.level)
// clears/ resets buffer completely
// _id: C conform identifier
#define _fff_reset(_id) do{_id.read=0; _id.write=0; _id.level=0;} while (0)
// removes a certain number of elements or less, if not enough elements are available.
// This function is especially useful after data has been used by _fff_peek(...)
// _id: C conform identifier
// amount: Amount of elements which will be removed, amount >= 0 (positive integer)
#define _fff_remove(_id, amount) \
do{ \
typeof(_id.level) _amount = amount; \
if(amount > _id.level) \
_amount = _id.level; \
_fff_remove_lite(_id, _amount); \
}while(0)
// removes a certain number of elements. The user must ensure that the given amount of elements can
// be removed; values larger than _fff_depth(_id) are invalid! If you require argument checking use
// _fff_remove().
// This function is especially useful after data has been used by _fff_peek(...)
// _id: C conform identifier
// amount: Amount of elements which will be removed; must be 0 <= amount <= _fff_mem_level(_id);
#define _fff_remove_lite(_id, amount) \
do{ \
_id.level -= amount; \
_id.read = _fff_wrap(_id, _id.read+amount); \
}while(0)
// returns the next element from the fifo and removes it from the memory
// Use if(!_fff_is_empty(_id)) if amount of stored data is unknown
// _id: C conform identifier
#define _fff_read_lite(_id) \
({ \
typeof(_id.data[0]) _return; \
_id.level--; \
_return = _id.data[_id.read]; \
_id.read = _fff_wrap(_id, (_id.read+1)); \
_return; \
})
// returns the next element from the fifo and removes it from the memory
// if no element is available, 0 is returned
// _id: C conform identifier
#define _fff_read(_id) \
({ \
typeof(_id.data[0]) _return = (typeof(_id.data[0])){0}; \
if(!_fff_is_empty(_id)) \
_return = _fff_read_lite(_id); \
_return; \
})
// adds an element to the fifo
// Use if(!_fff_is_full(_id)) if amount of stored data is unknown
// _id: C conform identifier
// newdata: data to be written
#define _fff_write_lite(_id, newdata) \
do{ \
_id.data[_id.write] = (newdata); \
_id.write = _fff_wrap(_id, (_id.write+1)); \
_id.level++; \
}while(0)
// adds an element to the fifo, if space is available
// if full element will be dismissed
// _id: C conform identifier
// newdata: data to be written
#define _fff_write(_id, newdata) \
do{ \
if(!_fff_is_full(_id)) \
_fff_write_lite(_id, newdata); \
}while(0)
// copies an array of elements to the fifo as long as space is available
// if full all excess elements will be dismissed
// _id: C conform identifier
// newdata: array of data to be written
// n: amount of data do be written
#define _fff_write_multiple(_id, newdata, n) \
do{ \
typeof(_id.level) tocopy, btw; \
btw = _min(_fff_mem_free(_id), (n)); \
if (btw == 0) { \
break; \
} \
tocopy = _min(btw, _fff_mem_depth(_id) - _id.write); \
memcpy(&_id.data[_id.write], (newdata), tocopy); \
_id.level += tocopy; \
_id.write = _fff_wrap(_id, (_id.write+tocopy)); \
btw -= tocopy; \
if (btw > 0) { \
memcpy(&_id.data[_id.write], (newdata)+tocopy, btw); \
_id.write = btw; \
_id.level += btw; \
} \
}while(0)
// adds an element to the fifo, but does not write any data to it. instead, a pointer to the data
// section is returned. The caller may write up to _fff_data_size(_id) bytes to this location.
// Use if(!_fff_is_full(_id)) if amount of stored data is unknown
// _id: C conform identifier
#define _fff_add_lite(_id) \
({ \
typeof(&_id.data[0]) _return = & _id.data[_id.write]; \
_id.write = _fff_wrap(_id, (_id.write+1)); \
_id.level++; \
_return; \
})
// like _fff_add_lite(_id), but checks if space is available before writing. Returns 'null' if full.
// _id: C conform identifier
#define _fff_add(_id) \
({ \
typeof(&_id.data[0]) _return = (typeof(&_id.data[0]))NULL; \
if(!_fff_is_full(_id)) \
_return = _fff_add_lite(_id); \
_return; \
})
// allows accessing the data of a fifo as an array without removing any elements
// Like any array this function can be used as a right or left site operant. Attempting to access
// more elements than currently in the buffer will return undefined data on read and will have no
// effect on write. Accidental read/write operations outside the assigned data space are not
// possible.
// This macro is NOT ATOMIC. If fifo "_id" is accessed within an ISR at least once, this macro MUST
// be placed within an atomic block outside of any ISR.
// _id: C conform identifier
// idx: Offset from the first element in the buffer
#define _fff_peek(_id, idx) _id.data[_fff_wrap(_id, _id.read+(idx))]
// re-writes the internal array, so that the element _fff_peek(0) will be at the physical idx 0
// Although this does not effect any of the fifo functions, it does simplify operations on string
// stored in the fifo.
// NOTE that this macro copies element-by-element and might take very long for large fifos with many
// elements or large data sizes.
//
// Macro inspired by Jon Bentley's array rotation algorithm and this stackoverflow answer:
// https://stackoverflow.com/a/22079960/6215916
//
// This version has only one call of _FFF_REVERSE to safe program memory
#define _fff_rebase(_id) \
do{ \
/* check if rebase required */ \
if (_id.read == 0) \
break; \
\
typeof(_id.read) idx1, idx2; \
\
/* reversing 1st half, 2nd half and everything together */ \
/* rotates the array */ \
for (uint8_t rep = 0; rep<3; rep++) \
{ \
switch (rep) \
{ \
default: \
case 0: \
idx1 = 0; \
idx2 = _id.read-1; \
break; \
case 1: \
idx1 = _id.read; \
idx2 = _fff_mem_mask(_id); \
break; \
case 2: \
idx1 = 0; \
idx2 = _fff_mem_mask(_id); \
break; \
} \
\
/* reverse section from idx1 to idx2 */ \
for (; idx1 < idx2; idx1++, idx2--) \
{ \
typeof(_id.data[0]) tmp; \
tmp = _id.data[idx1]; \
_id.data[idx1] = _id.data[idx2]; \
_id.data[idx2] = tmp; \
} \
} \
\
/* Update data indices */ \
_id.write = _id.write - _id.read; \
_id.read = 0; \
}while(0)
//////////////////////////////////////////////////////////////////////////
// Inline functions
//////////////////////////////////////////////////////////////////////////
// Inline functions MUST be defined in the .h, not in the .c file to work correctly!
// auxiliary functions
static inline fff_index_t fff_wrap(fff_proto_t *fifo, fff_index_t idx)
{
return (idx & fifo->mask);
}
static inline void* fff_data_p(fff_proto_t *fifo, fff_index_t idx)
{
return &(fifo->data[idx * fifo->data_size]);
}
static inline fff_index_t fff_mem_mask(fff_proto_t *fifo)
{
return (fifo->mask);
}
static inline fff_index_t fff_data_size(fff_proto_t *fifo)
{
return fifo->data_size;
}
//
static inline uint8_t fff_is_empty(fff_proto_t *fifo)
{
return (fifo->level == 0);
}
static inline uint8_t fff_is_full(fff_proto_t *fifo)
{
return (fifo->level > fifo->mask);
}
static inline fff_level_t fff_mem_level(fff_proto_t *fifo)
{
return (fifo->level);
}
static inline fff_index_t fff_mem_free(fff_proto_t *fifo)
{
return (fifo->mask - fifo->level + 1);
}
//
static inline void fff_reset(fff_proto_t *fifo)
{
fifo->read = 0;
fifo->write = 0;
fifo->level = 0;
}
static inline void fff_remove(fff_proto_t *fifo, fff_level_t amount)
{
if (amount > fifo->level)
amount = fifo->level;
fff_remove_lite(fifo, amount);
}
static inline void fff_remove_lite(fff_proto_t *fifo, fff_level_t amount)
{
fifo->level -= amount;
fifo->read = fff_wrap(fifo, fifo->read + amount);
}
static inline void fff_write(fff_proto_t *fifo, void *data)
{
if (!fff_is_full(fifo))
fff_write_lite(fifo,data);
}
static inline void fff_write_lite(fff_proto_t *fifo, void *data)
{
memcpy(fff_data_p(fifo, fifo->write), data, fifo->data_size);
fifo->write = fff_wrap(fifo, fifo->write+1);
fifo->level++;
}
// the peek function MUST be split into two to work as a normal c function
// BOTH function STILL refer to the top (read) end of the fifo
static inline void* fff_peek_read(fff_proto_t *fifo, fff_index_t idx)
{
return fff_data_p(fifo, fff_wrap(fifo, fifo->read+idx));
}
static inline void fff_peek_write(fff_proto_t *fifo, fff_index_t idx, void *data)
{
memcpy(fff_data_p(fifo, fff_wrap(fifo, fifo->read+idx)), data, fifo->data_size);
}
#endif /* FIFOFAST_H_ */
================================================
FILE: fifofast_demo.c
================================================
/*
* fifofast_demo.c
*
* Created: 17.09.2018 19:21:17
* Author: Dennis aka nqtronix (github.com/nqtronix)
*
* Description:
* This file contains some demo code to try out the provided macros. Simply run it in the simulator
* and inspect the step-by-step changes with the debugger.
*/
#include "fifofast_demo.h"
#include "fifofast_test.h"
int main(void)
{
//////////////////////////////////////////////////////////////////////////
// Test Environment
//////////////////////////////////////////////////////////////////////////
// Note: only fifo_uint8 is tested here as uint8 are most easy to work with
// after each change (or set of changes) call all returning functions
//
// The compiler MAY optimize these test strongly, and may not generate all assembler code.
// To debug the tests, please disable optimization.
fifofast_test_macro_initial();
fifofast_test_macro_write(0x10);
fifofast_test_macro_peek(0x20);
fifofast_test_macro_read(0x30);
fifofast_test_macro_add(0x40);
fifofast_test_macro_remove_lite(0x50);
fifofast_test_macro_remove(0x60);
fifofast_test_macro_rebase(0x70);
fifofast_test_macro_write_multiple(0x80);
fifofast_test_func_initial((fff_proto_t*)&fifo_uint8p);
fifofast_test_func_write((fff_proto_t*)&fifo_uint8p, 0x80);
fifofast_test_func_peek((fff_proto_t*)&fifo_uint8p, 0x90);
fifofast_test_func_remove_lite((fff_proto_t*)&fifo_uint8p, 0xa0);
fifofast_test_func_remove((fff_proto_t*)&fifo_uint8p, 0xb0);
UT_BREAK();
//////////////////////////////////////////////////////////////////////////
// Demonstrate usage of fifo array
//////////////////////////////////////////////////////////////////////////
// macros have been proved to work, so to see the result just look into the SRAM directly
// These two loops copy the printable chars into the sram, each fifo gets a section of 16
// consecutive characters.
uint8_t data = ' ';
for (uint8_t fifo_nr = 0; fifo_nr < _sizeof_array(fifo_array); fifo_nr++)
{
for (uint8_t idx = 0; idx < _fff_mem_depth(fifo_array[0]); idx++)
{
_fff_write(fifo_array[fifo_nr], data);
data += 1;
}
}
// End simulation
UT_BREAK();
while (1);
}
================================================
FILE: fifofast_demo.h
================================================
/*
* fifofast_demo.h
*
* Created: 08.11.2018 08:46:08
* Author: Dennis
*
* Description:
* This file declares all fifos used by the demo. If declared in a header, they can be accessed by
* all files, that include this header. In this particular case it is needed for 'fifofast_test.c'.
* If a fifo does NOT need to be access from multiple files (i.e. if used as a buffer for ISRs),
* they can be declared in a c file.
*/
#ifndef FIFOFAST_DEMO_H_
#define FIFOFAST_DEMO_H_
#include "fifofast.h"
// declare a fifo with 4 elements of type 'uint8_t' with the name 'fifo_uint8'
_fff_declare(uint8_t __attribute__((aligned(4))), fifo_uint8, 4);
// declare same fifo as above, but it can be passed by pointer to functions
_fff_declare_p(uint8_t, fifo_uint8p, 4);
// declare a fifo with 8 elements (6 elements is not possible, so it is automatically bumped to 8)
// of type 'int_16' with the name 'fifo_uint16'
_fff_declare(int16_t, fifo_int16, 6);
// fifofast also supports more complicated data such as frames used for serial data transmission.
// It is often useful to access the data not only in binary format ('raw'), but also as a struct
// ('header'). In this case the header contains a variable >1 byte, which needs to be stored aligned
// on most architectures. However the union is treaded by default as a 'uint8_t' array, so no
// alignment is enforced. To do this manually, GCC supports the attribute 'aligned(n)'. See
// documentation of GCC for more details.
typedef struct
{
uint16_t control;
uint8_t length;
uint8_t id;
uint8_t data[];
} header_t;
typedef union __attribute__((aligned(4), packed))
{
uint8_t raw[32];
header_t header;
} frame_u;
// declare a fifo to store 4 elements of the typedef'd union 'frame_u'
_fff_declare(frame_u, fifo_frame, 4);
// declare an array (indicated by the suffix _a) of 5 fifos with 16 elements each.
_fff_declare_a(uint8_t, fifo_array, 16, 5);
#endif /* FIFOFAST_DEMO_H_ */
================================================
FILE: fifofast_test.c
================================================
/*
* fifofast_test.c
*
* Created: 08.11.2018 08:34:58
* Author: Dennis
*/
#include "fifofast_test.h"
///////////////////////////////////////////////////////////////////////////////
// Initialize fifos for global access
///////////////////////////////////////////////////////////////////////////////
// initialize all fifos
_fff_init(fifo_uint8);
_fff_init_p(fifo_uint8p);
_fff_init(fifo_int16);
_fff_init(fifo_frame);
_fff_init_a(fifo_array, 5);
//////////////////////////////////////////////////////////////////////////
// Test Macros
//////////////////////////////////////////////////////////////////////////
void fifofast_test_macro_initial()
{
UT_ASSERT(_fff_mem_depth(fifo_uint8) == 4); // constant
UT_ASSERT(_fff_mem_mask(fifo_uint8) == 3); // constant = 0b11
UT_ASSERT(_fff_mem_level(fifo_uint8) == 0);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 4);
UT_ASSERT(_fff_is_empty(fifo_uint8) != 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
}
void fifofast_test_macro_write(uint8_t startvalue)
{
// add 2 values with the fast '_lite' variant (after init we know it is empty)
// use unusual values (NOT 0, 1, 2 ...) to decrease the likelihood of false positives
_fff_write_lite(fifo_uint8, startvalue+0);
_fff_write_lite(fifo_uint8, startvalue+1);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 2);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 2);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+0);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+1);
UT_BREAK();
// add 3 values to demonstrate overflow safety
_fff_write(fifo_uint8, startvalue+2);
_fff_write(fifo_uint8, startvalue+3);
_fff_write(fifo_uint8, startvalue+4);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 4);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 0);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) != 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+0);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+1);
UT_ASSERT(_fff_peek(fifo_uint8, 2) == startvalue+2);
UT_ASSERT(_fff_peek(fifo_uint8, 3) == startvalue+3);
_fff_reset(fifo_uint8);
// Test reset once only
UT_ASSERT(_fff_mem_level(fifo_uint8) == 0);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 4);
UT_ASSERT(_fff_is_empty(fifo_uint8) != 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
}
void fifofast_test_macro_peek(uint8_t startvalue)
{
_fff_write_lite(fifo_uint8, startvalue+0);
_fff_write_lite(fifo_uint8, startvalue+1);
_fff_write_lite(fifo_uint8, startvalue+2);
// modify existing value (index 0 and 2) with _fff_peek()
_fff_peek(fifo_uint8, 0) = startvalue+3;
_fff_peek(fifo_uint8, 2) = startvalue+4;
UT_ASSERT(_fff_mem_level(fifo_uint8) == 3);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 1);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+3);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+1);
UT_ASSERT(_fff_peek(fifo_uint8, 2) == startvalue+4);
// demonstrate "out of bounds" safety (no over-read/ over-write possible)
UT_ASSERT(_fff_peek(fifo_uint8, 4) == startvalue+3);
_fff_reset(fifo_uint8);
}
void fifofast_test_macro_read(uint8_t startvalue)
{
_fff_write_lite(fifo_uint8, startvalue+0);
_fff_write_lite(fifo_uint8, startvalue+1);
_fff_write_lite(fifo_uint8, startvalue+2);
// read 2 values with the fast '_lite' variant (we know we have more than two entries)
UT_ASSERT(_fff_read_lite(fifo_uint8) == startvalue+0);
UT_ASSERT(_fff_read_lite(fifo_uint8) == startvalue+1);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 1);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 3);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+2);
// read 2 values to demonstrate overflow safety
UT_ASSERT(_fff_read(fifo_uint8) == startvalue+2);
UT_ASSERT(_fff_read(fifo_uint8) == 0x00);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 0);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 4);
UT_ASSERT(_fff_is_empty(fifo_uint8) != 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
_fff_reset(fifo_uint8);
}
void fifofast_test_macro_add(uint8_t startvalue)
{
__attribute__ ((unused)) volatile uint8_t* dbg_p0 = 0;
__attribute__ ((unused)) volatile uint8_t* dbg_p1 = 0;
__attribute__ ((unused)) volatile uint8_t* dbg_p2 = 0;
// add 2 values with the fast '_lite' variant (after init we know it is empty)
dbg_p0 = _fff_add_lite(fifo_uint8);
dbg_p1 = _fff_add_lite(fifo_uint8);
UT_ASSERT(dbg_p0 != 0);
UT_ASSERT(dbg_p1 != 0);
if(dbg_p0 != 0) *dbg_p0 = startvalue+0;
if(dbg_p1 != 0) *dbg_p1 = startvalue+1;
UT_ASSERT(_fff_mem_level(fifo_uint8) == 2);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 2);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+0);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+1);
// add 3 values to demonstrate overflow safety
dbg_p0 = _fff_add(fifo_uint8);
dbg_p1 = _fff_add(fifo_uint8);
dbg_p2 = _fff_add(fifo_uint8);
UT_ASSERT(dbg_p0 != 0);
UT_ASSERT(dbg_p1 != 0);
UT_ASSERT(dbg_p2 == 0); // the fifo is full, so a null pointer must be returned
if(dbg_p0 != 0) *dbg_p0 = startvalue+2;
if(dbg_p1 != 0) *dbg_p1 = startvalue+3;
if(dbg_p2 != 0) *dbg_p2 = startvalue+4;
UT_ASSERT(_fff_mem_level(fifo_uint8) == 4);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 0);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) != 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+0);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+1);
UT_ASSERT(_fff_peek(fifo_uint8, 2) == startvalue+2);
UT_ASSERT(_fff_peek(fifo_uint8, 3) == startvalue+3);
_fff_reset(fifo_uint8);
}
void fifofast_test_macro_remove_lite(uint8_t startvalue)
{
// fill with any data (macros have been proved to work before)
_fff_write_lite(fifo_uint8, startvalue+0);
_fff_write_lite(fifo_uint8, startvalue+1);
_fff_write_lite(fifo_uint8, startvalue+2);
_fff_write_lite(fifo_uint8, startvalue+3);
// fifo is now full
// _remove 2 (Test case: fifo full before macro)
_fff_remove_lite(fifo_uint8, 2);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 2);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 2);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+2);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+3);
// _remove 1 (Test case: fifo not full before and not empty after macro)
_fff_remove_lite(fifo_uint8, 1);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 1);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 3);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+3);
// _remove 1 (Test case: fifo empty after macro)
_fff_remove_lite(fifo_uint8, 1);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 0);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 4);
UT_ASSERT(_fff_is_empty(fifo_uint8) != 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
_fff_reset(fifo_uint8);
}
void fifofast_test_macro_remove(uint8_t startvalue)
{
// fill with any data (macros have been proved to work before)
_fff_write_lite(fifo_uint8, startvalue+0);
_fff_write_lite(fifo_uint8, startvalue+1);
_fff_write_lite(fifo_uint8, startvalue+2);
_fff_write_lite(fifo_uint8, startvalue+3);
// fifo is now full
// _remove 0 (Test case: amount == 0 elements)
_fff_remove(fifo_uint8, 0);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 4);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 0);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) != 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+0);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+1);
UT_ASSERT(_fff_peek(fifo_uint8, 2) == startvalue+2);
UT_ASSERT(_fff_peek(fifo_uint8, 3) == startvalue+3);
// _remove 4 (Test case: amount == _fff_mem_level() w/ fifo full)
_fff_remove(fifo_uint8, 4);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 0);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 4);
UT_ASSERT(_fff_is_empty(fifo_uint8) != 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 0);
}
void fifofast_test_macro_rebase(uint8_t startvalue)
{
//////////////////////////////////////////////////////////////////////////
// Test 1: no rebase needed
// fill with any data (macros have been proved to work before)
_fff_write_lite(fifo_uint8, startvalue+0);
_fff_write_lite(fifo_uint8, startvalue+1);
_fff_write_lite(fifo_uint8, startvalue+2);
_fff_write_lite(fifo_uint8, startvalue+3);
_fff_rebase(fifo_uint8);
// Confirm it is exactly like before:
UT_ASSERT(_fff_mem_level(fifo_uint8) == 4);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 0);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) != 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+0);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+1);
UT_ASSERT(_fff_peek(fifo_uint8, 2) == startvalue+2);
UT_ASSERT(_fff_peek(fifo_uint8, 3) == startvalue+3);
// confirm that the element 0 is at array index 0
UT_ASSERT(&_fff_peek(fifo_uint8, 0) == &fifo_uint8.data[0]);
_fff_reset(fifo_uint8);
//////////////////////////////////////////////////////////////////////////
// Test 3: move 2 elements
// fill with any data (macros have been proved to work before)
_fff_write_lite(fifo_uint8, startvalue+0);
_fff_write_lite(fifo_uint8, startvalue+1);
_fff_write_lite(fifo_uint8, startvalue+2);
_fff_write_lite(fifo_uint8, startvalue+3);
_fff_read_lite(fifo_uint8);
_fff_write_lite(fifo_uint8, startvalue+4);
_fff_rebase(fifo_uint8);
// Confirm it is exactly like before:
UT_ASSERT(_fff_mem_level(fifo_uint8) == 4);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 0);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) != 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+1);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+2);
UT_ASSERT(_fff_peek(fifo_uint8, 2) == startvalue+3);
UT_ASSERT(_fff_peek(fifo_uint8, 3) == startvalue+4);
// confirm that the element 0 is at array index 0
UT_ASSERT(&_fff_peek(fifo_uint8, 0) == &fifo_uint8.data[0]);
_fff_reset(fifo_uint8);
//////////////////////////////////////////////////////////////////////////
// Test 3: move 2 elements
// fill with any data (macros have been proved to work before)
_fff_write_lite(fifo_uint8, startvalue+0);
_fff_write_lite(fifo_uint8, startvalue+1);
_fff_write_lite(fifo_uint8, startvalue+2);
_fff_write_lite(fifo_uint8, startvalue+3);
_fff_read_lite(fifo_uint8);
_fff_read_lite(fifo_uint8);
_fff_write_lite(fifo_uint8, startvalue+4);
_fff_write_lite(fifo_uint8, startvalue+5);
_fff_rebase(fifo_uint8);
// Confirm it is exactly like before:
UT_ASSERT(_fff_mem_level(fifo_uint8) == 4);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 0);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) != 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+2);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+3);
UT_ASSERT(_fff_peek(fifo_uint8, 2) == startvalue+4);
UT_ASSERT(_fff_peek(fifo_uint8, 3) == startvalue+5);
// confirm that the element 0 is at array index 0
UT_ASSERT(&_fff_peek(fifo_uint8, 0) == &fifo_uint8.data[0]);
_fff_reset(fifo_uint8);
//////////////////////////////////////////////////////////////////////////
// Test 2: move 3 elements
// fill with any data (macros have been proved to work before)
_fff_write_lite(fifo_uint8, startvalue+0);
_fff_write_lite(fifo_uint8, startvalue+1);
_fff_write_lite(fifo_uint8, startvalue+2);
_fff_write_lite(fifo_uint8, startvalue+3);
_fff_read_lite(fifo_uint8);
_fff_read_lite(fifo_uint8);
_fff_read_lite(fifo_uint8);
_fff_write_lite(fifo_uint8, startvalue+4);
_fff_write_lite(fifo_uint8, startvalue+5);
_fff_write_lite(fifo_uint8, startvalue+6);
_fff_rebase(fifo_uint8);
// Confirm it is exactly like before:
UT_ASSERT(_fff_mem_level(fifo_uint8) == 4);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 0);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) != 0);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+3);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+4);
UT_ASSERT(_fff_peek(fifo_uint8, 2) == startvalue+5);
UT_ASSERT(_fff_peek(fifo_uint8, 3) == startvalue+6);
// confirm that the element 0 is at array index 0
UT_ASSERT(&_fff_peek(fifo_uint8, 0) == &fifo_uint8.data[0]);
_fff_reset(fifo_uint8);
}
void fifofast_test_macro_write_multiple(uint8_t startvalue) {
uint8_t multidata[4] = {startvalue + 3, startvalue + 4, startvalue + 5, startvalue + 6};
// initialize fifo with some data
_fff_write_lite(fifo_uint8, startvalue+0);
_fff_write_lite(fifo_uint8, startvalue+1);
_fff_write_lite(fifo_uint8, startvalue+2);
_fff_remove_lite(fifo_uint8, 2);
// write test data, case: all data fits
_fff_write_multiple(fifo_uint8, multidata, 3);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+2);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+3);
UT_ASSERT(_fff_peek(fifo_uint8, 2) == startvalue+4);
UT_ASSERT(_fff_peek(fifo_uint8, 3) == startvalue+5);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 4);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 0);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 1);
_fff_reset(fifo_uint8);
// re-initialize fifo with some data
_fff_write_lite(fifo_uint8, startvalue+7);
_fff_write_multiple(fifo_uint8, multidata, 3);
// write test data, case: NOT all data fits (overflow is discarded)
_fff_write_multiple(fifo_uint8, multidata, 4);
UT_ASSERT(_fff_peek(fifo_uint8, 0) == startvalue+7);
UT_ASSERT(_fff_peek(fifo_uint8, 1) == startvalue+3);
UT_ASSERT(_fff_peek(fifo_uint8, 2) == startvalue+4);
UT_ASSERT(_fff_peek(fifo_uint8, 3) == startvalue+5);
UT_ASSERT(_fff_mem_level(fifo_uint8) == 4);
UT_ASSERT(_fff_mem_free(fifo_uint8) == 0);
UT_ASSERT(_fff_is_empty(fifo_uint8) == 0);
UT_ASSERT(_fff_is_full(fifo_uint8) == 1);
}
//////////////////////////////////////////////////////////////////////////
// Test Functions
//////////////////////////////////////////////////////////////////////////
void fifofast_test_func_initial(fff_proto_t* fifo)
{
//UT_ASSERT(fff_mem_depth(fifo) == 4); // constant
UT_ASSERT(fff_mem_mask(fifo) == 3); // constant = 0b11
UT_ASSERT(fff_mem_level(fifo) == 0);
UT_ASSERT(fff_mem_free(fifo) == 4);
UT_ASSERT(fff_is_empty(fifo) != 0);
UT_ASSERT(fff_is_full(fifo) == 0);
}
void fifofast_test_func_write(fff_proto_t* fifo, uint8_t startvalue)
{
// add 2 values with the fast '_lite' variant (after init we know it is empty)
// use unusual values (NOT 0, 1, 2 ...) to decrease the likelihood of false positives
uint8_t tmp0 = startvalue+0;
uint8_t tmp1 = startvalue+1;
fff_write_lite(fifo, &tmp0);
fff_write_lite(fifo, &tmp1);
UT_ASSERT(fff_mem_level(fifo) == 2);
UT_ASSERT(fff_mem_free(fifo) == 2);
UT_ASSERT(fff_is_empty(fifo) == 0);
UT_ASSERT(fff_is_full(fifo) == 0);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 0) == startvalue+0);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 1) == startvalue+1);
// add 3 values to demonstrate overflow safety
uint8_t tmp2 = startvalue+2;
uint8_t tmp3 = startvalue+3;
uint8_t tmp4 = startvalue+4;
fff_write(fifo, &tmp2);
fff_write(fifo, &tmp3);
fff_write(fifo, &tmp4);
UT_ASSERT(fff_mem_level(fifo) == 4);
UT_ASSERT(fff_mem_free(fifo) == 0);
UT_ASSERT(fff_is_empty(fifo) == 0);
UT_ASSERT(fff_is_full(fifo) != 0);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 0) == startvalue+0);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 1) == startvalue+1);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 2) == startvalue+2);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 3) == startvalue+3);
fff_reset(fifo);
// Test reset once only
UT_ASSERT(fff_mem_level(fifo) == 0);
UT_ASSERT(fff_mem_free(fifo) == 4);
UT_ASSERT(fff_is_empty(fifo) != 0);
UT_ASSERT(fff_is_full(fifo) == 0);
}
void fifofast_test_func_peek(fff_proto_t* fifo, uint8_t startvalue)
{
uint8_t tmp0 = startvalue+0;
uint8_t tmp1 = startvalue+1;
uint8_t tmp2 = startvalue+2;
fff_write_lite(fifo, &tmp0);
fff_write_lite(fifo, &tmp1);
fff_write_lite(fifo, &tmp2);
// modify existing value (index 0 and 2) with _fff_peek()
uint8_t tmp3 = startvalue+3;
uint8_t tmp4 = startvalue+4;
fff_peek_write(fifo, 0, &tmp3);
fff_peek_write(fifo, 2, &tmp4);
UT_ASSERT(fff_mem_level(fifo) == 3);
UT_ASSERT(fff_mem_free(fifo) == 1);
UT_ASSERT(fff_is_empty(fifo) == 0);
UT_ASSERT(fff_is_full(fifo) == 0);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 0) == startvalue+3);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 1) == startvalue+1);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 2) == startvalue+4);
// demonstrate "out of bounds" safety (no over-read/ over-write possible)
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 4) == startvalue+3);
fff_reset(fifo);
}
void fifofast_test_func_remove_lite(fff_proto_t* fifo, uint8_t startvalue)
{
// fill with any data (macros have been proved to work before)
uint8_t tmp0 = startvalue+0;
uint8_t tmp1 = startvalue+1;
uint8_t tmp2 = startvalue+2;
uint8_t tmp3 = startvalue+3;
fff_write_lite(fifo, &tmp0);
fff_write_lite(fifo, &tmp1);
fff_write_lite(fifo, &tmp2);
fff_write_lite(fifo, &tmp3);
// fifo is now full
// _remove 2 (Test case: fifo full before macro)
fff_remove_lite(fifo, 2);
UT_ASSERT(fff_mem_level(fifo) == 2);
UT_ASSERT(fff_mem_free(fifo) == 2);
UT_ASSERT(fff_is_empty(fifo) == 0);
UT_ASSERT(fff_is_full(fifo) == 0);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 0) == startvalue+2);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 1) == startvalue+3);
// _remove 1 (Test case: fifo not full before and not empty after macro)
fff_remove_lite(fifo, 1);
UT_ASSERT(fff_mem_level(fifo) == 1);
UT_ASSERT(fff_mem_free(fifo) == 3);
UT_ASSERT(fff_is_empty(fifo) == 0);
UT_ASSERT(fff_is_full(fifo) == 0);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 0) == startvalue+3);
// _remove 1 (Test case: fifo empty after macro)
fff_remove_lite(fifo, 1);
UT_ASSERT(fff_mem_level(fifo) == 0);
UT_ASSERT(fff_mem_free(fifo) == 4);
UT_ASSERT(fff_is_empty(fifo) != 0);
UT_ASSERT(fff_is_full(fifo) == 0);
fff_reset(fifo);
}
void fifofast_test_func_remove(fff_proto_t* fifo, uint8_t startvalue)
{
// fill with any data (macros have been proved to work before)
uint8_t tmp0 = startvalue+0;
uint8_t tmp1 = startvalue+1;
uint8_t tmp2 = startvalue+2;
uint8_t tmp3 = startvalue+3;
fff_write_lite(fifo, &tmp0);
fff_write_lite(fifo, &tmp1);
fff_write_lite(fifo, &tmp2);
fff_write_lite(fifo, &tmp3);
// fifo is now full
// _remove 0 (Test case: amount == 0 elements)
fff_remove(fifo, 0);
UT_ASSERT(fff_mem_level(fifo) == 4);
UT_ASSERT(fff_mem_free(fifo) == 0);
UT_ASSERT(fff_is_empty(fifo) == 0);
UT_ASSERT(fff_is_full(fifo) != 0);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 0) == startvalue+0);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 1) == startvalue+1);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 2) == startvalue+2);
UT_ASSERT(*(uint8_t*)fff_peek_read(fifo, 3) == startvalue+3);
// _remove 4 (Test case: amount == _fff_mem_level() w/ fifo full)
fff_remove(fifo, 4);
UT_ASSERT(fff_mem_level(fifo) == 0);
UT_ASSERT(fff_mem_free(fifo) == 4);
UT_ASSERT(fff_is_empty(fifo) != 0);
UT_ASSERT(fff_is_full(fifo) == 0);
}
================================================
FILE: fifofast_test.h
================================================
/*
* fifofast_test.h
*
* Created: 08.11.2018 08:35:16
* Author: Dennis
*
* Description:
* This file contains automated tests powered by unittrace.
*/
#ifndef FIFOFAST_TEST_H_
#define FIFOFAST_TEST_H_
#include "fifofast_demo.h"
#include "unittrace/unittrace.h"
//////////////////////////////////////////////////////////////////////////
// Function Declarations
//////////////////////////////////////////////////////////////////////////
void fifofast_test_macro_initial(void);
void fifofast_test_macro_write(uint8_t startvalue);
void fifofast_test_macro_peek(uint8_t startvalue);
void fifofast_test_macro_read(uint8_t startvalue);
void fifofast_test_macro_add(uint8_t startvalue);
void fifofast_test_macro_remove_lite(uint8_t startvalue);
void fifofast_test_macro_remove(uint8_t startvalue);
void fifofast_test_macro_rebase(uint8_t startvalue);
void fifofast_test_macro_write_multiple(uint8_t startvalue);
void fifofast_test_func_initial(fff_proto_t* fifo);
void fifofast_test_func_write(fff_proto_t* fifo, uint8_t startvalue);
void fifofast_test_func_peek(fff_proto_t* fifo, uint8_t startvalue);
void fifofast_test_func_remove_lite(fff_proto_t* fifo, uint8_t startvalue);
void fifofast_test_func_remove(fff_proto_t* fifo, uint8_t startvalue);
#endif /* FIFOFAST_TEST_H_ */
================================================
FILE: subrepos/readme (license info).txt
================================================
This folder contains other projects with their own source code and associated files. The included LICENSE files ONLY apply to the folder (and the project) they are located in.
================================================
FILE: subrepos/unittrace/.gitignore
================================================
/Debug
/.vs
================================================
FILE: subrepos/unittrace/LICENSE.md
================================================
MIT License
Copyright (c) 2018 nqtronix (github.com/nqtronix)
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 OR COPYRIGHT HOLDERS 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.
================================================
FILE: subrepos/unittrace/README.md
================================================
<h1 align="center" style="font-weight: bold; margin-top: 20px; margin-bottom: 20px;">unittrace</h4>
<h4 align="center"> A simple testing and debugging tool for MCUs inspired by <a href="http://www.jera.com/techinfo/jtns/jtn002.html" target="_blank">MinUnit</a>.</h4>
<p align="center">
<a href="#changelog"><img src="https://img.shields.io/github/release-pre/nqtronix/unittrace.svg" alt="release: NA"></a>
<a href="#about"><img src="https://img.shields.io/badge/language-C_(GCC_5.4.0)-blue.svg" alt="language: C GCC (5.4.0)"></a>
<a href="#about"><img src="https://img.shields.io/badge/platform-MCUs, AVR8-blue.svg" alt="platform: MCUs, AVR8"></a>
<a href="#about"><img src="https://img.shields.io/badge/status-maintained-green.svg" alt="status: maintained"></a>
<a href="https://github.com/nqtronix/unittrace/issues"><img src="https://img.shields.io/github/issues/nqtronix/unittrace.svg" alt="issues: NA"></a>
<a href="#license"><img src="https://img.shields.io/github/license/nqtronix/unittrace.svg" alt="license: NA"></a>
</p>
<p align="center">
<a href="#getting-started">Getting Started</a> •
<a href="#documentation">Documentation</a> •
<a href="#under-the-hood">Under the Hood</a> •
<a href="#support">Need Help?</a> •
<a href="#about">About</a> •
<a href="#credits-and-references">Credits</a>
</p>
## Introduction
Testing is an important part of writing code, especially if the code is meant to be re-used for other projects. On small MCUs testing can be quite tricky as the memory is limited and errors can't be easily reported with `printf()` or similar.
**unittrace** provides basic testing functionality and is designed with the limits of MCUs in mind. It runs directly on the hardware and thus can catch errors other test software can't. Fetch the results through a debugger or with a function.
<br>
## Key Features
- **lightweight:** absolute minimum Flash & RAM usage
- **runtime test:** catches both software and hardware errors
- **static memory**: no malloc overhead
<br>
## Limitations
- on AVR8 MCUs the maximum usable flash memory is 128KB or 64K words, as the address must fit into the 16bit pointer. On 32bit MCUs pointers are always 32bit and therefore there is no upper limit.
<br>
## Usage Example
**unittrace** is ridiculously simple to use. You can place the `ut_assert` functions wherever you want:
```c
int main(void)
{
// using inline functions
ut_assert(5 == 5); // passes
ut_assert(5 == 7); // fails and code address gets stored in array
// using macros (equivalently behavior to functions)
UT_ASSERT(5 == 5); // passes
UT_ASSERT(5 == 7); // fails and code address gets stored in array
// set a breakpoint
UT_BREAK();
while (1);
}
```
[](#interpreting-the-data)
<br>
## Getting Started
This section is written especially for everyone who is **not familiar** with the used tools. If you run into problems, please [ask for clarification](#get-help).<br>
### Step 1: Software and Tools
- [**Atmel Studio 7.0**][tool-atmel-studio-7-0]** (Build 1931) [free**]<br>
The installer contains all tools you need to open, edit, compile, simulate and flash this code. If you favor another development tool, feel free to use it instead. (But please understand that I can not provide any support).
- **An AVR8 ISP/JTAG programmer [optional]**<br>
To program AVR8 MCUs I use the [AVR Dragon][tool-avr-dragon]. It can be also used as a debugger and is available within Atmel Studio by default.
### Step 2: Download unittrace
- Clone this repository or hit [Download][git-download] and extract the .zip file.
### Step 3: Browse the project
- **Open the project in Atmel Studio:**<br>
Either double click `unittrace.atsln` or open Atmel Studio and select "File -> Open -> Project/Solution..."
- **Open any file of your interest:**<br>
Select the file in the top right window "Solution Explorer". If the window is not visible, open it by pressing `CTRL + ALT + L` or selecting "View -> Solution Explorer" from the menu.
### Step 4: Run the demo
- **Compile the demo code:**<br>
Press `F7` or select "Build -> Build Solution" from the menu
- **Run the demo code in the simulator:**<br>
Press `CTRL + F5` or select "Debug -> Start Debugging and Break" from the menu
- **Place breakpoints:**<br>
Left-Click on the light grey area left of the code to place or remove a breakpoint. Select lines with the comment "easy breakpoint".
- **View variable values:**<br>
When the code is paused, hover over any variable to display its value. Alternately you can "Right-Click -> Watch" to display the value in the "watch window".
- **Run Code:**<br>
Press `F5` to run to the next breakpoint or `F10` to execute one step.
### Step 5: Going further
- **Changing the target device:**<br>
Press `ALT + F7` and select "Device -> Change device..." to select your desired MCU.
- **Program a real device:**<br>
Connect your programmer, press `ALT + F7` and select "Tool". Choose your tool, your programming interface and wire up your MCU. Press `CTRL + ALT + F7` to flash the code to the MCU. Non-official programmers are not supported by Atmel Studio.
<br>
## Documentation
### Interpreting the Data
The results of the test can be accessed at any time, even if the test is not fully complete. The global variable `unittrace_count` contains the number of failed asserts, the array `unittrace_array` contains pointers to the location of the failed instruction in the flash memory. Both variables can be read at runtime or in the debugger with right-click -> watch.
However due to limitations of the build-in debugger there is no direct way to jump to the corresponding line of code. Instead you have to:
1. Start the debug session
2. Right-click on the source code -> open disassembly
3. Scroll and search the correct line
4. Right-click on the line -> Go to source code
This section will be updated, if I figure out a better way.
<br>
### API
To keep the documentation up-to-date with the least hassle, all configuration options, functions and their arguments are explained in a comment right in front of the declaration. See `unittrace.h` for more information. This section will be updated as soon as this project hits version 1.0.0.
<br>
## Under the Hood
I possibly should note that I've never used any "professional" unit testing library, so this may be unlike the unit testing you are used to. The code was written from scratch and inspired by [MinUnit][tool-minunit].
> Of course, if you have access to a full-featured testing framework like JUnit, by all means use it. But if you don't, you can still use a simple framework like MinUnit, or whip up your own in a few hours. There's no excuse for not unit testing.
<br>
### Development History
I didn't want to use `printf` or any UART equivalent, but the debugger instead. This is not as easy as it seems. Here are the ideas I've tried:
<br>
#### Idea 1
Create a global `static void*` pointer and one for each assert macro. All shall be initialized with `NULL`. Whenever the assert condition is false, the local pointer gets the value from the global one and the global is updated with a reference to the local one. After multiple failed asserted this generates a linked list.
Problems:
1. The Atmel Studio 7 Debugger can't jump to the location of a variable, so one can not tell which assert failed.
2. After multiple de-referencing a pointer like this multiple times the 'watch-window' gets progressively slower.
<br>
#### Idea 2
Create an array of structs in SRAM, each containing:
- pointer to a String in FLASH containing the filename and path
- uint16_t with the line number of the failed assert
New struct shall be added whenever an assert fails. Filename and line number can be accessed with the macros `__FILE__` and `__LINE__`, respectively.
Problem:<br>
To show strings in the watch window, the suffix `,s` must be added manually. If the suffix is added to a struct array like this, ALL contents are interpreted as string.
<br>
#### Idea 3
Instead of saving filename and line number individually, put them in one string into flash. Now whenever a assert fails, store a pointer to the corresponding string in the array.
Problem:<br>
The debugger can't access the data of a FLASH pointer address, if the address is stored in SRAM (or at least I couldn't figure out how). In short: you can't mix SRAM and FLASH pointer as one expression in the debugger.
<br>
#### Idea 4
Instead of generating a list of human readable strings, store the physical location of the assert macro in a SRAM array. The location in the program memory is a 16 bit pointer, so the amount of SRAM required is pretty low. Also this solution requires almost no flash memory, except the few instructions to per assert.
Downside:<br>
Although it is easy to get the value of the pointer in the array, you can not jump to the corresponding location in the C source code. See section [Interpreting the Data](#interpreting-the-data) for more details.
As of now idea 4 is the only one that works, so it is the implemented solution. Please [tell me](#contribute) if you know a more straightforward solution.
<br>
## Support
### Get Help
**Something doesn't work as expected?** No worries! Just open up a new issue in the [GitHub issue tracker][git-issues]. Please provide all information to reproduce your problem. If you don't have a GitHub account (and can't be bothered to create one,) you can [contact](#contact) me directly.
<br>
### Contribute
**Spotted an error?** [Open an issue][git-issues] or submit a pull request.
There is no CONTRIBUTING.md yet, sorry. Contributions will inherit the [license](#license) of this project. If you have any questions, just ask.
<br>
## About
### Status
**This project is currently classified as** <a href="https://github.com/nqtronix/git-template/blob/master/badges.md#project-status"><img src="https://img.shields.io/badge/status-maintained-green.svg" alt="status: maintained"></a><br>
_The developers intend to keep the code in working condition by updating dependencies, fixing bugs and solving issues._
As my testing needs increase I will likely add the functionality I need.
<br>
### Changelog
This project uses [**Semantic Versioning 2.0.0**][semver.org]. During initial development (0.x.x versions) any _major_ increase is substituted with a _minor_ increase (0.1.0->0.2.0 instead of 0.1.0->1.0.0).
The message of each commit contains detailed information about the changes made. The list below is a summary about all significant improvements.
- **0.1.0 (latest)** <br>
- initial release
<br>
### Contact
If you haven't done so already, please check out [Get Help](#get-help) for the fastest possible help on your issue. Alternatively you can find my public email address on my [profile][git-profile].
<br>
## Credits and References
### Projects Used
- [**MinUnit**][tool-minunit] - _a minimal unit testing framework for C_<br>
Great inspiration, thanks!
- [**git-template**][git-repo-git-template] - _A simple and clean git repository template._<br>
<br>
### Related Projects
- none (yet)
Want yours to be listed here, too? Create a merge request or [**get in touch**](#get-help).
<br>
### Additional Resources
- [**Catch2**][git-repo-catch2] by [@catchorg][at-catchorg]<br>
A header-only test framework. Found it by chance and it seems like a great choice for PC applications.
<br>
## License
This project is proudly licensed under the [MIT license][git-license].
The MIT license was chosen to give you the freedom to use this project in any way you want, while protecting all contributors from legal claims. Good code works, great code works for everyone. If this code has become a part of one of your projects, a link back to us would be highly appreciated. Thanks!
<!-- Links -->
[git-readme]:README.md
[git-license]:LICENSE.md
[git-profile]:https://github.com/nqtronix
[git-issues]:https://github.com/nqtronix/unittrace/issues
[git-download]:https://github.com/nqtronix/unittrace/archive/master.zip
[git-repo-git-template]:https://github.com/nqtronix/git-template
[semver.org]:semver.org
[tool-atmel-studio-7-0]:https://www.microchip.com/mplab/avr-support/atmel-studio-7
[tool-avr-dragon]:https://www.microchip.com/Developmenttools/ProductDetails/ATAVRDRAGON
[tool-minunit]:http://www.jera.com/techinfo/jtns/jtn002.html
[at-catchorg]:https://github.com/catchorg
[git-repo-catch2]:https://github.com/catchorg/Catch2
================================================
FILE: subrepos/unittrace/unittrace.atsln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Atmel Studio Solution File, Format Version 11.00
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "unittrace", "unittrace.cproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|AVR = Debug|AVR
Release|AVR = Release|AVR
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Debug|AVR.ActiveCfg = Debug|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Debug|AVR.Build.0 = Debug|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.ActiveCfg = Release|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.Build.0 = Release|AVR
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
================================================
FILE: subrepos/unittrace/unittrace.c
================================================
/*
* unittrace.c
*
* Created: 29.10.2018 20:34:06
* Author: Dennis aka nqtronix (github.com/nqtronix)
*/
#include "unittrace.h"
#include <stddef.h> // required for 'NULL'
//////////////////////////////////////////////////////////////////////////
// Variables
//////////////////////////////////////////////////////////////////////////
volatile void* unittrace_array[UNITTRACE_LIST_SIZE] = {[0 ... UNITTRACE_LIST_SIZE-1] = NULL};
ut_cnt_t unittrace_count = 0;
//////////////////////////////////////////////////////////////////////////
// Functions
//////////////////////////////////////////////////////////////////////////
void ut_assert_manual(void* addr, uint8_t cond)
{
// only store event, if condition is 'false'
if (cond == 0)
{
#ifndef UNITTRACE_USE_EXT_COUNTER
// store event data into array, if it is not full yet
if (unittrace_count < UNITTRACE_LIST_SIZE)
{
unittrace_array[unittrace_count] = addr;
unittrace_count++;
}
#else
// store event data into array, if it is not full yet
if (unittrace_count < UNITTRACE_LIST_SIZE)
unittrace_array[unittrace_count] = addr;
// increment counter, reset if overflow detected
// this odd code works with any positive integer type
unittrace_count++;
if (unittrace_count == 0)
unittrace_count--;
#endif
}
}
void* ut_get_array(void)
{
return unittrace_array;
}
ut_cnt_t ut_get_count(void)
{
return unittrace_count;
}
================================================
FILE: subrepos/unittrace/unittrace.componentinfo.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<Store xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="AtmelPackComponentManagement">
<ProjectComponents>
<ProjectComponent z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<CApiVersion></CApiVersion>
<CBundle></CBundle>
<CClass>Device</CClass>
<CGroup>Startup</CGroup>
<CSub></CSub>
<CVariant></CVariant>
<CVendor>Atmel</CVendor>
<CVersion>1.2.0</CVersion>
<DefaultRepoPath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs</DefaultRepoPath>
<DependentComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
<Description></Description>
<Files xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.2.209\include</AbsolutePath>
<Attribute></Attribute>
<Category>include</Category>
<Condition>C</Condition>
<FileContentHash i:nil="true" />
<FileVersion></FileVersion>
<Name>include</Name>
<SelectString></SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.2.209\include\avr\iom328p.h</AbsolutePath>
<Attribute></Attribute>
<Category>header</Category>
<Condition>C</Condition>
<FileContentHash>UMk4QUzkkuShabuoYtNl/Q==</FileContentHash>
<FileVersion></FileVersion>
<Name>include/avr/iom328p.h</Name>
<SelectString></SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.2.209\templates\main.c</AbsolutePath>
<Attribute>template</Attribute>
<Category>source</Category>
<Condition>C Exe</Condition>
<FileContentHash>GD1k8YYhulqRs6FD1B2Hog==</FileContentHash>
<FileVersion></FileVersion>
<Name>templates/main.c</Name>
<SelectString>Main file (.c)</SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.2.209\templates\main.cpp</AbsolutePath>
<Attribute>template</Attribute>
<Category>source</Category>
<Condition>C Exe</Condition>
<FileContentHash>YXFphlh0CtZJU+ebktABgQ==</FileContentHash>
<FileVersion></FileVersion>
<Name>templates/main.cpp</Name>
<SelectString>Main file (.cpp)</SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.2.209\gcc\dev\atmega328p</AbsolutePath>
<Attribute></Attribute>
<Category>libraryPrefix</Category>
<Condition>GCC</Condition>
<FileContentHash i:nil="true" />
<FileVersion></FileVersion>
<Name>gcc/dev/atmega328p</Name>
<SelectString></SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
</Files>
<PackName>ATmega_DFP</PackName>
<PackPath>C:/Program Files (x86)/Atmel/Studio/7.0/Packs/atmel/ATmega_DFP/1.2.209/Atmel.ATmega_DFP.pdsc</PackPath>
<PackVersion>1.2.209</PackVersion>
<PresentInProject>true</PresentInProject>
<ReferenceConditionId>ATmega328P</ReferenceConditionId>
<RteComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d4p1:string></d4p1:string>
</RteComponents>
<Status>Resolved</Status>
<VersionMode>Fixed</VersionMode>
<IsComponentInAtProject>true</IsComponentInAtProject>
</ProjectComponent>
</ProjectComponents>
</Store>
================================================
FILE: subrepos/unittrace/unittrace.cproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<ProjectVersion>7.0</ProjectVersion>
<ToolchainName>com.Atmel.AVRGCC8.C</ToolchainName>
<ProjectGuid>dce6c7e3-ee26-4d79-826b-08594b9ad897</ProjectGuid>
<avrdevice>ATmega328P</avrdevice>
<avrdeviceseries>none</avrdeviceseries>
<OutputType>Executable</OutputType>
<Language>C</Language>
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
<AssemblyName>unittrace</AssemblyName>
<Name>unittrace</Name>
<RootNamespace>unittrace</RootNamespace>
<ToolchainFlavour>Native</ToolchainFlavour>
<KeepTimersRunning>true</KeepTimersRunning>
<OverrideVtor>false</OverrideVtor>
<CacheFlash>true</CacheFlash>
<ProgFlashFromRam>true</ProgFlashFromRam>
<RamSnippetAddress>0x20000000</RamSnippetAddress>
<UncachedRange />
<preserveEEPROM>true</preserveEEPROM>
<OverrideVtorValue>exception_table</OverrideVtorValue>
<BootSegment>2</BootSegment>
<eraseonlaunchrule>0</eraseonlaunchrule>
<AsfFrameworkConfig>
<framework-data xmlns="">
<options />
<configurations />
<files />
<documentation help="" />
<offline-documentation help="" />
<dependencies>
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.40.0" />
</dependencies>
</framework-data>
</AsfFrameworkConfig>
<avrtool>com.atmel.avrdbg.tool.simulator</avrtool>
<avrtoolserialnumber />
<avrdeviceexpectedsignature>0x1E950F</avrdeviceexpectedsignature>
<com_atmel_avrdbg_tool_simulator>
<ToolOptions>
<InterfaceProperties>
</InterfaceProperties>
<InterfaceName>
</InterfaceName>
</ToolOptions>
<ToolType>com.atmel.avrdbg.tool.simulator</ToolType>
<ToolNumber>
</ToolNumber>
<ToolName>Simulator</ToolName>
</com_atmel_avrdbg_tool_simulator>
<avrtoolinterface />
<ResetRule>0</ResetRule>
<EraseKey />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<ToolchainSettings>
<AvrGcc>
<avrgcc.common.Device>-mmcu=atmega328p -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\gcc\dev\atmega328p"</avrgcc.common.Device>
<avrgcc.common.optimization.RelaxBranches>True</avrgcc.common.optimization.RelaxBranches>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>NDEBUG</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcc.linker.libraries.Libraries>
<avrgcc.assembler.general.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\include</Value>
</ListValues>
</avrgcc.assembler.general.IncludePaths>
</AvrGcc>
</ToolchainSettings>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<ToolchainSettings>
<AvrGcc>
<avrgcc.common.Device>-mmcu=atmega328p -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\gcc\dev\atmega328p"</avrgcc.common.Device>
<avrgcc.common.optimization.RelaxBranches>True</avrgcc.common.optimization.RelaxBranches>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>DEBUG</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcc.linker.libraries.Libraries>
<avrgcc.assembler.general.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.2.209\include</Value>
</ListValues>
</avrgcc.assembler.general.IncludePaths>
<avrgcc.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcc.assembler.debugging.DebugLevel>
</AvrGcc>
</ToolchainSettings>
</PropertyGroup>
<ItemGroup>
<Compile Include="unittrace.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="unittrace.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="unittrace_demo.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="utility\macros\com\macro_type.h">
<SubType>compile</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="utility" />
<Folder Include="utility\macros" />
<Folder Include="utility\macros\com" />
</ItemGroup>
<ItemGroup>
<None Include="README.md">
<SubType>compile</SubType>
</None>
<None Include="utility\macros\com\com readme.txt">
<SubType>compile</SubType>
</None>
</ItemGroup>
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
</Project>
================================================
FILE: subrepos/unittrace/unittrace.h
================================================
/*
* unittrace.h
*
* Created: 29.10.2018 20:36:59
* Author: Dennis aka nqtronix (github.com/nqtronix)
*
* Description:
* Testing is an important part of writing code, especially if the code is meant to be re-used for
* other projects. On small MCUs testing can be quite tricky as the memory is limited and errors
* can't be easily reported with `printf()` or similiar.
*
* unittrace provides basic testing functionality and is designed with the limits of MCUs in mind.
* It runs directly on the hardware and thus can catch errors other test software can't. Fetch the
* results through a debugger or with a function.
*
* See included 'README.md' for additional information.
*/
#ifndef UNITTRACE_H_
#define UNITTRACE_H_
#include <stdint.h> // required for data types (uint8_t, uint16_t, ...)
#include "utility/macros/com/macro_type.h"
//////////////////////////////////////////////////////////////////////////
// Check requirements
//////////////////////////////////////////////////////////////////////////
#ifndef __GNUC__
#error unittrace.h requires "always inline functions", "local labels" and "typeof" offered by a GNU C/ GCC compiler!
#endif
// Workarounds are possible. See comments in the source code.
//////////////////////////////////////////////////////////////////////////
// General Info
//////////////////////////////////////////////////////////////////////////
// version numbering is based on "Semantic Versioning 2.0.0" (semver.org)
#define UNITTRACE_VERSION_MAJOR 0
#define UNITTRACE_VERSION_MINOR 1
#define UNITTRACE_VERSION_PATCH 0
#define UNITTRACE_VERSION_SUFFIX
#define UNITTRACE_VERSION_META
// For all development versions (0.x.x) the patch version is increased whenever a function was renamed
//////////////////////////////////////////////////////////////////////////
// Settings (This section can be modified by the user)
//////////////////////////////////////////////////////////////////////////
// Define the amount of failed events to log in detail. The same event can be logged multiple times.
// Each event required 4 byte of RAM. There is no upper limit except the device's SRAM. The compiler
// will automatically choose the smallest possible data type for most efficient operation.
#define UNITTRACE_LIST_SIZE 8
// Especially on small embedded systems it is best to choose the smallest data type for every
// variable. This macro selects the smallest possible type based on 'UNITTRACE_LIST_SIZE'. Change
// to 'uint8_t', 'uint16_t' or 'uint32_t' to manually overwrite this feature.
//
// extra '#define UNITTRACE_COUNTER_TYPE' prevents VAssistX from marking 'ut_cnt_t' red
#define UNITTRACE_COUNTER_TYPE _type_min(UNITTRACE_LIST_SIZE)
typedef UNITTRACE_COUNTER_TYPE ut_cnt_t;
// If 'UNITTRACE_USE_EXT_COUNTER' is defined, failed asserts will be counted till the maximum value,
// even if no space in the array is available. This might be helpful for some test cases, but does
// increase the assert execution time slightly.
#define UNITTRACE_USE_EXT_COUNTER
//////////////////////////////////////////////////////////////////////////
// Global Variables
//////////////////////////////////////////////////////////////////////////
// I couldn't figure out how to pass a void array half-way decently, so both variables are
// now global instead. This is not "how it's don", I know, but unittrace is for testing only anyway.
// WATCH THESE VARIABLES IN THE DEBUGGER
// contains all traced events
volatile void* unittrace_array[UNITTRACE_LIST_SIZE];
// amount of traced events
ut_cnt_t unittrace_count;
//////////////////////////////////////////////////////////////////////////
// Function Declarations
//////////////////////////////////////////////////////////////////////////
// 'ut_assert' is used to validate any given condition. If the condition is false (cond == 0), the
// assembler instruction address is stored to an array (unless it is full). This array can be read
// during debugging.
// If your compiler does not support inline functions, use the macro 'UT_ASSERT(cond)'.
static inline void ut_assert(uint8_t cond) __attribute__((always_inline));
// If you manually want to write a value to the array, you can use this macro. Usually the first few
// instructions are generated by the compiler and their addresses will never naturally appear in
// said array. Thus you may abuse low values for your own, custom purpose (<50 should be safe on
// AVR8 MCUs)
void ut_assert_manual(void* addr, uint8_t cond);
//////////////////////////////////////////////////////////////////////////
// Macros
//////////////////////////////////////////////////////////////////////////
// alternative implementation of the macro 'ut_assert(cond)'
#define UT_ASSERT(cond) do{__label__ lcl; lcl: ut_assert_manual(&&lcl, cond);}while(0)
// places a single 'nop' instruction. This will not be optimized an is ideal to place a breakpoint.
// Unlike an inline function a macro is taken "as is". In this case it make debugging a little
// easier, so support for the equivalent inline function has been removed.
#define UT_BREAK() asm("nop")
//////////////////////////////////////////////////////////////////////////
// Inline functions
//////////////////////////////////////////////////////////////////////////
// Inline functions MUST be defined in the .h, not in the .c file to work correctly!
// This function MUST be always inline to create an individual label for each call. In GCC inline
// functions declared with '__attribute__((always_inline))' are as fast as the respective macro.
// '__label__' creates a local label to prevent a littered namespace. Alternatively you could use
// the build-in macro '__COUNTER__' and concat '##' to generate unique labels.
static inline void ut_assert(uint8_t cond)
{
__label__ local_label;
local_label:
ut_assert_manual(&&local_label, cond);
}
#endif /* UNITTRACE_H_ */
================================================
FILE: subrepos/unittrace/unittrace_demo.c
================================================
/*
* unittrace.c
*
* Created: 29.10.2018 20:32:06
* Author : Dennis aka nqtronix (github.com/nqtronix)
*/
#include "unittrace.h"
int main(void)
{
// using inline functions
ut_assert(5 == 5); // passes
UT_BREAK();
ut_assert(5 == 7); // fails and code address gets stored in array
UT_BREAK();
// using macros (equivalently behavior to functions)
UT_ASSERT(5 == 5); // passes
UT_BREAK();
UT_ASSERT(5 == 7); // fails and code address gets stored in array
UT_BREAK();
// Access generated data
// You can access the data either with a debugger by watching the two global variables or use
// them in your code. See pseudocode below:
for (uint8_t cnt=0; cnt < unittrace_count; cnt++)
{
/*usedata(unittrace_array[cnt]);*/
}
// End test
UT_BREAK();
while (1);
}
================================================
FILE: subrepos/unittrace/utility/macros/com/com readme.txt
================================================
This folder "com" contains macros for common functionalities and can result in significant cleaner
code. In addition the majority of macros can be highly optimized by the compiler, if the imput
arguments are literals.
================================================
FILE: subrepos/unittrace/utility/macros/com/macro_type.h
================================================
/*
* macro_type.h
*
* Created: 14.10.2018 14:34:03
* Author: Dennis
*
* Description:
* Provides typ-related macros
*/
#ifndef MACRO_TYPE_H_
#define MACRO_TYPE_H_
// returns a value not smaller or larger than the limit
#define _limit(arg, lo, hi) ((arg)<lo? lo : (arg)>hi? hi : (arg))
#define _limit_lo(arg, lo) ((arg)<lo? lo : (arg))
#define _limit_hi(arg, hi) ((arg)>hi? hi : (arg))
// returns smallest type for given integer
#define _type_min(_integer) typeof(_type_cast_min(_integer))
// By default all literals are interpreted as a int, whose size varies between systems
// This macro forces minimum memory usage by casting a literal to the smallest data type suitable.
// Unsigned types are proffered.
#define _type_cast_min(_integer) \
(__builtin_choose_expr((_integer)>0, \
__builtin_choose_expr((_integer)<=UINT8_MAX, (uint8_t) (_integer), \
__builtin_choose_expr((_integer)<=UINT16_MAX, (uint16_t) (_integer), \
__builtin_choose_expr((_integer)<=UINT32_MAX, (uint32_t) (_integer), (uint64_t) (_integer)))), \
__builtin_choose_expr((_integer)>=INT8_MIN, (int8_t) (_integer), \
__builtin_choose_expr((_integer)>=INT16_MIN, (int16_t) (_integer), \
__builtin_choose_expr((_integer)>=INT32_MIN, (int32_t) (_integer), (int64_t) (_integer))))))
#endif /* MACRO_TYPE_H_ */
================================================
FILE: utility/macros/com/com readme.txt
================================================
This folder "com" contains macros for common functionalities and can result in significant cleaner
code. In addition the majority of macros can be highly optimized by the compiler, if the imput
arguments are literals.
================================================
FILE: utility/macros/com/macro_array.h
================================================
/*
* macro_array.h
*
* Created: 14.10.2018 14:38:19
* Author: Dennis
*/
#ifndef MACRO_ARRAY_H_
#define MACRO_ARRAY_H_
// like sizeof(), but returns the element count of a const array
#define _sizeof_array(_array) (sizeof(_array)/sizeof(_array[0]))
// returns the length of a string
#define _sizeof_str(_string) ((sizeof(_string)/sizeof(_string[0]))-sizeof(_string[0]))
// Allows access of nth byte in uint16_t, uint32_t, ...
#define _get_nth_element(_type, input, n) ((_type)(input>>(n*sizeof(_type))))
// returns the index of an array that matches the pointer. the pointer MUST point to an element of
// the array, checks are NOT performed
#define _array_pointer2idx(_array_id, pointer) ((pointer-_array_id)/sizeof(_array_id[0]))
#endif /* MACRO_ARRAY_H_ */
================================================
FILE: utility/macros/com/macro_math.h
================================================
/*
* macro_math.h
*
* Created: 14.10.2018 14:34:32
* Author: Dennis
*
* Description:
* Provides math operations, which can be solved at compile time, given the inputs are literals.
* If used during runtime, these macros might be significant slower than the usual implementations.
*/
#ifndef MACRO_MATH_H_
#define MACRO_MATH_H_
//////////////////////////////////////////////////////////////////////////
// Comparisons
//////////////////////////////////////////////////////////////////////////
// returns 0 if argument is not power of two
#define _is_power_of_two(arg) ((arg) && !((arg) & ((arg) - 1)))
// returns 0 if both arguments have the same sign
#define _is_sign_opposite(arg1, arg2) (((arg1) ^ (arg2)) < 0)
//////////////////////////////////////////////////////////////////////////
// Calc
//////////////////////////////////////////////////////////////////////////
// returns the absolute value
#define _abs(arg) ((arg) >= 0? (arg) : -(arg))
// shifts 'input' left or right by 'shiftAmount' , but unlike the C standard operators << and >>
// negative values are allowed and result in a shift of the opposite direction
#define _shift_left(arg, shiftAmount) \
(shiftAmount > 0 ? arg << shiftAmount : arg >> (-shiftAmount))
#define _shift_right(arg, shiftAmount) \
(shiftAmount > 0 ? arg >> shiftAmount : arg << (-shiftAmount))
// returns the minimum of both parameters, if x == y, y is returned
#define _min(x, y) \
(((x) > (y)) ? (y) : (x))
// returns the mathematical function log2(x), rounded down to nearest integer
// this marco-like function makes use of a GCC build-in function and can be used eg. for if(log2(x)>y)
// If a literal is passed to this function, the result is calculated at compile time and stored in flash.
#define _log2(n) ((unsigned) (8*sizeof(uint64_t) - __builtin_clzll((n)) - 1))
#endif /* MACRO_MATH_H_ */
================================================
FILE: utility/macros/com/macro_type.h
================================================
/*
* macro_type.h
*
* Created: 14.10.2018 14:34:03
* Author: Dennis
*
* Description:
* Provides typ-related macros
*/
#ifndef MACRO_TYPE_H_
#define MACRO_TYPE_H_
// returns a value not smaller or larger than the limit
#define _limit(arg, lo, hi) ((arg)<lo? lo : (arg)>hi? hi : (arg))
#define _limit_lo(arg, lo) ((arg)<lo? lo : (arg))
#define _limit_hi(arg, hi) ((arg)>hi? hi : (arg))
// returns smallest type for given integer
#define _type_min(_integer) typeof(_type_cast_min(_integer))
// By default all literals are interpreted as a int, whose size varies between systems
// This macro forces minimum memory usage by casting a literal to the smallest data type suitable.
// Unsigned types are proffered.
#define _type_cast_min(_integer) \
(__builtin_choose_expr((_integer)>0, \
__builtin_choose_expr((_integer)<=UINT8_MAX, (uint8_t) (_integer), \
__builtin_choose_expr((_integer)<=UINT16_MAX, (uint16_t) (_integer), \
__builtin_choose_expr((_integer)<=UINT32_MAX, (uint32_t) (_integer), (uint64_t) (_integer)))), \
__builtin_choose_expr((_integer)>=INT8_MIN, (int8_t) (_integer), \
__builtin_choose_expr((_integer)>=INT16_MIN, (int16_t) (_integer), \
__builtin_choose_expr((_integer)>=INT32_MIN, (int32_t) (_integer), (int64_t) (_integer))))))
#endif /* MACRO_TYPE_H_ */
================================================
FILE: utility/macros/mpl/macro_cat.h
================================================
/*
* macro_cat.h
*
* Created: 14.10.2018 11:09:54
* Author: Dennis
*
* Description:
* concatenates up to 16 arguments without inhibition
*/
#ifndef MACRO_CAT_H_
#define MACRO_CAT_H_
#include "../mpl/macro_vfunc.h"
#define CAT(...) VFUNC_0ARG(CAT, __VA_ARGS__)
#define CAT0()
#define CAT1(a) a
#define CAT2(a,b) a ## b
#define CAT3(a,b,c) a ## b ## c
#define CAT4(a,b,c,d) a ## b ## c ## d
#define CAT5(a,b,c,d,e) a ## b ## c ## d ## e
#define CAT6(a,b,c,d,e,f) a ## b ## c ## d ## e ## f
#define CAT7(a,b,c,d,e,f,g) a ## b ## c ## d ## e ## f ## g
#define CAT8(a,b,c,d,e,f,g,h) a ## b ## c ## d ## e ## f ## g ## h
#define CAT9(a,b,c,d,e,f,g,h,i) a ## b ## c ## d ## e ## f ## g ## h ## i
#define CAT10(a,b,c,d,e,f,g,h,i,j) a ## b ## c ## d ## e ## f ## g ## h ## i ## j
#define CAT11(a,b,c,d,e,f,g,h,i,j,k) a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k
#define CAT12(a,b,c,d,e,f,g,h,i,j,k,l) a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k ## l
#define CAT13(a,b,c,d,e,f,g,h,i,j,k,l,m) a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k ## l ## m
#define CAT14(a,b,c,d,e,f,g,h,i,j,k,l,m,n) a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k ## l ## m ## n
#define CAT15(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k ## l ## m ## n ## o
#define CAT16(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k ## l ## m ## n ## o ## p
#endif /* MACRO_CAT_H_ */
================================================
FILE: utility/macros/mpl/macro_narg.h
================================================
/*
* macro_narg.h
*
* Created: 14.10.2018 11:01:15
* Author: Dennis
*
* Description:
* Returns the number of passed (macro) arguments
*/
#ifndef MACRO_NARG_H_
#define MACRO_NARG_H_
// get number of arguments with __NARG__
#define __NARG__(...) __NARG_I_("ignored", ##__VA_ARGS__, __RSEQ_N())
#define __NARG_I_(...) __ARG_N(__VA_ARGS__)
#define __ARG_N( \
_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,_87,_88,_89,_90, \
_91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
_101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
_111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
_121,_122,_123,_124,_125,_126,_127,_128,N,...) N
#define __RSEQ_N() \
127,126,125,124,123,122,121,120, \
119,118,117,116,115,114,113,112,111,110, \
109,108,107,106,105,104,103,102,101,100, \
99,98,97,96,95,94,93,92,91,90, \
89,88,87,86,85,84,83,82,81,80, \
79,78,77,76,75,74,73,72,71,70, \
69,68,67,66,65,64,63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
#endif /* MACRO_NARG_H_ */
================================================
FILE: utility/macros/mpl/macro_vfunc.h
================================================
/*
* macro_vfunc.h
*
* Created: 14.10.2018 11:05:26
* Author: Dennis
*
* Description:
* vfunc expands to a different macro depending on the argument count. See usage example below.
*/
#ifndef MACRO_VFUNC_H_
#define MACRO_VFUNC_H_
#include "macro_narg.h"
// general definition for any function name
// VFUNC_NARG passes N arguments to each function in addition to the variable arguments
#define _VFUNC_(name,n) name##n
#define _VFUNC(name,n) _VFUNC_(name,n)
#define VFUNC_0ARG(func,...) _VFUNC(func,__NARG__(__VA_ARGS__)) (__VA_ARGS__)
#define _VFUNC1(name,arg1,n) _VFUNC_(name,n)
#define VFUNC_1ARG(func,arg1,...) _VFUNC1(func,arg1,__NARG__(__VA_ARGS__)) (arg1,__VA_ARGS__)
#define _VFUNC2(name,arg1,arg2,n) _VFUNC_(name,n)
#define VFUNC_2ARG(func,arg1,arg2,...) _VFUNC2(func,arg1,arg2,__NARG__(__VA_ARGS__)) (arg1,arg2,__VA_ARGS__)
#define _VFUNC3(name,arg1,arg2,arg3,n) _VFUNC_(name,n)
#define VFUNC_3ARG(func,arg1,arg2,arg3,...) _VFUNC3(func,arg1,arg2,arg3,__NARG__(__VA_ARGS__)) (arg1,arg2,arg3,__VA_ARGS__)
// USAGE:
// EXAMPLE 1:
// Change all 5 'SUM' in the example below to your desired macro name
//
// #define SUM(...) VFUNC_0ARG(SUM, __VA_ARGS__)
// #define SUM1(x) (x)
// #define SUM2(x, y) ((x) + (y))
// #define SUM3(x, y, z) ((x) + (y) + (z))
//
// EXAMPLE 2:
// Use it for default arguments:
//
// #define func(...) VFUNC_0ARG(func, __VA_ARGS__)
// #define func2(a, b) func4(a, b, NULL, NULL)
// #define func3(a, b, c) func4(a, b, c, NULL)
//
// // real function:
// int func4(int a, int b, void* c, void* d) { /* ... */ }
#endif /* MACRO_VFUNC_H_ */
================================================
FILE: utility/macros/mpl/mpl readme.txt
================================================
This folder "mpl" (short for "macro programming language) contains macros for more advanced
functionality. It is not complete by any means and serves more like a sketchbook for ideas and
enables a few special use cases. Changes which break compatibility in the future are likely.
================================================
FILE: utility/macros/readme.txt
================================================
MACRO NAMING CONVENTIONS:
Although macros make many complicated things easier they can also easily generate problems by
incorrect use, which are very hard to debug. Therfore naming conventions are important to
distinguish various functions.
Example: Description:
__NAME__ Compiler defined macros. Might be used in rare cases to create compiler macro
depended macros such as __TIME_UNIX__
NAME Literals.
NAME() Preprocessor macro. Performs various operations with the preprocessor, typically
# or ##, but can be more complex as well. Their result can be used to create further
macros.
_NAME() Hidden preprocessor macro.
_name() Function-like macro. Unlike preprocessor macros these macros are intended to be used
like any other C function. The _ differentiates it from a normal function and hints
to possible subtle problems in use.
_name(id) Any normally written parameter of a function-like macro can be any C expression of
the correct type (such as uint8_t, uint16_t, ...)
_name(_id) Any parameter starting with _ is taken literally and thus has to be known at compile
time. See description of macro in question for allowed inputs.
type_t _tmp All local variables of a function like macro are marked with _ to prevent conflicts
substituted C names for the parameter. DO NOT pass any C identifier starting with _
_return Is used within compound statements as a label for the return value.
From https://gcc.gnu.org/onlinedocs/gcc/Typeof.html
The reason for using names that start with underscores for the local variables is to avoid
conflicts with variable names that occur within the expressions that are substituted for a and b.
Eventually we hope to design a new form of declaration syntax that allows you to declare variables
whose scopes start only after their initializers; this will be a more reliable way to prevent such
conflicts.
gitextract_fki9r3a0/
├── .gitignore
├── LICENSE.md
├── README.md
├── fifofast.atsln
├── fifofast.componentinfo.xml
├── fifofast.cproj
├── fifofast.h
├── fifofast_demo.c
├── fifofast_demo.h
├── fifofast_test.c
├── fifofast_test.h
├── subrepos/
│ ├── readme (license info).txt
│ └── unittrace/
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── unittrace.atsln
│ ├── unittrace.c
│ ├── unittrace.componentinfo.xml
│ ├── unittrace.cproj
│ ├── unittrace.h
│ ├── unittrace_demo.c
│ └── utility/
│ └── macros/
│ └── com/
│ ├── com readme.txt
│ └── macro_type.h
└── utility/
└── macros/
├── com/
│ ├── com readme.txt
│ ├── macro_array.h
│ ├── macro_math.h
│ └── macro_type.h
├── mpl/
│ ├── macro_cat.h
│ ├── macro_narg.h
│ ├── macro_vfunc.h
│ └── mpl readme.txt
└── readme.txt
SYMBOL INDEX (37 symbols across 7 files)
FILE: fifofast.h
type FIFOFAST_INDEX_T (line 118) | typedef FIFOFAST_INDEX_T fff_index_t;
type FIFOFAST_LEVEL_T (line 119) | typedef FIFOFAST_LEVEL_T fff_level_t;
type fff_proto_t (line 121) | typedef struct
function fff_index_t (line 494) | inline fff_index_t fff_wrap(fff_proto_t *fifo, fff_index_t idx)
function fff_index_t (line 502) | static inline fff_index_t fff_mem_mask(fff_proto_t *fifo)
function fff_index_t (line 506) | static inline fff_index_t fff_data_size(fff_proto_t *fifo)
function fff_is_empty (line 512) | static inline uint8_t fff_is_empty(fff_proto_t *fifo)
function fff_is_full (line 516) | static inline uint8_t fff_is_full(fff_proto_t *fifo)
function fff_level_t (line 520) | static inline fff_level_t fff_mem_level(fff_proto_t *fifo)
function fff_index_t (line 524) | static inline fff_index_t fff_mem_free(fff_proto_t *fifo)
function fff_reset (line 530) | static inline void fff_reset(fff_proto_t *fifo)
function fff_remove (line 538) | static inline void fff_remove(fff_proto_t *fifo, fff_level_t amount)
function fff_remove_lite (line 544) | static inline void fff_remove_lite(fff_proto_t *fifo, fff_level_t amount)
function fff_write (line 550) | static inline void fff_write(fff_proto_t *fifo, void *data)
function fff_write_lite (line 555) | static inline void fff_write_lite(fff_proto_t *fifo, void *data)
function fff_peek_write (line 568) | static inline void fff_peek_write(fff_proto_t *fifo, fff_index_t idx, vo...
FILE: fifofast_demo.c
function main (line 16) | int main(void)
FILE: fifofast_demo.h
type header_t (line 37) | typedef struct
FILE: fifofast_test.c
function fifofast_test_macro_initial (line 26) | void fifofast_test_macro_initial()
function fifofast_test_macro_write (line 36) | void fifofast_test_macro_write(uint8_t startvalue)
function fifofast_test_macro_peek (line 77) | void fifofast_test_macro_peek(uint8_t startvalue)
function fifofast_test_macro_read (line 102) | void fifofast_test_macro_read(uint8_t startvalue)
function fifofast_test_macro_add (line 131) | void fifofast_test_macro_add(uint8_t startvalue)
function fifofast_test_macro_remove_lite (line 182) | void fifofast_test_macro_remove_lite(uint8_t startvalue)
function fifofast_test_macro_remove (line 226) | void fifofast_test_macro_remove(uint8_t startvalue)
function fifofast_test_macro_rebase (line 259) | void fifofast_test_macro_rebase(uint8_t startvalue)
function fifofast_test_macro_write_multiple (line 388) | void fifofast_test_macro_write_multiple(uint8_t startvalue) {
function fifofast_test_func_initial (line 434) | void fifofast_test_func_initial(fff_proto_t* fifo)
function fifofast_test_func_write (line 444) | void fifofast_test_func_write(fff_proto_t* fifo, uint8_t startvalue)
function fifofast_test_func_peek (line 489) | void fifofast_test_func_peek(fff_proto_t* fifo, uint8_t startvalue)
function fifofast_test_func_remove_lite (line 519) | void fifofast_test_func_remove_lite(fff_proto_t* fifo, uint8_t startvalue)
function fifofast_test_func_remove (line 567) | void fifofast_test_func_remove(fff_proto_t* fifo, uint8_t startvalue)
FILE: subrepos/unittrace/unittrace.c
function ut_assert_manual (line 25) | void ut_assert_manual(void* addr, uint8_t cond)
function ut_cnt_t (line 59) | ut_cnt_t ut_get_count(void)
FILE: subrepos/unittrace/unittrace.h
type UNITTRACE_COUNTER_TYPE (line 67) | typedef UNITTRACE_COUNTER_TYPE ut_cnt_t;
function ut_assert (line 131) | static inline void ut_assert(uint8_t cond)
FILE: subrepos/unittrace/unittrace_demo.c
function main (line 11) | int main(void)
Condensed preview — 32 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (146K chars).
[
{
"path": ".gitignore",
"chars": 21,
"preview": "/Debug\r\n/.vs\r\n*.bak\r\n"
},
{
"path": "LICENSE.md",
"chars": 1113,
"preview": "MIT License\r\n\r\nCopyright (c) 2018-2022 nqtronix (github.com/nqtronix)\r\n\r\nPermission is hereby granted, free of charge, t"
},
{
"path": "README.md",
"chars": 21669,
"preview": "\t\t\n<h1 align=\"center\" style=\"font-weight: bold; margin-top: 20px; margin-bottom: 20px;\">fifofast</h4>\n\n<h3 align=\"center"
},
{
"path": "fifofast.atsln",
"chars": 963,
"preview": "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Atmel Studio Solution File, Format Version 11.00\r\nVisu"
},
{
"path": "fifofast.componentinfo.xml",
"chars": 3815,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Store xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"AtmelPackComp"
},
{
"path": "fifofast.cproj",
"chars": 9906,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/m"
},
{
"path": "fifofast.h",
"chars": 22509,
"preview": "/*\n * fifofast.h\n *\n * Created: 23.10.2017 06:45:20\n * Author: Dennis aka nqtronix (github.com/nqtronix)\n *\n * Descript"
},
{
"path": "fifofast_demo.c",
"chars": 2166,
"preview": "/*\n * fifofast_demo.c\n *\n * Created: 17.09.2018 19:21:17\n * Author: Dennis aka nqtronix (github.com/nqtronix)\n *\n * Des"
},
{
"path": "fifofast_demo.h",
"chars": 2003,
"preview": "/*\r\n * fifofast_demo.h\r\n *\r\n * Created: 08.11.2018 08:46:08\r\n * Author: Dennis\r\n *\r\n * Description:\r\n * This file decla"
},
{
"path": "fifofast_test.c",
"chars": 19588,
"preview": "/*\n * fifofast_test.c\n *\n * Created: 08.11.2018 08:34:58\n * Author: Dennis\n */ \n\n#include \"fifofast_test.h\"\n\n//////////"
},
{
"path": "fifofast_test.h",
"chars": 1288,
"preview": "/*\n * fifofast_test.h\n *\n * Created: 08.11.2018 08:35:16\n * Author: Dennis\n *\n * Description:\n * This file contains aut"
},
{
"path": "subrepos/readme (license info).txt",
"chars": 175,
"preview": "This folder contains other projects with their own source code and associated files. The included LICENSE files ONLY app"
},
{
"path": "subrepos/unittrace/.gitignore",
"chars": 12,
"preview": "/Debug\r\n/.vs"
},
{
"path": "subrepos/unittrace/LICENSE.md",
"chars": 1108,
"preview": "MIT License\r\n\r\nCopyright (c) 2018 nqtronix (github.com/nqtronix)\r\n\r\nPermission is hereby granted, free of charge, to any"
},
{
"path": "subrepos/unittrace/README.md",
"chars": 12780,
"preview": "<h1 align=\"center\" style=\"font-weight: bold; margin-top: 20px; margin-bottom: 20px;\">unittrace</h4>\r\n\r\n<h4 align=\"center"
},
{
"path": "subrepos/unittrace/unittrace.atsln",
"chars": 965,
"preview": "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Atmel Studio Solution File, Format Version 11.00\r\nVisu"
},
{
"path": "subrepos/unittrace/unittrace.c",
"chars": 1507,
"preview": "/*\r\n * unittrace.c\r\n *\r\n * Created: 29.10.2018 20:34:06\r\n * Author: Dennis aka nqtronix (github.com/nqtronix)\r\n */ \r\n\r\n"
},
{
"path": "subrepos/unittrace/unittrace.componentinfo.xml",
"chars": 3822,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Store xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"AtmelPackComp"
},
{
"path": "subrepos/unittrace/unittrace.cproj",
"chars": 7604,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/m"
},
{
"path": "subrepos/unittrace/unittrace.h",
"chars": 6030,
"preview": "/*\r\n * unittrace.h\r\n *\r\n * Created: 29.10.2018 20:36:59\r\n * Author: Dennis aka nqtronix (github.com/nqtronix)\r\n *\r\n * D"
},
{
"path": "subrepos/unittrace/unittrace_demo.c",
"chars": 825,
"preview": "/*\r\n * unittrace.c\r\n *\r\n * Created: 29.10.2018 20:32:06\r\n * Author : Dennis aka nqtronix (github.com/nqtronix)\r\n */ \r\n\r\n"
},
{
"path": "subrepos/unittrace/utility/macros/com/com readme.txt",
"chars": 220,
"preview": "This folder \"com\" contains macros for common functionalities and can result in significant cleaner\r\ncode. In addition th"
},
{
"path": "subrepos/unittrace/utility/macros/com/macro_type.h",
"chars": 1400,
"preview": "/*\r\n * macro_type.h\r\n *\r\n * Created: 14.10.2018 14:34:03\r\n * Author: Dennis\r\n *\r\n * Description:\r\n * Provides typ-relat"
},
{
"path": "utility/macros/com/com readme.txt",
"chars": 220,
"preview": "This folder \"com\" contains macros for common functionalities and can result in significant cleaner\r\ncode. In addition th"
},
{
"path": "utility/macros/com/macro_array.h",
"chars": 803,
"preview": "/*\r\n * macro_array.h\r\n *\r\n * Created: 14.10.2018 14:38:19\r\n * Author: Dennis\r\n */ \r\n\r\n\r\n#ifndef MACRO_ARRAY_H_\r\n#define"
},
{
"path": "utility/macros/com/macro_math.h",
"chars": 1910,
"preview": "/*\n * macro_math.h\n *\n * Created: 14.10.2018 14:34:32\n * Author: Dennis\n *\n * Description:\n * Provides math operations,"
},
{
"path": "utility/macros/com/macro_type.h",
"chars": 1400,
"preview": "/*\r\n * macro_type.h\r\n *\r\n * Created: 14.10.2018 14:34:03\r\n * Author: Dennis\r\n *\r\n * Description:\r\n * Provides typ-relat"
},
{
"path": "utility/macros/mpl/macro_cat.h",
"chars": 1558,
"preview": "/*\r\n * macro_cat.h\r\n *\r\n * Created: 14.10.2018 11:09:54\r\n * Author: Dennis\r\n *\r\n * Description:\r\n * concatenates up to "
},
{
"path": "utility/macros/mpl/macro_narg.h",
"chars": 1581,
"preview": "/*\r\n * macro_narg.h\r\n *\r\n * Created: 14.10.2018 11:01:15\r\n * Author: Dennis\r\n *\r\n * Description:\r\n * Returns the number"
},
{
"path": "utility/macros/mpl/macro_vfunc.h",
"chars": 1678,
"preview": "/*\r\n * macro_vfunc.h\r\n *\r\n * Created: 14.10.2018 11:05:26\r\n * Author: Dennis\r\n *\r\n * Description:\r\n * vfunc expands to "
},
{
"path": "utility/macros/mpl/mpl readme.txt",
"chars": 281,
"preview": "This folder \"mpl\" (short for \"macro programming language) contains macros for more advanced\r\nfunctionality. It is not co"
},
{
"path": "utility/macros/readme.txt",
"chars": 1929,
"preview": "\r\n\r\n\r\nMACRO NAMING CONVENTIONS:\r\n\r\nAlthough macros make many complicated things easier they can also easily generate pro"
}
]
About this extraction
This page contains the full source code of the nqtronix/fifofast GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 32 files (129.7 KB), approximately 38.4k tokens, and a symbol index with 37 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.