X-Git-Url: http://wien.tomnetworks.com/gitweb/?p=hwmod.git;a=blobdiff_plain;f=src%2Falu.vhd;h=09097e5d3808f47a492f45fcdd823577b4f547f0;hp=3c70d36e03da10ebda2b924af90803d9115a8a19;hb=HEAD;hpb=503ef4a16a5aafc239ad82e978065cef5341efd7 diff --git a/src/alu.vhd b/src/alu.vhd index 3c70d36..09097e5 100644 --- a/src/alu.vhd +++ b/src/alu.vhd @@ -4,219 +4,240 @@ use ieee.numeric_std.all; use work.gen_pkg.all; entity alu is - port - ( + 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_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, SDIV_DONE, SDONE); - signal state, state_next : ALU_STATE; - signal done_intern, div_calc_done, div_go_calc : std_logic; - signal op3_int, op3_next : csigned; + type ALU_STATE is (SIDLE, SADD, SSUB, SMUL, SDIV, SDIV_SHIFT_TO_MSB, + SDIV_CALC, SDIV_DONE, 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, dividend_msb_next, laengediv, laengediv_next : natural; - signal quo, quo_next, aktdiv, aktdiv_next, op1_int, op1_next, op2_int, op2_next : csigned; - signal sign, sign_next : std_logic; + signal laengediv_int, laengediv_next : divinteger; + signal quo_int, quo_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 <= SIDLE; + state_int <= SIDLE; op3_int <= (others => '0'); + opM_int <= (others => '0'); calc_done_int <= '0'; + calc_error_int <= '0'; --div - dividend_msb <= 0; - laengediv <= 0; - quo <= (others => '0'); - aktdiv <= (others => '0'); + laengediv_int <= (others => '0'); + quo_int <= (others => '0'); op1_int <= (others => '0'); op2_int <= (others => '0'); - sign <= '0'; + sign_int <= '0'; elsif rising_edge(sys_clk) then - state <= state_next; + 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 <= dividend_msb_next; - laengediv <= laengediv_next; - quo <= quo_next; - aktdiv <= aktdiv_next; + laengediv_int <= laengediv_next; + quo_int <= quo_next; op1_int <= op1_next; op2_int <= op2_next; - sign <= sign_next; + sign_int <= sign_next; end if; end process; - -- next state - process(state, opcode, done_intern, do_calc, div_calc_done, div_go_calc) + -- next state & out + process(opcode, do_calc, state_int, op1, op2, laengediv_int, quo_int, + sign_int, op1_int, op2_int, op3_int, opM_int) + variable multmp : signed(((2*CBITS)-1) downto 0); + variable mulsign : std_logic; + variable tmp : csigned; + -- vars fuer div + variable laengediv_var, divtmp : divinteger; + variable aktdiv_int_var, op1_var, op2_var : csigned; + variable quobit : std_logic; begin + calc_done_next <= '0'; + calc_error_next <= '0'; + -- default fuer div + laengediv_next <= laengediv_int; + quo_next <= quo_int; + op1_next <= op1_int; + op2_next <= op2_int; + sign_next <= sign_int; + op3_next <= op3_int; + opM_next <= opM_int; + -- set a default value for next state - state_next <= state; + state_next <= state_int; -- next state berechnen - case state is + case state_int is when SIDLE => + sign_next <= '0'; + op1_next <= (others => '0'); + op2_next <= (others => '0'); + opM_next <= (others => '0'); + if do_calc = '1' then case opcode is - when ADD => - state_next <= SADD; - when SUB => - state_next <= SSUB; - when MUL => - state_next <= SMUL; - when DIV => - state_next <= SDIV; - when others => - state_next <= SIDLE; + 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 => - if done_intern = '1' then - state_next <= SDONE; + 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 => - if done_intern = '1' then - state_next <= SDONE; + 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 => - if done_intern = '1' then - state_next <= SDONE; - end if; - when SDIV => - if div_go_calc = '1' then - state_next <= SDIV_CALC; - end if; - when SDIV_CALC => - if div_calc_done = '1' then - state_next <= SDIV_DONE; - end if; - when SDIV_DONE => - if done_intern = '1' then - state_next <= SDONE; + 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; - when SDONE => - if do_calc = '0' then - state_next <= SIDLE; + state_next <= SDONE; + -- overflow? + if(multmp((2*CBITS)-2 downto (CBITS-1)) > 0) then + state_next <= SERROR; end if; - end case; - end process; - - -- output - process(state, op1, op2, dividend_msb, laengediv, quo, aktdiv, sign, op1_int, op2_int) - variable tmperg : csigned; - variable multmp : signed(((2*CBITS)-1) downto 0); - -- vars fuer div - variable laengediv_var, dividend_msb_var : natural; - variable aktdiv_var, quo_var, op1_var, op2_var : csigned; - begin - op3_next <= (others => '0'); - calc_done_next <= '0'; - div_calc_done <= '0'; - div_go_calc <= '0'; - done_intern <= '0'; - -- default fuer div - dividend_msb_next <= 0; - laengediv_next <= 0; - quo_next <= (others => '0'); - aktdiv_next <= (others => '0'); - op1_next <= (others => '0'); - op2_next <= (others => '0'); - sign_next <= '0'; - - case state is - when SIDLE => - tmperg := (others => '0'); - when SADD => - tmperg := op1 + op2; - done_intern <= '1'; - when SSUB => - tmperg := op1 - op2; - done_intern <= '1'; - when SMUL => - multmp := op1 * op2; - tmperg(CBITS-1) := multmp((2*CBITS)-1); - tmperg((CBITS-2) downto 0) := multmp((CBITS-2) downto 0); - done_intern <= '1'; when SDIV => - -- division implementiert nach ~hwmod/doc/division.pdf - tmperg := (others => '0'); - if op2 = to_signed(0,CBITS) then - -- TODO: err out signal - done_intern <= '1'; + -- modifizierte binaere division nach ~hwmod/doc/division.pdf + + -- division durch 0 checken + if 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); + op1_var := (not op1_var) + 1; end if; if op2(CBITS-1) = '1' then - op2_var := not (op2_var + 1); + op2_var := (not op2_var) + 1; end if; - dividend_msb_var := find_msb(op1_var)-1; - laengediv_var := find_msb(op2_var)-1; - - aktdiv_next <= op1_var srl (dividend_msb_var - laengediv_var + 1); - - div_go_calc <= '1'; - 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); + state_next <= SDONE; + -- anmerkung: xst (xilinx) kann folgende zeile nicht uebersetzen + -- > if op1 = to_signed(-2147483648, CBITS) then + -- darum folgende schreibweise -> + if op1 = x"80000000" then + if op2 = to_signed(10,CBITS) then + -- so ziemlich das boeseste was passieren kann + -- deswegen, weil divisor und dividend in positive + -- zahlen umgewandelt werden. + -- daher: hardcodiertes ergebnis fuer division durch 10 (fuer parser) + op3_next <= to_signed(-214748364,CBITS); + opM_next <= to_signed(8,CBITS); + else + -- sonst fehler.. + state_next <= SERROR; + end if; + elsif (op1_var < op2_var) then + -- => man braucht nix grossartiges rechnen + op3_next <= to_signed(0,CBITS); + opM_next <= op1_var; + else + -- spannend! signale initialisieren fuer den naechsten schritt + state_next <= SDIV_SHIFT_TO_MSB; + laengediv_next <= (others => '0'); + 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_SHIFT_TO_MSB => + -- shifte divisor so lange nach links, bis MSB = '1' ist + if op2_int(CBITS-1) = '1' then + state_next <= SDIV_CALC; + else + -- und mitzaehlen wie oft geshiftet wurde + laengediv_next <= laengediv_int + 1; + op2_next <= op2_int sll 1; end if; when SDIV_CALC => - tmperg := (others => '0'); + multmp := (others => '0'); + multmp(CBITS downto 0) := signed('0' & std_logic_vector(op1_int)) - signed('0' & std_logic_vector(op2_int)); - if (dividend_msb - laengediv + 1) > 0 then - aktdiv_var := aktdiv sll 1; - aktdiv_var(0) := op1_int(dividend_msb - laengediv); + -- beim sign bit sieht man ob sich op2_int in op1_int ausging oder nicht + -- es muss daher negiert werden + quobit := not multmp(CBITS); - quo_var := quo sll 1; - if aktdiv_var >= op2_int then - quo_var(0) := '1'; - aktdiv_var := aktdiv_var - op2_int; - end if; + -- ergebnis dieser runde uebernehmen + quo_next <= quo_int(CBITS-2 downto 0) & quobit; - quo_next <= quo_var; - aktdiv_next <= aktdiv_var; - dividend_msb_next <= dividend_msb; - laengediv_next <= laengediv + 1; - op1_next <= op1_int; - op2_next <= op2_int; - sign_next <= sign; - else - if sign = '1' then - quo_next <= (not quo) + 1; - else - quo_next <= quo; - end if; - div_calc_done <= '1'; + -- wenn es sich ausging, soll neuer dividend uebernommen werden + if quobit = '1' then + op1_next <= multmp(CBITS-1 downto 0); + end if; + + laengediv_next <= laengediv_int - 1; + -- divisor nach rechts shiften + op2_next <= op2_int srl 1; + + -- wenn laengediv_int in *dieser* runde =0 ist dann ist man + -- fertig, weil man braucht (runden zum shiften + 1) runden zum + -- berechnen des eregbnisses + if laengediv_int = 0 then + state_next <= SDIV_DONE; end if; when SDIV_DONE => - tmperg := quo; - done_intern <= '1'; - when SDONE => - done_intern <= '1'; + if sign_int = '1' then + op3_next <= (not quo_int) + 1; + else + op3_next <= quo_int; + end if; + -- wichtig: rest nur fuer postive paare gueltig! + -- (fuer unsere zwecke ausreichend) + opM_next <= op1_int; + state_next <= SDONE; + when SDONE | SERROR => calc_done_next <= '1'; - op3_next <= tmperg; - tmperg := (others => '0'); + 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; -