Sylvain Munaut | bd83e53 | 2020-09-15 22:11:29 +0200 | [diff] [blame] | 1 | /* |
| 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 | |
| 16 | module 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 | |
| 330 | endmodule // sr_btn_if |