Tim Edwards | 04ba17f | 2020-10-02 22:27:50 -0400 | [diff] [blame^] | 1 | //---------------------------------------------------------------------------- |
| 2 | // Module: simple_spi_master |
| 3 | // |
| 4 | //---------------------------------------------------------------------------- |
| 5 | // Copyright (C) 2019 efabless, inc. |
| 6 | // |
| 7 | // This source file may be used and distributed without |
| 8 | // restriction provided that this copyright statement is not |
| 9 | // removed from the file and that any derivative work contains |
| 10 | // the original copyright notice and the associated disclaimer. |
| 11 | // |
| 12 | // This source file is free software; you can redistribute it |
| 13 | // and/or modify it under the terms of the GNU Lesser General |
| 14 | // Public License as published by the Free Software Foundation; |
| 15 | // either version 2.1 of the License, or (at your option) any |
| 16 | // later version. |
| 17 | // |
| 18 | // This source is distributed in the hope that it will be |
| 19 | // useful, but WITHOUT ANY WARRANTY; without even the implied |
| 20 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
| 21 | // PURPOSE. See the GNU Lesser General Public License for more |
| 22 | // details. |
| 23 | // |
| 24 | //-------------------------------------------------------------------- |
| 25 | // |
| 26 | // resetn: active low async reset |
| 27 | // clk: master clock (before prescaler) |
| 28 | // stream: |
| 29 | // 0 = apply/release CSB separately for each byte |
| 30 | // 1 = apply CSB until stream bit is cleared |
| 31 | // mlb: |
| 32 | // 0 = msb 1st |
| 33 | // 1 = lsb 1st |
| 34 | // invsck: |
| 35 | // 0 = normal SCK |
| 36 | // 1 = inverted SCK |
| 37 | // invcsb: |
| 38 | // 0 = normal CSB (active low) |
| 39 | // 1 = inverted CSB (active high) |
| 40 | // mode: |
| 41 | // 0 = read and change data on opposite SCK edges |
| 42 | // 1 = read and change data on the same SCK edge |
| 43 | // enable: |
| 44 | // 0 = disable the SPI master |
| 45 | // 1 = enable the SPI master |
| 46 | // irqena: |
| 47 | // 0 = disable interrupt |
| 48 | // 1 = enable interrupt |
| 49 | // prescaler: count (in master clock cycles) of 1/2 SCK cycle. |
| 50 | // |
| 51 | // reg_dat_we: |
| 52 | // 1 = data write enable |
| 53 | // reg_dat_re: |
| 54 | // 1 = data read enable |
| 55 | // reg_cfg_*: Signaling for read/write of configuration register |
| 56 | // reg_dat_*: Signaling for read/write of data register |
| 57 | // |
| 58 | // err_out: Indicates attempt to read/write before data ready |
| 59 | // (failure to wait for reg_dat_wait to clear) |
| 60 | // |
| 61 | // Between "mode" and "invsck", all four standard SPI modes are supported |
| 62 | // |
| 63 | //-------------------------------------------------------------------- |
| 64 | |
| 65 | module simple_spi_master_wb #( |
| 66 | parameter BASE_ADR = 32'h2100_0000, |
| 67 | parameter CONFIG = 8'h00, |
| 68 | parameter DATA = 8'h04 |
| 69 | ) ( |
| 70 | input wb_clk_i, |
| 71 | input wb_rst_i, |
| 72 | input [31:0] wb_adr_i, |
| 73 | input [31:0] wb_dat_i, |
| 74 | input [3:0] wb_sel_i, |
| 75 | input wb_we_i, |
| 76 | input wb_cyc_i, |
| 77 | input wb_stb_i, |
| 78 | output wb_ack_o, |
| 79 | output [31:0] wb_dat_o, |
| 80 | |
| 81 | input sdi, // SPI input |
| 82 | output csb, // SPI chip select |
| 83 | output sck, // SPI clock |
| 84 | output sdo, // SPI output |
| 85 | output irq // interrupt output |
| 86 | ); |
| 87 | |
| 88 | wire [31:0] simple_spi_master_reg_cfg_do; |
| 89 | wire [31:0] simple_spi_master_reg_dat_do; |
| 90 | |
| 91 | wire resetn = ~wb_rst_i; |
| 92 | wire valid = wb_stb_i && wb_cyc_i; |
| 93 | wire simple_spi_master_reg_cfg_sel = valid && (wb_adr_i == (BASE_ADR | CONFIG)); |
| 94 | wire simple_spi_master_reg_dat_sel = valid && (wb_adr_i == (BASE_ADR | DATA)); |
| 95 | |
| 96 | wire [1:0] reg_cfg_we = (simple_spi_master_reg_cfg_sel) ? |
| 97 | (wb_sel_i[1:0] & {2{wb_we_i}}): 2'b00; |
| 98 | wire reg_dat_we = (simple_spi_master_reg_dat_sel) ? (wb_sel_i[0] & wb_we_i): 1'b0; |
| 99 | |
| 100 | wire [31:0] mem_wdata = wb_dat_i; |
| 101 | wire reg_dat_re = simple_spi_master_reg_dat_sel && !wb_sel_i && ~wb_we_i; |
| 102 | |
| 103 | assign wb_dat_o = (simple_spi_master_reg_cfg_sel) ? simple_spi_master_reg_cfg_do : |
| 104 | simple_spi_master_reg_dat_do; |
| 105 | assign wb_ack_o = (simple_spi_master_reg_cfg_sel || simple_spi_master_reg_dat_sel) |
| 106 | && (!reg_dat_wait); |
| 107 | |
| 108 | simple_spi_master spi_master ( |
| 109 | .resetn(resetn), |
| 110 | .clk(wb_clk_i), |
| 111 | .reg_cfg_we(reg_cfg_we), |
| 112 | .reg_cfg_di(mem_wdata), |
| 113 | .reg_cfg_do(simple_spi_master_reg_cfg_do), |
| 114 | .reg_dat_we(reg_dat_we), |
| 115 | .reg_dat_re(reg_dat_re), |
| 116 | .reg_dat_di(mem_wdata), |
| 117 | .reg_dat_do(simple_spi_master_reg_dat_do), |
| 118 | .reg_dat_wait(reg_dat_wait), |
| 119 | |
| 120 | .sdi(sdi), // SPI input |
| 121 | .csb(csb), // SPI chip select |
| 122 | .sck(sck), // SPI clock |
| 123 | .sdo(sdo), // SPI output |
| 124 | .irq_out(irq) // interrupt |
| 125 | ); |
| 126 | endmodule |
| 127 | |
| 128 | module simple_spi_master ( |
| 129 | input resetn, |
| 130 | input clk, // master clock (assume 100MHz) |
| 131 | |
| 132 | input [1:0] reg_cfg_we, |
| 133 | input [31:0] reg_cfg_di, |
| 134 | output [31:0] reg_cfg_do, |
| 135 | |
| 136 | input reg_dat_we, |
| 137 | input reg_dat_re, |
| 138 | input [31:0] reg_dat_di, |
| 139 | output [31:0] reg_dat_do, |
| 140 | output reg_dat_wait, |
| 141 | output irq_out, |
| 142 | output err_out, |
| 143 | |
| 144 | input sdi, // SPI input |
| 145 | output csb, // SPI chip select |
| 146 | output sck, // SPI clock |
| 147 | output sdo // SPI output |
| 148 | ); |
| 149 | |
| 150 | parameter IDLE = 2'b00; |
| 151 | parameter SENDL = 2'b01; |
| 152 | parameter SENDH = 2'b10; |
| 153 | parameter FINISH = 2'b11; |
| 154 | |
| 155 | reg done; |
| 156 | reg isdo, hsck, icsb; |
| 157 | reg [1:0] state; |
| 158 | reg isck; |
| 159 | reg err_out; |
| 160 | |
| 161 | reg [7:0] treg, rreg, d_latched; |
| 162 | reg [2:0] nbit; |
| 163 | |
| 164 | reg [7:0] prescaler; |
| 165 | reg [7:0] count; |
| 166 | reg invsck; |
| 167 | reg invcsb; |
| 168 | reg mlb; |
| 169 | reg irqena; |
| 170 | reg stream; |
| 171 | reg mode; |
| 172 | reg enable; |
| 173 | |
| 174 | wire csb; |
| 175 | wire irq_out; |
| 176 | wire sck; |
| 177 | wire sdo; |
| 178 | |
| 179 | // Define behavior for inverted SCK and inverted CSB |
| 180 | assign csb = (invcsb) ? ~icsb : icsb; |
| 181 | assign sck = (invsck) ? ~isck : isck; |
| 182 | |
| 183 | // No bidirectional 3-pin mode defined, so SDO is enabled whenever CSB is low. |
| 184 | assign sdo = icsb ? 1'bz : isdo; |
| 185 | |
| 186 | assign irq_out = irqena & done; |
| 187 | |
| 188 | // Read configuration and data registers |
| 189 | assign reg_cfg_do = {17'd0, irqena, enable, stream, mode, invsck, invcsb, mlb, prescaler}; |
| 190 | assign reg_dat_wait = ~done; |
| 191 | assign reg_dat_do = done ? rreg : ~0; |
| 192 | |
| 193 | // Write configuration register |
| 194 | always @(posedge clk or negedge resetn) begin |
| 195 | if (resetn == 1'b0) begin |
| 196 | prescaler <= 8'd2; |
| 197 | invcsb <= 1'b0; |
| 198 | invsck <= 1'b0; |
| 199 | mlb <= 1'b0; |
| 200 | enable <= 1'b0; |
| 201 | irqena <= 1'b0; |
| 202 | stream <= 1'b0; |
| 203 | mode <= 1'b0; |
| 204 | end else begin |
| 205 | if (reg_cfg_we[0]) prescaler <= reg_cfg_di[7:0]; |
| 206 | if (reg_cfg_we[1]) begin |
| 207 | mlb <= reg_cfg_di[8]; |
| 208 | invcsb <= reg_cfg_di[9]; |
| 209 | invsck <= reg_cfg_di[10]; |
| 210 | mode <= reg_cfg_di[11]; |
| 211 | stream <= reg_cfg_di[12]; |
| 212 | enable <= reg_cfg_di[13]; |
| 213 | irqena <= reg_cfg_di[14]; |
| 214 | end //reg_cfg_we[1] |
| 215 | end //resetn |
| 216 | end //always |
| 217 | |
| 218 | // Watch for read and write enables on clk, not hsck, so as not to |
| 219 | // miss them. |
| 220 | |
| 221 | reg w_latched, r_latched; |
| 222 | |
| 223 | always @(posedge clk or negedge resetn) begin |
| 224 | if (resetn == 1'b0) begin |
| 225 | err_out <= 1'b0; |
| 226 | w_latched <= 1'b0; |
| 227 | r_latched <= 1'b0; |
| 228 | d_latched <= 8'd0; |
| 229 | end else begin |
| 230 | // Clear latches on SEND, otherwise latch when seen |
| 231 | if (state == SENDL || state == SENDH) begin |
| 232 | if (reg_dat_we == 1'b0) begin |
| 233 | w_latched <= 1'b0; |
| 234 | end |
| 235 | end else begin |
| 236 | if (reg_dat_we == 1'b1) begin |
| 237 | if (done == 1'b0 && w_latched == 1'b1) begin |
| 238 | err_out <= 1'b1; |
| 239 | end else begin |
| 240 | w_latched <= 1'b1; |
| 241 | d_latched <= reg_dat_di[7:0]; |
| 242 | err_out <= 1'b0; |
| 243 | end |
| 244 | end |
| 245 | end |
| 246 | |
| 247 | if (reg_dat_re == 1'b1) begin |
| 248 | if (r_latched == 1'b1) begin |
| 249 | r_latched <= 1'b0; |
| 250 | end else begin |
| 251 | err_out <= 1'b1; // byte not available |
| 252 | end |
| 253 | end else if (state == FINISH) begin |
| 254 | r_latched <= 1'b1; |
| 255 | end if (state == SENDL || state == SENDH) begin |
| 256 | if (r_latched == 1'b1) begin |
| 257 | err_out <= 1'b1; // last byte was never read |
| 258 | end else begin |
| 259 | r_latched <= 1'b0; |
| 260 | end |
| 261 | end |
| 262 | end |
| 263 | end |
| 264 | |
| 265 | // State transition. |
| 266 | |
| 267 | always @(posedge hsck or negedge resetn) begin |
| 268 | if (resetn == 1'b0) begin |
| 269 | state <= IDLE; |
| 270 | nbit <= 3'd0; |
| 271 | icsb <= 1'b1; |
| 272 | done <= 1'b1; |
| 273 | end else begin |
| 274 | if (state == IDLE) begin |
| 275 | if (w_latched == 1'b1) begin |
| 276 | state <= SENDL; |
| 277 | nbit <= 3'd0; |
| 278 | icsb <= 1'b0; |
| 279 | done <= 1'b0; |
| 280 | end else begin |
| 281 | icsb <= ~stream; |
| 282 | end |
| 283 | end else if (state == SENDL) begin |
| 284 | state <= SENDH; |
| 285 | end else if (state == SENDH) begin |
| 286 | nbit <= nbit + 1; |
| 287 | if (nbit == 3'd7) begin |
| 288 | state <= FINISH; |
| 289 | end else begin |
| 290 | state <= SENDL; |
| 291 | end |
| 292 | end else if (state == FINISH) begin |
| 293 | icsb <= ~stream; |
| 294 | done <= 1'b1; |
| 295 | state <= IDLE; |
| 296 | end |
| 297 | end |
| 298 | end |
| 299 | |
| 300 | // Set up internal clock. The enable bit gates the internal clock |
| 301 | // to shut down the master SPI when disabled. |
| 302 | |
| 303 | always @(posedge clk or negedge resetn) begin |
| 304 | if (resetn == 1'b0) begin |
| 305 | count <= 8'd0; |
| 306 | hsck <= 1'b0; |
| 307 | end else begin |
| 308 | if (enable == 1'b0) begin |
| 309 | count <= 8'd0; |
| 310 | end else begin |
| 311 | count <= count + 1; |
| 312 | if (count == prescaler) begin |
| 313 | hsck <= ~hsck; |
| 314 | count <= 8'd0; |
| 315 | end // count |
| 316 | end // enable |
| 317 | end // resetn |
| 318 | end // always |
| 319 | |
| 320 | // sck is half the rate of hsck |
| 321 | |
| 322 | always @(posedge hsck or negedge resetn) begin |
| 323 | if (resetn == 1'b0) begin |
| 324 | isck <= 1'b0; |
| 325 | end else begin |
| 326 | if (state == IDLE || state == FINISH) |
| 327 | isck <= 1'b0; |
| 328 | else |
| 329 | isck <= ~isck; |
| 330 | end // resetn |
| 331 | end // always |
| 332 | |
| 333 | // Main procedure: read, write, shift data |
| 334 | |
| 335 | always @(posedge hsck or negedge resetn) begin |
| 336 | if (resetn == 1'b0) begin |
| 337 | rreg <= 8'hff; |
| 338 | treg <= 8'hff; |
| 339 | isdo <= 1'b0; |
| 340 | end else begin |
| 341 | if (isck == 1'b0 && (state == SENDL || state == SENDH)) begin |
| 342 | if (mlb == 1'b1) begin |
| 343 | // LSB first, sdi@msb -> right shift |
| 344 | rreg <= {sdi, rreg[7:1]}; |
| 345 | end else begin |
| 346 | // MSB first, sdi@lsb -> left shift |
| 347 | rreg <= {rreg[6:0], sdi}; |
| 348 | end |
| 349 | end // read on ~isck |
| 350 | |
| 351 | if (w_latched == 1'b1) begin |
| 352 | if (mlb == 1'b1) begin |
| 353 | treg <= {1'b1, d_latched[7:1]}; |
| 354 | isdo <= d_latched[0]; |
| 355 | end else begin |
| 356 | treg <= {d_latched[6:0], 1'b1}; |
| 357 | isdo <= d_latched[7]; |
| 358 | end // mlb |
| 359 | end else if ((mode ^ isck) == 1'b1) begin |
| 360 | if (mlb == 1'b1) begin |
| 361 | // LSB first, shift right |
| 362 | treg <= {1'b1, treg[7:1]}; |
| 363 | isdo <= treg[0]; |
| 364 | end else begin |
| 365 | // MSB first shift LEFT |
| 366 | treg <= {treg[6:0], 1'b1}; |
| 367 | isdo <= treg[7]; |
| 368 | end // mlb |
| 369 | end // write on mode ^ isck |
| 370 | end // resetn |
| 371 | end // always |
| 372 | |
| 373 | endmodule |