Skip to content

Commit

Permalink
Print one line error messages for wiring
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardt committed Aug 20, 2018
1 parent 13e2f9e commit d8a3b78
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 26 deletions.
8 changes: 8 additions & 0 deletions doc/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ $ MAGMA_LOG_LEVEL=DEBUG MAGMA_COREIR_BACKEND_LOG_LEVEL=WARN MAGMA_LOG_STREAM=std
Same inputs as `MAGMA_LOG_LEVEL`. Sets the logging level specifically for the
coreir backend.

* `MAGMA_INCLUDE_WIRE_TRACEBACK`
Setting this variable enables the traceback feature of magma's error
reporting. When certain wiring errors are made, magma will dump a traceback
similar to the message seen from Python Exceptions, which can be useful for
debugging complex wiring errors with multiple levels of indirection (calls to
multiple generators).


* `MAGMA_ERROR_TRACEBACK_LIMIT`
Valid Inputs - Positive integers (default: 5)
Set the maximum number of stack frames printed in the traceback for magma
Expand Down
12 changes: 8 additions & 4 deletions magma/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .bit import VCC, GND
from .bitutils import seq2int
from .debug import debug_wire, get_callee_frame_info
from .port import report_wiring_error

__all__ = ['ArrayType', 'ArrayKind', 'Array']

Expand Down Expand Up @@ -80,13 +81,16 @@ def wire(i, o, debug_info):
# print('Array.wire(', o, ', ', i, ')')

if not isinstance(o, ArrayType):
error(f'Wiring Error: wiring {repr(o)} ({type(o)}) to'
f' {repr(i)} ({type(i)})', include_wire_traceback=True)
report_wiring_error(f'Cannot wire {repr(o)} (type={type(o)}) to'
f' {repr(i)} (type={type(i)}) because'
f' {repr(o)} is not an Array', debug_info)
return

if i.N != o.N:
error(f'Wiring Error: Arrays must have the same length'
f' {i.N} != {o.N}', include_wire_traceback=True)
report_wiring_error(f'Cannot wire {repr(o)} (type={type(o)},'
f' len={i.N}) to {repr(i)} (type={type(i)},'
f' len={o.N}) because the arrays do not have'
f' the same length', debug_info)
return

for k in range(len(i)):
Expand Down
2 changes: 1 addition & 1 deletion magma/bit.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def wire(i, o, debug_info):
include_wire_traceback=True)
return

i.port.wire(o.port)
i.port.wire(o.port, debug_info)
i.debug_info = debug_info
o.debug_info = debug_info

Expand Down
17 changes: 12 additions & 5 deletions magma/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
log = logging.getLogger("magma")
handler = colorlog.StreamHandler(log_stream)
handler.setFormatter(colorlog.ColoredFormatter(
'%(log_color)s%(levelname)s:%(name)s:%(message)s'))
'%(name)s:%(log_color)s%(levelname)s%(reset)s:%(message)s'))
log.addHandler(handler)


Expand All @@ -38,6 +38,9 @@
logging.warning(f"Unsupported value for MAGMA_LOG_LEVEL: {level}")


__magma_include_wire_traceback = os.getenv("MAGMA_INCLUDE_WIRE_TRACEBACK", False)


traceback_limit = int(os.getenv("MAGMA_ERROR_TRACEBACK_LIMIT", "5"))


Expand All @@ -62,16 +65,15 @@ def print_wire_traceback_wrapped(*args, **kwargs):
include_wire_traceback = kwargs.get("include_wire_traceback", False)
if include_wire_traceback:
del kwargs["include_wire_traceback"]
if include_wire_traceback:
fn("="*20 + " BEGIN: MAGMA ERROR " + "="*20)
if include_wire_traceback and __magma_include_wire_traceback:
fn("="*20 + " BEGIN: MAGMA WIRING ERROR TRACEBACK " + "="*20)
stack_frame = get_original_wire_call_stack_frame()
with StringIO() as io:
traceback.print_stack(f=stack_frame, limit=traceback_limit, file=io)
for line in io.getvalue().splitlines():
fn(line)
fn("="*20 + " END: MAGMA WIRING ERROR TRACEBACK " + "="*20)
res = fn(*args, **kwargs)
if include_wire_traceback:
fn("="*20 + " END: MAGMA ERROR " + "="*20)
return res
return print_wire_traceback_wrapped

Expand All @@ -94,3 +96,8 @@ def warning(message, *args, **kwargs):
@print_wire_traceback
def error(message, *args, **kwargs):
log.error(message, *args, **kwargs)


def get_source_line(filename, lineno):
with open(filename, "r") as f:
return f.readlines()[lineno - 1]
20 changes: 14 additions & 6 deletions magma/port.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .logging import error, warning
from .logging import error, warning, get_source_line
from .backend.util import make_relative

__all__ = ['INPUT', 'OUTPUT', 'INOUT']
__all__ += ['flip']
Expand All @@ -8,6 +9,13 @@
OUTPUT = 'output'
INOUT = 'inout'


def report_wiring_error(message, debug_info):
error(f"\033[1m{make_relative(debug_info[0])}:{debug_info[1]}: {message}",
include_wire_traceback=True)
error(get_source_line(debug_info[0], debug_info[1]))


def flip(direction):
assert direction in [INPUT, OUTPUT, INOUT]
if direction == INPUT: return OUTPUT
Expand Down Expand Up @@ -39,7 +47,7 @@ def __init__(self):
self.inputs = []
self.outputs = []

def connect( self, o, i ):
def connect( self, o, i , debug_info):

# anon Ports are added to the input or output list of this wire
#
Expand All @@ -53,7 +61,7 @@ def connect( self, o, i ):
if not o.anon():
#assert o.bit.direction is not None
if o.bit.isinput():
error("WIRING ERROR: Using an input as an output {}".format(repr(o)), include_wire_traceback=True)
report_wiring_error(f"Using {repr(o)} (an input) as an output", debug_info)
return

if o not in self.outputs:
Expand All @@ -65,7 +73,7 @@ def connect( self, o, i ):
if not i.anon():
#assert i.bit.direction is not None
if i.bit.isoutput():
error("WIRING ERROR: Using an output as an input {}".format(repr(i)), include_wire_traceback=True)
report_wiring_error(f"Using {repr(i)} (an output) as an input", debug_info)
return

if i not in self.inputs:
Expand Down Expand Up @@ -117,7 +125,7 @@ def anon(self):
return self.bit.anon()

# wire a port to a port
def wire(i, o):
def wire(i, o, debug_info):
#if o.bit.direction is None:
# o.bit.direction = OUTPUT
#if i.bit.direction is None:
Expand All @@ -139,7 +147,7 @@ def wire(i, o):
else:
w = Wire()

w.connect(o, i)
w.connect(o, i, debug_info)

#print("after",o,"->",i, w)

Expand Down
19 changes: 13 additions & 6 deletions tests/test_type/test_type_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ def test_array_lengths(caplog):

buf = Buf()
wire(main.O, buf.I)
assert caplog.records[-2].msg == "Wiring Error: Arrays must have the same length 8 != 7"
assert caplog.records[-3].msg == " wire(main.O, buf.I)"
assert "\n".join(x.msg for x in caplog.records) == """\
\033[1mtests/test_type/test_type_errors.py:10: Cannot wire main.O (type=Array(7,In(Bit)), len=8) to inst0.I (type=Array(8,In(Bit)), len=7) because the arrays do not have the same length
wire(main.O, buf.I)
"""


def test_array_to_bit(caplog):
Expand All @@ -19,8 +21,11 @@ def test_array_to_bit(caplog):

buf = Buf()
wire(main.O, buf.I)
assert caplog.records[-2].msg == "Wiring Error: wiring main.O (In(Bit)) to inst0.I (Array(8,In(Bit)))"
assert caplog.records[-3].msg == " wire(main.O, buf.I)"
assert "\n".join(x.msg for x in caplog.records) == """\
\033[1mtests/test_type/test_type_errors.py:23: Cannot wire main.O (type=In(Bit)) to inst0.I (type=Array(8,In(Bit))) because main.O is not an Array
wire(main.O, buf.I)
"""


def test_bit_to_array(caplog):
Buf = DeclareCircuit('Buf', "I", In(Bit), "O", Out(Array(8, Bit)))
Expand All @@ -29,5 +34,7 @@ def test_bit_to_array(caplog):

buf = Buf()
wire(buf.I, main.O)
assert caplog.records[-2].msg == "Wiring Error: wiring inst0.I (In(Bit)) to main.O (Array(7,In(Bit)))"
assert caplog.records[-3].msg == " wire(buf.I, main.O)"
assert "\n".join(x.msg for x in caplog.records) == """\
\033[1mtests/test_type/test_type_errors.py:36: Cannot wire inst0.I (type=In(Bit)) to main.O (type=Array(7,In(Bit))) because inst0.I is not an Array
wire(buf.I, main.O)
"""
12 changes: 8 additions & 4 deletions tests/test_wire/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ def test_input_as_output(caplog):

buf = Buf()
wire(main.O, buf.I)
assert caplog.records[-2].msg == "WIRING ERROR: Using an input as an output main.O"
assert caplog.records[-3].msg == " wire(main.O, buf.I)"
assert "\n".join(x.msg for x in caplog.records) == """\
\033[1mtests/test_wire/test_errors.py:10: Using main.O (an input) as an output
wire(main.O, buf.I)
"""


def test_output_as_input(caplog):
Expand All @@ -19,8 +21,10 @@ def test_output_as_input(caplog):

a = A()
wire(main.I, a.O)
assert caplog.records[-2].msg == "WIRING ERROR: Using an output as an input inst0.O"
assert caplog.records[-3].msg == " wire(main.I, a.O)"
assert "\n".join(x.msg for x in caplog.records) == """\
\033[1mtests/test_wire/test_errors.py:23: Using inst0.O (an output) as an input
wire(main.I, a.O)
"""


if __name__ == "__main__":
Expand Down

0 comments on commit d8a3b78

Please sign in to comment.