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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
|
//
// Copyright 2021 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: spi_slave
//
// Description:
//
// SPI slave for configuration CPOL = CPHA = 0.
// Transfers 8 bit = 1 byte MSB first. Parallel data has to be
// provided and consumed immediately when flags are asserted.
//
// Limitation: clk frequency <= 2*sclk frequency
//
// Data request from sclk domain is triggered towards the clk domain ahead of
// time. This is due to the clock domain crossing using the synchronizer and
// processing pipeline stages.
//
// The worst case propagation delay of the used synchronizer is:
//
// 4 'clk' clock cycles:
// 1 clock cycle of signal propagation to synchronizer
// (data_request_sclk assertion)
// 1 clock cycle to capture data with instability in first stage
// 1 clock cycle to stabilize first stage
// 1 clock cycle to capture data in second stage
// (data_request_clk available in 'clk' domain)
//
// Once synchronized in 'clk' domain, there is one additional clock cycle to
// derive data_out_valid and data_in_required. To ensure that transmit data
// is registered a 'clk' cycle ahead of the actual transmission we need 2
// more 'clk' clock cycles. This ensures that transmit_word has changed and
// is stable for at least one 'clk' cycle before 'sclk' asserts again. Any
// additional time required externally to respond to the control port
// requests should be considered in this crossing as well. This is a total of
// 7 clock cycles (+ctrlport response margin) @ clk domain. The minimum
// required time in sclk domain to issue the request is calculated based on
// the clock frequencies.
//
// Parameters:
//
// CLK_FREQUENCY : Frequency of "clk"
// SPI_FREQUENCY : Frequency of "sclk"
//
`default_nettype none
module spi_slave #(
parameter CLK_FREQUENCY = 50000000,
parameter SPI_FREQUENCY = 10000000
) (
//---------------------------------------------------------------
// SPI Interface
//---------------------------------------------------------------
input wire sclk,
input wire cs_n,
input wire mosi,
output wire miso,
//---------------------------------------------------------------
// Parallel Interface
//---------------------------------------------------------------
input wire clk,
input wire rst,
output reg data_in_required,
input wire data_in_valid,
input wire [7:0] data_in,
output reg data_out_valid,
output reg [7:0] data_out,
output wire active
);
wire [0:0] data_request_clk;
wire [0:0] reception_complete_clk;
//---------------------------------------------------------------
// SPI Receiver @ sclk
//---------------------------------------------------------------
reg [7:0] receiver_reg;
reg [2:0] current_bit_index;
reg reception_complete_sclk = 1'b0;
reg [7:0] received_word;
always @(posedge sclk or posedge cs_n) begin
// Reset logic on positive cs_n edge = slave idle
if (cs_n) begin
receiver_reg <= 8'b0;
end
// Rising edge of sclk
else begin
// Capture bits into shift register MSBs first
receiver_reg <= {receiver_reg[6:0], mosi};
end
end
// Reset with cs_n might occur too early during clk sync.
// Reset half way through the reception.
always @(posedge sclk) begin
// Complete word was received
if (current_bit_index == 7) begin
reception_complete_sclk <= 1'b1;
received_word <= {receiver_reg[6:0], mosi};
// Reset after half transaction
end else if (current_bit_index == 3) begin
reception_complete_sclk <= 1'b0;
end
end
//---------------------------------------------------------------
// Handover of data sclk -> clk
//---------------------------------------------------------------
synchronizer #(
.WIDTH (1),
.STAGES (2),
.INITIAL_VAL (1'b0),
.FALSE_PATH_TO_IN (1)
) data_sync_inst (
.clk (clk),
.rst (1'b0),
.in (reception_complete_sclk),
.out (reception_complete_clk)
);
//---------------------------------------------------------------
// Parallel interface data output @ clk
//---------------------------------------------------------------
reg reception_complete_clk_delayed = 1'b0;
// Propagate toggling signal without reset to ensure stability on reset
always @(posedge clk) begin
// Capture last state of reception
reception_complete_clk_delayed <= reception_complete_clk;
end
// Derive data and control signal
always @(posedge clk) begin
if (rst) begin
data_out_valid <= 1'b0;
data_out <= 8'b0;
end
else begin
// Default assignment
data_out_valid <= 1'b0;
// Provide data to output on rising_edge
if (reception_complete_clk & ~reception_complete_clk_delayed) begin
// Data can simply be captured as the reception complete signal
// indicates stable values in received_word.
data_out <= received_word;
data_out_valid <= 1'b1;
end
end
end
//---------------------------------------------------------------
// SPI Transmitter @ sclk
//---------------------------------------------------------------
// Data request calculation:
// SCLK_CYCLES_DURING_DATA_REQ = 8 clk period / sclk period
// Clock periods are expressed by reciprocal of frequencies.
// Term "+CLK_FREQUENCY-1" is used to round up the result in integer logic.
localparam SCLK_CYCLES_DURING_DATA_REQ = (8*SPI_FREQUENCY + CLK_FREQUENCY-1)/CLK_FREQUENCY;
// subtract from 8 bits per transfer to get target index
localparam DATA_REQ_BIT_INDEX = 8 - SCLK_CYCLES_DURING_DATA_REQ;
reg [7:0] transmit_bits;
reg [7:0] transmit_word;
reg data_request_sclk = 1'b0;
always @(negedge sclk or posedge cs_n) begin
// Reset logic on positive cs_n edge = slave idle
if (cs_n) begin
current_bit_index <= 3'b0;
data_request_sclk <= 1'b0;
transmit_bits <= 8'b0;
end
// Falling edge of sclk
else begin
// Fill or move shift register for byte transmissions
if (current_bit_index == 7) begin
transmit_bits <= transmit_word;
end else begin
transmit_bits <= {transmit_bits[6:0], 1'b0};
end
// Update bit index
current_bit_index <= current_bit_index + 1'b1;
// Trigger request for new word at start of calculated index
if (current_bit_index == DATA_REQ_BIT_INDEX-1) begin
data_request_sclk <= 1'b1;
// Reset after half the reception in case cs_n is not changed in between
// two transactions.
end else if (current_bit_index == (DATA_REQ_BIT_INDEX+4-1)%8) begin
data_request_sclk <= 1'b0;
end
end
end
// Drive miso output with data when cs_n low
assign miso = cs_n ? 1'bz : transmit_bits[7];
//---------------------------------------------------------------
// Handover of Data Request sclk -> clk
//---------------------------------------------------------------
synchronizer #(
.WIDTH (1),
.STAGES (2),
.INITIAL_VAL (1'b0),
.FALSE_PATH_TO_IN (1)
) request_sync_inst (
.clk (clk),
.rst (rst),
.in (data_request_sclk),
.out (data_request_clk)
);
//---------------------------------------------------------------
// Parallel Interface Data Input Control
//---------------------------------------------------------------
reg data_request_clk_delayed;
always @(posedge clk) begin
if (rst) begin
data_request_clk_delayed <= 1'b0;
data_in_required <= 1'b0;
transmit_word <= 8'b0;
end
else begin
// Default assignment
data_in_required <= 1'b0;
// Capture last state of data request
data_request_clk_delayed <= data_request_clk;
// Request data from input
if (~data_request_clk_delayed & data_request_clk) begin
data_in_required <= 1'b1;
end
// Capture new data if valid data available, 0 otherwise.
if (data_in_required) begin
if (data_in_valid) begin
transmit_word <= data_in;
end else begin
transmit_word <= 8'b0;
end
end
end
end
//---------------------------------------------------------------
// Chip Select
//---------------------------------------------------------------
// Driven as active signal in parallel clock domain
wire cs_n_clk;
assign active = ~cs_n_clk;
synchronizer #(
.WIDTH (1),
.STAGES (2),
.INITIAL_VAL (1'b1),
.FALSE_PATH_TO_IN (1)
) active_sync_inst (
.clk (clk),
.rst (rst),
.in (cs_n),
.out (cs_n_clk)
);
endmodule
`default_nettype wire
|