Fellow Freaks,
I'm making a framework for Tiny26 (and more small devices, in the future). The goal of this framework is hide detail and present a programming API with easy to use functions and arguments. This is aimed at people who know C.
Since this device is very short on memory, I'm thinking hard on code size.
I reached a point where the "user-friendliness" of some functions demands calculations that take up to much space, even when invoked with constant arguments.
One specific example is the timers. Let's pick the function
timer0_SetupPeriodicHz(uint32_t frequency)
which mainly sets the prescaler and initial counter value.
Because of the prescaler fixed options I need to perform a small algorithm to calculate the values, and not only a simple (C) mathematical expression (which the compiler would perfectly optimise).
In a "perfect World", if my application only invokes timer0_SetupPeriodicHz() with a constant argument, which is almost always true, I would like the "compiler" to run the algorithm and replace the call by its constant results. When *all* inputs to a function are constant, the result is also constant. Additionally, I would like to be able to issue warnings or errors, like for example if the requested frequency could not be achieved with targeted CPU clock.
This would allow me to have simple high-level calls like timer0_SetupPeriodicHz() giving arguments in Hz, and still have the smallest code size possible.
But, of course, this would force the compiler to include a C interpreter!
I thought of and tried to implement several methods using macros, but without success. I concluded that I could do it in a reasonable time only by inserting a new processing step in the build pipeline, and ended-up doing it. I made a simple parser which I named "AVR-C preprocessor", which runs between the C preprocessor and the C compiler.
This allows me to have "calls" to special macros that make complex compile-time calculations. For example, my current implementation of the function mentioned above is
#define timer0_SetupPeriodicHz(frequency) \ { \ TCCR0 = 0; \ TCNT0 = _timer_cnt_init[0] = [[t0cnt_hz frequency]]; \ TCCR0 = (TCCR0 & 0xFC) | [[t0pre_hz frequency]]; \ TIMSK |= _BV(TOIE0); \ }
The "AVR-C preprocessor" runs the "special macros" inside [[ and ]] and replaces the invocation by the macro's result (the macros are pre-defined functions in TCL).
I also thought about having a tool to calculate aside the values (like some of the existing tools, as AVRCALC), but putting the values in the code is more friendly and improves code readability. Also, this extra pre-processing tool is fully transparent, you don't notice it's there; all it's needed is a change in the project's makefile.
Well, I was wondering if anyone has a better idea. Any thoughts?...
Edit: fixed spelling errors