2

I'm trying to learn MyHDL and for that I was trying to create a very simple artificial neuron that could later be used in a simple artificial neural network. First I designed an artificial neuron that could only handle a single input and weight signal and returned an output signal.

@block
def input_perceptron(clk, max_res, in_signal, w_signal, out_signal):
    '''a hidden layer neuron
    out_signal is the result of: transfer_function( input_i x weight_i )
    '''
    @always(clk.posedge)
    def logic():
        # Multiply inputs and scale down with the shift
        weighted_signal = (in_signal * w_signal) >> max_res+1
        # Perform desired transfer function
        tf_result = rect_transfer_func( weighted_signal, max_res )
        # Assign new result
        out_signal.next = tf_result
    # The perceptron block must return this - MyHDL syntax
    return instances()

To convert it to Verilog I used another function:

def converter(max_res=16, hdl='Verilog'):
    # Initialize Signals
    max_val    = 1 << max_res + 1
    clk        = Signal( bool(0) )
    in_signal  = Signal( intbv( randrange(max_val), min=0, max=max_val ) )
    w_signal   = Signal( intbv( randrange(max_val), min=0, max=max_val ) )
    out_signal = Signal( intbv( 0,                  min=0, max=max_val ) )
    # Instantiate component
    perceptron_inst = input_perceptron(clk, max_res, in_signal, w_signal, out_signal)
    # Convert component to desired HDL language
    perceptron_inst.convert(hdl=hdl)

Everything was going great so far. Not only were the simulations showing me what I expected, but also the converted code was correct and I was able to synthesize it in Vivado. However, in a neural network, the hidden and output layer neurons must be able to handle information coming from multiple sources, a.k.a a list or bus of signals, and this is where I'm having trouble.

By using MyHDL's and Python's amazing functions for running simulations I was able to correctly simulate a simple perceptron with the following code by using lists of signals for the inputs and weights:

@block
def perceptron(clk, max_val, max_res, in_bus, w_bus, out_signal):
    ''' Perceptron
    out_signal is the result of: transfer_function( sum( input_i x weight_i ) )
    '''
    @always(clk.posedge)
    def logic():
        # Multiply inputs and scale down
        sum_weighted_inputs = 0
        for i in range(len(in_bus)):
            weighted_input = in_bus[i] * w_bus[i] >> max_res+1
            sum_weighted_inputs += weighted_input
        # Perform desired transfer function
        tf = step_transfer_func(sum_weighted_inputs, max_res, max_val)
        # Assign new result to ouput port
        out_signal.next = tf
    return instances()

After going through the docs (docs) I understood that in MyHDL a block can't use a list of signals as a port, and that according to this chapter I should convert my list of signals into a ConcatSignal, which I did like so:

def converter(max_res, num_inputs, hdl='Verilog'):
    # Clock parameters
    clk        = Signal( bool(0) )
    # Signal parameters: inputs, weights and outputs
    max_val    = 1 << max_res + 1
    out_signal = Signal( intbv( 0, min=0, max=max_val )[max_res+1:] )
    in_list    = [Signal( intbv( randrange(max_val), min=0, max=max_val )[max_res+1:] ) for i in range(num_inputs)]
    w_list     = [Signal( intbv( randrange(max_val), min=0, max=max_val )[max_res+1:] ) for i in range(num_inputs)]
    # Converting to a bus in HDL
    input_bus  = ConcatSignal(*reversed(in_list))
    weight_bus = ConcatSignal(*reversed(w_list))
    # Instantiate component
    perceptron_inst = perceptron(clk, max_val, max_res, input_bus, weight_bus, out_signal)
    # Convert component to desired HDL language
    perceptron_inst.convert(hdl=hdl)

When I try to simulate the design everything works as expected, however when converting there is a small error: the in_bus and w_bus inputs to the perceptron are defined in Verilog as output ports instead of input ports. As a consequence of this 2 wires for those ports are also generated and they're assigned a value of None.

The interesting bit is that the rest of the Verilog code is correct and if I simply delete the generated wires and None assignment, and if I manually change output to input for the in_bus and w_bus in the Verilog file then the code is synthesizable. If I look at the elaborated design by Vivado after these changes the module seems to be exactly what I intended.

Although I could manually fix this problem in the Verilog files I feel this problem comes from a wrong use/lack of understanding of MyHDL on my part. I cannot understand why the use of a ConcatSignal suddenly turns the input ports into output ports.

Any idea on what I'm missing here?

Mira
  • 1,983
  • 1
  • 11
  • 10

1 Answers1

1

You cannot use list-of-signals as a top-level port, you will want to create a wrapper that has a single input, creates your list-of-signals, then it can be passed to the generic.

  • I see. So I would have to create a wrapper around the entire Neural Network that receives the clock and maximum resolution? Maybe the number of neurons per layer as well, but the actual list of signals for the inputs and weights, etc, must be created inside this wrapper. But then how would I interact with it? Should I create memories in this wrapper so that the inputs and weights would be read from a memory and it would write the resulting output vectors to another memory? I could instantiate memories in the wrapper similar to the manual. But how to interact with them with the converter func? – Mira Dec 04 '18 at 00:03