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;
