from __future__ import absolute_import
from __future__ import print_function

import veriloggen.core.vtypes as vtypes
import veriloggen.core.module as module

def mkMultiplierCore(index, lwidth=32, rwidth=32, lsigned=True, rsigned=True, depth=6):
    retwidth = lwidth + rwidth
    
    m = module.Module('multiplier_core_%d' % index)
    
    clk = m.Input('CLK')
    update = m.Input('update')
    
    a = m.Input('a', lwidth)
    b = m.Input('b', rwidth)
    c = m.Output('c', retwidth)
    
    _a = m.Reg('_a', lwidth, signed=lsigned)
    _b = m.Reg('_b', rwidth, signed=rsigned)
    tmpval = [ m.Reg('_tmpval%d' % i, retwidth, signed=True) for i in range(depth-1) ]
    rslt = m.Wire('rslt', retwidth, signed=True)

    __a = _a
    __b = _b
    if not lsigned:
        __a = vtypes.SystemTask('signed', vtypes.Cat(vtypes.Int(0, width=1), _a))
    if not rsigned:
        __b = vtypes.SystemTask('signed', vtypes.Cat(vtypes.Int(0, width=1), _b))
    
    m.Assign( rslt(__a * __b) )
    m.Assign( c(tmpval[depth-2]) )
    
    m.Always(vtypes.Posedge(clk))(
        vtypes.If(update)(
            _a(a),
            _b(b),
            tmpval[0](rslt),
            [ tmpval[i](tmpval[i-1]) for i in range(1, depth-1) ]
        ))
    
    return m

def mkMultiplier(index, lwidth=32, rwidth=32, lsigned=True, rsigned=True, depth=6):
    if lwidth < 0: raise ValueError("data width must be greater than 0.")
    if rwidth < 0: raise ValueError("data width must be greater than 0.")
    if depth < 2: raise ValueError("depth must be greater than 2.")
    
    retwidth = lwidth + rwidth
    
    mult = mkMultiplierCore(index, lwidth, rwidth, lsigned, rsigned, depth)
    
    m = module.Module('multiplier_%d' % index)
    
    clk = m.Input('CLK')
    rst = m.Input('RST')
    
    update = m.Input('update')
    enable = m.Input('enable')
    valid = m.Output('valid')
    
    a = m.Input('a', lwidth)
    b = m.Input('b', rwidth)
    c = m.Output('c', retwidth)

    valid_reg = [ m.Reg('valid_reg%d' % i) for i in range(depth) ]

    m.Assign( valid(valid_reg[depth-1]) )

    m.Always(vtypes.Posedge(clk))(
        vtypes.If(rst)(
            [ valid_reg[i](0) for i in range(depth) ]
        ).Else(
            vtypes.If(update)(
                valid_reg[0](enable),
                [ valid_reg[i](valid_reg[i-1]) for i in range(1, depth) ]
            )
        ))
    
    ports = [ ('CLK', clk), ('update', update), ('a', a), ('b', b), ('c', c) ]
    m.Instance(mult, 'mult', ports=ports)
    
    return m

# global multiplier count
index_count = 0
def get_mul(lwidth=32, rwidth=32, lsigned=True, rsigned=True, depth=6):
    global index_count
    mul = mkMultiplier(index_count, lwidth, rwidth, lsigned, rsigned, depth)
    index_count += 1
    return mul

def reset():
    global index_count
    index_count = 0
