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, 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:
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 systemrst_in
: The reset signal for the system (assume from aclk_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 (inclk_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 indata_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 approximatelyDATA_CLK_PERIOD
clock cycles. Read thedata_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 thedata_out
line. In particular, whendata_clk_out
transitions from low to high, that is when the receiving party should sample the value this module has placed ondata_out
. In order to ensure that data is maximally stable around this sample point, new bits of data should be placed ondata_out
whendata_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 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
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.
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.