SPI Transmitter
Good Old SPI
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 or 2024, you basically already wrote this, albeit with some minor updates, so this should be a review. 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 module is effectively a Transmit-Only implementation of a particular Serial Peripheral Interface mode. If you forget about SPI, you're welcome to refer back to the slightly more in-depth discussion of it on the 6.205 Lab page here. Keep in mind, on that page, students are building a slightly more general SPI controller. In our class, for this week, we're going to control a display, and the display (the peripheral) has nothing to say back to the Zynq (the Controller), so there's no need for the Controller-In-Peripheral-Out (CIPO) data line here.
It should have the following two standard "utilty" inputs. These control its overall operation and give it its heartbeat/life:
clk
: The system clock for the systemrst
: The reset signal for the system (assume from aclk
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 (inclk
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
: 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 indata_in
, and start to transmit it beginning with the Most Significant Bit and going downwards.busy
: 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:
copi
: (standing for Controller Out Peripheral In) that contains the actual data being sent. During transmission, each bit (starting with the msb) will exist on this line for approximatelyDATA_CLK_PERIOD
clock cycles. Read thedclk
pin information carefully for caveats about this timing.dclk
: 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 thecopi
line. In particular, whendclk
transitions from low to high, that is when the receiving party should sample the value this module has placed oncopi
. In order to ensure that data is maximally stable around this sample point, new bits of data should be placed oncopi
whendclk
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 givenDATA_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.
- If
cs
: 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,
input wire rst,
input wire [DATA_WIDTH-1:0] data_in,
input wire trigger,
output logic busy,
output logic copi, //Controller Out Peripheral In
output logic dclk, //Data Clock
output logic cs //Chip Select
);
// 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.
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
). - 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
cs
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.