-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.py
183 lines (137 loc) · 4.88 KB
/
utils.py
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
from math import floor, ceil, log2
def fixed_point_to_float_analytical(fp_num, m, n, debug=False):
"""
Converts a fix point integer number back to (real) float.
May introduce quantization error.
We're using Q number notation: https://en.wikipedia.org/wiki/Q_(number_format)
INPUT:
- n: Fixed point integer number
- m: number of integer bits
- n: number of decimal bits
OUTPUT: The quantized result of the fixed point number
"""
# expand the number to word length
bfp = bin(fp_num)[2:]
bfp = bfp.zfill(m+n)
# Extract integer part
bin_int_part = bfp[0:m]
rev_bin_int_part = bin_int_part[::-1]
# Integer part calculation
int_part_sum = 0
for i in range(len(rev_bin_int_part)):
if debug:
print(f"i={i}, n={rev_bin_int_part[i]}")
int_part_sum += int(rev_bin_int_part[i]) * 2**i
# Extract decimal part
bin_float_part = bfp[m:]
# Float part calculation
float_part_sum = 0
for i in range(len(bin_float_part)):
if debug:
print(f"i={i}, n={bin_float_part[i]}, {1/2**(i+1)}")
if bin_float_part[i] == '1':
float_part_sum += 1/2**(i+1)
return int_part_sum + float_part_sum
def fixed_point_to_float(fp_num, n):
"""
Improved conversion from fixed point to float.
Deprecated version: fixed_point_to_float_analytical().
"""
return fp_num/(2**n)
def float_to_fixed_point_int(fl_num, n):
"""
Returns the fixed point integer of the given floating point number.
INPUT:
- fl_num : the number we want to convert to fixed point
- m : the number of fractional bits
Example:
float_to_fixed_point_int(3.14, 13)
>>> 25722
"""
return floor(fl_num * 2**n)
def float_to_fixed_point_bin(fl_num, m, n):
s1 = floor(fl_num * 2**n)
s2 = bin(s1)[2:]
return s2.zfill(n+m)
def twos_comp(val, bits):
"""compute the 2's complement of int value val"""
if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255
val = val - (1 << bits) # compute negative value
return val
def fixed_point_add(n1, n2):
"""
Perform addition of fixed point numbers.
"""
return n1 + n1
def fixed_point_sub(n1, n2):
"""
Perform addition of fixed point numbers.
"""
return n1 - n1
def fixed_point_mul(num1, num2):
"""
INPUT:
The input is in the form of a tuple: t, where
t = (fixed_point_number, n) where n is the number of fractional bits.
OUTPUT:
- num3: fixed point multiplication result
- frac: the fractional bitwidth of the new number
You can use that result to convert it back to a real number:
fixed_point_to_float(num3, frac)
EXAMPLE:
fp1 = utils.float_to_fixed_point_int(3.1, 13)
fp2 = utils.float_to_fixed_point_int(2.7, 12)
nfp, frac = utils.fixed_point_mul(num1=(fp1, 13), num2=(fp2, 12))
utils.fixed_point_to_float(nfp, frac)
>>> 8.369782716035843
"""
num3 = num1[0] * num2[0]
frac = num1[1] + num2[1]
return num3, frac
def get_bw(magnitude):
"""
Return the magnitude of the number and the required signal bitwidth.
"""
return magnitude, ceil(log2(abs(magnitude)+1))
def sigint_add_bw(n1_range, n2_range):
"""
Calculate the required number of bits for an addition of two signed
signals. The calculation assumes that negative values are always in
two's complement.
---
IMPORTANT: the input needs to include the range of the numbers in
the signals. So if signal1 = 255 (8bits) and signal2 = -100 to 100
then the bitwidth will be n=9.
HOW: range from 255-100 to 255+100, so
sigint_add_bw((0, 255), (-100, 100))
It's not the value that is taken into account but the magnitube of the
numbers.
----
INPUT: range of numbers arranged in tupples (min_val, max_val)
OUTPUT: maximum magnitude, number of required bits
EXAMPLES:
With negative values:
signal1_range = (-100, 100)
signal2_range = (-20, 20)
sigint_add_bw((-100, 100), (-20, 20))
One positive value:
signal1_range = (0, 100)
signal2_range = (-20, 20)
sigint_add_bw((0, 100), (-20, 20))
"""
# Convert to absolute magnitudes
min_result = abs(n1_range[0] + n2_range[0])
max_result = abs(n1_range[1] + n2_range[1])
max_magnitude = max(min_result, max_result)
return get_bw(max_magnitude)
def sigint_mul_bw(n1_range, n2_range):
"""
Calculate the required number of bits for a multiplication of two signed
signals. The calculation assumes that negative values are always in
two's complement.
Does the same thing as sigint_add_bw() but for multiplication.
"""
min_result = abs(n1_range[0] * n2_range[0])
max_result = abs(n1_range[1] * n2_range[1])
max_magnitude = max(min_result, max_result)
return get_bw(max_magnitude)