aboutsummaryrefslogtreecommitdiffstats
path: root/src/synth/synth-ieee-utils.adb
blob: db1a48d71a46bcbc0e6293f65e83f8652d8c5fa2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
--  Simple logic utilities for ieee.std_logic
--  Copyright (C) 2019 Tristan Gingold
--
--  This file is part of GHDL.
--
--  This program is free software: you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation, either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with this program.  If not, see <gnu.org/licenses>.

package body Synth.Ieee.Utils is
   procedure Neg_Vec (Src : Memory_Ptr; Dst : Memory_Ptr; Len : Uns32)
   is
      Vb, Carry : Sl_X01;
   begin
      Carry := '1';
      for I in 1 .. Len loop
         Vb := Sl_To_X01 (Read_Std_Logic (Src, Len - I));
         Vb := Not_Table (Vb);
         Write_Std_Logic (Dst, Len - I, Xor_Table (Carry, Vb));
         Carry := And_Table (Carry, Vb);
      end loop;
   end Neg_Vec;

   procedure Abs_Vec (Src : Memory_Ptr; Dst : Memory_Ptr; Len : Uns32) is
   begin
      if Len > 0 and then Sl_To_X01 (Read_Std_Logic (Src, 0)) = '1' then
         Neg_Vec (Src, Dst, Len);
      else
         for I in 1 .. Size_Type (Len) loop
            Write_U8 (Dst + (I - 1), Read_U8 (Src + (I - 1)));
         end loop;
      end if;
   end Abs_Vec;

   procedure Fill (Res : Memory_Ptr; Len : Uns32; V : Std_Ulogic) is
   begin
      for I in 1 .. Len loop
         Write_Std_Logic (Res, I - 1, V);
      end loop;
   end Fill;

   procedure Mul_Vec (L, R : Memory_Ptr;
                      Llen, Rlen : Uns32;
                      L_Sign, R_Sign : Boolean;
                      Res : Memory_Ptr)
   is
      Res_Len : constant Uns32 :=
        Llen + Rlen + Boolean'Pos (L_Sign xor R_Sign);
      Lb, Rb, Vb, Carry : Sl_X01;
   begin
      --  Check for 'X' in L.
      for I in 1 .. Llen loop
         if Read_Std_Logic (L, I - 1) = 'X' then
            Fill (Res, Res_Len, 'X');
            return;
         end if;
      end loop;

      --  Init RES.
      Fill (Res, Res_Len, '0');

      if Rlen = 0 then
         return;
      end if;

      --  Shift and add L.
      for I in 1 .. Rlen - Boolean'Pos (R_Sign) loop
         Rb := Sl_To_X01 (Read_Std_Logic (R, Rlen - I));
         if Rb = '1' then
            --  Compute res := res + shift_left (l, i).
            Carry := '0';
            for J in 1 .. Llen loop
               Lb := Read_Std_Logic (L, Llen - J);
               Vb := Read_Std_Logic (Res, Res_Len - (I + J - 1));
               Write_Std_Logic
                 (Res, Res_Len - (I + J - 1), Compute_Sum (Carry, Vb, Lb));
               Carry := Compute_Carry (Carry, Vb, Lb);
            end loop;
            --  Propagate carry.
            if L_Sign then
               --  Sign extend.
               Lb := Read_Std_Logic (L, 0);
            else
               Lb := '0';
            end if;
            for J in I + Llen .. Res_Len loop
               exit when Lb = '0' and Carry = '0';
               Vb := Read_Std_Logic (Res, Res_Len - J);
               Write_Std_Logic (Res, Res_Len - J, Compute_Sum (Carry, Vb, Lb));
               Carry := Compute_Carry (Carry, Vb, Lb);
            end loop;
         elsif Rb = 'X' then
            Fill (Res, Res_Len, 'X');
            exit;
         end if;
      end loop;
      if R_Sign and then Read_Std_Logic (R, 0) = '1' then
         --  R is a negative number.  It is considered as:
         --   -2**n + (Rn-1 Rn-2 ... R0).
         --  Compute res := res - 2**n * l.
         Carry := '1';
         for I in 1 .. Llen loop
            --  Start at len - (rlen - 1) = llen + 1
            Vb := Read_Std_Logic (Res, Llen - I + 1);
            Lb := Not_Table (Read_Std_Logic (L, Llen - I));
            Write_Std_Logic (Res, Llen - I + 1, Compute_Sum (Carry, Vb, Lb));
            Carry := Compute_Carry (Carry, Vb, Lb);
         end loop;
         --  The last bit.
         Vb := Read_Std_Logic (Res, 0);
         Lb := Not_Table (Read_Std_Logic (L, 0));
         Write_Std_Logic (Res, 0, Compute_Sum (Carry, Vb, Lb));
      end if;
   end Mul_Vec;

   function Compare_Bit (Lb, Rb : Sl_01;
                         L_Sign, R_Sign : Boolean) return Order_Type is
   begin
      if Lb = '1' and Rb = '0' then
         if L_Sign then
            return Less;
         else
            return Greater;
         end if;
      elsif Lb = '0' and Rb = '1' then
         if R_Sign then
            return Greater;
         else
            return Less;
         end if;
      else
         return Equal;
      end if;
   end Compare_Bit;

   function Compare_Vec (L, R : Memory_Ptr;
                         Llen, Rlen : Uns32;
                         L_Sign, R_Sign : Boolean) return Order_Type
   is
      Lb, Rb : Sl_01;
   begin
      --  The sign.
      if L_Sign and Llen > 0 then
         Lb := Sl_To_01 (Read_Std_Logic (L, 0));
      else
         Lb := '0';
      end if;
      if R_Sign and Rlen > 0 then
         Rb := Sl_To_01 (Read_Std_Logic (R, 0));
      else
         Rb := '0';
      end if;
      if Lb /= Rb then
         return Compare_Bit (Lb, Rb, L_Sign, R_Sign);
      end if;

      --  Same sign.
      for I in reverse 1 .. Uns32'Max (Llen, Rlen) loop
         if I <= Llen then
            Lb := Sl_To_01 (Read_Std_Logic (L, Llen - I));
         end if;
         if I <= Rlen then
            Rb := Sl_To_01 (Read_Std_Logic (R, Rlen - I));
         end if;
         if Lb = '0' and Rb = '1' then
            return Less;
         elsif Lb = '1' and Rb = '0' then
            return Greater;
         end if;
      end loop;
      return Equal;
   end Compare_Vec;

end Synth.Ieee.Utils;