Skip to content

Latest commit

 

History

History
94 lines (82 loc) · 6.16 KB

0107-2023-07-02.md

File metadata and controls

94 lines (82 loc) · 6.16 KB

2 Jul 2023

Previous journal: Next journal:
0106-2023-06-30.md 0108-2023-07-03.md

Changing vga_cursor to SPI

  • I'm going to try making vga_cursor accept SPI-style updates:
    • SPI is typically a "4-wire" interface.
    • My implementation will use probably 3:
      • SCLK: Serial Clock; rising edge means "load another bit".
      • MOSI: Master Out Sale In, sometimes also called SDI (Serial Data In); data being sent from host controller to our slave device.
      • /SS: Slave Select (active low);
    • For now we don't need MISO (i.e. "read" access, or return data).
    • A data transaction that completes (i.e. host controller starting and finishing a single complete transmission of a command, or data, or whtever) is usually called a "frame".
    • In SPI, data is usually shifted out MSB first.
  • What are typical ways of ensuring synchronisation, i.e. returning to a "let's start talking" state, in case things get out of order? I suppose that's one advantage of using addressed registers rather than sequential writes.
    • I'm going to define the falling edge of /SS to be the start of a frame. This is consistent with this comment:

      Some slaves require a falling edge of the chip select signal to initiate an action.

      i.e. I will make the design reset its input controller when /SS goes low, abandoning a frame that was in progress (if any) and starting a new one.

  • Different SPI implementations could be;
    • Write one long status update word ("frame") of 19+ bits.
    • Write a 4-bit or 8-bit command, followed by a number of bits specific to the command.
    • Always use 8 bits, e.g. 8-bit command followed by 8-bit data.
    • Always send (say) an 8-bit address word, followed by an 8-bit data word.
  • If this works out, it could be adapted to 8 bits at a time (typical CPU bus) either with adressed registers (i.e. A0..A?) or a known number of sequential writes to form an expected command or state update.
  • For now I think it best to take a synchronous approach for crossing clock domains, i.e. SCLK sync'd to the design's internal clock.
    • Let's assume a reduced version of this design might ultimately target 320x240 (or 320x480) resolution, so a 12.5MHz pixel/system clock.
    • If we're oversampling SCLK, I think worst-case would be ÷5, or a 2.5MHz max SPI clock.
    • If we're talking about Raybox's needs in the future, then we need to send perhaps up to 192 bits per VGA frame refresh. 2.5MHz÷192 is ~13,000 SPI transmissions per second, or on the order of (at worst) at least 150 updates per VGA frame. That's way more than we need :)
    • If we are assuming a very old CPU controlling this, though, then consider a 6502 clocked at about 1MHz, having to bit-bang it. It might only be able to send on the order of (at worst) 100,000 bits per second, or as low as 1,100 bits per VGA frame. Still plenty though :)

Recent updates

  • I've written code for the USE_SPI mode.
  • It uses pairs of DFFs to synchronise the SPI inputs to the design's clock domain.
  • The SPI bus allows a host controller to assert /SS (active-low) and then clock in 19 bits (first cursor_x's 10 bits starting with MSB, then cursor_y's 9 bits starting with MSB).
  • Following completion of the last bit, the next cycle of the design's main clock will copy the captured SPI bit stream into a ready_buffer.
  • The instant the current VGA frame ends, the contents of the ready_buffer are copied (once) into cursor_x and cursor_y.
  • This should hopefully allow the SPI master to stream in new cursor data whenever it wants, and know that: (1) it will not be invalidated; (2) it will not be interrupted or otherwise need to wait; (3) it will only be used at the right time before a new VGA frame is rendered.

Next steps

  • BUG: spi_done never gets reset.
  • Might need to test this design with Verilator or a Verilog TB that gives us a VCD.
  • Implement PicoDeo firmware support for this SPI mode. Maybe make it callable via a slash-command, since it is specific to this test and probably can't be bit-banged by the raybox-bridge Python code fast enough.
  • Make sure the spi_buffer will ONLY be copied into ready_buffer (or subsequently into the cursor registers) while the data is valid, i.e. in response to a new SPI bit stream.

Notes

  • I'm using an spi_done signal to indicate completion of the last SPI bit, before then using this signal to copy the SPI bit stream buffer into ready_buffer (so that it can be safely used at the end of the VGA frame), but is this necessary? We are oversampling the SPI signals, so maybe it doesn't matter...? I still think it does tho. Also, spi_done will be repeatedly re-asserted by the current if conditions...? Or maybe not, because sclk_rise only occurs on exactly one system clock cycle.
  • Need a full system RESET signal, too, so that SPI buffers don't start random?
  • Read more about SPI on fpga4fun.
  • /circle c and /circle 23 give a pretty stable image with a few glitches.
  • Besides circle, try some more complex Lissajous curves (or even increasing frequency on X or Y) and see if it exposes more glitching.
  • Make /circle count updates and elapsed time, to prove an average update frequency.
  • When SPI is implemented, try pushing PicoDeo update rate to the point that the SPI fails.