Hi,

On 12/20/2011 10:49 PM, Bob Cunningham wrote:
First, I'm very impressed by your creation of Migen and FHDL!

Don't be too impressed with FHDL, it's just a small practical tool. I covered it more in the documentation because I think it is less obvious and self documenting than the other Migen components.

I'm an FPGA newbie, with an embedded/real-time software background
rather than hardware. I was initially attracted to MyHDL because of its
ability to make full use of Python to not only create circuits, but also
to implement both synthesizable and software testbenches using the full
power of Python.

(...)

we then rely on Python algorithms to build complex
structures by combining FHDL elements and encapsulating them in
"fragments".

In general, it seems to me that you tend to use names that are way too
generic, which hide the intent and meaning of both the concept and the
implementation of it in Migen/FHDL.

'Fragments'? To me, 'fragments' are what you get when you drop a mug or
explode a grenade.

This doesn't sound "generic" in the context of programming :)

In Migen/FHDL, these are actually chunks of pre-written HDL code.

No, they are not. They are formal descriptions of synchronous circuits.

Rather than call them 'fragments', I would
recommend a more descriptive term such as 'source', 'HDL', or
'implementation'.

"Fragment" actually describes not too badly the way they are used - pieces of circuits returned by functions, which are combined to form a whole. I could have used "circuit", "module" or "component", but _that_ would have been too generic :)

The FHDL module also contains a back-end to produce synthesizable
Verilog, and some basic analysis functions. It would be possible to
develop a VHDL back-end as well, though more difficult than for Verilog -
we are "cheating" a bit now as Verilog provides most of the FHDL
semantics.

Does FHDL address Jon Decaluwe's concerns about Verilog?
http://www.sigasi.com/content/verilogs-major-flaw

In a trivial way, yes - the Verilog output of Migen consists of a single large synchronous always block. If we decide to have a back-end that splits the generated code over several blocks or modules in order to improve readability, the back-end would have to take care of those Verilog problems.

FHDL differs from MyHDL [2] in fundamental ways. MyHDL follows the
event-driven paradigm of traditional HDLs (see Background, #1) while FHDL
separates the code into combinatorial statements, synchronous statements,
and reset values.

MyHDL supports combinatorial and synchronous logic just fine, doesn't
it? And reset values too, right?

Yes, just like Verilog and VHDL do. But it's quite some manual coding (see Background, #1).

In MyHDL, the logic is described directly in the Python
AST. The converter to Verilog or VHDL then examines the Python AST and
recognizes a subset of Python that it translates into V*HDL statements.
This seriously impedes the capability of MyHDL to generate logic
procedurally.

Isn't this the specific property that also makes MyHDL test benches so
powerful, permitting synthesizable and non-synthesizable code to mix so
well together?

That's one strength of MyHDL, yes. But the main focus of Migen is to automate the generation of complex synchronous hardware circuits, which doesn't seem to be that of MyHDL.

If there is any 'limitation', it is simply that MyHDL is not yet
complete, and still has much growing to do.

Doing what the other FHDL components do using MyHDL requires generating Python ASTs and pass them to the converter. Also, MyHDL requires that signal be created in a special way (and the code creating the signals have to be passed to the converter as well). This makes it a lot less flexible than FHDL when it comes to procedural logic generation.

Constant
--------
This object should be self-explanatory. All constant objects contain a BV
object and a value. If no BV object is specified, one will be made up
using the following rules:
- If the value is positive, the BV is unsigned and has the minimum
number of bits needed to represent the constant's value in the canonical
base-2 system.
- If the value is negative, the BV is signed, and has the minimum
number of bits needed to represent the constant's value in the canonical
two's complement, base-2 system.

Is it possible to create a signed constant with a positive value?

Read carefully: "***If no BV object is specified***, one will be made up..."
You can create a signed positive constant simply by specifying a signed BV object, which bypasses these rules, and passing a positive value.

What happens if I create a 15-bit positive constant (say, 32767), and
try to set a 16-bit signed register (with a reset value of -32767) to
its value? Is the bit alignment guaranteed to be correct for every
possible case?

FHDL just dumbly translates the assignment to Verilog, which has its own (non-perfect) sign extension rules. In this particular case, they would produce the correct result, but they do have several gotchas. You are welcome to implement a better system, if you wish (MyHDL's intbv is pretty good, but more work, and I pick my battles).

I would prefer to see signed and unsigned integer logic never be
permitted to mix, other than by explicit conversion functions. That is,
implement them as completely separate types, where the conversion
functions take a value of one type and either return an error (out of
range), or a value of the other type.

I have seldom made sign-related mistakes when writing HDL (honestly!). I have, however, made tons of mistakes with missing resets, typos, missing signals in groups, connecting signals from the wrong pipeline stage, incorrect latency compensation, etc. Migen is designed to address the problems that I have encountered in practice, not theoretical ones.

The properties of a signal object are:
- a bit vector description
- a name, used as a hint for the V*HDL back-end name mangler.
- a boolean "variable". If true, the signal will behave like a VHDL
variable, or a Verilog reg that uses blocking assignment. This parameter
only has an effect when the signal's value is modified in a synchronous
statement.

Would you ever have a single signal instance that would then be modified
in *both* synchronous and combinatorial statements?

This never happens in synthesizable code, which is what I'm concerned with, and rarely in simulations. And code that does this can often be easily rewritten to avoid this situation.

If not, then shouldn't there be two types of signals (rather than a
parameter) to avoid mistakes in this area (by accidentally setting the
boolean parameter incorrectly)?

Again, strong type checking will likely be needed to prevent misuse.

Can you give an example of such misuse?

- the signal's reset value. It must be an integer, and defaults to 0.
When the signal's value is modified with a synchronous statement, the
reset value is the initialization value of the associated register.
When the signal is assigned to in a conditional combinatorial statement
(If or Case), the reset value is the value that the signal has when no
condition that causes the signal to be driven is verified. This enforces
the absence of latches in designs. If the signal is permanently driven
using a combinatorial statement, the reset value has no effect.

Is this implemented as a 'hidden constant'? If so, won't it make code
maintenance much more difficult, since it prevents constant values from
being declared together?

You can try to keep signals and their associated logic close together in the code, in order to minimize the impact of the problem. See for example the UART code:
https://github.com/milkymist/milkymist-ng/blob/master/milkymist/uart/__init__.py#L19
which first defines the enable16 signals, their driver, and then the rest of the signals and logic.

And you are free to modify the reset value after creation of the signal, so you can always keep that statement close to the other assignments to the signal.

An error should be raised (and processing aborted) whenever a signal
driven by a combinatorial statement has a reset value. Never allow such
code to run successfully.

As I said, it makes sense to have a reset value even there, unless the signal is driven by an unconditional statement. But if you want to implement a "linter" that verifies that defined reset values are not completely overridden by unconditional statements, you can go ahead and do it :)

The sole purpose of the name property is to make the generated V*HDL code
easier to understand and debug. From a purely functional point of view,
it is perfectly OK to have several signals with the same name property.
The back-end will generate a unique name for each object. If no name
property is specified, Migen will analyze the code that created the
signal object, and try to extract the variable or member name from there.
It then uses the module name that created the signal, a underscore, and
the variable name. For example, if we are in module "foo", the following
statements will create one or several signal(s) named "foo_bar":
bar = Signal()
self.bar = Signal()
self.baz.bar = Signal()
bar = [Signal() for x in range(42)]

Won't this make testbenches harder (or impossible) to write?

Let's say you want to examine some signals in the list (the hardest case). You have several options: 1) put them into the pads set of the fragment so they are brought as ports to the top level 2) give the signal(s) you want to examine a distinctive name post-creation, so it will stand out in the generated Verilog
3) use bar = [Signal(name="foo_bar_list"+str(x) for x in range(42)]

Doesn't "d.be(a * b + c)" look quite odd? I'd really much prefer to see
'd = a * b + c'.

As far as I know, Python does not allow you to redefine assignment. MyHDL can have this syntax only because the V*HDL converter operates on the Python AST, but this has fundamental disadvantages (see above).

Beware! Slices work like Python slices, not like VHDL or Verilog slices.
The first bound is the index of the LSB and is inclusive. The second
bound is the index of MSB and is exclusive. In V*HDL, bounds are MSB:LSB
and both are inclusive.

Does the above match what MyHDL does?

Yes.

As an example, the statement:
a[0].be(b)
is equivalent to:
_Assign(_Slice(a, 0, 1), b)

'Be' is the MOST generic word in the entire English language! Why not
use something much more specific and descriptive, such as 'gets'

About as generic, and it's two letters more.

or 'set'

Clashes with the built-in set().

or even 'assign'?

Too long.

Better yet, why not use '='? a[0] = b

See above.

Example:
If(tx_count16 == 0,
tx_bitcount.be(tx_bitcount + 1),
If(tx_bitcount == 8,
self.tx.be(1)
).Elif(tx_bitcount == 9,
self.tx.be(1),
tx_busy.be(0)
).Else(
self.tx.be(tx_reg[0]),
tx_reg.be(Cat(tx_reg[1:], 0))
)
)

The above is Just Plain Ugly. It is closer to macro assembly language
than Python.

Those elements are not macros, the structure stays in the list/fragment, and is only translated into Verilog at the last moment.

I hope your methods have strong parameter checking, for this could be a
PITA to debug.

It's actually quite easy to debug. The generated Verilog has proper indentation and meaningful identifiers, so problems there can be easily spotted. But you are right - adding some assert(isinstance(...)...) into the methods would help noticing the issues a bit earlier.

I have several concerns:

1. How are testbenches implemented in FHDL?

Convert the fragment(s) you want to simulate and use a Verilog simulator.

2. Can I use FHDL to 'wrap' existing Verilog and VHDL code so they can
be easily accessed within the Migen/FHDL environment?

Yes, with instances.
Here is an example of a LM32 wrapper (compatible with Migen Bus):
https://github.com/milkymist/milkymist-ng/blob/master/milkymist/lm32/__init__.py

3. How much debugging will I have to do outside of FHDL because of
errors generated by FHDL? What will FHDL do to ensure only valid
Verilog/VHDL can be generated?

My idea of Migen is that you build and test small components individually, then when putting those components together, automation helps building code with few errors. So I'm unlikely to develop much into that direction. But most linters, which help finding problems earlier, are welcome additions. Soo... this directly depends on how many Migen patches you send to this mailing list :-)

4. Does FHDL support cosimulation like MyHDL?

FHDL doesn't provide any simulation environment, but relies on conversion + a external V*HDL simulator. Now, it is possible to have one (and probably a high performance one quite easily, since it can be directly cycle-based), and support cosimulation, but it's definitely not my priority.

5. Will FHDL and MyHDL play nice together?

In what regard?

While I applaud your work, I wish you had forked MyHDL instead. The few
forward steps FHDL takes seem to me to be overwhelmed by the major steps
FHDL doesn't take (compared to MyHDL).

This depends on your perspective. Mine is algorithmic generation of synthesizable RTL. MyHDL is about "V*HDL done right": powerful test benches, and ironing out of various annoying details (e.g. intbv vs. messy arithmetic rules).

Now we can certainly have a MyHDL back-end for FHDL, but I'm not a big fan of heavily-layered systems. I'd prefer to have the missing MyHDL bits implemented into FHDL, which should not be such a big deal (basically, intbv + ability to generate VHDL).

FHDL seems to be more of a macro-based Verilog editor/scripter than
anything like Python.

Again - and I stress that - FHDL is not a macro preprocessor. The AST (i.e. the structure of what you call "macros") stays and can be examined and manipulated until the Verilog conversion. To give a simple example, see the code that performs synchronous reset insertion, just before Verilog conversion:
https://github.com/milkymist/migen/blob/master/migen/fhdl/convtools.py#L107
Using a macro preprocessor, you would have a hard time telling which signals have to be synchronously reset...

In fact, half the point of FHDL is being a tool used by higher-level Migen components.

Again, I'm 'just a newbie', so some of my comments and questions may be
exposing my ignorance instead of issues with FHDL. That's also why I'm
responding to you directly

One of the points of a mailing list is to permit horizontal communication, e.g. answering of beginner questions by other beginners or intermediates.

Regards,
Sébastien
_______________________________________________
http://lists.milkymist.org/listinfo.cgi/devel-milkymist.org
IRC: #milkymist@Freenode

Reply via email to