There's a common expression: "The fourth time's a charm." Or maybe it's "The fifth time's a charm." I don't really remember at the moment. In any case, this is step four on my continuing quest to create my own SPI interface.
In the comments associated with my previous blog on this topic, The Mighty Hamster (a fellow blogger here on All Programmable Planet) suggested that I reverse the order of the "Increment" vs. "Check" portions of my "If-Else" block in which I divide down the main clock to create the SPI clock. The effect is the same, but his version is clearer, so I'll do that.
I also discovered that I somehow missed a number of fundamental concepts and introduced a few errors when reusing my existing code. After making the above modification, I ran out the configuration with ChipScope monitoring it. Here's what I saw:
One "basement-level" error I noticed is with static numbers, like "8'b10000000." It's binary, hence the "b," and there are eight bits. That's cool, but somehow I decided that the length number ("8" in this case) represented the number of digits in the specific base. Based on this belief, elsewhere I had used cc"2'hAA" as a static value to be assigned to a register, where I intended the "2" to indicate "two hexadecimal digits." Unfortunately, the tools understand this "2" to indicate "two binary bits," with the result that I was loading only the two least-significant bits into the register. This obviously was not what I had intended. Even worse, I used the same amazing trick in a number of places in my code.
My other brilliant move came in my de-bounce module. This is basically the same de-bounce code I created in an earlier blog, but I turned it into a module and instantiated it here to synchronize and de-bounce switch SW5. Well, when I brought the code over, I missed a register name that I had changed. The end result was that although I did all of the syncing and debouncing, I never got around to connecting anything to the output of the module.
Another thing I "discovered" (as in re-discovered after having read it before) is that if a portion of the code is never executed, the hardware behind it won't be built. In software, if you do something like "if (0) {...}", the code represented by "..." will still be sitting there in memory. That won't happen with an FPGA. The system is smart enough to know that said code block will never be reached, so it doesn't bother to create it. Here's the clue from Captain Obvious: If the RTL schematic shows your output register as being tied directly to +V or to ground, you are not going to get the results you want.
Now, as I write these words a few hours later, I'm much closer. I'm still not quite there, but I feel confident that I'm well on the way as illustrated by the following image (click here to see a larger, more detailed version of this image):
My SPI clock ("spi_clk") is running and it's clocking out eight pulses to the SPI clock line ("SCLKin_OBUF"), as it should. Also, it's doing this while the slave select ("SS") is low, as it should. My data ("MOSIis_OBUF") still isn't there, but the basic system is now working. My slave does, in fact, recognize the transmission of a data byte equaling "0x00."
I've spend way more time on this than I should have, but that's the price I pay sometimes. The last order of business for today is to properly shift the MSB out to the MOSI wire and the MISO into the FPGA. For some reason, I tried to overcomplicate that last bit when really, all I need is to do the following:
Here's the result of all of that being displayed on the terminal window from my mbed (170 d /0xAA going to the mbed and 153 d / 0x99). "LED2_OBUF" is tied to the MISO line.
This should be recognizable as an SPI waveform as illustrated below (click here to see a larger, more detailed version of this image):
From this point one, all I need to do is a little fiddling with the timing and order of things to get the MISO data correct and then I'm basically done. After that, it shouldn't take too much work to wrap this up into a separate module, which I can instantiate whenever I need it in the future.
Of course there is more to the SPI bus, like multiple master collision avoidance and fault recovery, but this will do what I need for the moment, and I can improve upon my module over time. If you want to play with this yourself, click here to download a ZIP file containing my code.
Related posts: