首页 > 编程知识 正文

FPGA跨时钟域,FPGA时钟域

时间:2023-05-03 15:19:42 阅读:273646 作者:3629

一种解决总线同步问题的方法是使用一个保持寄存器和握手信号”,这也就是“先异步暂存,后同步写入”的方法

下面介绍握手方式进行异步时钟域的通信。

所谓握手,即通信双方使用了专用控制信号进行状态指示。这个控制信号既有发送域给接收域的,也有接收域给发送域的,有别于的单向控制信号检测方式。

使用握手协议方式处理跨时钟域数据传输,只需要对双方的握手信号(req和ack)分别使用脉冲检测方法进行同步。在具体 实现中,假设req、ack、data总线在初始化时都处于无效状态,发送域先把数据放入总线,随后发送有效的req信号给接收域。接收域在检测到有效的req信号后 锁存数据总线,然后回送一个有效的ack信号表示读取完成应答。发送域在检测到有效ack信号后撤销当前的req信号,接收域在检测到req撤销后也相应撤销ack信号,此时完成一次正常 握手通信。此后,发送域可以继续开始下一次握手通信,如此循环。该方式能够使接收到的数据稳定可靠,有效的避免了亚稳态的出现,但控制信号握手检测会消耗通信双方较多的时间。

我分别编写了发送时钟域和接收时钟域的代码进行测试,用到两组MEM,以便于观察实验结果,进一步加深对基本握手方式解决跨时钟域问题的认识。

发送端代码:

//接收域应答信号ack采用两级寄存器同步,便于时序收敛always @ (negedge rst_n or posedge t_clk)begin  if(rst_n == 1'b0)  begin     ack_reg1 <= 1'b0;     ack_reg2 <= 1'b0;  end  else  begin     ack_reg1 <= ack;     ack_reg2 <= ack_reg1;  endend

//数据处理状态机

always @ (negedge rst_n or posedge t_clk)begin  if(rst_n == 1'b0)  begin     data_buf <= 'b0;     tr_state <= TR_IDLE;     TR_MEM_Addr <= 0;  end  else     case(tr_state)     TR_IDLE :     //初始化状态           begin        req <= 1'b0;        TR_MEM_Addr <= 0;        tr_state <= SND_DATA_REQ;     end     SND_DATA_REQ: //送数据到总线上和发送请求信号          begin        data_buf <= TR_MEM[TR_MEM_Addr];        req <= 1'b1;//发送请求信号,请求信号为高,表示请求接收        TR_MEM_Addr <= TR_MEM_Addr + 1;        tr_state <= CHK_ACK_ACTIVE;     end     CHK_ACK_ACTIVE ://检测应答信号为高,释放请求信号     begin        if(ack_reg2 == 1'b1)  //便于时序收敛        begin           req <= 1'b0;        //释放请求信号           tr_state <= CHK_COMM_END;        end        else          tr_state <= CHK_ACK_ACTIVE;     end     CHK_COMM_END :   //检测握手通信结束,如果应答信号被释放,一次握手结束                        begin        if(ack_reg2 == 1'b0)           tr_state <= SND_DATA_REQ;        else           tr_state <= CHK_COMM_END;     end     default : tr_state <= TR_IDLE;         endcaseendassign dout = data_buf;

接收端代码:

//发送域请求信号req采用两级寄存器同步,便于时序收敛always @ (negedge rst_n or posedge r_clk)begin  if(rst_n == 1'b0)  begin     req_reg1 <= 1'b0;     req_reg2 <= 1'b0;  end  else  begin     req_reg1 <= req;     req_reg2 <= req_reg1;  endend

//数据处理状态机

always @ (negedge rst_n or posedge r_clk)begin  if(rst_n == 1'b0)  begin     re_state <= RE_IDLE;     ack <= 1'b0;     RE_MEM_Addr <= 0;  end  else     case(re_state)     RE_IDLE :           //初始化状态     begin        RE_MEM_Addr <= 0;        re_state <= CHK_REQ_ACTIVE;     end     CHK_REQ_ACTIVE :    //检测发送端的数据发送请求信号     begin       if(req_reg2 == 1'b1)     //如果有请求信号,接收数据        begin           RE_MEM[RE_MEM_Addr] <= din;//接收数据存放到MEM           ack <= 1'b1;//检测到请求信号,发送接收端应答信号          RE_MEM_Addr <= RE_MEM_Addr + 1;   //接收数据存储器地址累加           re_state <= CHK_REQ_RELEASE;        end        else           re_state <= CHK_REQ_ACTIVE;     end     CHK_REQ_RELEASE :     //检测请求信号释放,释放应答信号     begin        if(req_reg2 == 1'b0)        begin           ack <= 1'b0;  //释放应答信号,一次握手通信结束           re_state <= CHK_REQ_ACTIVE;        end        else           re_state <= CHK_REQ_RELEASE;     end     default : re_state <= RE_IDLE;  endcaseend

具体方案 : 假设req,ack,data在初始化时都处于无效状态, 发送域先把数据放入总线,随后发送有效的req信号给接收域. 接收域在检测到有效的req信号后锁存数据总线,然后回送一个有效的ack信号表示读取完成应答. 发送域在检测到有效ack信号后撤销当前的req信号, 接收域在检测到req撤销后也相应撤销ack信号, 此时完成一次正常握手通信. 此后,发送域可以继续开始下一次握手通信.

验证 : 经过验证此模块可以实现数据从快时钟域发送至慢时钟域(发送端时钟:120M,接收端时钟:1M), 也可以实现数据从慢时钟域发送至快时钟域(发送端时钟:1M,接收端时钟120M),两个MEM中的数据完全一致。

1.本文部分素材来源网络,版权归原作者所有,如涉及作品版权问题,请与我联系删除。

2.未经原作者允许不得转载本文内容,否则将视为侵权;

3.转载或者引用本文内容请注明来源及原作者;

4.对于不遵守此声明或者其他违法使用本文内容者,本人依法保留追究权等。

下面是我的个人微信公众号,关注【一个早起的程序员】精彩系列文章每天不断。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。