library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.gen_pkg.all; entity alu is port ( sys_clk : in std_logic; sys_res_n : in std_logic; opcode : in alu_ops; op1 : in csigned; op2 : in csigned; op3 : out csigned; opM : out csigned; do_calc : in std_logic; calc_done : out std_logic; calc_error : out std_logic ); end entity alu; architecture beh of alu is type ALU_STATE is (SIDLE, SADD, SSUB, SMUL, SDIV, SDIV_CALC, SDONE, SERROR); signal state_int, state_next : ALU_STATE; signal op3_int, op3_next, opM_int, opM_next : csigned; signal calc_done_int, calc_done_next : std_logic; signal calc_error_int, calc_error_next : std_logic; -- signale fuer division signal dividend_msb_int, dividend_msb_next, laengediv_int, laengediv_next : divinteger; signal quo_int, quo_next, aktdiv_int, aktdiv_int_next, op1_int, op1_next, op2_int, op2_next : csigned; signal sign_int, sign_next : std_logic; begin op3 <= op3_int; opM <= opM_int; calc_done <= calc_done_int; calc_error <= calc_error_int; -- sync process(sys_clk, sys_res_n) begin if sys_res_n = '0' then state_int <= SIDLE; op3_int <= (others => '0'); opM_int <= (others => '0'); calc_done_int <= '0'; calc_error_int <= '0'; --div dividend_msb_int <= (others => '0'); laengediv_int <= (others => '0'); quo_int <= (others => '0'); aktdiv_int <= (others => '0'); op1_int <= (others => '0'); op2_int <= (others => '0'); sign_int <= '0'; elsif rising_edge(sys_clk) then state_int <= state_next; op3_int <= op3_next; opM_int <= opM_next; calc_done_int <= calc_done_next; calc_error_int <= calc_error_next; -- div dividend_msb_int <= dividend_msb_next; laengediv_int <= laengediv_next; quo_int <= quo_next; aktdiv_int <= aktdiv_int_next; op1_int <= op1_next; op2_int <= op2_next; sign_int <= sign_next; end if; end process; -- next state & out process(opcode, do_calc, state_int, op1, op2, dividend_msb_int, laengediv_int, quo_int, aktdiv_int, sign_int, op1_int, op2_int, op3_int, opM_int) -- http://www.velocityreviews.com/forums/showpost.php?p=137148&postcount=5 function find_msb(a : std_logic_vector) return std_logic_vector is function bits_to_fit(n : positive) return natural is variable nn, bits : natural := 0; begin nn := n; while nn > 0 loop bits := bits + 1; nn := nn/2; end loop; return bits; end; function or_all(p : std_logic_vector) return std_logic is variable r : std_logic; begin r := '0'; for i in p'range loop r := r or p(i); end loop; return r; end; constant wN : positive := bits_to_fit(a'length - 1); constant wP : positive := 2 ** wN; variable pv : std_logic_vector(wP-1 downto 0); variable n : std_logic_vector(wN downto 1); begin if a'length <= 2 then n(n'right) := a(a'left); else pv(a'length-1 downto 0) := a; if or_all(pv(wP-1 downto wP/2)) = '1' then n := '1' & find_msb((pv(wP-1 downto wP/2))); else n := '0' & find_msb((pv(wP/2-1 downto 0))); end if; end if; return n; end function find_msb; -- -- alternativ: eleganter, braucht aber mehr logic cells -- for i in (CBITS-1) downto 0 loop -- exit when a(i) = '1'; -- r := r+1; -- end loop; -- return (CBITS - r); variable multmp : signed(((2*CBITS)-1) downto 0); variable mulsign : std_logic; variable tmp : csigned; -- vars fuer div variable laengediv_var, dividend_msb_var, divtmp : divinteger; variable aktdiv_int_var, quo_var, op1_var, op2_var : csigned; begin calc_done_next <= '0'; calc_error_next <= '0'; -- default fuer div dividend_msb_next <= (others => '0'); laengediv_next <= (others => '0'); quo_next <= (others => '0'); aktdiv_int_next <= aktdiv_int; op1_next <= (others => '0'); op2_next <= (others => '0'); sign_next <= '0'; op3_next <= op3_int; opM_next <= opM_int; -- set a default value for next state state_next <= state_int; -- next state berechnen case state_int is when SIDLE => aktdiv_int_next <= (others => '0'); -- opM_next <= (others => '0'); if do_calc = '1' then case opcode is when ALU_ADD => state_next <= SADD; when ALU_SUB => state_next <= SSUB; when ALU_MUL => state_next <= SMUL; when ALU_DIV => state_next <= SDIV; when others => state_next <= SIDLE; end case; end if; when SADD => tmp := op1 + op2; op3_next <= tmp; state_next <= SDONE; -- over- bzw. underflow? if (op1(CBITS-1) = op2(CBITS-1)) and (op1(CBITS-1) /= tmp(CBITS -1)) then state_next <= SERROR; end if; when SSUB => tmp := op1 - op2; op3_next <= tmp; state_next <= SDONE; -- over- bzw. underflow? if (op1(CBITS-1) /= op2(CBITS-1)) and (op1(CBITS-1) /= tmp(CBITS -1)) then state_next <= SERROR; end if; when SMUL => mulsign := op1(CBITS-1) xor op2(CBITS-1); multmp := op1 * op2; op3_next((CBITS-2) downto 0) <= multmp((CBITS-2) downto 0); op3_next(CBITS-1) <= mulsign; if mulsign = '1' then multmp := (not multmp) + 1; end if; state_next <= SDONE; -- overflow? if(multmp((2*CBITS)-2 downto (CBITS-1)) > 0) then state_next <= SERROR; end if; when SDIV => -- division implementiert nach ~hwmod/doc/division.pdf if ((op1 = x"80000000" and op2 = to_signed(-1, CBITS)) or op2 = to_signed(0, CBITS)) then state_next <= SERROR; else -- sign check op1_var := op1; op2_var := op2; if op1(CBITS-1) = '1' then op1_var := (not op1_var) + 1; end if; if op2(CBITS-1) = '1' then op2_var := (not op2_var) + 1; end if; dividend_msb_var := divinteger(find_msb(std_logic_vector(op1_var)))-1; laengediv_var := divinteger(find_msb(std_logic_vector(op2_var)))-1; aktdiv_int_next <= op1_var srl to_integer(dividend_msb_var - laengediv_var + 1); -- anmerkung: xst (xilinx) kann folgende zeile nicht uebersetzen -- > if op1 = to_signed(-2147483648, CBITS) then -- darum folgende schreibweise -> state_next <= SDONE; if op1 = x"80000000" then -- so ziemlich das boeseste was passieren kann op3_next <= to_signed(-214748364,CBITS); opM_next <= to_signed(8,CBITS); elsif (op1_var < op2_var) then op3_next <= to_signed(0,CBITS); opM_next <= op1_var; else state_next <= SDIV_CALC; dividend_msb_next <= dividend_msb_var; laengediv_next <= laengediv_var; quo_next <= (others => '0'); op1_next <= op1_var; op2_next <= op2_var; sign_next <= op1(CBITS-1) xor op2(CBITS-1); end if; end if; when SDIV_CALC => divtmp := dividend_msb_int - laengediv_int + 1; if divtmp > 0 then aktdiv_int_var := aktdiv_int sll 1; aktdiv_int_var(0) := op1_int(to_integer(divtmp) - 1); quo_var := quo_int sll 1; if aktdiv_int_var >= op2_int then quo_var(0) := '1'; aktdiv_int_var := aktdiv_int_var - op2_int; end if; quo_next <= quo_var; aktdiv_int_next <= aktdiv_int_var; dividend_msb_next <= dividend_msb_int; laengediv_next <= laengediv_int + 1; op1_next <= op1_int; op2_next <= op2_int; sign_next <= sign_int; else if sign_int = '1' then op3_next <= (not quo_int) + 1; else op3_next <= quo_int; end if; opM_next <= aktdiv_int; state_next <= SDONE; end if; when SDONE | SERROR => calc_done_next <= '1'; if state_int = SERROR then calc_error_next <= '1'; end if; if do_calc = '0' then state_next <= SIDLE; end if; end case; end process; end architecture beh;