Consider a very simple parameterized module, permute, with a single input and output of equal width. Output bits are driven from input bits according to a single generation parameter,
For example: mapping = (1, 2, 0) would generate a module containing these assignments:
Making parameterized assignments like this is in europa straightforward (assume @mapping contains the particular permutation to generate):
(europa generator, derived from europa_module_factory)
(complete Verilog listing, as generated)
A similar implementation in MyHDL turns out to be pretty clean:
(initial attempt in its entirety)
Using the above, I wrote some simple unit tests which try a variety of permutation mappings of various widths, driving a bunch of input values and verify the resulting output values. This works great! But trouble loomed when I tried to generate some Verilog. My innocent-looking code apparently angers toVerilog, and elicits a giant stack dump. Here's the final part of the stack dump, which I think is the actual error:
So... it is documented that toVerilog does not support everything you can write in python; in fact, you're limited to a pretty small subset of the language in code which will be translated to HDL. This might be what I've run into, here, but sadly I don't know what a "(down)range call" is, nor who was expecting it.
I asked Stefaan, who originally pointed me toward MyHDL, about this, and he mentioned a discussion of an issue in a forum posting. In that thread, someone attempted a different implementation of a permuting module, and ran into trouble. Mr. MyHDL himself, Jan Decaluwe, came to the rescue with a method. The solution is to loop through the mapping not directly by element, but instead by index number, and to use a temp variable to index into the mapping list. (I think this is leveraging off of the same special case which allows inferred RAMs to work.) Here's what the modified generator function looks like:
I think it's pretty clear that the above version of the generator does the same thing as the initial version (though it's a bit more cluttered), and, since my unit tests pass just fine with this version, I'm pretty happy with it. And, toVerilog runs without error. So what does the generated Verilog look like?
(complete Verilog listing, as generated)
This is a lot more complex than the simple list of assignments I was hoping for.
So, the tradeoffs: with MyHDL, it's easy to write unit tests with the full power of python, and the tested behavior can then be written out as Verilog. If the conversion process is successful, the resulting generated Verilog can be regarded (though warily) as tested. But, the generated Verilog may not be so readable, and confirming that it matches the original design intent requires work (via PLI, you can run your unit tests against the generated Verilog in a simulator - haven't tried this yet).
Writing the behavioral definition can be a struggle, since it may not be clear which aspects of the language toVerilog will accept (though that's probably just my lack of experience with the tool showing through).
On the whole I think the tradeoff is worth it: MyHDL should make a strong foundation for building up a library of tested functional building blocks.
As usual, I'm attaching the various files associated with this article. At the top level are the MyHDL generator script, with its test script, verilog generator script and output file. One level down in subdirectory permute, you'll find the Europa generator, associated scripts and output file.
Say, do people prefer winzip files over rar files? Let me know.
20080213 20:50: Edit: for no good reason I posted used one parameterization (mapping) for the europa example, and a different one for MyHDL example. That doesn't help to make things clear! I fixed it... sorry if you were confused by the original version.
assign x[0] = a[1];
assign x[1] = a[2];
assign x[2] = a[0];
for my $i (0 .. -1 + @mapping)
{
$module->add_contents(
e_assign->new({
lhs => "x\[$i\]",
rhs => "a\[$mapping[$i]\]",
}),
);
}
@always_comb
def logic():
for i in mapping:
x.next[i] = a[mapping[i]]
(lots of lines of stack trace deleted)
myhdl.ToVerilogError: in file .../permute.py, line 15:
Requirement violation: Expected (down)range call
@always_comb
def logic():
tmp = intbv(0, min=0, max=len(mapping))
for i in range(len(mapping)):
index[:] = mapping[i]
x.next[i] = a[int(index)]
always @(a) begin: _permute_logic
reg [2-1:0] tmp;
integer i;
tmp = 0;
for (i=0; i<3; i=i+1) begin
// synthesis parallel_case full_case
case (i)
0: tmp = 1;
1: tmp = 2;
default: tmp = 0;
endcase
x[i] <= a[tmp];
end
end
python testPermute.py