程序是用verilog写的,可以读数据,写程序是先擦除,延时tse,再写入,但是读出来是ff,应该是没有写成功,然后我把程序中擦除部分删掉了直接写入数据,就可以读出来正确的数据
这是我用在线逻辑分析仪看的一部分波形,看起来时序应该没问题,但是擦除延时后面的程序波形逻辑分析仪显示不出来
下擦除命令后,不能简单延时,要通过读取状态来判断是否操作完成
这里放一下写程序的代码,希望各位大佬能帮忙看一下,也是根据网上的程序改的
module flash_write(
input clk ,
input rst_n ,
//key
input write ,
input [ 7:0] wr_data ,//写入的数据
input [23:0] wr_addr ,//flash写地址
//spi_master
output trans_req ,
output [7:0] tx_dout ,
input [7:0] rx_din ,
input trans_done ,
//output
output [31:0] dout ,
output dout_vld
);
//参数定义
//主状态机状态参数
localparam M_IDLE = 5'b0_0001,
M_WREN = 5'b0_0010,//主机发写使能序列
M_SE = 5'b0_0100,//主机发扇区擦除序列
M_RDSR = 5'b0_1000,//主机发读状态寄存器序列
M_PP = 5'b1_0000;//主机发页编程序列
//从状态机状态参数
localparam S_IDLE = 5'b0_0001,
S_CMD = 5'b0_0010,//发送命令
S_ADDR = 5'b0_0100,//发送地址
S_DATA = 5'b0_1000,//发送数据、接收数据
S_DELA = 5'b1_0000;//延时 拉高片选信号
localparam CMD_WREN = 8'h06,//写使能命令
CMD_SE = 8'h20,//擦除命令
CMD_RDSR = 8'h05,//读状态寄存器命令
CMD_PP = 8'h02;//页编程命令
parameter TIME_DELAY = 16,//发完WREN、SE、PP序列 拉高片选
TIME_SE = 50000000,//扇区擦除1s
TIME_PP = 200000,//页编程4ms
TIME_RDSR = 2000;//读状态寄存器 最大2000次
//信号定义
reg [4:0] m_state_c ;
reg [4:0] m_state_n ;
reg [4:0] s_state_c ;
reg [4:0] s_state_n ;
reg [3:0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
reg [27:0] xx ;
reg [31:0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
reg [31:0] yy ;
reg rdsr_wip ;
reg rdsr_wel ;
reg [2:0] flag ;
reg [7:0] tx_data ;
reg tx_req ;
reg [31:0] data ;
reg data_vld ;
wire m_idle2m_wren ;
wire m_wren2m_se ;
wire m_se2m_rdsr ;
wire m_rdsr2m_wren ;
wire m_rdsr2m_pp ;
wire m_wren2m_pp ;
wire m_rdsr2m_idle ;
wire m_pp2m_rdsr ;
wire s_idle2s_cmd ;
wire s_cmd2s_addr ;
wire s_cmd2s_data ;
wire s_cmd2s_dela ;
wire s_addr2s_data ;
wire s_addr2s_dela ;
wire s_data2s_dela ;
wire s_dela2s_idle ;
//状态机设计
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
m_state_c <= M_IDLE ;
end
else begin
m_state_c <= m_state_n;
end
end
always @(*) begin
case(m_state_c)
M_IDLE :begin
if(m_idle2m_wren)
m_state_n = M_WREN ;
else
m_state_n = m_state_c ;
end
M_WREN :begin
if(m_wren2m_se)
m_state_n = M_SE ;
else if(m_wren2m_pp)
m_state_n = M_PP ;
else
m_state_n = m_state_c ;
end
M_SE :begin
if(m_se2m_rdsr)
m_state_n = M_RDSR ;
else
m_state_n = m_state_c ;
end
M_RDSR :begin
if(m_rdsr2m_wren)
m_state_n = M_WREN ;
else if(m_rdsr2m_pp)
m_state_n = M_PP ;
else if(m_rdsr2m_idle)
m_state_n = M_IDLE ;
else
m_state_n = m_state_c ;
end
M_PP :begin
if(m_pp2m_rdsr)
m_state_n = M_RDSR ;
else
m_state_n = m_state_c ;
end
default : m_state_n = M_IDLE ;
endcase
end
assign m_idle2m_wren = m_state_c==M_IDLE && (write);
assign m_wren2m_se = m_state_c==M_WREN && (s_dela2s_idle && flag[0]);
assign m_se2m_rdsr = m_state_c==M_SE && (s_dela2s_idle);
assign m_rdsr2m_wren = m_state_c==M_RDSR && (s_dela2s_idle && ~rdsr_wel && ~rdsr_wip && flag[1]);
assign m_rdsr2m_pp = m_state_c==M_RDSR && (s_dela2s_idle && rdsr_wel && ~rdsr_wip && flag[1]);
assign m_wren2m_pp = m_state_c==M_WREN && (s_dela2s_idle && flag[1]);
assign m_rdsr2m_idle = m_state_c==M_RDSR && (s_dela2s_idle && flag[2]);
assign m_pp2m_rdsr = m_state_c==M_PP && (s_dela2s_idle);
//从状态机设计
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
s_state_c <= S_IDLE ;
end
else begin
s_state_c <= s_state_n;
end
end
always @(*) begin
case(s_state_c)
S_IDLE :begin
if(s_idle2s_cmd)
s_state_n = S_CMD ;
else
s_state_n = s_state_c ;
end
S_CMD :begin
if(s_cmd2s_addr)
s_state_n = S_ADDR ;
else if(s_cmd2s_data)
s_state_n = S_DATA ;
else if(s_cmd2s_dela)
s_state_n = S_DELA ;
else
s_state_n = s_state_c ;
end
S_ADDR :begin
if(s_addr2s_data)
s_state_n = S_DATA ;
else if(s_addr2s_dela)
s_state_n = S_DELA ;
else
s_state_n = s_state_c ;
end
S_DATA :begin
if(s_data2s_dela)
s_state_n = S_DELA ;
else
s_state_n = s_state_c ;
end
S_DELA :begin
if(s_dela2s_idle)
s_state_n = S_IDLE ;
else
s_state_n = s_state_c ;
end
default : s_state_n = S_IDLE ;
endcase
end
assign s_idle2s_cmd = s_state_c==S_IDLE && (m_state_c != M_IDLE);
assign s_cmd2s_addr = s_state_c==S_CMD && (trans_done && (m_state_c == M_SE || m_state_c == M_PP));
assign s_cmd2s_data = s_state_c==S_CMD && (trans_done && m_state_c == M_RDSR);
assign s_cmd2s_dela = s_state_c==S_CMD && (trans_done && m_state_c == M_WREN);
assign s_addr2s_data = s_state_c==S_ADDR && (end_cnt0 && (m_state_c == M_RDSR || m_state_c == M_PP));
assign s_addr2s_dela = s_state_c==S_ADDR && (end_cnt0 && m_state_c == M_SE);
assign s_data2s_dela = s_state_c==S_DATA && (end_cnt0 && m_state_c == M_PP || m_state_c == M_RDSR && end_cnt1);
assign s_dela2s_idle = s_state_c==S_DELA && (end_cnt1);
//计数器设计
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt0 <= 0;
end
else if(add_cnt0) begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0+1 ;
end
end
assign add_cnt0 = (m_state_c != M_RDSR && trans_done);
assign end_cnt0 = add_cnt0 && cnt0 == (xx)-1 ;
always @(*)begin
if(s_state_c == S_CMD) //发命令1字节
xx = 1;
else if(s_state_c == S_ADDR) //发地址3字节
xx = 3;
else //发数据1字节
xx = 4;
end
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt1 <= 0;
end
else if(add_cnt1) begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1+1 ;
end
end
assign add_cnt1 = (s_state_c == S_DELA || m_state_c == M_RDSR && s_state_c == S_DATA && trans_done);
assign end_cnt1 = add_cnt1 && (cnt1 == (yy)-1 || trans_done && ~rdsr_wip);
always @(*)begin
if((m_state_c == M_WREN || m_state_c == M_RDSR) && s_state_c == S_DELA) //发完写使能延时
yy = TIME_DELAY;
else if(m_state_c == M_SE)//发完擦除延时
yy = TIME_SE;
else if(m_state_c == M_PP)//发完页编程延时
yy = TIME_PP;
else if(m_state_c == M_RDSR && s_state_c == S_DATA)//读状态寄存器值 yy 次
yy = TIME_RDSR;
else
yy = TIME_DELAY;
end
//rdsr_wel rdsr_wip
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rdsr_wel <= 1'b0;
rdsr_wip <= 1'b0;
end
else if(m_state_c == M_RDSR && s_state_c == S_DATA && trans_done)begin
rdsr_wel <= rx_din[1];
rdsr_wip <= rx_din[0];
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag <= 3'b000;
end
else if(m_idle2m_wren)begin
flag <= 3'b001;
end
else if(m_se2m_rdsr)begin
flag <= 3'b010;
end
else if(m_pp2m_rdsr)begin
flag <= 3'b100;
end
else
flag<=flag;
end
//输出
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
tx_data <= 0;
end
else if(m_state_c == M_WREN)begin
tx_data <= CMD_WREN;
end
else if(m_state_c == M_SE)begin
case(s_state_c)
S_CMD :tx_data <= CMD_SE;
S_ADDR:tx_data <= wr_addr[23-cnt0*8 -:8];
default:tx_data <= 0;
endcase
end
else if(m_state_c == M_RDSR)begin //在读状态寄存器时,可以发一次命令,也可以连续发命令
tx_data <= CMD_RDSR;
end
else if(m_state_c == M_PP)begin
case(s_state_c)
S_CMD :tx_data <= CMD_PP;
S_ADDR:tx_data <= wr_addr[23-cnt0*8 -:8];
S_DATA:tx_data <= wr_data;
default:tx_data <= 0;
endcase
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
tx_req <= 1'b0;
end
else if(s_idle2s_cmd)begin
tx_req <= 1'b1;
end
else if(s_cmd2s_dela | s_addr2s_dela | s_data2s_dela)begin
tx_req <= 1'b0;
end
else
tx_req <=tx_req;
end
//data data_mask
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data <= 0;
end
else if(m_rdsr2m_idle & ~rdsr_wip)begin //PP completed
data <=wr_data;
end
else if(m_rdsr2m_idle & rdsr_wip)begin //PP failed
data <=32'b0;
end
end
//data_vld ,
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_vld <= 0;
end
else begin
data_vld <= m_rdsr2m_idle;
end
end
//输出
assign tx_dout = tx_data;
assign trans_req = tx_req;
assign dout_vld = data_vld;
assign dout = data;
endmodule
可能是你在写入数据之前,擦除了错误的位置或者没有对应的地址进行擦除。在诊断时序问题时,建议通过逻辑分析仪提取和比对信号波形,确保每个时钟周期内的信号值都符合预期。
另外,请检查你使用的擦除算法是否正确,并且每个操作的延时时间是否足够。如果延时不够可能会导致 FPGA 写入失败。最好参考 FPGA 芯片的相关文档,以确保所使用的擦除方法和写入方案是正确的。
如果以上方法不能解决问题,建议您尝试将问题范围进一步缩小到某个特定模块,以定位问题所在。如果你还无法解决问题,可以向特定技术社区或 FPGA 厂家寻求帮助,以获得更准确的解决方案。
根据您提供的信息,可能有以下几种原因导致无法正确写入数据:
1.擦除操作存在问题:如果擦除操作没有正确执行,可能会导致数据写入失败。建议检查擦除操作是否正确,并尝试增加擦除操作的延时时间,以确保擦除完成。
2.写入操作存在问题:如果写入操作没有正确执行,可能会导致数据写入失败。建议检查写入操作是否正确,并尝试增加写入操作的延时时间,以确保数据写入完成。
3.时序问题:时序问题可能会导致写入操作失败。建议检查时序是否正确,并尝试根据实际情况进行调整。
对于无法正常读取的现象,可能有以下几种原因:
1.写入操作失败:如果写入操作没有成功,可能导致数据无法正确读取。建议检查写入操作是否正确执行,并尝试增加写入操作的延时时间。
2.读取操作存在问题:如果读取操作没有正确执行,可能会导致数据无法正确读取。建议检查读取操作是否正确,并尝试增加读取操作的延时时间。
3.时序问题:时序问题可能会导致读取操作失败。建议检查时序是否正确,并尝试根据实际情况进行调整。
针对无法显示出擦除延时后面的程序波形,可能是由于逻辑分析仪的采样率不足或者采样时机不对导致的。建议检查逻辑分析仪的设置和采样时机,并尝试调整采样参数,以确保正确显示波形。
综上所述,建议您仔细检查写入和读取操作的时序和参数,并根据实际情况进行调整。如果问题仍然存在,建议使用其他工具或方法进行调试,以确定问题所在。
可以借鉴下
`timescale 1ns/1ns
module file_ctrl;
reg clk;
reg rst_n;
reg [7:0] data_in; //定义的数据输入寄存器
reg [7:0] data_out;
reg [7:0] cnt;
integer file_rd; //定义数据读指针
integer file_wr; //定义数据写指针
initial begin //定义时钟
clk = 1'b1;
forever
#10
clk = ~clk;
end
initial begin //定义复位
rst_n = 1'b0;
#21
rst_n = 1'b1;
end
initial begin //打开读取和写入的文件,这里的路径要对
file_rd = $fopen("E:/Project/file_ctrl/sim/tb/data_in.txt","r");
file_wr = $fopen("E:/Project/file_ctrl/sim/tb/data_out.txt","w");
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)begin
data_in <= 8'd0;
cnt <= 8'd0;
end
else if(cnt < 10)begin
$fscanf(file_rd,"%h",data_in); //读取每行
cnt <= cnt +1;
$fwrite(file_wr,"%h\n", data_in); //换行写入
end
else begin
data_in <= 8'd0;
$fclose(file_rd); //关闭文件
$fclose(file_wr);
cnt <= 8'd11;
end
end
endmodule
以下答案由GPT-4.5Plus大模型(语音)与博主A努力中.......共同编写:
阅读这个帖子,主要问题也是gd25q128闪存的擦除和编程时序引起的。
根据帖子的代码和描述,这里有几点建议:
所以,总体的改进建议如下:
要解决这个问题,您可以:
增加更长的擦除延时,确保擦除操作完成后再进行下一步。典型的擦除时间可以查阅芯片的datasheet。
在擦除后立即读取状态寄存器,确认擦除是否完成,然后再进行下一步操作。例如:
verilog
erase_flash(); // 擦除操作
#tse; // 延时
status = read_status_reg(); // 读取状态寄存器
while (status == BUSY) begin // 等待擦除完成
#1;
status = read_status_reg();
end
write_flash(data); // 擦除完成后写入数据