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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
/* (c) Peter McGoron 2022 v0.2
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v.2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/* CYCLE_HALF_WAIT should take into account the setup time of the slave
* device, and also master buffering (MISO is one cycle off to stabilize
* the input).
*/
module
`ifdef SPI_MASTER_NO_READ
spi_master_no_read
`else
`ifdef SPI_MASTER_NO_WRITE
spi_master_no_write
`else
spi_master
`endif
`endif
#(
parameter WID = 24, // Width of bits per transaction.
parameter WID_LEN = 5, // Length in bits required to store WID
parameter CYCLE_HALF_WAIT = 1, // Half of the wait time of a cycle minus 1.
// One SCK cycle is 2*(CYCLE_HALF_WAIT + 1) clock cycles.
parameter TIMER_LEN = 3, // Length in bits required to store CYCLE_HALF_WAIT
parameter POLARITY = 0, // 0 = sck idle low, 1 = sck idle high
parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read.
)
(
input clk,
`ifndef SPI_MASTER_NO_READ
output reg [WID-1:0] from_slave,
input miso,
`endif
`ifndef SPI_MASTER_NO_WRITE
input [WID-1:0] to_slave,
output reg mosi,
`endif
output reg sck_wire,
output reg finished,
input arm
);
`ifndef SPI_MASTER_NO_READ
/* MISO is almost always an external wire, so buffer it.
* This might not be necessary, since the master and slave do not respond
* immediately to changes in the wires, but this is just to be safe.
* It is trivial to change, just do
* wire read_miso = miso;
*/
reg miso_hot = 0;
reg read_miso = 0;
always @ (posedge clk) begin
read_miso <= miso_hot;
miso_hot <= miso;
end
`endif
parameter WAIT_ON_ARM = 0;
parameter ON_CYCLE = 1;
parameter CYCLE_WAIT = 2;
parameter WAIT_FINISHED = 3;
reg [1:0] state = WAIT_ON_ARM;
reg [WID_LEN-1:0] bit_counter = 0;
reg [TIMER_LEN-1:0] timer = 0;
`ifndef SPI_MASTER_NO_WRITE
reg [WID-1:0] send_buf = 0;
`endif
reg sck = 0;
assign sck_wire = sck;
task idle_state();
if (POLARITY == 0) begin
sck <= 0;
end else begin
sck <= 1;
end
`ifndef SPI_MASTER_NO_WRITE
mosi <= 0;
`endif
timer <= 0;
bit_counter <= 0;
endtask
task read_data();
`ifndef SPI_MASTER_NO_READ
from_slave <= from_slave << 1;
from_slave[0] <= read_miso;
`endif
endtask
task write_data();
`ifndef SPI_MASTER_NO_WRITE
mosi <= send_buf[WID-1];
send_buf <= send_buf << 1;
`endif
endtask
task setup_bits();
/* at Mode 00, the transmission starts with
* a rising edge, and at mode 11, it starts with a falling
* edge. For both modes, these are READs.
*
* For mode 01 and mode 10, the first action is a WRITE.
*/
if (POLARITY == PHASE) begin
`ifndef SPI_MASTER_NO_WRITE
mosi <= to_slave[WID-1];
send_buf <= to_slave << 1;
`endif
state <= CYCLE_WAIT;
end else begin
`ifndef SPI_MASTER_NO_WRITE
send_buf <= to_slave;
`endif
state <= ON_CYCLE;
end
endtask
task cycle_change();
// Stop transfer when the clock returns to its original polarity.
if (bit_counter == WID[WID_LEN-1:0] && sck == POLARITY[0]) begin
state <= WAIT_FINISHED;
end else begin
sck <= !sck;
state <= ON_CYCLE;
end
endtask
always @ (posedge clk) begin
case (state)
WAIT_ON_ARM: begin
if (!arm) begin
idle_state();
finished <= 0;
end else begin
setup_bits();
end
end
ON_CYCLE: begin
if (sck) begin // rising edge
if (PHASE == 1) begin
write_data();
end else begin
read_data();
end
if (POLARITY == 0) begin
bit_counter <= bit_counter + 1;
end
end else begin // falling edge
if (PHASE == 1) begin
read_data();
end else begin
write_data();
end
if (POLARITY == 1) begin
bit_counter <= bit_counter + 1;
end
end
if (CYCLE_HALF_WAIT == 0) begin
cycle_change();
end else begin
state <= CYCLE_WAIT;
end
end
CYCLE_WAIT: begin
if (timer == CYCLE_HALF_WAIT) begin
timer <= 1;
cycle_change();
end else begin
timer <= timer + 1;
end
end
WAIT_FINISHED: begin
finished <= 1;
idle_state();
if (!arm) begin
state <= WAIT_ON_ARM;
end
end
endcase
end
endmodule
|