Matt Venn | 08cd6eb | 2020-11-16 12:01:14 +0100 | [diff] [blame] | 1 | `default_nettype none |
Tim Edwards | cd64af5 | 2020-08-07 11:11:58 -0400 | [diff] [blame] | 2 | /* |
| 3 | * PicoSoC - A simple example SoC using PicoRV32 |
| 4 | * |
| 5 | * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at> |
| 6 | * |
| 7 | * Permission to use, copy, modify, and/or distribute this software for any |
| 8 | * purpose with or without fee is hereby granted, provided that the above |
| 9 | * copyright notice and this permission notice appear in all copies. |
| 10 | * |
| 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 18 | * |
| 19 | */ |
| 20 | |
| 21 | `timescale 1 ns / 1 ps |
| 22 | |
| 23 | // |
| 24 | // Simple SPI flash simulation model |
| 25 | // |
| 26 | // This model samples io input signals 1ns before the SPI clock edge and |
| 27 | // updates output signals 1ns after the SPI clock edge. |
| 28 | // |
| 29 | // Supported commands: |
| 30 | // AB, B9, FF, 03, BB, EB, ED |
| 31 | // |
| 32 | // Well written SPI flash data sheets: |
| 33 | // Cypress S25FL064L http://www.cypress.com/file/316661/download |
| 34 | // Cypress S25FL128L http://www.cypress.com/file/316171/download |
| 35 | // |
| 36 | |
| 37 | module spiflash #( |
| 38 | parameter FILENAME = "firmware.hex" |
| 39 | )( |
| 40 | input csb, |
| 41 | input clk, |
| 42 | inout io0, // MOSI |
| 43 | inout io1, // MISO |
| 44 | inout io2, |
| 45 | inout io3 |
| 46 | ); |
| 47 | localparam verbose = 0; |
| 48 | localparam integer latency = 8; |
| 49 | |
| 50 | reg [7:0] buffer; |
| 51 | integer bitcount = 0; |
| 52 | integer bytecount = 0; |
| 53 | integer dummycount = 0; |
| 54 | |
| 55 | reg [7:0] spi_cmd; |
| 56 | reg [7:0] xip_cmd = 0; |
| 57 | reg [23:0] spi_addr; |
| 58 | |
| 59 | reg [7:0] spi_in; |
| 60 | reg [7:0] spi_out; |
| 61 | reg spi_io_vld; |
| 62 | |
| 63 | reg powered_up = 0; |
| 64 | |
| 65 | localparam [3:0] mode_spi = 1; |
| 66 | localparam [3:0] mode_dspi_rd = 2; |
| 67 | localparam [3:0] mode_dspi_wr = 3; |
| 68 | localparam [3:0] mode_qspi_rd = 4; |
| 69 | localparam [3:0] mode_qspi_wr = 5; |
| 70 | localparam [3:0] mode_qspi_ddr_rd = 6; |
| 71 | localparam [3:0] mode_qspi_ddr_wr = 7; |
| 72 | |
| 73 | reg [3:0] mode = 0; |
| 74 | reg [3:0] next_mode = 0; |
| 75 | |
| 76 | reg io0_oe = 0; |
| 77 | reg io1_oe = 0; |
| 78 | reg io2_oe = 0; |
| 79 | reg io3_oe = 0; |
| 80 | |
| 81 | reg io0_dout = 0; |
| 82 | reg io1_dout = 0; |
| 83 | reg io2_dout = 0; |
| 84 | reg io3_dout = 0; |
| 85 | |
| 86 | assign #1 io0 = io0_oe ? io0_dout : 1'bz; |
| 87 | assign #1 io1 = io1_oe ? io1_dout : 1'bz; |
| 88 | assign #1 io2 = io2_oe ? io2_dout : 1'bz; |
| 89 | assign #1 io3 = io3_oe ? io3_dout : 1'bz; |
| 90 | |
| 91 | wire io0_delayed; |
| 92 | wire io1_delayed; |
| 93 | wire io2_delayed; |
| 94 | wire io3_delayed; |
| 95 | |
| 96 | assign #1 io0_delayed = io0; |
| 97 | assign #1 io1_delayed = io1; |
| 98 | assign #1 io2_delayed = io2; |
| 99 | assign #1 io3_delayed = io3; |
| 100 | |
| 101 | // 16 MB (128Mb) Flash |
| 102 | reg [7:0] memory [0:16*1024*1024-1]; |
| 103 | |
| 104 | initial begin |
| 105 | $display("Memory 5 bytes = 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", |
| 106 | memory[1048576], memory[1048577], memory[1048578], |
| 107 | memory[1048579], memory[1048580]); |
| 108 | $display("Reading %s", FILENAME); |
| 109 | $readmemh(FILENAME, memory); |
| 110 | $display("%s loaded into memory", FILENAME); |
| 111 | $display("Memory 5 bytes = 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", |
| 112 | memory[1048576], memory[1048577], memory[1048578], |
| 113 | memory[1048579], memory[1048580]); |
| 114 | end |
| 115 | |
| 116 | task spi_action; |
| 117 | begin |
| 118 | spi_in = buffer; |
| 119 | |
| 120 | if (bytecount == 1) begin |
| 121 | spi_cmd = buffer; |
| 122 | |
| 123 | if (spi_cmd == 8'h ab) |
| 124 | powered_up = 1; |
| 125 | |
| 126 | if (spi_cmd == 8'h b9) |
| 127 | powered_up = 0; |
| 128 | |
| 129 | if (spi_cmd == 8'h ff) |
| 130 | xip_cmd = 0; |
| 131 | end |
| 132 | |
| 133 | if (powered_up && spi_cmd == 'h 03) begin |
| 134 | if (bytecount == 2) |
| 135 | spi_addr[23:16] = buffer; |
| 136 | |
| 137 | if (bytecount == 3) |
| 138 | spi_addr[15:8] = buffer; |
| 139 | |
| 140 | if (bytecount == 4) |
| 141 | spi_addr[7:0] = buffer; |
| 142 | |
| 143 | if (bytecount >= 4) begin |
| 144 | buffer = memory[spi_addr]; |
| 145 | spi_addr = spi_addr + 1; |
| 146 | end |
| 147 | end |
| 148 | |
| 149 | if (powered_up && spi_cmd == 'h bb) begin |
| 150 | if (bytecount == 1) |
| 151 | mode = mode_dspi_rd; |
| 152 | |
| 153 | if (bytecount == 2) |
| 154 | spi_addr[23:16] = buffer; |
| 155 | |
| 156 | if (bytecount == 3) |
| 157 | spi_addr[15:8] = buffer; |
| 158 | |
| 159 | if (bytecount == 4) |
| 160 | spi_addr[7:0] = buffer; |
| 161 | |
| 162 | if (bytecount == 5) begin |
| 163 | xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00; |
| 164 | mode = mode_dspi_wr; |
| 165 | dummycount = latency; |
| 166 | end |
| 167 | |
| 168 | if (bytecount >= 5) begin |
| 169 | buffer = memory[spi_addr]; |
| 170 | spi_addr = spi_addr + 1; |
| 171 | end |
| 172 | end |
| 173 | |
| 174 | if (powered_up && spi_cmd == 'h eb) begin |
| 175 | if (bytecount == 1) |
| 176 | mode = mode_qspi_rd; |
| 177 | |
| 178 | if (bytecount == 2) |
| 179 | spi_addr[23:16] = buffer; |
| 180 | |
| 181 | if (bytecount == 3) |
| 182 | spi_addr[15:8] = buffer; |
| 183 | |
| 184 | if (bytecount == 4) |
| 185 | spi_addr[7:0] = buffer; |
| 186 | |
| 187 | if (bytecount == 5) begin |
| 188 | xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00; |
| 189 | mode = mode_qspi_wr; |
| 190 | dummycount = latency; |
| 191 | end |
| 192 | |
| 193 | if (bytecount >= 5) begin |
| 194 | buffer = memory[spi_addr]; |
| 195 | spi_addr = spi_addr + 1; |
| 196 | end |
| 197 | end |
| 198 | |
| 199 | if (powered_up && spi_cmd == 'h ed) begin |
| 200 | if (bytecount == 1) |
| 201 | next_mode = mode_qspi_ddr_rd; |
| 202 | |
| 203 | if (bytecount == 2) |
| 204 | spi_addr[23:16] = buffer; |
| 205 | |
| 206 | if (bytecount == 3) |
| 207 | spi_addr[15:8] = buffer; |
| 208 | |
| 209 | if (bytecount == 4) |
| 210 | spi_addr[7:0] = buffer; |
| 211 | |
| 212 | if (bytecount == 5) begin |
| 213 | xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00; |
| 214 | mode = mode_qspi_ddr_wr; |
| 215 | dummycount = latency; |
| 216 | end |
| 217 | |
| 218 | if (bytecount >= 5) begin |
| 219 | buffer = memory[spi_addr]; |
| 220 | spi_addr = spi_addr + 1; |
| 221 | end |
| 222 | end |
| 223 | |
| 224 | spi_out = buffer; |
| 225 | spi_io_vld = 1; |
| 226 | |
| 227 | if (verbose) begin |
| 228 | if (bytecount == 1) |
| 229 | $write("<SPI-START>"); |
| 230 | $write("<SPI:%02x:%02x>", spi_in, spi_out); |
| 231 | end |
| 232 | |
| 233 | end |
| 234 | endtask |
| 235 | |
| 236 | task ddr_rd_edge; |
| 237 | begin |
| 238 | buffer = {buffer, io3_delayed, io2_delayed, io1_delayed, io0_delayed}; |
| 239 | bitcount = bitcount + 4; |
| 240 | if (bitcount == 8) begin |
| 241 | bitcount = 0; |
| 242 | bytecount = bytecount + 1; |
| 243 | spi_action; |
| 244 | end |
| 245 | end |
| 246 | endtask |
| 247 | |
| 248 | task ddr_wr_edge; |
| 249 | begin |
| 250 | io0_oe = 1; |
| 251 | io1_oe = 1; |
| 252 | io2_oe = 1; |
| 253 | io3_oe = 1; |
| 254 | |
| 255 | io0_dout = buffer[4]; |
| 256 | io1_dout = buffer[5]; |
| 257 | io2_dout = buffer[6]; |
| 258 | io3_dout = buffer[7]; |
| 259 | |
| 260 | buffer = {buffer, 4'h 0}; |
| 261 | bitcount = bitcount + 4; |
| 262 | if (bitcount == 8) begin |
| 263 | bitcount = 0; |
| 264 | bytecount = bytecount + 1; |
| 265 | spi_action; |
| 266 | end |
| 267 | end |
| 268 | endtask |
| 269 | |
| 270 | always @(csb) begin |
| 271 | if (csb) begin |
| 272 | if (verbose) begin |
| 273 | $display(""); |
| 274 | $fflush; |
| 275 | end |
| 276 | buffer = 0; |
| 277 | bitcount = 0; |
| 278 | bytecount = 0; |
| 279 | mode = mode_spi; |
| 280 | io0_oe = 0; |
| 281 | io1_oe = 0; |
| 282 | io2_oe = 0; |
| 283 | io3_oe = 0; |
| 284 | end else |
| 285 | if (xip_cmd) begin |
| 286 | buffer = xip_cmd; |
| 287 | bitcount = 0; |
| 288 | bytecount = 1; |
| 289 | spi_action; |
| 290 | end |
| 291 | end |
| 292 | |
| 293 | always @(csb, clk) begin |
| 294 | spi_io_vld = 0; |
| 295 | if (!csb && !clk) begin |
| 296 | if (dummycount > 0) begin |
| 297 | io0_oe = 0; |
| 298 | io1_oe = 0; |
| 299 | io2_oe = 0; |
| 300 | io3_oe = 0; |
| 301 | end else |
| 302 | case (mode) |
| 303 | mode_spi: begin |
| 304 | io0_oe = 0; |
| 305 | io1_oe = 1; |
| 306 | io2_oe = 0; |
| 307 | io3_oe = 0; |
| 308 | io1_dout = buffer[7]; |
| 309 | end |
| 310 | mode_dspi_rd: begin |
| 311 | io0_oe = 0; |
| 312 | io1_oe = 0; |
| 313 | io2_oe = 0; |
| 314 | io3_oe = 0; |
| 315 | end |
| 316 | mode_dspi_wr: begin |
| 317 | io0_oe = 1; |
| 318 | io1_oe = 1; |
| 319 | io2_oe = 0; |
| 320 | io3_oe = 0; |
| 321 | io0_dout = buffer[6]; |
| 322 | io1_dout = buffer[7]; |
| 323 | end |
| 324 | mode_qspi_rd: begin |
| 325 | io0_oe = 0; |
| 326 | io1_oe = 0; |
| 327 | io2_oe = 0; |
| 328 | io3_oe = 0; |
| 329 | end |
| 330 | mode_qspi_wr: begin |
| 331 | io0_oe = 1; |
| 332 | io1_oe = 1; |
| 333 | io2_oe = 1; |
| 334 | io3_oe = 1; |
| 335 | io0_dout = buffer[4]; |
| 336 | io1_dout = buffer[5]; |
| 337 | io2_dout = buffer[6]; |
| 338 | io3_dout = buffer[7]; |
| 339 | end |
| 340 | mode_qspi_ddr_rd: begin |
| 341 | ddr_rd_edge; |
| 342 | end |
| 343 | mode_qspi_ddr_wr: begin |
| 344 | ddr_wr_edge; |
| 345 | end |
| 346 | endcase |
| 347 | if (next_mode) begin |
| 348 | case (next_mode) |
| 349 | mode_qspi_ddr_rd: begin |
| 350 | io0_oe = 0; |
| 351 | io1_oe = 0; |
| 352 | io2_oe = 0; |
| 353 | io3_oe = 0; |
| 354 | end |
| 355 | mode_qspi_ddr_wr: begin |
| 356 | io0_oe = 1; |
| 357 | io1_oe = 1; |
| 358 | io2_oe = 1; |
| 359 | io3_oe = 1; |
| 360 | io0_dout = buffer[4]; |
| 361 | io1_dout = buffer[5]; |
| 362 | io2_dout = buffer[6]; |
| 363 | io3_dout = buffer[7]; |
| 364 | end |
| 365 | endcase |
| 366 | mode = next_mode; |
| 367 | next_mode = 0; |
| 368 | end |
| 369 | end |
| 370 | end |
| 371 | |
| 372 | always @(posedge clk) begin |
| 373 | if (!csb) begin |
| 374 | if (dummycount > 0) begin |
| 375 | dummycount = dummycount - 1; |
| 376 | end else |
| 377 | case (mode) |
| 378 | mode_spi: begin |
| 379 | buffer = {buffer, io0}; |
| 380 | bitcount = bitcount + 1; |
| 381 | if (bitcount == 8) begin |
| 382 | bitcount = 0; |
| 383 | bytecount = bytecount + 1; |
| 384 | spi_action; |
| 385 | end |
| 386 | end |
| 387 | mode_dspi_rd, mode_dspi_wr: begin |
| 388 | buffer = {buffer, io1, io0}; |
| 389 | bitcount = bitcount + 2; |
| 390 | if (bitcount == 8) begin |
| 391 | bitcount = 0; |
| 392 | bytecount = bytecount + 1; |
| 393 | spi_action; |
| 394 | end |
| 395 | end |
| 396 | mode_qspi_rd, mode_qspi_wr: begin |
| 397 | buffer = {buffer, io3, io2, io1, io0}; |
| 398 | bitcount = bitcount + 4; |
| 399 | if (bitcount == 8) begin |
| 400 | bitcount = 0; |
| 401 | bytecount = bytecount + 1; |
| 402 | spi_action; |
| 403 | end |
| 404 | end |
| 405 | mode_qspi_ddr_rd: begin |
| 406 | ddr_rd_edge; |
| 407 | end |
| 408 | mode_qspi_ddr_wr: begin |
| 409 | ddr_wr_edge; |
| 410 | end |
| 411 | endcase |
| 412 | end |
| 413 | end |
| 414 | endmodule |