MEP | 115 |
Author | Jan Decaluwe |
Status | Draft |
Created | 05-Mar-2016 |
MyHDL-version | 1.0 |
Background
Traditionally, the myhdl
library is often imported using wildcard import, as
follows:
from myhdl import *
The obvious convenience is that all objects in the library are readily available. For example:
from myhdl import * @module def mod(...) s = Signal(intbv(0)[8:]) @always_comb def comb(): i = intbv(0)[4:0] ... @instance def inst(): ... return comb, inst, ... sim = Simulation(...) sim.run()
The rationale behind wildcard import is that MyHDL is like a mini-language in
itself that exports many objects. At some point, wildcard imports were
considered acceptable in such a context according to some sources, for example
for the Tkinter
library.
However, the current Python guidelines make it clear that wildcard import should be avoided in production code, except for some very specific applications. From the MyHDL perspective, the disadvantages are the following:
- As the Python guidelines state, wildcard import makes it unclear which names are present in the namespace, confusing both readers and many automated tools.
- There is a chance that Python built-ins or standard library objects are
implicitly shadowed, increasing the potential confusion. Over the years, this
has happened twice already with MyHDL, with the
bin()
function and theenum
module. - Some names may have a different meaning in the Python context, adding to the
confusion when used unqualified. Examples in MyHDL are
module
andinstance
.
Furthermore, the argument of MyHDL as a mini-language is somewhat misleading. It is explicitly intended and encouraged to use MyHDL in conjunction with other Python libraries. That is also what many users are doing. In such scenarios, it becomes increasingly likely that wildcard import causes problems.
The conclusion is that there should be a good way to import MyHDL objects
without relying on wildcard import. The goal of this MEP is to investigate the
issues and to define a recommended approach to import the myhdl
library.
Explicit import
An obvious way to address some of the issues is by an explicit import of the required names. For example:
from myhdl import module, Signal, intbv, always_comb, instance, Simulation
This solves the issue of the potential ambiguity of names. However, it has several disadvantages:
- the import line can become quite lengthy
- any required object name has to be added explicitly to the import line
- all names in the code are still unqualified, which may be confusing to a reviewer
Module import
A straightforward approach is to import the module as is:
import myhdl
This addresses all the issues but has one important disadvantage: some code may look heavy. For example:
import myhdl ... s = myhdl.Signal(myhdl.intbv(0)[8:])
Types such as Signal
and intbv
types are used intensively in behavioral
descriptions. In this case, qualified names can be an inconvenient distraction
to code writers and reviewers.
A dual approach
To find good solution, we take the standpoint of a code reviewer and recognize
that not all objects serve the same purpose. Some objects, like Simulation
and
toVerilog
, are typically only used at a few locations in the code. Other
objects are used as decorators on functions. They serve as anchor points in the
code. Moreover, as functions are used for a lot of different purposes, it is
important to define the particular purpose very clearly. For all those objects,
qualified names are an appropriate choice.
On the other hand, types such as Signal
and intbv
are used to describe the
detailed actual behavior. In that case, it can be more convenient to use
unqualified names.
The conclusion is that a dual approach is appropriate, where some objects are referred to with a qualified name, and others with an unqualified name.
Proposal for a recommended import strategy
The dual import approach consists of two import lines: the first line for the module itself, the second for a limited set of unqualified names.
The module usage can be made more lightweight without jeopardizing clarity by
importing it as the shorthand hdl
, as follows:
import myhdl as hdl
This import reads nicely as "import MyHDL as the HDL". Qualified objects also read nicely, for example as "HDL module" and "HDL simulation".
The second line imports a limited set of unqualified type names from the
library, such as Signal
and intbv
.
The example shown earlier now becomes:
import myhdl as hdl from myhdl import Signal, intbv @hdl.module def mod(...) s = Signal(intbv(0)[8:]) @hdl.always_comb def comb(): i = intbv(0)[4:0] ... @hdl.instance def inst(): ... return comb, inst, ... sim = hdl.Simulation(...) sim.run()
The recommendation would be that only objects that are used within behavioral descriptions (i.e. within generator code) may be referred to with unqualified names. In case of ambiguity in a particular piece of code, the module shorthand is always available as a fallback solution.
Alternatives
An alternative for the shorthand name hdl
is the even shorter name hw
, that
stands for "hardware". Qualified objects would read nicely as "hardware module"
or "hardware simulation".
Another possibility is to define the shorthand name as another namespace object in the library itself. This would enable single line imports as follows:
from myhdl import hdl, Signal, intbv
This would also be a more direct way to suggest the use of recommended shorthand name. On the other hand, this approach may be "too clever", and more confusing than the more explicit dual import strategy.
Decision
To be completed