[myhdl-list] List Of Constants
Brought to you by:
jandecaluwe
From: Josy B. <jos...@gm...> - 2015-03-15 17:49:15
|
In VHDL we can pass constants in the generic section. I mimic that in MyHDL by passing constants as the first arguments in the function call. Until now I have always used singular constants to do this, but now had the case where a List Of Constants looked more appealing than writing each one apart. So I threw in a list an passed that to the function. The simulation works fine. But the conversion fails depending on the contents of the list: * plain ints, intbvs: raise an exception ```text myhdl.ConversionError: in file C:\qdesigns\MyHDL\Source\issues\ListOfConstants\ListOfConstants.py, line 32: Object type is not supported in this context: Coeff, <type 'list'> ``` * Signals,: the conversion finishes, but the converted code generates uninitialized signals (wires in Verilog) ```VHDL type t_array_components_1_Coeff is array (0 to 2 - 1) of unsigned(3 downto 0); signal components_1_Coeff : t_array_components_1_Coeff; type t_array_components_0_Coeff is array (0 to 2 - 1) of unsigned(3 downto 0); signal components_0_Coeff : t_array_components_0_Coeff; ``` The workaround was returning to spelling out every constant in the call. I distilled a (self-contained this time) example program showing my intent: ```python ''' Created on 14 Mar 2015 @author: Josy ''' from __future__ import print_function import os, random from myhdl import * def flattenlov(D, Q): ''' a simple utility to turn a list of vectors into a flattened vector ''' # in theory, ConcatSignal() would be usable too, # but that constructs a new Signal, so we would still need # an @always_comb to assign that newly created Signal to the output Signal LNBR_VECTORS = len(D) LWIDTH_D = len(D[0]) @always_comb def flattenlov(): for i in range(LNBR_VECTORS): Q.next[(i+1) * LWIDTH_D : i * LWIDTH_D ] = D[i] return flattenlov def simplefunc( Coeff, Clk, DA, DB, Q): ''' a simple operation, just for the effect ... ''' @always_seq( Clk.posedge, reset = None) def calc(): Q.next = Coeff[0] * DA + Coeff[1] * DB return calc def loc( NBR_VECTORS, Clk, DA, DB, Q ): """ List Of Constants DA, DB and Q are flattened Lists Of Vectors """ # derive a few constants LWIDTH_D = len(DA) / NBR_VECTORS LWIDTH_Q = len(Q) / NBR_VECTORS LWIDTH_C = LWIDTH_Q - LWIDTH_D # must split input data into list Of Vectors lova = [ DA((i+1) * LWIDTH_D, i * LWIDTH_D) for i in range(NBR_VECTORS)] lovb = [ DB((i+1) * LWIDTH_D, i * LWIDTH_D) for i in range(NBR_VECTORS)] # we use a List Of Vectors to collect the results too result = [ Signal( intbv(0)[LWIDTH_Q:]) for _ in range(NBR_VECTORS)] # for testing our 'options', we use a local variable for the coefficients if USE_COEFF_INT: # this simulates, but doesn't convert LCOEFF = COEFF elif USE_COEFF_INTBV: # this simulates as well, but doesn't convert either LCOEFF = [ [intbv(COEFF[i][0])[LWIDTH_C:], intbv(COEFF[i][1]) [LWIDTH_C:]] for i in range(NBR_VECTORS)] elif USE_COEFF_SIGNAL: # this simulates _and_ converts, but the conversion doesn't work ... LCOEFF = [ [Signal(intbv(COEFF[i][0])[LWIDTH_C:]), Signal(intbv(COEFF[i][1])[LWIDTH_C:])] for i in range(NBR_VECTORS)] # instantiate as many 'simple functions' as vectors in the input components = [] for i in range( NBR_VECTORS ): components.append( simplefunc(LCOEFF[i], Clk, lova[i], lovb[i], result[i])) # must flatten collected results into the output vector components.append( flattenlov( result, Q )) return components def tb_loc(): ''' testing ''' # helper routines to build/disassemble the flattened in- and out-put vectors def flatten( v , w): ''' flattens a list of integer values v: list w: width of element in bits assume that the values fit in the given bit-width 'w' ''' r = 0 # start with Most Significant Value (or at end of list) for i in range(len(v)-1,-1,-1): if v[i] >= 0: t = v[i] else: # negative so use 2's complement number t = (2**w + v[i]) # shift previous result 'w' bits left and insert the new set r = (r << w) + t return r def cut( v, w, n, signed = False): ''' cut a flattened value up into a list of integers v: value w: width of element in bits n: number of elements signed: if True: interpret the 'cut w' bits as a 2's complement representation ''' r = [ 0 for _ in range(n)] for i in range(n): # mask out the 'w' lowest bits t = v & (2**w - 1) if signed: # test highest bit if t & 2**(w-1): #negative, convert 2's complement number r[i] = t - 2**w else: r[i] = t else: r[i] = t # shift input value right by 'w' bits v >>= w return r # construct the test data if not USE_RANDOM: # use a regular pattern? tda = [[j + i for i in range(NBR_VECTORS)] for j in range(NBR_OPS)] tdb = [[j + i for i in range(NBR_VECTORS-1,-1,-1)] for j in range(NBR_OPS-1,-1,-1)] else: # perhaps random is better ... random.seed('This gives us repeatable randomness') tda = [[random.randrange(2**WIDTH_D) for _ in range(NBR_VECTORS)] for __ in range(NBR_OPS)] tdb = [[random.randrange(2**WIDTH_D) for _ in range(NBR_VECTORS- 1,-1,-1)] for __ in range(NBR_OPS-1,-1,-1)] print( 'tda: {}'.format( tda)) print( 'tdb: {}'.format( tdb)) # calculate the expected result print ('expected result:', end = " ") r = [ [0 for i in range(NBR_VECTORS)] for j in range(NBR_OPS)] for j in range(NBR_OPS): for i in range(NBR_VECTORS): r[j][i] = (tda[j][i] * COEFF[i][0] + tdb[j][i] * COEFF[i] [1]) print( r ) print('received Q: ', end = ' ') rq =[None for _ in range(NBR_OPS)] dut = loc( NBR_VECTORS, Clk, DA, DB, Q ) tCK = 10 @instance def clkgen(): while True: Clk.next = 1 yield delay( int( tCK / 2 )) Clk.next = 0 yield delay( int( tCK / 2 )) @instance def stimulus(): DA.next = 0 DB.next = 0 yield Clk.posedge yield delay(int( tCK / 4 )) for j in range(NBR_OPS): DA.next = flatten( tda[j], WIDTH_D) DB.next = flatten( tdb[j], WIDTH_D) yield Clk.posedge yield delay(0) #check the result rq[j] = cut(int(Q), WIDTH_Q, NBR_VECTORS) # print( cut(int(Q), WIDTH_Q, NBR_VECTORS), end = ' ' ) # this doesn't work? for i in range(NBR_VECTORS): if rq[j][i] != r[j][i]: print( 'Failure: j {}, i {} -> received {} != expected {}'.format( j, i, rq[j][i], r[j][i])) yield delay(int( tCK / 4 )) print( rq ) yield Clk.posedge raise StopSimulation return dut, clkgen, stimulus def convert(): toVHDL(loc, NBR_VECTORS, Clk, DA, DB, Q ) toVerilog(loc, NBR_VECTORS, Clk, DA, DB, Q ) if __name__ == '__main__': def simulate(timesteps, mainclass): """Runs simulation for MyHDL Class""" # Remove old .vcd file, otherwise we get a list of renamed .vcd files lingering about filename = (mainclass.__name__ +".vcd") if os.access(filename, os.F_OK): os.unlink(filename) # Run Simulation tb = traceSignals(mainclass) sim = Simulation(tb) sim.run(timesteps) NBR_OPS = 8 NBR_VECTORS = 2 USE_RANDOM = True COEFF = [ [i+1,i+2] for i in range(NBR_VECTORS)] # select how to describe the coefficients USE_COEFF_INT , USE_COEFF_INTBV , USE_COEFF_SIGNAL = [ False, False, True] WIDTH_D = 8 WIDTH_C = 4 # >= log2(NBR_VECTORS+2), but keep to multiples of 4, this makes viewing hex-values in the waveform a lot easier WIDTH_Q = WIDTH_D + WIDTH_C Clk = Signal(bool(0)) # flattened vectors as input DA = Signal( intbv()[WIDTH_D * NBR_VECTORS :]) DB = Signal( intbv()[WIDTH_D * NBR_VECTORS :]) # and as output Q = Signal( intbv()[WIDTH_Q * NBR_VECTORS :]) simulate( 3000 , tb_loc) convert() ``` I understand that using _ints_ or _intbvs_ doesn't work, as it is not (yet) supported. But in the case of the _Signals_ am I wrong to expect _initialised_ signals in the converted code? |