blob: eafa98fa9210cd2335c11e2b646076d0ddb994d6 [file] [log] [blame]
Sylvain Munautbd83e532020-09-15 22:11:29 +02001/*
2 * sr_btn_if.v
3 *
4 * vim: ts=4 sw=4
5 *
6 * Combined SPI + Shift Register + Button input interface
7 *
8 * Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
9 * SPDX-License-Identifier: CERN-OHL-S-2.0
10 */
11
12`default_nettype none
13
14`define SMALL
15
16module sr_btn_if #(
17 parameter integer TICK_LOG2_DIV = 3
18)(
19 // Pads
20 output wire flash_mosi,
21 input wire flash_miso,
22 output wire flash_clk,
23 output wire flash_cs_n,
24
25 inout wire e1_led_rclk,
26
27 // SPI module interface
28 output wire spi_mosi_i,
29 input wire spi_mosi_o,
30 input wire spi_mosi_oe,
31
32 output wire spi_miso_i,
33 input wire spi_miso_o,
34 input wire spi_miso_oe,
35
36 output wire spi_clk_i,
37 input wire spi_clk_o,
38 input wire spi_clk_oe,
39
40 input wire spi_csn_o,
41 input wire spi_csn_oe,
42
43 // SPI muxing req/gnt
44 input wire spi_req,
45 output wire spi_gnt,
46
47 // Shift Register interface
48 input wire [7:0] sr_val,
49 input wire sr_go,
50 output wire sr_rdy,
51
52 // Button status
53 output reg btn_val,
54 output reg btn_stb,
55
56 // Clock / Reset
57 input wire clk,
58 input wire rst
59);
60
61 // Signals
62 // -------
63
64 // FSM
65 localparam
66 ST_IDLE = 0,
67 ST_SPI = 1,
68 ST_SHIFT_LO = 2,
69 ST_SHIFT_HI = 3,
70 ST_LATCH = 4,
71 ST_SENSE = 5,
72 ST_PAUSE = 6;
73
74 reg [2:0] state;
75 reg [2:0] state_nxt;
76
77 // Shift Register IO
78 reg srio_clk_o;
79 reg srio_dat_o;
80 reg srio_rclk_o;
81 reg srio_rclk_oe;
82 wire srio_rclk_i;
83
84 // SPI IO
85 reg sio_sel;
86
87 wire sio_miso_o, sio_miso_oe, sio_miso_i;
88 wire sio_mosi_o, sio_mosi_oe, sio_mosi_i;
89 wire sio_clk_o, sio_clk_oe, sio_clk_i;
90 wire sio_csn_o, sio_csn_oe;
91
92 // Input synchronizer
93 reg [1:0] btn_sync;
94
95 // Counters
96 reg [TICK_LOG2_DIV:0] tick_cnt;
97 wire tick;
98
99 reg [3:0] bit_cnt;
100 wire bit_cnt_last;
101
102 reg [3:0] sense_cnt_in;
103 reg [3:0] sense_cnt;
104 wire sense_cnt_last;
105
106 // Shift register
107 reg [7:0] shift_data;
108
109
110 // FSM
111 // ---
112
113 // State register
114 always @(posedge clk)
115 if (rst)
116 state <= ST_IDLE;
117 else
118 state <= state_nxt;
119
120 // Next-State
121 always @(*)
122 begin
123 // Default
124 state_nxt = state;
125
126 // Change conditions
127 case (state)
128 ST_IDLE:
129 if (sr_go)
130 state_nxt = ST_SHIFT_LO;
131 else if (spi_req)
132 state_nxt = ST_SPI;
133
134 ST_SPI:
135 if (~spi_req)
136 state_nxt = ST_IDLE;
137
138 ST_SHIFT_LO:
139 if (tick)
140 state_nxt = ST_SHIFT_HI;
141
142 ST_SHIFT_HI:
143 if (tick)
144 state_nxt = bit_cnt_last ? ST_LATCH : ST_SHIFT_LO;
145
146 ST_LATCH:
147 if (tick)
148 state_nxt = ST_SENSE;
149
150 ST_SENSE:
151 if (tick & sense_cnt_last)
152 state_nxt = ST_PAUSE;
153
154 ST_PAUSE:
155 if (tick)
156 state_nxt = ST_IDLE;
157 endcase
158 end
159
160
161 // IO pads
162 // -------
163
164 // Muxing & Sharing
165 assign spi_mosi_i = sio_mosi_i;
166 assign spi_miso_i = sio_miso_i;
167 assign spi_clk_i = sio_clk_i;
168
169 assign sio_mosi_o = sio_sel ? spi_mosi_o : srio_dat_o;
170 assign sio_mosi_oe = sio_sel ? spi_mosi_oe : 1'b1;
171 assign sio_miso_o = sio_sel ? spi_miso_o : 1'b0;
172 assign sio_miso_oe = sio_sel ? spi_miso_oe : 1'b0;
173 assign sio_clk_o = sio_sel ? spi_clk_o : srio_clk_o;
174 assign sio_clk_oe = sio_sel ? spi_clk_oe : 1'b1;
175 assign sio_csn_o = sio_sel ? spi_csn_o : 1'b1;
176 assign sio_csn_oe = sio_sel ? spi_csn_oe : 1'b1;
177
178 // MOSI / MISO / SCK / RCLK
179 SB_IO #(
180 .PIN_TYPE(6'b101001),
181 .PULLUP(1'b1)
182 ) iob_I[3:0] (
183 .PACKAGE_PIN ({flash_mosi, flash_miso, flash_clk, e1_led_rclk}),
184 .OUTPUT_ENABLE({sio_mosi_oe, sio_miso_oe, sio_clk_oe, srio_rclk_oe}),
185 .D_OUT_0 ({sio_mosi_o, sio_miso_o, sio_clk_o, srio_rclk_o}),
186 .D_IN_0 ({sio_mosi_i, sio_miso_i, sio_clk_i, srio_rclk_i})
187 );
188
189 // Bypass OE for CS_n line
190 assign flash_cs_n = sio_csn_o;
191
192
193 // SPI grant
194 // ---------
195
196 // Mux select
197 always @(posedge clk)
198 sio_sel <= (state_nxt == ST_SPI);
199
200 assign spi_gnt = sio_sel;
201
202
203 // Button input synchronizer
204 // -------------------------
205
206 always @(posedge clk)
207 btn_sync <= { btn_sync[0], srio_rclk_i };
208
209
210 // Control
211 // -------
212
213 // Tick counter
214 always @(posedge clk)
215`ifdef SMALL
216 tick_cnt <= { (TICK_LOG2_DIV+1){ (~tick & (state != ST_IDLE)) } } & (tick_cnt + 1);
217`else
218 if (state == ST_IDLE)
219 tick_cnt <= 0;
220 else
221 tick_cnt <= tick ? 0 : (tick_cnt + 1);
222`endif
223
224 assign tick = tick_cnt[TICK_LOG2_DIV];
225
226 // Bit counter
227 always @(posedge clk)
228`ifdef SMALL
229 bit_cnt <= { 4{state != ST_IDLE} } & (bit_cnt + (tick & (state == ST_SHIFT_LO)));
230`else
231 if (state == ST_IDLE)
232 bit_cnt <= 4'h0;
233 else if (tick & (state == ST_SHIFT_LO))
234 bit_cnt <= bit_cnt + 1;
235`endif
236
237 assign bit_cnt_last = bit_cnt[3];
238
239 // Sense counters
240`ifdef SMALL
241 (* keep *) wire state_is_not_latch = (state != ST_LATCH);
242`endif
243 always @(posedge clk)
244`ifdef SMALL
245 begin
246 sense_cnt <= { 4{state_is_not_latch} } & (sense_cnt + tick);
247 sense_cnt_in <= { 4{state_is_not_latch} } & (sense_cnt_in + (tick & btn_sync[1]));
248 end
249`else
250 if (state == ST_LATCH) begin
251 sense_cnt <= 0;
252 sense_cnt_in <= 0;
253 end else if (tick) begin
254 sense_cnt <= sense_cnt + 1;
255 sense_cnt_in <= sense_cnt_in + btn_sync[1];
256 end
257`endif
258
259 assign sense_cnt_last = sense_cnt[3];
260
261 // Decision
262 always @(posedge clk)
263`ifdef SMALL
264 btn_val <= ((state == ST_PAUSE) & (sense_cnt_in[3:2] == 2'b00)) | ((state != ST_PAUSE) & btn_val);
265`else
266 if (state == ST_PAUSE)
267 btn_val <= (sense_cnt_in[3:2] == 2'b00);
268`endif
269
270 always @(posedge clk)
271 btn_stb <= (state == ST_PAUSE) & tick;
272
273 // Data shift register
274`ifdef SMALL
275 wire [7:0] m = { 8{ sr_go & sr_rdy } };
276 wire [7:0] shift_data_nxt = (sr_val & m) | ({ shift_data[6:0], 1'b0 } & ~m);
277 wire shift_ce = (sr_go & sr_rdy) | (tick & (state == ST_SHIFT_HI));
278
279 always @(posedge clk)
280 if (shift_ce)
281 shift_data <= shift_data_nxt;
282`else
283 always @(posedge clk)
284 if (sr_go & sr_rdy)
285 shift_data <= sr_val;
286 else if (tick & (state == ST_SHIFT_HI))
287 shift_data <= { shift_data[6:0], 1'b0 };
288`endif
289
290 // IO control
291 always @(posedge clk)
292 begin
293 // Defaults
294 srio_clk_o <= 1'b0;
295 srio_dat_o <= 1'b0;
296 srio_rclk_o <= 1'b0;
297 srio_rclk_oe <= 1'b1;
298
299 // Depends on state
300 case (state)
301 ST_SHIFT_LO: begin
302 srio_dat_o <= shift_data[7];
303 srio_clk_o <= 1'b0;
304 end
305
306 ST_SHIFT_HI: begin
307 srio_dat_o <= shift_data[7];
308 srio_clk_o <= 1'b1;
309 end
310
311 ST_LATCH: begin
312 srio_rclk_o <= 1'b1;
313 end
314
315 ST_SENSE: begin
316 srio_rclk_o <= 1'b1;
317 srio_rclk_oe <= 1'b0;
318 end
319
320 ST_PAUSE: begin
321 srio_rclk_o <= 1'b0;
322 srio_rclk_oe <= 1'b0;
323 end
324 endcase
325 end
326
327 // External status
328 assign sr_rdy = (state == ST_IDLE);
329
330endmodule // sr_btn_if