SPI Transmitter

Good Old SPI

The questions below are due on Friday September 20, 2024; 04:59:00 PM.
 
You are not logged in.

Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.

In this section, you're going to write a data serializer that takes in a certain number of bits in parallel (the "data") at once, and then transmits them out, one bit at a time for downstream consumption. If you took 6.205 in 2023, you basically already wrote this. There's a couple changes, so you should still read. This module will implement a variant of the Serial Peripheral Interface (SPI) protocol, a de-facto standard for synchronous, serial communication between modules. You'll often find it used for communication between different chips or devices. It is one of the easier protocols to get going since it uses several wires for different purposes making synchronization easier.

The module, called spi_tx will look like the following:

The spi_tx module. Arrows (and names) indicate the direction of data into and out of this device. Signals on the left side are intended for internal-to-the-FPGA controls. Signals on the right will interface with external devices over SPI.

It should have the following two standard "utilty" inputs. These control its overall operation and give it its heartbeat/life:

  • clk_in: The system clock for the system
  • rst_in: The reset signal for the system (assume from a clk_in synchronized system)

The module should have two parameters:

  • DATA_WIDTH: that specifies how many bits the message to be transmitted is. Default value should be 8.
  • DATA_CLK_PERIOD: that specifies how long (in clk_in clock cycles), each bit of data should be on the transmission line while being sent. Default value should be 100. On a 100 MHz system, this corresponds to 1 microsecond per bit or a data rate of 1 Mbps.

In addition, the module should have the following two data-oriented inputs:

  • [DATA_WIDTH-1:0] data_in: The data to be serialized out. This data must be in place when the trigger is pulled high.
  • trigger_in: A signal that will trigger the transmission of the serialized data. When high, if the module is not already transmitting data, the module should grab the data in data_in, and start to transmit it beginning with the Most Significant Bit and going downwards.
  • busy_out: A signal used to indicate the module is transmitting. Hi when transmitting, low when not.

The module must have three output ports (all one bit) that together comprise the serial communication "channel". They are:

  • chip_data_out: that contains the actual data being sent. During transmission, each bit (starting with the msb) will exist on this line for approximately DATA_CLK_PERIOD clock cycles. Read the data_clk_out pin information carefully for caveats about this timing.
  • chip_clk_out: a 50% duty cycle synchronization signal. We call it a "clock" but it shouldn't be used the same as your system clock. No Flip-Flops should be clocked off of this signal. Instead what it does is provide the receiving party downstream a signal to know when to sample the values on the data_out line. In particular, when data_clk_out transitions from low to high, that is when the receiving party should sample the value this module has placed on data_out. In order to ensure that data is maximally stable around this sample point, new bits of data should be placed on data_out when data_clk_out falls from high to low. The duty cycle of this signal must be exactly 50%. Any deviation (even to 49%) will result in additional harmonic distortion noise from the clock line which can impede the reading of data. Consequently, the module must find the closest lower even-count period for a given DATA_CLK_PERIOD. This sounds confusing, but shouldn't be. Here's an example:
    • If DATA_CLK_PERIOD is specified to be 42, the module can implement the data period as requested (high for 21 clock cycles, and low for 21 clock cycles).
    • If DATA_CLK_PERIOD is specified to be 39, the module must actually implement a data period of 38, so that it can cleanly have its signal on for 19 clock cycles and off for 19 clock cycles.
  • chip_sel_out: A signal that normally sits high, but is dropped low before the start of transmission of data and is brought up after the completion of the transmission of data.

All together a skeleton of the module is shown below:

`timescale 1ns / 1ps
`default_nettype none // prevents system from inferring an undeclared logic (good practice)
module spi_tx
       #(   parameter DATA_WIDTH = 8,
            parameter DATA_CLK_PERIOD = 100
        )
        ( input wire clk_in,
          input wire rst_in,
          input wire [DATA_WIDTH-1:0] data_in,
          input wire trigger_in,
          output logic busy_out,
          output logic chip_data_out,
          output logic chip_clk_out,
          output logic chip_sel_out
        );
  // your code here
endmodule
`default_nettype wire // prevents system from inferring an undeclared logic (good practice)

An example of the signals generated by this module is shown below starting in an idle state.

An example timing diagram for a system instantiated with DATA_WIDTH=16 and DATA_CLK_PERIOD=4. Note the dashed boxes are "Don't Care" regions where the value isn't specified, and which means you can do whatever there.

A few edge-cases that might jump out at you:

  • Once the module is triggered to start transmitting it should continue to do so until either complete or the system is reset (via rst_in).
  • If the module is triggered while it is still transmitting a previous message, it should ignore this trigger.
  • The module should grab the data off of data_in at the time of triggering and store it internally. Avoid assuming that data stays on the inputs after a trigger.
  • The module must pull chip_sel_out low prior to sending the data and must pull it high after the completion of sending data. There is a little bit of flexibilty on the timing for that, but do not start or finish while data is being sent (as dictated by the clock edges).

Once you have what you think is maybe working, upload it below. We'll actually focus on trying to test this on the next page, so tbh your module doesn't need to be "working" for this upload...consider this just your first pass at it.

You have a SPI TX module written. You haven't verified if it is good yet. That's ok. You can upload what you have here.
 No file selected