willX 发表于 2016-1-26 15:24:25

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:15

楼主,你时序图用什么软件画的?

NJ8888 发表于 2016-1-26 16:02:51

fy024 发表于 2016-1-26 15:54
楼主,你时序图用什么软件画的?

看顶端有的

willX 发表于 2016-1-26 23:20:53

本帖最后由 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]
查看完整版本: verilog 状态机实现SPI接口RAM不稳定,求助!