第5章 CHAPTER 5 基于IP的设计 第5章基于IP的设计 本章主要介绍Quartus Prime中可重复利用的参数化模块库(LPM)设计资源,讲述如何配置和实例引用参数化模块等IP资源。希望读者通过这一章的内容,能够利用Quartus Prime软件工具提供的参数化模块资源对常用电路进行高效快速的HDL设计。 5.1IP核 IP(Intellectual Property)原指知识产权、著作权等,在IC设计领域通常被理解为实现某种功能的设计。IP核则是完成某种常用但是比较复杂的算法或功能(如FIR滤波器、SDRAM控制器、PCI接口等),并且参数可修改的电路模块,又称为IP模块。随着CPLD/FPGA的规模越来越大,设计越来越复杂,越来越多的人开始认识到IP核以及IP复用技术的优越性,并努力推动IP复用设计技术的发展。 根据实现的不同,IP核可以分为三类: 完成行为域描述的软核(Soft Core),完成结构域描述的固核(Firm Core)和基于物理域描述并经过工艺验证的硬核(Hard Core)。三种IP核的特点比较见表51。不同的用户可以根据自己的需要订购不同的IP产品。 表51三种IP核的特点比较 软(soft)IP核固(firm)IP核硬(hard)IP核 描述内容模块功能模块逻辑结构物理结构 提供方式HDL文档门电路级网表,对应具体工艺网表电路物理结构掩模版图和全套工艺文件 优点灵活,可移植 缺点后期开发时间长介于两者之间 后期开发时间短 灵活性差,不同工艺难移植 (1) 软核: 用硬件描述语言(HDL)的形式描述功能的IP核,与具体的实现技术无关。软核是集成电路设计的高层描述,灵活性大。软核可以用于多种制作工艺,在新功能模块中重新配置,以实现重定目标电路。此类IP核只通过了功能和时序验证,其他的实现内容及相关测试等均需要使用者自己完成,因此软核IP用户的后继工作较大。 (2) 硬核: IP硬核是基于半导体工艺的物理设计,已有固定的拓扑布局和具体工艺,并已经过工艺验证,具有可保证的性能。提供给用户的形式是电路物理结构掩膜版图和全套工艺文件,允许设计者将IP快速集成在衍生产品中。因为与工艺相关,硬核IP的灵活性较差。 (3) 固核: 在设计阶段介于软核和硬核之间的IP核。除了完成软核所有设计外,固核还完成了门级电路综合和时序仿真等环节,以RTL描述和可综合网表的形式提交。固核的用户使用灵活性介于软核和硬核之间。 图51LPM种类选择界面 Intel公司以及第三方IP合作伙伴给用户提供了很多可用的功能模块,它们基本可以分为两类: 免费的LPM宏功能模块(Megafunctions/LPM)和需要授权使用的IP知识产权(MEGACORE)。这两者只是从实现的功能上区分,使用方法上则基本相同。 LPM宏功能模块是一些复杂或高级的构建模块,可以在Quartus Prime设计文件中和门、触发器等基本单元一起使用,这些模块的功能一般都是通用的,例如Counter、FIFO、RAM等。Altera提供的可参数化LPM宏功能模块和LPM函数均为Altera器件结构做了优化,而且必须使用宏功能模块才可以使用一些Altera特定器件的功能,例如存储器、DSP块、LVDS驱动器、PLL电路。 通过菜单Tools→IP Catalog,并在IP Catalog中输入LPM,会出现Quartus Prime软件已安装的LPM种类,如图51所示。通过选择需要的LPM,单击并进行修改。 5.2触发器IP核的VHDL设计应用 触发器(FlipFlop)是数字电路设计中的基本单元,尤其是D触发器,通常被用来做延时和缓存处理。第4章给出了利用多个D触发器构造移位寄存器和m序列发生器的示例。将图429和图430给出的移位寄存器和m序列发生器结合在一起,可以形成串行输入初始状态的序列发生器,利用原理图方式进行设计,结果如图52所示。 图52利用触发器构造序列发生器 触发器的延迟功能与移位寄存器功能类似,Altera LPM宏功能模块中将两种功能结合在一起,用同一个模块实现。 如图53所示,在原理图输入模式下,可以在Symbol界面下,在megafunctions →storage下使用宏功能模块LPM_DFF完成功能更复杂的D触发器。 BDF(原理图)文件中插入LPM_DFF后,双击右上角参数列表或者选择右键菜单Properties后,可以进行LPM_DFF的端口和参数设置,如图54和图55所示。 图53原理图输入方式下的LPM_DFF 图54LPM_DFF端口设置 图55LPM_DFF参数设置 利用图54所示页面,对端口的状态设置为使用或不使用,可以改变LPM_DFF的端口,从而使相应的功能有效或无效。图55所示的参数设置,可以确定D触发器的级数和初始值等。 通过MegaWizard PlugIn Manager同样可以进行D触发器设计。在MegaWizard PlugIn Manager中,没有LPM_DFF,而是命名为LPM_SHIFTREG。在IP Catalog栏中输入LPM_SHIFTREG,并单击,弹出Save IP Variation对话框,如图56所示; 选择文件类型为VHDL,并命名为LPM_SHIFTREG1,然后单击OK,打开MegaWizard PlugIn Manager界面。 图56触发器应用设计——LPM_SHIFTREG 在图57和图58所示参数设置页面,可以对LPM_SHIFTREG进行各种属性设置,这里设置了并行输出q的宽度为5bit,表示内部有5级D触发器,形成5位的移位寄存器。另外还有输出端口的选择和输入端口的配置,如并行输入、输出端口以及同步、异步端口设置等。 图57LPM_SHIFTREG参数设置(1) 图58LPM_SHIFTREG参数设置(2) 比较LPM_DFF和LPM_SHIFTREG可以看到,二者实现的功能相同,对比分析可以更好地理解各个端口的功能和使用方法。LPM_SHIFTREG的其他设置可采取默认值,最终可以实现定制的LPM_SHIFTREG功能。 编写例5.1所示VHDL代码,对产生的5位LPM_SHIFTREG进行调用,可以产生第4章m序列产生器设计的相同功能。 【例5.1】调用LPM_SHIFTREG模块形成m序列。 LIBRARY ieee; USE ieee.std_logic_1164.all; ENTITY myShift5Reg IS port(clk : in std_logic; qout : buffer STD_LOGIC_VECTOR(0 to 4); dout: out std_logic ); END myShift5Reg; ARCHITECTURE rtl OF myShift5Reg IS COMPONENT LPM_SHIFTREG1 IS PORT ( clock: IN STD_LOGIC ; shiftin: IN STD_LOGIC ; q: OUT STD_LOGIC_VECTOR (0 to 4); shiftout: OUT STD_LOGIC ); END COMPONENT; begin mySReg_inst : LPM_SHIFTREG1 PORT MAP ( clock => clk, shiftin => not (qout(0) xor qout(2)), q => qout, shiftout => dout ); end rtl; 5.3存储器IP核的VHDL设计应用 存储器是FPGA设计中常用的模块之一,包括RAM、ROM等。可以通过模板(Template)很快给出完整代码,如例5.2给出的最基本单口RAM代码就是通过菜单Edit→Insert Template获得的。 【例5.2】单口RAM模板。 library ieee; use ieee.std_logic_1164.all; entity single_port_ram is generic (DATA_WIDTH : natural ∶= 8; ADDR_WIDTH : natural ∶= 6 ); port (clk : in std_logic; addr : in natural range 0 to 2**ADDR_WIDTH - 1; data : in std_logic_vector((DATA_WIDTH-1) downto 0); we : in std_logic ∶= '1'; q : out std_logic_vector((DATA_WIDTH -1) downto 0) ); end entity; architecture rtl of single_port_ram is --Build a 2-D array type for the RAM subtype word_t is std_logic_vector((DATA_WIDTH-1) downto 0); type memory_t is array(2**ADDR_WIDTH-1 downto 0) of word_t; --Declare the RAM signal. signal ram : memory_t; --Register to hold the address signal addr_reg : natural range 0 to 2**ADDR_WIDTH-1; begin process(clk) begin if(rising_edge(clk)) then if(we = '1') then ram(addr) <= data; end if; --Register the address for reading addr_reg <= addr; end if; end process; q <= ram(addr_reg); end rtl; 下面利用DES数据加密算法中的S盒设计,给出通过MegaWizard PlugIn Manager进行单口RAM设计的过程。 通过菜单Tools→IPCatalog,在IPCatalog栏中输入RAM,然后会列出相关的IP核,在这里选择RAM:1PORT,并双击进入如图56所示的界面。设计语言选择VHDL,输入输出文件名,然后单击OK按钮,依次进入图59到图511所示的参数设置界面。 图59单端口RAM模块的参数设置(1) 图510单端口RAM模块的参数设置(2) 因为DES算法的S盒是6进4出即包含64个4bit数据,因此,在图59页面中设置存储容量64words、数据宽度4bit,输入输出使用相同时钟; 图510页面中设置字节使能、寄存器存储、独立读使能等相关属性; 在图511中可以指定RAM的初始内容,这里使用的是内存初始化文件sbox.mif,该初始化文件的生成过程参照图512和图513所示。 图511单端口RAM模块的参数设置(3) 首先选择文件菜单下的新建文件,选择Memory Initialization File,在如图512所示弹出对话框中设置存储字数和字大小,单击OK按钮后,可以生成如图513所示mif文件编辑界面,将DES的S盒数据输入,然后保存即可。 图512内存初始化文件容量设置 图513内存初始化文件内容编辑 下面是定制的RAM模块实现代码,可以看到,RamTest_SBox是通过例化Altera内部模块altsyncram,然后进行端口配置实现的。 LIBRARY ieee; USE ieee.std_logic_1164.all; LIBRARY altera_mf; USE altera_mf.all; ENTITY RamTest_SBox IS PORT ( address : IN STD_LOGIC_VECTOR (5 DOWNTO 0); clock : IN STD_LOGIC ∶= '1'; data : IN STD_LOGIC_VECTOR (3 DOWNTO 0); rden : IN STD_LOGIC ∶= '1'; wren : IN STD_LOGIC ; q : OUT STD_LOGIC_VECTOR (3 DOWNTO 0) ); END RamTest_SBox; ARCHITECTURE SYN OF ramtest_sbox IS SIGNAL sub_wire0 : STD_LOGIC_VECTOR (3 DOWNTO 0); COMPONENT altsyncram GENERIC ( clock_enable_input_a: STRING; clock_enable_output_a: STRING; init_file: STRING; intended_device_family: STRING; lpm_hint: STRING; lpm_type: STRING; numwords_a: NATURAL; operation_mode: STRING; outdata_aclr_a: STRING; outdata_reg_a: STRING; power_up_uninitialized : STRING; read_during_write_mode_port_a : STRING; widthad_a : NATURAL; width_a : NATURAL; width_byteena_a : NATURAL ); PORT ( address_a : IN STD_LOGIC_VECTOR (5 DOWNTO 0); clock0 : IN STD_LOGIC ; data_a : IN STD_LOGIC_VECTOR (3 DOWNTO 0); wren_a : IN STD_LOGIC ; q_a : OUT STD_LOGIC_VECTOR (3 DOWNTO 0); rden_a : IN STD_LOGIC ); END COMPONENT; BEGIN q<= sub_wire0(3 DOWNTO 0); altsyncram_component : altsyncram GENERIC MAP ( clock_enable_input_a => "BYPASS", clock_enable_output_a => "BYPASS", init_file => "sbox.mif", intended_device_family => "Cyclone IV GX", lpm_hint => "ENABLE_RUNTIME_MOD=NO", lpm_type => "altsyncram", numwords_a => 64, operation_mode => "SINGLE_PORT", outdata_aclr_a => "NONE", outdata_reg_a => "CLOCK0", power_up_uninitialized => "FALSE", read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ", widthad_a => 6, width_a => 4, width_byteena_a => 1 ) PORT MAP ( address_a => address, clock0 => clock, data_a => data, wren_a => wren, rden_a => rden, q_a => sub_wire0 ); END SYN; 对生成程序进行综合,然后编写测试用例进行仿真,例5.3给出的测试用例向测试对象提供时钟激励,读信号rden始终有效,并且在该时钟上升沿顺序给出地址数据,从而完成S盒内容读取的仿真。 【例5.3】基于存储器IP的DES算法S盒实现的仿真测试用例。 LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; ENTITY RamTest_SBox_vhd_tst IS END RamTest_SBox_vhd_tst; ARCHITECTURE RamTest_SBox_arch OF RamTest_SBox_vhd_tst IS SIGNAL address : STD_LOGIC_VECTOR(5 DOWNTO 0) ∶= "000000"; SIGNAL clock : STD_LOGIC ∶= '0'; SIGNAL data : STD_LOGIC_VECTOR(3 DOWNTO 0); SIGNAL q : STD_LOGIC_VECTOR(3 DOWNTO 0); SIGNAL rden : STD_LOGIC ∶= '1';--读信号始终有效 SIGNAL wren : STD_LOGIC ∶= '0';--不允许写 COMPONENT RamTest_SBox PORT ( address : IN STD_LOGIC_VECTOR(5 DOWNTO 0); clock : IN STD_LOGIC; data : IN STD_LOGIC_VECTOR(3 DOWNTO 0); q : OUT STD_LOGIC_VECTOR(3 DOWNTO 0); rden : IN STD_LOGIC; wren : IN STD_LOGIC ); END COMPONENT; BEGIN i1 : RamTest_SBox PORT MAP ( address => address, clock => clock, data => data, q => q, rden => rden, wren => wren ); always : PROCESS (clock)--时钟下降沿给出地址激励信号 BEGIN if falling_edge(clock) then if (address = "11111") then address <= "00000"; else address <= address + "00001"; end if; end if; END PROCESS always; CLOCK_Pro : PROCESS --提供时钟激励信号,周期20ns BEGIN wait for 10 ns; clock <= not clock; END PROCESS CLOCK_Pro; END RamTest_SBox_arch; 基于上述测试用例,通过ModelSim进行仿真,得到图514所示仿真波形,如图中所示,地址输入在时钟下降沿发生变化,读信号始终有效,在时钟上升沿数据正常输出。图中data始终显示红色,是因为测试用例中没有给出该信号的具体值。该信号是向RAM写入的数据,由于写信号wren始终为0,因此没有真正写入RAM中。 图514基于RAM存储器IP的DES算法S盒的仿真波形 密码算法的S盒变换通常是固定数据的,读者可以将上述设计修改为ROM实现,也可以通过wren和data端口对S盒内容进行更改,从而可以用在不同的算法中。 5.4锁相环IP核的VHDL设计应用 Intel在很多型号的FPGA芯片中都提供有专用锁相环电路,用来实现设计所需多种时钟频率。通过Quartus Ⅱ或者Quartus Prime软件的参数化模块库中的PLL模块可以很好地利用FPGA芯片中的锁相环资源。下面利用ALTPLL模块的参数配置和实例化对锁相环电路IP核的VHDL设计进行简单介绍。 在IPCatalog栏中输入PLL或者ALTPLL,然后在Library中单击ALTPLL,在弹出的如图515所示的Save IP Variation界面,选择VHDL作为创建的设计文件语言,将输出文件命名为mypll。单击OK按钮后进入图516所示的对话框,在这里对输入时钟inclk0的频率和PLL的工作模式进行设置,假设输入频率为100MHz,工作模式采用normal模式。输入频率用于输出频率设置的参考,不与实际工作频率相关。Altera器件共有四种工作模式: normal模式,PLL的输入引脚与I/O单元的输入时钟寄存器相关联; zero delay buffer模式,PLL的输入引脚和PLL的输出引脚的延时相关联,通过PLL的调整,到达两者“零”延时; External feedback模式,PLL的输入引脚和PLL的反馈引脚延时相关联; no compensation模式,不对PLL的输入引脚进行延时补偿。 图515创建新的参数化模块——锁相环PLL 图516参数化模块ALTPLL的参数设置 参数化模块ALTPLL可以设置9个输出时钟,这里仅使用两个输出时钟: c0和c1,分别设置为300MHz和75MHz,如图517和图518所示。这里的时钟输出频率都是以设定乘因子和除因子的方式给出,也可以直接输入预期时钟频率(Requested Setting)。 图517参数化模块ALTPLL的参数设置c0 图518参数化模块ALTPLL的参数设置c1 时钟模块的其他设置均采用默认设置。通过给定输入时钟频率进行仿真,可以得到如图519所示的仿真图。 图519锁相环PLL的仿真结果 分析图519所示仿真波形,其中inclk0是输入时钟信号,时钟周期为10000ps,时钟频率为100MHz; c0和c1是输出信号,三个时钟信号都是占空比1∶1的时钟信号。如图所示,inclk0经过1个时钟周期后,c0恰好经过了3个时钟周期,即c0的频率是inclk0的3倍,即300MHz。再分析c1和c0的周期特性,可以发现,c1的频率是c0的1/4,即75MHz。所以,通过仿真波形可知,仿真结果与ALTPLL的设置一致,PLL设计正确。 5.5运算电路IP核的VHDL设计应用 Quartus Prime软件的参数化模块库对运算单元的IP模块有很好的支持,常用的数学运算都可以在这里完成。下面利用LPM_ADD_SUB模块设计一个简单的8位加法器,对运算电路IP核的VHDL设计进行简单介绍。 在IP Catalog栏中输入LPM_ADD_SUB,在Library中选择LPM_ADD_SUB并双击,弹出Save IP Variation界面,如图520所示,选择VHDL作为创建的设计文件语言,将输出文件命名为adder。单击OK按钮后进入如图521所示的对话框,指定输入数据的位宽为8bit,只选择加法功能; 单击Next按钮,进入图522所示对话框确认输入数据的类型,按默认值设置两个操作数均为可变无符号数。再次单击Next按钮,在图523所示对话框,指定加入进位输入端和进位输出端。然后,在图524页面下,设置流水线设计参数,其他内容可按默认设置使用。 图520创建新的参数化模块——运算电路LPM_ADD_SUB 图521参数化模块LPM_ADD_SUB的参数设置(1) 图522参数化模块LPM_ADD_SUB的参数设置(1) 图523参数化模块LPM_ADD_SUB的参数设置(2) 图524参数化模块LPM_ADD_SUB的参数设置(3) 通过向导定制完加法器后,可以创建相应的VHDL文件,然后编写如例5.4所示的测试用例,利用ModelSim进行仿真,可以得到如图525所示的仿真结果。 【例5.4】加法器测试用例。 LIBRARY ieee; USE ieee.std_logic_1164.all; ENTITY adder_vhd_tst IS END adder_vhd_tst; ARCHITECTURE adder_arch OF adder_vhd_tst IS SIGNAL cin : STD_LOGIC; SIGNAL cout : STD_LOGIC; SIGNAL dataa : STD_LOGIC_VECTOR(7 DOWNTO 0); SIGNAL datab : STD_LOGIC_VECTOR(7 DOWNTO 0); SIGNAL result : STD_LOGIC_VECTOR(7 DOWNTO 0); COMPONENT adder PORT ( cin : IN STD_LOGIC; cout : OUT STD_LOGIC; dataa : IN STD_LOGIC_VECTOR(7 DOWNTO 0); datab : IN STD_LOGIC_VECTOR(7 DOWNTO 0); result : OUT STD_LOGIC_VECTOR(7 DOWNTO 0) ); END COMPONENT; BEGIN i1 : adder PORT MAP ( --list connections between master ports and signals cin => cin, cout => cout, dataa => dataa, datab => datab, result => result ); init : PROCESS --variable declarations BEGIN --code that executes only once wait for 1 ns; cin <= '0' ; dataa <= x"3f"; datab <= x"57"; --等待1ns后数据有效 wait for 1 ns; cin <= '1' ; dataa <= x"7f"; datab <= x"57"; wait for 1 ns; cin <= '0' ; dataa <= x"9a"; datab <= x"85"; wait for 1 ns; cin <= '1' ; dataa <= x"97"; datab <= x"68"; WAIT; END PROCESS init; END adder_arch; 图525给出了上述测试用例仿真后波形,图中数据开始的红色部分是由于测试用例中首先等待了1ns后才给出有效数据,这一段时间输入没有初始值,所以数据均为X。这里给出的运算电路是一个带进位的8位全加器,数据显示均为十六进制。从图中可以看出,在1000ps处,输入数据为: 进位位0,加数dataa和datab分别是0x3F和0x57,结果result为0x86,进位输出0,之后在2000ps、3000ps、4000ps处,随着输入数据的变化,输出结果做出相应变化,分析可知,输出符合设计要求,设计结果正确。 图525加法器仿真结果