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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
|
/////////////////////////////////////////////////////////////////////
//// ////
//// OpenCores MC68HC11E based SPI interface ////
//// ////
//// Author: Richard Herveille ////
//// richard@asics.ws ////
//// www.asics.ws ////
//// ////
/////////////////////////////////////////////////////////////////////
//// ////
//// Copyright (C) 2002 Richard Herveille ////
//// richard@asics.ws ////
//// ////
//// This source file may be used and distributed without ////
//// restriction provided that this copyright statement is not ////
//// removed from the file and that any derivative work contains ////
//// the original copyright notice and the associated disclaimer.////
//// ////
//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ////
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ////
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ////
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ////
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ////
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ////
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ////
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ////
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ////
//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ////
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ////
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ////
//// POSSIBILITY OF SUCH DAMAGE. ////
//// ////
/////////////////////////////////////////////////////////////////////
// CVS Log
//
// $Id: simple_spi_top.v,v 1.5 2004/02/28 15:59:50 rherveille Exp $
//
// $Date: 2004/02/28 15:59:50 $
// $Revision: 1.5 $
// $Author: rherveille $
// $Locker: $
// $State: Exp $
//
// Change History:
// $Log: simple_spi_top.v,v $
// Revision 1.5 2004/02/28 15:59:50 rherveille
// Fixed SCK_O generation bug.
// This resulted in a major rewrite of the serial interface engine.
//
// Revision 1.4 2003/08/01 11:41:54 rherveille
// Fixed some timing bugs.
//
// Revision 1.3 2003/01/09 16:47:59 rherveille
// Updated clkcnt size and decoding due to new SPR bit assignments.
//
// Revision 1.2 2003/01/07 13:29:52 rherveille
// Changed SPR bits coding.
//
// Revision 1.1.1.1 2002/12/22 16:07:15 rherveille
// Initial release
//
//
//
// Motorola MC68HC11E based SPI interface
//
// Currently only MASTER mode is supported
//
// synopsys translate_off
`include "timescale.v"
// synopsys translate_on
module simple_spi_top(
// 8bit WISHBONE bus slave interface
input wire clk_i, // clock
input wire rst_i, // reset (asynchronous active low)
input wire cyc_i, // cycle
input wire stb_i, // strobe
input wire [1:0] adr_i, // address
input wire we_i, // write enable
input wire [7:0] dat_i, // data input
output reg [7:0] dat_o, // data output
output reg ack_o, // normal bus termination
output reg inta_o, // interrupt output
// SPI port
output reg sck_o, // serial clock output
output wire mosi_o, // MasterOut SlaveIN
input wire miso_i // MasterIn SlaveOut
);
//
// Module body
//
reg [7:0] spcr; // Serial Peripheral Control Register ('HC11 naming)
wire [7:0] spsr; // Serial Peripheral Status register ('HC11 naming)
reg [7:0] sper; // Serial Peripheral Extension register
reg [7:0] treg, rreg; // Transmit/Receive register
// fifo signals
wire [7:0] rfdout;
reg wfre, rfwe;
wire rfre, rffull, rfempty;
wire [7:0] wfdout;
wire wfwe, wffull, wfempty;
// misc signals
wire tirq; // transfer interrupt (selected number of transfers done)
wire wfov; // write fifo overrun (writing while fifo full)
reg [1:0] state; // statemachine state
reg [2:0] bcnt;
//
// Wishbone interface
wire wb_acc = cyc_i & stb_i; // WISHBONE access
wire wb_wr = wb_acc & we_i; // WISHBONE write access
// dat_i
always @(posedge clk_i or negedge rst_i)
if (~rst_i)
begin
spcr <= #1 8'h10; // set master bit
sper <= #1 8'h00;
end
else if (wb_wr)
begin
if (adr_i == 2'b00)
spcr <= #1 dat_i | 8'h10; // always set master bit
if (adr_i == 2'b11)
sper <= #1 dat_i;
end
// write fifo
assign wfwe = wb_acc & (adr_i == 2'b10) & ack_o & we_i;
assign wfov = wfwe & wffull;
// dat_o
always @(posedge clk_i)
case(adr_i) // synopsys full_case parallel_case
2'b00: dat_o <= #1 spcr;
2'b01: dat_o <= #1 spsr;
2'b10: dat_o <= #1 rfdout;
2'b11: dat_o <= #1 sper;
endcase
// read fifo
assign rfre = wb_acc & (adr_i == 2'b10) & ack_o & ~we_i;
// ack_o
always @(posedge clk_i or negedge rst_i)
if (~rst_i)
ack_o <= #1 1'b0;
else
ack_o <= #1 wb_acc & !ack_o;
// decode Serial Peripheral Control Register
wire spie = spcr[7]; // Interrupt enable bit
wire spe = spcr[6]; // System Enable bit
wire dwom = spcr[5]; // Port D Wired-OR Mode Bit
wire mstr = spcr[4]; // Master Mode Select Bit
wire cpol = spcr[3]; // Clock Polarity Bit
wire cpha = spcr[2]; // Clock Phase Bit
wire [1:0] spr = spcr[1:0]; // Clock Rate Select Bits
// decode Serial Peripheral Extension Register
wire [1:0] icnt = sper[7:6]; // interrupt on transfer count
wire [1:0] spre = sper[1:0]; // extended clock rate select
wire [3:0] espr = {spre, spr};
// generate status register
wire wr_spsr = wb_wr & (adr_i == 2'b01);
reg spif;
always @(posedge clk_i)
if (~spe)
spif <= #1 1'b0;
else
spif <= #1 (tirq | spif) & ~(wr_spsr & dat_i[7]);
reg wcol;
always @(posedge clk_i)
if (~spe)
wcol <= #1 1'b0;
else
wcol <= #1 (wfov | wcol) & ~(wr_spsr & dat_i[6]);
assign spsr[7] = spif;
assign spsr[6] = wcol;
assign spsr[5:4] = 2'b00;
assign spsr[3] = wffull;
assign spsr[2] = wfempty;
assign spsr[1] = rffull;
assign spsr[0] = rfempty;
// generate IRQ output (inta_o)
always @(posedge clk_i)
inta_o <= #1 spif & spie;
//
// hookup read/write buffer fifo
fifo4 #(8)
rfifo(
.clk ( clk_i ),
.rst ( rst_i ),
.clr ( ~spe ),
.din ( treg ),
.we ( rfwe ),
.dout ( rfdout ),
.re ( rfre ),
.full ( rffull ),
.empty ( rfempty )
),
wfifo(
.clk ( clk_i ),
.rst ( rst_i ),
.clr ( ~spe ),
.din ( dat_i ),
.we ( wfwe ),
.dout ( wfdout ),
.re ( wfre ),
.full ( wffull ),
.empty ( wfempty )
);
//
// generate clk divider
reg [11:0] clkcnt;
always @(posedge clk_i)
if(spe & (|clkcnt & |state))
clkcnt <= #1 clkcnt - 11'h1;
else
case (espr) // synopsys full_case parallel_case
4'b0000: clkcnt <= #1 12'h0; // 2 -- original M68HC11 coding
4'b0001: clkcnt <= #1 12'h1; // 4 -- original M68HC11 coding
4'b0010: clkcnt <= #1 12'h3; // 16 -- original M68HC11 coding
4'b0011: clkcnt <= #1 12'hf; // 32 -- original M68HC11 coding
4'b0100: clkcnt <= #1 12'h1f; // 8
4'b0101: clkcnt <= #1 12'h7; // 64
4'b0110: clkcnt <= #1 12'h3f; // 128
4'b0111: clkcnt <= #1 12'h7f; // 256
4'b1000: clkcnt <= #1 12'hff; // 512
4'b1001: clkcnt <= #1 12'h1ff; // 1024
4'b1010: clkcnt <= #1 12'h3ff; // 2048
4'b1011: clkcnt <= #1 12'h7ff; // 4096
endcase
// generate clock enable signal
wire ena = ~|clkcnt;
// transfer statemachine
always @(posedge clk_i)
if (~spe)
begin
state <= #1 2'b00; // idle
bcnt <= #1 3'h0;
treg <= #1 8'h00;
wfre <= #1 1'b0;
rfwe <= #1 1'b0;
sck_o <= #1 1'b0;
end
else
begin
wfre <= #1 1'b0;
rfwe <= #1 1'b0;
case (state) //synopsys full_case parallel_case
2'b00: // idle state
begin
bcnt <= #1 3'h7; // set transfer counter
treg <= #1 wfdout; // load transfer register
sck_o <= #1 cpol; // set sck
if (~wfempty) begin
wfre <= #1 1'b1;
state <= #1 2'b01;
if (cpha) sck_o <= #1 ~sck_o;
end
end
2'b01: // clock-phase2, next data
if (ena) begin
sck_o <= #1 ~sck_o;
state <= #1 2'b11;
end
2'b11: // clock phase1
if (ena) begin
treg <= #1 {treg[6:0], miso_i};
bcnt <= #1 bcnt -3'h1;
if (~|bcnt) begin
state <= #1 2'b00;
sck_o <= #1 cpol;
rfwe <= #1 1'b1;
end else begin
state <= #1 2'b01;
sck_o <= #1 ~sck_o;
end
end
2'b10: state <= #1 2'b00;
endcase
end
assign mosi_o = treg[7];
// count number of transfers (for interrupt generation)
reg [1:0] tcnt; // transfer count
always @(posedge clk_i)
if (~spe)
tcnt <= #1 icnt;
else if (rfwe) // rfwe gets asserted when all bits have been transfered
if (|tcnt)
tcnt <= #1 tcnt - 2'h1;
else
tcnt <= #1 icnt;
assign tirq = ~|tcnt & rfwe;
endmodule
|