初学者浅谈FPGA的时序约束

接触FPGA已经差不多两年了,在学习的过程中总能偶尔听到FPGA需要时序约束。

那个时候就是什么都不懂,不懂为什么要约束,以及约束的是什么东西,以及代码的风格对约束有什么影响。

直到在后来的一次调试中踩了雷,才明白时序约束的重要性,如下verilog代码。

/********************************************************************************************/

reg [31:0]get_num;

reg [7:0]a;

reg [7:0]b;

reg [7:0]c;

reg [7:0]d;

reg[7:0]e;

reg[7:0]f;

always@(posedge clk)

begin

       get_num<=a*b*c*d/e/f;

end

/**************************************************************************************/

在调试的过程中就发现当clk的频率高于某一个值,get_num的值就会出现错误,然后我下意识的想到这种写法不符合时序约束规则。

我们来分析一下,get_num每得到一次数值就会经过三次乘法两次除法。而乘法与除法虽然是组合逻辑(也可以用多周期的时序逻辑实现)。

但是组合逻辑在硬件上也是有延迟的。假设一次乘法需要xns,一次除法需要yns,则组合逻辑的总延迟就是(3x+2y)ns。

然而FPGA的always@(posedge clk)会综合成D触发器,来一个上升沿,D触发器就会更新一次数据。

相邻两个上升沿的时间差就是clk的周期,D触发器上升沿前需要有建立时间,假设为ans,D触发器上升沿后需要有保持时间,假设为bns

(注意:综合和编译是有本质意义上的区别,综合后的结果是一堆数字门电路。编译后的结果是一堆2进制代码)

那么(3x+2y+a+b)的时间应该要小于时钟的周期,这样的时序才是收敛的,不然的话,时序就会发散。

时序发散对于FPGA来说是致命的打击。

那如果想优化上面的代码(应该叫硬件描述语言),应该怎么办呢?

我们可以用流水线法做优化。

/**************************************一级流水线**********************************************/

/******/

reg[15:0]buff1;

always@(posedge clk)

begin

    buff1<=a*b;

end

/******/

reg[15:0]buff2;

always@(posedge clk)

begin

    buff2<=c*d;

end

/******/

reg[15:0]buff3;

always@(posedge clk)

begin

    buff3<=e*f;

end

/*******************************************二级流水线*********************************************/

reg [31:0]buff4;

always@(posedge clk)

begin

    buff4<=buff1*buff2;

end

/**********/

reg [15:0]buff5;

always@(posedge clk)

begin

    buff5<=buff3;

end

/*************************************************三级流水线*******************************************************/

reg [31:0]get_num;

always@(posedge clk)

begin

    get_num<=buff4/buff5;

end

/********************************************************************************************************************************************************************/

优化后就变成了三级流水线实现,这种实现方式比第一种方式可接受的时钟频率要高很多。例如第一种只能跑10mhz,第二种流水线优化后的就能跑远远大于10mhz的时钟频率。

/*************************************************************************************************************************************************************************************************************/

下面再来举一个例子,外部信号上升沿触发数据传输。(ps:这要是在单片机中就要用到外部中断啦,我也是一个单片机爱好者呢,stm32,msp430,stc89c51,当然还有dsp的c2000,c5000,c6000,等,还有可以跑linux的nuc977,imx6ull等等,qt也是我的最爱。不对,我扯远了,我这个主题是FPGA)

/******************************************/

reg signal_test;

reg main_clk;

reg [7:0]adc_num;

reg [7:0]out_num;

always@(posedge signal_test)

begin

    out_num<=adc_num;

end

//上面这个写法用到了门控时钟这个概念,这种写法相对于全局的工程来说是异步的,不利于时序约束。

/***************************************************************************************************************************************************************************************************************/

reg signal_test;

reg main_clk;

reg [7:0]adc_num;

reg [7:0]out_num;

reg buff_signal_testa=0;

reg buff_signal_testb=0;

/*********************************************************/

wire pos_get;

wire neg_get;

always@(posedge clk)

begin

    buff_signal_testa<=signal_test;

    buff_signal_testb<=buff_signal_testa;

end

assign pos_get=(buff_signal_testb==0)&&(buff_signal_testa==1);

assign neg_get=(buff_signal_testb==1)&&(buff_signal_testa==0);

/*******************************************************/

always@(posedge main_clk)

begin

    if(pos_get)begin

            out_num<=adc_num;

    end

end

//上面的写法就比较符合时序约束

/*******************************************************************************************************************************************************************************************************/

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值