IC每日一题
1 组合逻辑VS时序逻辑
1.1 组合逻辑
组合逻辑:使用各种组合逻辑门(逻辑计算+数值运算)来设计,可以理想理解没有时延,瞬时输出;组合逻辑输出结果仅仅取决于当前的输入信号;
问题:组合逻辑容易产生竞争冒险现象;
//一般实现方式
//--way-1
wire out_1;
assign out_1 = a & b | c;
//--way-2
reg out_2;
always@(*) begin
out_2 = a & b | c;
end
上述两种实现方式最后在综合都是等价的,都是wire型;
1.1.1 竞争冒险
竞争冒险概念:在组合逻辑电路中,由于门输入信号的不同延迟,导致输入信号变化有先有后,从而任何一个门电路只要有两个输入信号同时向相反方向变化,因为传输延迟,输出端就可能产生干扰脉冲的现象;
延迟产生原因:逻辑门改变状态时经历的一段极短的过渡时间;输入信号传输路径path不同在汇合后导致的传输延迟;这两者造成了信号的延迟;
危害:由于竞争冒险现象的存在,会造成后续逻辑的误动作–比如计数器错误计数,寄存器误操作等;
1.1.2 解决方法
尤其是有竞争冒险存在的情况下,负载端是对脉冲敏感的电路;
-------->常用解决方法:函数代数法增加冗余项、滤波电容、格雷码代替二进制、(引入封锁脉冲、选通脉冲);
- 代数法增加冗余项:通过在函数表达式中“加”上多余的“与”项或“乘”上多余的“或”项,使原函数避免化成X+X或X·X的形式,从而消除可能产生的竞争冒险,冗余项的选择可用代数法或卡诺图法。
- 格雷码:因为格雷码每一次的输出变化只有一个跳变,从而减少两个信号及以上的变化来传给下一个输入端;
- 同步电路:在关键路劲上使用边沿出发来存储中间结果,通过时钟沿来同步输入信号的变化,避免竞争冒险;
解释:加了一级寄存器后,毛刺信号不会被寄存器采样到,因为寄存器只在时钟的跳变沿变化,毛刺信号是组合逻辑产生的,实际电路里面组合逻辑的信号变化一般也是由寄存器变化引起的信号变化,所以一般发生在时钟跳变之后,而寄存器只在时钟的跳变沿对输入信号采样,所以毛刺信号一般不会被采样到
- 输出端并联电容:是因为竞争冒险产生的干扰脉冲一般很窄;
1.2 时序逻辑
概念:时序逻辑的输出不仅取决于当前的输入信号,还依赖于过去的状态;时序逻辑电路的设计是由触发器和组合逻辑门的组合来实现;
实现:
//--时序逻辑
always @(posedge clk or negedge rst_n) begin
if() begin
end else begin
end
end
1.3 比较
- 代码: 从代码层面来看,时序逻辑即敏感列表里面带有时钟上升沿,如果是是带有“*”号的或者assign代码,为组合逻辑。
//--组合
reg a,b,c;
always @(*) begin
c =a + b;
end
//--wire c;
//--assign c = a + b;
//--时序
always @(posedge clk or negedge rst_n) begin
if(rst_n==1'b0) begin
c <= 0;
end else begin
c <= a + b;
end
end
-
电路:时序逻辑相当于在组合逻辑的基础上多了一个D触发器;
-
波形图:波形图层面,组合逻辑的波形是即刻反映变化的,与时钟无关;但是时序逻辑的波形不会立刻反映出来,只有在时钟的上升沿发生变化;
组合逻辑任意时刻的输出仅仅取决该时刻的输入,与时钟无关;时序逻辑先算好当前输入信号的结果,但还不影响输出,只有等到时钟上升沿的一瞬间,才把结果给了输出。
1.4 场景
根据上述来区分组合逻辑和时序逻辑,来讨论其使用场景;
一般来说:首先我们需要保证信号的结果是正确的,只要满足目标需求,这时使用时序逻辑还是组合逻辑都是可以的;但是在这里也要防止关键路径过长,从而时序违例;
模块的输出一般都要求是时序逻辑输出的;
状态机是需要时序逻辑的;
在后续在出同步逻辑和时序逻辑典型的代码;
2 计数器
2.1 代码片段法
设计范式:代码片段法;----有助于代码逻辑表达清晰;从代码中可以明显看出;
在这里,计数器是计满保持;以这个例子来,主要是感受module代码的写法;
时序图如下:
实现代码如下:
//====写的代码逻辑太清楚了,逻辑清楚方便review,也相信后续方便进行debug;
module power_ctrl
//========================< 端口 >==========================================
(
//system --------------------------------------------
input wire clk , // 50MHz
input wire rst_n ,
//ov5640 --------------------------------------------
output wire ov5640_pwdn , // ov5640上电
output wire ov5640_rst_n , // ov5640复位
output wire power_done // power_ctrl全面有效,SCCB可以开始工作
);
//========================< 参数 >==========================================
localparam T2_6MS = 30_0000 ; // T2>5ms
localparam T3_2MS = 10_0000 ; // T3>1ms
localparam T4_21MS = 105_0000 ; // T4>20ms
//========================< 信号 >==========================================
reg [18:0] cnt_6ms ;
reg [16:0] cnt_2ms ;
reg [20:0] cnt_21ms ;
//==========================================================================
//== ov5640_pwdn
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_6ms <= 'd0;
end
else if(ov5640_pwdn == 1'b1) begin
cnt_6ms <= cnt_6ms + 1'b1;
end
end
assign ov5640_pwdn = (cnt_6ms >= T2_6MS) ? 1'b0 : 1'b1;
//==========================================================================
//== ov5640_rst_n
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_2ms <= 'd0;
end
else if(ov5640_rst_n == 1'b0 && ov5640_pwdn == 1'b0) begin
cnt_2ms <= cnt_2ms + 1'b1;
end
end
assign ov5640_rst_n = (cnt_2ms >= T3_2MS) ? 1'b1 : 1'b0;
//==========================================================================
//== power_done
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_21ms <= 'd0;
end
else if(power_done == 1'b0 && ov5640_rst_n == 1'b1) begin
cnt_21ms <= cnt_21ms + 1'b1;
end
end
assign power_done = (cnt_21ms >= T4_21MS) ? 1'b1 : 1'b0;
endmodule
2.2 实现计数器–异步复位,带clear端,计10则归0;
题目:如上所示,这是常规的cnt计数,但是注意在这里使用到的设计思想;
要求:代码片段法
module cnt(
//==========================< 端口 >=========================
input wire clk,
input wire rst_n,
input wire clear_n,
output reg[3:0] cnt
);
//==========================< 参数 >=========================
parameter CNT_MAX = 10;
//==========================< 信号 >=========================
wire add_cnt_vld;
wire end_cnt_vld;
//=========================================================
//-- 0到10循环
//=========================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 4'b0;
end else if(!clear_n) begin
cnt <= 4'b0;
end else if(add_cnt_vld) begin
if(end_cnt)begin
cnt <= 4'b0;
end else begin
cnt <= cnt + 1'b1;
end
end
end
assign add_cnt_vld = 1'b1;
assign end_cnd_vld = add_cnt_vld && cnt==10 -1;
endmodule
【REF】
https://fpga.eetrend.com/content/2022/100562812.html
https://hui-shao.com/digital-circuits-race-and-hazard/
https://fpga.eetrend.com/content/2022/100562812.html
https://www.cnblogs.com/xianyuIC/p/10908740.html
https://mp.weixin.qq.com/s?__biz=Mzg4NzcyMzU2Mg==&mid=2247484281&idx=1&sn=44fedaa86a7f5ffc47579d3a990bb429&chksm=cf87403cf8f0c92a863226c85b4a8af9d667d8ffe0013c873edc74fe666279669e4aac63a4b1&token=172677512&lang=zh_CN#rd