一、握手toggle
握手又称结绳法,适用场景为数据有效信号在数据信号前一个周期或数据有效信号在数据的第一个周期,且在握手期间,数据需要保持不变,直到源时钟域收到了解绳的有效信号。
源码如下:
module toggle(
input wire clka,
input wire clkb,
input wire rst_n,
input wire a_en,
input wire [7:0] data_a_in,
output reg [7:0] data_b_out,
output wire b_en,
output wire ack_a
);
reg a_en_d1;
reg a_en_d2;
wire a_en_neg;
reg a_req;
reg req_d1;
reg req_d2;
reg ack_d1;
reg ack_d2;
wire ack_pos;
always@(posedge clka or negedge rst_n)begin
if(!rst_n)begin
a_en_d1 <= 1'b0;
a_en_d2 <= 1'b0;
end
else begin
a_en_d1 <= a_en;
a_en_d2 <= a_en_d1;
end
end
assign a_en_neg = a_en_d2 && (~a_en_d1);
always@(posedge clka or negedge rst_n)begin
if(!rst_n)
a_req <= 1'b0;
else if(a_en_neg)
a_req <= 1'b1;
else if(ack_pos)
a_req <= 1'b0;
end
always@(posedge clkb or negedge rst_n)begin
if(!rst_n)begin
req_d1 <= 1'b0;
req_d2 <= 1'b0;
end
else begin
req_d1 <= a_req;
req_d2 <= req_d1;
end
end
always@(posedge clka or negedge rst_n)begin
if(!rst_n)begin
ack_d1 <= 1'b0;
ack_d2 <= 1'b0;
end
else begin
ack_d1 <= req_d2;
ack_d2 <= ack_d1;
end
end
assign b_en = (~req_d2) && (req_d1);
assign ack_pos = (~ack_d2) && ack_d1;
assign ack_a = ack_d2;
always@(posedge clkb or negedge rst_n)begin
if(!rst_n)
data_b_out <= 8'd0;
else if(b_en)
data_b_out <= data_a_in;
end
endmodule
testbench如下:
`timescale 1ns/1ns
module toggle_tb();
reg clka;
reg clkb;
reg rst_n;
reg a_en;
reg [7:0] data_a_in;
wire [7:0] data_b_out;
wire b_en;
wire ack_a;
always #5 clka = ~clka;
always #10 clkb = ~clkb;
initial begin
rst_n = 1'b0;
clka = 1'b0;
clkb = 1'b0;
#11;
rst_n = 1'b1;
#10000;
$finish;
end
reg ack_a_d1;
always@(posedge clka or negedge rst_n)begin
if(!rst_n)begin
a_en <= 1'b1;
ack_a_d1 <= 1'b0;
end
else begin
ack_a_d1 <= ack_a;
a_en <= (~ack_a_d1) && ack_a;
end
end
always@(posedge clka or negedge rst_n)begin
if(!rst_n)
data_a_in <= 8'd0;
else if(a_en)
data_a_in <= {$random}%256;
end
toggle toggle_inst(
.clka (clka),
.clkb (clkb),
.rst_n (rst_n),
.a_en (a_en),
.data_a_in (data_a_in),
.data_b_out (data_b_out),
.b_en (b_en),
.ack_a (ack_a)
);
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars;
end
endmodule
仿真结果如下:
其中G1中为源时钟域信号,G2为目的时钟域信号
二,DMUX
原理图如下:
DMUX适用于慢时钟域向快时钟域传递(因为两级同步器),且数据有效信号在数据信号的前一个周期或者数据有效信号在数据信号的第一个周期。数据信号要保证在有效信号同步期间保持不变。
源代码如下:
module dmux(
input wire clka,
input wire clkb,
input wire a_en,
input wire rst_n,
input wire [7:0] data_in,
output reg [7:0] data_out
);
reg a_en_d1;
reg a_en_d2;
always@(posedge clkb or negedge rst_n)begin
if(!rst_n)begin
a_en_d1 <= 1'b0;
a_en_d2 <= 1'b0;
end
else begin
a_en_d1 <= a_en;
a_en_d2 <= a_en_d1;
end
end
always@(posedge clkb or negedge rst_n)begin
if(!rst_n)
data_out <= 8'd0;
else if(a_en_d2)
data_out <= data_in;
end
endmodule
testbench如下:
`timescale 1ns/1ns;
module dmux_tb();
reg clka;
reg clkb;
reg rst_n;
reg a_en;
reg [7:0] data_in;
wire [7:0] data_out;
always #10 clka = ~clka;
always #5 clkb = ~clkb;
initial begin
clka = 1'b0;
clkb = 1'b0;
rst_n = 1'b0;
a_en = 1'b0;
data_in = 8'd0;
#11;
rst_n = 1'b1;
@(posedge clka)
a_en = 1'b1;
data_in = {$random} %256;
@(posedge clka)
a_en = 1'b0;
#33;
@(posedge clka)
a_en = 1'b1;
@(posedge clka)
a_en = 1'b0;
data_in = ($random)%256;
#100;
$finish;
end
dmux dmux_inst(
.clka (clka),
.clkb (clkb),
.rst_n (rst_n),
.a_en (a_en),
.data_in (data_in),
.data_out (data_out)
);
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars;
end
endmodule
仿真结果如下:
可以清晰看到两种情况。