STARTUPE2 在spi flash 上的使用
7 series fpga CCLK 使用
当我们使用spartan 7 fpga 时, 需要fpga 逻辑对spi flash 进行读写操作, 就会使用fpga 指定的pin。包括bank0 当中的CCLK, BANK14 中的 D[00], DIN/D[01], FCS_B, D[02],D[03] 等。
在之前的fpga 中(spartan 3, spartan 6), CCLK 启动时,有FPGA自主控制,产生时钟(不论fpga 是否被编程过)当从flash 中读取需要配置的数据后, CCLK pin 可以当中普通的gpio ,有用户编程使用。在7 series fpga 中, 由于CCLK 是特定的pin, 不能被用户当作普通的gpio 使用, 也不能设置约束文件。(例如xdc)
如图所示:

或者

但在很多开发板中, 我们即需要使用CCLK 用来启动fpga (从spi flash 中读取数据来配置fpga )又希望在fpga 启动后, 我们依然可以操作CCLK pin ,为用户自己定义的spi controller来对spi flash 进行读写。

在我们的prx100t开发板中,不但连接了7 series bank 0 当中的CCLK,同时又有一个gpio pin 和spi flash 相连。

这样,就可以避免使用STARTUPE2原语配置spi flash。原因, 启动时,fpga 自主从CCLK 输出时钟, 驱动spi flash。 在启动后, 如果用户需要使用spi flash ,可以驱动gpio_pin 来实现。(这时,CCLK 处于高组态)。
如果使用的其他的开发板,或者硬件设计没有预留gpio 连接spi flash,那么,我们必须是由STARTUPE2 原语控制CCLK。在阅读UG470 文档 (7 Series FPGA Configuration User Guide) 中有关 CCLK的使用时,发现CCLK 是Dedicated pin , 而不是Multi-function pin。 也就是说CCLK 不能被fpga 内部逻辑直接使用,也不能在xdc 文件中对CCLK 进行约束。需要使用STARTUPE2 这个原语来实现。即fpga 内部逻辑可以控制STARTUPE2 原语, 而STARTUPE2 原语可以驱动CCLK。

STARTUPE2 原语使用
在spartan 6 fpga 中CCLK 是一个multi-function pin 。 可以被fpga 内部逻辑直接驱动。在7 series fpga 中CCLK pin 是Dedicated pin,不能被fpga 直接驱动,所以需要使用STARTUPE2原语间接驱动CCLK。
通过分析STARTUPE2的原语使用,我们发现在系统启动后需要3个USRCCLKO 的时钟后,才能正常的使用CCLK

为此, 我们设计了一个简单的仿真程序,测试STARTUPE2 的相关功能。
STARTUPE2 仿真工程:
首先, 我们模拟一个基本的spi flash 的操作过程 , fpga 正常驱动spi_cs, spi_clk, spi_mosi 等。状态机 0 直接跳转到状态机6。 spi_clk 用来驱动STARTUPE2 primitive 中的 USRCCLKO pin。
工程代码: (vivado)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
`timescale 1ns / 1ps module TOP# ( parameter SIM_CCLK_FREQ = 0.0 ) ( input OSC_20M, // output FLASH_CLK, output FLASH_CS, output FLASH_MOSI, input FLASH_MISO, input reset ); //======================================================================================= wire FLASH_CLK; STARTUPE2 #( .PROG_USR("FALSE"), // Activate program event security feature. Requires encrypted bitstreams. .SIM_CCLK_FREQ(SIM_CCLK_FREQ) // Set the Configuration Clock Frequency(ns) for simulation. ) STARTUPE2_inst ( .CFGCLK(), // 1-bit output: Configuration main clock output .CFGMCLK(), // 1-bit output: Configuration internal oscillator clock output .EOS(), // 1-bit output: Active high output signal indicating the End Of Startup. .PREQ(), // 1-bit output: PROGRAM request to fabric output .CLK(0), // 1-bit input: User start-up clock input .GSR(0), // 1-bit input: Global Set/Reset input (GSR cannot be used for the port name) .GTS(0), // 1-bit input: Global 3-state input (GTS cannot be used for the port name) .KEYCLEARB(1), // 1-bit input: Clear AES Decrypter Key input from Battery-Backed RAM (BBRAM) .PACK(1), // 1-bit input: PROGRAM acknowledge input .USRCCLKO(FLASH_CLK), // 1-bit input: User CCLK input // For Zynq-7000 devices, this input must be tied to GND .USRCCLKTS(0), // 1-bit input: User CCLK 3-state enable input // For Zynq-7000 devices, this input must be tied to VCC .USRDONEO(1), // 1-bit input: User DONE pin output control .USRDONETS(1) // 1-bit input: User DONE 3-state enable output ); //======================================================================================= reg [7:0] spi_cmd = 8'h39; // test data reg spi_clk = 0; reg spi_mosi = 0; reg spi_cs = 1; wire spi_miso; reg [4:0] spi_cnt = 0; reg [3:0] spi_st = 0; always @ (posedge OSC_20M or posedge reset) if(reset) begin spi_cnt <= 0; spi_cs <= 1; spi_st <= 0; end else case (spi_st) 0: begin spi_cs <= 1; spi_clk <= 0; spi_st <= 1; // add 3 usrcclko for startupe2 primitive // spi_st <= 6; // normal spi operation including drive spi_clk, spi_cs, spi_mosi end 1: // add 3 usrcclko for startupe2 primitive begin spi_clk <= 1; spi_st <= 2; end 2: begin spi_clk <= 0; spi_st <= 3; end 3: begin spi_clk <= 1; spi_st <= 4; end 4: begin spi_clk <= 0; spi_st <= 5; end 5: begin spi_clk <= 1; spi_st <= 6; end 6: // siumlate a spi flash operation , drive spi_clk, spi_cs, spi_mosi begin spi_cs <= 1; spi_clk <= 0; spi_cnt <= spi_cnt + 1; if(&spi_cnt) begin spi_cnt <= 7; spi_st <= 7; end end 7: begin spi_cs <= 0; spi_mosi <= spi_cmd[spi_cnt]; spi_st <= 8; end 8: begin spi_clk <= 1; if(spi_cnt == 0) spi_st <= 10; else begin spi_cnt <= spi_cnt - 1; spi_st <= 9; end end 9: begin spi_clk <= 0; spi_mosi <= spi_cmd[spi_cnt]; spi_st <= 8; end 10: begin spi_clk <= 0; spi_st <= 11; end 11: begin spi_cs <= 1; spi_cnt <= 0; spi_st <= 6; end default: spi_st <= 0; endcase //======================================================================================= //======================================================================================= assign FLASH_CLK = spi_clk; assign FLASH_CS = spi_cs; assign FLASH_MOSI = spi_mosi; assign spi_miso = FLASH_MISO; endmodule |
仿真文件:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
`timescale 1ns / 1ps module tb_sim( ); reg clk = 0; always clk = #25 ~clk; reg reset = 1; initial begin reset = 1; # 100; reset = 0; #10000; $stop; end wire FLASH_CS; wire FLASH_MOSI; reg FLASH_MISO = 0; TOP # (.SIM_CCLK_FREQ(10.0)) top_inst ( .OSC_20M (clk), .FLASH_CS (FLASH_CS), .FLASH_MOSI (FLASH_MOSI), .FLASH_MISO (FLASH_MISO), .reset (reset) ); //================================================================== endmodule |
仿真波形:

在第一次输出spi_clk 时, 我们发现在真实的CCLK 上缺少2 个时钟。
解决方案:
根据UG470 文档的介绍, 只要spi 使用前, 在USRCCLKO 上增加3个clk 后, USRCCLKO 就可以正常的输出到CCLK 上了。
修改状态机:
|
1 2 3 4 5 6 7 |
0: begin spi_cs <= 1; spi_clk <= 0; spi_st <= 1; // add 3 usrcclko for startupe2 primitive // spi_st <= 6; // normal spi operation including drive spi_clk, spi_cs, spi_mosi end |
仿真波形:

结论:
如果使用STARTUPE2 来驱动CCLK pin , 就要在spi controller 使用之前, 增加3个USRCCLKO 时钟,之后, STARTUPE2 primitive 就可以正常使用了。
参考文档:
https://www.xilinx.com/content/dam/xilinx/support/documentation/user_guides/ug470_7Series_Config.pdf