FPGA学习杂记
for语句在RTL级编码中极少使用,原因是for循环会被综合器展开为所有变量情况的执行语句,每个变量单独占用寄存器资源,不能有效地复用资源,造成巨大的资源浪费。
if……else if……else语句是有优先级的,而case语句是平行的结构。建立优先级结构会消耗大量的组合逻辑,所以能使用case语句的地方尽量用case替代。
同步时序电路
- 电路的核心逻辑用触发器实现;
- 电路的主要信号和输出信号等都是由时钟沿驱动触发器产生的;
- 同步时序电路可以很好地避免毛刺;
- 有利于静态时序分析,验证时序性能;
- 利于器件移植。
稳定可靠的数据采样必须遵从以下两个基本原则:
- 在有效时钟沿到达前,数据输入至少已经稳定了采样寄存器的setup时间之久,这条原则简称满足setup时间原则;
- 在有效时钟沿到达后,数据输入至少还将稳定保持采样寄存器的hold时间之久,这条原则简称满足hold时间原则。
reg型不一定综合成寄存器
- 一般来说,wire型指定的数据和网线通过组合逻辑实现,reg型的定义有可能只是纯组合逻辑。
case(Addr) 2'b00: Dout = Din[1:0]; 2'b01: Dout = Din[3:2]; 2'b10: Dout = Din[5:4]; default: Dout = Din[7:6]; endcase
- 一般来说,wire型指定的数据和网线通过组合逻辑实现,reg型的定义有可能只是纯组合逻辑。
亚稳态
如果触发器的setup和hold时间不满足,就可能产生亚稳态,此时触发器输出端Q在有效时钟沿之后较长一段时间内处于不确定的状态,这段时间内Q端产生毛刺并不断振荡,最终固定在某一电压值。
亚稳态的危害主要体现在破坏系统的稳定性上。毛刺、振荡或固定的某一电压值可能会造成逻辑误判,使状态输出出错。只要系统中有异步元件,亚稳态就无法避免。
模块复用
模块复用or逻辑复制属于速度or面积的问题。简单地从组合逻辑上来说,当时序满足要求的情况下,一般采用模块复用的方式节省面积,相反当时序紧张时,采用逻辑复制的方式提高速度。
//使用了两个乘法器 assign databar = ~datain + 1; assign square = (datain[7]) ? (databar * databar) : (datain * datain); //只使用一个乘法器 assign datatmp = (datain[7]) ? (~datain + 1) : datain; assign square = datatmp * datatmp; //上面两种设计占用的资源相差一倍以上。因此对时序要求没有太高的时候尽量少用乘法器。
状态机的设计
待续。
可编程逻辑设计中常用的4种设计思想与技巧
乒乓操作
乒乓操作通“输入数据流选择单元”和“输出数据流选择单元”按节拍切换,将经过缓冲的数据没有时间停顿地送到“数据流运算处理模块”。乒乓操作非常适合对数据流进行流水线式处理。
乒乓操作一般可用双口ram和fifo。如果数据完全是按顺序进行存取的话,fifo会更加简便(无需对地址进行操作)。
/*控制单元*/ always @(posedge CLK or negedge nRST) begin if(!nRST) begin wr_flag <= 0; rd_flag <= 0; end else begin case(flag) 1'b0: begin wr_flag <= 0; rd_flag <= 1; end 1'b1: begin wr_flag <= 1; rd_flag <= 0; end endcase end end /*输入数据流选择单元*/ always @(posedge CLK or negedge nRST) begin if(!nRST) begin buffer1 <= 0; buffer2 <= 0; end else begin case(wr_flag) 1'b0: buffer1 <= datain; 1'b1: buffer2 <= datain; endcase end end /*输出数据流选择单元*/ always @(posedge CLK or negedge nRST) begin if(!nRST) dataout <= 0; else begin case(rd_flag) 1'b0: dataout <= buffer1; 1'b1: dataout <= buffer2; endcase end end
串并转换
串并转换的核心思想是速度与面积的互换。数据量小的串并转换一般使用寄存器移位即可完成。
temp <= {temp[6:0],datain};
流水线操作
如果某个设计的处理流程分为若干步骤,且整个数据处理是单向的,前一步骤的输出是下一步骤的输入,则可以使用流水线设计方式,实现类似移位寄存器组的设计。进行流水线操作时应统筹考虑各模块的数据速率应相同。
if(!inflag) begin if(inclk) begin outclk <= 1; outflag <= 0; end else outclk <= 0; end else begin if(inclk) begin outclk <= 1; outflag <= 1; outdata <= {outdata[6:0],outdata[7]}; //在这里对输出进行处理// end else outclk <= 0; end
数据接口同步
数据接口同步中主要问题在于异步时钟域数据的同步,其中主要有以下两种情况:
两个时钟频率相同,但相差不固定,或相差固定但是不可测,简称同频异相问题。
同频异相问题简单的解决方法是用后级时钟对前级数据采样两次。可靠的做法是用DPRAM或FIFO完成数据的存取。
两个时钟域频率根本不同,简称异频问题。
异频问题的解决办法也是通过DPRAM或FIFO实现的。由于时钟频率不同,两个端口的数据吞吐率不同,设计时应开好缓冲区,并监控确保数据不会溢出。
状态机的设计
选择状态机的编码方式
Binary Gray Code(格雷码)使用最少的触发器和较多的组合逻辑,而one-hot编码相反。由于CPLD更多地提供组合逻辑资源,而FPGA更多提供触发器资源,因而CPLD多使用格雷码,FPGA多使用one-hot编码。另一方面,对于小型设计使用格雷码更有效,而大型状态机使用one-hot更高效。
状态机的描述方法
状态机描述时关键是要描述清楚几个状态机的要素,即如何进行状态转移,每个状态的输出是什么,状态转移的条件等。具体描述时方法各种各样,最常见的有三种描述方式:
- 一段式:整个状态机写到一个always模块里面,在该模块中既描述状态转移,又描述状态的输入和输出;
- 二段式:用两个always模块来描述状态机,其中一个always模块采用同步时序描述状态转移;另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律以及输出;
- 三段式:在两个always模块描述方法基础上,使用三个always模块,一个always模块采用同步时序描述状态转移,一个always采用组合逻辑判断状态转移条件,描述状态转移规律,另一个always模块描述状态输出(可以用组合电路输出,也可以时序电路输出。
一般而言,推荐的FSM 描述方法是后两种。这是因为:FSM和其他设计一样,最好使用同步时序方式设计,以提高设计的稳定性,消除毛刺。状态机实现后,一般来说,状态转移部分是同步时序电路而状态的转移条件的判断是组合逻辑。
第二种描述方法同第一种描述方法相比,将同步时序和组合逻辑分别放到不同的always模块中实现,这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。但组合逻辑容易使输出产生毛刺。
第三种描述方式与第二种相比,关键在于根据状态转移规律,在上一状态根据输入条件判断出当前状态的输出,从而在不插入额外时钟节拍的前提下,实现了寄存器输出。