FPGA之道(68)原语的使用

前言

本文节选自《FPGA之道》。

原语的使用

什么是原语

原语,英文名称primitive,是FPGA软件集成开发环境所提供的一系列底层逻辑功能单元。由于是底层逻辑功能单元,所以它们往往跟目标FPGA芯片以及芯片厂商紧密相关,因此不同厂商、不同器件的原语往往不能通用。当编译器对我们的HDL代码进行编译时,其中间环节的一些输出往往就是由原语组成的逻辑网表。因此,原语往往是不参与综合过程的,而使用原语描述的逻辑往往也不会被综合工具所优化。例如,Xilinx公司的ISE软件集成开发环境中的unisims库中定义了所有用于综合的原语,而simprims库中则定义了所有用于实现的原语。需要注意的是,如果我们去ISE安装目录下的verilog\src\unisims或verilog\src\simprims文件夹下去看这些原语的代码,可以发现其实这些并不是真正的原语,而是在原语的基础上又封装了一层,不过人们常常将它们也泛称为原语。
例如,如下为unisims库中一个二输入与门的定义:

`timescale  1 ps / 1 ps
module AND2 (O, I0, I1);
    output O;
    input  I0, I1;
    and A1 (O, I0, I1);
endmodule

至于其中的and是不是真正的原语,则需要找进一步的定义。
而simprims库由于涉及到了实现,因此在建模的时候就需要加入时序信息,例如,如下为simprims库中一个二输入与门的定义:

`timescale 1 ps/1 ps
module X_AND2 (O, I0, I1);
  parameter LOC = "UNPLACED";
  output O;
  input I0, I1;
  and (O, I0, I1);
  specify
(I0 => O) = (0:0:0, 0:0:0);
(I1 => O) = (0:0:0, 0:0:0);
specparam PATHPULSE$ = 0;
  endspecify
endmodule

可见,这里面用到了我们在【本篇->编程语法->Verilog基本语法->Verilog的并行语句->Verilog模块说明语句】小节中介绍的specify语法,来在原语的基础上,对该功能模块进行时序建模,这应该也是为什么库里面不提供直接的原语调用的原因之一。不过上述定义中的时间延迟信息均为0,这是由于不同系列器件或不同等级器件中的延迟不相等,所以这些参数会在编译的时候根据具体目标器件进行替换。
再多观察一些库中的基本逻辑单元,可以发现其中一些是带有真正的原语定义的,例如simprims库下的一个两输入查找表的定义:

`timescale 1 ps/1 ps
module X_LUT2 (O, ADR0, ADR1);
  parameter INIT = 4'h0;
  parameter LOC = "UNPLACED";
  output O;
  input ADR0, ADR1;
  wire out, a0, a1;
  buf b0 (a0, ADR0);
  buf b1 (a1, ADR1);
  x_lut2_mux4 (O, INIT[3], INIT[2], INIT[1], INIT[0], a1, a0);
  specify
		(ADR0 => O) = (0:0:0, 0:0:0);
		(ADR1 => O) = (0:0:0, 0:0:0);
		specparam PATHPULSE$ = 0;
  endspecify
endmodule

primitive x_lut2_mux4 (o, d3, d2, d1, d0, s1, s0);
  output o;
  input d3, d2, d1, d0;
  input s1, s0;
  table
    // d3  d2  d1  d0  s1  s0 : o;
       ?   ?   ?   1   0   0  : 1;
       ?   ?   ?   0   0   0  : 0;
       ?   ?   1   ?   0   1  : 1;
       ?   ?   0   ?   0   1  : 0;
       ?   1   ?   ?   1   0  : 1;
       ?   0   ?   ?   1   0  : 0;
       1   ?   ?   ?   1   1  : 1;
       0   ?   ?   ?   1   1  : 0;

       ?   ?   0   0   0   x  : 0;
       ?   ?   1   1   0   x  : 1;
       0   0   ?   ?   1   x  : 0;
       1   1   ?   ?   1   x  : 1;

       ?   0   ?   0   x   0  : 0;
       ?   1   ?   1   x   0  : 1;
       0   ?   0   ?   x   1  : 0;
       1   ?   1   ?   x   1  : 1;

       0   0   0   0   x   x  : 0;
       1   1   1   1   x   x  : 1;
  endtable
endprimitive

上述定义中,primitive关键字定义的就是真正的原语,我们将在后续的【UDP简介】小节中具体介绍primitive的语法。

需要使用原语的情况

一般来说,在进行HDL代码编写时,不需要直接或间接的进行原语调用,因为随着FPGA设计规模的越来越庞杂,人脑应该集中于抽象层次较高的工作中去,而将这些具体实现细节交给编译器来完成。不过有些时候,原语或者库中底层模块的调用还是十分必要的。例如,在Xilinx的ISE工具中,可以通过edit->Language Templates菜单调出语法模板,在其中的VHDL或Verilog子菜单下,都可以看到一个Device Primitive Instantiation子项,里面列举出了所有我们在Xilinx FPGA平台上开发项目时可能会用到的原语例化方式。举例简介如下:

时钟相关原语

如果时钟信号不是由FPGA芯片的专用时钟pin(或pad)引入FPGA的,那么它通常就需要在FPGA内部被显式的连接到时钟树资源上,否则,直接使用这种不经过时钟树的时钟信号,会给FPGA设计的时序带来非常麻烦的问题,进而导致逻辑行为失败。
可是HDL代码仅仅描述功能,无法向编译器表达“希望将某一时钟信号连接到时钟树资源”这样的一层意思,那么此时,就需要使用类似BUFG这样的库里提供的底层模块来进行指示。例如:

-- VHDL example
singal innerclk, gclk : std_logic;

onToGlobalClockTree: BUFG
port map (
	I => innerclk,  -- in  std_logic
	O => gclk);    -- out std_logic

process(gclk)
begin
	……
end process;
// Verilog example
wire innerclk, gclk;

BUFG onToGlobalClockTree(.I (innerclk),
	   		 		  .O (gclk));

always@(posedge gclk)
begin
	……
end

那么,像上例这样,通过显式调用BUFG这样一个库中的底层模块,就可以告诉编译器,我们希望将innerclk信号引入全局时钟树,而其经过全局时钟树后的名字就改为gclk。后续HDL代码便可以放心的基于gclk编写逻辑。需要说明的是,直接从全局时钟pin(或pad)引入的时钟信号,对于xilinx的FPGA芯片来说,实际上是通过IBUFG+BUFG这样的组合直接连接到全局时钟树上的,只不过此时我们不需要显式例化这两个原语。
类似的时钟相关原语还有BUFR、BUFIO等,它们分别对应区域时钟树和IO时钟树资源,这里就不再赘述。

差分输入、输出原语

FPGA的接口具有单端和差分两种形式,同样,HDL代码只能描述功能,无法表达“某两个pin(或pad)脚互为一个差分对”这样的一层意思。那么此时,就需要使用类似IBUFDS、IBUFGDS、OBUFDS这样的库里提供的底层模块或原语来进行指示。例如:

-- VHDL example
	clklvds : IBUFGDS
	generic map (
		DIFF_TERM => TRUE, 
		IBUF_DELAY_VALUE => "0", 
		IOSTANDARD => "DEFAULT")
	port map (
		I  => LVDSClk_p,   
		IB => LVDSClk_n,    
		O  => sclk); 
onToGlobalClockTree: BUFG
	port map (
		I => sclk,  -- in  std_logic
		O => gclk);    -- out std_logic
// Verilog example
IBUFGDS clklvds(.I (LVDSClk_p),
			  .
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李锐博恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值