SPI controller implementation in Verilog - Time & Space Complexity
We want to understand how the time it takes for an SPI controller to run grows as the amount of data changes.
How does the controller's work increase when we send more bits?
Analyze the time complexity of the following code snippet.
module spi_controller(
input clk,
input rst,
input start,
input [7:0] data_in,
output reg mosi,
output reg sclk,
output reg done
);
reg [3:0] bit_cnt;
reg [7:0] shift_reg;
always @(posedge clk or posedge rst) begin
if (rst) begin
bit_cnt <= 0;
done <= 0;
sclk <= 0;
end else if (start) begin
shift_reg <= data_in;
bit_cnt <= 8;
done <= 0;
end else if (bit_cnt > 0) begin
sclk <= ~sclk;
if (sclk) begin
mosi <= shift_reg[7];
shift_reg <= shift_reg << 1;
bit_cnt <= bit_cnt - 1;
end
if (bit_cnt == 1) done <= 1;
end
end
endmodule
This code sends 8 bits one by one over SPI, toggling the clock and shifting data out.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Sending each bit by shifting and toggling the clock.
- How many times: 8 times for 8 bits (bit_cnt counts down from 8 to 0).
Each bit requires a fixed number of clock toggles and shifts to send.
| Input Size (bits) | Approx. Operations |
|---|---|
| 10 | About 10 bit sends, each with clock toggles and shifts |
| 100 | About 100 bit sends, more clock toggles and shifts |
| 1000 | About 1000 bit sends, many clock toggles and shifts |
Pattern observation: The work grows directly with the number of bits to send.
Time Complexity: O(n)
This means the time to send data grows linearly with the number of bits.
[X] Wrong: "The SPI controller sends all bits at once, so time does not grow with data size."
[OK] Correct: The controller sends bits one after another, so more bits mean more steps and more time.
Understanding how hardware controllers like SPI handle data bit by bit helps you explain timing and performance in real devices.
"What if we changed the data width from 8 bits to 16 bits? How would the time complexity change?"
Practice
cs (chip select) signal in an SPI controller?Solution
Step 1: Understand the function of
Thecsin SPIcssignal is used to select which SPI device the master wants to communicate with by enabling it.Step 2: Differentiate
The clock signal iscsfrom other signalssclk, data sent from master ismosi, and data received ismiso. Socsis not for clock or data transfer but device selection.Final Answer:
To enable communication with the specific SPI device -> Option AQuick Check:
cs= device enable [OK]
- Confusing cs with clock signal
- Thinking cs sends or receives data
- Mixing up mosi and miso signals
shift_reg?Solution
Step 1: Recall Verilog register declaration syntax
Registers are declared withreg [width-1:0] name;format. For 8 bits, it is[7:0].Step 2: Check each option
reg [7:0] shift_reg; uses correct syntax. reg shift_reg[7:0]; reverses the range and name. wire [7:0] shift_reg; uses wire, not reg. reg shift_reg:8; uses invalid colon syntax.Final Answer:
reg [7:0] shift_reg; -> Option DQuick Check:
reg [7:0] name; = correct reg declaration [OK]
- Using wire instead of reg for storage
- Incorrect bit range syntax
- Misplacing brackets around name
always @(posedge sclk) begin
if (cs == 0) begin
shift_reg <= {shift_reg[6:0], miso};
end
endWhat does this code do on each rising edge of
sclk when cs is low?Solution
Step 1: Analyze the shift operation
The code concatenatesshift_reg[6:0](bits 6 down to 0) withmisoat the right side, effectively shifting left by 1 bit and insertingmisoat the least significant bit (LSB).Step 2: Confirm behavior when
The shift happens only whencsis lowcs == 0, which means the device is selected and data is being received bit by bit.Final Answer:
Shifts shift_reg left by 1 and inserts miso at LSB -> Option CQuick Check:
shift_reg <= {shift_reg[6:0], miso} = left shift + miso LSB [OK]
- Thinking it shifts right instead of left
- Confusing MSB and LSB positions
- Ignoring the condition on cs signal
always @(posedge clk) begin
if (cs == 0) begin
sclk <= ~sclk;
end else begin
sclk <= 0;
end
endWhat is the main problem with this code?
Solution
Step 1: Understand clock toggling logic
The code togglessclkon every rising edge ofclkifcsis low, effectively dividing the clock frequency by 2 during active communication.Step 2: Identify the problem
This behavior is expected for SPI clock generation. The code also resetssclkto 0 whencsis high, which is correct. There is no asynchronous toggling or sensitivity list issue.Final Answer:
It toggles sclk on every clk edge, causing sclk frequency to be half of clk only when cs is low -> Option AQuick Check:
sclk toggles at half clk frequency when cs=0 [OK]
- Thinking sclk toggles asynchronously
- Expecting sclk to run continuously
- Confusing sensitivity list with always block usage
tx_data over mosi and receives 8 bits into rx_data from miso. Which approach correctly handles the bit counting and shifting in Verilog?Solution
Step 1: Determine counter size and control
For 8 bits, a 3-bit counter (0 to 7) is enough. It should increment onsclkrising edge only whencsis low (active communication).Step 2: Understand shifting direction and data lines
SPI usually sends MSB first, so shiftingtx_dataleft outputs MSB onmosi. Incoming bits frommisoare shifted intorx_data.Step 3: Evaluate options
Use a 3-bit counter that increments on eachsclkrising edge whencsis low, shifttx_dataleft to output MSB onmosi, and shift inmisointorx_data. matches correct counter size, control, and shifting direction. Use a 4-bit counter that increments on eachclkrising edge regardless ofcs, shifttx_dataright to output LSB onmosi, and ignoremiso. uses wrong counter size and ignoresmiso. Use an 8-bit counter that decrements on eachsclkfalling edge whencsis high, shifttx_dataright to output MSB onmosi, and shift inmisointorx_data. uses wrong edge and counter behavior. Use a 3-bit counter that resets only whencsis high, shifttx_dataleft to output LSB onmosi, and shift inmisointorx_data. incorrectly outputs LSB first and counter reset logic.Final Answer:
Use a 3-bit counter that increments on each sclk rising edge when cs is low, shift tx_data left to output MSB on mosi, and shift in miso into rx_data. -> Option BQuick Check:
3-bit counter + sclk rising + MSB first shift = correct SPI send/receive [OK]
- Using wrong counter size or clock edge
- Shifting in wrong direction for MSB first
- Ignoring chip select signal during counting
Ready to prove it?
Pass the test and this pattern moves unknown
