top.v 模块为整个工程的最顶层模块, 这个模块包括pll (负责系统时钟, spi phy 时钟处理);reset 复位逻辑; 开发版上的按键滤波;spi_IF.v (控制spi );uart 串口通讯模块; fpga 心跳led逻辑。目前我们使用的是 FII-PRX100T-DDR 开发版,当然,这个工程可以非常方便的移植到其他的fpga 开发班上。
参考文章:
SPI 通讯协议 及 SPI 相关工程 详解
FII_PRX100T_DDR_V1.2 硬件原理图
FII-PRX100-D(ARTIX 100T,XC7A100T)硬件参考指南
TOP.v 结构图
参考代码
|
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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: Fraser Innovation Inc // Engineer: WilliamG // // Create Date: 2020/06/22 09:12:51 // Design Name: // Module Name: top.v // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module TOP# ( SIM_MODE = "FALSE" ) ( input IN_CLK_50M, //外部时钟 input [7:1] PB, //外部按键 消抖后选择需要的使用 input RXD, //串口接收 output TXD, //串口发送 output led, //fpga心跳 output FLASH_CLK, //SPI时钟 output FLASH_CS, //SPI片选 output FLASH_MOSI, //SPI主器件输出从器件输入 input FLASH_MISO, //SPI主器件输入从器件输出 output FLASH_WP_N //SPI主器件输入从器件输出 ); //======================================================================================= //时钟模块 复位电路 wire clk_50m; wire clk_25m; wire locked; pll SYS_PLL ( .clk_in1 (IN_CLK_50M), .locked (locked), .clk_out1 (clk_50m), .clk_out2 (clk_25m), .reset (1'b0) ); wire pb_rst; //按键复位脉冲 高电平有效 wire rst_r_n; //复位寄存 assign rst_r_n = locked & (~pb_rst); //低有效 locked 或按键触发 //======================================================================================= reg [31:0] rst_cnt = 0; //复位延时计数 reg sys_rst_n = 0; //系统时钟 locked 系统复位 always @ (posedge clk_50m) if(!rst_r_n) sys_rst_n <= 0; else begin if(rst_cnt >= 500) //delay 500ms sys_rst_n <= 1; else begin sys_rst_n <= 0; //计数不到 保持复位状态 rst_cnt <= rst_cnt +1; end end //======================================================================================= //按键消抖模块 wire [7:1] pb_flag; //按键脉冲 key_filter # (.SIM_MODE(SIM_MODE)) key_filter_inst ( .in_clk (clk_50m), //系统时钟50MHz .rst_n (sys_rst_n), //复位信号,低电平有效 .pb (PB), //开发板输入按键信号 .pb_flag(pb_flag) //消抖后脉冲信号 ); //======================================================================================= //按键分配 wire pb_rd_id, pb_wr_addr, pb_wr, pb_rd, pb_erase; assign pb_erase = pb_flag[1]; //PB1作为擦除扇区指令 assign pb_wr = pb_flag[2]; //PB2作为写数据 256Byte assign pb_rd = pb_flag[3]; //PB3作为读数据 assign pb_rd_id = pb_flag[5]; //PB5作为读id //assign pb_wr_addr = pb_flag[5]; //PB5作为写地址 assign pb_rst = pb_flag[7]; //PB7作为复位按键 //======================================================================================= //flash模块 wire phy_clk = clk_25m; wire sys_clk = clk_50m; wire uart_fifo_rd; wire [7:0] uart_fifo_dout; wire uart_fifo_empty; wire uart_done; wire spi_rd; wire [7:0] spi_dout; wire spi_empty; (* mark_debug = "true" *)wire spi_cmd_done; spi_IF #( .SIM_MODE (SIM_MODE) ) spi_IF_inst ( .sys_clk (sys_clk), //系统时钟 .pb_rd_id (pb_rd_id), //PB7作为读id .pb_wr (pb_wr), //PB4作为写数据 256Byte .pb_rd (pb_rd), //PB6作为读数据 .pb_erase (pb_erase), //PB2作为擦除扇区指令 .spi_cmd_done (spi_cmd_done), .spi_rd (spi_rd), .spi_dout (spi_dout), .spi_empty (spi_empty), .uart_fifo_rd (uart_fifo_rd), .uart_fifo_dout (uart_fifo_dout), .uart_fifo_empty (uart_fifo_empty), .uart_done (uart_done), .phy_clk (phy_clk), //spi时钟 .FLASH_CS (FLASH_CS), //SPI片选 .FLASH_CLK (FLASH_CLK), //SPI时钟 .FLASH_MOSI (FLASH_MOSI), //SPI主器件输出从器件输入 .FLASH_MISO (FLASH_MISO), //SPI主器件输入从器件输出 .rst_n (sys_rst_n) ); //======================================================================================= pc_uart #( .MAIN_CLK (50) ) pc_uart_inst ( .clk (sys_clk), .txd (TXD), .rxd (RXD), .spi_rd (spi_rd), .spi_dout (spi_dout), .spi_empty (spi_empty), .spi_done (spi_cmd_done), .uart_fifo_rd (uart_fifo_rd), .uart_fifo_dout (uart_fifo_dout), .uart_fifo_empty (uart_fifo_empty), .rxd_done (uart_done), .reset (!sys_rst_n) ); //======================================================================================= reg [23:0] led_cnt = 0; always @ (posedge sys_clk) led_cnt <= led_cnt + 1; assign led = led_cnt[23]; assign FLASH_WP_N = 1'b0; //======================================================================================= endmodule |
顶层文件主要是将各个模块进行连接,同时提供相关的时钟;reset 复位信号等;对外部pin (按键)进行滤波,提取系统时钟单沿信号;以及提供一个fpga 的心跳led。
相关的仿真工程
|
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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: Fraser Innovation Inc // Engineer: WilliamG // // Create Date: 03/02/2021 03:51:26 PM // Design Name: // Module Name: tb_sim // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module tb_sim( ); reg clk = 0; always clk = #10 ~clk; wire led; wire rx = 1; wire tx; integer i; reg [7:1] PB = 7'h7f; /* assign pb_erase = PB[1]; //PB1作为擦除扇区指令 assign pb_wr = PB[2]; //PB2作为写数据 256Byte assign pb_rd = PB[3]; //PB3作为读数据 assign pb_rd_id = PB[5]; //PB5作为读id assign pb_rst = PB[1]; //PB1作为复位按键 */ //`define SIM_TEST `ifdef SIM_TEST wire wp_n = 1'b0; reg sck = 0; reg cs = 1; reg mosi = 1; wire miso; initial begin sck = 0; cs = 1; mosi = 1; #20000; @(posedge clk); cs = 0; #100; // 发送05 命令 mosi = 0; #20; sck = 1; #20; sck = 0; // 0 mosi = 0; #20; sck = 1; #20; sck = 0; // 1 mosi = 0; #20; sck = 1; #20; sck = 0; // 2 mosi = 0; #20; sck = 1; #20; sck = 0; // 3 mosi = 0; #20; sck = 1; #20; sck = 0; // 4 mosi = 1; #20; sck = 1; #20; sck = 0; // 5 mosi = 0; #20; sck = 1; #20; sck = 0; // 6 mosi = 1; #20; sck = 1; #20; sck = 0; // 7 #100; // 发送 8 clock , 等待miso 返回数据 mosi = 1; #20; sck = 1; #20; sck = 0; // 0 mosi = 1; #20; sck = 1; #20; sck = 0; // 1 mosi = 1; #20; sck = 1; #20; sck = 0; // 2 mosi = 1; #20; sck = 1; #20; sck = 0; // 3 mosi = 1; #20; sck = 1; #20; sck = 0; // 4 mosi = 1; #20; sck = 1; #20; sck = 0; // 5 mosi = 1; #20; sck = 1; #20; sck = 0; // 6 mosi = 1; #20; sck = 1; #20; sck = 0; // 7 #100; cs = 1; #800; // 发送02 写命令 cs = 0; #100; mosi = 0; #20; sck = 1; #20; sck = 0; // 0 mosi = 0; #20; sck = 1; #20; sck = 0; // 1 mosi = 0; #20; sck = 1; #20; sck = 0; // 2 mosi = 0; #20; sck = 1; #20; sck = 0; // 3 mosi = 0; #20; sck = 1; #20; sck = 0; // 4 mosi = 0; #20; sck = 1; #20; sck = 0; // 5 mosi = 1; #20; sck = 1; #20; sck = 0; // 6 mosi = 0; #20; sck = 1; #20; sck = 0; // 7 #100; // 发送 add[23:16] == 0x10 mosi = 0; #20; sck = 1; #20; sck = 0; // 0 mosi = 0; #20; sck = 1; #20; sck = 0; // 1 mosi = 0; #20; sck = 1; #20; sck = 0; // 2 mosi = 1; #20; sck = 1; #20; sck = 0; // 3 mosi = 0; #20; sck = 1; #20; sck = 0; // 4 mosi = 0; #20; sck = 1; #20; sck = 0; // 5 mosi = 0; #20; sck = 1; #20; sck = 0; // 6 mosi = 0; #20; sck = 1; #20; sck = 0; // 7 #100; // 发送 add[15:08] == 0x00 mosi = 0; #20; sck = 1; #20; sck = 0; // 0 mosi = 0; #20; sck = 1; #20; sck = 0; // 1 mosi = 0; #20; sck = 1; #20; sck = 0; // 2 mosi = 0; #20; sck = 1; #20; sck = 0; // 3 mosi = 0; #20; sck = 1; #20; sck = 0; // 4 mosi = 0; #20; sck = 1; #20; sck = 0; // 5 mosi = 0; #20; sck = 1; #20; sck = 0; // 6 mosi = 0; #20; sck = 1; #20; sck = 0; // 7 #100; // 发送 add[07:00] == 0x00 mosi = 0; #20; sck = 1; #20; sck = 0; // 0 mosi = 0; #20; sck = 1; #20; sck = 0; // 1 mosi = 0; #20; sck = 1; #20; sck = 0; // 2 mosi = 0; #20; sck = 1; #20; sck = 0; // 3 mosi = 0; #20; sck = 1; #20; sck = 0; // 4 mosi = 0; #20; sck = 1; #20; sck = 0; // 5 mosi = 0; #20; sck = 1; #20; sck = 0; // 6 mosi = 0; #20; sck = 1; #20; sck = 0; // 7 #100; // 发送 2 bytes 数据 0xb7 for (i = 0; i < 2; i = i + 1) begin mosi = 1; #20; sck = 1; #20; sck = 0; // 0 mosi = 0; #20; sck = 1; #20; sck = 0; // 1 mosi = 1; #20; sck = 1; #20; sck = 0; // 2 mosi = 1; #20; sck = 1; #20; sck = 0; // 3 mosi = 0; #20; sck = 1; #20; sck = 0; // 4 mosi = 1; #20; sck = 1; #20; sck = 0; // 5 mosi = 1; #20; sck = 1; #20; sck = 0; // 6 mosi = 1; #20; sck = 1; #20; sck = 0; // 7 #100; end cs = 1; #800; cs = 0; #100; // 发送05 命令 mosi = 0; #20; sck = 1; #20; sck = 0; // 0 mosi = 0; #20; sck = 1; #20; sck = 0; // 1 mosi = 0; #20; sck = 1; #20; sck = 0; // 2 mosi = 0; #20; sck = 1; #20; sck = 0; // 3 mosi = 0; #20; sck = 1; #20; sck = 0; // 4 mosi = 1; #20; sck = 1; #20; sck = 0; // 5 mosi = 0; #20; sck = 1; #20; sck = 0; // 6 mosi = 1; #20; sck = 1; #20; sck = 0; // 7 #100; // 发送 8 clock , 等待miso 返回数据 mosi = 1; #20; sck = 1; #20; sck = 0; // 0 mosi = 1; #20; sck = 1; #20; sck = 0; // 1 mosi = 1; #20; sck = 1; #20; sck = 0; // 2 mosi = 1; #20; sck = 1; #20; sck = 0; // 3 mosi = 1; #20; sck = 1; #20; sck = 0; // 4 mosi = 1; #20; sck = 1; #20; sck = 0; // 5 mosi = 1; #20; sck = 1; #20; sck = 0; // 6 mosi = 1; #20; sck = 1; #20; sck = 0; // 7 #100; cs = 1; #800; #200000; $stop; end `else wire sck; wire cs; wire mosi; wire miso; wire wp_n; initial begin PB = 7'h7f; #20000; /* // erase sector PB[1] = 1'b0; #2000; PB[1] = 1'b1; while (spi_top_inst.spi_cmd_done == 0) @(posedge clk); */ // read id PB[5] = 1'b0; #2000; PB[5] = 1'b1; while (spi_top_inst.spi_cmd_done == 0) @(posedge clk); // write 128 data PB[2] = 1'b0; #2000; PB[2] = 1'b1; while (spi_top_inst.spi_cmd_done == 0) @(posedge clk); // read 128 data PB[3] = 1'b0; #2000; PB[3] = 1'b1; while (spi_top_inst.spi_cmd_done == 0) @(posedge clk); #200000; $stop; end TOP #( .SIM_MODE("TRUE")) spi_top_inst ( .IN_CLK_50M (clk), //50M .PB (PB), //SPI的4根线 .FLASH_CLK (sck), .FLASH_CS (cs), .FLASH_MOSI (mosi), .FLASH_MISO (miso), .FLASH_WP_N (wp_n), //串口 .RXD (rx), .TXD (tx), .led (led) ); `endif SST25WF080B SST25WF080B_inst ( .SCK (sck), .CEB (cs), .SI (mosi), .SO (miso), .WPB (wp_n), .HOLDB (1) ); //================================================================== endmodule |
当定义 define SIM_TEST , 仿真工程模拟 mosi 发送 时钟, 数据,片选等,这时仿真并不需要top.v ,但可以帮助大家理解spi 通讯。
当没有定义 define SIM_TEST , 仿真工程模拟按键发送 读flash ID , 写数据,读数据操作。没有模拟扇区擦除操作, 因为需要很长时间等待05 寄存器 ok。但在开发部上运行,擦除按键是没有问题的。

