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 [cci]sw/sim/[/cci] 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.