A few weeks back, I wrote about my attempt to translate a low-fidelity circuit board design into something that could be run through a laser cutter. Today, Ponoko delivered the results:

You can click through to a large, high resolution image. In a word, it came out beautifully.

I do work surrounding the use of parallel languages in embedded applications. This summer, along with two students, we began work on a novel control system for an unmanned aircraft (a UAV), and managed to achieve autonomous level flight. Last term, Rich Bowden (Environmental Science) asked me if we could do a sensing project in Environmental Research Methods. During the break, I developed a board, built some prototypes, and wrote the control control code. I’m now developing the documentation (video coming soon!) for students, so they can assemble circuits and deploy their sensors to collect data. It’s exactly the kind of cross-disciplinary opportunity that we want to create. (Next time, we’ll get CS students involved early, and they can help develop the control code, and possibly even the board design.)

Sadly, we won’t be using these laser cut boards for this project; instead, the students will be poking holes in manila folders, and it should go well enough. Why not? First, the lead time was several weeks – it takes too long to send out for the boards to use in any “impromptu” manner in a classroom or research setting. Second, Ponoko charged me $21 for that board.

How is this a $21 piece of cardboard? The laser time cost $2/minute, and this design took just over 5 minutes to engrave and cut. The shipping was a flat rate of around $8. And, the cardboard… $0.41. It was worth it for a prototype, because I know that a laser cutter will meet real needs, and have high utility in multiple educational and outreach contexts.

I have students prototyping sensors as part of their senior projects, building robots as independent studies, core research that involves prototyping and construction, and I’d love to be doing more tangible, computational work across disciplines (eg. with colleagues in Art, Environmental Science, and so on). I’d go broke trying to do this via a service like Ponoko, and the turn-around (for prototyping and exploration) would kill me. Waiting three weeks for a design to come back means that, if you’re lucky, you can do two design iterations (maybe three) in a semester.

Hence, we’re going to try and get a laser cutter. $20,000 is my round-numbers target for a laser with a 2′ by 4′ bed. At the rates Ponoko is charging, it will pay for itself after 10,000 minutes of usage. We have excellent connections into the community (schools, etc.), and I think it will be easy to run this printer for all kinds of awesome projects that let teachers and students at the College and from area schools come together to explore the intersection of computer science and manufacturing.

As a member of the faculty, it is often hard to justify spending time learning new technologies. This, to me, is a rather ugly Catch-22: to learn a new technology, I must justify it either with a publication or a clear, immediate application to my classrooms. If you’re driving towards a clear, concrete deliverable, it really isn’t exploratory learning… it’s work, under pressure, with a deadline. That’s why I’m excited about a project I’m undertaking this semester with one of our seniors.

The student was looking for a 2-credit independent study (this is roughly a 1/2 course load). We brainstormed a great deal, and in the end, the student thought they’d like to dig into some more Scheme programming, so we decided a compiler might be a good way to focus our efforts. However, I didn’t expect that it would end up being a project with so much potential.

At Indiana, I saw how we can write compilers front-to-back in the nanopass style. However, several grad students in the languages group insisted the right way to write a compiler was back-to-front. What does this mean?

First, you take your assembly language, and generate an executable.

In our case, we will be working with LLVM Compiler Infrastructure project. Specifically, we’ll begin by writing programs in LLVM assembly, and using the LLVM toolchain to turn that into a native executable.

Although our input language is not C, we might imagine starting with a simple program in our language LNOT (L0) that is nothing more than the number 8. A single expression. When compiled and executed, it should return the number 8. Expressed in C, this would look something like:

1
2
3
4
5
6
7
int main(int argc, char **argv) {
  int retval;
  /* Assign the result of our entire program to retval */
  /* In this case, the entire program is the constant 8. */
  retval = 8;
  printf("%d\n", retval);
}

The disassembled LLVM looks something like:

1
2
3
4
5
6
7
define i32 @main(i32 %argc, i8** nocapture %argv) nounwind {
entry:
  %0 = tail call i32 (i8*, ...)* @printf(i8* noalias getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), i32 8) nounwind ;  [#uses=0]
  ret i32 undef
}
 
declare i32 @printf(i8* nocapture, ...) nounwind

As we are writing the compiler “back-to-front,” we now need to take one step backwards towards our input language. Because we are working in Scheme, we’ll represent our language in S-expressions (to minimize parsing effort). The first pass (or, the last pass, in front-to-back thinking) will be one that transforms an S-expression based syntax for LLVM assembly into actual LLVM assembly. Therefore, the input language to the penultimate pass might look something like:

1
2
3
4
5
6
7
8
(define (main i32)
  (args [i32 argc]
        [(i8** nocapture) argv])
  (entry
   (tail-call (printf (i32 (i8* ...)) ...))
   (ret (i32 undef))))
 
(declare (printf (i32 (i8* ...) ...)))

This elides many things, but my point is that we might take the ASM of LLVM and “lift” it into a syntax that can be easily pattern-matched in Scheme and manipulated. As we work from the back-end of the compiler to the front, our next step might be something trivial: for example, we might get rid of the entry form, because (with a very simple input language), we know we will only ever have one function, and therefore we can generate this automatically. Or, we might be more aggressive, and acknowledge that we don’t need to generate any of the wrapper code… so, we should only need to develop a language for statements that will proceed and/or live on the right-hand side of the retval assignment statement.

For me, it is exciting on a number of levels. I get to learn with a student, which happens less often than one would like. I don’t know LLVM, and am glad to be spending time getting to know it. Also, I haven’t ever written a compiler back-to-front, and evaluating this (both technically and pedagogically) has value — I’ll probably be teaching our compilers course next spring. And, I get to do some hacking, which I simply don’t get much time to do.

When we have a repository up, I’ll post a link to it, although it won’t be an “open” project, per se. We’re not doing this to engage in a new compiler authoring project so much as learn, but if you want to, you can watch us as we work.