-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathparameterized_spi_master.v
More file actions
executable file
·124 lines (111 loc) · 4.83 KB
/
Copy pathparameterized_spi_master.v
File metadata and controls
executable file
·124 lines (111 loc) · 4.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
module parameterized_spi_master #(
/* verilator lint_off UNUSEDPARAM */
parameter CLK_FREQ = 50000000, // System clock frequency in Hz
parameter SPI_FREQ = 1000000, // SPI clock frequency in Hz
parameter CPHA = 0, // Clock phase (0: sample on first edge, 1: sample on second edge)
/* verilator lint_on UNUSEDPARAM */
parameter DATA_WIDTH = 8, // Width of data to transmit
parameter CPOL = 0 // Clock polarity (0: idle low, 1: idle high)
)(
input wire clk, // System clock
input wire rst_n, // Active low reset
input wire [DATA_WIDTH-1:0] tx_data, // Data to transmit
input wire tx_valid, // Signal to start transmission
output reg tx_ready, // Ready for new data to transmit
output reg [DATA_WIDTH-1:0] rx_data, // Received data
output reg rx_valid, // Received data is valid
// SPI interface
output reg spi_clk, // SPI clock
output reg spi_mosi, // Master Out Slave In
input wire spi_miso, // Master In Slave Out
output reg spi_cs_n // Chip select (active low)
);
// State definitions
localparam IDLE = 2'b00;
localparam ACTIVE = 2'b01;
localparam DONE = 2'b10;
// Registers
reg [1:0] state;
reg [3:0] bit_count;
reg [DATA_WIDTH-1:0] tx_shift;
reg [DATA_WIDTH-1:0] rx_shift;
reg [2:0] clk_divider;
// Main state machine
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
bit_count <= 0;
spi_clk <= CPOL;
spi_mosi <= 1'b0;
spi_cs_n <= 1'b1;
tx_ready <= 1'b1;
rx_valid <= 1'b0;
rx_data <= 0;
tx_shift <= 0;
rx_shift <= 0;
clk_divider <= 0;
end else begin
// Default values
rx_valid <= 1'b0;
case (state)
IDLE: begin
spi_clk <= CPOL;
spi_cs_n <= 1'b1;
tx_ready <= 1'b1;
clk_divider <= 0;
if (tx_valid) begin
tx_ready <= 1'b0;
tx_shift <= tx_data;
bit_count <= 0;
spi_cs_n <= 1'b0;
spi_mosi <= tx_data[DATA_WIDTH-1]; // MSB first
state <= ACTIVE;
end
end
ACTIVE: begin
// Simple clock divider - toggle SPI clock every 4 system clocks
if (clk_divider == 3) begin
clk_divider <= 0;
spi_clk <= ~spi_clk;
if (spi_clk == 1) begin
// On falling edge of SPI clock
/* verilator lint_off WIDTHEXPAND */
if (bit_count < DATA_WIDTH - 1) begin
/* verilator lint_on WIDTHEXPAND */
// Prepare next bit to transmit
tx_shift <= {tx_shift[DATA_WIDTH-2:0], 1'b0};
spi_mosi <= tx_shift[DATA_WIDTH-2];
bit_count <= bit_count + 1;
/* verilator lint_off WIDTHEXPAND */
end else if (bit_count == DATA_WIDTH - 1) begin
/* verilator lint_on WIDTHEXPAND */
// Last bit already sent, wait for one more clock cycle
bit_count <= bit_count + 1;
end
end else begin
// On rising edge of SPI clock
// Sample MISO
/* verilator lint_off WIDTHEXPAND */
rx_shift[DATA_WIDTH-1-bit_count] <= spi_miso;
// Check if all bits have been received
if (bit_count == DATA_WIDTH) begin
/* verilator lint_on WIDTHEXPAND */
state <= DONE;
end
end
end else begin
clk_divider <= clk_divider + 1;
end
end
DONE: begin
spi_cs_n <= 1'b1;
spi_clk <= CPOL;
rx_data <= rx_shift;
rx_valid <= 1'b1;
state <= IDLE;
end
default: state <= IDLE;
endcase
end
end
endmodule