1//
2// Copyright 2012 Ettus Research LLC
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16//
17
18// Simple SPI core, the simplest, yet complete spi core I can think of
19
20// Settings register controlled.
21// 2 settings regs, control and data
22// 1 32-bit readback and status signal
23
24// Settings reg map:
25//
26// BASE+0 divider setting
27// bits [15:0] spi clock divider
28//
29// BASE+1 configuration input
30// bits [23:0] slave select, bit0 = slave0 enabled
31// bits [29:24] num bits (1 through 32)
32// bit [30] data input edge = in data bit latched on rising edge of clock
33// bit [31] data output edge = out data bit latched on rising edge of clock
34//
35// BASE+2 input data
36// Writing this register begins a spi transaction.
37// Bits are latched out from bit 0.
38// Therefore, load this register in reverse.
39//
40// Readback
41// Bits are latched into bit 0.
42// Therefore, data will be in-order.
43
44module simple_spi_core
45    #(
46        //settings register base address
47        parameter BASE = 0,
48
49        //width of serial enables (up to 24 is possible)
50        parameter WIDTH = 8,
51
52        //idle state of the spi clock
53        parameter CLK_IDLE = 0,
54
55        //idle state of the serial enables
56        parameter SEN_IDLE = 24'hffffff
57    )
58    (
59        //clock and synchronous reset
60        input clock, input reset,
61
62        //32-bit settings bus inputs
63        input set_stb, input [7:0] set_addr, input [31:0] set_data,
64
65        //32-bit data readback
66        output [31:0] readback,
67
68        //read is high when spi core can begin another transaction
69        output ready,
70
71        //spi interface, slave selects, clock, data in, data out
72        output [WIDTH-1:0] sen,
73        output sclk,
74        output mosi,
75        input miso,
76
77        //optional debug output
78        output [31:0] debug
79    );
80
81    wire [15:0] sclk_divider;
82    setting_reg #(.my_addr(BASE+0),.width(16)) divider_sr(
83        .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
84        .out(sclk_divider),.changed());
85
86    wire [23:0] slave_select;
87    wire [5:0] num_bits;
88    wire datain_edge, dataout_edge;
89    setting_reg #(.my_addr(BASE+1),.width(32)) config_sr(
90        .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
91        .out({dataout_edge, datain_edge, num_bits, slave_select}),.changed());
92
93    wire [31:0] mosi_data;
94    wire trigger_spi;
95    setting_reg #(.my_addr(BASE+2),.width(32)) data_sr(
96        .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
97        .out(mosi_data),.changed(trigger_spi));
98
99    localparam WAIT_TRIG = 0;
100    localparam PRE_IDLE = 1;
101    localparam CLK_REG = 2;
102    localparam CLK_INV = 3;
103    localparam POST_IDLE = 4;
104    localparam IDLE_SEN = 5;
105
106    reg [2:0] state;
107
108    reg ready_reg;
109    assign ready = ready_reg && ~trigger_spi;
110
111    //serial clock either idles or is in one of two clock states
112    reg sclk_reg;
113    assign sclk = sclk_reg;
114
115    //serial enables either idle or enabled based on state
116    wire sen_is_idle = (state == WAIT_TRIG) || (state == IDLE_SEN);
117    wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select);
118    reg [WIDTH-1:0] sen_reg;
119    always @(posedge clock) sen_reg <= sen24[WIDTH-1:0];
120    assign sen = sen_reg;
121
122    //data output shift register
123    reg [31:0] dataout_reg;
124    wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0};
125    assign mosi = dataout_reg[31];
126
127    //data input shift register
128    reg [31:0] datain_reg;
129    wire [31:0] datain_next = {datain_reg[30:0], miso};
130    assign readback = datain_reg;
131
132    //counter for spi clock
133    reg [15:0] sclk_counter;
134    wire sclk_counter_done = (sclk_counter == sclk_divider);
135    wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1;
136
137    //counter for latching bits miso/mosi
138    reg [6:0] bit_counter;
139    wire [6:0] bit_counter_next = bit_counter + 1;
140    wire bit_counter_done = (bit_counter_next == num_bits);
141
142    always @(posedge clock) begin
143        if (reset) begin
144            state <= WAIT_TRIG;
145            sclk_reg <= CLK_IDLE;
146            ready_reg <= 0;
147        end
148        else begin
149            case (state)
150
151            WAIT_TRIG: begin
152                if (trigger_spi) state <= PRE_IDLE;
153                ready_reg <= ~trigger_spi;
154                dataout_reg <= mosi_data;
155                sclk_counter <= 0;
156                bit_counter <= 0;
157                sclk_reg <= CLK_IDLE;
158            end
159
160            PRE_IDLE: begin
161                if (sclk_counter_done) state <= CLK_REG;
162                sclk_counter <= sclk_counter_next;
163                sclk_reg <= CLK_IDLE;
164            end
165
166            CLK_REG: begin
167                if (sclk_counter_done) begin
168                    state <= CLK_INV;
169                    if (datain_edge  != CLK_IDLE)                     datain_reg  <= datain_next;
170                    if (dataout_edge != CLK_IDLE && bit_counter != 0) dataout_reg <= dataout_next;
171                    sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0
172                end
173                sclk_counter <= sclk_counter_next;
174            end
175
176            CLK_INV: begin
177                if (sclk_counter_done) begin
178                    state <= (bit_counter_done)? POST_IDLE : CLK_REG;
179                    bit_counter <= bit_counter_next;
180                    if (datain_edge  == CLK_IDLE)                      datain_reg  <= datain_next;
181                    if (dataout_edge == CLK_IDLE && ~bit_counter_done) dataout_reg <= dataout_next;
182                    sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0
183                end
184                sclk_counter <= sclk_counter_next;
185            end
186
187            POST_IDLE: begin
188                if (sclk_counter_done) state <= IDLE_SEN;
189                sclk_counter <= sclk_counter_next;
190                sclk_reg <= CLK_IDLE;
191            end
192
193            IDLE_SEN: begin
194                if (sclk_counter_done) state <= WAIT_TRIG;
195                sclk_counter <= sclk_counter_next;
196                sclk_reg <= CLK_IDLE;
197            end
198
199            default: state <= WAIT_TRIG;
200
201            endcase //state
202        end
203    end
204
205    assign debug = {
206        trigger_spi, state, //4
207        sclk, mosi, miso, ready, //4
208        sen[7:0], //8
209        1'b0, bit_counter[6:0], //8
210        sclk_counter_done, bit_counter_done, //2
211        sclk_counter[5:0] //6
212    };
213
214endmodule //simple_spi_core
215