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),
			  .IB (LVDSClk_n),
	   		  .O (sclk));
BUFG onToGlobalClockTree(.I (sclk),
	   		 		  .O (gclk));

上述代码通过调用原语IBUFGDS,表达了“LVDSClk_p、LVDSClk_n是一对差分时钟输入信号且它们是经由FPGA芯片的专用差分时钟输入管脚引入FPGA芯片”的意思,这样编译器就会采用接口资源中的专用差分时钟输入电路来接入这对时钟输入信号,并转换成为一个单端时钟sclk,紧接着,在通过一个BUFG原语将其引致全局时钟网络,这样全局时钟网络输出的gclk就可以作为后续内部逻辑的时钟源。

接口相关原语

当需要使用接口资源中的寄存器来实现高速数据采集时,除了使用【本篇->编程思路->代码中的约束信息->HDL中的常用约束示例->寄存器的相关约束】小节中介绍的方法外,如果直接调用相关的原语,编译器便会利用接口资源中的寄存器来做事情。例如,使用ODDR、IDDR、IDDR2CLK、ISERDES、OSERDES等原语后,编译器便会利用接口资源的寄存器来实现相关接口功能,这样便能达到比较高的性能。例如:

-- VHDL example
	is0 : ISERDES_NODELAY
   	generic map (
      		BITSLIP_ENABLE => TRUE, -- TRUE/FALSE to enable bitslip controller
                       			 -- Must be "FALSE" in interface type is "MEMORY" 
      		DATA_RATE => "DDR", -- Specify data rate of "DDR" or "SDR" 
      		DATA_WIDTH => 6, -- Specify data width - 
                       		-- NETWORKING SDR: 2, 3, 4, 5, 6, 7, 8 : DDR 4, 6, 8, 10
                       		-- MEMORY SDR N/A : DDR 4
      		INTERFACE_TYPE => "NETWORKING", 
							-- Use model - "MEMORY" or "NETWORKING" 
      		NUM_CE => 2, -- Define number or clock enables to an integer of 1 or 2
      		SERDES_MODE => "MASTER") --Set SERDES mode to "MASTER" or "SLAVE" 
 	port map (
      		Q1 => data0Line(0),		-- 1-bit registered SERDES output
      		Q2 => data0Line(1),  	-- 1-bit registered SERDES output
      		Q3 => data0Line(2),  	-- 1-bit registered SERDES output
      		Q4 => data0Line(3),  	-- 1-bit registered SERDES output
      		Q5 => data0Line(4),  	-- 1-bit registered SERDES output
      		Q6 => data0Line(5),  	-- 1-bit registered SERDES output
      		SHIFTOUT1 => open,  	-- 1-bit cascade Master/Slave output
      		SHIFTOUT2 => open,  	-- 1-bit cascade Master/Slave output
      		BITSLIP => '0',       	-- 1-bit Bitslip enable input
      		CE1 => '1',          	-- 1-bit clock enable input
      		CE2 => '1',          	-- 1-bit clock enable input
      		CLK => clkFast,      	-- 1-bit master clock input
      		CLKB => clkFastInvert,	-- 1-bit secondary clock input for DATA_RATE=DDR
      		CLKDIV => clkSlow,  	-- 1-bit divided clock input
      		D => din,            	-- 1-bit data input, connects to IODELAY or input buffer
      		OCLK => '0',      		-- 1-bit fast output clock input
			RST => rst,        	-- 1-bit asynchronous reset input
      		SHIFTIN1 => '0', 		-- 1-bit cascade Master/Slave input
      		SHIFTIN2 => '0' 		-- 1-bit cascade Master/Slave input
   		);
// Verilog example
ISERDES_NODELAY #(
	.BITSLIP_ENABLE("FALSE"),  // "TRUE"/"FALSE" to enable bitslip controller 
                           // Must be "FALSE" if INTERFACE_TYPE set to "MEMORY" 
  		.DATA_RATE("DDR"),	// Specify data rate of "DDR" or "SDR" 
  		.DATA_WIDTH(4),		// Specify data width - 
                       		// NETWORKING SDR: 2, 3, 4, 5, 6, 7, 8 : DDR 4, 6, 8, 10
                        	// MEMORY SDR N/A : DDR 4
  		.INTERFACE_TYPE("MEMORY"), // Use model - "MEMORY" or "NETWORKING" 
  		.NUM_CE(2),         	// Number of clock enables used, 1 or 2
  		.SERDES_MODE("MASTER") // Set SERDES mode to "MASTER" or "SLAVE" 
) is0 (
  		.Q1(data0Line[0]),		// 1-bit registered SERDES output
  		.Q2(data0Line[1]),  	// 1-bit registered SERDES output
  		.Q3(data0Line[2]),  	// 1-bit registered SERDES output
  		.Q4(data0Line[3]),  	// 1-bit registered SERDES output
  		.Q5(data0Line[4]),  	// 1-bit registered SERDES output
  		.Q6(data0Line[5]),  	// 1-bit registered SERDES output
  		.SHIFTOUT1(open), 	// 1-bit cascade Master/Slave output
  		.SHIFTOUT2(open), 	// 1-bit cascade Master/Slave output
  		.BITSLIP(1'b0),     	// 1-bit Bitslip enable input
  		.CE1(1'b1),   			// 1-bit clock enable input
 		.CE2(1'b1),     		// 1-bit clock enable input
  		.CLK(clkFast),    		// 1-bit master clock input
  		.CLKB(clkFastInvert),	// 1-bit secondary clock input for DATA_RATE=DDR
  		.CLKDIV(clkSlow),		// 1-bit divided clock input
		.D(din),         		// 1-bit data input, connects to IODELAY or input buffer
  		.OCLK('0'),          	// 1-bit fast output clock input
  		.RST(rst),             // 1-bit asynchronous reset input
  		.SHIFTIN1(1'b0),   	// 1-bit cascade Master/Slave input
  		.SHIFTIN2(1'b0)    	// 1-bit cascade Master/Slave input
);

使用上述原语后,编译器便会利用接口资源中的寄存器实现一个针对输入接口的高性能1:6串并转换器。当然了,为了保证串并处理的成功,仅仅使用ISERDES原语还远远不够,时钟信号也必须按照要求来处理,具体的要求大家可以去查阅Xilinx公司相应器件的用户手册。

用原语表示IP核的好处

其实,FPGA软件集成开发环境中提供的IP核基本上都具有原语的表示形式。相比于调用IP核智能生成器对IP核进行配置、生成、例化等一系列过程,直接通过原语调用显得要简便得多,当然了,前提是你要对IP核的各个属性和参数配置比较熟悉。尤其是碰到版本升级或者整体细微修改这样的情况,如果采用在HDL中调用原语的方式,将会使修改的工作量大大降低。
例如,使用原语例化一个锁相环如下:

-- VHDL example
BUFG_COut0 : BUFG
port map (
	O => clkOut0,     -- Clock buffer output
	I => pllClkOut0      -- Clock buffer input
);

BUFG_FB : BUFG
port map (
	O => feedBackG,     -- Clock buffer output
	I => feedBack      -- Clock buffer input
);

PLL_ADV_inst : PLL_ADV
generic map (
	BANDWIDTH => "OPTIMIZED",	-- "HIGH", "LOW" or "OPTIMIZED" 
	CLKFBOUT_MULT => 7,        	-- Multiplication factor for all output clocks
	CLKFBOUT_PHASE => 0.0,    	-- Phase shift (degrees) of all output clocks
	CLKIN1_PERIOD => 16.67,    	-- Clock period (ns) of input clock on CLKIN1
	CLKIN2_PERIOD => 10.00,    	-- Clock period (ns) of input clock on CLKIN2
	CLKOUT0_DIVIDE => 1,       	-- Division factor for CLKOUT0  (1 to 128)
	CLKOUT0_DUTY_CYCLE => 0.5, 	-- Duty cycle for CLKOUT0 (0.01 to 0.99)
	CLKOUT0_PHASE => 45.0,      	-- Phase shift (degrees) for CLKOUT0 (0.0 to 360.0)
	CLKOUT1_DIVIDE => 1,       	-- Division factor for CLKOUT1 (1 to 128)
	CLKOUT1_DUTY_CYCLE => 0.5, 	-- Duty cycle for CLKOUT1 (0.01 to 0.99)
	CLKOUT1_PHASE => 0.0,      	-- Phase shift (degrees) for CLKOUT1 (0.0 to 360.0)
	CLKOUT2_DIVIDE => 1,       	-- Division factor for CLKOUT2 (1 to 128)
	CLKOUT2_DUTY_CYCLE => 0.5, 	-- Duty cycle for CLKOUT2 (0.01 to 0.99)
	CLKOUT2_PHASE => 0.0,      	-- Phase shift (degrees) for CLKOUT2 (0.0 to 360.0)
	CLKOUT3_DIVIDE => 1,       	-- Division factor for CLKOUT3 (1 to 128)
	CLKOUT3_DUTY_CYCLE => 0.5, 	-- Duty cycle for CLKOUT3 (0.01 to 0.99)
	CLKOUT3_PHASE => 0.0,      	-- Phase shift (degrees) for CLKOUT3 (0.0 to 360.0)
	CLKOUT4_DIVIDE => 1,       	-- Division factor for CLKOUT4 (1 to 128)
	CLKOUT4_DUTY_CYCLE => 0.5, 	-- Duty cycle for CLKOUT4 (0.01 to 0.99)
	CLKOUT4_PHASE => 0.0,      	-- Phase shift (degrees) for CLKOUT4 (0.0 to 360.0)
	CLKOUT5_DIVIDE => 1,       	-- Division factor for CLKOUT5 (1 to 128)
	CLKOUT5_DUTY_CYCLE => 0.5, 	-- Duty cycle for CLKOUT5 (0.01 to 0.99)
	CLKOUT5_PHASE => 0.0,      	-- Phase shift (degrees) for CLKOUT5 (0.0 to 360.0)
	COMPENSATION => "SYSTEM_SYNCHRONOUS",  
								-- "SYSTEM_SYNCHRNOUS", 
								--"SOURCE_SYNCHRNOUS", "INTERNAL", 
								-- "EXTERNAL", "DCM2PLL", "PLL2DCM" 
	DIVCLK_DIVIDE => 1,      		-- Division factor for all clocks (1 to 52)
	EN_REL => FALSE,         		-- Enable release (PMCD mode only)
	PLL_PMCD_MODE => FALSE,  	-- PMCD Mode, TRUE/FASLE
	REF_JITTER => 0.000,     		-- Input reference jitter (0.000 to 0.999 UI%)
	RST_DEASSERT_CLK => "CLKIN1") 	-- In PMCD mode, clock to synchronize RST release
	port map (
	CLKFBDCM => open,		-- Output feedback signal used when PLL feeds a DCM
	CLKFBOUT => feedBack,		-- General output feedback signal
	CLKOUT0 => pllClkOut0,  	-- One of six general clock output signals
	CLKOUT1 => open,        	-- One of six general clock output signals
	CLKOUT2 => open,        	-- One of six general clock output signals
	CLKOUT3 => open,        	-- One of six general clock output signals
	CLKOUT4 => open,        	-- One of six general clock output signals
	CLKOUT5 => open,        	-- One of six general clock output signals
	CLKOUTDCM0 => open,  	-- One of six clock outputs to connect to the DCM
	CLKOUTDCM1 => open,  	-- One of six clock outputs to connect to the DCM
	CLKOUTDCM2 => open,  	-- One of six clock outputs to connect to the DCM
	CLKOUTDCM3 => open,  	-- One of six clock outputs to connect to the DCM
	CLKOUTDCM4 => open,  	-- One of six clock outputs to connect to the DCM
	CLKOUTDCM5 => open,  	-- One of six clock outputs to connect to the DCM
	DO => open,       		-- Dynamic reconfig data output (16-bits)
	DRDY => open,         	-- Dynamic reconfig ready output
	LOCKED => locked,     	-- Active high PLL lock signal
	CLKFBIN => feedBackG,   	-- Clock feedback input
	CLKIN1 => clkIn,          	-- Primary clock input
	CLKIN2 => '0',          	-- Secondary clock input
	CLKINSEL => '1',      		-- Selects CLKIN1 or CLKIN2
	DADDR => "00000",       	-- Dynamic reconfig address input (5-bits)
	DCLK => '0',              	-- Dynamic reconfig clock input
	DEN => '0',           		-- Dynamic reconfig enable input
	DI => "0000000000000000",	-- Dynamic reconfig data input (16-bits)
	DWE => '0',             	-- Dynamic reconfig write enable input
	REL => '0',           		-- Clock release input (PMCD mode only)
	RST => rst               	-- Asynchronous PLL reset
);
// Verilog example
BUFG BUFG_COut0(.I (pllClkOut0), .O (clkOut0));
BUFG BUFG_FB(.I (feedBack), .O (feedBackG));
PLL_ADV #(
	.BANDWIDTH("OPTIMIZED"),		// "HIGH", "LOW" or "OPTIMIZED" 
	.CLKFBOUT_MULT(7),        	// Multiplication factor for all output clocks
	.CLKFBOUT_PHASE(0.0),     	// Phase shift (degrees) of all output clocks
	.CLKIN1_PERIOD(16.67),    		// Clock period (ns) of input clock on CLKIN1
	.CLKIN2_PERIOD(10.00),    		// Clock period (ns) of input clock on CLKIN2
	.CLKOUT0_DIVIDE(1),       	// Division factor for CLKOUT0 (1 to 128)
	.CLKOUT0_DUTY_CYCLE(0.5), 	// Duty cycle for CLKOUT0 (0.01 to 0.99)
	.CLKOUT0_PHASE(45.0),      	// Phase shift (degrees) for CLKOUT0 (0.0 to 360.0)
	.CLKOUT1_DIVIDE(1),       	// Division factor for CLKOUT1 (1 to 128)
	.CLKOUT1_DUTY_CYCLE(0.5), 	// Duty cycle for CLKOUT1 (0.01 to 0.99)
	.CLKOUT1_PHASE(0.0),      	// Phase shift (degrees) for CLKOUT1 (0.0 to 360.0)
	.CLKOUT2_DIVIDE(1),       	// Division factor for CLKOUT2 (1 to 128)
	.CLKOUT2_DUTY_CYCLE(0.5), 	// Duty cycle for CLKOUT2 (0.01 to 0.99)
	.CLKOUT2_PHASE(0.0),      	// Phase shift (degrees) for CLKOUT2 (0.0 to 360.0)
	.CLKOUT3_DIVIDE(1),       	// Division factor for CLKOUT3 (1 to 128)
	.CLKOUT3_DUTY_CYCLE(0.5), 	// Duty cycle for CLKOUT3 (0.01 to 0.99)
	.CLKOUT3_PHASE(0.0),      	// Phase shift (degrees) for CLKOUT3 (0.0 to 360.0)
	.CLKOUT4_DIVIDE(1),       	// Division factor for CLKOUT4 (1 to 128)
	.CLKOUT4_DUTY_CYCLE(0.5), 	// Duty cycle for CLKOUT4 (0.01 to 0.99)
	.CLKOUT4_PHASE(0.0),      	// Phase shift (degrees) for CLKOUT4 (0.0 to 360.0)
	.CLKOUT5_DIVIDE(1),       	// Division factor for CLKOUT5 (1 to 128)
	.CLKOUT5_DUTY_CYCLE(0.5), 	// Duty cycle for CLKOUT5 (0.01 to 0.99)
	.CLKOUT5_PHASE(0.0),      	// Phase shift (degrees) for CLKOUT5 (0.0 to 360.0)
	.COMPENSATION("SYSTEM_SYNCHRONOUS"), 
								// "SYSTEM_SYNCHRONOUS", 
								// "SOURCE_SYNCHRONOUS", "INTERNAL", 
								// "EXTERNAL", "DCM2PLL", "PLL2DCM" 
	.DIVCLK_DIVIDE(1),        		// Division factor for all clocks (1 to 52)
	.EN_REL("FALSE"),         		// Enable release (PMCD mode only)
	.PLL_PMCD_MODE("FALSE"),  	// PMCD Mode, TRUE/FASLE
	.REF_JITTER(0.100),       		// Input reference jitter (0.000 to 0.999 UI%)
	.RST_DEASSERT_CLK("CLKIN1") 	// In PMCD mode, clock to synchronize RST release
) PLL_ADV_inst (
	.CLKFBDCM(open),			// Output feedback signal used when PLL feeds a DCM
	.CLKFBOUT(feedBack),      // General output feedback signal
	.CLKOUT0(pllClkOut0),		// One of six general clock output signals
	.CLKOUT1(open),        	// One of six general clock output signals
	.CLKOUT2(open),        	// One of six general clock output signals
	.CLKOUT3(open),        	// One of six general clock output signals
	.CLKOUT4(open),        	// One of six general clock output signals
	.CLKOUT5(open),        	// One of six general clock output signals
	.CLKOUTDCM0(open),  		// One of six clock outputs to connect to the DCM
	.CLKOUTDCM1(open),  		// One of six clock outputs to connect to the DCM
	.CLKOUTDCM2(open),  		// One of six clock outputs to connect to the DCM
	.CLKOUTDCM3(open),  		// One of six clock outputs to connect to the DCM
	.CLKOUTDCM4(open),  		// One of six clock outputs to connect to the DCM
	.CLKOUTDCM5(open),  		// One of six clock outputs to connect to the DCM
	.DO(open),          		// Dynamic reconfig data output (16-bits)
	.DRDY(open),         		// Dynamic reconfig ready output
	.LOCKED(locked),          // Active high PLL lock signal
	.CLKFBIN(feedBackG), 		// Clock feedback input
	.CLKIN1(clkIn),       		// Primary clock input
	.CLKIN2(1'b0),          	// Secondary clock input
	.CLKINSEL(1'b1),      		// Selects '1' = CLKIN1, '0' = CLKIN2
	.DADDR(5'b0),           	// Dynamic reconfig address input (5-bits)
	.DCLK(1'b0),              // Dynamic reconfig clock input
	.DEN(1'b0),             	// Dynamic reconfig enable input
	.DI(16'b0),           		// Dynamic reconfig data input (16-bits)
	.DWE(1'b0),            	// Dynamic reconfig write enable input
	.REL(1'b0),              	// Clock release input (PMCD mode only)
	.RST(rst)                	// Asynchronous PLL reset
);

如果今后我们需要将锁相环的倍频系数改为5的话,直接修改CLKFBOUT_MULT属性的值即可,非常方便。

UDP 简介

这里的UDP不是网络协议,而是用户自定义原语的意思,英文全称为User Defined Primitive。对于FPGA开发者来说的,其实没有必要去写什么自定义原语,因为FPGA芯片底层的硬件都已经明确,但对于从事ASIC芯片开发的工程师来说会比较有用。因此这里,我们仅简单介绍一下Verilog中关于UDP的语法如下:

primitive <primitive_name>(<output_port_name>, <input_port_name_list>);
	<输出端口类型说明>;
<输入端口类型说明>;
<输出端寄存器变量说明>;
<原语初始状态说明>;
table
	<table element 1>;
	<table element 2>;
	……
	<table element n>;
endtable
	endprimitive

上述即为UDP的语法,table的内容其实就是该原语的真值表。关于UDP有一些注意事项,诸如:每个UDP只能有一个输出;最多有10个输入,但尽量控制在5个以内,否则table将会巨大无比;端口只能是标量,不能是向量,即均为1bit宽的;不支持高阻(z)逻辑;不能被综合等等。以下即为一个使用UDP语法描述的2输入1输出多路复用器的原语例子:

primitive mux2in1(dOut, dIn1, dIn2, sel);
output dOut;
input dIn1, dIn2, sel;
		table
		//dIn1	dIn2		sel	:	dOut
		0		?		0	:	0
		1		?		0	:	1
		?		0		1	:	0
		?		1		1	:	1
		endtable
endprimitive

其中,“?”号表示该输入的值不需要关心。

发布了806 篇原创文章 · 获赞 1541 · 访问量 151万+

猜你喜欢

转载自blog.csdn.net/Reborn_Lee/article/details/105020930