【芯片设计】异步电路碎碎念(八)多比特握手同步器

乐活   2024-10-11 12:26   北京  

DMUX同步器完成之后继续完成多比特握手同步器,异步时钟域的握手同步器典型结构不只一种,还区分全握手和半握手,这里面的门道我也不是很专业,所以就只把熟悉的结构拿出来大家一起看下就好了。握手同步器的结构简图是这样的:

这个结构简图里还可以补充两个信号,分别是目的时钟域的输出数据有效信号和源始终与的ready信号,一会在代码中我们尝试加一下。这个结构的核心是一句话,当in_enable信号为高时开始进行数据同步,当数据同步没有完成前,tx_sel信号是不会落下去。

代码实现

那么通过代码来看,先来明确顶层:

module async_nbit_hand #(
parameter DL = 2,
parameter WD = 1,
parameter FF = 1
)( /*AUTOARG*/
// Outputs
i_ready, o_data, o_en,
// Inputs
i_clk, i_rst_n, i_data, i_en, o_clk, o_rst_n
);

// ----------------------------------------------------------------
// Interface declare
// ----------------------------------------------------------------
input i_clk;
input i_rst_n;
input [WD -1:0]i_data;
input i_en;
output i_ready;

input o_clk;
input o_rst_n;
output[WD -1:0]o_data;
output o_en;
同样的,如果FF不为0时需要打拍,以i_data_in和i_en_in作为真实的跨异步输入信号:
// ----------------------------------------------------------------
// i_data dff
// ----------------------------------------------------------------
wire [WD -1:0]i_data_in;
wire i_en_in;
generate
if(FF == 0)begin: NO_IN_DFF
assign i_data_in = i_data;
assign i_en_in = i_en;
end //if(FF == 0)begin: NO_IN_DFF
else begin: IN_DFF
reg [WD -1:0]i_data_ff;
reg i_en_ff;
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)begin
i_data_ff <= {WD{1'b0}};
i_en_ff <= 1'b0;
end
else begin
i_data_ff <= i_data;
i_en_ff <= i_en;
end
end
assign i_data_in = i_data_ff;
assign i_en_in = i_en_ff;
end //else begin: IN_DFF
endgenerate
之后做TX侧的逻辑,对着结构图做可以了:
// ----------------------------------------------------------------
// tx enable logic
// ----------------------------------------------------------------
wire tx_en, tx_sel;
wire rx_sel, rx_sel_sync;
reg tx_en_ff;

assign tx_en = (i_en_in || tx_sel) && (!rx_sel_sync);
assign tx_sel = tx_en_ff;
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)
tx_en_ff <= 1'b0;
else
tx_en_ff <= tx_en;
end
TX和RX之间的同步器:
// ----------------------------------------------------------------
// tx_sel rx_sel async
// ----------------------------------------------------------------
async_1bit_delay #(.DL(DL), .FF(0))
u_tx_sel_sync(
.i_clk (i_clk),
.i_rst_n (i_rst_n),
.i_data (tx_sel),
.o_clk (o_clk),
.o_rst_n (o_rst_n),
.o_data (rx_sel)
);

async_1bit_delay #(.DL(DL), .FF(0))
u_rx_sel_sync(
.i_clk (o_clk),
.i_rst_n (o_rst_n),
.i_data (rx_sel),
.o_clk (i_clk),
.o_rst_n (i_rst_n),
.o_data (rx_sel_sync)
);
RX侧的逻辑,同样对照着结构图做就可以,在这里我补充一个rx_sel_pulse标记rx_sel的上升沿:
// ----------------------------------------------------------------
// rx_sel_pulse
// ----------------------------------------------------------------
reg rx_sel_ff, rx_sel_pulse_ff;
wire rx_sel_pulse;

always @(posedge o_clk or negedge o_rst_n) begin
if(!o_rst_n)
rx_sel_ff <= 1'b0;
else
rx_sel_ff <= rx_sel;
end

assign rx_sel_pulse = (rx_sel_ff == 1'b0) && (rx_sel == 1'b1);

always @(posedge o_clk or negedge o_rst_n) begin
if(!o_rst_n)
rx_sel_pulse_ff <= 1'b0;
else
rx_sel_pulse_ff <= rx_sel_pulse;
end
控制信号结束,后面是数据信号采样,这里跟结构有一些不同,我把输入数据在源时钟域通过使能信号寄存了:
// ----------------------------------------------------------------
// i_data sample
// ----------------------------------------------------------------
reg [WD -1:0]i_data_lock;
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)
i_data_lock <= {WD{1'b0}};
else if(i_en_in && !tx_sel)
i_data_lock <= i_data_in;
end

reg [WD -1:0]i_data_sync;
always @(posedge o_clk or negedge o_rst_n) begin
if(!o_rst_n)
i_data_sync <= {WD{1'b0}};
else if(rx_sel)
i_data_sync <= i_data_lock;
end
最后的部分就是输出逻辑,这里有三个输出:o_data、o_en和i_ready:
// ----------------------------------------------------------------
// out logic
// ----------------------------------------------------------------
assign o_en = rx_sel_pulse_ff;
assign o_data = i_data_sync;

generate
if(FF == 0)begin: NO_IN_DFF_RD
assign i_ready = (tx_sel == 1'b0) && (rx_sel_sync == 1'b0);
end
else begin: IN_DFF_RD
assign i_ready = (tx_sel == 1'b0) && (rx_sel_sync == 1'b0) && (i_en_in == 1'b0);
end
endgenerate

endmodule

波形分析

握手同步器面对的主场景是脉冲使能的多比特数据,在脉冲信号有效时,输入数据被锁存:

而因为在锁存时的逻辑包含了i_ready,所以即使连续脉冲到来,也只会锁存i_ready有效时的数:

而后看TX->RX控制逻辑链i_en_in -> tx_sel -> rx_sel:

于是在目的时钟域,rx_sel_pulse时就可以采样在源始终域被寄存的数据i_data_lock了:

同时将rx_sel_pulse打一拍作为输出信号的使能:

同时,rx_sel信号要同步回去,因为在rx_sel上升沿时就已经把数据采样,因此rx_sel同步回去并取反告诉源时钟域已经同步完成了可以处理下一个值。rx_sel_sync会将tx_en置0,而后tx_sel也被置0,同步后rx_sel也归0,完成一次握手反馈的传输:

当rx_sel_sync也为0后,一次完整的握手同步宣告完成,那么i_ready就可以置1:

i_ready为1后可以进行下一次传输:

需要注意的是,如果入口进行了打拍操作,i_ready的逻辑需要适当调整:

generate
if(FF == 0)begin: NO_IN_DFF_RD
assign i_ready = (tx_sel == 1'b0) && (rx_sel_sync == 1'b0);
end
else begin: IN_DFF_RD
assign i_ready = (tx_sel == 1'b0) && (rx_sel_sync == 1'b0) && (i_en_in == 1'b0);
end
endgenerate

此时i_ready会看i_en_in信号即i_en_ff,不会和i_en信号产生逻辑环。

系列文章入口——

【芯片设计】SoC 101(一):绪论
【芯片设计】FIFO漫谈(零)从无处不在的FIFO开始说起
【芯片设计】计算机体系结构(一)虚拟内存

【芯片设计】深入理解AMBA总线(零)绪论

【芯片设计】握手协议的介绍与时序说明
【芯片设计】复位那些小事 —— 复位消抖
【芯片设计】快速入门数字芯片设计(一)Introduction
【芯片验证】UVM源码计划(零)下定决心读源码前的自测环节
【芯片设计】异步电路碎碎念(一) 到底什么是异步电路
【芯片设计】从RTL到GDS(一):Introduction
其他文章链接——
【芯片验证】sva_assertion: 15道助力飞升的断言练习
【芯片验证】可能是RTL定向验证的巅峰之作
【芯片验证】RTL仿真中X态行为的传播 —— 从xprop说起
【芯片验证】年轻人的第一个systemVerilog验证环境全工程与解析
【芯片设计】verilog中有符号数和无符号数的本质探究
【芯片设计】论RTL中always语法的消失术
【芯片设计】代码即注释,注释即代码
【芯片设计】700行代码的risc处理器你确实不能要求太多了
入职芯片开发部门后,每天摸鱼之外的时间我们要做些什么呢
如何计算系统的outstanding 和 burst length?
芯片搬砖日常·逼死强迫症的关键词不对齐事件
熟人社会里,一群没有社会价值的局外人


芯时代青年
专心数字前端全流程,芯时代有为青年的自我修养
 最新文章