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)
`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
仿真文件:
`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 上了。
修改状态机:
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