add FPSCR with parts
[ieee754fpu.git] / src / ieee754 / fpcommon / fpscr.py
1 # SPDX-License-Identifier: LGPLv3+
2 # Funded by NLnet https://nlnet.nl/
3
4 """ Record for FPSCR as defined in
5 Power ISA v3.1B Book I section 4.2.2 page 136(162)
6
7 FPSCR fields in MSB0:
8
9 |Bits |Mnemonic | Description |
10 |-----|---------|-------------------------------------------------------------|
11 |0:28 |   | Reserved |
12 |29:31| DRN | Decimal Rounding Mode |
13 |32 | FX | FP Exception Summary |
14 |33 | FEX | FP Enabled Exception Summary |
15 |34 | VX | FP Invalid Operation Exception Summary |
16 |35 | OX | FP Overflow Exception |
17 |36 | UX | FP Underflow Exception |
18 |37 | ZX | FP Zero Divide Exception |
19 |38 | XX | FP Inexact Exception |
20 |39 | VXSNAN | FP Invalid Operation Exception (SNaN) |
21 |40 | VXISI | FP Invalid Operation Exception (∞ - ∞) |
22 |41 | VXIDI | FP Invalid Operation Exception (∞ ÷ ∞) |
23 |42 | VXZDZ | FP Invalid Operation Exception (0 ÷ 0) |
24 |43 | VXIMZ | FP Invalid Operation Exception (∞ × 0) |
25 |44 | VXVC | FP Invalid Operation Exception (Invalid Compare) |
26 |45 | FR | FP Fraction Rounded |
27 |46 | FI | FP Fraction Inexact |
28 |47:51| FPRF | FP Result Flags |
29 |47 | C | FP Result Class Descriptor |
30 |48:51| FPCC | FP Condition Code |
31 |48 | FL | FP Less Than or Negative |
32 |49 | FG | FP Greater Than or Positive |
33 |50 | FE | FP Equal or Zero |
34 |51 | FU | FP Unordered or NaN |
35 |52 |   | Reserved |
36 |53 | VXSOFT | FP Invalid Operation Exception (Software-Defined Condition) |
37 |54 | VXSQRT | FP Invalid Operation Exception (Invalid Square Root) |
38 |55 | VXCVI | FP Invalid Operation Exception (Invalid Integer Convert) |
39 |56 | VE | FP Invalid Operation Exception Enable |
40 |57 | OE | FP Overflow Exception Enable |
41 |58 | UE | FP Underflow Exception Enable |
42 |59 | ZE | FP Zero Divide Exception Enable |
43 |60 | XE | FP Inexact Exception Enable |
44 |61 | NI | FP Non-IEEE Mode |
45 |62:63| RN | FP Rounding Control |
46
47
48 https://bugs.libre-soc.org/show_bug.cgi?id=1135
49 to allow FP ops to compute in parallel despite each fp op semantically reading
50 the FPSCR output from the previous op, the FPSCR will be split into 3 parts
51 (I picked names that aren't necessarily standard names):
52 * volatile part: written nearly every insn but is rarely read
53 FR, FI, FPRF
54 * sticky part: usually doesn't change but is read/written by nearly all insns:
55 all the sticky exception bits
56 * control part: generally doesn't change and is only read by nearly all insns:
57 all the other bits
58
59 The explanation of why FPSCR is split into 3 parts follows, we may not
60 implement it this way.
61
62 Additionally, as of Aug 2023 we're not planning on implementing it this way
63 anytime soon.
64
65 the idea is that the cpu will have all three parts in separate registers and
66 will speculatively execute fp insns with the current value of the sticky part
67 register (not the one from the previous instruction, but the one from the
68 register, avoiding needing a dependency chain), and then will cancel and retry
69 all later insns if it turns out that the insn changed the sticky part (which
70 is rare).
71
72 if desired the control part can be put in the same register and handled the
73 same way as the sticky part, but this makes code that temporarily changes the
74 rounding mode slower than necessary (common in x87 emulation and some math
75 library functions).
76 """
77
78 from nmigen import Record, Shape
79 from enum import Enum, Flag, unique
80 from functools import lru_cache
81
82
83 class RoundingMode(Enum):
84 # names match:
85 # * PowerISA rounding modes (v3.1B 4.3.6 page 143(169))
86 # * PowerISA bfp_ROUND_<mode> functions (v3.1B 7.6.2.2 page 607(633))
87 # * SMTLIB abbreviated rounding modes
88 # https://smtlib.cs.uiowa.edu/theories-FloatingPoint.shtml
89 RNE = 0b00
90 NearEven = RNE
91 RoundToNearest = RNE
92 RTZ = 0b01
93 Trunc = RTZ
94 RoundTowardZero = RTZ
95 RTP = 0b10
96 Ceil = RTP
97 RoundTowardPosInfinity = RTP
98 RTN = 0b11
99 Floor = RTN
100 RoundTowardNegInfinity = RTN
101
102
103 assert Shape.cast(RoundingMode) == Shape(width=2, signed=False)
104
105
106 class FPSCRBase(Record):
107 @unique
108 class Part(Enum):
109 Volatile = "volatile"
110 "the part written nearly every insn but rarely read"
111
112 Sticky = "sticky"
113 """usually doesn't change but is read/written by nearly all
114 instructions in order to or-in exception bits
115 """
116
117 Control = "control"
118 "generally doesn't change and is only read by nearly all instructions"
119
120 Everything = "everything"
121
122 @property
123 @lru_cache(maxsize=None)
124 def layout(self):
125 Part = __class__
126 l = (
127 ("RN", RoundingMode, Part.Control),
128 ("NI", 1, Part.Control),
129 ("XE", 1, Part.Control),
130 ("ZE", 1, Part.Control),
131 ("UE", 1, Part.Control),
132 ("OE", 1, Part.Control),
133 ("VE", 1, Part.Control),
134 ("VXCVI", 1, Part.Sticky),
135 ("VXSQRT", 1, Part.Sticky),
136
137 # we may decide to set VXSOFT if something like `fcospi`
138 # causes an invalid exception, because it doesn't have an
139 # assigned exception bit and because doing it that way makes
140 # it match the math library better.
141 ("VXSOFT", 1, Part.Sticky),
142 ("rsvd1", 1, Part.Control),
143 ("FPRF", (
144 ("FPCC", (
145 ("FU", 1),
146 ("FE", 1),
147 ("FG", 1),
148 ("FL", 1),
149 )),
150 ("C", 1),
151 ), Part.Volatile),
152 ("FI", 1, Part.Volatile),
153 ("FR", 1, Part.Volatile),
154 ("VXVC", 1, Part.Sticky),
155 ("VXIMZ", 1, Part.Sticky),
156 ("VXZDZ", 1, Part.Sticky),
157 ("VXIDI", 1, Part.Sticky),
158 ("VXISI", 1, Part.Sticky),
159 ("VXSNAN", 1, Part.Sticky),
160 ("XX", 1, Part.Sticky),
161 ("ZX", 1, Part.Sticky),
162 ("UX", 1, Part.Sticky),
163 ("OX", 1, Part.Sticky),
164 ("VX", 1, Part.Sticky),
165 ("FEX", 1, Part.Sticky),
166 ("FX", 1, Part.Sticky),
167 ("DRN", 3, Part.Control),
168 ("rsvd2", 29, Part.Control),
169 )
170 everything = self is Part.Everything
171 return tuple((n, s) for n, s, p in l if everything or p is self)
172
173 PART = Part.Everything
174 layout = PART.layout
175
176 def __init__(self, *, name=None, fields=None, src_loc_at=0):
177 super().__init__(layout=self.PART.layout, name=name,
178 fields=fields, src_loc_at=src_loc_at + 1)
179
180
181 class FPSCR(FPSCRBase):
182 def calc_summary(self):
183 """calculate and assign the summary bits in self"""
184 return [
185 self.fields['VX'].eq(self.VX),
186 self.fields['FEX'].eq(self.FEX),
187 ]
188
189 @property
190 def VX(self):
191 return (self.VXSNAN |
192 self.VXISI |
193 self.VXIDI |
194 self.VXZDZ |
195 self.VXIMZ |
196 self.VXVC |
197 self.VXSOFT |
198 self.VXSQRT |
199 self.VXCVI)
200
201 @property
202 def FEX(self):
203 return ((self.VX & self.VE) |
204 (self.OX & self.OE) |
205 (self.UX & self.UE) |
206 (self.ZX & self.ZE) |
207 (self.XX & self.XE))
208
209 @staticmethod
210 def __make_record(fields, cls, name, src_loc_at):
211 return cls(name=name, fields=fields, src_loc_at=1+src_loc_at)
212
213 def volatile_part(self, *, name=None, src_loc_at=0):
214 return FPSCR.__make_record(self.fields, cls=FPSCRVolatilePart,
215 name=name, src_loc_at=1+src_loc_at)
216
217 def sticky_part(self, *, name=None, src_loc_at=0):
218 return FPSCR.__make_record(self.fields, cls=FPSCRStickyPart,
219 name=name, src_loc_at=1+src_loc_at)
220
221 def control_part(self, *, name=None, src_loc_at=0):
222 return FPSCR.__make_record(self.fields, cls=FPSCRControlPart,
223 name=name, src_loc_at=1+src_loc_at)
224
225 @staticmethod
226 def from_parts(*, volatile_part, sticky_part, control_part,
227 name=None, src_loc_at=0):
228 fields = {**volatile_part.fields,
229 **sticky_part.fields,
230 **control_part.fields}
231 return FPSCR.__make_record(fields, cls=FPSCR,
232 name=name, src_loc_at=1+src_loc_at)
233
234
235 class FPSCRVolatilePart(FPSCRBase):
236 """ the part of FPSCR that is written by nearly every FP instruction,
237 but is rarely read.
238 """
239 PART = FPSCR.Part.Volatile
240 layout = PART.layout
241
242
243 class FPSCRStickyPart(FPSCRBase):
244 """ the part of FPSCR that usually doesn't change but is read/written by
245 nearly all FP instructions in order to or-in exception bits.
246 """
247 PART = FPSCR.Part.Sticky
248 layout = PART.layout
249
250
251 class FPSCRControlPart(FPSCRBase):
252 """ the part of FPSCR that generally doesn't change and is read by
253 nearly all FP instructions.
254 """
255
256 PART = FPSCR.Part.Control
257 layout = PART.layout
258
259
260 if __name__ == "__main__":
261 from pprint import pprint
262 for part in FPSCR.Part:
263 print(f"{part}.layout:")
264 pprint(part.layout)