1 //////////////////////////////////////////////////////////////////////
5 //// This file is part of the SPI IP core project ////
6 //// http://www.opencores.org/projects/spi/ ////
9 //// - Simon Srot (simons@opencores.org) ////
11 //// All additional information is avaliable in the Readme.txt ////
14 //////////////////////////////////////////////////////////////////////
16 //// Copyright (C) 2002 Authors ////
18 //// This source file may be used and distributed without ////
19 //// restriction provided that this copyright statement is not ////
20 //// removed from the file and that any derivative work contains ////
21 //// the original copyright notice and the associated disclaimer. ////
23 //// This source file is free software; you can redistribute it ////
24 //// and/or modify it under the terms of the GNU Lesser General ////
25 //// Public License as published by the Free Software Foundation; ////
26 //// either version 2.1 of the License, or (at your option) any ////
27 //// later version. ////
29 //// This source is distributed in the hope that it will be ////
30 //// useful, but WITHOUT ANY WARRANTY; without even the implied ////
31 //// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ////
32 //// PURPOSE. See the GNU Lesser General Public License for more ////
35 //// You should have received a copy of the GNU Lesser General ////
36 //// Public License along with this source; if not, download it ////
37 //// from http://www.opencores.org/lgpl.shtml ////
39 //////////////////////////////////////////////////////////////////////
42 `include "spi_defines.v"
43 `include "timescale.v"
48 wb_clk_i, wb_rst_i, wb_adr_i, wb_dat_i, wb_dat_o, wb_sel_i,
49 wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_err_o, wb_int_o,
52 ss_pad_o, sclk_pad_o, mosi_pad_o, miso_pad_i
58 input wb_clk_i; // master clock input
59 input wb_rst_i; // synchronous active high reset
60 input [4:0] wb_adr_i; // lower address bits
61 input [32-1:0] wb_dat_i; // databus input
62 output [32-1:0] wb_dat_o; // databus output
63 input [3:0] wb_sel_i; // byte select inputs
64 input wb_we_i; // write enable input
65 input wb_stb_i; // stobe/core select signal
66 input wb_cyc_i; // valid bus cycle input
67 output wb_ack_o; // bus cycle acknowledge output
68 output wb_err_o; // termination w/ error
69 output wb_int_o; // interrupt request signal output
72 output [`SPI_SS_NB-1:0] ss_pad_o; // slave select
73 output sclk_pad_o; // serial clock
74 output mosi_pad_o; // master out slave in
75 input miso_pad_i; // master in slave out
77 reg [32-1:0] wb_dat_o;
82 reg [`SPI_DIVIDER_LEN-1:0] divider; // Divider register
83 reg [`SPI_CTRL_BIT_NB-1:0] ctrl; // Control and status register
84 reg [`SPI_SS_NB-1:0] ss; // Slave select register
85 reg [32-1:0] wb_dat; // wb data out
86 wire [`SPI_MAX_CHAR-1:0] rx; // Rx register
87 wire rx_negedge; // miso is sampled on negative edge
88 wire tx_negedge; // mosi is driven on negative edge
89 wire [`SPI_CHAR_LEN_BITS-1:0] char_len; // char len
91 wire lsb; // lsb first on line
92 wire ie; // interrupt enable
93 wire ass; // automatic slave select
94 wire spi_divider_sel; // divider register select
95 wire spi_ctrl_sel; // ctrl register select
96 wire [3:0] spi_tx_sel; // tx_l register select
97 wire spi_ss_sel; // ss register select
98 wire tip; // transfer in progress
99 wire pos_edge; // recognize posedge of sclk
100 wire neg_edge; // recognize negedge of sclk
101 wire last_bit; // marks last character bit
104 assign spi_divider_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_DEVIDE);
105 assign spi_ctrl_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_CTRL);
106 assign spi_tx_sel[0] = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_TX_0);
107 assign spi_tx_sel[1] = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_TX_1);
108 assign spi_tx_sel[2] = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_TX_2);
109 assign spi_tx_sel[3] = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_TX_3);
110 assign spi_ss_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_SS);
112 // Read from registers
113 always @(wb_adr_i or rx or ctrl or divider or ss)
115 case (wb_adr_i[`SPI_OFS_BITS])
116 `ifdef SPI_MAX_CHAR_128
117 `SPI_RX_0: wb_dat = rx[31:0];
118 `SPI_RX_1: wb_dat = rx[63:32];
119 `SPI_RX_2: wb_dat = rx[95:64];
120 `SPI_RX_3: wb_dat = {{128-`SPI_MAX_CHAR{1'b0}}, rx[`SPI_MAX_CHAR-1:96]};
122 `ifdef SPI_MAX_CHAR_64
123 `SPI_RX_0: wb_dat = rx[31:0];
124 `SPI_RX_1: wb_dat = {{64-`SPI_MAX_CHAR{1'b0}}, rx[`SPI_MAX_CHAR-1:32]};
125 `SPI_RX_2: wb_dat = 32'b0;
126 `SPI_RX_3: wb_dat = 32'b0;
128 `SPI_RX_0: wb_dat = {{32-`SPI_MAX_CHAR{1'b0}}, rx[`SPI_MAX_CHAR-1:0]};
129 `SPI_RX_1: wb_dat = 32'b0;
130 `SPI_RX_2: wb_dat = 32'b0;
131 `SPI_RX_3: wb_dat = 32'b0;
134 `SPI_CTRL: wb_dat = {{32-`SPI_CTRL_BIT_NB{1'b0}}, ctrl};
135 `SPI_DEVIDE: wb_dat = {{32-`SPI_DIVIDER_LEN{1'b0}}, divider};
136 `SPI_SS: wb_dat = {{32-`SPI_SS_NB{1'b0}}, ss};
137 default: wb_dat = 32'bx;
142 always @(posedge wb_clk_i or posedge wb_rst_i)
145 wb_dat_o <= #Tp 32'b0;
147 wb_dat_o <= #Tp wb_dat;
151 always @(posedge wb_clk_i or posedge wb_rst_i)
154 wb_ack_o <= #Tp 1'b0;
156 wb_ack_o <= #Tp wb_cyc_i & wb_stb_i & ~wb_ack_o;
160 assign wb_err_o = 1'b0;
163 always @(posedge wb_clk_i or posedge wb_rst_i)
166 wb_int_o <= #Tp 1'b0;
167 else if (ie && tip && last_bit && pos_edge)
168 wb_int_o <= #Tp 1'b1;
170 wb_int_o <= #Tp 1'b0;
174 always @(posedge wb_clk_i or posedge wb_rst_i)
177 divider <= #Tp {`SPI_DIVIDER_LEN{1'b0}};
178 else if (spi_divider_sel && wb_we_i && !tip)
180 `ifdef SPI_DIVIDER_LEN_8
182 divider <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:0];
184 `ifdef SPI_DIVIDER_LEN_16
186 divider[7:0] <= #Tp wb_dat_i[7:0];
188 divider[`SPI_DIVIDER_LEN-1:8] <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:8];
190 `ifdef SPI_DIVIDER_LEN_24
192 divider[7:0] <= #Tp wb_dat_i[7:0];
194 divider[15:8] <= #Tp wb_dat_i[15:8];
196 divider[`SPI_DIVIDER_LEN-1:16] <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:16];
198 `ifdef SPI_DIVIDER_LEN_32
200 divider[7:0] <= #Tp wb_dat_i[7:0];
202 divider[15:8] <= #Tp wb_dat_i[15:8];
204 divider[23:16] <= #Tp wb_dat_i[23:16];
206 divider[`SPI_DIVIDER_LEN-1:24] <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:24];
212 always @(posedge wb_clk_i or posedge wb_rst_i)
215 ctrl <= #Tp {`SPI_CTRL_BIT_NB{1'b0}};
216 else if(spi_ctrl_sel && wb_we_i && !tip)
219 ctrl[7:0] <= #Tp wb_dat_i[7:0] | {7'b0, ctrl[0]};
221 ctrl[`SPI_CTRL_BIT_NB-1:8] <= #Tp wb_dat_i[`SPI_CTRL_BIT_NB-1:8];
223 else if(tip && last_bit && pos_edge)
224 ctrl[`SPI_CTRL_GO] <= #Tp 1'b0;
227 assign rx_negedge = ctrl[`SPI_CTRL_RX_NEGEDGE];
228 assign tx_negedge = ctrl[`SPI_CTRL_TX_NEGEDGE];
229 assign go = ctrl[`SPI_CTRL_GO];
230 assign char_len = ctrl[`SPI_CTRL_CHAR_LEN];
231 assign lsb = ctrl[`SPI_CTRL_LSB];
232 assign ie = ctrl[`SPI_CTRL_IE];
233 assign ass = ctrl[`SPI_CTRL_ASS];
235 // Slave select register
236 always @(posedge wb_clk_i or posedge wb_rst_i)
239 ss <= #Tp {`SPI_SS_NB{1'b0}};
240 else if(spi_ss_sel && wb_we_i && !tip)
244 ss <= #Tp wb_dat_i[`SPI_SS_NB-1:0];
248 ss[7:0] <= #Tp wb_dat_i[7:0];
250 ss[`SPI_SS_NB-1:8] <= #Tp wb_dat_i[`SPI_SS_NB-1:8];
254 ss[7:0] <= #Tp wb_dat_i[7:0];
256 ss[15:8] <= #Tp wb_dat_i[15:8];
258 ss[`SPI_SS_NB-1:16] <= #Tp wb_dat_i[`SPI_SS_NB-1:16];
262 ss[7:0] <= #Tp wb_dat_i[7:0];
264 ss[15:8] <= #Tp wb_dat_i[15:8];
266 ss[23:16] <= #Tp wb_dat_i[23:16];
268 ss[`SPI_SS_NB-1:24] <= #Tp wb_dat_i[`SPI_SS_NB-1:24];
273 assign ss_pad_o = ~((ss & {`SPI_SS_NB{tip & ass}}) | (ss & {`SPI_SS_NB{!ass}}));
275 spi_clgen clgen (.clk_in(wb_clk_i), .rst(wb_rst_i), .go(go), .enable(tip), .last_clk(last_bit),
276 .divider(divider), .clk_out(sclk_pad_o), .pos_edge(pos_edge),
277 .neg_edge(neg_edge));
279 spi_shift shift (.clk(wb_clk_i), .rst(wb_rst_i), .len(char_len[`SPI_CHAR_LEN_BITS-1:0]),
280 .latch(spi_tx_sel[3:0] & {4{wb_we_i}}), .byte_sel(wb_sel_i), .lsb(lsb),
281 .go(go), .pos_edge(pos_edge), .neg_edge(neg_edge),
282 .rx_negedge(rx_negedge), .tx_negedge(tx_negedge),
283 .tip(tip), .last(last_bit),
284 .p_in(wb_dat_i), .p_out(rx),
285 .s_clk(sclk_pad_o), .s_in(miso_pad_i), .s_out(mosi_pad_o));