乘法器之二(并行乘法器)
5.用存储器块实现软乘法器
可以用StratixII、Stratix和StratixGX M512或者M4K 和CycloneII和Cyclone的M4K RAM存储器块作为LUTs实现DSP应用中的乘法器。
所有系数的组合会预先算好并保存在M512或者M4K中。RAM块的地址对应乘法器的一个操作数,每个地址内存放了一个唯一的计算结果,
这个结果是基于要实现的乘法器的类型由输入操作数和一个已知的参数计算得到的。
StratixII、Stratix、StratixGX、CycloneII和Cyclone器件支持的5种软乘法器的类型是:
(1)并行乘法器(Parallel multiplication)-每个时钟周期由多个存储器块产生一个乘法结果。该模式在高速数据应用中非常有用。
(2)半并行乘法器(Semi-parallel multiplication)-每个存储器块多个时钟周期产生一个乘法器结果。
该模式在最小均方值(LMS)更新和参数均衡中非常有用。
(3)乘加器(Sum of multiplication)-一个存储器或者一组存储器块产生一组乘法器运算结果的总和。
该模式多用于FIR滤波器和DCTs(离散余弦变换)。
(4)混和式乘法器(Hybrid multiplication)-这是半并行模式和乘加器的结合和优化。该模式非常适合用于复数乘法,比如复数FFT和IIR滤波器。
(5)全变量乘法器(Fully variable multiplication)-该模式适用于输入数据和参数都是变量的软乘法器。该模式很适合实现低精度乘法运算。
下面将举例说明各个模式的乘法器。
1) 并行乘法器(Parallel multiplication)
并行乘法是是指分别将输入乘数的一部份位,乘上被乘数或者系数,然后将所有的部份积相加得到结果。
输入的所有位都是并行载入到RAM块的地址寄存器端口,每个时钟周期完成一次乘法。例如,一个16位的输入总线
被分成两组8位(一组低8个最低有效位[LSB],另一组高8个最高有效位[MSB]),且同时被移位到两个RAM块的地
址端口。RAM块的输出为乘数和被乘数(或系数)相乘的积。图4显示了16位数据输入、10位常系数并行乘法器的分解。
图5是由RAM LUT实现图4所示的并行乘法器的示意图。由于一个并行乘法器每个时钟周期允许一个新的数据输入,所以图5中的这种实
现方法需要三个时钟周期(一个时钟周期用于加载输入值到RAM块的地址端口,另两个时钟周期用于流水延迟)来运算得到最终的乘法
结果。每个时钟周期均可以从RAM块中得到最新的部份积,得到的这些部份积会根据其权位求和。每个部份积乘法会产生一个18位的输出,
所有部份积累加后得到一个26位的输出。
上图中的表中的C分别为输入数据的高8位和低8位。
图5 用M4K RAM块做LUTs来实现16位输入,10位系数的并行乘法器(1)
注:(1)用于增加系统性能的可选的流水寄存器。
图5显示了一个16位输入的乘法器实现,将16位的输入拆分成高低8位两块。该乘法器需要两个M4K RAM块,一个用于
最低有效位部分,另一个用于最高有效位部分,对于有符号的输入总线,接收高8位(MSB)的M4K RAM块必须包含预
先计算好的有符号系数值,因为流入M4K RAM块的高8位(MSB)被认为是有符号值。而接收低8位(LSB)的M4K RAM
块则必须包含预先计算好的无符号系数值,因为流入M4K RAM块的低8位被认为是无符号数。
M4K RAM块有256X18位,所以每个M4K RAM块可存放这种系数的最大地址位数是8(2^8=256个地址)。输入总线
和系数尺寸直接影响了用于实现乘法器的RAM块的数目以及其配置形式。并行乘法模式能达到最大的数据吞
吐量(例如每周期允许更新一次数据值)。
还可以用QuartusII Megafunction函数altmenmult来实现并行定系数乘法器。可以用MegaWizard Plug-In Manager
参数化配置altmenmult来定制一个并行、定系数软乘法器。通过设定altmenmult输入和系数的位宽、RAM块的类型来
选定来更有效的实现一个半并行或并行模式软乘发器。图6和图7分别显示了实现图5所示最高有效位和最低有效位M4K RAM
块的16位输入、10位系数并行乘法器所需的设置,本例中乘法器的系数是一个常数值5。
图6 MSB RAM块16位输入、10位系数并行乘法器的altmemmult MegaWizard设置
图7 LSB RAM块16位输入、10位系数并行乘法器的altmemmult MegaWizard设置
sload_data信号和MegaWizard窗口右下方显示的信息指示altmemmult Megafunction实现的是半并行或者并行
模式软乘法器。并行软乘法器没有sload_data信号且Megafunction每个时钟周期都可以接受一个新输入。
Megafunction altmemmult只能实现较小的并行乘法器(例如:8位输入,10位系数乘法器)。更大的乘法器需
要多个Megafunction altmemmult分别产生各个部份积来得到所需结果。为了得到最终的乘法结果,所有的部份
积必须在Megafunction altmemmult以外的一个后期加法器中加起来。
a、定系数乘法器
图8显示了在图5中所举例子的仿真结果。本例输入有一个十进制数297,乘上常系数5。
表16和表17分别显示了在StratixII和Stratix器件中实现并行常系数乘法器的结果。例中乘法器是用altmemmult
Megafunction实现的。
b、可变系数乘法器
实现定系数乘法器的时候,可以把StratixII、Stratix、StratixGX、CycloneII和Cyclone中的存储器
用作ROM。对于变系数乘法器,存储器块必须被实现为RAM块,如此方可以写入或者更新预先计算出的
系数。图9显示了一个用M4K单口RAM块实现一个变系数并行乘法器的例子。用此方法,当系数更新的
时候,乘法器会暂停工作。但如果用多组RAM块来存储不同的预算系数的时候,就可以使乘法器在一个
时钟周期内在两组不同系数之间切换。实现这个功能的一种方法是将RAM块分区,不同区存储不同的系数
,并用MSB地址位来选择哪一组系数被使用。此外使用双口RAM块,可以在一个RAM区写入或者更新系
数时,同时用另一个RAM区的系数来实现乘法运算。
提醒:altmemmult兆核也可以通过在MegaWizard窗口中使能“Create ports to allow loading
coefficients”选项来支持变系数并行和半并行软乘法器。
表18和表19分别显示了在StratixII和Stratix器件中实现并行变系数乘法器的结果。
参考程序(自己写的,希望大家多多指点)如下:
LIBRARY altera_mf;
USE altera_mf.altera_mf_components.all;
library lpm;
use lpm.lpm_components.all;
LIBRARY ieee;
USE ieee.std_logic_1164.all;
use ieee.std_logic_signed.all;
use ieee.std_logic_arith.all;
entity parallel_mul is
generic( width1 : integer := 8;
width2 : integer := 18);
port(
multiplier : in std_logic_vector(15 downto 0);
wr_address : in std_logic_vector(width1-1 downto 0);
wr_data2 : in std_logic_vector(width2-1 downto 0);
wr_data1 : in std_logic_vector(width2-1 downto 0);
wr_enable : in std_logic ;
clk : in std_logic;
result : out std_logic_vector(width1 +width2 - 1 downto 0));
end parallel_mul;
architecture behaver of parallel_mul is
signal ram1_out,ram2_out : std_logic_vector(width2-1 downto 0);
signal add_data1, add_data2 : std_logic_vector(width1+width2-1 downto 0);
signal add_data1_temp, add_data2_temp : std_logic_vector(width1 + width2-1 downto 0);
signal result_temp : std_logic_vector(width1 + width2 -1 downto 0);
begin
ram1 : altdpram
generic map(
WIDTH => WIDTH2,
WIDTHAD => WIDTH1,
LPM_FILE => "lpm_ram1.mif")
port map(
data => wr_data1,
rdaddress => multiplier(15 downto 8),
wraddress => wr_address,
wren => wr_enable,
q => ram1_out,
inclock => clk,
outclock => clk);
ram2 : altdpram
generic map(
WIDTH => WIDTH2,
WIDTHAD => WIDTH1,
LPM_FILE => "lpm_ram2.mif")
port map(
data => wr_data1,
rdaddress => multiplier(7 downto 0),
wraddress => wr_address,
wren => wr_enable,
q => ram2_out,
inclock => clk,
outclock => clk);
process(clk)
begin
if clk'event and clk = '1' then
add_data1 <= ram1_out&"00000000" ;
add_data2 <= "00000000" & ram2_out;
--add_data1_temp <= add_data1 sll 8;
--add_data2_temp <= add_data2;
result_temp <= add_data1 + add_data2;
end if;
end process;
result <= result_temp;
end behaver;