I’m not dead (yet)

It’s almost over. I have to get my thesis signed, take a final, and give a demo and then that’ll be it. Degree #2 will be in my hands and I can begin the process of… well I’ll tell that story later.

I’ve been able to get practically nothing done on personal projects this year. I did manage to do some work with ACRIS and lpctrl to build an interface that would let me control my lighting system with my Novation Launchpad. I used this for a performance for MIT’s annual Steer Roast. I’ll do a demo video for this at some point soon. It was pretty cool, although I ran into some latency and crashing issues.

But I don’t want to talk about that either. I want to talk about the beat tracker I built and what it taught me about Bluespec. As I had posted before, I got it working a while ago and have been tweaking it a bit since then to make it work better. It figures out the tempo of most tracks pretty well, but there are some serious problems with the design and they all stem from the beat classifier module, i.e. the module that reports when it thought it detected a beat. First, it’s not very accurate — it should be using the variance of energies to determine how much higher the most recent energy has to be compared to the average energy. Secondly, it doesn’t report on how confident it thought it saw a beat. This is important because it should dictate how much the metronomes adjust their own phase.

I’m going to post a demo video up soon.

I made this project for Arvind’s Bluespec class, 6.375. Arvind was the professor whose grad student originally came up with the idea for Bluespec. The class had a bunch of labs where you’d learn different advantages of Bluespec over traditional hardware design. Theoretically, you’d do these labs, discover how magical this new programming paradigm is, and use it forever after for all your hardware design needs.

Now, don’t get me wrong. There’s some stuff that Bluespec does right. Or at least, there’s some stuff that the Bluespec developers have thought about that I think was good for them to think about. For example, Bluespec makes it easy to build complex state machines and circular pipelines using properly crafted rules. It features some nice libraries for manipulating numbers. But it doesn’t take these far enough. I had some thoughts with regard to developing a new language that would make it easy for developers to create their own number systems by specifying different attributes or capabilities (saturating vs. non-saturating arithmetic, unsigned vs. two’s complement vs. one’s complement vs. Gray coding number representation, etc.) One of these days, I’ll try to expand on that underlying idea to see if it’s actually useful… I think it will be.

Bluespec is great for prototyping and simulating your ideas — it allows you to produce a lot of hardware very fast and play around with subtle configuration changes. It makes producing a gold-standard, bit-accurate model really easy. This does not, however, mean that Bluespec is good for producing an actually implementable design. Something occurred to me while I was watching the other students in the group present on their project results: Bluespec adds a lot of overhead because it deceives people into thinking how much or little hardware a given portion of their design might produce.

For example, it’s pretty easy to make a FIFO in Bluespec. Do you want a FIFO? Easy, just mkFIFO(). But what does that actually produce? Well, it draws upon a 150-line verilog file corresponding to a FIFO of depth 1 (larger FIFOs are chained together, I think). And this file actually produces an enormous amount of hardware. But, it does make producing a nice pipeline much easier.

Most students in the class took an existing algorithm that solved some problem (like image compression, flow analysis, etc.), re-implemented it in Bluespec, and tested it on an FPGA to see if their results were actually accelerated. The interface that most students to push data onto and pull data off of the FPGA used was called SCE-MI; we had used this framework a few times during the class. But, it’s not really suitable for a lot of applications due to bandwidth and latency limitations.

It’s just… ugh, SO MUCH STUFF when the designs don’t have to be so large.

And it showed in the students’ results. Most students found while they achieved correctness, they got little to no actual speed-up. I also realized that most students in the class were algorithms/comp sci people and I was more or less the only hardware person in the group. It seemed like a lot of those people drew incorrect conclusions about hardware design being inefficient and not worth it. I just wanted to keep yelling “no, it’s Bluespec, I swear!”

I was the only one who had a live demo at the presentations. I was even lucky in a sense; I ran out of time to do a lot of the more complicated things that I wanted to do because it just took me forever to figure out how the hell Bluespec does things. For example, it turns out that their FixedPoint library makes some strange choices in its arithmetic library that make it impossible to do what I want it to do. Since my project also sat on a measly Spartan 3A FPGA, it was also hard to meet timing and resource constraints. In my talk, I kind of ripped on a few parts about BlueSpec that I really didn’t like and then later realized that Arvind had invited Bluespec engineers to the presentations. Oops. :)

They were actually surprised that I managed to get Bluespec to compile to a verilog module and include it in a VHDL project. Apparently that’s very rare. But to me, that’s probably the best way to use it: design optimized hardware with VHDL or verilog and then include the parts of your design that are complicated but not yet optimized (i.e. the Bluespec components). Then, as you optimize each of those modules, you can replace the Bluespec modules with low-level ones.

I think the summary here is this. At the beginning of the class, Arvind made the analogy that verilog is to Bluespec as assembly is to Java. I agree with this. Bluespec abstracts away a lot of low-level language features just as Java does to assembly; it makes accessing some of those low-level features possible, but inconvenient. The second part of his argument is that nowadays, we don’t care about how many registers our CPUs have if we write in Java, but we can still produce code that does things in a reasonable amount of time thanks to the fact that the tools that translate what we design into something machines understand are very good. I do not believe that this same philosophy ports to hardware. Hardware design is very different than software design. The reason this argument doesn’t hold in my opinion is that for the vast majority of hardware design cases, you are going to be pushing the very limits of what the hardware has available. In fact, given that microprocessors are getting better and encroaching on territories that FPGAs typically held, the remaining territories are areas where you’re trying to do super-optimized stuff. That doesn’t happen in software. In software, the majority of the use cases make it good enough to use a high level abstraction to accomplish things. In hardware, the majority of the use cases necessitate that you do a lot of low-level optimization. This is the fundamental fallacy of Bluespec, in my opinion — it does not properly reveal lower-level techniques for optimization.

Nevertheless, I had a lot of fun in this class and I learned a lot. My work with Bluespec really got me thinking about how I could design a hardware description language that could utilize some of the philosophies of cleanly defining state transitions and coming up with a better way of handling the translation between numeric representations.

First Results from Beat Tracker!

So I finished a preliminary version of my live beat tracker, bt. I fed in a 126 BPM song and got:

The software reads a data stream from the FPGA and prints out a cute little UTF-8 table and keeps track of the best average tempo. The FPGA handles all of the hard work: running a lot of metronomes (I can fit upwards of like 80-100 on there), and classifying beats.

That’s the good news. The not so good news is that the beat classifier, the thing that says “I think I found a beat, does it match any of the metronomes?” needs some serious improvement and tweaking before it can actually classify most songs. K-Pop works really well right now because the differential between beat energy and not-beat energy is very large — a metronome is basically built right into the track. On the other hand, it doesn’t get dubstep at all.

Not-Quite-Tinfoil-Hat-Grade Security Swiss Army Knife

In times like these where I’m taking two oh-shit-this-is-turning-out-to-be-more-work-than-I-thought-it-would-be classes while trying to finish an arguably overly-long-winded thesis (I’ve got like 3 of the 7 chapters to go and I’m already at almost 80 pages and like a quarter of the amount of figures I plan to include), yes, in times like these, I call upon every brain cell to stay focused to the tasks at hand so that I can make it through this semester while retaining at least a decent portion of what little sanity I still have.

And what does my brain do? But of course, it can’t stay focused on a single goddamn thing. Awesome. Instead, I seem to be coming up with ideas for new side projects, taking on more tasks and stuff that I know I will have little time for, and just generally struggling to keep my head on straight for a few hours.

I’ve been pretty good lately. I’ve been generally keeping up with 6.302, an analog feedback class that I decided to take to try to push myself out of my comfort zone a little bit. And as much as Bluespec makes my brain hurt when I try to write it, I seem to be making at least some progress on my final project for 6.375. And my thesis, well it’s getting there.

Except that for the past few hours, I seem to be gripped by the idea of a high security swiss army knife of sorts to protect important data. I basically saw a way to combine two previous project ideas (secure password storage and two-factor authentication dongles) under one master framework that, as best it can, balances personal security with convenience.

Here’s the idea in a nutshell. There are a series of modules, each with some function (like displaying a two-factor auth code or providing a secure key via mass storage or whatever). Some may be self-powered, but they don’t have to be. Each module has an RFID base station transciever chip on it that can talk to a challenge-response RFID transponder (I was originally looking at the TK5561 chip from Atmel, but they appear to be phasing out in favor of their 13MHz cousins). In order to use the module after it’s been powered up, you need to wave your RFID transponder tag over it. This will provide a key to the module which will decrypt sensitive data and unlock the module for use. When power is removed from the module, it is effectively automatically locked again. If the RFID transponder is destroyed, then the modules are basically rendered useless.

Oh yeah, that brings me to my next point. They all have some common interface to a computer so that they can be configured with the appropriate keys and so forth. In order to be able to configure a module after it’s built, I was thinking of having a limited password system. So, the user has like 5 tries to enter the correct maintenance password and if they fail, the device will lock forever and nothing short of uncapping the chip and flipping bits will do anything.

The primary use case in my mind is a hardware equivalent of the Iron Key. The Iron Key system is not my favorite because it requires the user to run a program on the host computer to unlock the drive before they can access it. I believe that the threat model they’re trying to fight is just the simple “oh shit, I dropped my flash drive somewhere between my office and the parking lot” and not trying to fight cases of malware on a given computer they might plug the drive into.

But the thing that bothers me is that I don’t want to have to run a program and type in a potentially weak password in order to be able to mount the drive; I’d rather have a hardware solution. RFID is perfect for this. Plug the module in, wave your hand over it, and the module unlocks and appears as a flash drive on your computer. I was thinking of implementing this with something like an ATMEGA32U4 with LUFA and a flash chip. The flash chip would hold some encrypted data and the AVR would essentially act as a USB storage driver that decrypts the flash data on demand as it flows.

We’re not talking a very big or fast drive here — a few megabytes at most and probably slow as hell. But that’s okay. I would mainly use this device for storing things like private certificates that themselves are used to encrypt and decrypt large blocks of data on the computer itself.

The second module I’d like to implement would be a device capable of performing Google’s two-factor authentication scheme. Their spec is published right now (it’s a pretty standard 2FA algo) and it’s not too hard to implement on an AVR (someone even did it on an old PocketStation device. My version would probably just use LEDs to display the decimal digits in binary., but yeah, same deal: press a button to fire it up and wave your RFID over it to get the unlock code.

I was thinking of putting the magical RFID transponder that unlocks everything onto my watch or something somehow; i.e. attach it to something that I simply never let out of my sight.

I think this approach is fairly secure, which is why I call it not-quite-tinfoil-hat-grade. It doesn’t handle cases where the machine you may be trying to use your module on itself is compromised, but I don’t intend to use it for that purpose. I’m only going to use them on trusted systems.

Maybe I can even figure out how to make a really large and decently fast flash drive so that I could run an entire VM from it. That’d be cool.

Woo paranoia! All this just to secure a couple of bank and email passwords. Overkill simply isn’t in my vocabulary.

bt – Live Hardware Beat Tracker

This hasn’t been a great semester for my blogging… I’ll probably post a massive update on stuff that’s happened so far in 2013 in a few weeks, but for now, I thought I’d talk a bit about my final project for 6.375 — a digital design class taught in Bluespec.

So first of all, Bluespec aims to provide a high-level approach to hardware design, which is cool and all, but frankly, I could write volumes on the poor design decisions it makes. In some ways, I regret taking this class (and I hope my instructors don’t come across this post until after the class is over because they’re responsible for the initial design of Bluespec). In fact, I think I’m going to compile my thoughts on hardware design in a separate post at some points, but let’s leave it at this: even if we ignore the fact that the language itself is incredibly poorly designed (really looks like it was pieced together at random), Bluespec has a lot of fundamental design flaws that make it difficult if not impossible to predict how a given design will look as it’s implemented in hardware. There are a lot of good philosophies that Bluespec brings to the table, and I think that many of them can be implemented in VHDL, but this fundamental overarching limitation is really, really frustrating. I believe that the analogy “Bluespec is to Verilog as Java is to assembly” is not accurate.

Anyways, whatever, the class has a final project and I like absurd final projects. I have to write (most of) this project in Bluespec. So, I came up with the idea of a piece of hardware that, in realtime, estimates the tempo of a live stream of audio.

I’ve seen a few methods for solving this problem before. Turns out that two of my friends independently wrote their own software versions. But I wanted to do a hardware one because I thought that I might be able to improve accuracy and convergence time by using more complicated signal processing algorithms. I’m going to also use a slightly novel architecture that takes advantage of the parallelism of hardware. The way it’s going to work is that audio data will be fed into a beat classifier, whose job is simply to identify potential “beats” in the stream. Ideally, it’d be able to identify a probability that it thought what it saw was a beat. At the same time, there are a bunch of metronomes that are just running freely at different tempos. E.g. one might be going at 140 BPM, another at 120 BPM, etc. The number of metronomes is probably going to be limited only by how many I can physically fit on the FPGA. I implemented a basic form of this system purely in Python, which you can find in the sw/sim/ directory of my github repository.

Each time the beat classifier thinks it sees a beat, it sends a signal to all of the metronomes, which then compute the phase error between a subdivision of the received beat and their internal sense of beat. The metronomes then adjust their phase to be closer to the current time based on how likely the beat classifier thought it saw a beat. They also send their phase error to a master metronome controller, which keeps track of this data and sends out essentially a probability distribution of potential tempos to the user.

The last thing is that the metronome controller will be able to adjust the target tempos of the metronomes so that it can start out over a broad range of potential tempos and then zoom into the most likely candidates to get a more accurate result.

Here’s a rough estimate of what the architecture will look like:

Okay, so I’ve got the algorithm idea, now what about the actual implementation? Instead of using the class hardware, I decided to use my own because I will probably be doing a lot more hardware testing than other groups (which will mainly just simulate their design and throw it on the FPGA at the end). I’m using the Spartan 3-AN Starter Kit, which has a bunch of useful features, such as an on-board ADC with pre-amp. The implementation architecture will look like this:

The first step was to set up the outer framework. I was hoping this was going to be easier than it actually was, but alas, apparently nobody has actually made an interface for the ADC and pre-amp. Well, one person did, but it was horribly written and didn’t work. So, I wrote my own controller from scratch. After startup, it waits for an instruction to initialize the pre-amp. When it receives that, it sets the pre-amp gain and then waits for a conversion request. Each time it receives one, it initiates a conversion to the ADC and returning samples for the two input channels. I’m also distributing the module separately (with interface instructions).

One slight issue with the ADC: it’s centered around 1.65V and audio is typically biased around 0V, so I had to add a little DC biaser to my board:

It simply divides VCC (3.3V) by 2 and mixes that with the two incoming channels.

So right now, the hardware framework is basically set up and ready to go.

knocker – Fast Port Knocking in Python

I use port knocking on my most sensitive systems. “Security through obscurity doesn’t work yadda yadda” aside, I think it’s a pretty effective way to hide ports from being open all the time.

Previously, I had written a shell script that invoked nmap to do the job for me, but I wanted to streamline it, make it faster, and more generic. So, I wrote knocker, a configurable port knocking tool.

Basically, you give it the following info (either in a ~/.knocker config file or on the command line):

  • target host
  • target port to open
  • command to run after opening
  • open knock sequence
  • close knock sequence

It then checks to see if the target port is already open. If it isn’t, it knocks the open sequence. It runs whatever command you want and then knocks the close sequence.

There are plenty of ways it can be improved, but it’s working happily for me right now.

I’d like to write a knock server (right now, I use knockd) that uses one-time authentication to cycle through sequences of ports, either as a time-based or counter-based system. Then, replay attacks are next to impossible!

I hate backups.

Ah, backups. I hate backups. Everyone hates backups. But the only thing worse than taking backups is not taking backups.

I have a couple of scripts that I’ve written to take backups of my life and now I’ve gotten them to a point where I think other people might find them useful.

I used to use dar. dar is pretty cool because it lets you perform incremental, encrypted backups. It’s pretty not-cool because it’s hard as hell to use. In fact, I’ve never been able to get incremental backups working with encryption. I’ve gotten tired of trying.

So I decided to go back to the one true archiving utility, tar. Few people know that tar actually also supports incremental backups using the \g command. So, for example, you can do:

1
$ tar -czp -g incrlist.snar -f first-backup.tgz /some/directory

Edit files and stuff, and when you want to do the next backup, do

1
$ tar -czp -g incrlist.snar -f second-backup.tgz

.

You can then restore these backups by first restoring first-backup.tgz and then second-backup.tgz, using the -g switch as before. This will even delete files that you deleted between taking first-backup and second-backup.

Okay, cool, so let’s make that into a script. I wrote a script for backing up my home directory and other important stuff on my hard drive to an external drive. Unfortunately, I was running out of space on my RAID array, so I incorporated 640GB of a 1TB drive that I used to use as a “buffer zone” for backups into another drive for the array. So, now I just issue my backups directly to my external drive.

The system generates one full backup each month and then incremental backups for the rest of that month. It’s configured by sourcing a config file that sets the appropriate variables. An example config file might look like this:

1
2
3
4
backupdir=/mnt/backup
targetdir=/home/me
prefix=home
excludes="--exclude=.VirtualBox --exclude=*.pkg.tar*"

Line 1 specifies where to store the backup (and look for an existing full backup from the same month to diff against). 2 says what to backup. 3 specifies a prefix name to apply to the archive. Finally, 4 offers the ability to exclude files. Config files can use the variable $scriptdir to reference the directory that the backup script is stored in and $configdir to reference the directory the config file is stored in.

You can even specify commands to run before and after the backup by filling in the functions precmd() and postcmd():

1
2
3
4
5
6
7
precmd() {
    echo "This is run before tar."
}

postcmd() {
    echo "This is run after tar."
}

So what about encryption? Well, since I only take backups to one specific drive, I’ve decided to opt for drive-level encryption with LUKS/dm-crypt. This is a far more robust solution that has also proven to be secure.

I’ll talk about how I back up my server at some other point.

Making Xilinx Suck Less: A VHDL Preprocessor

Oh VHDL, you make my life so wonderful. I just want to be able to enable or disable different debugging features at compile time. But no, there’s no real preprocessor support in the VHDL world.

So my solution was to write a wrapper script that fakes out GCC into using its preprocessor and then generating a new VHDL file. It takes two files as input: the prj file you would use for XST and a config file that has a bunch of key=value pairs that get passed as directives for the GCC preprocessor.

For each file, it then invokes the following magic:

1
gcc -Dwhatever=whatever -traditional-cpp -E -x c -P -C file.vhd

This causes GCC to treat the VHDL file as just a generic C file and only processes the standard preprocessor directives like #if.

It then saves the result to a new file. So if you were processing file.vhd, it would generate (by default) file.gen.vhd. It stores these files in the same directory as their pre-preprocessed versions.

Finally it creates a new prj file that you should pass into XST.

So, it’s super easy to use. All you need to do is run vhd-preproc.py -p /path/to/project.prj -c /path/to/config and all your files will be preprocessed.

ACRIS 2.0 Firmware

So close and yet so far… Long story short: after assembling everything and fixing a whole host of firmware problems, ACRIS boards are about 0.002″ away from being done. Yup, one of the hole sizes is wrong, but other than that, the boards are perfect!

Buuuuut ignoring that, I’m very happy with the results. Take a look:

So what you have there is, among other things, 12-bit resolution! That’s right; the new firmware finally supports the full 4096 levels of brightness of the LED drivers instead of 256.

The new firmware supports a couple of commands: low res setting (8-bit like before) and high res setting (12-bit). For each of these, there is a subcommand to set either all LEDs on the board to be the same color or each LED separately.

The protocol is not very complicated: SYNC CMD ARG0 ARG1 ... ARGn

SYNC is now 0x55 instead of 0xAA. I made this revision backwards compatible by making the command the old SYNC command. That way, as long as you send the new SYNC command before that (which would just get ignored by the old firmware), you can still control both firmwares at the same time. This is probably entirely unnecessary, but whatever. I thought it was cute.

I describe how the commands work in detail in the firmware README.

The high res commands pack 12-bit values into a series of bytes and then those values are unpacked in the firmware. The code is not super fast, but it’s not horrible either. I wish I could do some kind of hashing thing to speed up the unpacking process, but the memory requirements there would be ginormous.

The new command system isn’t the only thing that I added. One common failure mode in the past was that the voltage would sag causing the micro to stop running and then the LED drivers would go full brightness. The problem was that when you plugged the board back in, they would retain that state and it would happen all over again. I thought the fix was to just add a pull-up to the blank pin which is usually just controlled by the micro. This would prevent the pin from floating when the micro was browning out and would therefore shut the drivers off. However after I tested this fix out (BTW I forgot to solder one of the resistor pads down so I was very confused for a while why it wasn’t pulling up at all) the failure changed to just continually resetting the micro followed by a brief draw of high current and voltage sagging.

The problem turned out to be that I completely forgot to clear out the LED driver shift registers BEFORE starting them… just a totally stupid error on my part that’s been since fixed.

After I finished writing this new firmware, I used a little pre-processor magic to handle the hardware differences between the two board revisions. So, making with BRDREV=1 makes the project for the first revision and BRDREV=2 makes it for the second version. This allows me to have unified firmware.

I want to do a few more things with the firmware. E.g. I want to add some commands that would allow me to read the status of a board back on the bus. The hardware is now all there to do this and it would be good to know when the board is overheating. I could also add firmware-side brightness limiting when overheating occurs. I also need to finish the bootloader at some point — it still lacks a mechanism for verification.

After getting everything running, I finally was able to solve the question that had been bothering me the most: would my board be able to sustain 5.4A, the maximum current for all channels?

For this test, I hooked up separate 5V for the logic and started the LED power voltage out low. I then told the LED controllers to output full brightness and started ramping up the voltage. Small problem: the resistive loss on the wire was sizable as the current draw grew. I had to get up to around 11V output to hit the correct voltage drop for the LEDs on the other side. Things got a little melty but I sustained this for almost a minute before turning it back down as I wasn’t heatsinking the drivers at all. The point is that the board itself could handle that kind of current… I feel like I actually designed something correctly for once. :)

Here is a list of the things that the new board revision fixes:

  • screw terminal option for LED outputs
  • if you don’t want to do that, then you can twist and solder the wires to the board securely
  • data direction pins on the RS485 chips have pull-ups on them to prevent multiple devices from trying to drive the bus on system startup
  • blank pin on the LED drivers has a pull-up on it to prevent the drivers from trying to drive the LEDs before the system has started sending data to them
  • power connectors have the right pin ordering
  • power planes reduce noise and can handle the full current rating of the drivers
  • error pins of the tlc are brought back to the micro in order to read thermal errors
  • cleaner routing
  • board fits within iteadstudio’s 10cm x 10cm $35 color soldermask option

I’m planning on doing one minor revision to the board to address these problems:

  • power connector + slot is too small
  • soldermask accidentally applied over the back terminals
  • some silkscreening is illegible

I probably won’t ever order that revision unless I somehow need more LED controllers for something. Maybe an externally-funded project? Hopefully?

How to Not Suck at Transcoding Music

Sometimes I get really interested in something and can’t sleep and end up spending all night working on it. This was one of those nights.

I wrote flacsync a while ago in order to make an MP3 with suitable tags for every FLAC I own. I do this so that I can actually play my favorite music on my iPhone and also so that I can keep a lightweight copy of my music database on my laptop, which has far less space than my desktop.

The idea was to keep a database containing an MD5 hash for every FLAC I have and whenever I run the script, check that hash to see if the FLAC has changed and needs to be re-transcoded. If so, transcode it and store the new hash to the database.

I made some remarkably stupid initial design choices. I knew that I wanted to thread it in order to maximize throughput, but I had some ridiculous bottlenecks. For example, for some reason, I thought it would be a good idea to use a SQLite database to store the hashes and then have a complicated DBWorker thread that would interface with all the processing threads. Although I got this original design working, it was slow. It took maybe 30 minutes to run through all my FLACs.

I later redesigned the script to just load a Python dictionary containing all the hashes from a file into memory. Then, I could update the table freely and wouldn’t even really need to worry about locking since only one thread acted on a track.

But this was still slow for a few reasons:

  • I wasn’t ordering the list of files intelligently at all. It would make sense to try to process the most recently changed files first, wouldn’t it?
  • Python’s threading module isn’t actually capable of performing tasks on multiple processors. It can still only perform on at most one core.
  • Running md5sum on an entire FLAC is slow and therefore dumb.

So, I redesigned the whole script to use the multiprocessing module’s Pool abstraction where you can apply a function onto a list with a pool of workers and then gather the results. Now, each worker returns either an indication that the file didn’t change or a new hash for the file. The system tallies up all the new hashes at the end, updates the database, saves it to disk, and exits. Oh and when it first starts up and finds all the FLACs in my music directory, it sorts them so that the most recently changed files are first.

Moreover, I was lazy in that I was just calling the md5sum program on the entire FLAC, so I used Python’s hashlib module to only take the MD5 of the first 4096 bytes of the FLAC. This is pretty okay because the header information is almost always entirely contained there.

The result is that I can fly through my entire music library in like a second (okay some of that is coming from disk cache — I haven’t tried it on a cold boot yet). Transcoding on my computer (piping flac to lame and then copying tags over) takes about 15 seconds on average.

So 30 minutes just to check a already-sync’d database to a few seconds. Pretty good speedup.

I guess now I should go to work.

ACRIS boards are here!

Take a look:

Overall the quality looks pretty excellent, though there are a few spots that I want to test — I think some vias may have spilled over. Also, it seems like the bottom solder mask was screwed up — the LED connector pads are supposed to go to the board edge.

I’m probably going to put in a big digikey order soon. I will probably go for slightly smaller micros because the program I built only takes about 2.5k of flash. I think I’ll also buy the expensive components on Avnet instead of Digikey because it works out to be cheaper that way, even accounting for shipping. If I were to buy everything on Digikey, it would be about $246 for 10 boards worth of parts. That makes the total per-board cost about $30.

At larger quantities, that price goes down significantly. I think I may finally be closing in on a finished design.