写好CPLD的程序的经验总结

来源:网络整理 作者:2017年11月28日 17:11

  CPLD(Complex Programmable Logic Device)复杂可编程逻辑器件,是从PAL和GAL器件发展出来的器件,相对而言规模大,结构复杂,属于大规模集成电路范围。是一种用户根据各自需要而自行构造逻辑功能的数字集成电路。其基本设计方法是借助集成开发软件平台,用原理图、硬件描述语言等方法,生成相应的目标文件,通过下载电缆(“在系统”编程)将代码传送到目标芯片中,实现设计的数字系统。

  CPLD主要是由可编程逻辑宏单元(MC,Macro Cell)围绕中心的可编程互连矩阵单元组成。其中MC结构较复杂,并具有复杂的I/O单元互连结构,可由用户根据需要生成特定的电路结构,完成一定的功能。由于CPLD内部采用固定长度的金属线进行各逻辑块的互连,所以设计的逻辑电路具有时间可预测性,避免了分段式互连结构时序不完全预测的缺点。

  写好CPLD的程序的经验总结

  写好CPLD的程序的经验总结

  一个简单的数字电路中主要有三种基本单元:组合逻辑部分、触发器和锁存器。这些基本的单元通常都可以用FPGA内部的通用逻辑资源LE来综合得到,也就是说通过LE就能够搭建出满足需要的电路,然而用通用逻辑资源来做乘法、SRAM等非常消耗LE。所以FPGA中引入了SRAM、硬件乘法器和PLL等专用逻辑资源,满足一些特殊的用途,调用它们能够更加方便简单的搭建出功能更加强大的电路出来。所以衡量一个FPGA的资源光看LE的个数是不行的,还要看其里面的专用逻辑资源等。

  从上面的分析中,我们可以将FPGA中的通用逻辑资源看成单片机的内核,其他的像乘法器等专用硬件资源可以看成是片内的外设,这样可以和单片机的思想关联起来:针对逻辑资源,我们可以用比较灵活的语言去组合成上述三种基本单元,而片内的外设只要用专用的指令或者标准的库去调用即可,其作为内核的辅助单元。

  其中,在内核部分的编程中,组合逻辑部分可以用assign或者内部逻辑判断封闭的电平敏感always块来描述;触发器用边缘敏感的always块来描述;锁存器用内部逻辑判断不封闭的电平敏感always块来描述。

  上面已经给出了各种基本单元的verilog语言描述,不管一个多么复杂的电路,也可以用以上三种基本单元的基本描述方法来组合,就能够组合出很复杂的电路描述出来。

  上面我们讲述了FPGA和单片机开发上的相似点,但是他们还是有本质上的区别:FPGA和单片机的思想不一样,其语言是一种描述语言,包括行为级描述和门级描述,其不是一种设计语言,不能和C语言一样创造出新的东西出来,只能借助于FPGA内部的硬件资源,来表达电路的功能,只能按照米来下锅,否则就可能写出不能综合的代码,另外代码执行的方式也不一样,不在赘述。

  故写FPGA程序的正确方法是:首先,我们要对所需求的电路怎么用硬件电路搭建有一个比较清楚的概念,然后再用规范的硬件描述语言去描述该电路,然后再去仿真分析。

  ALTERA的LE结构

  要能写出能够综合的代码,首先要掌握LE的内部逻辑资源。LE的结构主要分为三部分:基于查表法的带进位链的四输入函数发生器,包括时钟编程(包括选择以及使能)、同步置位(清零)、异步置位(清零)的可编程寄存器,以及一些可编程互联线。

  其中,可编程寄存器可以配置成各种触发器,常见的D触发器,或者也可以配置成锁存器。有些需要注意的是:配置成触发器的时候,要看其寄存器的输出有没有Q反,倘若有Q反,则可以写成具有反向输出的D触发器,要是没有,则并不能配置成具有反向输出的D触发器,其将占用两个不同的逻辑单元来完成,这种写法是不好的,采用逻辑取反更好。

  reg data_reg, data_regn ;

  always@(posedge clk)

  begin

  data_reg 《= data_in;

  data_regn 《= ~ data_in;

  end

  这种写法是不好的,应该用逻辑取反获得反向信号。

  四输入函数发生器可以配置成组合逻辑,也可以配置成同步RAM,同时函数运算的结果可以通过后面的寄存器,也可以不通过后面的寄存器直接输出,这样从而可以方便的写出纯组合逻辑电路,也可以写成时序电路。

  一、延时的方法

  1.在代码中写:

  假设延时的路径为a到b,dly为延时门:

  reg dly/*synthesis keep*/;

  always @(posedge clk)

  begin

  dly《=a;

  b《=dly;

  end

  综合时,上述写法往往会被综合器认为是冗余逻辑而把dly优化掉,为了保住dly,需要在声明dly时加入/*synthesis keep*/注释,需注意,这种注释仅仅适用于Quartus自带综合器。若使用synplify(为/* synthesis syn_keep=1 */)等其他第三方综合器请查阅该综合器的文档获得显式综合保留声明的方法。

  2.用assignment editor约束编辑器实现:

  在from栏和to栏,用node finder选择延时路径的起点和终点。Assignment Name栏选择LCELL InserTIon,Value栏填入要加几个门即可。

  二、触发器和锁存器的标准写法

  1、D触发器

  //D触发器

  reg data_reg;

  always@(posedge clk)

  begin

  data_reg 《= data_in;

  end

  (1)、always@(posedge clk or negedge clk) 不合法

  需要注意的是一般都是单边缘触发,不能上下沿同时计数,双边缘触发的需要特殊的硬件结构的支持,但是一般需要注意的如果需要这种支持,则说明需要改设计。也就是说不能写成always@(posedge clk or negedge clk)的形式,要么可以改成电平敏感的事件,或者就是错误的方式,当要综合成计数器是错误的,同时最好不要混合使用上升沿和下降沿的触发器,最好统一用上升沿,或者下降沿。用D触发器设计状态机的时候,一定要注意采用同步的状态机。

  (2)也不要再一个always模块中采用一信号的上升沿,在另一个模块中采用其的下降沿。

  (3)采用《=的非阻塞赋值的方式。采用非阻塞和阻塞赋值的原则一般为:

  触发器和锁存器采用非阻塞赋值,纯组合逻辑的always块用阻塞赋值,既有组合逻辑又有时序逻辑的always块要用非阻塞赋值,并且不要一个always块内部既用阻塞赋值又有非阻塞赋值。

  // D触发器,带异步清零(置位)

  reg data_reg;

  always@(posedge clk or posedge clr)

  begin

  if(clr) data_reg《=0;

  else data_reg 《= data_in;

  end

  // D触发器,带同步清零(置位)

  reg data_reg;

  always@(posedge clk)

  begin

  if(clr) data_reg《=0;

  else data_reg 《= data_in;

  end

  (1)、异步和同步方式的区别就在于控制信号是否出现在敏感时间列表中

  异步方式中,清零或置位等控制信号需要出现中敏感列表中,同步则不需要出现中敏感列表中,直接在模块内部用于条件判断即可。由于FPGA的LE中的寄存器结构中有异步加载等引线的结构,这样直接导致同步和异步的综合方式不一样,异步清零或置位直接依靠寄存器的内部结构实现的,比较简单,而同步的方式则是在输入端添加控制信号逻辑来实现的,略复杂些。在下图中可以清楚的看到不同。

  图1 AlTERA的LE内部结构简明示意图

  (2)、异步清零或置位虽然是电平触发的时间,但是为了和clk一致,也要写成上升沿触发的方式。

  // D触发器,带时钟使能

  reg data_reg;

  always@( posedge clk)

  begin

  if(clk_enable) data_reg 《 = data_in;

  end

  当然,该语句和如下的例子是等价的:

  reg data_reg;

  assign clk2 = clk1 & clk_enable;

  always@( posedge clk1)

  begin

  data_reg = data_in;

  end

  需要注意的是:

  第二种写法是没有第一种好的,在LE的内部,连接寄存器的有两个时钟线以及时钟选择使能信号线。当采用一个时钟线的时候,也将采用其时钟使能信号线,也就是说时钟的使能最好能使用内部和寄存器直接相连的使能线,而不要采用另外的组合逻辑。

  2、D锁存器

  reg data_latch;

  always@(data_in or enable)

  begin

  if(enable) data_latch = data_in;

  end

  (1)、锁存器的输入信号也在敏感列表中出现,这点和寄存器是不一样的。

  (2)、根据锁存器的特性,要综合成锁存器,if语句中一定不要用一个封闭的判断语句,如下面一个语句就不会综合成锁存器,而会综合成组合逻辑。其实,这一点也说明了always也能描述组合逻辑。

  (3)、采用阻塞赋值的方式。

  reg data_latch;

  always@(data_in or enable)

  begin

  if(enable) data_latch = data_in;

  else data_latch =0;

  end

  为了用always块要描述组合逻辑,为了不要综合成锁存器,里面的if语句一定要写成一个封闭的形式,采用case也要加default。

  其实,上述电路描述可以等效为:

  reg data_latch;

  always@(data_in or enable)

  begin

  data_latch = data_in & enable;

  end

  这个就是一个组合逻辑的描述方式,再这主要是为了说明if else 和逻辑运算符&是等价的,两者可以互换,但是逻辑运算符更符合电路的描述方法,应该更多的采用逻辑运算符。

  3、always描述组合逻辑

  除了在上面讲到要组成一个组合逻辑电路,最好用逻辑运算符来描述,但是需要强调是,所有的参与赋值的信号都必须在always后的敏感列表中列出,倘若没有列出就有可能引入透明的锁存器:倘若信号A没有在敏感列表中列出,A的变化只有在列出的变量变化的时候才能反映出来,这点对不对需要我们去试验验证。

  所以写好组合逻辑电路的两点:1、快内的逻辑要封闭;2、赋值信号全部出现在敏感列表中。

关注电子发烧友微信

有趣有料的资讯及技术干货

下载发烧友APP

打造属于您的人脉电子圈

关注发烧友课堂

锁定最新课程活动及技术直播
声明:电子发烧友网转载作品均尽可能注明出处,该作品所有人的一切权利均不因本站而转移。
作者如不同意转载,既请通知本站予以删除或改正。转载的作品可能在标题或内容上或许有所改动。
收藏 人收藏
分享:

相关阅读

发表评论

elecfans网友

分享到:

用户评论(0