fhdl, back: trace and emit source locations of values.
authorwhitequark <cz@m-labs.hk>
Thu, 13 Dec 2018 11:35:20 +0000 (11:35 +0000)
committerwhitequark <cz@m-labs.hk>
Thu, 13 Dec 2018 11:44:06 +0000 (11:44 +0000)
examples/cdc.py
nmigen/back/rtlil.py
nmigen/fhdl/ast.py
nmigen/fhdl/cd.py
nmigen/fhdl/xfrm.py

index 7e38885334a90c8d78da927409631fa9ff367959..8bbdc11da6ae9fad45eb692d4eb23a5274187330 100644 (file)
@@ -4,6 +4,8 @@ from nmigen.genlib.cdc import *
 
 
 i, o = Signal(name="i"), Signal(name="o")
-frag = MultiReg(i, o).get_fragment(platform=None)
+m = Module()
+m.submodules += MultiReg(i, o)
+frag = m.lower(platform=None)
 # print(rtlil.convert(frag, ports=[i, o]))
 print(verilog.convert(frag, ports=[i, o]))
index 311a053b73e9e0f89869f84b2a8a4c9df3f1fd38..fdb7fb9f2ce14a3adbc0ae11a3eecf32a14871b3 100644 (file)
@@ -38,7 +38,7 @@ class _Bufferer:
 
     def _src(self, src):
         if src:
-            self._append("  attribute \\src {}", repr(src))
+            self._append("  attribute \\src \"{}\"\n", src.replace("\"", "\\\""))
 
 
 class _Builder(_Namer, _Bufferer):
@@ -70,9 +70,9 @@ class _ModuleBuilder(_Namer, _Bufferer):
 
     def attribute(self, name, value):
         if isinstance(value, str):
-            self._append("attribute \\{} \"{}\"\n", name, value.replace("\"", "\\\""))
+            self._append("  attribute \\{} \"{}\"\n", name, value.replace("\"", "\\\""))
         else:
-            self._append("attribute \\{} {}\n", name, int(value))
+            self._append("  attribute \\{} {}\n", name, int(value))
 
     def wire(self, width, port_id=None, port_kind=None, name=None, src=""):
         self._src(src)
@@ -190,6 +190,11 @@ class _SyncBuilder:
         self.rtlil._append("      update {} {}\n", lhs, rhs)
 
 
+def src(src_loc):
+    file, line = src_loc
+    return "{}:{}".format(file, line)
+
+
 class _ValueTransformer(xfrm.ValueTransformer):
     operator_map = {
         (1, "~"):    "$not",
@@ -275,9 +280,11 @@ class _ValueTransformer(xfrm.ValueTransformer):
             for attr_name, attr_value in node.attrs.items():
                 self.rtlil.attribute(attr_name, attr_value)
             wire_curr = self.rtlil.wire(width=node.nbits, name=wire_name,
-                                        port_id=port_id, port_kind=port_kind)
+                                        port_id=port_id, port_kind=port_kind,
+                                        src=src(node.src_loc))
             if node in self.driven:
-                wire_next = self.rtlil.wire(width=node.nbits, name=wire_curr + "$next")
+                wire_next = self.rtlil.wire(width=node.nbits, name=wire_curr + "$next",
+                                            src=src(node.src_loc))
             else:
                 wire_next = None
             self.wires[node] = (wire_curr, wire_next)
@@ -301,7 +308,7 @@ class _ValueTransformer(xfrm.ValueTransformer):
             "A_SIGNED": arg_sign,
             "A_WIDTH": arg_bits,
             "Y_WIDTH": res_bits,
-        })
+        }, src=src(node.src_loc))
         return res
 
     def match_shape(self, node, new_bits, new_sign):
@@ -318,7 +325,7 @@ class _ValueTransformer(xfrm.ValueTransformer):
                 "A_SIGNED": node_sign,
                 "A_WIDTH": node_bits,
                 "Y_WIDTH": new_bits,
-            })
+            }, src=src(node.src_loc))
             return res
         else:
             return "{} [{}:0]".format(self(node), new_bits - 1)
@@ -347,7 +354,7 @@ class _ValueTransformer(xfrm.ValueTransformer):
             "B_SIGNED": rhs_sign,
             "B_WIDTH": rhs_bits,
             "Y_WIDTH": res_bits,
-        })
+        }, src=src(node.src_loc))
         return res
 
     def on_Operator_mux(self, node):
@@ -366,7 +373,7 @@ class _ValueTransformer(xfrm.ValueTransformer):
             "\\Y": res,
         }, params={
             "WIDTH": res_bits
-        })
+        }, src=src(node.src_loc))
         return res
 
     def on_Operator(self, node):
index 9d3af7deafc970dd611cd8fb1d3ee57b5dd8876a..c5f03e11246b264d85798f14bc16bd7ef476b039 100644 (file)
@@ -1,4 +1,5 @@
 import builtins
+import traceback
 from collections import OrderedDict
 from collections.abc import Iterable, MutableMapping, MutableSet
 
@@ -35,6 +36,16 @@ class Value:
             raise TypeError("Object {} of type {} is not a Migen value"
                             .format(repr(obj), type(obj)))
 
+    def __init__(self, src_loc_at=0):
+        super().__init__()
+
+        src_loc_at += 3
+        tb = traceback.extract_stack(limit=src_loc_at)
+        if len(tb) < src_loc_at:
+            self.src_loc = None
+        else:
+            self.src_loc = (tb[0].filename, tb[0].lineno)
+
     def __bool__(self):
         raise TypeError("Attempted to convert Migen value to boolean")
 
@@ -206,6 +217,7 @@ class Const(Value):
     signed : bool
     """
     def __init__(self, value, shape=None):
+        super().__init__()
         self.value = int(value)
         if shape is None:
             shape = self.value.bit_length(), self.value < 0
@@ -229,8 +241,8 @@ C = Const  # shorthand
 
 
 class Operator(Value):
-    def __init__(self, op, operands):
-        super().__init__()
+    def __init__(self, op, operands, src_loc_at=0):
+        super().__init__(src_loc_at=1 + src_loc_at)
         self.op = op
         self.operands = [Value.wrap(o) for o in operands]
 
@@ -316,7 +328,7 @@ def Mux(sel, val1, val0):
     Value, out
         Output ``Value``. If ``sel`` is asserted, the Mux returns ``val1``, else ``val0``.
     """
-    return Operator("m", [sel, val1, val0])
+    return Operator("m", [sel, val1, val0], src_loc_at=1)
 
 
 class Slice(Value):
@@ -499,8 +511,8 @@ class Signal(Value, DUID):
     """
 
     def __init__(self, shape=None, name=None, reset=0, reset_less=False, min=None, max=None,
-                 attrs=None):
-        super().__init__()
+                 attrs=None, src_loc_at=0):
+        super().__init__(src_loc_at=src_loc_at)
 
         if name is None:
             try:
@@ -537,7 +549,7 @@ class Signal(Value, DUID):
         self.attrs = OrderedDict(() if attrs is None else attrs)
 
     @classmethod
-    def like(cls, other, **kwargs):
+    def like(cls, other, src_loc_at=0, **kwargs):
         """Create Signal based on another.
 
         Parameters
@@ -549,7 +561,7 @@ class Signal(Value, DUID):
         if isinstance(other, cls):
             kw.update(reset=other.reset, reset_less=other.reset_less, attrs=other.attrs)
         kw.update(kwargs)
-        return cls(**kw)
+        return cls(**kw, src_loc_at=1 + src_loc_at)
 
     def shape(self):
         return self.nbits, self.signed
index a838c21e7155a87005bb36cae7f6c625e91fcdbc..09d930a4dd96b244fefb1ecc2c849cf8878aa018 100644 (file)
@@ -40,10 +40,10 @@ class ClockDomain:
             name = name[3:]
         self.name = name
 
-        self.clk = Signal(name=self.name + "_clk")
+        self.clk = Signal(name=self.name + "_clk", src_loc_at=1)
         if reset_less:
             self.rst = None
         else:
-            self.rst = Signal(name=self.name + "_rst")
+            self.rst = Signal(name=self.name + "_rst", src_loc_at=1)
 
         self.async_reset = async_reset
index 86268f62cda50813a5ec8b3f55befea5aa9c7f7e..a2befa4fa9c4f251eb4bff4616c0b70bf6f0cf67 100644 (file)
@@ -38,25 +38,28 @@ class ValueTransformer:
 
     def on_value(self, value):
         if isinstance(value, Const):
-            return self.on_Const(value)
+            new_value = self.on_Const(value)
         elif isinstance(value, Signal):
-            return self.on_Signal(value)
+            new_value = self.on_Signal(value)
         elif isinstance(value, ClockSignal):
-            return self.on_ClockSignal(value)
+            new_value = self.on_ClockSignal(value)
         elif isinstance(value, ResetSignal):
-            return self.on_ResetSignal(value)
+            new_value = self.on_ResetSignal(value)
         elif isinstance(value, Operator):
-            return self.on_Operator(value)
+            new_value = self.on_Operator(value)
         elif isinstance(value, Slice):
-            return self.on_Slice(value)
+            new_value = self.on_Slice(value)
         elif isinstance(value, Part):
-            return self.on_Part(value)
+            new_value = self.on_Part(value)
         elif isinstance(value, Cat):
-            return self.on_Cat(value)
+            new_value = self.on_Cat(value)
         elif isinstance(value, Repl):
-            return self.on_Repl(value)
+            new_value = self.on_Repl(value)
         else:
             raise TypeError("Cannot transform value {!r}".format(value)) # :nocov:
+        if isinstance(new_value, Value):
+            new_value.src_loc = value.src_loc
+        return new_value
 
     def __call__(self, value):
         return self.on_value(value)