赞
踩
------热爱生活 热爱时钟 热爱逻辑------
在面试的时候面试官谈到如何解决跨时域的问题,作为一个初学者只知道用异步FIFO来实现同步。然后在博客里看了一些文章,有一些自己的理解记在这里,肯定有不妥之处,仅仅写一写自己的想法。
复杂的握手协议后期再工作中继续学习会给出自己的理解,目前水平有限,就只有写出简单的握手协议。下图是自己对握手协议的理解:
图1.1 握手协议模型图
如上图所示,两个时域之间通过请求和应答信号线进行握手,时域A发送请求发送数据信号req,同时是准备好数据;时域B接收到时域A发送的请求信号后,回应一个应答信号ack,同时将数据接收进行寄存;时域A接收到应答信号后重新发送请求信号req,进行第二个数据传输,依次直到完成时域A数据发送完成。
但是要注意程序设计,不正确的程序设计将会发生数据漏取(快时域到慢时域)或者插入数据(慢时域到快时域)。这种握手信号设计是以牺牲时钟为代价而进行的设计,因为跨时域涉及到亚稳态,亚稳态的重要性以及解决方法在其他博文里有详细的解释,请大家自行搜索,本文在接收数据时采用了两级寄存,以保证数据传输的准确性。下面将详细叙述我的程序设计方案(Verilog HDL)。
在本实际案列中使用100mHz频率作为快时域A,A时域控制ROM地址变化,读取ROM的数据,发送到慢时域B,B时域使用50mHz接收时域A的数据。
图2.1 架构设计
A时域检测应答信号 ack为低电平时拉高发送请求信号req(高有效),发送数据,A时域检测到应答信号ack上升沿到来时则拉低请求信号req。
B时域检测到 A时域 req请求信号上升沿到来时拉高应答信号ack,接收数据。
设计顶层模块进行仿真测试。
//----跨时域顶层 仿真使用---- module time_top( input clk, //输入系统时钟50mHz input rst_n, output [7:0]data_o, ); wire clk_100m; wire rst_en; wire ack1; wire [7:0]data_r; wire req1; wire [7:0]addr; wire [7:0]data; //----10mHz频率产生---- my_pll inst_my_pll( .areset(!rst_n), .inclk0(clk), .c0(clk_100m), .locked(rst_en) ); //----ROM 模块 ---- my_rom inst_my_rom( .address(addr), .clock(clk_100m), .q(data) ); //----快时域A---- transmit_100m transimt( .clk(clk_100m), .rst_n(rst_en), .ack(ack1), .data_in(data), .data_out(data_r), .req(req1), .addr(addr) ); //----慢时域B---- receive_50m recive( .clk(clk), .rst_n(rst_n), .req(req1), .data_in(data_r), .data_out(data_o), .ack(ack1) ); endmodule
//---快时域 100mHz 发送ROM中的数数据---- module transmit_100m( input clk, //100mHz input rst_n, //低电平复位 input ack, //应答信号 input [7:0]data_in, //数据收入 output [7:0]data_out, //输出数据 output reg req, //发送请求信号 output reg [7:0]addr //ROM地址 ); //-----接收应答信号并寄存---- reg ack_r1; reg ack_r2; reg ack_r3; always @ (posedge clk or negedge rst_n) begin if(!rst_n)begin ack_r1<=0; ack_r2<=0; ack_r3<=0; end else begin ack_r1<=ack; ack_r2<=ack_r1; ack_r3<=ack_r2; end end //----两级寄存,原理请参考其他博文----- wire pos_ack1; wire pos_ack2; assign pos_ack1=ack_r1 & (~ack_r2); //检测ack信号上升沿 assign pos_ack2=ack_r2 & (~ack_r3); //ack信号上升沿延时一个周期 //----产生发送数据请求信号----- always @ (posedge clk or negedge rst_n) begin if(!rst_n)begin req<=0; end else begin if(ack==0)begin //应答信号为低电平时表示数据总线空闲,可以接收数据 req<=1; //如果外部控制 req,可引入使能信号,使能信号有效时req拉高 end else if(pos_ack2)begin //检测到ack应答信号上升沿,请求信号拉低,停止发送数据 req<=0; end end end //----请求信号下降沿检测,当请求信号下降沿到来时,ROM地址加一,为下一次发送数据做好准备---- reg req1; reg req2; wire neg_req; always @ (posedge clk or negedge rst_n) begin if(!rst_n)begin req1<=0; req2<=0; end else begin req1<=req; req2<=req1; end end assign neg_req=req2&(~req1); //检测请求信号req下降沿 //----发送数据状态设计---- reg [1:0]state; reg [7:0]data_r; //数据寄存器,对输入数据进行寄存 parameter idle=2'd0; parameter transimt=2'd1; always @ (posedge clk or negedge rst_n) begin if(!rst_n)begin state<=idle; addr<=8'd0; end else begin case(state) idle : begin if(req)begin //发送数据请求信号到来,则接收输入数据 state<=transimt; data_r<=data_in; end else begin state<=idle; end end transimt : begin if(neg_req)begin //请求信号下降沿到来,地址加1,为下一次发送数据做好准备 addr<=addr+1; state<=idle; end else begin state<=transimt; end end default : state<=idle; endcase end end assign data_out=data_r; //将寄存器的数据输出 endmodule
//----慢时域接收数据---- module receive_50m( input clk,//50m input rst_n, input req, input [7:0]data_in, output [7:0]data_out, output reg ack ); //----寄存数据请求信号---- reg req_r1; reg req_r2; reg req_r3; always @ (posedge clk or negedge rst_n) begin if(!rst_n)begin req_r1<=0; req_r2<=0; req_r3<=0; end else begin req_r1<=req; req_r2<=req_r1; req_r3<=req_r2; end end wire pos_req1; wire pos_req2; assign pos_req1=req_r1 & (~req_r2);//检测req信号上升沿 assign pos_req2=req_r2 & (~req_r3);//两级寄存 //----进行接收数据---- reg [7:0]data_r;//数据寄存 always @ (posedge clk or negedge rst_n) begin if(!rst_n)begin data_r<=0; end else if(pos_req1) data_r<=data_in; end //----应答信号产生---- always @ (posedge clk or negedge rst_n) begin if(!rst_n)begin ack<=0; end else begin if(pos_req2) //检测到 req信号的上升沿,则进行数据接收 ack<=1; else if(!req) //当检测到 req信号拉底后,应答信号拉低 ack<=0; end end assign data_out=data_r; endmodule
------热爱生活 热爱时钟 热爱逻辑------
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。