【技术专辑】FPGA入门:查找表和触发器

究竟是什么让FPGA与微控制器如此不同而又如此多样化?本文继续探索FPGA,重点关注触发器和查找表(LUT)在逻辑块中的作用。

 

FPGA内部的基本构建模块是触发器和查找表(LUT)。它们组合在一起形成所谓的逻辑块。逻辑块通过可编程互连相互连接,外部世界相互连接。触发器或多或少地像你多年来偶然发现的任何触发器一样。另一方面,LUT并不是一个普遍的概念。

 

支持信息

 

•Xilinx Spartan-6系列概述

 

FPGA内部

 

在我了解FPGA的内部工作原理之前,我想知道如何使用创建工作逻辑器件所需的所有随机门来加载器件。这是一个合理的问题,但我应该想到查找表。我自己在其他环境中使用过它们。

 

如果你看一下逻辑门的数据表,你会发现一个真值表。这很简单。在输入行上放置1和0的特定组合,在另一端出现一个或零。

 

【技术专辑】FPGA入门:查找表和触发器

 

FPGA中的LUT包含一个自定义真值表,在芯片上电时加载。将LUT视为小型便笺式RAM。LUT输入用作相应的一位宽RAM单元的地址线。配置FPGA时,LUT的位根据所需的真值表加载1或0。它不是连接一堆创建所需真值表的逻辑门,而是在一种特殊类型的RAM中进行模拟。

 

传统的双输入逻辑门为四种不同的输入组合提供特定输出,因此可以用4×1位暂存器RAM进行模拟。四输入LUT将采用16×1位暂存器RAM。

 

【技术专辑】FPGA入门:查找表和触发器

 

我在本文中使用的FPGA中的逻辑模块Xilinx Spartan 6 LX9具有64×1个RAM单元,每个单元都带有一个双触发器。LUT设置为模拟逻辑门组合,触发器用作存储形式和计数器和分频器。组合LUT /双触发器可用作“逻辑,分布式RAM或移位寄存器”,如上面链接的“Spartan-6系列概述”中所述。许多FPGA还有更大的RAM存储区,称为块RAM,只能用于存储。有关此芯片的更多具体细节可以在上面链接的概述文档中找到。

 

在实践中,这意味着我们的硬件描述语言(HDL)代码配置这些逻辑块的集合,以提供我们需要的所有FPGA功能。

 

LUT的一个注意事项

 

LUT的一个缺点是被称为“故障”的现象。就任何通用的错误而言,这不是一个“故障”,而是基于LUT的逻辑的特定特征。

 

考虑一个谨慎的逻辑门。您在输入上放置逻辑1或0,输出几乎立即改变。将会有一些传播延迟,但在输出方面从来没有任何模糊性。将双输入AND门的输入从“零,零”更改为“一,一”,输出保证从零变为1。

 

但是,在LUT中,您实际上是在一位宽RAM存储区中更改地址。当LUT输入位发生变化时,可能会有一个短暂的时刻 - RAM地址正在被改变 - 当RAM有效地不知道输出什么时。如果在那个不确定的时期内,LUT输出与预期输出不同,我们称之为故障。

 

使用离散逻辑芯片,您可以异步执行操作:只需对输入执行某些操作,并且更改将一直传播到您的逻辑门组。但是,对于LUT,在故障周期结束之前,您的预期输出可能不准确。

 

在今天的例子中,故障不是问题,但从一些意识开始是好的。处理它们将在本系列的后续文章中介绍。

 

回到开发板

 

在我的上一期中,我选择了一个开发板,安装了开发环境,并合成并加载了一个简单的LED闪烁“Hello World”FPGA配置。在本文中,我将探讨一下该配置的内容,并继续阐明微控制器和FPGA之间的差异。

 

但首先,我将添加更多硬件来帮助说明正在发生的事情。我需要一个按钮开关和四个LED(如果包括板载LED1,则需要五个)。按钮将用于启用计数,LED将显示寄存器部分的输出。

 

【技术专辑】FPGA入门:查找表和触发器

 

显示的是我的Papilio Pro带按钮/ LED机翼。我正在使用所有四个LED,但只使用其中一个按钮。

 

您可以重复上一部分中的步骤来创建新项目,但使用名称“RegisterCounting”作为项目和文件名而不是“HelloWorld”。

 

UCF和端口

 

完整的新UCF代码如下:

 

                    NET clk_in        LOC = P94   | IOSTANDARD=LVTTL | PERIOD=31.25ns;

 

NET reset         LOC = P121  | IOSTANDARD=LVTTL | PULLDOWN;

 

NET ExtLEDs<0>    LOC = P112  | IOSTANDARD=LVTTL | DRIVE = 8 | SLEW = SLOW ;

NET ExtLEDs<1>    LOC = P120  | IOSTANDARD=LVTTL | DRIVE = 8 | SLEW = SLOW ;

NET ExtLEDs<2>    LOC = P118  | IOSTANDARD=LVTTL | DRIVE = 8 | SLEW = SLOW ;

NET ExtLEDs<3>    LOC = P116  | IOSTANDARD=LVTTL | DRIVE = 8 | SLEW = SLOW ;

NET ExtLEDs<4>    LOC = P114  | IOSTANDARD=LVTTL | DRIVE = 8 | SLEW = SLOW ;

 

UCF的第一行与我之前的版本相同。它为时钟信号创建一个标签,该标签在引脚P94进入芯片。

 

之后,我为机翼上的1号按钮创建了“重置”标签。按钮1连接到芯片引脚P121。它在UCF中未指定为输入或输出。这发生在Verilog代码的模块声明部分,您很快就会看到。线路末端的参数“PULLDOWN”准确描述 - 下拉电阻将连接到芯片引脚P121。

 

下一部分说明了那些习惯于使用微控制器的人可能会遇到的主要概念之一。“ExtLEDs”看起来像一个相当传统的阵列,但事实并非如此。

 

在UCF中,我们将连接(可编程互连)标记为连接到外部世界的芯片引脚。NET“ExtLEDs”更类似于来自微控制器I / O端口而不是阵列的一组信号线。将数据放入ExtLED或单独访问每个数据的代码看起来类似于用于操作软件数组的代码。但是,ExtLED的每个成员只代表一条信号线,用作与寄存器中某个位的连接。在FPGA术语中,我们在UCF中引用的每个成员都被正确地称为信号。ExtLED是一组五个信号,可以作为一组引用。

 

您可能会注意到P112与我们在前一篇文章中使用的板载LED芯片引脚相同。它是。使用ExtLED,我们只是定义一组信号,我们正在为它们分配标签。引脚不需要是顺序的,甚至不需要物理接近。与ExtLED连接的其他芯片引脚连接到I / O连接器上的引脚,这些引脚连接到我安装的按钮/ LED机翼上的LED。

 

扩展的Verilog

 

Verilog代码如下:

 

                    `timescale 1ns / 1ps

 

module RegisterCounting(

        input wire reset,

        input wire clk_in,

        output wire [4:0] ExtLEDs

   );

 

    reg  [25:0] freq_div;                  // 26-bit register that I will be using as the frequency dividing counter

    

    assign ExtLEDs[4:0] = freq_div[25:21]; // connects the 4 led outputs to register bits at 25, 24, 23, and 22

    

    always @(posedge clk_in) begin         // Clocked on the rising clock edge

        if(reset)                          // Restart the counter at 0, if the button is pressed

             freq_div <= 0;

        else

            freq_div <= freq_div + 1'b1;   // Increment the register by 1

 

    end

        

endmodule

               

在模块声明中,reset和clk_in被定义为输入。五位信号“ExtLEDs”被声明为输出。

 

我已将寄存器“freq_div”扩展为26位(在上一篇文章中,它定义为21:0,即22位宽)。板载LED仍然显示寄存器的第21位的值,我的Papilio机翼上的四个LED显示位22:25的值。每个位将以前一位的频率的一半闪烁。

 

在下一个语句中,“分配ExtLEDs [4:0] = freq_div [25:21];”,来自“ExtLEDs”的五个信号(4:0)连接到寄存器中的五个位(25:21) “freq_div。”

 

这是另一个混乱警报的原因。如果您正在考虑使用MCU代码术语,则可能会在执行该行代码时将该分配解释为正在发生。这不正确。assign语句在这些寄存器位和输出之间创建稳定的连接。

 

我倾向于将Verilog“assign”语句想象成连接电线的东西。在这种情况下,它是“ExtLEDs”中五个输出信号和寄存器“freq_div”中五个最高有效位之间的五线带状电缆。这些连接将在上电时在FPGA中形成,并始终处于活动状态。

 

这意味着两件事:

 

•一,在您放置assign语句的代码中的位置并不重要。路由在合成步骤期间进行,而不是在执行时进行。如果它在代码的末尾,则该语句同样有效。您可以通过将assign语句向下移动到“endmodule”行的上方来证明。

 

•二,无论您在HDL代码中的哪个位置设置寄存器中的值,它都会立即出现在这些输出上。您不需要像在MCU语言中那样再次分配。即使您的assign语句位于代码的底部,该信号仍然可以在代码中的任何其他位置访问。

 

数字符号

 

在我之前的文章中,我在行中使用了略有不同的表示法来递增寄存器。在本文的代码中,我使用(freq_div <= freq_div + 1'b1;),而在上一篇文章中我使用了数字符号1'd1。两者在技术上都是正确的,但正如比尔指出的那样,1'b1更正确。

 

符号中的第一个数字是指用于存储值的位数 - 寄存器位宽。例如,1'b1创建一位宽的寄存器并将数字1放入其中,4'b1创建一个4位宽的寄存器并将数字0001放入其中。“b”表示尾随数的基数(或基数),在本例中为二进制; “d”,“h”和“o”分别表示十进制,十六进制和八进制。它不区分大小写,所以d等于D.

 

无论基数如何,第一个数字意味着相同的意思。1'd1创建一个1位寄存器,其中包含数字1。4'd1创建一个0001的寄存器,符号4'b1也是如此。第一个数字始终表示寄存器的位宽。基数表示您用于数字的数字集,尾随数字是值。

 

这里我使用1'd1在技术上是正确的,但可能会导致麻烦。符号1'd2仍然创建一位寄存器。这可以很好地表示值1.但是,由于2的二进制是10,MSB被丢弃,只留下0.1d2将等于零。1'd3等于1,而1'd4再次等于零。

 

摘要

 

检查查找表,触发器和可编程互连的使用对于理解FPGA如何工作有很大帮助。

 

逻辑在LUT中创建。触发器用于创建存储数据的寄存器。可编程互连将数据信号从逻辑和寄存器路由到外部世界。

  • 【技术专辑】FPGA入门:查找表和触发器已关闭评论
    A+
发布日期:2019年03月04日  所属分类:参考设计