Verilog width expansion and reduction operator equivalence?


#1

I’m using MyHDL on some of my small projects, and I want to implement some simple functions using width expansion and reduced unary operator, like following snippets as in verilog:

// svalid: 1-bit; en, dready: 4-bit
// width expansion
assign dvalid = {NUM_W{svalid}} & en;

//  reduced or
assign sready = | (dready & en)

But i dont have a clue how to code them using MyHDL.
Could somebody give me a hint on that ?

Regards!


#2

There will a few different opinions on your question.
IMHO you are swapping the cart and the horse. Obviously you know Verilog quite well (I don’t …) and you are asking whether there is an equivalent way to write this in MyHDL. The idea behind MyHDL is that we write standard Python to simulate (first) and convert (second) to an intermediate representation, at the time VHDL or Verilog.
The question becomes: do we have a Python construct that later in conversion can come out in the desired format(s)?
The way I would (currently do) write this:

from myhdl import Signal, intbv, block, always_comb


@block
def vwe(svalid, en, dready, sready, dvalid):

    @always_comb
    def assign():
        for i in range(len(en)):
            dvalid.next[i] = svalid and en[i]

        sready.next = 1 if (dready & en) else 0

    return assign


if __name__ == '__main__':

    def convert():
        en, dready, dvalid = [Signal(intbv(0)[4:]) for _ in range(3)]
        sready, svalid = [Signal(bool(0)) for _ in range(2)]

        dfc = vwe(svalid, en, dready, sready, dvalid)
        dfc.convert(hdl='Verilog')
        dfc.convert(hdl='VHDL')

    convert()

produces this Verilog:

// File: vwe.v
// Generated by MyHDL 0.10
// Date: Sat Oct  6 11:50:18 2018


`timescale 1ns/10ps

module vwe (
	svalid,
	en,
	dready,
	sready,
	dvalid
);

	input svalid;
	input [3:0] en;
	input [3:0] dready;
	output sready;
	reg sready;
	output [3:0] dvalid;
	reg [3:0] dvalid;


	always @(en, svalid, dready) begin: VWE_ASSIGN
		integer i;
		for (i=0; i<4; i=i+1) begin
			dvalid[i] = (svalid && en[i]);
		end
		sready = (dready & en) ? 1 : 0;
	end

endmodule

Which will work fine, but alas doesn’t have the conciseness of the Verilog code you presented.

Ideally you want to write something like:

@block
def vwe(svalid, en, dready, sready, dvalid):

    @always_comb
    def assign():
        dvalid.next[i] = bitextend(svalid, NUM_W) and en[i]
        sready.next = 1 if (dready & en) else 0

    return assign

Assuming we have the function bitextend defined (as a built-in?) to do what it needs to do, this code will simulate fine. It does convert fine too, but instead of the targeted Verilog construct it will include a function.

function [4-1:0] MYHDL2_bitextend;
    input [1-1:0] b;
    input w;
    integer w;
    reg [4-1:0] r;
    integer i;
begin: MYHDL5_RETURN
    r = 4'h0;
    for (i=0; i<w; i=i+1) begin
        r[i] = b;
    end
    MYHDL2_bitextend = r;
    disable MYHDL5_RETURN;
end
endfunction



assign dvalid = (MYHDL2_bitextend(svalid, 4) & en);
assign sready = (dready & en) ? 1 : 0;

endmodule

Now we could make the converter(s?) smarter and if we define a number of functions as built-in we could have the converter recognise them and implement the appropriate construct.
But I am sure that there are other priorities to extend MyHDL.
@cfelton, @hgomersall, and others … Your ideas?


#3

@josyb captured the gist. Furthermore, since the reduction is only combinatorial logic you can write a function for this operation: reduce_and(bitvector: intbv), reduce_or(bitvector: intbv). If you do this the Python gods will be quite pleased as your code readability has improved immensely :smile:.

This is the scenario us VHDL’ers (although it has been years since I have done serious VHDL) have always been in. In my opinion the functions gives you more options, example you can add a warning if you are reducing too many bits.