取时钟线的下降沿时的数据错位问题解决
在项目中,涉及到不同时钟域通信的时候,往往要在时钟边沿时刻取数据线的值,但是如果数据线上的数据持续时间比较短,取时钟沿之后将无法捕捉到数据线上的数据,此时读取的数据都是错误的数据。此时可以把数据线上的数据也相应的延迟几个时钟周期,则就可以读到正确的数据。
例:
读不到正确的数据的例程:
程序:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
module testedge( clk, rst_n, sclk, sdat, dataout, flag ); input clk; input rst_n; input sclk; input sdat; output [7:0] dataout; output flag; reg flag; reg [7:0] dataout; //------------------------------------- /* 取边沿时刻 */ reg sclk_1; reg sclk_2; reg sclk_3; wire sclk_pos; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin {sclk_3,sclk_2,sclk_1} <= 3'd0; end else begin {sclk_3,sclk_2,sclk_1} <= {sclk_2,sclk_1,sclk}; end end assign sclk_pos = ~sclk_3&sclk_2; reg [2:0] n; reg [2:0] state; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin dataout <= 0; flag <= 0; state <= 0; n <= 0; end else begin case(state) 'd0: begin if(sclk_pos) begin dataout[n] <= sdat; state <= 'd1; end end 'd1: begin if(n == 7) begin n <= 0; state <= 'd2; end else begin n <= n + 1; state <= 'd0; end end 'd2: begin flag <= 1; state <= 'd3; end 'd3: begin flag <= 0; state <= 'd0; end default:state <= 'd0; endcase end end endmodule
仿真程序:
发送的数据为8'h55;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :.v ** CreateDate :2015. ** Funtions : ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content: *******************************************************************************/ module testedg_tb; reg clk; reg rst_n; reg sclk; reg sdat; wire [7:0] dataout; wire flag; testedge testedge_1( .clk, .rst_n, .sclk, .sdat, .dataout, .flag ); parameter tck = 24; parameter t = 1000/tck; always #(t/2) clk = ~clk; task send; input [7:0] datain; begin #(5*t) sclk = 0; #(2*t) sdat = datain[0]; #(2*t) sclk = 1; #(2*t) sclk = 0; sdat = datain[1]; #(2*t) sclk = 1; #(2*t) sclk = 0; sdat = datain[2]; #(2*t) sclk = 1; #(2*t) sclk = 0; sdat = datain[3]; #(2*t) sclk = 1; #(2*t) sclk = 0; sdat = datain[4]; #(2*t) sclk = 1; #(2*t) sclk = 0; sdat = datain[5]; #(2*t) sclk = 1; #(2*t) sclk = 0; sdat = datain[6]; #(2*t) sclk = 1; #(2*t) sclk = 0; sdat = datain[7]; #(2*t) sclk = 1; #(2*t) sclk = 0; sdat = 0; end endtask initial begin clk = 0; rst_n = 0; sclk = 0; sdat = 0; #(6*t) rst_n = 1; #(6*t) send(8'h55); end endmodule
仿真图:
数据线上加入延时之后可以读到正确的数据:
程序:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
module testedge( clk, rst_n, sclk, sdat, dataout, flag ); input clk; input rst_n; input sclk; input sdat; output [7:0] dataout; output flag; reg flag; reg [7:0] dataout; //------------------------------------- /* 取边沿时刻 */ reg sclk_1; reg sclk_2; reg sclk_3; wire sclk_pos; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin {sclk_3,sclk_2,sclk_1} <= 3'd0; end else begin {sclk_3,sclk_2,sclk_1} <= {sclk_2,sclk_1,sclk}; end end assign sclk_pos = ~sclk_3&sclk_2; //---------------------------------------------- /* 添加部分 */ reg sdat_1; reg sdat_2; reg sdat_3; wire sdat_flag; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin {sdat_3,sdat_2,sdat_1} <= 3'd0; end else begin {sdat_3,sdat_2,sdat_1} <= {sdat_2,sdat_1,sdat}; end end assign sdat_flag = sdat_3; //---------------------------------------- reg [2:0] n; reg [2:0] state; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin dataout <= 0; flag <= 0; state <= 0; n <= 0; end else begin case(state) 'd0: begin if(sclk_pos) begin dataout[n] <= sdat_flag; /* 修改为延时之后的输入数据 */ state <= 'd1; end end 'd1: begin if(n == 7) begin n <= 0; state <= 'd2; end else begin n <= n + 1; state <= 'd0; end end 'd2: begin flag <= 1; state <= 'd3; end 'd3: begin flag <= 0; state <= 'd0; end default:state <= 'd0; endcase end end endmodule
仿真程序同上。
仿真图:
注:并非所有情况都要这么做,视情况而定,而这样做的好处是更严谨。