We will illustrate the process for designing a SPI interface by continuing to work with the ADCS7476 analog-to-digital converter. This is the chip used by the Digilent Pmod AD1 1
microphone module. Both are popular devices for student projects in this class.
The core of the receiver, of course, is a deserializing (serial-to-parallel) shift register. The ADCS7476 is a 12-bit converter, converting a voltage between 0 and 3.3V to an integer between 0 and 2\(^{12}-\)1=4095. There are three or four preamble bits, extending the ’7476 output to 15 or 16 bits, depending on the relative timing of the CS’ and SCLK signals (see the data sheet for details). Here, a 16-bit register will be used. The preamble bits are shifted out, followed by the data, from the MSB (bit 11) to the LSB (bit 0). To keep the bits in the right order, the register must shift left rather than right. The datapath will also need a 12-bit parallel-load output register to hold the result. The shift register will have a shift control input, and the output register will have a load control input.
Block diagram
Figure19.3.1.Datapath for ADCS7476 SPI bus interface.
The A/D converter’s maximum sampling rate is specified to be 1 MSPS (million samples per second) with a maximum 20MHz SCLK frequency. This 20:1 ratio holds for other sampling rates, i.e., if the sampling rate is 48kHz (typical for high quality audio), the SCLK frequency must be at least 960kHz (round up to 1MHz). If minimizing power consumption is a design goal (and it usually is), the FPGA and the SPI bus should be run at the lowest practical speeds. If we determine that 1MHz is also an acceptable speed for the FPGA, then it will be convenient to divide the 100MHz master clock down to 1MHz and use it for both the FPGA and the SPI bus. The FPGA and the SPI bus can use the same clock up to 20MHz, but for a higher FPGA speed we have to separate the clocks. Multi-clock systems are routinely built, but are beyond the scope of this course. Let’s assume, therefore, that 1MHz is a good clock speed for both the FPGA and the SPI bus.
The controller initiates a conversion when it sees an assertion of an input called take_sample. The controller supplies two signals to the SPI bus: spi_cs (CS’) and spi_sclk (SCLK). It also supplies the control signals for the two registers: shift_en and load_en (Figure 19.3.2). The diagram shows SCLK simply being forwarded from the FPGA to the SPI bus. This is actually not permitted by the Xilinx EDA tools (clock signals are treated specially), but for simplicity we’ll defer this detail to the lab.
Block diagram
Figure19.3.2.Controller signals for ADCS7476 SPI bus interface.
Inside the controller, we need a state machine to manage the control signals and a counter to keep track of the register shifts. The state machine idles, waiting for take_sample. During this time the shift counter is held in its clear state. When take_sample is asserted, the state machine moves through its sequence:
CS’ is asserted (LOW) to start the conversion and the shift register is enabled with shift_enable, which also enables the shift counter.
Since SCLK is also the system clock, we can count shifts by just letting the counter run until enough shifts have occurred. Read the ADCS7476 timing diagram and datasheet [10] carefully to make sure that you don’t stop shifting one clock cycle too early or one cycle too late.
Deassert CS’ and shift_en. Assert load_en to transfer the lower 12 bits of the shift register to the output register.
Return to the idle state.
ExercisesExercises
1.
Write a state diagram for the SPI bus controller.
2.
Translate the block diagram and the state diagram for the SPI interface into a VHDL model. Organize your design with these five processes:
Shift register
Output register
Controller: state update and next-state/output logic, as usual
Shift counter
3.
Here are the key declarations and stimulus processes for a testbench that simulates the output of the A/D converter. Complete the testbench and use it to validate your design.
-- Complete testbench with UUT declaration, instantiation, and clock process,
-- entity/architecture, libraries, etc.
-- Clock period definitions
constant sclk_period : time := 1 us; -- 1 MHz serial clock
constant sampling_count_tc : integer := 25; -- 40 kHz sampling rate, for testing
-- Data definitions
constant TxData : std_logic_vector(14 downto 0) := "111000001101001";
signal bit_count : integer := 14;
-- Internal definitions
signal sampling_count : integer := 0;
-- Stimulus process: assert take_sample periodically to initiate conversions
stim_proc_1: process(sclk)
begin
if rising_edge(sclk) then
if sampling_count < sampling_count_tc-1 then
sampling_count <= sampling_count + 1;
take_sample <= '0';
else
sampling_count <= 0;
take_sample <= '1';
end if;
end if;
end process stim_proc_1;
-- Stimulus process: testbench pretends to be the A/D converter
stim_proc_2: process(spi_sclk)
begin
if falling_edge(spi_sclk) then -- clock data out on falling edge, MSB first
if spi_cs = '0' then -- watch for SPI interface to activate the A/D
spi_sdata <= TxData(bit_count);
if bit_count = 0 then bit_count <= 14;
else bit_count <= bit_count - 1;
end if;
end if;
end if;
end process stim_proc_2;