Hi everyone.
Some time ago I hinted that I was trying to implement a SPI like peripheral in a CPLD, namely for use in a project that I am working on, and also just because I felt like doing something with a CPLD.
I finally managed to find some time to have a real crack at it, and I think Ive managed to do it. My initial goal was only to implement "write and shift" functionality, since that was what I had implemented as a purely discrete logic solution for my project, and since I am only shifting data out to drive some displays, but it looks like I have also managed to implement read functionality as well.
Ive written it in CUPL, since WinCUPL comes with WinSIM which meant I was able to simulate the design and test it. I'd give it a go in some kind of HDL if I could find a good set of simulation tools and if I could work out whether VHDL or Verilog is what I want to learn. Ive been doing a bit of poking around that kind of stuff lately, but yet to find *the* setup that will work for me.
Anyhow, attached is a screenshot of the output from WinSIM, with a few notes:
* Vector 5 I start to initiate a "write and shift" operation, but it isnt really until vector 6 that this operation begins, as the internal state machine is clocked by the system clock
* At vector 8 you can see that the storage register sr is loaded from the value presented on the data bus
* When the chip select signal is released at vector 11 you can see that the state machine transitions to the next state at vector 12, this is the beginning of the shift operation now that the write operation has completed. The ser_out signal simply follows the value of sr7 which you can see also started to present a value at vector 8 when the storage register was loaded.
* From vector 13 onwards, the chip outputs an inverted version of the system clock, this is used to clock the slave device so that it loads each bit into its own storage register. sr0 will have its input value clocked by the system clock, and this only takes effect as long as the state machine is in the shifting state which it is from vector 12 (but only actually takes effect from vector 14).
* Vector 14-27 are a bit of a jumble, but you'll see if you look very carefully that the value of sr7..0 is being shifted out, and the value of the ser_in signal is being shifted in place.
* When ctr3 goes high, this indicates that 8 bits have been shifted and will terminate the shift opeation and return the state machine to its idle state where it waits for the next operation. That concludes a write/shift operation.
* From vector 33 Im playing around with setting up a read operation, which is successfully initiated at vector 35 once both the !cs and !rd signals are asserted. This will cause the state machine to transition to the reading state, although really this has no actual effect or impact on the operation - the tri-state function of the databus latches is a simple combinatorial function of those two signals. So you'll see at vector 35, even though this is a falling edge period of the system clock, the data bus is driven with the value that was in the storage register. The storage register is transferred to the data bus latches each clock cycle.
* At vector 40 you can see the state machine transitioning back to its idle state once the !cs and !rd signals are no longer in a valid state and we encounter a positive edge of the system clock. And that concludes a read operation.
* At vector 43 you can see that if I assert the !clr signal then all counters, registers etc are reset.
* At vector 47 I try another read operation just to make sure the device is outputting all 0's since a reset has occurred. Reset is asynchronous as you can see by it occurring within a falling edge of the system clock.
I think that about covers it...
Have popped the source and simulation files up on github (
https://github.com/tomstorey/CPLD-Projects/tree/master/z80_sr). If you have the inclination to look over it, I'd like to hear if Ive really goofed anything up - although the simulation results fill me with a sense of confidence. One of my design goals was to operate it at full system clock frequency, so that ideally you could use instructions like OUTI et al to sit in a tight loop moving data from memory to the peripheral, and without needing to fiddle the WAIT line.
Also, chip select for slave devices should be implemented externally.
Im yet to program an actual device to try it out while being driven from an actual Z80 CPU, I'll hopefully get to that in the next month (or at this rate, two). Would be good to put my dev kit to use. :-)
Tom