第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核的特点比较见表51。不同的用户可以根据自己的需要订购不同的IP产品。


表51三种IP核的特点比较



软(soft)IP核固(firm)IP核硬(hard)IP核

描述内容模块功能模块逻辑结构物理结构
提供方式HDL文档门电路级网表,对应具体工艺网表电路物理结构掩模版图和全套工艺文件

优点灵活,可移植
缺点后期开发时间长介于两者之间
后期开发时间短
灵活性差,不同工艺难移植

(1) 软核: 用硬件描述语言(HDL)的形式描述功能的IP核,与具体的实现技术无关。软核是集成电路设计的高层描述,灵活性大。软核可以用于多种制作工艺,在新功能模块中重新配置,以实现重定目标电路。此类IP核只通过了功能和时序验证,其他的实现内容及相关测试等均需要使用者自己完成,因此软核IP用户的后继工作较大。
(2) 硬核: IP硬核是基于半导体工艺的物理设计,已有固定的拓扑布局和具体工艺,并已经过工艺验证,具有可保证的性能。提供给用户的形式是电路物理结构掩膜版图和全套工艺文件,允许设计者将IP快速集成在衍生产品中。因为与工艺相关,硬核IP的灵活性较差。
(3) 固核: 在设计阶段介于软核和硬核之间的IP核。除了完成软核所有设计外,固核还完成了门级电路综合和时序仿真等环节,以RTL描述和可综合网表的形式提交。固核的用户使用灵活性介于软核和硬核之间。






图51LPM种类选择界面

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种类,如图51所示。通过选择需要的LPM,单击并进行修改。
5.2触发器IP核的VHDL设计应用
触发器(FlipFlop)是数字电路设计中的基本单元,尤其是D触发器,通常被用来做延时和缓存处理。第4章给出了利用多个D触发器构造移位寄存器和m序列发生器的示例。将图429和图430给出的移位寄存器和m序列发生器结合在一起,可以形成串行输入初始状态的序列发生器,利用原理图方式进行设计,结果如图52所示。


图52利用触发器构造序列发生器


触发器的延迟功能与移位寄存器功能类似,Altera LPM宏功能模块中将两种功能结合在一起,用同一个模块实现。
如图53所示,在原理图输入模式下,可以在Symbol界面下,在megafunctions →storage下使用宏功能模块LPM_DFF完成功能更复杂的D触发器。
BDF(原理图)文件中插入LPM_DFF后,双击右上角参数列表或者选择右键菜单Properties后,可以进行LPM_DFF的端口和参数设置,如图54和图55所示。


图53原理图输入方式下的LPM_DFF





图54LPM_DFF端口设置





图55LPM_DFF参数设置


利用图54所示页面,对端口的状态设置为使用或不使用,可以改变LPM_DFF的端口,从而使相应的功能有效或无效。图55所示的参数设置,可以确定D触发器的级数和初始值等。
通过MegaWizard PlugIn Manager同样可以进行D触发器设计。在MegaWizard PlugIn Manager中,没有LPM_DFF,而是命名为LPM_SHIFTREG。在IP Catalog栏中输入LPM_SHIFTREG,并单击,弹出Save IP Variation对话框,如图56所示; 选择文件类型为VHDL,并命名为LPM_SHIFTREG1,然后单击OK,打开MegaWizard PlugIn Manager界面。


图56触发器应用设计——LPM_SHIFTREG


在图57和图58所示参数设置页面,可以对LPM_SHIFTREG进行各种属性设置,这里设置了并行输出q的宽度为5bit,表示内部有5级D触发器,形成5位的移位寄存器。另外还有输出端口的选择和输入端口的配置,如并行输入、输出端口以及同步、异步端口设置等。


图57LPM_SHIFTREG参数设置(1)





图58LPM_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 PlugIn Manager进行单口RAM设计的过程。
通过菜单Tools→IPCatalog,在IPCatalog栏中输入RAM,然后会列出相关的IP核,在这里选择RAM:1PORT,并双击进入如图56所示的界面。设计语言选择VHDL,输入输出文件名,然后单击OK按钮,依次进入图59到图511所示的参数设置界面。


图59单端口RAM模块的参数设置(1)





图510单端口RAM模块的参数设置(2)


因为DES算法的S盒是6进4出即包含64个4bit数据,因此,在图59页面中设置存储容量64words、数据宽度4bit,输入输出使用相同时钟; 图510页面中设置字节使能、寄存器存储、独立读使能等相关属性; 在图511中可以指定RAM的初始内容,这里使用的是内存初始化文件sbox.mif,该初始化文件的生成过程参照图512和图513所示。


图511单端口RAM模块的参数设置(3)


首先选择文件菜单下的新建文件,选择Memory Initialization File,在如图512所示弹出对话框中设置存储字数和字大小,单击OK按钮后,可以生成如图513所示mif文件编辑界面,将DES的S盒数据输入,然后保存即可。



图512内存初始化文件容量设置




图513内存初始化文件内容编辑



下面是定制的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进行仿真,得到图514所示仿真波形,如图中所示,地址输入在时钟下降沿发生变化,读信号始终有效,在时钟上升沿数据正常输出。图中data始终显示红色,是因为测试用例中没有给出该信号的具体值。该信号是向RAM写入的数据,由于写信号wren始终为0,因此没有真正写入RAM中。


图514基于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,在弹出的如图515所示的Save IP Variation界面,选择VHDL作为创建的设计文件语言,将输出文件命名为mypll。单击OK按钮后进入图516所示的对话框,在这里对输入时钟inclk0的频率和PLL的工作模式进行设置,假设输入频率为100MHz,工作模式采用normal模式。输入频率用于输出频率设置的参考,不与实际工作频率相关。Altera器件共有四种工作模式: normal模式,PLL的输入引脚与I/O单元的输入时钟寄存器相关联; zero delay buffer模式,PLL的输入引脚和PLL的输出引脚的延时相关联,通过PLL的调整,到达两者“零”延时; External feedback模式,PLL的输入引脚和PLL的反馈引脚延时相关联; no compensation模式,不对PLL的输入引脚进行延时补偿。


图515创建新的参数化模块——锁相环PLL





图516参数化模块ALTPLL的参数设置





参数化模块ALTPLL可以设置9个输出时钟,这里仅使用两个输出时钟: c0和c1,分别设置为300MHz和75MHz,如图517和图518所示。这里的时钟输出频率都是以设定乘因子和除因子的方式给出,也可以直接输入预期时钟频率(Requested Setting)。



图517参数化模块ALTPLL的参数设置c0





图518参数化模块ALTPLL的参数设置c1


时钟模块的其他设置均采用默认设置。通过给定输入时钟频率进行仿真,可以得到如图519所示的仿真图。


图519锁相环PLL的仿真结果


分析图519所示仿真波形,其中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界面,如图520所示,选择VHDL作为创建的设计文件语言,将输出文件命名为adder。单击OK按钮后进入如图521所示的对话框,指定输入数据的位宽为8bit,只选择加法功能; 单击Next按钮,进入图522所示对话框确认输入数据的类型,按默认值设置两个操作数均为可变无符号数。再次单击Next按钮,在图523所示对话框,指定加入进位输入端和进位输出端。然后,在图524页面下,设置流水线设计参数,其他内容可按默认设置使用。


图520创建新的参数化模块——运算电路LPM_ADD_SUB





图521参数化模块LPM_ADD_SUB的参数设置(1)





图522参数化模块LPM_ADD_SUB的参数设置(1)





图523参数化模块LPM_ADD_SUB的参数设置(2)





图524参数化模块LPM_ADD_SUB的参数设置(3)


通过向导定制完加法器后,可以创建相应的VHDL文件,然后编写如例5.4所示的测试用例,利用ModelSim进行仿真,可以得到如图525所示的仿真结果。
【例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;

图525给出了上述测试用例仿真后波形,图中数据开始的红色部分是由于测试用例中首先等待了1ns后才给出有效数据,这一段时间输入没有初始值,所以数据均为X。这里给出的运算电路是一个带进位的8位全加器,数据显示均为十六进制。从图中可以看出,在1000ps处,输入数据为: 进位位0,加数dataa和datab分别是0x3F和0x57,结果result为0x86,进位输出0,之后在2000ps、3000ps、4000ps处,随着输入数据的变化,输出结果做出相应变化,分析可知,输出符合设计要求,设计结果正确。



图525加法器仿真结果