alu: syntactic sugar
[hwmod.git] / src / alu.vhd
1 library ieee;
2 use ieee.std_logic_1164.all;
3 use ieee.numeric_std.all;
4 use work.gen_pkg.all;
5
6 entity alu is
7         port
8         (
9                 sys_clk : in std_logic;
10                 sys_res_n : in std_logic;
11                 opcode : in alu_ops;
12                 op1 : in csigned;
13                 op2 : in csigned;
14                 op3 : out csigned;
15                 do_calc : in std_logic;
16                 calc_done : out std_logic
17                 -- TODO: hier debug ports hinzufuegen ;)
18         );
19 end entity alu;
20
21 architecture beh of alu is
22         type ALU_STATE is (SIDLE, SADD, SSUB, SMUL, SDIV, SDIV_CALC, SDIV_DONE, SDONE);
23         signal state_int, state_next : ALU_STATE;
24         signal done_intern, div_calc_done, div_go_calc : std_logic;
25         signal op3_int, op3_next : csigned;
26         signal calc_done_int, calc_done_next : std_logic;
27         -- signale fuer division
28         signal dividend_msb_int, dividend_msb_next, laengediv_int, laengediv_next : natural;
29         signal quo_int, quo_next, aktdiv, aktdiv_next, op1_int, op1_next, op2_int, op2_next : csigned;
30         signal sign_int, sign_next : std_logic;
31 begin
32         op3 <= op3_int;
33         calc_done <= calc_done_int;
34
35         -- sync
36         process(sys_clk, sys_res_n)
37         begin
38                 if sys_res_n = '0' then
39                         state_int <= SIDLE;
40                         op3_int <= (others => '0');
41                         calc_done_int <= '0';
42                         --div
43                         dividend_msb_int <= 0;
44                         laengediv_int <= 0;
45                         quo_int <= (others => '0');
46                         aktdiv <= (others => '0');
47                         op1_int <= (others => '0');
48                         op2_int <= (others => '0');
49                         sign_int <= '0';
50                 elsif rising_edge(sys_clk) then
51                         state_int <= state_next;
52                         op3_int <= op3_next;
53                         calc_done_int <= calc_done_next;
54                         -- div
55                         dividend_msb_int <= dividend_msb_next;
56                         laengediv_int <= laengediv_next;
57                         quo_int <= quo_next;
58                         aktdiv <= aktdiv_next;
59                         op1_int <= op1_next;
60                         op2_int <= op2_next;
61                         sign_int <= sign_next;
62                 end if;
63         end process;
64
65         -- next state
66         process(state_int, opcode, done_intern, do_calc, div_calc_done, div_go_calc)
67         begin
68                 -- set a default value for next state
69                 state_next <= state_int;
70                 -- next state berechnen
71                 case state_int is
72                         when SIDLE =>
73                                 if do_calc = '1' then
74                                         case opcode is
75                                                 when ALU_ADD =>
76                                                         state_next <= SADD;
77                                                 when ALU_SUB =>
78                                                         state_next <= SSUB;
79                                                 when ALU_MUL =>
80                                                         state_next <= SMUL;
81                                                 when ALU_DIV =>
82                                                         state_next <= SDIV;
83                                                 when others =>
84                                                         state_next <= SIDLE;
85                                         end case;
86                                 end if;
87                         when SADD | SSUB | SMUL | SDIV_DONE =>
88                                 if done_intern = '1' then
89                                         state_next <= SDONE;
90                                 end if;
91                         when SDIV =>
92                                 if div_go_calc = '1' then
93                                         state_next <= SDIV_CALC;
94                                 end if;
95                         when SDIV_CALC =>
96                                 if div_calc_done = '1' then
97                                         state_next <= SDIV_DONE;
98                                 end if;
99                         when SDONE =>
100                                 if do_calc = '0' then
101                                         state_next <= SIDLE;
102                                 end if;
103                 end case;
104         end process;
105
106         -- output
107         process(state_int, op1, op2, dividend_msb_int, laengediv_int, quo_int, aktdiv, sign_int, op1_int, op2_int)
108                 variable tmperg : csigned;
109                 variable multmp : signed(((2*CBITS)-1) downto 0);
110                 -- vars fuer div
111                 variable laengediv_var, dividend_msb_var : natural;
112                 variable aktdiv_var, quo_var, op1_var, op2_var : csigned;
113         begin
114                 op3_next <= (others => '0');
115                 calc_done_next <= '0';
116                 div_calc_done <= '0';
117                 div_go_calc <= '0';
118                 done_intern <= '0';
119                 -- default fuer div
120                 dividend_msb_next <= 0;
121                 laengediv_next <= 0;
122                 quo_next <= (others => '0');
123                 aktdiv_next <= (others => '0');
124                 op1_next <= (others => '0');
125                 op2_next <= (others => '0');
126                 sign_next <= '0';
127
128                 case state_int is
129                         when SIDLE =>
130                                 tmperg := (others => '0');
131                         when SADD =>
132                                 tmperg := op1 + op2;
133                                 done_intern <= '1';
134                         when SSUB =>
135                                 tmperg := op1 - op2;
136                                 done_intern <= '1';
137                         when SMUL =>
138                                 multmp := op1 * op2;
139                                 tmperg(CBITS-1) := multmp((2*CBITS)-1);
140                                 tmperg((CBITS-2) downto 0) := multmp((CBITS-2) downto 0);
141                                 done_intern <= '1';
142                         when SDIV =>
143                                 -- division implementiert nach ~hwmod/doc/division.pdf
144                                 tmperg := (others => '0');
145                                 if op2 = to_signed(0,CBITS) then
146                                         -- TODO: err out signal
147                                         done_intern <= '1';
148                                 else
149                                         -- sign check
150                                         op1_var := op1;
151                                         op2_var := op2;
152                                         if op1(CBITS-1) = '1' then
153                                                 op1_var := not (op1_var + 1);
154                                         end if;
155                                         if op2(CBITS-1) = '1' then
156                                                 op2_var := not (op2_var + 1);
157                                         end if;
158
159                                         dividend_msb_var := find_msb(op1_var)-1;
160                                         laengediv_var := find_msb(op2_var)-1;
161
162                                         aktdiv_next <= op1_var srl (dividend_msb_var - laengediv_var + 1);
163
164                                         div_go_calc <= '1';
165                                         dividend_msb_next <= dividend_msb_var;
166                                         laengediv_next <= laengediv_var;
167                                         quo_next <= (others => '0');
168                                         op1_next <= op1_var;
169                                         op2_next <= op2_var;
170                                         sign_next <= op1(CBITS-1) xor op2(CBITS-1);
171                                 end if;
172                         when SDIV_CALC =>
173                                 tmperg := (others => '0');
174
175                                 if (dividend_msb_int - laengediv_int + 1) > 0 then
176                                         aktdiv_var := aktdiv sll 1;
177                                         aktdiv_var(0) := op1_int(dividend_msb_int - laengediv_int);
178
179                                         quo_var := quo_int sll 1;
180                                         if aktdiv_var >= op2_int then
181                                                 quo_var(0) := '1';
182                                                 aktdiv_var := aktdiv_var - op2_int;
183                                         end if;
184
185                                         quo_next <= quo_var;
186                                         aktdiv_next <= aktdiv_var;
187                                         dividend_msb_next <= dividend_msb_int;
188                                         laengediv_next <= laengediv_int + 1;
189                                         op1_next <= op1_int;
190                                         op2_next <= op2_int;
191                                         sign_next <= sign_int;
192                                 else
193                                         if sign_int = '1' then
194                                                 quo_next <= (not quo_int) + 1;
195                                         else
196                                                 quo_next <= quo_int;
197                                         end if;
198                                         div_calc_done <= '1';
199                                 end if;
200                         when SDIV_DONE =>
201                                 tmperg := quo_int;
202                                 done_intern <= '1';
203                         when SDONE =>
204                                 done_intern <= '1';
205                                 calc_done_next <= '1';
206                                 op3_next <= tmperg;
207                                 tmperg := (others => '0');
208                 end case;
209         end process;
210 end architecture beh;
211