[英]4bit ALU VHDL code
我正在為4位ALU編寫代碼,當我想編寫左移操作時遇到問題。 我有兩個輸入(操作數A和操作數B)。 我想將操作數B轉換為十進制(例如,將“ 0010”轉換為“ 2”),然后將操作數A左移2次。 我的代碼已編譯,但我不確定它是否正確。 先感謝您。
entity ALU is
port(
reset_n : in std_logic;
clk : in std_logic;
OperandA : in std_logic_vector(3 downto 0);
OperandB : in std_logic_vector(3 downto 0);
Operation : in std_logic_vector(2 downto 0);
Start : in std_logic;
Result_Low : out std_logic_vector(3 downto 0);
Result_High : out std_logic_vector(3 downto 0);
Ready : out std_logic;
Errorsig : out std_logic);
end ALU;
architecture behavior of ALU is
signal loop_nr : integer range 0 to 15;
begin
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
for i in 0 to loop_nr loop
loop_nr <= to_integer(unsigned(OperandB));
Result_Low <= OperandA(2 downto 0)&'0';
Result_High <= tempHigh(2 downto 0) & OperandA(3);
end loop;
Ready <= '1';
Errorsig <= '0';
when "010" =>
Result_Low <= OperandB(0)& OperandA(3 downto 1);
Result_High <= OperandB(3 downto 1);
Ready <= '1';
when others =>
Result_Low <= (others => '0');
ready <= '0';
Errorsig <= '0';
end case;
end if;
end process;
end behavior;
要向左移動兩次,語法應如下所示:
A <= A sll 2; -左移邏輯2位
我不太明白為什么需要將操作數B轉換為十進制。 可以將其用作二進制或十進制值,或與此有關的十六進制值,與保存該基准的時間無關。
VHDL-2008之前,操作符sll
可能不會一直按預期工作(請在此處閱讀更多信息 ),因此請考慮改用ieee.numeric_std
函數進行移位,例如:
y <= std_logic_vector(shift_left(unsigned(OperandA), to_integer(unsigned(OperandB))));
還要注意,在端口Result_High
聲明為std_logic_vector(3 downto 0)
,但在第41行中將Result_High <= OperandB(3 downto 1)
為Result_High <= OperandB(3 downto 1)
,而賦值的大小要比大小小一位。
假設代碼是使用ieee.numeric_std
的。
敦促您使用sll之類的原因是因為在一般情況下,綜合工具不支持帶有非靜態邊界(loop_nr)的循環語句。 展開的循環需要靜態值來確定展開的循環迭代次數(生成多少硬件)。
正如Morten所指出的那樣,您的代碼沒有分析,這與您斷言它可以編譯相反。
在代碼開頭插入以下四行之后,我們在第41行看到錯誤:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--(blank, a spacer that doesn't show up in the code highlighter)
ghdl -a ALU.vhdl ALU.vhdl:41:26: length of value does not match length of target ghdl: compilation error
看起來像
Result_High <= '0' & OperandB(3 downto 1);
是在case語句中選擇“ 010”(硬編碼為1的srl等效項,大概是為了匹配sll等效項的正確行為)。 之后,您的設計說明將進行分析。
此外,還有其他算法描述錯誤未反映在VHDL語法或語義錯誤中。
編寫一個簡單的測試平台:
library ieee;
use ieee.std_logic_1164.all;
entity alu_tb is
end entity;
architecture foo of alu_tb is
signal reset_n: std_logic := '0';
signal clk: std_logic := '0';
signal OperandA: std_logic_vector(3 downto 0) :="1100"; -- X"C"
signal OperandB: std_logic_vector(3 downto 0) :="0010"; -- 2
signal Operation: std_logic_vector(2 downto 0):= "001"; -- shft right
signal Start: std_logic; -- Not currently used
signal Result_Low: std_logic_vector(3 downto 0);
signal Result_High: std_logic_vector(3 downto 0);
signal Ready: std_logic;
signal Errorsig: std_logic;
begin
DUT:
entity work.ALU
port map (
reset_n => reset_n,
clk => clk,
OperandA => OperandA,
OperandB => OperandB,
Operation => Operation,
Start => Start,
Result_Low => Result_Low,
Result_High => Result_High,
Ready => Ready,
Errorsig => Errorsig
);
CLOCK:
process
begin
wait for 10 ns;
clk <= not clk;
if Now > 100 ns then
wait;
end if;
end process;
STIMULUS:
process
begin
wait for 20 ns;
reset_n <= '1';
wait;
end process;
end architecture;
給我們示范:
首先要注意的是Result_High得到一些“ U”。 這是由於未初始化或分配tempHigh引起的。
接下來要注意的是移位結果是錯誤的(Result_Low和Result_High)。 我希望您希望在Result_High中輸入“ 0011”,在Result_Low中輸入“ 0000”。
您將看到僅左移的結果-Result_High中的('U','U','U','1')和Result_Low中的“ 1000”。
這是由於以增量周期執行循環語句(沒有介入模擬時間流逝)引起的。 在過程語句中,每個信號只有一個驅動程序。 最終結果是,當前模擬時間只有一個將來值,而最后分配的值將是當前模擬時間在投影輸出波形中計划的那個值。 (本質上,循環語句中對信號的分配只發生一次,並且由於連續值取決於發生的分配,因此看起來只有一個分配)。
有兩種方法可以解決此問題。 第一種是使用在循環內部分配的變量,並在循環語句之后將相應的信號分配給變量。 如前所述,循環綁定不是靜態的,您無法合成循環。
第二種方法是通過順序執行換檔分配來消除循環。 每個時鍾基本上1個移位,表示最后一個移位發生后為就緒。
還可以通過使用case語句(或在VHDL 2008中使用順序條件信號分配或順序選擇的信號分配,如果您的綜合工具供應商支持的話)來規避循環的靜態邊界問題。 這具有在一個時鍾中運行的優點。
請注意,所有這些都需要具有一個整數變量,該變量包含to_integer(unsigned(OperandB))。
當您的綜合工具供應商支持sll(對於另一種情況為srl)或軟件包numeric_std中的SHIFT_LEFT和SHIFT_RIGHT,並且您可以使用它們時,所有這些都可以避免。
不使用sll或SHIFT_LEFT的通用(VHDL 2008之前)修補程序可能是:
begin
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
variable loop_int: integer range 0 to 15;
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
loop_int := to_integer(unsigned(OperandB));
case loop_int is
when 0 =>
Result_Low <= OperandA;
Result_High <= (others => '0');
when 1 =>
Result_Low <= OperandA(2 downto 0) & '0';
Result_High <= "000" & OperandA(3);
when 2 =>
Result_Low <= OperandA(1 downto 0) & "00";
Result_High <= "00" & OperandA(3 downto 2);
when 3 =>
Result_Low <= OperandA(0) & "000";
Result_High <= "0" & OperandA(3 downto 1);
when 4 =>
Result_Low <= (others => '0');
Result_High <= OperandA(3 downto 0);
when 5 =>
Result_Low <= (others => '0');
Result_High <= OperandA(2 downto 0) & '0';
when 6 =>
Result_Low <= (others => '0');
Result_High <= OperandA(1 downto 0) & "00";
when 7 =>
Result_Low <= (others => '0');
Result_High <= OperandA(0) & "000";
when others =>
Result_Low <= (others => '0');
Result_High <= (others => '0');
end case;
-- for i in 0 to loop_nr loop
-- loop_nr <= to_integer(unsigned(OperandB));
-- Result_Low <= OperandA(2 downto 0)&'0';
-- Result_High <= tempHigh(2 downto 0) & OperandA(3);
-- end loop;
Ready <= '1';
Errorsig <= '0';
這使:
正確答案(全部不使用信號loop_nr)。
注意,case語句中的所有選擇都沒有包含在簡單的測試平台中。
當然,像大多數事情一樣,有兩種以上的方法可以獲得期望的結果。
您可以對Result_High和Result_Low使用連續的2到1多路復用器,每個級均從上一級的輸出(或第一級的OperandA)作為A輸入,選擇是OperandB的適當“位”,並且B輸入到多路復用器,前一級輸出邏輯上移位1(填充為“ 0”)。
多路復用器可以是函數,組件或過程語句。 通過使用三對一多路復用器,可以實現對稱移位操作指定的兩種操作(向左和向右)。 如果要包括帶符號的移位,可以用符號位值代替“ 0”填充的右移位。 ...
對於那些可以分派有效的連續“操作”值的情況,您還應該分配“就緒<='0”。
並且由於您對答案之一的評論要求使用具有整數值的循環:
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
variable tempLow: std_logic_vector(3 downto 0); --added
variable loop_int: integer range 0 to 15; --added
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
tempLow := OperandA; --added
tempHigh := (others => '0'); --added
loop_int := to_integer(unsigned(OperandB)); --added
-- for i in 0 to loop_nr loop
-- loop_nr <= to_integer(unsigned(OperandB));
-- Result_Low <= OperandA(2 downto 0)&'0';
-- Result_High <= tempHigh(2 downto 0) & OperandA(3);
-- end loop;
-- More added:
if loop_int /= 0 then
for i in 1 to loop_int loop
tempHigh (3 downto 0) := tempHigh (2 downto 0) & tempLow(3);
-- 'read' tempLow(3) before it's updated
tempLow := tempLow(2 downto 0) & '0';
end loop;
Result_Low <= tempLow;
Result_High <= tempHigh(3 downto 0);
else
Result_Low <= OperandA;
Result_High <= (others => '0');
end if;
Ready <= '1';
Errorsig <= '0';
這使:
為了說明兩半的Result都在工作,OperandA的默認值已更改為“ 0110”:
還要注意,循環從1而不是0開始,以防止發生額外的移位,並且檢查非零loop_int以防止for循環至少執行一次。
在這些情況下是否可以進行可綜合循環?
是。
循環必須處理所有可能的移位(loop_int的范圍),並測試i
是否低於移位閾值:
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
variable tempLow: std_logic_vector(3 downto 0); --added
subtype loop_range is integer range 0 to 15;
variable loop_int: integer range 0 to 15; --added
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
tempLow := OperandA; --added
tempHigh := (others => '0'); --added
loop_int := to_integer(unsigned(OperandB)); --added
for i in loop_range loop
if i < loop_int then
tempHigh (3 downto 0) := tempHigh (2 downto 0) & tempLow(3);
-- 'read' tempLow(3) before it's updated
tempLow := tempLow(2 downto 0) & '0';
end if;
end loop;
Result_Low <= tempLow;
Result_High <= tempHigh(3 downto 0);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.