forked from vpecanins/max1000-tutorial
-
Notifications
You must be signed in to change notification settings - Fork 8
/
spi_master.v
123 lines (107 loc) · 3.07 KB
/
spi_master.v
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 spi_master (
input wire clk_in, // Logic clock
input wire nrst, // SPI is active when nreset is HIGH
output reg spi_sck, // SPI clock output
output reg spi_mosi, // SPI master data output, slave data input
input wire spi_miso, // SPI master data input, slave data output
output reg spi_csn, // SPI CSN output (active LOW)
input wire [31:0] mosi_data, // Parallel FPGA data write to SPI
output reg [31:0] miso_data, // Parallel FPGA data read from SPI
input wire [5:0] nbits, // Number of bits: nbits==0 means 1 bit
input wire request, // Request to start transfer: Active HIGH
output reg ready // Active HIGH when transfer has finished
);
parameter div_coef = 32'd10000;
// Frequency divider
reg [31:0] divider;
reg divider_out;
always @(posedge clk_in or negedge nrst) begin
if (nrst == 1'b0) begin
divider <= 32'b0;
divider_out <= 1'b0;
end else begin
if (divider != div_coef) begin
divider <= divider + 1;
divider_out <= 1'b0;
end else begin
divider <= 32'b0;
divider_out <= 1'b1;
end
end
end
localparam
STATE_Idle = 4'd0,
STATE_Run = 4'd1,
STATE_High = 4'd2,
STATE_Low = 4'd3,
STATE_Finish = 4'd4,
STATE_End = 4'd5;
reg [3:0] state;
reg [31:0] data_in_reg;
reg [5:0] nbits_reg;
reg [5:0] bit_counter;
always @(posedge clk_in or negedge nrst)
if (nrst == 1'b0) begin
spi_csn <= 1'b1;
spi_sck <= 1'b1;
spi_mosi <= 1'b1;
ready <= 1'b0;
miso_data <= 32'b0;
data_in_reg <= 32'b0;
nbits_reg <= 6'b0;
bit_counter <= 6'b0;
state <= STATE_Idle;
end else begin
case (state)
STATE_Idle: begin
if (request) begin
state <= STATE_Run;
ready <= 1'b0;
spi_csn <= 1'b0;
data_in_reg <= mosi_data;
nbits_reg <= nbits;
bit_counter <= nbits;
end
end
// Shift left output data word to align MSBit to position 31
STATE_Run: begin
if (nbits_reg == 6'b011111) begin
state <= STATE_High;
end else begin
data_in_reg <= data_in_reg << 1;
nbits_reg <= nbits_reg + 6'b1;
end
end
// During this state SCK is High
// Transition to SCK=LOW and output MOSI data in position 31
STATE_High: if (divider_out) begin
state <= STATE_Low;
spi_sck <= 1'b0;
spi_mosi <= data_in_reg[31];
end
// During this state SCK is LOW & DATA is in the MOSI line
// Transition to SCK==HIGH and sample MISO line
STATE_Low: if (divider_out) begin
if (bit_counter == 6'b0) begin
state <= STATE_Finish;
end else begin
state <= STATE_High;
bit_counter <= bit_counter - 6'b1;
data_in_reg <= data_in_reg << 1'b1; // this must be out of the if (counter==0)?
end
spi_sck <= 1'b1;
miso_data <= {miso_data[30:0], spi_miso}; // Sample MISO at SCK posedge
end
STATE_Finish: if (divider_out) begin
state <= STATE_End;
spi_csn <= 1'b1;
spi_sck <= 1'b1;
spi_mosi <= 1'b0;
end
STATE_End: if (divider_out) begin
state <= STATE_Idle;
ready <= 1'b1;
end
endcase
end
endmodule