1.多bit数据跨时钟域的处理方式
跨时钟域:一颗芯片上会有许多不同的信号工作在不同的时钟频率下,不同时钟(频率)下工作的信号往往需要相互沟通和传递信号。
单bit数据:发送方给出数据,接收方用本地时钟同步打两拍再使用;(慢时钟域到快时钟域);快时钟域的信号脉宽较窄,慢时钟域不一定能采到,可以通过握手机制让窄脉冲展宽,慢时钟域采集到信号后再“告诉”快时钟域已经采集到信号,确保能采集到;
多bit数据:多bit跨时钟域不能简单使用打两拍,打拍后可能数据错乱;异步FIFO(发送方把数据写到异步FIFO,接收方从异步FIFO里读出),格雷码(对于连续变化的信号,发送方转为格雷码发送,接收方收到后再转为二进制),握手信号(发送方给出数据,发送方给出握手请求,接收方收到后回复,发送方撤销数据)
2.电路最大时钟频率表达式
求最大时钟频率,就是分析每个DFF的setup time;
step1:求数据到达D端的时间;即时钟的延迟加上触发器CK到Q端的延迟再加上组合逻辑的延迟
step2:求数据所需时间 ;数据到达的最晚时间点。一个时钟周期时间加上时钟信号到达触发器时钟输入端的延迟时间减去触发器的建立时间
step3:建立时间裕量 Tsetup_slack = step2 - step1 >= 0
下图非常生动形象,所以放这里供自己复习时看(Tmargin设计裕量,倒数为最大频率)
后续还提问说如果考虑寄存器R1时钟上升沿比寄存器R2时钟时钟delay到,没怎么看清题,留个坑,以后学。
3.格雷码在异步电路中的应用
格雷码主要应用于FIFO等需要判断空满的设计以及一些对误码率和出现亚稳态率较低的设计中。格雷码是一种安全码,因为相邻的格雷码只有一位不同,和二进制不同,二进制一般相邻的都有多位不同。格雷码在传输中,因为相邻只有一位不同,所以其误码率比二进制低得多。
拙见:简单意思就是每次只改变一个位置的值,而二进制每次进位会有2个位置的值发生改变。这样可以使设计简单,而且不容易出错。属于数电基础知识内容,看下图就懂了
异步FIFO使用格雷码的唯一目的就是即使在亚稳态进行读写指针抽样也能进行正确的空满状态判断。
在异步FIFO中,写操作和读操作工作在不同的时钟域,需要跨时钟域来传递FIFO的空满状态信息。一般是通过传递读写的地址来实现空满状态控制的。
格雷码应用:可以在将地址跨越时钟域之前,先转换成格雷码,然后跨越时钟域。到达另一时钟域后,通过两级寄存器消除亚稳态,再将格雷码转换成二进制数。这样,每次地址变化,跨越时钟域时只有一位可能出错,可以大幅降低出错概率,提高FIFO运行的可靠性。
4.用verilog编写深度为16的同步时钟FIFO,8bit数据位宽,同时会产生empty,full信号
【Verilog】同步FIFO原理及verilog实现(参数化)_子墨祭的博客-CSDN博客
具体看这个博客就行,写的很清楚了(这个每次都考,我遇到3次了,必须手撕!!!)
5.CMOS功耗
CMOS电路主要有动态功耗和静态功耗组成,动态功耗又分为开关功耗、短路功耗两部分;
当CMOS反相器从一种稳定工作状态突然转变到另一种稳定状态的过程中,将产生附加的功耗,我们称之为动态功耗。
动态功耗分为两部分:一部分是对负载电容充放电所消耗的功率Pc(开关功耗)
电路在开关过程中对输出节点的负载电容充放电所消耗的功耗。
另一部分是由于两个MOS管T1和T2在短时间内同时导通所消耗的瞬时导通功率Pt(短路功耗)
由于输入电压波形并不是理想的阶跃输入信号,有一定的上升时间和下降时间,在输入波形上升下降的过程中,在某个电压输入范围内,NMOS和PMOS管都导通,这时就会出现电源到地的直流导通电流,这就是开关过程中的短路功耗。
漏电流引起的功耗称为静态功耗。近似为零
6.搭建时钟二分频电路,占空比1:3,画出原理图
0基础介绍如下,讲的很好:
FPGA分频器设计(偶数分频、奇数分频)_孤独的单刀的博客-CSDN博客_奇数分频器
个人提炼——偶数分频:2分频就是使用基准时钟在第一个周期全输出高电平(或低电平),第二个周期相反。把基准时钟的2个周期变为1个周期
(1)时钟6分频电路1:1占空比,代码及仿真结果
//6分频电路设计50%占空比
`timescale 1ns/1ns //时间刻度:单位1ns,精度1ns
module divider_6 //模块名
(
input sys_clk, //时钟(设定为 50MHz)
input sys_rst_n, //复位信号(n 表示低电平有效)
output reg clk_6 //输出6分频信号
);
reg [1:0] cnt; //reg 定义
//计数模块
//从0计数到2共计3个时钟周期
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt <= 2'd0; //复位清零
else if(cnt == 2'd2) //从0开始计数,所以需要 -1
cnt <= 2'd0; //计满则清零
else
cnt <= cnt + 2'd1; //没记满就一直计数
end
//6分频时钟输出模块
//满足计数条件则对2分频时钟进行反转
//6分频时钟每隔3个周期反转一次,所以6分频的周期即为6个时钟周期
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
clk_6 <= 1'b0; //复位清零
else if(cnt == 2'd2) //记满3个时钟周期
clk_6 <= ~clk_6; //计满则输出反转
else
clk_6 <= clk_6; //没记满就保持原来状态
end
endmodule
module tb(); //仿真模块
//输入reg 定义
reg sys_clk;
reg sys_rst_n;
//输出wire定义
wire clk_6;
//例化被测试模块
divider_6 dut
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_6 (clk_6 )
);
initial begin
sys_clk <= 0;
forever begin
#5 sys_clk <= !sys_clk;
end
end
initial begin
sys_rst_n <= 0;
repeat(2) @(posedge sys_clk);
sys_rst_n <= 1;
end
initial begin
#500 $finish;
end
endmodule
(2)时钟3分频占空比1:2,代码及仿真结果
//3分频电路设计占空比1:2
`timescale 1ns/1ns //时间刻度:单位1ns,精度1ns
module divider_3 //模块名
(
input sys_clk, //时钟(设定为 50MHz)
input sys_rst_n, //复位信号(n 表示低电平有效)
output reg clk_3 //输出3分频信号
);
reg [1:0] cnt; //reg 定义
//计数模块
//从0计数到2共计3个时钟周期
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt <= 2'd0; //复位清零
else if(cnt == 2'd2) //从0开始计数,所以需要 -1
cnt <= 2'd0; //计满则清零
else
cnt <= cnt + 2'd1; //没记满就一直计数
end
//3分频时钟输出模块
//满足计数条件则对3分频时钟进行反转
//3分频时钟隔1个周期反转一次,过2个周期再反转一次
always @(posedge sys_clk or negedge sys_rst_n) begin //clk divided
if (!sys_rst_n) begin //复位清零
clk_3 <= 1;
end
else if (cnt == 2'd1) begin //记满1个时钟周期
clk_3 <= ~clk_3; //计满则输出反转
end
else if (cnt == 2'd2) begin
clk_3 <= ~clk_3;
end
else
clk_3 <= clk_3; //没记满就保持原来状态
end
endmodule
module tb(); //仿真模块
//输入reg 定义
reg sys_clk;
reg sys_rst_n;
//输出wire定义
wire clk_3;
//例化被测试模块
divider_3 dut
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_3 (clk_3 )
);
initial begin
sys_clk <= 0;
forever begin
#5 sys_clk <= !sys_clk;
end
end
initial begin
sys_rst_n <= 0;
repeat(2) @(posedge sys_clk);
sys_rst_n <= 1;
end
initial begin
#500 $finish;
end
endmodule
2分频占空比1:3(将上升沿和下降沿都计数)代码及仿真结果
but上升和下降同时采样实际不能实现,此方法错误
//2分频电路设计占空比1:3
`timescale 1ns/1ns //时间刻度:单位1ns,精度1ns
module divider_2 //模块名
(
input sys_clk, //时钟(设定为 50MHz)
input sys_rst_n, //复位信号(n 表示低电平有效)
output reg clk_2 //输出2分频信号
);
reg [1:0] cnt; //reg 定义
//计数模块
//从0计数到2共计3个时钟周期
always@(posedge sys_clk or negedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt <= 2'd0; //复位清零
else if(cnt == 2'd3) //从0开始计数,所以需要 -1
cnt <= 2'd0; //计满则清零
else
cnt <= cnt + 2'd1; //没记满就一直计数
end
//2分频时钟输出模块
//满足计数条件则对2分频时钟进行反转
//2分频时钟隔1个周期反转一次,过3个周期再反转一次
always @(posedge sys_clk or negedge sys_clk or negedge sys_rst_n) begin //clk divided
if (!sys_rst_n) begin //复位清零
clk_2 <= 0;
end
else if (cnt == 2'd0) begin //记满1个时钟周期
clk_2 <= ~clk_2; //计满则输出反转
end
else if (cnt == 2'd1) begin
clk_2 <= ~clk_2;
end
else
clk_2 <= clk_2; //没记满就保持原来状态
end
endmodule
module tb(); //仿真模块
//输入reg 定义
reg sys_clk;
reg sys_rst_n;
//输出wire定义
wire clk_2;
//例化被测试模块
divider_2 dut
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_2 (clk_2 )
);
initial begin
sys_clk <= 0;
forever begin
#5 sys_clk <= !sys_clk;
end
end
initial begin
sys_rst_n <= 0;
repeat(2) @(posedge sys_clk);
sys_rst_n <= 1;
end
initial begin
#500 $finish;
end
endmodule