spi_IF.v 模块为上层的系统模块, 这个模块 主要是接收系统的命令, 同时向下面的模块(spi_cmd.v) 发送spi 的指令。 这个模块的主要时钟是sys_clk ,通过fifo 和 异步握手方式和spi_cmd.v 模块通讯。 通过接收上层模块的指令,得到相应的操作命令。
参考文章:
SPI 通讯协议 及 SPI 相关工程 详解
spi_IF.v 结构图
spi_IF.v 模块主要是从上层模块 接收 几个按键的信息:在当前的例子中会接收:
1)spi flash DEV-ID ,
2)向 spi flash 中的 0x80_0000 地址 写入 128 bytes 的数据 , 数据为 0x00 – 0x7f
3)向 spi flash 中的 0x80_0000 地址 读取 128 bytes 的数据。
4)擦除 地址为 0x80_0000 的扇区
除此之外, spi_IF.v 的状态机 还可以接收 上层模块传递下来的 uart 信息,在uart 模块中, 可以产生各种不同的spi 命令(不但可以控制spi flash, 也可以用来控制其他的spi 设备)。
代码介绍:
|
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 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: Fraser Innovation Inc // Engineer: WilliamG // // Create Date: 04/21/2021 02:23:43 PM // Design Name: // Module Name: spi_IF.v // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module spi_IF # ( SIM_MODE = "FALSE" ) ( input sys_clk, // 系统时钟 input pb_rd_id, // PB7作为读id input pb_wr, // PB4作为写数据 128Byte , 地址为80_0000, 数据 00 -- 127 input pb_rd, // PB6作为读数据 128Byte , 地址为80_0000, 数据 00 -- 127 input pb_erase, // PB2作为擦除扇区指令, 地址为80_0000, output spi_cmd_done, // transaction ok , 当前整个操作结束标志 input spi_rd, // 读信号,读fifo中 由 spi 接收到的数据 output [7:0] spi_dout, // 读fifo中 由 spi 接收到的数据 output spi_empty, // fifo empty 标志 output reg uart_fifo_rd = 0, // 读信号,读uart 接收fifo 中的数据 input [7:0] uart_fifo_dout, // 读uart 接收fifo 中的数据 input uart_fifo_empty, // uart fifo empty 标志 input uart_done, // uart 接收一个操作结束, 所有命令,数据已经在uart fifo 中 input phy_clk, // SPI时钟 output FLASH_CLK, // SPI时钟 output FLASH_CS, // SPI片选 output FLASH_MOSI, // SPI主器件输出从器件输入 input FLASH_MISO, // SPI主器件输入从器件输出 input rst_n ); //======================================================================================= localparam PROG_CMD = 8'h01; localparam ERASE_CMD = 8'h02; wire [7:0] wr_addr[2:0]; wire [7:0] rd_addr[2:0]; wire [7:0] er_addr[2:0]; assign wr_addr[2] = (SIM_MODE == "TRUE") ? 8'h10 : 8'h80; assign wr_addr[1] = (SIM_MODE == "TRUE") ? 8'h00 : 8'h00; assign wr_addr[0] = (SIM_MODE == "TRUE") ? 8'h00 : 8'h00; assign rd_addr[2] = (SIM_MODE == "TRUE") ? 8'h10 : 8'h80; assign rd_addr[1] = (SIM_MODE == "TRUE") ? 8'h00 : 8'h00; assign rd_addr[0] = (SIM_MODE == "TRUE") ? 8'h00 : 8'h00; assign er_addr[2] = (SIM_MODE == "TRUE") ? 8'h10 : 8'h80; assign er_addr[1] = (SIM_MODE == "TRUE") ? 8'h00 : 8'h00; assign er_addr[0] = (SIM_MODE == "TRUE") ? 8'h00 : 8'h00; //======================================================================================= wire spi_cmd_ack; reg [1:0] sys_cmd_ack = 0; always @ (posedge sys_clk or negedge rst_n) if(!rst_n) sys_cmd_ack <= 0; else sys_cmd_ack <= {sys_cmd_ack[0], spi_cmd_ack}; wire pos_cmd_ack = (sys_cmd_ack == 2'b01) ? 1'b1 : 1'b0; reg sys_cmd_vld = 0; reg [7:0] sys_cmd_vld_r = 0; always @ (posedge sys_clk ) if(sys_cmd_vld) sys_cmd_vld_r <= 8'hff; else sys_cmd_vld_r <= {sys_cmd_vld_r[6:0], 1'b0}; wire spi_cmd_vld = sys_cmd_vld_r[7]; //======================================================================================= (* mark_debug = "true" *)reg [17:0] spi_din = 0; (* mark_debug = "true" *)reg spi_wr = 0; reg rd_dev_state_en = 0; wire [7:0] rd_dev_state; // flash 05 state register reg spi_cmd_done_r = 1; reg [7:0] curr_cmd = 0; reg [7:0] test_cnt = 0; (* mark_debug = "true" *)reg [4:0] cmd_st = 0; always @ (posedge sys_clk) if(!rst_n) begin uart_fifo_rd <= 0; rd_dev_state_en <= 0; spi_cmd_done_r <= 1; sys_cmd_vld <= 0; spi_wr <= 0; cmd_st <= 0; end else case (cmd_st) 0: begin uart_fifo_rd <= 0; curr_cmd <= 0; spi_cmd_done_r <= 1; sys_cmd_vld <= 0; spi_wr <= 0; test_cnt <= test_cnt + 1; if(&test_cnt) cmd_st <= 1; end 1: begin rd_dev_state_en <= 0; test_cnt <= 0; curr_cmd <= 0; sys_cmd_vld <= 0; spi_wr <= 0; if(pb_rd_id) //PB5作为读id cmd_st <= 2; else if(pb_wr) //PB2作为写数据 128Byte , 地址为80_0000, 数据 00 -- 127 begin curr_cmd <= PROG_CMD; cmd_st <= 5; end else if(pb_erase) //PB1作为擦除扇区指令, 地址为80_0000, begin curr_cmd <= ERASE_CMD; cmd_st <= 5; end else if(pb_rd) //PB3作为读数据 128Byte , 地址为80_0000, 数据 00 -- 127 cmd_st <= 22; else if(uart_done) cmd_st <= 26; end 2: //read flash dev id begin spi_cmd_done_r <= 0; spi_din <= 8'h9f; // command spi_wr <= 1; cmd_st <= 3; end 3: begin spi_din <= 18'h2_0003; spi_wr <= 1; sys_cmd_vld <= 1; cmd_st <= 4; end 4: begin spi_wr <= 0; sys_cmd_vld <= 0; if(pos_cmd_ack) cmd_st <= 31; end 5: // write 06 register : write enable 写数据 128Byte , 地址为80_0000, 数据 00 -- 127 begin spi_cmd_done_r <= 0; spi_din <= 8'h06; // command write enable spi_wr <= 1; cmd_st <= 6; end 6: begin spi_din <= 18'h2_0000; spi_wr <= 1; sys_cmd_vld <= 1; cmd_st <= 7; end 7: begin spi_wr <= 0; sys_cmd_vld <= 0; if(pos_cmd_ack) begin if(curr_cmd == PROG_CMD) cmd_st <= 8; else cmd_st <= 18; end end 8: // write data into flash , 1:0x02 register (write command); 2: flash address 0x80_0000; 3: write data 0- 255 begin spi_din <= 8'h02; // command write enable spi_wr <= 1; test_cnt <= 2; cmd_st <= 9; end 9: // write flash address 0x80_0000 begin if(test_cnt == 0) cmd_st <= 10; else begin spi_din <= wr_addr[test_cnt]; spi_wr <= 1; test_cnt <= test_cnt - 1; end end 10: // write data 0 - 127 begin spi_din <= 0; spi_wr <= 1; test_cnt <= 1; cmd_st <= 11; end 11: // write data 0 - 127 begin spi_din <= spi_din + 1; spi_wr <= 1; test_cnt <= test_cnt + 1; if(test_cnt == 127) cmd_st <= 12; end 12: begin test_cnt <= 0; spi_din <= 18'h2_0000; // no read data spi_wr <= 1; sys_cmd_vld <= 1; cmd_st <= 13; end 13: begin spi_wr <= 0; sys_cmd_vld <= 0; if(pos_cmd_ack) cmd_st <= 14; end 14: // read status register begin test_cnt <= test_cnt + 1; if(test_cnt == 128) begin spi_din <= 8'h05; // read status register spi_wr <= 1; cmd_st <= 15; end end 15: begin test_cnt <= 0; spi_din <= 18'h2_0001; spi_wr <= 1; sys_cmd_vld <= 1; rd_dev_state_en <= 1; cmd_st <= 16; end 16: begin spi_wr <= 0; sys_cmd_vld <= 0; if(pos_cmd_ack) cmd_st <= 17; end 17: begin rd_dev_state_en <= 0; if(rd_dev_state[0]) cmd_st <= 14; else cmd_st <= 31; end 18: // erase_cmd begin spi_din <= 8'h20; // erase command spi_wr <= 1; test_cnt <= 2; cmd_st <= 19; end 19: // erase_sector begin if(test_cnt == 0) cmd_st <= 20; else begin spi_din <= er_addr[test_cnt]; // erase sector 0x80_0000 spi_wr <= 1; test_cnt <= test_cnt - 1; end end 20: begin test_cnt <= 0; spi_din <= 18'h2_0000; spi_wr <= 1; sys_cmd_vld <= 1; cmd_st <= 21; end 21: begin spi_wr <= 0; sys_cmd_vld <= 0; if(pos_cmd_ack) cmd_st <= 14; end 22: //read flash begin spi_din <= 8'h03; // read command spi_wr <= 1; test_cnt <= 2; cmd_st <= 23; end 23: // read start address begin spi_cmd_done_r <= 0; if(test_cnt == 0) cmd_st <= 24; else begin spi_din <= rd_addr[test_cnt]; // erase sector 0x80_0000 spi_wr <= 1; test_cnt <= test_cnt - 1; end end 24: begin test_cnt <= 0; spi_din <= 18'h2_0080; spi_wr <= 1; sys_cmd_vld <= 1; cmd_st <= 25; end 25: begin spi_wr <= 0; sys_cmd_vld <= 0; if(pos_cmd_ack) cmd_st <= 31; end 26: // uart rev interface begin if(!uart_fifo_empty) begin uart_fifo_rd <= 1; cmd_st <= 27; end end 27: begin spi_wr <= 0; uart_fifo_rd <= 0; cmd_st <= 28; end 28: begin if(uart_fifo_empty) begin spi_din <= {2'b10, 8'h00, uart_fifo_dout}; spi_wr <= 1; sys_cmd_vld <= 1; cmd_st <= 29; end else begin spi_cmd_done_r <= 0; spi_din <= uart_fifo_dout; spi_wr <= 1; uart_fifo_rd <= 1; cmd_st <= 27; end end 29: begin spi_wr <= 0; sys_cmd_vld <= 0; if(pos_cmd_ack) cmd_st <= 31; end /* 28: // disable write begin spi_din <= 8'h04; // write disable spi_wr <= 1; cmd_st <= 29; end 29: begin spi_din <= 18'h2_0000; spi_wr <= 1; sys_cmd_vld <= 1; cmd_st <= 30; end 30: begin spi_wr <= 0; sys_cmd_vld <= 0; if(pos_cmd_ack) cmd_st <= 31; end */ 31: begin spi_cmd_done_r <= 1; cmd_st <= 1; end default : cmd_st <= 0; endcase assign spi_cmd_done = spi_cmd_done_r; spi_cmd #(.SPI_MODE ("FLASH") ) spi_cmd_inst ( .phy_clk (phy_clk), //主时钟 .spi_cs (FLASH_CS), //SPI时钟 .spi_clk (FLASH_CLK), //SPI片选 .spi_mosi (FLASH_MOSI), //SPI主器件输出从器件输入 .spi_miso (FLASH_MISO), //SPI主器件输入从器件输出 .sys_clk (sys_clk), .spi_din (spi_din), .spi_wr (spi_wr), .spi_rd (spi_rd), .spi_dout (spi_dout), .spi_empty (spi_empty), .spi_cmd_vld (spi_cmd_vld), .spi_cmd_ack (spi_cmd_ack), .rd_dev_state_en (rd_dev_state_en), // for spi flash .rd_dev_state (rd_dev_state), // read spi flash 05 state register, this value cannot be written to fifo .rst_n (rst_n) ); //======================================================================================= 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 |
input <span style="color: #0000ff;">sys_clk</span>, // 系统时钟 input <span style="color: #0000ff;">pb_rd_id</span>, // 按键中提取信号:作为读id input <span style="color: #0000ff;">pb_wr</span>, // 按键中提取信号:作为写数据 128Byte , 地址为80_0000, 数据 00 -- 127 input <span style="color: #0000ff;">pb_rd</span>, // 按键中提取信号:作为读数据 128Byte , 地址为80_0000, 数据 00 -- 127 input <span style="color: #0000ff;">pb_erase</span>, // 按键中提取信号:作为擦除扇区指令, 地址为80_0000, output <span style="color: #0000ff;">spi_cmd_done</span>, // transaction ok , 当前整个操作结束标志 input <span style="color: #0000ff;">spi_rd</span>, // 读信号,读fifo(spi_fifo_rd)中 由 spi_phy.v 接收到的数据 output [7:0] <span style="color: #0000ff;">spi_dout</span>, // 读fifo(spi_fifo_rd)中 由 spi_phy.v 接收到的数据 output <span style="color: #0000ff;">spi_empty</span>, // fifo empty 标志(spi_fifo_rd) output reg <span style="color: #0000ff;">uart_fifo_rd</span> = 0, // 读信号,读uart 接收fifo 中的数据 input [7:0] <span style="color: #0000ff;">uart_fifo_dout</span>, // 读uart 接收fifo 中的数据 input <span style="color: #0000ff;">uart_fifo_empty</span>, // uart fifo empty 标志 input <span style="color: #0000ff;">uart_done</span>, // uart 接收一个操作结束, 所有命令,数据已经在uart fifo 中 input <span style="color: #0000ff;">phy_clk</span>, // SPI工作时钟 output <span style="color: #0000ff;">FLASH_CLK</span>, // SPI时钟 output <span style="color: #0000ff;">FLASH_CS</span>, // SPI片选 output <span style="color: #0000ff;">FLASH_MOSI</span>, // SPI主器件输出从器件输入 input <span style="color: #0000ff;">FLASH_MISO</span>, // SPI主器件输入从器件输出 input <span style="color: #0000ff;">rst_n</span> |
与spi_cmd.v 交互的异步握手逻辑
|
1 2 3 4 5 6 7 8 |
wire spi_cmd_ack; reg [1:0] <span style="color: #0000ff;">sys_cmd_ack</span> = 0; always @ (posedge sys_clk or negedge rst_n) if(!rst_n) <span style="color: #0000ff;">sys_cmd_ack</span> <= 0; else <span style="color: #0000ff;">sys_cmd_ack</span> <= {<span style="color: #0000ff;">sys_cmd_ack</span>[0], spi_cmd_ack}; wire pos_cmd_ack = (sys_cmd_ack == 2'b01) ? 1'b1 : 1'b0; |
|
1 2 3 4 5 6 7 8 |
reg sys_cmd_vld = 0; reg [7:0] <span style="color: #0000ff;">sys_cmd_vld_r</span> = 0; always @ (posedge sys_clk ) if(sys_cmd_vld) <span style="color: #0000ff;">sys_cmd_vld_r</span> <= 8'hff; else <span style="color: #0000ff;">sys_cmd_vld_r</span> <= {<span style="color: #0000ff;">sys_cmd_vld_r</span>[6:0], 1'b0}; wire spi_cmd_vld = <span style="color: #0000ff;">sys_cmd_vld_r</span>[7]; //======================================================================================= |
状态机逻辑:
状态机0:初始化所有变量状态,等待上电时所有fifo 复位结束, 跳转状态1
状态机1:空闲状态,等待上层模块的相关spi 操作,如果:
按键读取spi flash ID命令, 跳转状态2
按键写数据命令, 跳转状态5
按键擦除命令, 跳转状态5
按键读数据, 跳转状态22
uart 接收到spi 命令, 跳转状态26
// 读取spi flash ID
状态机2:发送spi flash ID 读命令,向spi_fifo_wr 中写入 (18’h0_009f)跳转状态3
状态机3:发送spi flash 读长度3,结束命令,向spi_fifo_wr 中写入 (18’h2_0003)跳转状态4
状态机4:等待下层模块发送结束, 跳转状态31
// 设置spi flash 06 寄存器 (打开写操作)
状态机5:写spi flash 06 寄存器,write enable 寄存器打开, 允许对spi flash 进行写(编程),擦除等操作, 跳转状态6
状态机6:没有需要从spif flash 读的数据,结束标志,跳转状态7
状态机7:f当前的操作(transaction) 结束后,如果是写(编程)操作, 跳转8; 如果是擦除操作,跳转18
// 写(编程) 操作
状态机8:写(编程)操作, 02 命令字,跳转9
状态机9:连续写3次spi flash 地址, 跳转 10
状态机10:写 0x00 数据到spi flash 中 ,跳转11
状态机11:写0x01 – 0x7f 数据到 spi flash 中, 跳转12
状态机12:没有数据需要读取, 发送结束标志。 跳转13
状态机13:等待当前的操作(transaction) 结束,即 spi_phy.v 完全将数据发送到spi 设备时,跳转14
// 读spi flash 05 寄存器 (读取flash 的状态寄存器内容)
状态机14:读spi flash 05 (状态寄存器)操作(transaction), 先等待128个clock (没有必要快速读取05 寄存器, spi flash 需要一定的反映时间,然后spi flash 设备才能更新05 状态寄存器),跳转15
状态机15:发送结束标志,从spi flash 中准备读取 1 byte数据, 跳转16
状态机16:等待操作(transaction)结束,跳转17
状态机17:检查05 状态寄存器的数据,如果 bit[0] == 1,编程或者擦除等命令还没有结束,重新检查05寄存器, 跳转14; 如果 bit[0] == 0, 跳转31。
// 擦除操作
状态机18:发送擦除命令,跳转19
状态机19:连续发送3bytes 的地址到spi flash, 跳转20
状态机20:发送结束标志,没有数据需要从spi flash 中读取。 跳转21
状态机21:等待操作(transaction) 结束,跳转14。 (检查05 状态寄存器)
// 读spi flash 操作
状态机22:发送读spi flash 命令, 跳转23
状态机23:连续发送3bytes 的地址数据到spi flash,跳转24
状态机24:发送结束标志,并且需要读取128 bytes的数据,跳转25
状态机25:等待当前的操作(transaction)结束,跳转31
// 从uart 端 接收的相关spi 操作(可能是任何一种spi 操作)
状态机26:如果uart 接收fifo 不是为空,从uart fifo 中读取一个数据,跳转27
状态机27:读信号结束,跳转28
状态机28:如果uart fifo 为空,表示当前从fifo 里读取的最后一个数据为结束标志,发送这个数据(包括读取spi flash 的长度),跳转29。如果uart fifo 不为空,继续读取uart fifo 中的数据,跳转27
状态机29:等待当前操作结束(transaction),跳转31
// 结束当前操作
状态机31:通知上层模块,当前的操作(transaction)结束,跳转1
总结
到此,所有关于spi 的操作,介绍完毕。 上层的应用(按键发送相关的命令, 或者 uart 发送相关的spi 命令)只需要发送命令即可,上层模块不再关心具体的spi 操作细节。



