--- /dev/null
+# This file is Copyright (c) 2019 Sean Cross <sean@xobs.io>\r
+\r
+from migen.fhdl.module import DUID\r
+from migen.util.misc import xdir\r
+\r
+from litex.soc.interconnect.csr_eventmanager import EventManager\r
+\r
+import textwrap\r
+import inspect\r
+\r
+class ModuleDoc(DUID):\r
+ """Module Documentation Support\r
+\r
+ ModuleDoc enables you to add documentation to your Module. This documentation is in addition to\r
+ any CSR-level documentation you may add to your module, if applicable.\r
+\r
+ There are two ways to use :obj:`ModuleDoc`:\r
+\r
+ 1. Inherit :obj:`ModuleDoc` as part of your class. The docstring of your class will become the\r
+ first section of your class' module documentation\r
+ 2. Add a :obj:`ModuleDoc` object to your class and inherit from :obj:`AutoDoc`.\r
+\r
+ If you inherit from :obj:`ModuleDoc`, then there is no need to call ``__init__()``\r
+\r
+ Synopsis\r
+ --------\r
+\r
+ ::\r
+\r
+ class SomeClass(Module, ModuleDoc, AutoDoc):\r
+ \"\"\"Some Special Hardware Module\r
+\r
+ This is a hardware module that implements something really cool.\r
+ \"\"\"\r
+\r
+ def __init__(self):\r
+ self.other_section = ModuleDoc(title="Protocol Details", body="This section details more\r
+ information about the protocol")\r
+\r
+ """\r
+\r
+ def __init__(self, body=None, title=None, file=None, format="rst"):\r
+ """Construct a :obj:`ModuleDoc` object for use with :obj:`AutoDoc`\r
+\r
+ Arguments\r
+ ---------\r
+\r
+ body (:obj:`str`): Main body of the document. If ``title`` is omitted, then the\r
+ title is taken as the first line of ``body``.\r
+\r
+ title (:obj:`str` Optional): Title of this particular section.\r
+\r
+ file (:obj:`str` Optional): It is possible to load the documentation from an external\r
+ file instead of specifying it inline. This allows for the use of an external text\r
+ editor. If a ``file`` is specified, then it will override the ``body`` argument.\r
+\r
+ format (:obj:`str` Optional): The text format. Python prefers reStructured Text, so this\r
+ defaults to `rst`. If specifying a `file`, then the suffix will be used instead of\r
+ `format`. If you specify a format other than `rst`, you may need to install a converter.\r
+ """\r
+\r
+ import os\r
+ DUID.__init__(self)\r
+ self._title = title\r
+ self._format = format\r
+\r
+ if file == None and body == None and self.__doc__ is None:\r
+ raise ValueError("Must specify `file` or `body` when constructing a ModuleDoc()")\r
+ if file is not None:\r
+ if not os.path.isabs(file):\r
+ relative_path = inspect.stack()[1][1]\r
+ file = os.path.dirname(relative_path) + os.path.sep + file\r
+ (_, self._format) = os.path.splitext(file)\r
+ self._format = self._format[1:] # Strip off "." from extension\r
+\r
+ # If it's a reStructured Text file, read the whole thing in.\r
+ if self._format == "rst":\r
+ with open(file, "r") as f:\r
+ self.__doc__ = f.read()\r
+ # Otherwise, we'll simply make a link to it and let sphinx take care of it\r
+ else:\r
+ self._path = file\r
+ elif body is not None:\r
+ self.__doc__ = body\r
+\r
+ def title(self):\r
+ # This object might not have _title as an attribute, because\r
+ # the ModuleDoc constructor may not have been called. If this\r
+ # is the case, manipulate the __doc__ string directly.\r
+ if hasattr(self, "_title") and self._title is not None:\r
+ return self._title\r
+ _lines = self.__doc__.splitlines()\r
+ return textwrap.dedent(_lines[0])\r
+\r
+ def body(self):\r
+ if hasattr(self, "_title") and self._title is not None:\r
+ return self.__doc__\r
+ _lines = self.__doc__.splitlines()\r
+ _lines.pop(0)\r
+ return textwrap.dedent("\n".join(_lines))\r
+\r
+ def format(self):\r
+ if hasattr(self, "_format") and self._format is not None:\r
+ return self._format\r
+ return "rst"\r
+\r
+ def path(self):\r
+ if hasattr(self, "_path"):\r
+ return self._path\r
+ return None\r
+\r
+def documentationprefix(prefix, documents, done):\r
+ for doc in documents:\r
+ if doc.duid not in done:\r
+ # doc.name = prefix + doc.name\r
+ done.add(doc.duid)\r
+\r
+def _make_gatherer(method, cls, prefix_cb):\r
+ def gatherer(self):\r
+ try:\r
+ exclude = self.autodoc_exclude\r
+ except AttributeError:\r
+ exclude = {}\r
+ try:\r
+ prefixed = self.__prefixed\r
+ except AttributeError:\r
+ prefixed = self.__prefixed = set()\r
+ r = []\r
+ for k, v in xdir(self, True):\r
+ if k not in exclude:\r
+ if isinstance(v, cls):\r
+ r.append(v)\r
+ elif hasattr(v, method) and callable(getattr(v, method)):\r
+ items = getattr(v, method)()\r
+ prefix_cb(k + "_", items, prefixed)\r
+ r += items\r
+ return sorted(r, key=lambda x: x.duid)\r
+ return gatherer\r
+\r
+class AutoDoc:\r
+ """MixIn to provide documentation support.\r
+\r
+ A module can inherit from the ``AutoDoc`` class, which provides ``get_module_documentation``.\r
+ This will iterate through all objects looking for ones that inherit from ModuleDoc.\r
+\r
+ If the module has child objects that implement ``get_module_documentation``,\r
+ they will be called by the``AutoCSR`` methods and their documentation added to the lists returned,\r
+ with the child objects' names as prefixes.\r
+ """\r
+ get_module_documentation = _make_gatherer("get_module_documentation", ModuleDoc, documentationprefix)\r