一、什么是原语?
原语,英文名称primitive,是FPGA软件集成开发环境所提供的一系列底层逻辑功能单元。由于是底层逻辑功能单元,所以它们往往跟目标FPGA芯片以及芯片厂商紧密相关,因此不同厂商、不同器件的原语往往不能通用。当编译器对我们的HDL代码进行编译时,其中间环节的一些输出往往就是由原语组成的逻辑网表。因此,原语往往是不参与综合过程的,而使用原语描述的逻辑往往也不会被综合工具所优化。例如,Xilinx公司的ISE软件集成开发环境中的unisims库中定义了所有用于综合的原语,而simprims库中则定义了所有用于实现的原语。需要注意的是,在ISE安装目录下的verilog\src\unisims或verilog\src\simprims文件夹下去看这些原语的代码,可以发现其实这些并不是真正的原语,而是在原语的基础上又封装了一层,不过人们常常将它们也泛称为原语。
二、原语的使用
时钟相关原语
如果时钟信号不是由FPGA芯片的专用时钟pin(或pad)引入FPGA的,那么它通常就需要在FPGA内部被显式的连接到时钟树资源上,否则,直接使用这种不经过时钟树的时钟信号,会给FPGA设计的时序带来非常麻烦的问题,进而导致逻辑行为失败。
可是HDL代码仅仅描述功能,无法向编译器表达“希望将某一时钟信号连接到时钟树资源”这样的一层意思,那么此时,就需要使用类似BUFG这样的库里提供的底层模块来进行指示。例如:
// 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这样的库里提供的底层模块或原语来进行指示。
其中IBUFDS、和OBUFDS都是差分信号缓冲器,用于不同电平接口之间的缓冲和转换。IBUFDS 用于差分输入,OBUFDS用于差分输出。
- IBUFDS是差分输入缓冲器,支持低压差分信号(如LVCMOS、LVDS等)。在IBUFDS中,一个电平接口用两个独特的电平接口(I和IB)表示。一个可以认为是主信号,另一个可以认为是从信号。
- OBUFDS 是一个差分输出缓冲器,用于将来自 FPGA 内部逻辑的信号转换成差分信号输出,支持 TMDS、LVDS等电平标准。它的输出用O和OB两个独立接口表示。一个可以认为是主信号,另一个可以认为是从信号。
用法:
//------------------------------------------------------------------------
//--IBUFDS测试模块
//------------------------------------------------------------------------
//------------<模块及端口声明>----------------------------------------
module IBUFDS_test(
input clk , //时钟,50M
input rst_n , //复位,低电平有效
input data_p , //输入数据,差分+
input data_n , //输入数据,差分-
output out
);
//------------<例化原语>----------------------------------------
IBUFDS #(
.DIFF_TERM ("FALSE") , // Differential Termination
.IBUF_LOW_PWR ("TRUE") , // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD ("DEFAULT") // 选择I/O电平标准,这里选择默认
)
IBUFDS_inst (
.O (out) , // 输出
.I (data_p) , // 差分输入+(需要直接连接到顶层端口)
.IB (data_n) // 差分输入-(需要直接连接到顶层端口)
);
endmodule
//------------------------------------------------------------------------
//--OBUFDS测试模块
//------------------------------------------------------------------------
//------------<模块及端口声明>----------------------------------------
module OBUFDS_test(
input clk , //时钟,50M
input rst_n , //复位,低电平有效
input data , //输入数据
output out_p , //输出数据,差分+
output out_n //输出数据,差分-
);
//------------<例化原语>----------------------------------------
OBUFDS #(
.IOSTANDARD ("DEFAULT") , // 选择I/O电平标准,这里选择默认
.SLEW ("SLOW") // 选择输出速率,这里选择SLOW
)
OBUFDS_inst (
.O (out_p) , // 差分输出+(需要直接连接到顶层端口)
.OB (out_n) , // 差分输出-(需要直接连接到顶层端口)
.I (data) // 输入
);
endmodule
接口相关原语
当需要使用接口资源中的寄存器来实现高速数据采集时,如果直接调用相关的原语,编译器便会利用接口资源中的寄存器来做事情。例如,使用ODDR、IDDR、IDDR2CLK、ISERDES、OSERDES等原语后,编译器便会利用接口资源的寄存器来实现相关接口功能,这样便能达到比较高的性能。例如:
// 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中调用原语的方式,将会使修改的工作量大大降低。
// 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 简介
英文全称为User Defined Primitive,是用户自定义原语的意思。
语法:文章来源:https://www.toymoban.com/news/detail-692912.html
primitive <primitive_name>(<output_port_name>, <input_port_name_list>);
<输出端口类型说明>;
<输入端口类型说明>;
<输出端寄存器变量说明>;
<原语初始状态说明>;
table
<table element 1>;
<table element 2>;
……
<table element n>;
endtable
endprimitive
table的内容其实就是该原语的真值表。每个UDP只能有一个输出;最多有10个输入,但尽量控制在5个以内,否则table将会巨大无比;端口只能是标量,不能是向量,即均为1bit宽的;不支持高阻(z)逻辑;不能被综合等等。文章来源地址https://www.toymoban.com/news/detail-692912.html
到了这里,关于Xilinx原语的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!