Fixes #107.
         self._append("  memory width {} size {} {}\n", width, size, name)
         return name
 
-    def cell(self, kind, name=None, params={}, ports={}, src=""):
+    def cell(self, kind, name=None, params={}, ports={}, attrs={}, src=""):
         self._src(src)
         name = self._make_name(name, local=False)
+        for attr_name, attr_value in attrs.items():
+            self.attribute(attr_name, attr_value)
         self._append("  cell {} {}\n", kind, name)
         for param, value in params.items():
             if isinstance(value, str):
             return "\\{}".format(fragment.type), port_map
 
     module_name  = hierarchy[-1] or "anonymous"
-    module_attrs = {}
+    module_attrs = OrderedDict()
     if len(hierarchy) == 1:
         module_attrs["top"] = 1
     module_attrs["nmigen.hierarchy"] = ".".join(name or "anonymous" for name in hierarchy)
                     compiler_state.resolve_curr(signal, prefix=sub_name)
                 sub_ports[port] = rhs_compiler(value)
 
-            module.cell(sub_type, name=sub_name, ports=sub_ports, params=sub_params)
+            module.cell(sub_type, name=sub_name, ports=sub_ports, params=sub_params,
+                        attrs=subfragment.attrs)
 
         # If we emit all of our combinatorial logic into a single RTLIL process, Verilog
         # simulators will break horribly, because Yosys write_verilog transforms RTLIL processes
 
         self.statements = []
         self.domains = OrderedDict()
         self.subfragments = []
+        self.attrs = OrderedDict()
         self.generated = OrderedDict()
         self.flatten = False
 
         self.named_ports = OrderedDict()
 
         for (kind, name, value) in args:
-            if kind == "p":
+            if kind == "a":
+                self.attrs[name] = value
+            elif kind == "p":
                 self.parameters[name] = value
             elif kind in ("i", "o", "io"):
                 self.named_ports[name] = (value, kind)
                                 .format((kind, name, value)))
 
         for kw, arg in kwargs.items():
-            if kw.startswith("p_"):
+            if kw.startswith("a_"):
+                self.attrs[kw[2:]] = arg
+            elif kw.startswith("p_"):
                 self.parameters[kw[2:]] = arg
             elif kw.startswith("i_"):
                 self.named_ports[kw[2:]] = (arg, "i")
 
         else:
             new_fragment = Fragment()
             new_fragment.flatten = fragment.flatten
+        new_fragment.attrs = OrderedDict(fragment.attrs)
         self.map_ports(fragment, new_fragment)
         self.map_subfragments(fragment, new_fragment)
         self.map_domains(fragment, new_fragment)
 
         s5 = Signal()
         s6 = Signal()
         inst = Instance("foo",
+            ("a", "ATTR1", 1),
             ("p", "PARAM1", 0x1234),
             ("i", "s1", s1),
             ("o", "s2", s2),
             ("io", "s3", s3),
+            a_ATTR2=2,
             p_PARAM2=0x5678,
             i_s4=s4,
             o_s5=s5,
             io_s6=s6,
         )
+        self.assertEqual(inst.attrs, OrderedDict([
+            ("ATTR1", 1),
+            ("ATTR2", 2),
+        ]))
         self.assertEqual(inst.parameters, OrderedDict([
             ("PARAM1", 0x1234),
             ("PARAM2", 0x5678),
         self.assertEqual(fp.ports, SignalDict([
             (s, "o"),
         ]))
+
+    def test_prepare_attrs(self):
+        self.setUp_cpu()
+        self.inst.attrs["ATTR"] = 1
+        f = self.inst.prepare()
+        self.assertEqual(f.attrs, OrderedDict([
+            ("ATTR", 1),
+        ]))