verilog 状态机实现SPI接口RAM不稳定,求助!
本帖最后由 willX 于 2016-1-26 23:18 编辑最近为一个项目做了一个SPI接口的RAM,由单片机发送命令、数据控制FPGA进行数据保存,或者数据读取。
因为考虑到稳定性和后续的方便维护,我使用了状态机(暂定保存和读取两种命令)。
现在的问题是,数据做回环检测,数据能够正常发送和接收,但是增加了状态机之后,就发现数据有小概率无法保存的问题。
具体问题是 : 对256字节RAM进行测试,按照地址依次保存数据,每写一字节就全部读取出来进行验证是否写进去了,这样需要进行256次验证,就发现总有大约10%的验证是失败的。数据没有写进去。
求教啊,如图所示的波形,该怎么写状态机好?
公司不允许联网,我晚上回去手写一份代码上传。感谢。
module spi_port(
input clk_25M,
input cs,
input sclk,
input mosi,
output miso,
);
assign clk_sys = clk_25M;
//==========1K SRAM的例化====================
reg ram_wdata = 8'd0;
wire ram_rdata;
reg ram_waddr = 10'd0;
reg ram_raddr = 10'd0;
reg ram_wen = 1'd0;
fifo u4 (
.data (ram_wdata),
.wraddress (ram_waddr),
.wrclock (sclk),
.wren (ram_wen), //wren高有效
.rdaddress (ram_raddr),
.rdclock (clk_25M),
.q (ram_rdata)
);
//=============SPI数据接收===================
reg bit_counter = 7; //接收到的数据“位”计数器
reg spidata_in = 0;
wire ri; //接收到8位数据后,应该发送系统内部信号,告诉别人数据已经完整接收到了
always @ (posedge sclk)
begin
if(!cs) //cs低有效,有数据传来
begin
bit_counter <= bit_counter - 1;
spidata_in <= mosi;
end
else
begin
bit_counter <= 7;
end
end
//采用组合电路输出ri信号,这样使得ri与接收字节同步,不会延迟一个时钟
assign ri = (bit_counter==7) ? 1 : 0;
//生成ri上升沿信号ri_pos
reg ri1, ri2;
wire ri_pos;
always @ (posedge clk_sys)
begin
ri1 <= ri;
ri2 <= ri1;
end
assign ri_pos = ( (ri1)&&(~ri2) ) ? 1 : 0;
//==================SPI数据发送====================
reg miso_r = 0;
reg spidata_out = 0;
always @ (posedge sclk)
begin
if(!cs)
miso_r <= spidata_out;
else
miso_r <= 1'bz;
end
assign miso = miso_r;
// //==================SPI数据回环测试=======================
// always @ (posedge sclk)
// begin
// if(ri_pos)
// spidata_out <= spidata_in;
// end
//=================状态机==========================
parameter IDLE = 4'd0;
parameter S1 = 4'd1;
parameter S2 = 4'd2;
parameter S3 = 4'd3;
parameter S4 = 4'd4;
parameter S5 = 4'd5;
parameter S6 = 4'd6;
parameter S7 = 4'd7;
//----------------发送状态机---------------------
//整个代码有两个状态机,一个接收命令字0x5A,地址,数据……负责将FPGA的SRAM上保存数据发送出去
reg send_cstate;
reg send_nstate;
reg ram_raddr_temp;
always @ (posedge ri or posedge cs) //使用 ri 信号作为状态机状态驱动
begin
if(cs == 1)
send_cstate <= IDLE;
else
send_cstate <= send_nstate;
end
always @ (*)
begin
send_nstate = IDLE;//复位
case(send_cstate)
IDLE : begin
if(ri_pos == 1)
begin
if(spidata_in == 8'h5A)
send_nstate = S1;
else
send_nstate = IDLE;
end
end
S1 : if(ri_pos == 1) send_nstate = S2;
S2 : if(ri_pos == 1) send_nstate = S3;
S3 : if(ri_pos == 1) send_nstate = S4;
S4 : if(ri_pos == 1) send_nstate = S5;
S5 : begin
if(cs == 0)
send_nstate = S5;
else
send_nstate = IDLE;
end
default: send_nstate = IDLE;
endcase
end
always @ (posedge clk_sys) //使用clk_25M时钟,作为状态机状态输出时钟
begin
if(ri_pos) //防止在固定状态下,输出数据随着spidata_in变化
case(send_nstate)
IDLE : begin spidata_out <= 8'h00; end
///*接到5A*/ S1 : begin ; end
///*接到A2*/ S2 : begin ; end
/*接到A1*/ S3 : begin ram_raddr_temp <= spidata_in; end
/*接到A0*/ S4 : begin ram_raddr <={ram_raddr_temp,spidata_in}; end
S5 : begin
spidata_out <= ram_rdata;
if(ri_pos)
ram_raddr <= ram_raddr + 1;
end
default : begin
spidata_out <= 8'h00;
end
endcase
end
//-------------------写入状态机------------------------
//这里是第二个状态机,负责接收0x5B命令字,地址,数据,用来保存单片机发给FPGA的数据
reg receive_cstate;
reg receive_nstate;
reg ram_waddr_temp;
always @ (posedge ri or posedge cs)
begin
if(cs)
receive_cstate <= IDLE;
else
receive_cstate <= receive_nstate;
end
always @ (*)
begin
// receive_nstate = IDLE;
case(receive_cstate)
IDLE : begin
if(ri_pos == 1)
begin
if(spidata_in == 8'h5B)
receive_nstate = S1;
else
receive_nstate = IDLE;
end
end
S1 : if(ri_pos == 1) receive_nstate = S2;
S2 : if(ri_pos == 1) receive_nstate = S3;
S3 : if(ri_pos == 1) receive_nstate = S4;
S4 : if(ri_pos == 1) receive_nstate = S5;
S5 : if(ri_pos == 1) receive_nstate = S6;
S6 : begin
if(!cs)
receive_nstate = S6;
else
receive_nstate = IDLE;
end
default : receive_nstate = IDLE;
endcase
end
always @ (posedge clk_sys)
begin
if(ri_pos)
case(receive_nstate)
IDLE : begin
ram_wen <= 0;
end
// S1 : ;
// S2 : ;
S3 : ram_waddr_temp <= spidata_in;
S4 : ram_waddr <= {ram_waddr_temp, spidata_in};
S5 : begin
ram_wen <= 1;
ram_wdata <= spidata_in;
end
S6 : begin
ram_wen <= 1;
ram_wdata <= spidata_in;
ram_waddr <= ram_waddr + 1;
end
default : begin
ram_wen <= 0;
ram_waddr <= 0;
end
endcase
end
以上的代码,经过测试发现,在少量的读写过程中(比如2、 5 字节测试)数据是能够正常保存和读取的。但是一旦使用大量数据进行读写后,发现数据有部分虽然写入了,但是读取到的是旧值。 楼主,你时序图用什么软件画的? fy024 发表于 2016-1-26 15:54
楼主,你时序图用什么软件画的?
看顶端有的 本帖最后由 willX 于 2016-1-26 23:22 编辑
fy024 发表于 2016-1-26 15:54
楼主,你时序图用什么软件画的?
用的TimeGen软件,是另一个帖子里的分享,参见:http://www.amobbs.com/thread-5530733-1-1.html
同时感谢“风波邪人”的推荐,真的是一个非常好用的软件。压缩包里有快速上手视频,确实很快,看3分钟,就知道怎么用TimeGen软件了。
页:
[1]