parser/alu: extrem mega super sonderfall bugfix
[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                 opM : out csigned;
16                 do_calc : in std_logic;
17                 calc_done : out std_logic;
18                 calc_error : out std_logic
19         );
20 end entity alu;
21
22 architecture beh of alu is
23         type ALU_STATE is (SIDLE, SADD, SSUB, SMUL, SDIV, SDIV_CALC, SDIV_DONE,
24         SDONE, SERROR);
25         signal state_int, state_next : ALU_STATE;
26         signal done_intern, error_intern : std_logic;
27         signal div_calc_done_int : std_logic;
28         signal div_calc_done2_int : std_logic;
29         signal div_go_calc_int : std_logic;
30         signal op3_int, op3_next, opM_int, opM_next : csigned;
31         signal calc_done_int, calc_done_next : std_logic;
32         signal calc_error_int, calc_error_next : std_logic;
33         -- signale fuer division
34         signal dividend_msb_int, dividend_msb_next, laengediv_int, laengediv_next : divinteger;
35         signal quo_int, quo_next, aktdiv_int, aktdiv_int_next, op1_int, op1_next, op2_int, op2_next : csigned;
36         signal sign_int, sign_next : std_logic;
37 begin
38         op3 <= op3_int;
39         opM <= opM_int;
40         calc_done <= calc_done_int;
41         calc_error <= calc_error_int;
42
43         -- sync
44         process(sys_clk, sys_res_n)
45         begin
46                 if sys_res_n = '0' then
47                         state_int <= SIDLE;
48                         op3_int <= (others => '0');
49                         opM_int <= (others => '0');
50                         calc_done_int <= '0';
51                         calc_error_int <= '0';
52                         --div
53                         dividend_msb_int <= 0;
54                         laengediv_int <= 0;
55                         quo_int <= (others => '0');
56                         aktdiv_int <= (others => '0');
57                         op1_int <= (others => '0');
58                         op2_int <= (others => '0');
59                         sign_int <= '0';
60                 elsif rising_edge(sys_clk) then
61                         state_int <= state_next;
62                         op3_int <= op3_next;
63                         opM_int <= opM_next;
64                         calc_done_int <= calc_done_next;
65                         calc_error_int <= calc_error_next;
66                         -- div
67                         dividend_msb_int <= dividend_msb_next;
68                         laengediv_int <= laengediv_next;
69                         quo_int <= quo_next;
70                         aktdiv_int <= aktdiv_int_next;
71                         op1_int <= op1_next;
72                         op2_int <= op2_next;
73                         sign_int <= sign_next;
74                 end if;
75         end process;
76
77         -- next state
78         process(state_int, opcode, done_intern, error_intern, do_calc,
79                 div_calc_done_int, div_calc_done2_int, div_go_calc_int)
80         begin
81                 -- set a default value for next state
82                 state_next <= state_int;
83                 -- next state berechnen
84                 case state_int is
85                         when SIDLE =>
86                                 if do_calc = '1' then
87                                         case opcode is
88                                                 when ALU_ADD =>
89                                                         state_next <= SADD;
90                                                 when ALU_SUB =>
91                                                         state_next <= SSUB;
92                                                 when ALU_MUL =>
93                                                         state_next <= SMUL;
94                                                 when ALU_DIV =>
95                                                         state_next <= SDIV;
96                                                 when others =>
97                                                         state_next <= SIDLE;
98                                         end case;
99                                 end if;
100                         when SADD | SSUB | SMUL | SDIV_DONE =>
101                                 if done_intern = '1' then
102                                         state_next <= SDONE;
103                                 end if;
104                                 if error_intern = '1' then
105                                         state_next <= SERROR;
106                                 end if;
107                         when SDIV =>
108                                 if div_go_calc_int = '1' then
109                                         state_next <= SDIV_CALC;
110                                 end if;
111                                 if div_calc_done2_int = '1' then
112                                         state_next <= SDIV_DONE;
113                                 end if;
114                                 if error_intern = '1' then
115                                         state_next <= SERROR;
116                                 end if;
117                         when SDIV_CALC =>
118                                 if div_calc_done_int = '1' then
119                                         state_next <= SDIV_DONE;
120                                 end if;
121                         when SDONE | SERROR =>
122                                 if do_calc = '0' then
123                                         state_next <= SIDLE;
124                                 end if;
125                 end case;
126         end process;
127
128         -- output
129         process(state_int, op1, op2, dividend_msb_int, laengediv_int, quo_int,
130                 aktdiv_int, sign_int, op1_int, op2_int, op3_int, opM_int)
131                 variable multmp, multmp2 : signed(((2*CBITS)-1) downto 0);
132                 variable mulsign : std_logic;
133                 variable tmp : csigned;
134                 -- vars fuer div
135                 variable laengediv_var, dividend_msb_var, divtmp : divinteger;
136                 variable aktdiv_int_var, quo_var, op1_var, op2_var : csigned;
137         begin
138                 calc_done_next <= '0';
139                 calc_error_next <= '0';
140                 div_calc_done_int <= '0';
141                 div_calc_done2_int <= '0';
142                 div_go_calc_int <= '0';
143                 done_intern <= '0';
144                 error_intern <= '0';
145                 -- default fuer div
146                 dividend_msb_next <= 0;
147                 laengediv_next <= 0;
148                 quo_next <= (others => '0');
149                 aktdiv_int_next <= aktdiv_int;
150                 op1_next <= (others => '0');
151                 op2_next <= (others => '0');
152                 sign_next <= '0';
153                 op3_next <= op3_int;
154                 opM_next <= opM_int;
155
156                 case state_int is
157                         when SIDLE =>
158                                 opM_next <= (others => '0');
159                                 aktdiv_int_next <= (others => '0');
160                         when SADD =>
161                                 tmp := op1 + op2;
162                                 op3_next <= tmp;
163
164                                 -- over- bzw. underflow?
165                                 if (op1(CBITS-1) = op2(CBITS-1)) and (op1(CBITS-1) /= tmp(CBITS -1)) then
166                                         error_intern <= '1';
167                                 else
168                                         done_intern <= '1';
169                                 end if;
170                         when SSUB =>
171                                 tmp := op1 - op2;
172                                 op3_next <= tmp;
173
174                                 -- over- bzw. underflow?
175                                 if (op1(CBITS-1) /= op2(CBITS-1)) and (op1(CBITS-1) /= tmp(CBITS -1)) then
176                                         error_intern <= '1';
177                                 else
178                                         done_intern <= '1';
179                                 end if;
180                         when SMUL =>
181                                 mulsign := op1(CBITS-1) xor op2(CBITS-1);
182                                 multmp := op1 * op2;
183                                 op3_next((CBITS-2) downto 0) <= multmp((CBITS-2) downto 0);
184                                 op3_next(CBITS-1) <= mulsign;
185
186                                 if mulsign = '1' then
187                                         multmp2 := (not multmp) + 1;
188                                 else
189                                         multmp2 := multmp;
190                                 end if;
191                                 -- overflow?
192                                 if(multmp2((2*CBITS)-2 downto (CBITS-1)) > 0) then
193                                         error_intern <= '1';
194                                 else
195                                         done_intern <= '1';
196                                 end if;
197
198                         when SDIV =>
199                                 -- division implementiert nach ~hwmod/doc/division.pdf
200                                 if ((op1 = x"80000000" and op2 = to_signed(-1, CBITS)) or op2 = to_signed(0, CBITS)) then
201                                         error_intern <= '1';
202                                 else
203                                         -- sign check
204                                         op1_var := op1;
205                                         op2_var := op2;
206                                         if op1(CBITS-1) = '1' then
207                                                 op1_var := (not op1_var) + 1;
208                                         end if;
209                                         if op2(CBITS-1) = '1' then
210                                                 op2_var := (not op2_var) + 1;
211                                         end if;
212
213                                         dividend_msb_var := find_msb(op1_var)-1;
214                                         laengediv_var := find_msb(op2_var)-1;
215
216                                         aktdiv_int_next <= op1_var srl (dividend_msb_var - laengediv_var + 1);
217
218                                         -- anmerkung: xst (xilinx) kann folgende zeile nicht uebersetzen
219                                         -- > if op1 = to_signed(-2147483648, CBITS) then
220                                         -- darum folgende schreibweise ->
221                                         if op1 = x"80000000" then
222                                                 -- so ziemlich das boeseste was passieren kann
223                                                 div_calc_done2_int <= '1';
224                                                 quo_next <= to_signed(-214748364,CBITS);
225                                                 aktdiv_int_next <= to_signed(8,CBITS);
226                                         elsif (op1_var < op2_var) then
227                                                 div_calc_done2_int <= '1';
228                                                 quo_next <= to_signed(0,CBITS);
229                                                 aktdiv_int_next <= op1_var;
230                                         else
231                                                 div_go_calc_int <= '1';
232                                                 dividend_msb_next <= dividend_msb_var;
233                                                 laengediv_next <= laengediv_var;
234                                                 quo_next <= (others => '0');
235                                                 op1_next <= op1_var;
236                                                 op2_next <= op2_var;
237                                                 sign_next <= op1(CBITS-1) xor op2(CBITS-1);
238                                         end if;
239                                 end if;
240                         when SDIV_CALC =>
241                                 divtmp := dividend_msb_int - laengediv_int + 1;
242
243                                 if divtmp > 0 then
244                                         aktdiv_int_var := aktdiv_int sll 1;
245                                         aktdiv_int_var(0) := op1_int(divtmp - 1);
246
247                                         quo_var := quo_int sll 1;
248                                         if aktdiv_int_var >= op2_int then
249                                                 quo_var(0) := '1';
250                                                 aktdiv_int_var := aktdiv_int_var - op2_int;
251                                         end if;
252
253                                         quo_next <= quo_var;
254                                         aktdiv_int_next <= aktdiv_int_var;
255                                         dividend_msb_next <= dividend_msb_int;
256                                         laengediv_next <= laengediv_int + 1;
257                                         op1_next <= op1_int;
258                                         op2_next <= op2_int;
259                                         sign_next <= sign_int;
260                                 else
261                                         if sign_int = '1' then
262                                                 quo_next <= (not quo_int) + 1;
263                                         else
264                                                 quo_next <= quo_int;
265                                         end if;
266                                         div_calc_done_int <= '1';
267                                 end if;
268                         when SDIV_DONE =>
269                                 op3_next <= quo_int;
270                                 opM_next <= aktdiv_int;
271                                 done_intern <= '1';
272                         when SDONE | SERROR =>
273                                 calc_done_next <= '1';
274                                 if (state_int = SERROR) then
275                                         calc_error_next <= '1';
276                                 end if;
277                 end case;
278         end process;
279 end architecture beh;
280