1 /* ****************************************************************************
\r
2 -- Source file: spi_prom.v
\r
5 -- Description: A generic LocalBus hardware interface to a SPI PROM that
\r
6 -- provides for a reusable software interface for configuring
\r
7 -- current and future SPI PROMs. Uses a single BRAM.
\r
8 -- Language: Verilog-2001 and VHDL-1993
\r
9 -- Simulation: Mentor-Modelsim
\r
10 -- Synthesis: Xilinst-XST
\r
11 -- License: This project is licensed with the CERN Open Hardware Licence
\r
12 -- v1.2. You may redistribute and modify this project under the
\r
13 -- terms of the CERN OHL v.1.2. (http://ohwr.org/cernohl).
\r
14 -- This project is distributed WITHOUT ANY EXPRESS OR IMPLIED
\r
15 -- WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY
\r
16 -- AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN OHL
\r
17 -- v.1.2 for applicable Conditions.
\r
20 -- reset --->| spi_prom.v |
\r
22 -- ck_divisor[7:0] --->| |
\r
24 -- lb_cs_prom_c ------>| |
\r
25 -- lb_cs_prom_d ------>| |
\r
26 -- lb_wr ------------->| |
\r
27 -- lb_rd ------------->| |---> lb_rd_rdy
\r
28 -- lb_wr_d[31:0] ----->| |---> lb_rd_d[31:0]
\r
32 -- spi_miso ---------->| |---> spi_mosi
\r
34 -- | |---> reconfig_req
\r
35 -- | |---> reconfig_addr[31:0]
\r
38 -- [ Control Register Write ]
\r
40 -- 0x0 CMD_IDLE NA : NOP
\r
41 -- 0x1 CMD_QUERY_ID NA : Read PROM ID
\r
42 -- 0x2 CMD_RD_TIMESTAMP NA : Read FPGA UNIX Timestamp
\r
43 -- 0x3 CMD_RD_PROM ADDR(27:8) : Read 256 Bytes from ADDR
\r
44 -- 0x4 CMD_UNLOCK 0x0AA5AA : Unlock for Erase and Write Ops
\r
45 -- 0x055A55 : Unlock for Reconfig Operation
\r
46 -- 0x5 CMD_ERASE_BULK NA : Erase entire PROM ( Slow )
\r
47 -- 0x6 CMD_ERASE_SECTOR ADDR(27:8) : Erase Sector at ADDR
\r
48 -- 0x7 CMD_WR_PROM ADDR(27:8) : Write 256 Bytes to ADDR
\r
49 -- 0x8 CMD_RECONFIG_FPGA ADDR(27:8) : Reconfig FPGA from ADDR
\r
50 -- 0x9 CMD_RELEASE_POWERDOWN NA : Release from Deep Power Down
\r
52 -- [ Control Register Read ]
\r
53 -- D(27:8) spi_addr[27:8]
\r
54 -- D(7) err_ram_wr_jk : MOSI RAM Overwrite Error
\r
55 -- D(6) stat_polling_reqd_jk : Polling is Required
\r
56 -- D(5) stat_mosi_buf_free : MOSI buffer is available.
\r
57 -- D(4) stat_miso_buf_rdy_jk : MISO buffer has data to be read.
\r
58 -- D(3) stat_fsm_wr : Interface in "Write" state.
\r
59 -- D(2) stat_unlocked_jk : PROM is unlocked for Writing and Erasing
\r
60 -- D(1) flag_wip : PROM is Writing or Erasing
\r
61 -- D(0) stat_spi_busy : SPI interface is in use
\r
64 -- [ Data Register Read/Write ]
\r
65 -- Up to 64 DWORD Burst Region for Writes and Reads
\r
67 -- Resources Required in Spartan3A XC3S200A device:
\r
68 -- Number of RAMB16BWEs: 1 out of 16 6%
\r
70 -- Number of Slice Flip Flops: 410 out of 3,584 11%
\r
71 -- Number of 4 input LUTs: 485 out of 3,584 13%
\r
73 -- Number of Slice Flip Flops: 410 out of 3,584 11%
\r
74 -- Number of 4 input LUTs: 410 out of 3,584 11%
\r
77 -- Note: ck_divisor[7:0] is number of clk_lb cycles per HALF spi_sck period.
\r
78 -- Example clk_lb = 80 MHz, ck_divisor=10, spi_sck will be 4 MHz.
\r
80 -- Note: Regarding 4-Byte Addressing. The 256Mb Micron datasheet is confusing.
\r
81 -- There are special 4-Byte commands, but they only exist for certain
\r
82 -- mask revs of their 256Mb parts ( N25Q256A83 ). All of the other
\r
83 -- N25Q256A revs require the "Enter 4-Byte" command which then extends
\r
84 -- the regular 3-Byte commands to 4-Bytes. This module issues the
\r
85 -- "Enter 4-Byte" at the beginning of ANY PROM command and then issues
\r
86 -- "Exit 4-Byte" after the command is done.
\r
90 -- Sector = 256 pages ( 64 KB )
\r
91 -- Sector Erase = ~500ms
\r
92 -- Page Program = ~1ms
\r
93 -- Measured Program Times:
\r
94 -- 256 0xFFs = 330 us
\r
95 -- 256 0x00s = 800 us
\r
96 -- 64 0x00s = 200 us
\r
97 -- 32 0x00s = 100 us
\r
100 -- Revision History:
\r
101 -- Ver# When Who What
\r
102 -- ---- -------- -------- ---------------------------------------------------
\r
103 -- 0.1 06.01.14 khubbard Creation
\r
104 -- 0.2 07.08.14 khubbard Added 4-byte addressing support
\r
105 -- 0.3 08.18.14 khubbard Added root protection for Slot-0 BootLoader
\r
106 -- ***************************************************************************/
\r
107 `default_nettype none // Strictly enforce all nets to be declared
\r
111 parameter depth_len = 128,
\r
112 parameter depth_bits = 7
\r
113 // parameter depth_len = 8,
\r
114 // parameter depth_bits = 3
\r
118 input wire prom_is_32b,
\r
119 input wire [31:0] slot_size,
\r
120 input wire protect_1st_slot,
\r
122 input wire [7:0] ck_divisor,
\r
123 input wire [3:0] spi_ctrl,
\r
125 input wire lb_cs_prom_c,
\r
126 input wire lb_cs_prom_d,
\r
129 input wire [31:0] lb_wr_d,
\r
130 output reg [31:0] lb_rd_d,
\r
131 output reg lb_rd_rdy,
\r
132 output reg flag_wip,
\r
133 input wire reconfig_2nd_slot,
\r
134 output reg reconfig_req,
\r
135 output reg [31:0] reconfig_addr,
\r
136 output reg bist_req,
\r
138 output wire spi_sck,
\r
139 output wire spi_cs_l,
\r
140 output wire spi_mosi,
\r
141 input wire spi_miso
\r
142 );// module spi_prom
\r
144 wire [31:0] all_zeros;
\r
145 wire [31:0] all_ones;
\r
151 reg [11:0] xfer_tx_bytes;
\r
152 reg [11:0] xfer_rx_bytes;
\r
154 reg [3:0] mosi_fsm;
\r
155 reg [11:0] spi_byte_cnt;
\r
156 reg [7:0] mosi_byte_d;
\r
159 wire mosi_byte_req;
\r
160 reg mosi_byte_req_p1;
\r
161 reg mosi_buf_rdy_jk;
\r
162 reg [1:0] mosi_byte_cnt;
\r
164 wire [7:0] miso_byte_d;
\r
165 wire miso_byte_rdy;
\r
166 reg [1:0] miso_byte_cnt;
\r
171 reg req_timestamp_p1;
\r
172 reg req_timestamp_p2;
\r
175 reg spi_is_idle_p1;
\r
176 reg spi_is_idle_jk;
\r
177 wire [11:0] page_size;
\r
178 reg [31:0] spi_addr;
\r
179 reg spi_addr_page_inc;
\r
180 reg spi_addr_page_inc_p1;
\r
184 reg [7:0] cmd_byte;
\r
186 reg [31:0] ram_array[depth_len-1:0];
\r
187 reg [depth_bits-1:0] a_addr;
\r
188 reg [depth_bits-1:0] a_addr_p1;
\r
189 reg [depth_bits-1:0] b_addr;
\r
193 reg [31:0] a_di_p1;
\r
195 reg [31:0] a_di_miso;
\r
198 reg [31:0] b_do_p1;
\r
200 reg [31:0] prom_ctrl_reg;
\r
201 reg [31:0] prom_data_reg;
\r
202 wire [31:0] time_stamp_d;
\r
203 reg [7:0] post_rst_cnt;
\r
205 reg strb_clear_fsm;
\r
210 reg rd_ping_pong_pend;
\r
211 reg [1:0] rd_ping_pong_actv;
\r
212 reg buffer_free_jk;
\r
213 reg prom_wr_locked_jk;
\r
214 reg prom_wr_locked_jk_p1;
\r
215 reg fpga_boot_locked_jk;
\r
216 reg fpga_root_locked_jk;
\r
218 reg stat_polling_reqd_jk;
\r
219 reg stat_mosi_buf_free;
\r
220 reg stat_miso_buf_rdy_jk;
\r
222 reg stat_unlocked_jk;
\r
226 reg done_enter_4byte;
\r
230 `define CMD_IDLE 4'h0
\r
231 `define CMD_QUERY_ID 4'h1
\r
232 `define CMD_RD_TIMESTAMP 4'h2
\r
233 `define CMD_RD_PROM 4'h3
\r
234 `define CMD_UNLOCK 4'h4
\r
235 `define CMD_ERASE_BULK 4'h5
\r
236 `define CMD_ERASE_SECTOR 4'h6
\r
237 `define CMD_WR_PROM 4'h7
\r
238 `define CMD_RECONFIG_FPGA 4'h8
\r
239 `define CMD_RELEASE_POWERDOWN 4'h9
\r
240 `define CMD_REL_PD 4'h9
\r
242 assign all_zeros = 32'h00000000;
\r
243 assign all_ones = 32'hFFFFFFFF;
\r
245 assign page_size = ( 2*depth_len );// Number of Bytes in a Page ( 1/2 RAM )
\r
247 //assign sump_events[15:12] = mosi_fsm[3:0];
\r
248 //assign sump_events[11:8] = cmd_nib[3:0];
\r
249 //assign sump_events[7] = err_ram_wr_jk;
\r
250 //assign sump_events[6] = stat_polling_reqd_jk;
\r
251 //assign sump_events[5] = stat_miso_buf_rdy_jk;
\r
252 //assign sump_events[4] = stat_mosi_buf_free;
\r
253 //assign sump_events[3] = stat_fsm_wr;
\r
254 //assign sump_events[2] = stat_unlocked_jk;
\r
255 //assign sump_events[1] = flag_wip_loc;
\r
256 //assign sump_events[0] = stat_spi_busy;
\r
258 //assign slot_size = 32'h00020000; // 1 Mbit slots for XC3S200A
\r
260 //-----------------------------------------------------------------------------
\r
261 // PROM_CTRL_REG :
\r
263 // D(27:8) : Address(27:8) for Write or Read
\r
264 // D(7:0) : Control Nibble:
\r
267 // D(7) : 1 = MOSI Buffer Overrun Error
\r
268 // D(6) : 1 = MOSI Polling Required ( Read D(4) before Writing )
\r
269 // D(5) : 1 = MOSI Buffer Free ( OK to Write 256 Bytes )
\r
270 // D(4) : 1 = MISO Buffer Read Ready ( OK to Read 256 Bytes )
\r
272 // D(3) : 1 = Write Bursting State
\r
273 // D(2) : 1 = Unlocked for Writing
\r
274 // D(1) : 1 = PROM Write In Progress ( WIP )
\r
275 // D(0) : 1 = SPI is Busy
\r
277 // PROM_DATA_REG :
\r
278 // D(31:0) : Write or Read Data ( 64 DWORDs at a time for Write and Read )
\r
279 //-----------------------------------------------------------------------------
\r
280 always @ ( posedge clk_lb ) begin : proc_lb_regs
\r
282 bist_req <= bist_req_jk;
\r
283 strb_clear_fsm <= 0;
\r
286 cmd_en_p1 <= cmd_en;
\r
288 req_timestamp <= 0;
\r
289 req_timestamp_p1 <= req_timestamp;
\r
290 req_timestamp_p2 <= req_timestamp_p1;
\r
292 in_reset_p1 <= in_reset;
\r
294 stat_unlocked_jk <= ~ prom_wr_locked_jk;
\r
295 prom_wr_locked_jk_p1 <= prom_wr_locked_jk;
\r
297 if ( spi_addr_page_inc == 1 ) begin
\r
298 spi_addr <= spi_addr[31:0] + page_size[11:0];
\r
301 // Process new Control Nibble when written
\r
302 if ( lb_wr == 1 && lb_cs_prom_c == 1 ) begin
\r
303 prom_ctrl_reg <= lb_wr_d[31:0];
\r
304 cmd_nib <= lb_wr_d[3:0];
\r
306 strb_clear_fsm <= 1;
\r
307 spi_addr[27:0] <= { lb_wr_d[27:8], 8'd0 };
\r
308 spi_addr[31:28] <= 4'd0;
\r
310 if ( lb_wr_d[3:0] == `CMD_RD_TIMESTAMP ) begin
\r
311 req_timestamp <= 1;
\r
315 if ( lb_wr_d[3:0] == `CMD_RECONFIG_FPGA ) begin
\r
317 if ( fpga_boot_locked_jk == 0 ) begin
\r
319 reconfig_addr[27:0] <= { lb_wr_d[27:8], 8'd0 };
\r
320 reconfig_addr[31:28] <= 4'd0;
\r
324 // Handle the Lock. Lock on idle or bad unlock code
\r
325 if ( lb_wr_d[3:0] == 4'h0 ) begin
\r
326 prom_wr_locked_jk <= 1;
\r
330 if ( lb_wr_d[3:0] == `CMD_UNLOCK ) begin
\r
331 fpga_boot_locked_jk <= 1;
\r
332 prom_wr_locked_jk <= 1;
\r
335 if ( lb_wr_d[27:8] == 20'haa5aa ) begin
\r
336 prom_wr_locked_jk <= 0;
\r
338 if ( lb_wr_d[27:8] == 20'h55a55 ) begin
\r
339 fpga_boot_locked_jk <= 0;
\r
341 if ( lb_wr_d[27:8] == 20'h5aaa5 ) begin
\r
342 fpga_root_locked_jk <= 0;// Note: This is sticky
\r
344 if ( lb_wr_d[27:8] == 20'h11111 ) begin
\r
349 end // if ( lb_wr == 1 && lb_cs_prom_c == 1 ) begin
\r
351 // Load BRAM for MOSI
\r
352 if ( lb_wr == 1 && lb_cs_prom_d == 1 ) begin
\r
353 prom_data_reg <= lb_wr_d[31:0];
\r
355 end // if ( lb_wr == 1 && lb_cs_prom_d == 1 ) begin
\r
357 // Leaving Reset, if reconfig_2nd_slot==1, request a reconfig at slot_size
\r
358 if ( post_rst_cnt == 8'hF0 ) begin
\r
359 if ( reconfig_2nd_slot == 1 ) begin
\r
361 reconfig_addr[27:0] <= { slot_size[27:8], 8'd0 };
\r
362 reconfig_addr[31:28] <= 4'd0;
\r
364 post_rst_cnt <= post_rst_cnt + 1;
\r
365 end else if ( post_rst_cnt == 8'hFF ) begin
\r
367 post_rst_cnt <= post_rst_cnt + 1;
\r
370 if ( reset == 1 ) begin
\r
371 post_rst_cnt <= 8'd0;
\r
374 prom_ctrl_reg <= 32'd0;
\r
375 prom_data_reg <= 32'd0;
\r
376 prom_wr_locked_jk <= 1;
\r
377 fpga_boot_locked_jk <= 1; // Prevents Reconfig
\r
378 fpga_root_locked_jk <= 1; // Prevents Erasing Slot-0
\r
380 bist_req_jk <= 0; // Request Built In Self Test mode
\r
382 end // if ( reset == 1 ) begin
\r
385 end // proc_lb_regs
\r
388 //-----------------------------------------------------------------------------
\r
389 // LocalBus Readback
\r
390 //-----------------------------------------------------------------------------
\r
391 always @ ( posedge clk_lb ) begin : proc_lb_rd
\r
397 // Read Control Reg
\r
398 if ( lb_rd == 1 && lb_cs_prom_c == 1 ) begin
\r
399 lb_rd_d[27:8] <= spi_addr[27:8];
\r
400 lb_rd_d[7] <= err_ram_wr_jk;
\r
401 lb_rd_d[6] <= stat_polling_reqd_jk;
\r
402 lb_rd_d[5] <= stat_mosi_buf_free;
\r
403 lb_rd_d[4] <= stat_miso_buf_rdy_jk;
\r
404 lb_rd_d[3] <= stat_fsm_wr;
\r
405 lb_rd_d[2] <= stat_unlocked_jk;
\r
406 lb_rd_d[1] <= flag_wip_loc;
\r
407 lb_rd_d[0] <= stat_spi_busy;
\r
409 end // if ( lb_rd == 1 && lb_cs_prom_c == 1 ) begin
\r
411 // Read BRAM for MISO
\r
412 if ( lb_rd == 1 && lb_cs_prom_d == 1 ) begin
\r
413 lb_rd_d <= b_do_p1[31:0];
\r
416 end // if ( lb_wr == 1 && lb_cs_prom_d == 1 ) begin
\r
422 //-----------------------------------------------------------------------------
\r
423 // Convert Generic Command Nibbles into Device Specific Command Bytes and Leng
\r
424 //-----------------------------------------------------------------------------
\r
425 always @ ( posedge clk_lb ) begin : proc_cmd_lut
\r
427 // Latch as cmd_nib returns to 0x0 immediately
\r
429 // Translate User Nibble Commands to Device Specific Byte Commands
\r
430 case( cmd_nib[3:0] )
\r
431 `CMD_WR_PROM : cmd_byte <= 8'h02;// Micron Page_Program
\r
432 `CMD_RD_PROM : cmd_byte <= 8'h03;// Micron Read_Data
\r
433 `CMD_QUERY_ID : cmd_byte <= 8'h9F;// Micron Read_ID
\r
434 `CMD_REL_PD : cmd_byte <= 8'hAB;// Micron Release from PowerDown
\r
435 `CMD_ERASE_SECTOR : cmd_byte <= 8'hd8;// Micron Sector_Erase
\r
436 `CMD_ERASE_BULK : cmd_byte <= 8'hc7;// Micron Bulk_Erase
\r
437 default : cmd_byte <= 8'h00;// NULL
\r
438 endcase // case( cmd_nib[3:0] )
\r
440 // Is a WriteEnable required before User Command ?
\r
441 case( cmd_nib[3:0] )
\r
442 `CMD_WR_PROM : we_reqd_jk <= 1; // Page_Program
\r
443 `CMD_RD_PROM : we_reqd_jk <= 0; // Read_Data
\r
444 `CMD_QUERY_ID : we_reqd_jk <= 0; // Read_ID
\r
445 `CMD_REL_PD : we_reqd_jk <= 0; // Release PowerDown
\r
446 `CMD_ERASE_SECTOR : we_reqd_jk <= 1; // Sector_Erase
\r
447 `CMD_ERASE_BULK : we_reqd_jk <= 1; // Bulk_Erase
\r
448 default : we_reqd_jk <= 0; // NULL
\r
449 endcase // case( cmd_nib[3:0] )
\r
451 // Will there be an address state ?
\r
452 case( cmd_nib[3:0] )
\r
453 `CMD_WR_PROM : addr_reqd_jk <= 1; // Page_Program
\r
454 `CMD_RD_PROM : addr_reqd_jk <= 1; // Read_Data
\r
455 `CMD_QUERY_ID : addr_reqd_jk <= 0; // Read_ID
\r
456 `CMD_REL_PD : addr_reqd_jk <= 0; // Release PowerDown
\r
457 `CMD_ERASE_SECTOR : addr_reqd_jk <= 1; // Sector_Erase
\r
458 `CMD_ERASE_BULK : addr_reqd_jk <= 0; // Bulk_Erase
\r
459 default : addr_reqd_jk <= 0; // NULL
\r
460 endcase // case( cmd_nib[3:0] )
\r
462 // Will there be MISO data to capture ?
\r
463 case( cmd_nib[3:0] )
\r
464 `CMD_WR_PROM : rd_reqd_jk <= 0; // Page_Program
\r
465 `CMD_RD_PROM : rd_reqd_jk <= 1; // Read_Data
\r
466 `CMD_QUERY_ID : rd_reqd_jk <= 1; // Read_ID
\r
467 `CMD_REL_PD : rd_reqd_jk <= 0; // Release Power Down
\r
468 `CMD_ERASE_SECTOR : rd_reqd_jk <= 0; // Sector_Erase
\r
469 `CMD_ERASE_BULK : rd_reqd_jk <= 0; // Bulk_Erase
\r
470 default : rd_reqd_jk <= 0; // NULL
\r
471 endcase // case( cmd_nib[3:0] )
\r
474 // UserCommand : Decide How Many MOSI and MISO Bytes to Xfer
\r
475 if ( mosi_fsm[3:0] == 4'h2 ) begin
\r
476 if ( prom_is_32b == 0 ) begin
\r
477 case( cmd_nib[3:0] )
\r
478 `CMD_WR_PROM : xfer_tx_bytes <= 12'd4 + page_size[11:0];
\r
479 `CMD_RD_PROM : xfer_tx_bytes <= 12'd4;
\r
480 `CMD_QUERY_ID : xfer_tx_bytes <= 12'd1;
\r
481 `CMD_REL_PD : xfer_tx_bytes <= 12'd1;
\r
482 `CMD_ERASE_SECTOR : xfer_tx_bytes <= 12'd4;
\r
483 `CMD_ERASE_BULK : xfer_tx_bytes <= 12'd1;
\r
484 default : xfer_tx_bytes <= 12'd0;
\r
485 endcase // case( cmd_nib[3:0] )
\r
487 case( cmd_nib[3:0] )
\r
488 `CMD_WR_PROM : xfer_tx_bytes <= 12'd5 + page_size[11:0];
\r
489 `CMD_RD_PROM : xfer_tx_bytes <= 12'd5;
\r
490 `CMD_QUERY_ID : xfer_tx_bytes <= 12'd1;
\r
491 `CMD_REL_PD : xfer_tx_bytes <= 12'd1;
\r
492 `CMD_ERASE_SECTOR : xfer_tx_bytes <= 12'd5;
\r
493 `CMD_ERASE_BULK : xfer_tx_bytes <= 12'd1;
\r
494 default : xfer_tx_bytes <= 12'd0;
\r
495 endcase // case( cmd_nib[3:0] )
\r
497 case( cmd_nib[3:0] )
\r
498 `CMD_WR_PROM : xfer_rx_bytes <= 12'd0;
\r
499 `CMD_RD_PROM : xfer_rx_bytes <= page_size[11:0];
\r
500 `CMD_QUERY_ID : xfer_rx_bytes <= 12'd4;
\r
501 `CMD_REL_PD : xfer_rx_bytes <= 12'd0;
\r
502 `CMD_ERASE_SECTOR : xfer_rx_bytes <= 12'd0;
\r
503 `CMD_ERASE_BULK : xfer_rx_bytes <= 12'd0;
\r
504 default : xfer_rx_bytes <= 12'd0;
\r
505 endcase // case( cmd_nib[3:0] )
\r
508 // ReadStatus WIP Poll
\r
509 if ( mosi_fsm[3:0] == 4'h8 ) begin
\r
510 xfer_tx_bytes <= 12'd1;
\r
511 xfer_rx_bytes <= 12'd1;
\r
514 // Single Byte Commands
\r
515 if ( mosi_fsm[3:0] == 4'h1 ||
\r
516 mosi_fsm[3:0] == 4'h9 ||
\r
517 mosi_fsm[3:0] == 4'ha ||
\r
518 mosi_fsm[3:0] == 4'hb ||
\r
519 mosi_fsm[3:0] == 4'hc ) begin
\r
520 xfer_tx_bytes <= 12'd1;
\r
521 xfer_rx_bytes <= 12'd0;
\r
525 end // proc_cmd_lut
\r
528 //-----------------------------------------------------------------------------
\r
529 // Decide when SPI is idle or not
\r
530 //-----------------------------------------------------------------------------
\r
531 always @ ( posedge clk_lb ) begin : proc_spi_idle
\r
533 spi_is_idle_p1 <= spi_is_idle;
\r
534 if ( xfer_start == 1 ) begin
\r
535 spi_is_idle_jk <= 0;
\r
536 end else if ( spi_is_idle == 1 && spi_is_idle_p1 == 0 ) begin
\r
537 spi_is_idle_jk <= 1;
\r
540 mosi_fsm == 4'hF ) begin
\r
541 spi_is_idle_jk <= 1;
\r
543 if ( mosi_fsm[3:0] == 4'h0 ) begin
\r
544 stat_spi_busy <= 0;
\r
546 stat_spi_busy <= 1;
\r
549 end // proc_spi_idle
\r
552 //-----------------------------------------------------------------------------
\r
553 // When cmd_nib[3:0] goes non-zero start a SPI command and continue until
\r
554 // the xfer count is tx+rx count.
\r
558 // 0x1 : WriteEnable
\r
559 // 0x2 : Command Byte
\r
560 // 0x3 : spi_addr[31:24] ( Skipped for 3-Byte )
\r
561 // 0x4 : spi_addr[23:16]
\r
562 // 0x5 : spi_addr[15:8]
\r
563 // 0x6 : spi_addr[7:0]
\r
564 // 0x7 : Data Stage
\r
565 // 0x8 : ReadStatus
\r
566 // 0x9 : Write-Enable for Enter 4-Byte Addr
\r
567 // 0xA : Enter 4-Byte Addr
\r
568 // 0xB : Write-Enable for Exit 4-Byte Addr
\r
569 // 0xC : Exit 4-Byte Addr
\r
570 //-----------------------------------------------------------------------------
\r
571 always @ ( posedge clk_lb ) begin : proc_spi_len_fsm
\r
573 mosi_byte_en <= mosi_byte_pre;
\r
574 mosi_byte_req_p1 <= mosi_byte_req;
\r
576 xfer_start_p1 <= xfer_start;
\r
577 spi_addr_page_inc <= 0;
\r
579 flag_wip <= flag_wip_loc;
\r
581 done_enter_4byte <= 0;
\r
583 if ( cmd_nib == `CMD_WR_PROM ) begin
\r
587 // Either Kickoff new SPI Xfer, or advance the Xfer in progress
\r
588 // If 32b PROM, Enable 4-Byte Mode 1st
\r
589 //if ( mosi_fsm == 4'h0 && spi_is_idle_jk == 1 ) begin
\r
590 if ( ( mosi_fsm == 4'h0 && spi_is_idle_jk == 1 ) ||
\r
591 ( prom_is_32b == 1 && done_enter_4byte == 1 ) ) begin
\r
593 ( cmd_en_p1 == 1 || done_enter_4byte == 1 ) &&
\r
594 cmd_nib != `CMD_IDLE &&
\r
595 cmd_nib != `CMD_RD_TIMESTAMP &&
\r
596 cmd_nib != `CMD_WR_PROM &&
\r
597 cmd_nib != `CMD_UNLOCK &&
\r
598 cmd_nib != `CMD_RECONFIG_FPGA ) begin
\r
600 if ( we_reqd_jk == 1 ) begin
\r
601 mosi_fsm <= 4'h1;// Start with WriteEnable
\r
603 mosi_fsm <= 4'h2;// Read can skip the WriteEnable
\r
605 if ( prom_is_32b == 1 && done_enter_4byte == 0 ) begin
\r
607 mosi_fsm <= 4'h9;// Enter 4-Byte Mode and defer cmd_nib
\r
611 // If Write Command, start when RAM buffer is full, not when cmd is issued
\r
613 ( mosi_buf_rdy_jk == 1 || done_enter_4byte == 1 ) &&
\r
614 cmd_nib == `CMD_WR_PROM ) begin
\r
615 // Only actually write if no overrun error has happened
\r
616 if ( err_ram_wr_jk == 0 ) begin
\r
618 mosi_fsm <= 4'h1;// Start with WriteEnable
\r
620 if ( prom_is_32b == 1 && done_enter_4byte == 0 ) begin
\r
622 mosi_fsm <= 4'h9;// Enter 4-Byte Mode and defer cmd_nib
\r
627 if ( prom_is_32b == 1 ) begin
\r
628 if ( spi_is_idle == 1 && spi_is_idle_p1 == 0 ) begin
\r
629 // Finished Write Enable for Entering 4-Byte Addr
\r
630 if ( mosi_fsm == 4'h9 ) begin
\r
634 // Finished Entering 4-Byte Addr, time for actual command
\r
635 if ( mosi_fsm == 4'ha ) begin
\r
636 // mosi_fsm <= 4'h0;// Done
\r
637 done_enter_4byte <= 1;
\r
639 // Finished Write Enable for Exiting 4-Byte Addr
\r
640 if ( mosi_fsm == 4'hb ) begin
\r
644 // Finished Exit 4-Byte Addr
\r
645 if ( mosi_fsm == 4'hc ) begin
\r
648 end // if ( spi_is_idle == 1 && spi_is_idle_p1 == 0 ) begin
\r
649 end // if ( prom_is_32b == 1 ) begin
\r
651 if ( xfer_start == 1 ) begin
\r
652 spi_byte_cnt <= 12'd0;
\r
656 // Finished Write Enable for Erase or Programming
\r
657 if ( mosi_fsm == 4'h1 ) begin
\r
658 if ( spi_is_idle == 1 && spi_is_idle_p1 == 0 ) begin
\r
660 mosi_fsm <= 4'h2;// Start Command Byte
\r
664 // Full Command,Addr
\r
665 if ( mosi_fsm == 4'h2 ||
\r
666 mosi_fsm == 4'h3 ||
\r
667 mosi_fsm == 4'h4 ||
\r
668 mosi_fsm == 4'h5 ||
\r
671 if ( mosi_byte_pre == 1 ) begin
\r
672 if ( mosi_fsm == 4'h2 ) begin
\r
673 if ( prom_is_32b == 0 ) begin
\r
674 mosi_fsm <= 4'h4;// Skip 0x3 for 3-Byte Addressing
\r
676 mosi_fsm <= 4'h3;// 0x3 is for 4-Byte Addressing
\r
679 mosi_fsm <= mosi_fsm[3:0] + 1;
\r
681 if ( mosi_fsm == 4'h2 && addr_reqd_jk == 0 ) begin
\r
682 mosi_fsm <= 4'h7;// Skip Address Entirely
\r
688 if ( mosi_fsm == 4'h7 ) begin
\r
689 if ( spi_is_idle == 1 && spi_is_idle_p1 == 0 ) begin
\r
690 if ( we_reqd_jk == 1 ) begin
\r
692 mosi_fsm <= 4'h8;// Poll WIP
\r
693 spi_addr_page_inc <= 1;
\r
695 if ( prom_is_32b == 1 ) begin
\r
697 mosi_fsm <= 4'hb;// WriteEnable then Exit 4-Byte
\r
699 mosi_fsm <= 4'h0;// Done
\r
706 if ( mosi_fsm == 4'h8 ) begin
\r
708 if ( miso_byte_rdy == 1 ) begin
\r
709 if ( miso_byte_d[0] == 0 ) begin
\r
710 if ( prom_is_32b == 1 ) begin
\r
712 mosi_fsm <= 4'hb;// WriteEnable then Exit 4-Byte
\r
714 mosi_fsm <= 4'h0;// Done
\r
718 mosi_fsm <= 4'h8;// Poll WIP
\r
723 // If we just xferred a SPI TX or RX Byte count it
\r
724 if ( ( xfer_mosi_jk == 1 && mosi_byte_pre == 1 ) ||
\r
725 ( xfer_mosi_jk == 0 && miso_byte_rdy == 1 ) )begin
\r
726 spi_byte_cnt <= spi_byte_cnt + 1;
\r
729 // Once all MOSI Bytes are transferred, switch to MISO Bytes
\r
730 if ( mosi_byte_en == 1 && spi_byte_cnt == xfer_tx_bytes ) begin
\r
734 if ( reset == 1 ) begin
\r
739 end // proc_spi_len_fsm
\r
742 //-----------------------------------------------------------------------------
\r
743 // MOSI Mux. Mux in a MOSI Byte based on FSM stage ( Cmd,Addr,Data)
\r
745 // cmd_byte[7:0]->|\
\r
746 // spi_addr[31:0]--->| |---> mosi_byte_d[7:0]
\r
747 // ram_d[31:0]--->|/
\r
750 //-----------------------------------------------------------------------------
\r
751 always @ ( posedge clk_lb ) begin : proc_mosi_mux
\r
753 mosi_byte_pre <= 0;
\r
755 // When SPI wants a new MOSI byte, Mux Byte into SPI path based on FSM state
\r
756 if ( mosi_byte_req == 1 && mosi_byte_req_p1 == 0 ) begin
\r
757 mosi_byte_pre <= 1;
\r
758 if ( mosi_fsm[3:0] != 4'h7 ) begin
\r
759 case( mosi_fsm[3:0] )
\r
760 4'h1 : mosi_byte_d <= 8'h06;// Micron WriteEnable
\r
761 4'h2 : mosi_byte_d <= cmd_byte[7:0];
\r
762 4'h3 : mosi_byte_d <= spi_addr[31:24];
\r
763 4'h4 : mosi_byte_d <= spi_addr[23:16];
\r
764 4'h5 : mosi_byte_d <= spi_addr[15:8];
\r
765 4'h6 : mosi_byte_d <= spi_addr[7:0];
\r
766 4'h8 : mosi_byte_d <= 8'h05;// Micron ReadStatus
\r
767 4'h9 : mosi_byte_d <= 8'h06;// Micron WriteEnable
\r
768 4'ha : mosi_byte_d <= 8'hb7;// Micron Enter 4-Byte Addr Mode
\r
769 4'hb : mosi_byte_d <= 8'h06;// Micron WriteEnable
\r
770 4'hc : mosi_byte_d <= 8'he9;// Micron Exit 4-Byte Addr Mode
\r
771 default : mosi_byte_d <= 8'h00;// NULL
\r
772 endcase // case( mosi_fsm[3:0] )
\r
773 mosi_byte_cnt <= 2'd0;
\r
775 // Prevent WriteEnable from happening if things are locked by issuing
\r
776 // a Micron ReadStatus command instead of the WriteEnable command
\r
777 if ( mosi_fsm[3:0] == 4'h1 ) begin
\r
779 // If PROM is locked, issue ReadStatus instead of WriteEnable
\r
780 if ( prom_wr_locked_jk_p1 == 1 ) begin
\r
781 mosi_byte_d <= 8'h05;// Micron ReadStatus
\r
784 // Protect BootLoader design at Slot-0
\r
785 // If protect_1st_slot==1 and fpga_root_locked_jk==1 check to see if
\r
786 // spi_addr < slot_size and issue ReadStatus instead of WriteEnable
\r
787 if ( protect_1st_slot == 1 && fpga_root_locked_jk == 1 &&
\r
788 spi_addr[31:0] < slot_size[31:0] ) begin
\r
789 mosi_byte_d <= 8'h05;// Micron ReadStatus
\r
792 end // if ( mosi_fsm[3:0] == 4'h1 ) begin
\r
795 case( mosi_byte_cnt[1:0] )
\r
796 2'h0 : mosi_byte_d <= b_do_p1[31:24];
\r
797 2'h1 : mosi_byte_d <= b_do_p1[23:16];
\r
798 2'h2 : mosi_byte_d <= b_do_p1[15:8];
\r
799 default : mosi_byte_d <= b_do_p1[7:0];
\r
800 endcase // case( mosi_byte_cnt[1:0] )
\r
802 mosi_byte_cnt <= mosi_byte_cnt + 1;
\r
803 if ( mosi_byte_cnt[1:0] == 2'd3 ) begin
\r
804 mosi_rd_inc <= 1;// Finished last Byte, Get next DWORD from BRAM
\r
807 end // if ( mosi_byte_req == 1 && mosi_byte_req_p1 == 0 ) begin
\r
810 end // proc_mosi_mux
\r
813 //-----------------------------------------------------------------------------
\r
814 // MISO State Machine. Take MISO data and push to BRAM.
\r
815 // Depending on the command either 1 byte or series of 4 bytes
\r
816 //-----------------------------------------------------------------------------
\r
817 always @ ( posedge clk_lb ) begin : proc_miso_fsm
\r
820 if ( miso_byte_rdy == 1 ) begin
\r
821 // Count MISO Bytes for storing DWORDs to BRAM
\r
822 miso_byte_cnt <= miso_byte_cnt[1:0] + 1;
\r
823 if ( xfer_rx_bytes == 12'd1 ) begin
\r
825 a_di_miso <= { 24'd0, miso_byte_d[7:0] };
\r
827 a_di_miso <= { miso_byte_d[7:0], a_di_miso[31:8] };
\r
828 if ( miso_byte_cnt == 2'd3 ) begin
\r
829 a_we_miso <= 1; // Write DWORD after shifting 4 bytes
\r
832 end // if ( miso_byte_rdy == 1 ) begin
\r
834 // At end of Read Command, assert MISO Buffer Ready JK
\r
835 if ( cmd_nib == `CMD_RD_PROM ) begin
\r
836 if ( spi_is_idle == 1 && spi_is_idle_p1 == 0 ) begin
\r
837 stat_miso_buf_rdy_jk <= 1;
\r
841 if ( strb_clear_fsm == 1 ) begin
\r
842 miso_byte_cnt <= 2'd0;
\r
843 stat_miso_buf_rdy_jk <= 0;
\r
846 end // proc_miso_fsm
\r
849 //-----------------------------------------------------------------------------
\r
850 // BRAM Write Access.
\r
851 // This is a ping-pong buffer so that Backdoor or PCIe can fill 256 bytes
\r
852 // while previous 256 bytes are transferred over SPI.
\r
853 // A FIFO would work too, but this is more portable using inferred BRAM.
\r
854 // As soon as there are 256 Bytes, a SPI xfer begins.
\r
855 // The next 256 bytes can be loaded while the 1st are shipped out.
\r
856 // If a new buffer is written while previous buffer is being read, a flag is
\r
857 // set to tell software it should poll for free buffer status after each 256
\r
858 // byte burst. Another flag indicates if the buffer was overrun.
\r
861 // (BD) |- 6ms -|------ 32 ms -----|
\r
862 // LB -< Ping >-----------------< Pong >----------------------
\r
863 // SPI ----------< Ping >---------------< Pong >---------------
\r
864 // WIP |--3ms--|< Ping >-------------< Pong >--------
\r
868 // LB -<Ping>--<Pong>--<Ping>---------------
\r
869 // SPI ----------< Ping >< Pong >---------------
\r
870 // WIP < Ping >-< Pong >--------
\r
871 // PollReqd _______/
\r
872 // ErrRAMwr _____________/
\r
873 //-----------------------------------------------------------------------------
\r
874 always @ ( posedge clk_lb ) begin : proc_ram_wr_fsm
\r
876 stat_mosi_buf_free <= buffer_free_jk;
\r
879 if ( strb_wr_data == 1 && mosi_buf_rdy_jk == 0 ) begin
\r
881 a_di <= prom_data_reg[31:0];
\r
882 a_addr[depth_bits-2:0] <= a_addr[depth_bits-2:0] + 1;
\r
883 a_addr[depth_bits-1] <= wr_ping_pong;
\r
884 if ( a_addr[depth_bits-2:0] == { all_ones[depth_bits-2:1], 1'b0 } ) begin
\r
885 buffer_free_jk <= 1; // Whole new Buffer available
\r
886 mosi_buf_rdy_jk <= 1; // Need to dump this buffer to SPI
\r
887 wr_ping_pong <= ~ wr_ping_pong;
\r
888 rd_ping_pong_pend <= wr_ping_pong;
\r
890 buffer_free_jk <= 0; // Filling buffer, can't start a new 256 burst
\r
892 end // if ( strb_wr_data == 1 ) begin
\r
894 // Check to see if writing to buffer while one is being read.
\r
895 if ( strb_wr_data == 1 && rd_ping_pong_actv[1] == 1 ) begin
\r
896 if ( wr_ping_pong == rd_ping_pong_actv[0] ) begin
\r
897 err_ram_wr_jk <= 1;
\r
899 stat_polling_reqd_jk <= 1;
\r
903 // Determine if this buffer has been claimed by SPI controller
\r
904 if ( xfer_start == 1 && mosi_fsm == 4'h1 ) begin
\r
905 if ( mosi_buf_rdy_jk == 1 ) begin
\r
906 rd_ping_pong_actv <= { 1'b1, rd_ping_pong_pend };
\r
907 mosi_buf_rdy_jk <= 0;
\r
909 end else if ( mosi_fsm == 4'h0 ) begin
\r
910 rd_ping_pong_actv[1] <= 0;
\r
913 if ( reset == 1 || strb_clear_fsm == 1 ) begin
\r
915 rd_ping_pong_pend <= 0;
\r
916 rd_ping_pong_actv <= 2'b01;
\r
917 buffer_free_jk <= 1;
\r
918 mosi_buf_rdy_jk <= 0;
\r
919 a_addr[depth_bits-2:0] <= all_ones[depth_bits-2:0];// Roll from Ones to Zero
\r
920 a_addr[depth_bits-1] <= 0;
\r
921 err_ram_wr_jk <= 0;
\r
922 stat_polling_reqd_jk <= 0;
\r
925 // Normally BRAM writes are from LB, but If MISO has a byte, write it to BRAM
\r
926 if ( a_we_miso == 1 && rd_reqd_jk == 1 ) begin
\r
928 a_di <= a_di_miso[31:0];
\r
929 a_addr[depth_bits-2:0] <= a_addr[depth_bits-2:0] + 1;
\r
930 a_addr[depth_bits-1] <= 1;
\r
933 // Normally BRAM writes are from LB, but load 32bit UNIX timetstamp on reqst
\r
934 if ( req_timestamp_p1 == 1 ) begin
\r
936 a_di <= time_stamp_d[31:0];
\r
937 a_addr[depth_bits-2:0] <= a_addr[depth_bits-2:0] + 1;
\r
938 a_addr[depth_bits-1] <= 1;
\r
940 // After the TimeStamp, provide the slot size
\r
941 if ( req_timestamp_p2 == 1 ) begin
\r
943 a_di <= slot_size[31:0];
\r
944 a_addr[depth_bits-2:0] <= a_addr[depth_bits-2:0] + 1;
\r
945 a_addr[depth_bits-1] <= 1;
\r
949 end // proc_ram_wr_fsm
\r
952 //-----------------------------------------------------------------------------
\r
953 // BRAM Read Port FSM : b_addr starts at 0x0 at beginning of a SPI command and
\r
954 // increments after a dword has been read.
\r
955 //-----------------------------------------------------------------------------
\r
956 always @ ( posedge clk_lb ) begin : proc_ram_rd_fsm
\r
958 b_addr[depth_bits-2:0] <= b_addr[depth_bits-2:0];
\r
959 b_addr[depth_bits-1] <= rd_ping_pong_actv[0];
\r
960 if ( strb_clear_fsm == 1 ) begin
\r
961 b_addr[depth_bits-2:0] <= all_zeros[depth_bits-2:0];
\r
962 b_addr[depth_bits-1] <= rd_ping_pong_actv[0];
\r
964 if ( miso_rd_inc == 1 || mosi_rd_inc == 1 ) begin
\r
965 b_addr[depth_bits-2:0] <= b_addr[depth_bits-2:0] + 1;
\r
966 b_addr[depth_bits-1] <= rd_ping_pong_actv[0];
\r
969 end // proc_ram_rd_fsm
\r
972 //-----------------------------------------------------------------------------
\r
973 // Dual Port RAM - Infer RAM here to make easy to change depth on the fly
\r
974 // Really small RAM is used for simulations
\r
975 //-----------------------------------------------------------------------------
\r
976 always @( posedge clk_lb )
\r
979 a_addr_p1 <= a_addr[depth_bits-1:0];
\r
980 a_di_p1 <= a_di[31:0];
\r
981 if ( a_we_p1 ) begin
\r
982 ram_array[a_addr_p1] <= a_di_p1[31:0];
\r
986 always @( posedge clk_lb )
\r
988 a_do <= ram_array[a_addr];
\r
991 always @( posedge clk_lb )
\r
993 b_do <= ram_array[b_addr];
\r
994 b_do_p1 <= b_do[31:0];
\r
998 //-----------------------------------------------------------------------------
\r
999 // 32bit UNIX TimeStamp of when the design was synthesized
\r
1000 //-----------------------------------------------------------------------------
\r
1001 time_stamp u_time_stamp
\r
1003 .time_dout ( time_stamp_d )
\r
1007 //-----------------------------------------------------------------------------
\r
1008 // Convert MOSI Bytes into SPI Bits and SPI Bits into MISO Bytes
\r
1009 //-----------------------------------------------------------------------------
\r
1010 spi_byte2bit u_spi_byte2bit
\r
1014 .ck_divisor ( ck_divisor[7:0] ),
\r
1015 .spi_ctrl ( spi_ctrl[3:0] ),
\r
1017 .spi_sck ( spi_sck ),
\r
1018 .spi_cs_l ( spi_cs_l ),
\r
1019 .spi_mosi ( spi_mosi ),
\r
1020 .spi_miso ( spi_miso ),
\r
1021 .spi_is_idle ( spi_is_idle ),
\r
1023 .xfer_start ( xfer_start_p1 ),
\r
1024 .xfer_stall ( 1'b0 ),
\r
1025 .xfer_tx_bytes ( xfer_tx_bytes[11:0] ),
\r
1026 .xfer_rx_bytes ( xfer_rx_bytes[11:0] ),
\r
1028 .mosi_byte_d ( mosi_byte_d[7:0] ),
\r
1029 .mosi_byte_en ( mosi_byte_en ),
\r
1030 .mosi_byte_req ( mosi_byte_req ),
\r
1033 .miso_byte_d ( miso_byte_d[7:0] ),
\r
1034 .miso_byte_rdy ( miso_byte_rdy )
\r
1038 endmodule // spi_prom.v
\r