1 # SPDX-License-Identifier: LGPL-3-or-later
2 # See Notices.txt for copyright information
4 SimdScope class - provides context for SIMD signals to make them useable
5 under the exact same API as scalar nmigen Signals.
7 Copyright (C) 2021 Jacob Lifshay
8 Copyright (C) 2021 Luke Kenneth Casson Leighton
13 with SimdScope(m, elwid) as s:
14 a = s.Signal(width=64, ....)
21 from ieee754
.part
.util
import (DEFAULT_FP_VEC_EL_COUNTS
,
22 DEFAULT_INT_VEC_EL_COUNTS
,
23 FpElWid
, IntElWid
, SimdMap
)
24 from nmigen
.hdl
.ast
import Signal
28 """The global scope object for SimdSignal and friends
31 * vec_el_counts: SimdMap
32 a map from `ElWid` values `k` to the number of elements in a vector
33 when `self.elwid == k`.
36 vec_el_counts = SimdMap({
44 vec_el_counts = SimdMap({
50 * elwid: ElWid or nmigen Value with a shape of some ElWid class
51 the current elwid (simd element type)
58 """get the current SimdScope. raises a ValueError outside of any
62 with SimdScope(...) as s:
63 assert SimdScope.get() is s
65 if len(cls
.__SCOPE
_STACK
) > 0:
66 retval
= cls
.__SCOPE
_STACK
[-1]
67 assert isinstance(retval
, SimdScope
), "inconsistent scope stack"
69 raise ValueError("not in a `with SimdScope()` statement")
72 self
.__SCOPE
_STACK
.append(self
)
75 def __exit__(self
, exc_type
, exc_value
, traceback
):
76 assert self
.__SCOPE
_STACK
.pop() is self
, "inconsistent scope stack"
79 def __init__(self
, *, module
, elwid
=None,
80 vec_el_counts
=None, elwid_type
=IntElWid
, scalar
=False):
82 # in SIMD mode, must establish module as part of context and inform
83 # the module to operate under "SIMD" Type 1 (AST) casting rules,
84 # not the # default "Value.cast" rules.
87 from ieee754
.part
.partsig
import SimdSignal
88 module
._setAstTypeCastFn
(SimdSignal
.cast
)
90 # TODO, explain what this is about
91 if isinstance(elwid
, (IntElWid
, FpElWid
)):
92 elwid_type
= type(elwid
)
93 if vec_el_counts
is None:
94 vec_el_counts
= SimdMap({elwid
: 1})
95 assert issubclass(elwid_type
, (IntElWid
, FpElWid
))
96 self
.elwid_type
= elwid_type
97 scalar_elwid
= elwid_type(0)
99 # TODO, explain why this is needed. Scalar should *NOT*
100 # be doing anything other than *DIRECTLY* passing the
101 # Signal() arguments *DIRECTLY* to nmigen.Signal.
102 # UNDER NO CIRCUMSTANCES should ANY attempt be made to
103 # treat SimdSignal as a "scalar Signal". fuller explanation:
104 # https://bugs.libre-soc.org/show_bug.cgi?id=734#c3
105 if vec_el_counts
is None:
107 vec_el_counts
= SimdMap({scalar_elwid
: 1})
108 elif issubclass(elwid_type
, FpElWid
):
109 vec_el_counts
= DEFAULT_FP_VEC_EL_COUNTS
111 vec_el_counts
= DEFAULT_INT_VEC_EL_COUNTS
113 # TODO, explain this function's purpose
114 def check(elwid
, vec_el_count
):
115 assert type(elwid
) == elwid_type
, "inconsistent ElWid types"
116 vec_el_count
= int(vec_el_count
)
117 assert vec_el_count
!= 0 \
118 and (vec_el_count
& (vec_el_count
- 1)) == 0,\
119 "vec_el_counts values must all be powers of two"
123 self
.vec_el_counts
= SimdMap
.map_with_elwid(check
, vec_el_counts
)
124 self
.full_el_count
= max(self
.vec_el_counts
.values())
127 if elwid
is not None:
130 self
.elwid
= scalar_elwid
132 self
.elwid
= Signal(elwid_type
)
135 return (f
"SimdScope(\n"
136 f
" elwid={self.elwid},\n"
137 f
" elwid_type={self.elwid_type},\n"
138 f
" vec_el_counts={self.vec_el_counts},\n"
139 f
" full_el_count={self.full_el_count})")
142 # from here, the functions are context-aware variants of standard
143 # nmigen API (Signal, Signal.like, Shape) which are to be redirected
144 # to either their standard scalar nmigen equivalents (verbatim)
145 # or to the SimdSignal equivalents. each one is to be documented
146 # CAREFULLY and CLEARLY.
149 def Signal(self
, shape
=None, *, name
=None, reset
=0, reset_less
=False,
150 attrs
=None, decoder
=None, src_loc_at
=0):
152 # scalar mode, just return a nmigen Signal. THIS IS IMPORTANT.
153 # when passing in SimdShape it should go "oh, this is
154 # an isinstance Shape, i will just use its width and sign"
155 # which is the entire reason why SimdShape had to derive
157 return Signal(shape
=shape
, name
=name
, reset
=reset
,
158 reset_less
=reset_less
, attrs
=attrs
,
159 decoder
=decoder
, src_loc_at
=src_loc_at
)
161 # SIMD mode. shape here can be either a SimdShape,
162 # a Shape, or anything else that Signal can take (int or
163 # a tuple (int,bool) for (width,sign)
164 s
= SimdSignal(mask
=self
, # should contain *all* context needed,
165 # which goes all the way through to
166 # the layout() function, passing
167 # 1) elwid 2) vec_el_counts
168 shape
=shape
, # should contain the *secondary*
169 # part of the context needed for
170 # the layout() function:
171 # 3) lane_shapes 4) fixed_width
172 name
=name
, reset
=reset
,
173 reset_less
=reset_less
, attrs
=attrs
,
174 decoder
=decoder
, src_loc_at
=src_loc_at
)
175 # set the module context so that the SimdSignal can create
176 # its own submodules during AST creation
177 s
.set_module(self
.module
)
180 def Signal_like(self
): pass
182 # scalar mode, just return nmigen Signal.like. THIS IS IMPORTANT.
187 def Shape(self
): pass
189 # scalar mode, just return nmigen Shape. THIS IS IMPORTANT.