1.写在前面
FPGA内部不具有掉电存储程序的功能,所以都需要外置的flash存储器来存储程序,上电后从flash加载程序到FPGA中运行。外置的flash可以存储程序,也可以存储任何用户数据,可以更有效的利用flash的存储空间。
值得注意的是,用于存储程序的flash和fpga连接用的是fpga的专用引脚,flash时钟信号不可以直接驱动,这个信号是fpga硬件直接管理的,需要使用原语才可以驱动时钟信号,这个原语叫STARTUPE2。
STARTUPE2 #(
.PROG_USR("FALSE"), // Activate program event security feature. Requires encrypted bitstreams.
.SIM_CCLK_FREQ(0.0) // 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
.USRCCLKTS(0), // 1-bit input: User CCLK 3-state enable input
.USRDONEO(1), // 1-bit input: User DONE pin output control
.USRDONETS(1) // 1-bit input: User DONE 3-state enable output
)
里边的flash_clk就是要约束的flash时钟信号,通过这个原语才能约束用于配置程序的flash的时钟信号。
不想这么麻烦也可以,在硬件上再加一片flash接到通用管脚上就完了,但是想做远程程序更新功能就必须这么干,通过串口或者网口或者什么其他通信接口将要更新的程序发送至fpga,fpga存储空间不够的话就暂存到ddr或者外置sram里,再按顺序操作flash烧入,扯远了,有机会一定要搞一下这个功能肯定有用,只是我现在还不会写上位机。
2.FPGA实现qspi flash读写
前面扯了很多没用的,直接上源码,自己写的,为了省事没搞qspi只用了spi,智商感人的领导把时间全给硬件开发了,硬件平台搭了一年多,软件只给了3个月,交货前2个月才知道具体需求,之前全是含糊其辞,没办法全是赶工出来的,凑活看吧,改改就能用,你问我为啥敢吐槽,已经被辞了,项目干的甲方无比懊恼,钱都不想给了,领导肯定是没错的,所以嘛只能这样了,硬件做的一言难尽时间全耽误了硬件平台到我手里连4个月都没有中间还反复改硬件,硬件做不到了就拿程序填,硬件做的垃圾最后全是做软件的背了锅,所以最后只有我走人,做垃圾的还好好的呢,说多了直接上码,前面发的牢骚各位直接忽略。
顶层命令控制
///
/* Document info
document class : RES
module name : flash
module purpose : n25q256 256byte per page
version : V1.0
author : mayidianzi
*/
///
module flash_ctrl(
input I_clk , // clk
input I_TEST_CLK ,
input I_reset_n , // module reset signal
input I_read_en ,
input I_write_en ,
input I_ID_en ,
input I_erase_en ,
input [63:0] I_wr_data ,
input I_wraddr_clear ,
input I_rdaddr_clear ,
output reg O_flash_ready,
output reg O_flash_end ,
input I_SDO , // flash D1信号
output O_SDI , // flash D0信号
output O_SCK , // flash SCK信号
output O_CS ,
input I_flash_dq3 ,
input I_flash_dq2 ,
output reg [63:0] O_rd_data,
output reg [23:0] O_flash_id
);
/ parameter set \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
parameter READ_STATUS_REGISTER = 16'h05_00,
READ_ID = 48'h9F_00_00_00_00_00,
WRITE_ENABLE = 8'h06,
BULK_ERASE = 8'hC7;
parameter C_empty = 8'd12,
C_READID_idle = 8'd0,
C_READID_valid = 8'd1,
C_erase_idle = 8'd2,
C_erase_valid = 8'd3,
C_check_idle = 8'd4,
C_check_valid = 8'd5,
C_write_idle = 8'd6,
C_write_valid = 8'd7,
C_read_idle = 8'd8,
C_read_valid = 8'd9,
C_wrenable_idle = 8'd10,
C_wrenable_valid= 8'd11;
internal signal \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
reg [23:0] S_rd_addr ;
reg [23:0] S_wr_addr ;
wire [95:0] S_rd_data ;
reg [63:0] S_wr_data ;
wire [15:0] S_rd_status ;
wire [95:0] READ_Command ;
wire [95:0] WRITE_Command ;
reg [7:0] S_status ;
reg [1:0] S_w_e_flag ;
reg S_readID_start ;
reg S_read_start ;
reg S_write_start ;
reg S_erase_start ;
reg S_wrenable_start ;
reg S_check_start ;
wire S_end ;
wire S_busy ;
wire [31:0] S_read_id ;
reg S_rdaddr_clear, S_wraddr_clear;
assign READ_Command = {8'h03,S_rd_addr,64'h00_00_00_00_00_00_00_00};
assign WRITE_Command = {8'h02,S_wr_addr,S_wr_data};
assign S_busy = S_rd_status[0];
instance \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//ila_1 ila_1_i(
//.clk(I_clk),
//.probe0(S_rd_addr),
//.probe1(S_wr_addr),
//.probe2(I_wraddr_clear),
//.probe3(I_rdaddr_clear),
//.probe4(O_rd_data),
//.probe5(I_wr_data),
//.probe6(I_write_en)
//);
spi_port spi_port_i(
.I_CLK(I_clk),
.I_reset(I_reset_n),
.I_wrcmd(WRITE_Command),
.I_rdcmd(READ_Command),
.I_erasecmd(BULK_ERASE),
.I_rdIDcmd(READ_ID),
.I_wrencmd(WRITE_ENABLE),
.I_checkcmd(READ_STATUS_REGISTER),
.I_wrstart(S_write_start),
.I_rdstart(S_read_start),
.I_readID_start(S_readID_start),
.I_erase_start(S_erase_start),
.I_wrenable_start(S_wrenable_start),
.I_check_start(S_check_start),
.I_SDO(I_SDO),
.O_end(S_end),
.O_SCK(O_SCK),
.O_CS(O_CS),
.O_SDI(O_SDI),
.I_flash_dq2(I_flash_dq2),
.I_flash_dq3(I_flash_dq3),
.O_rddata(S_rd_data),
.O_rd_status(S_rd_status),
.O_rd_id(S_read_id)
);
main programe \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
/* config procedure */
always @(posedge I_clk or negedge I_reset_n)
begin
if(!I_reset_n)
begin
S_status <= C_check_idle;
O_flash_ready <= 0;
S_w_e_flag <= 0;
O_flash_end <= 0;
end
else
begin
case(S_status)
C_empty:
begin
if(I_ID_en == 1)
begin
S_status <= C_READID_idle;
O_flash_ready <= 0;
O_flash_end <= 0;
end
else if(I_erase_en == 1)
begin
S_status <= C_wrenable_idle;
O_flash_ready <= 0;
S_w_e_flag <= 1;
O_flash_end <= 0;
end
else if(I_read_en == 1)
begin
S_status <= C_read_idle;
O_flash_ready <= 0;
O_flash_end <= 0;
end
else if(I_write_en == 1)
begin
S_status <= C_wrenable_idle;
O_flash_ready <= 0;
S_w_e_flag <= 2;
O_flash_end <= 0;
end
else
begin
O_flash_ready <= 1;
O_flash_end <= 0;
S_w_e_flag <= 0;
S_status <= C_empty;
end
end
C_READID_idle:
begin
S_status <= C_READID_valid;
end
C_READID_valid:
begin
if(S_end == 1)
begin
S_status <= C_empty;
O_flash_end <= 1;
end
end
C_wrenable_idle:
begin
S_status <= C_wrenable_valid;
end
C_wrenable_valid:
begin
if(S_end == 1)
begin
if(S_w_e_flag == 1)
S_status <= C_erase_idle;
else if(S_w_e_flag == 2)
S_status <= C_write_idle;
end
end
C_erase_idle:
begin
S_status <= C_erase_valid;
end
C_erase_valid:
begin
if(S_end == 1)
begin
S_status <= C_check_idle;
S_w_e_flag <= 0;
end
end
C_check_idle:
begin
S_status <= C_check_valid;
end
C_check_valid:
begin
if(S_end == 1)
begin
if(S_busy == 1)
S_status <= C_check_idle;
else
begin
S_status <= C_empty;
O_flash_end <= 1;
end
end
end
C_read_idle:
begin
S_status <= C_read_valid;
end
C_read_valid:
begin
if(S_end == 1)
S_status <= C_check_idle;
end
C_write_idle:
begin
S_status <= C_write_valid;
end
C_write_valid:
begin
if(S_end == 1)
begin
S_status <= C_check_idle;
S_w_e_flag <= 0;
end
end
default:
begin
S_status = C_empty;
S_w_e_flag <= 0;
O_flash_ready <= 0;
O_flash_end <= 0;
end
endcase
end
end
always @(posedge I_clk or negedge I_reset_n)
begin
if(!I_reset_n)
begin
S_readID_start <= 0;
S_read_start <= 0;
S_write_start <= 0;
S_erase_start <= 0;
S_wrenable_start <= 0;
S_check_start <= 0;
O_rd_data <= 0;
O_flash_id <= 0;
end
else
begin
case(S_status)
C_empty:
begin
S_readID_start <= 0;
S_read_start <= 0;
S_write_start <= 0;
S_erase_start <= 0;
S_wrenable_start <= 0;
S_check_start <= 0;
end
C_READID_idle:
begin
S_readID_start <= 1;
end
C_READID_valid:
begin
S_readID_start <= 0;
if(S_end == 1)
O_flash_id <= S_read_id[23:0];
end
C_wrenable_idle:
begin
S_wrenable_start <= 1;
end
C_wrenable_valid:
begin
S_wrenable_start <= 0;
end
C_erase_idle:
begin
S_erase_start <= 1;
end
C_erase_valid:
begin
S_erase_start <= 0;
end
C_check_idle:
begin
S_check_start <= 1;
end
C_check_valid:
begin
S_check_start <= 0;
end
C_read_idle:
begin
S_read_start <= 1;
end
C_read_valid:
begin
S_read_start <= 0;
if(S_end == 1)
O_rd_data <= S_rd_data[63:0];
end
C_write_idle:
begin
S_write_start <= 1;
S_wr_data <= I_wr_data;
end
C_write_valid:
begin
S_write_start <= 0;
end
default:
begin
S_readID_start <= 0;
S_read_start <= 0;
S_write_start <= 0;
S_erase_start <= 0;
S_wrenable_start <= 0;
S_check_start <= 0;
O_rd_data <= 0;
O_flash_id <= 0;
end
endcase
end
end
always @(posedge I_clk or negedge I_reset_n)
begin
if(!I_reset_n)
begin
S_wraddr_clear <= 0;
S_rdaddr_clear <= 0;
end
else
begin
S_wraddr_clear <= I_wraddr_clear;
S_rdaddr_clear <= I_rdaddr_clear;
end
end
always @(posedge I_clk or negedge I_reset_n)
begin
if(!I_reset_n)
begin
S_rd_addr <= 0;
end
else
begin
case(S_status)
C_read_valid:
if(S_end == 1)
S_rd_addr <= S_rd_addr + 8;
default:
begin
if(S_rdaddr_clear == 1)
S_rd_addr <= 0;
end
endcase
end
end
always @(posedge I_clk or negedge I_reset_n)
begin
if(!I_reset_n)
begin
S_wr_addr <= 0;
end
else
begin
case(S_status)
C_write_valid:
if(S_end == 1)
S_wr_addr <= S_wr_addr + 8;
default:
begin
if(S_wraddr_clear == 1)
S_wr_addr <= 0;
end
endcase
end
end
endmodule
这里边的读写地址是自动管理的,每做一次读写操作自行增加,根据外部清地址信号来清零的,不想这么用的可以自行更改。我太懒不想改了。这个模块怎么操作可以自己写个仿真文件仿真一下,或者来问我,我会捡我想得起来的说说,不解释了就是太懒了。文章来源:https://www.toymoban.com/news/detail-519994.html
通信底层spi文章来源地址https://www.toymoban.com/news/detail-519994.html
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/03/15 15:08:39
// Design Name:
// Module Name: spi_wr_port
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module spi_port
(
input I_CLK,
input I_reset,
input[95:0] I_wrcmd,
input[95:0] I_rdcmd,
input[7:0] I_erasecmd,
input[47:0] I_rdIDcmd,
input[7:0] I_wrencmd,
input[15:0] I_checkcmd,
input I_wrstart,
input I_rdstart,
input I_readID_start,
input I_erase_start,
input I_wrenable_start,
input I_check_start,
input I_SDO,
input I_flash_dq2,
input I_flash_dq3,
output reg O_end,
output reg O_SCK,
output reg O_CS,
output reg O_SDI,
output reg[95:0] O_rddata,
output reg[15:0] O_rd_status,
output reg[31:0] O_rd_id
);
//
reg[47:0] S_rd_id;
reg[95:0] S_rddata;
reg[15:0] S_rd_status;
reg[15:0] S_state;
reg[31:0] S_cnt;
parameter READ_STATUS_REGISTER = 16'h05_00,
READ_ID = 48'h9F_00_00_00_00_00,
WRITE_ENABLE = 8'h06,
BULK_ERASE = 8'hC7;
parameter C_idle = 0,
C_rdID_valid = 2,
C_rdID_end = 3,
C_rdID_stop = 4,
C_WEN_valid = 6,
C_WEN_end = 7,
C_WEN_stop = 8,
C_erase_valid = 10,
C_erase_end = 11,
C_erase_stop = 12,
C_check_valid = 14,
C_check_end = 15,
C_check_stop = 16,
C_rd_valid = 18,
C_rd_end = 19,
C_rd_stop = 20,
C_wr_valid = 22,
C_wr_end = 23,
C_wr_stop = 24;
//
//ila_1 ila_1_i(
//.clk(I_CLK),
//.probe0(O_rd_status),
//.probe1(O_rd_id),
//.probe2(O_CS),
//.probe3(O_SDI),
//.probe4(I_SDO),
//.probe5(O_SCK),
//.probe6(S_state),
//.probe7(I_readID_start),
//.probe8(O_rddata[63:0]),
//.probe9(I_rdcmd[95:64]),
//.probe10(I_wrcmd[95:64])
//);
//
always @(posedge I_CLK or negedge I_reset)
begin
if(!I_reset)
begin
S_state <= C_idle;
S_cnt <= 0;
end
else
begin
case(S_state)
C_idle:
begin
S_cnt <= 0;
if(I_readID_start == 1)
S_state <= C_rdID_valid;
else if(I_wrenable_start == 1)
S_state <= C_WEN_valid;
else if(I_erase_start == 1)
S_state <= C_erase_valid;
else if(I_check_start == 1)
S_state <= C_check_valid;
else if(I_rdstart == 1)
S_state <= C_rd_valid;
else if(I_wrstart == 1)
S_state <= C_wr_valid;
end
C_rdID_valid:
begin
if(S_cnt == 48*2)
begin
S_cnt <= 0;
S_state <= C_rdID_end;
end
else
S_cnt <= S_cnt + 1;
end
C_rdID_end:
begin
if(S_cnt == 30)
begin
S_cnt <= 0;
S_state <= C_rdID_stop;
end
else
S_cnt <= S_cnt + 1;
end
C_rdID_stop:
begin
S_state <= C_idle;
S_cnt <= 0;
end
//
C_WEN_valid:
begin
if(S_cnt == 8*2)
begin
S_cnt <= 0;
S_state <= C_WEN_end;
end
else
S_cnt <= S_cnt + 1;
end
C_WEN_end:
begin
if(S_cnt == 30)
begin
S_cnt <= 0;
S_state <= C_WEN_stop;
end
else
S_cnt <= S_cnt + 1;
end
C_WEN_stop:
begin
S_state <= C_idle;
S_cnt <= 0;
end
//
C_erase_valid:
begin
if(S_cnt == 8*2)
begin
S_cnt <= 0;
S_state <= C_erase_end;
end
else
S_cnt <= S_cnt + 1;
end
C_erase_end:
begin
if(S_cnt == 30)
begin
S_cnt <= 0;
S_state <= C_erase_stop;
end
else
S_cnt <= S_cnt + 1;
end
C_erase_stop:
begin
S_state <= C_idle;
S_cnt <= 0;
end
//
C_check_valid:
begin
if(S_cnt == 16*2)
begin
S_cnt <= 0;
S_state <= C_check_end;
end
else
S_cnt <= S_cnt + 1;
end
C_check_end:
begin
if(S_cnt == 30)
begin
S_cnt <= 0;
S_state <= C_check_stop;
end
else
S_cnt <= S_cnt + 1;
end
C_check_stop:
begin
S_state <= C_idle;
S_cnt <= 0;
end
//
C_rd_valid:
begin
if(S_cnt == 96*2)
begin
S_cnt <= 0;
S_state <= C_rd_end;
end
else
S_cnt <= S_cnt + 1;
end
C_rd_end:
begin
if(S_cnt == 30)
begin
S_cnt <= 0;
S_state <= C_rd_stop;
end
else
S_cnt <= S_cnt + 1;
end
C_rd_stop:
begin
S_state <= C_idle;
S_cnt <= 0;
end
//
C_wr_valid:
begin
if(S_cnt == 96*2)
begin
S_cnt <= 0;
S_state <= C_wr_end;
end
else
S_cnt <= S_cnt + 1;
end
C_wr_end:
begin
if(S_cnt == 30)
begin
S_cnt <= 0;
S_state <= C_wr_stop;
end
else
S_cnt <= S_cnt + 1;
end
C_wr_stop:
begin
S_state <= C_idle;
S_cnt <= 0;
end
default:
begin
S_state <= C_idle;
S_cnt <= 0;
end
endcase
end
end
always @(posedge I_CLK or negedge I_reset)
begin
if(!I_reset)
begin
O_end <= 0;
O_SCK <= 0;
O_CS <= 1;
O_SDI <= 0;
S_rd_id <= 0;
O_rd_id <= 0;
S_rddata <= 0;
O_rddata <= 0;
S_rd_status <= 0;
O_rd_status <= 0;
end
else
begin
case(S_state)
C_idle:
begin
O_end <= 0;
O_SCK <= 0;
O_CS <= 1;
O_SDI <= 0;
end
//
C_rdID_valid:
begin
O_CS <= 0;
case(S_cnt[0])
1'b0:
begin
O_SCK <= 0;
if(S_cnt == 48*2)
begin
O_SDI <= I_rdIDcmd[0];
end
else
begin
O_SDI <= I_rdIDcmd[47-S_cnt/2];
end
end
1'b1:
begin
O_SCK <= 1;
if(S_cnt == 48*2)
begin
O_SDI <= I_rdIDcmd[0];
S_rd_id[0] <= I_SDO;
end
else
begin
O_SDI <= I_rdIDcmd[47-S_cnt/2];
S_rd_id[47-S_cnt/2] <= I_SDO;
end
end
endcase
end
C_rdID_end:
begin
O_end <= 0;
O_CS <= 1;
O_SDI <= 0;
O_rd_id <= S_rd_id[39:8];
end
C_rdID_stop:
begin
O_end <= 1;
O_CS <= 1;
O_SDI <= 0;
end
//
C_WEN_valid:
begin
O_CS <= 0;
case(S_cnt[0])
1'b0:
begin
O_SCK <= 0;
if(S_cnt == 8*2)
O_SDI <= I_wrencmd[0];
else
O_SDI <= I_wrencmd[7-S_cnt/2];
end
1'b1:
begin
O_SCK <= 1;
if(S_cnt == 8*2)
O_SDI <= I_wrencmd[0];
else
O_SDI <= I_wrencmd[7-S_cnt/2];
end
endcase
end
C_WEN_end:
begin
O_end <= 0;
O_CS <= 1;
O_SDI <= 0;
end
C_WEN_stop:
begin
O_end <= 1;
O_CS <= 1;
O_SDI <= 0;
end
//
C_erase_valid:
begin
O_CS <= 0;
case(S_cnt[0])
1'b0:
begin
O_SCK <= 0;
if(S_cnt == 8*2)
O_SDI <= I_erasecmd[0];
else
O_SDI <= I_erasecmd[7-S_cnt/2];
end
1'b1:
begin
O_SCK <= 1;
if(S_cnt == 8*2)
O_SDI <= I_erasecmd[0];
else
O_SDI <= I_erasecmd[7-S_cnt/2];
end
endcase
end
C_erase_end:
begin
O_end <= 0;
O_CS <= 1;
O_SDI <= 0;
end
C_erase_stop:
begin
O_end <= 1;
O_CS <= 1;
O_SDI <= 0;
end
//
C_check_valid:
begin
O_CS <= 0;
case(S_cnt[0])
1'b0:
begin
O_SCK <= 0;
if(S_cnt == 16*2)
O_SDI <= I_checkcmd[0];
else
O_SDI <= I_checkcmd[15-S_cnt/2];
end
1'b1:
begin
O_SCK <= 1;
if(S_cnt == 16*2)
O_SDI <= I_checkcmd[0];
else
O_SDI <= I_checkcmd[15-S_cnt/2];
if(S_cnt == 16*2)
S_rd_status[0] <= I_SDO;
else
S_rd_status[15-S_cnt/2] <= I_SDO;
end
endcase
end
C_check_end:
begin
O_end <= 0;
O_CS <= 1;
O_SDI <= 0;
O_rd_status <= S_rd_status;
end
C_check_stop:
begin
O_end <= 1;
O_CS <= 1;
O_SDI <= 0;
end
//
C_rd_valid:
begin
O_CS <= 0;
case(S_cnt[0])
1'b0:
begin
O_SCK <= 0;
if(S_cnt == 96*2)
O_SDI <= I_rdcmd[0];
else
O_SDI <= I_rdcmd[95-S_cnt/2];
end
1'b1:
begin
O_SCK <= 1;
if(S_cnt == 96*2)
O_SDI <= I_rdcmd[0];
else
O_SDI <= I_rdcmd[95-S_cnt/2];
if(S_cnt == 96*2)
S_rddata[0] <= I_SDO;
else
S_rddata[95-S_cnt/2] <= I_SDO;
end
endcase
end
C_rd_end:
begin
O_end <= 0;
O_CS <= 1;
O_SDI <= 0;
O_rddata <= S_rddata;
end
C_rd_stop:
begin
O_end <= 1;
O_CS <= 1;
O_SDI <= 0;
end
//
C_wr_valid:
begin
O_CS <= 0;
case(S_cnt[0])
1'b0:
begin
O_SCK <= 0;
if(S_cnt == 96*2)
O_SDI <= I_wrcmd[0];
else
O_SDI <= I_wrcmd[95-S_cnt/2];
end
1'b1:
begin
O_SCK <= 1;
if(S_cnt == 96*2)
O_SDI <= I_wrcmd[0];
else
O_SDI <= I_wrcmd[95-S_cnt/2];
end
endcase
end
C_wr_end:
begin
O_end <= 0;
O_CS <= 1;
O_SDI <= 0;
end
C_wr_stop:
begin
O_end <= 1;
O_CS <= 1;
O_SDI <= 0;
end
default:
begin
O_end <= 0;
O_SCK <= 0;
O_CS <= 1;
O_SDI <= 0;
S_rd_id <= 0;
O_rd_id <= 0;
S_rddata <= 0;
O_rddata <= 0;
S_rd_status <= 0;
O_rd_status <= 0;
end
endcase
end
end
endmodule
到了这里,关于基于FPGA 外置qspi Flash的读写的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!