1"""
2========================================================================
3errors.py
4========================================================================
5Errors thrown by other files in this folder.
6
7Author : Shunning Jiang
8Date   : Dec 27, 2018
9"""
10import inspect
11
12
13class FieldReassignError( Exception ):
14  """ Raise when a deprecated API is called"""
15
16class PyMTLDeprecationError( Exception ):
17  """ Raise when a deprecated API is called"""
18
19class SignalTypeError( Exception ):
20  """ Raise when a declared signal is of wrong type """
21
22class MultiWriterError( Exception ):
23  """ Raise when a variable is written by multiple update blocks/nets """
24
25class NoWriterError( Exception ):
26  """ Raise when a net has no writer (driver) """
27  def __init__( self, nets ):
28    return super().__init__( \
29    "The following nets need drivers.\nNet:\n - {} ".format(
30      "\nNet:\n - ".join( [ "\n - ".join( [ repr(x) for x in y ] )
31                            for y in nets ]) ) )
32
33class WriteNonSignalError( Exception ):
34  """ In update/update_ff, raise when a component is assigned """
35  def __init__( self, hostobj, blk, lineno, obj ):
36
37    filepath = inspect.getfile( hostobj.__class__ )
38    blk_src, base_lineno  = inspect.getsourcelines( blk )
39
40    # Shunning: we need to subtract 1 from inspect's lineno when we add it
41    # to base_lineno because it starts from 1!
42    lineno -= 1
43    error_lineno = base_lineno + lineno
44
45    return super().__init__( \
46"""
47In file {}:{} in {}
48
49{} {}
50^^^ Cannot Assign to {} of type {}.
51In update block, assigning values to components/interfaces/method ports is forbidden.
52(when constructing instance {} of class \"{}\" in the hierarchy)
53
54Suggestion: check the declaration of the variables, or fix this assignment.""".format( \
55      filepath, error_lineno, blk.__name__,
56      error_lineno, blk_src[ lineno ].lstrip(''),
57      repr(obj), obj.__class__.__name__,
58      repr(hostobj), hostobj.__class__.__name__ )
59    )
60
61class UpdateBlockWriteError( Exception ):
62  """ In update, raise when signal is not @= -ed """
63  def __init__( self, hostobj, blk, op, lineno, suggestion ):
64
65    filepath = inspect.getfile( hostobj.__class__ )
66    blk_src, base_lineno  = inspect.getsourcelines( blk )
67
68    # Shunning: we need to subtract 1 from inspect's lineno when we add it
69    # to base_lineno because it starts from 1!
70    lineno -= 1
71    error_lineno = base_lineno + lineno
72
73    return super().__init__( \
74"""
75In file {}:{} in {}
76
77{} {}
78^^^ In update, only '@=' statements can assign value to signals, not {}
79(when constructing instance {} of class \"{}\" in the hierarchy)
80
81Suggestion: Line {} {}""".format( \
82      filepath, error_lineno, blk.__name__,
83      error_lineno, blk_src[ lineno ].lstrip(''), op,
84      repr(hostobj), hostobj.__class__.__name__,
85      error_lineno, suggestion )
86    )
87
88class UpdateFFBlockWriteError( Exception ):
89  """ In update_ff, raise when signal is not <<= -ed"""
90  def __init__( self, hostobj, blk, op, lineno, suggestion ):
91
92    filepath = inspect.getfile( hostobj.__class__ )
93    blk_src, base_lineno  = inspect.getsourcelines( blk )
94
95    # Shunning: we need to subtract 1 from inspect's lineno when we add it
96    # to base_lineno because it starts from 1!
97    lineno -= 1
98    error_lineno = base_lineno + lineno
99
100    return super().__init__( \
101"""
102In file {}:{} in {}
103
104{} {}
105^^^ In update_ff, only '<<=' statements can assign value to signals, not {}
106(when constructing instance {} of class \"{}\" in the hierarchy)
107
108Suggestion: Line {} {}""".format( \
109      filepath, error_lineno, blk.__name__,
110      error_lineno, blk_src[ lineno ].lstrip(''), op,
111      repr(hostobj), hostobj.__class__.__name__,
112      error_lineno, suggestion )
113    )
114
115class UpdateFFNonTopLevelSignalError( Exception ):
116  """ In update_ff, raise when non-top signal is <<= -ed"""
117  def __init__( self, hostobj, blk, lineno ):
118    filepath = inspect.getfile( hostobj.__class__ )
119    blk_src, base_lineno  = inspect.getsourcelines( blk )
120
121    # Shunning: we need to subtract 1 from inspect's lineno when we add it
122    # to base_lineno because it starts from 1!
123    lineno -= 1
124    error_lineno = base_lineno + lineno
125
126    return super().__init__( \
127"""
128In file {}:{} in {}
129
130{} {}
131^^^ In update_ff, currently only top level signals (not a slice or a subfield) can appear on the left-hand side of '<<='
132(when constructing instance {} of class \"{}\" in the hierarchy)
133""".format( \
134      filepath, error_lineno, blk.__name__,
135      error_lineno, blk_src[ lineno ].lstrip(''),
136      repr(hostobj), hostobj.__class__.__name__,
137      )
138    )
139
140class VarNotDeclaredError( Exception ):
141  """ Raise when a variable in an update block is not declared """
142  def __init__( self, obj, field, blk=None, blk_hostobj=None, lineno=0 ):
143
144    if not blk:
145      return super().__init__() # this is just temporary message
146
147    filepath = inspect.getfile( blk_hostobj.__class__ )
148    blk_src, base_lineno  = inspect.getsourcelines( blk )
149
150    # Shunning: we need to subtract 1 from inspect's lineno when we add it
151    # to base_lineno because it starts from 1!
152    lineno -= 1
153    error_lineno = base_lineno + lineno
154
155    return super().__init__( \
156"""
157In file {}:{} in {}
158
159{} {}
160^^^ Field \"{}\" of object \"{}\" (class \"{}\") is accessed in block \"{}\",
161    but {} does not have field \"{}\".
162(when constructing instance {} of class \"{}\" in the hierarchy)
163
164Suggestion: fix incorrect field access at line {}, or fix the declaration somewhere.""".format( \
165      filepath, error_lineno, blk.__name__,
166      error_lineno, blk_src[ lineno ].lstrip(''),
167      field, repr(obj), obj.__class__.__name__, blk.__name__,
168      repr(obj), field,
169      repr(blk_hostobj), blk_hostobj.__class__.__name__,
170      error_lineno ) )
171
172class InvalidIndexError( Exception ):
173  """ Raise when a variable in an update block is not declared """
174  def __init__( self, obj, idx, blk=None, blk_hostobj=None, lineno=0 ):
175
176    if not blk:
177      return super().__init__() # this is just temporary message
178
179    filepath = inspect.getfile( blk_hostobj.__class__ )
180    blk_src, base_lineno  = inspect.getsourcelines( blk )
181
182    # Shunning: we need to subtract 1 from inspect's lineno when we add it
183    # to base_lineno because it starts from 1!
184    lineno -= 1
185    error_lineno = base_lineno + lineno
186
187    if isinstance( idx, tuple ):
188      idx_str = f"[{idx.start}:{idx.stop}]"
189    else:
190      idx_str = f"[{idx}]"
191
192    from pymtl3.datatypes import Bits
193    return super().__init__( \
194"""
195In file {}:{} in {}
196
197{} {}
198^^^ Slice {} of object \"{}\" (class \"{}\") is accessed in block \"{}\",
199    but {} has \"{}\" type{}.
200(when constructing instance {} of class \"{}\" in the hierarchy)
201
202Suggestion: fix incorrect field access at line {}, or fix the declaration somewhere.""".format( \
203      filepath, error_lineno, blk.__name__,
204      error_lineno, blk_src[ lineno ].lstrip(''),
205      idx_str, repr(obj), obj.__class__.__name__, blk.__name__,
206      repr(obj), obj._dsl.Type.__name__, "" if issubclass( obj._dsl.Type, Bits ) else ", not BitsN type",
207      repr(blk_hostobj), blk_hostobj.__class__.__name__,
208      error_lineno ) )
209
210class UnmarkedUpdateOnceError( Exception ):
211  """ In update, raise when signal is not @= -ed """
212  def __init__( self, hostobj, blk, objs ):
213
214    filepath = inspect.getfile( hostobj.__class__ )
215    blk_src, base_lineno  = inspect.getsourcelines( blk )
216    return super().__init__( \
217"""
218In file {}:{} (component {}):
219Since update block {} calls the CL/FL method ports/interfaces below, it should be marked as @update_once
220- {}
221
222Suggestion: Mark update block {} as @update_once""".format( \
223      filepath, base_lineno, repr(hostobj), blk.__name__,
224      "\n- ".join( sorted( [repr(x) for x in objs] ) ),
225      blk.__name__ )
226    )
227
228class UpblkFuncSameNameError( Exception ):
229  """ Raise when two update block/function are declared with the same name """
230  def __init__( self, name ):
231    return super().__init__( \
232      " Cannot declare two update blocks/functions with the same name {}".format( name ) )
233
234class UpblkCyclicError( Exception ):
235  """ Raise when update blocks have cyclic dependencies """
236
237class InvalidConstraintError( Exception ):
238  """ Raise when a defined constraint is of wrong format """
239  def __init__( self ):
240    return super().__init__( \
241      "Constraints between two variables are not allowed!" )
242
243class InvalidConnectionError( Exception ):
244  """ Raise upon an inappropriate attempt to connect two variables """
245
246class InvalidFuncCallError( Exception ):
247  """ Raise upon an inappropriate @s.func function call """
248
249class InvalidPlaceholderError( Exception ):
250  """ Raise upon declaring an update block in a placeholder component. """
251
252class NotElaboratedError( Exception ):
253  """ Raise when processing a model that hasn't been elaborated yet """
254  def __init__( self ):
255    return super().__init__( \
256    "Please elaborate the model first." )
257
258class InvalidAPICallError( Exception ):
259  """ Raise when processing a model that hasn't been elaborated yet """
260  def __init__( self, api_name, obj, top ):
261    return super().__init__( \
262    "{} is only allowed to be called at the top module that .elaborate() "
263    "was called on (an instance of {}), but this API call is on {}." \
264    .format( api_name, top.__class__, "top."+repr(obj)[2:] ) )
265
266class UnsetMetadataError( Exception ):
267  """ Raised when the value of a given metadata key is not set. """
268  def __init__( self, key, obj ):
269    return super().__init__(
270        f"\nAttempting to retrieve unset metadata {key} from component {obj}!" )
271
272class LeftoverPlaceholderError( Exception ):
273  """ Raise upon declaring an update block in a placeholder component. """
274  def __init__( self, placeholders ):
275    return super().__init__( \
276    "Please replace all placeholders with valid components:\n - {}".format(
277      "\n - ".join( [ "top.{} (instance of {})".format( repr(x)[2:], x.__class__ ) \
278                     for x in placeholders ] ) ) )
279