myhdl / myhdl

The MyHDL development repository
http://www.myhdl.org
GNU Lesser General Public License v2.1
1.05k stars 247 forks source link

[enhancement] limited top-level list-of-signal port conversion #161

Open cfelton opened 8 years ago

cfelton commented 8 years ago

[enhancement] limited top-level list-of-signal port conversion

Currently, list-of-signals in the top-level is not supported for conversion. This issue/enhancement proposes that a subset of a list-of-signals, essentially those whose overall dimension remain 1, support conversion. In other words, the conversion compiler would convert list-of-signals of type bool (one-dimension) to a bit-vector in the target HDL.

There are occasions when a bit-vector (list-of-signals of type bool) or a bank of tristates are desired and/or needed. Currently, there is no conversion process to support individual control of a TristateSignal with more than one bit. One such scenario is described in a seven-segment implementation.

The proposal is to support conversion of top-level list-of-signal ports whose types are bool.

Example: top-level list-of-signal-of-bools

def list_of_bools(x, y):
    nbits = len(x)
    @always_comb
    def assign():
        for ii in range(nbits):
            y[ii].next = x[ii]
    return assign

x, y = [[Signal(bool(0)) for _ in range(27)]
         for __ in range(2)]
toVerilog(list_of_bools, x, y)
toVHDL(list_of_bools, x, y)

Example: top-level list-of-signal-of-tristates-of-bools

def list_of_tristate(tri, infromtri, outtotri):
""" drive the output when `outtotri` is 0 """
    nbits = len(tri)
    tdrv = [tri[ii].driver() for ii in range(nbits)]
    print(type(tdrv), tdrv)

    @always_comb
    def rtl_in():
        for ii in range(nbits):
            if tri[ii] == 0:
                infromtri.next[ii] = 0
            else:
                infromtri.next[ii] = 1

    @always_comb
    def rtl_out():
        for ii in range(nbits):
            if outtotri == 0:
                tdrv[ii].next = 0
            else:
                tdrv[ii].next = None

    return rtl_in, rtl_out

tri = [TristateSignal(bool(1)) for _ in range(8)]
ift = Signal(intbv(0)[8:])
ott = Signal(intbv(0)[8:])
toVerilog(tristate_bank, tri, ift, ott)
toVHDL(tristate_bank, tri, ift, ott)

In both of the above examples, the list-of-signals port would be converted (Verilog version) to input wire [7:0] x, output reg [7:0] y and inout wire [7:0] tri, respectively.

cfelton commented 8 years ago

pinging those who have had an interested in this issue in the past: @josyb, @devbisme.

josyb commented 8 years ago

@cfelton Did I? ;-) You specifically target TristateSignal - which is something I don't use, or rather defer to the vendor's primitives. I would more interested in the general solution, elaborating all LoS, because now and then I have to write a wrapper to check the conversion of a building block.

cfelton commented 8 years ago

@josyb you commented when I had the draft of this in a gist.

That is fine if you use vendor primitives but you will still need an external top-level wrapper or explicit lines for the inouts. This limitation, I think, warrants serious consideration.

As for the LoS wrappers, you should be able to create a single top-level wrapper that can be reused to test single module block conversion and synthesis (that's what I do). A wrapper is often needed, even when LoS is not used, if you want to limit the IO (Quartus has nice tools with the virtualio, ISE non, nor to I believe Vivado).

conversion_wrapper(clock, reset, sdi, sdo, module_to_wrap)
josyb commented 8 years ago

I hoped that you understood the wink as of course :) I never have inouts :) E.g. for an I2C connection I use:

        SCl         : out std_logic;
        SDaOut      : out std_logic;
        SDaIn       : in  std_logic

You are quite right about the wrapper when lots of signals are involved. Must think about that again.

josyb commented 8 years ago

Rereading previous comment: At least not in MyHDL, of course my top-level VHDL files (can) have bidirectional signals. Until now I haven't used a MyHDL block as the top-level file for a project. So I'd correct the previous comment into: For open-drain signals:

def somefunc( Clk, Reset,  ..., SCl, SDaIn, SDaOut)

And for tri-state:

def someotherfunc( Clk, Reset, ..., BusIn, BusOut, EnableBusOut)

can be reused to test single module block conversion

You don't to seem quite happy with block, neither am I, but isn't life too short ...

cfelton commented 8 years ago

can be reused to test single module block conversion

You don't to seem quite happy with block, neither am I, but isn't life too short ...

@josyb it is not intended to be cynical but I use it as a transition - until it is more common. The more places folks see the module-block relation the more ingrained it will become.

josyb commented 8 years ago

Excuse me.But I would simply refer to it as block tout-court.

etch32 commented 5 years ago

I wasn't sure if I should create a new issue or comment on this one. I've seen in a few places (here) & #181 that "Conversion of List of Signals for a top-level block is not supported."

I've tried to work around this by creating a top() wrapper which doesn't have a list of signals but conversion still fails (presumably since I have a list of signals in a lower level of hierarchy & I mis-understand what top-level block means).

Since it doesn't appear that list of signal conversion will be supported anytime soon (I've seen conversations on this dating back over a decade: https://sourceforge.net/p/myhdl/mailman/message/18646379/ ), what is the best way to handle this situation? I'm really enjoying writing in myhdl but will have this situation often as my designs are hierarchical where I often need to assign to individual bits of a bus.

Here's a simple example: I define a "dff" then want to create a bank of them in a block named "registers". Below is the syntax that I had to use to get it to simulate correctly but am unsure how I can convert this to verilog. Calling either convert_register() or convert_top() results in the same error:

myhdl.ConversionError: in file issue_161.py, line 37:    # line 37 is the ConcatSignal line
    Not supported: extra positional arguments

A working example of how to handle this situation (for verilog conversion) would be greatly appreciated. Below is my simplified code.

@block
def dff(clk, d, q):

    @always(clk.posedge)
    def logic():
        q.next = d

    return logic

@block
def registers(clk, d, q):
    """ bank of 8 dff's """
    insts = []

    # need to create a temporary storage for q's b/c shadow sig is read-only
    qs = [Signal(bool(0)) for _ in range(len(q))]

    for i in range(len(d)):

        inst = dff(clk, d(i), qs[i])
        inst.name = 'dff%d' % i
        insts.append(inst)

    # Concatenate the qs bits and send them out on the q output.
    @always_comb
    def make_q():
        q.next = ConcatSignal(*reversed(qs))

    return insts, make_q

@block
def top():
    """ Dummy level of hierarchy to see if it will convert """
    n_bits = 8
    clk = Signal(bool(0))
    d = Signal(intbv(0)[n_bits:])
    q = Signal(intbv(0)[n_bits:])
    inst = registers(clk, d, q)
    return inst

def convert_register():
    n_bits = 8
    clk = Signal(bool(0))
    d = Signal(intbv(0)[n_bits:])
    q = Signal(intbv(0)[n_bits:])

    toVerilog(registers(clk, d, q))

def convert_top():
    toVerilog(top())