5 ms·
As an embedded hacker, I'd love to read more about some of your ideas.
by joe_bleau 18y ago
As an embedded hacker, I'd love to read more about some of your ideas.
- coryrc 18y agoWell, I'm a big Lispnik and wish I could use it for everything -- so I try! but AVRs (an 8-bit microcontroller) can't run managed code anywhere near fast enough. My code is completely statically or stack allocated and almost all structures are in the global namespace, so it is nearly perfectly optimized. A lot less is in the global namespace on ARM processors because the word size can index the entire memory. One way I've used Lisp is to generate code in either an if-else form or switch-case form. If you have a sparse lookup table (say, 30 values out of 256 map to a byte) you don't want to allocate space for those useless 226 values, so you could write out each by hand. Instead, I maintain a list of pairs at the beginning of the file (along with all the other constants) and I wrote a CL function that converts them to C code. Sometimes using if-elses results in a smaller binary verses switch-case, and sometimes the other way. I only have to change two words and recompile to test the result both ways. Doing this by hand for each statement... yuck. But I saved over 100 bytes on my last project, enough to get a necessary feature working before shipping. I use emacs, so when I edit code inside the lisp preprocessor tags, I type 'M-x lisp-mode' and press enter. Then emacs properly indents the lines and I can use SLIME to execute the preprocessor functions right there. Sometimes I need to add the comment: ;| as the first line because the lisp mode catches a pipe in the C source (CL block comment) and formats everything incorrectly. Conversely, I sometimes have to place this after a Lisp section: //' ...for the single quote (even inside a comment) freaks out the emacs reader. Not sure how this works in emacs23 yet. Basically, I use Lisp to remove as many assumptions from the code as possible. As a result, my devices work just the way I say they will -- contrary to the other programmer here. Sadly, my coworkers think waterfall is a genius new idea they came up with. It's quite frustrating because I'd still like to learn from others, but I fear going somewhere where I'd be forced to follow a bad methodology (at least here I have the freedom to follow what I think are best practices, on the projects I develop/lead/design). Back to the lisp preprocessor: When I call 'make' each source file is attached to the standard input of a lisp process launched off a file of <processor_name>.lisp, and the output is written into <original_file>.c.cpp-lisp.c (yes, very ugly, but it works). The gcc is run as normal, with the output written back to <original_file>.o to clean up the file structure. One problem I ran into is CL up-casing all input. Fortunately, avr-libc has all constants as uppercase, and most of my preprocessor passes through strings unchanged (if it makes sense), but I have had many times when I had to work around this. The project above should allow these problems to be solved easily. Atmel releases all the register information (names, bit names, read/write/both, etc) in an XML file. The next step in upgrading my preprocessor is to automatically generate mapping functions from generic names to specific names from this XML file (which is first processed by xml-to-sexp). This function lets me write something like this: (set-bit 'RXENn :sequence COMPUTER_UART) and then change the zero to one in (defparameter COMPUTER_UART 0) to use the second uart instead of the first. If this was done using pure C, either a search and replace for RXEN0->RXEN1 (and you'd have to check that you don't use any other UART registers, nor in any other files, etc) or you have series of #define's at the top of the file like #define COMPUTER_UART_RX_ENABLE() ... Finally, the other major problem is changing output pins. As the AVR is 8-bit, the output pins are in groups of eight. Normally, you would have code like this: #define LED_PIN PC1 ... PORTC |= (1<<LED_PIN); but, what if you change LED_PIN to be PB0? Now it is on PORTB so you must change everywhere you write it. So you do: #define LED_PIN PB0 #define LED_PIN_PORT PORTB #define SET_LED_PIN() LED_PIN_PORT |= (1<<LED_PIN) ... SET_LED_PIN(); well, then you need CLEAR_LED_PIN(), and SET_LED_PIN_AS_OUTPUT() and SET_LED_PIN_AS_INPUT()... all of sudden you want one little constant and you get five extra garbage lines of boilerplate. Instead, my mapping function knows the port of PB0 is PORTB so you only need one line: (defparameter LED_PIN 'PB0) Now, if you end up needing more direct control, you can still use the Lisp code to generate assertions. Anyway, all the problems in these two posts are some of the most commons ones brought up to the forums at avrfreaks.net and that I've experienced, so I wrote these tools to mitigate them. Unfortunately, it is too gross (in the dark corners you can find some of my first real CL code!) and too poorly designed (should be automatically generated based off Atmel XML files) to be released to the public, but the proof is in the pudding: I came back to a six-month-old project, made what could have been major changes (we were upgrading the product) in less than a day and everything worked the first compile and passed all the "QA" tests.
- yters 18y agoThat is pretty sweet. On a much less cool level, I've been designing a similar system to do some basic formal verification of my java code.
- bayareaguy 18y agoYour system sounds like Cog http://nedbatchelder.com/code/cog http://nedbatchelder.com/code/cog except with lisp instead of python.