Thread: [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? |
From: Henry G. <he...@ca...> - 2015-03-15 18:38:29
|
Hi Josy, Would you mind creating a gist ( https://gist.github.com/ ) of this? I'm struggling to extract the lines from the email (it gets munged at various points) Forgive me if I've missed something, are you trying to do something more involved that a LUT? I'm using lists of bools and ints with an index referencing into them, which convert just fine, though it might not be quite your problem. Cheers, Henry On 15/03/15 17:48, Josy Boelen wrote: > 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? > > > ------------------------------------------------------------------------------ > Dive into the World of Parallel Programming The Go Parallel Website, sponsored > by Intel and developed in partnership with Slashdot Media, is your hub for all > things parallel software development, from weekly thought leadership blogs to > news, videos, case studies, tutorials and more. Take a look and join the > conversation now. http://goparallel.sourceforge.net/ > _______________________________________________ > myhdl-list mailing list > myh...@li... > https://lists.sourceforge.net/lists/listinfo/myhdl-list |
From: Josy B. <jos...@gm...> - 2015-03-15 19:12:30
|
Henry Gomersall <heng <at> cantab.net> writes: > > Hi Josy, > > Would you mind creating a gist ( https://gist.github.com/ ) of this? I'm > struggling to extract the lines from the email (it gets munged at > various points) > > Forgive me if I've missed something, are you trying to do something more > involved that a LUT? I'm using lists of bools and ints with an index > referencing into them, which convert just fine, though it might not be > quite your problem. > > Cheers, > > Henry > Henry, thanks for the attention, I created a gist: https://gist.github.com/josyb/af6b7809d976b2559ae4 The _example_ is distilled from a RGB to YCrCb converter, but the method will also be useful in convolution filters (edge detectors etc.) and other matrix-like operations. Regards, Josy |
From: Henry G. <he...@ca...> - 2015-03-15 19:33:56
|
On 15/03/15 19:12, Josy Boelen wrote: > Henry Gomersall <heng <at> cantab.net> writes: > >> > >> >Hi Josy, >> > >> >Would you mind creating a gist (https://gist.github.com/ ) of this? > I'm >> >struggling to extract the lines from the email (it gets munged at >> >various points) >> > >> >Forgive me if I've missed something, are you trying to do something > more >> >involved that a LUT? I'm using lists of bools and ints with an index >> >referencing into them, which convert just fine, though it might not be >> >quite your problem. >> > >> >Cheers, >> > >> >Henry >> > > Henry, > > thanks for the attention, > I created a gist:https://gist.github.com/josyb/af6b7809d976b2559ae4 > > The_example_ is distilled from a RGB to YCrCb converter, but the method > will also be useful in convolution filters (edge detectors etc.) and > other matrix-like operations. I Josy, my apologies but I'm running short of time this evening, but at first glance is it not somewhat similar to this: https://github.com/hgomersall/Veriutils/blob/master/veriutils/hdl_blocks.py#L264 Cheers, Henry |
From: Christopher F. <chr...@gm...> - 2015-03-17 04:18:13
|
] > > thanks for the attention, > I created a gist: https://gist.github.com/josyb/af6b7809d976b2559ae4 > > The _example_ is distilled from a RGB to YCrCb converter, but the method > will also be useful in convolution filters (edge detectors etc.) and > other matrix-like operations. > I looked at your code a little closer, first you want to create a `tuple` of ints or `tuple` of `intbv`. Because we are using a ROM and you want to access multiple index at a time you need to access first: def simplefunc( Coeff, Clk, DA, DB, Q): ''' a simple operation, just for the effect ... ''' Coeff = tuple(Coeff) @always_seq( Clk.posedge, reset = None) def calc(): c0 = Coeff[0] c1 = Coeff[1] Q.next = c0 * DA + c1 * DB The converter doesn't detect the constant/literal index value so it doesn't replace the const with the explicit value (this could possibly be a MEP). To use the ROM structure you need to capture with a variable first, the generated code will be kinda odd (optimized away) because it is generate for a variable index instead of a const. You could use a list-of-signals with initial values but initial values support has not been enabled because support was inconsistent between various synthesis tools: http://dev.myhdl.org/ideas/initial-values.html At one point someone had a patch that enabled initial value support for list-of-signals (i.e. RAM) but we never finished verifying support across the major synthesis tools. Hope that helps, Chris |
From: Josy B. <jos...@gm...> - 2015-03-17 09:15:09
|
> I looked at your code a little closer, first > you want to create a `tuple` of ints or `tuple` > of `intbv`. Because we are using a ROM and you > want to access multiple index at a time you need > to access first: > > def simplefunc( Coeff, Clk, DA, DB, Q): > ''' a simple operation, just for the effect ... ''' > > Coeff = tuple(Coeff) > <at> always_seq( Clk.posedge, reset = None) > def calc(): > c0 = Coeff[0] > c1 = Coeff[1] > Q.next = c0 * DA + c1 * DB > > The converter doesn't detect the constant/literal > index value so it doesn't replace the const with > the explicit value (this could possibly be a MEP). > > To use the ROM structure you need to capture with > a variable first, the generated code will be kinda > odd (optimized away) because it is generate for a > variable index instead of a const. > > You could use a list-of-signals with initial values > but initial values support has not been enabled because > support was inconsistent between various synthesis tools: > http://dev.myhdl.org/ideas/initial-values.html > > At one point someone had a patch that enabled initial > value support for list-of-signals (i.e. RAM) but we > never finished verifying support across the major > synthesis tools. > <snip> Chris, I tried that code, and although it functions, the VHDL code doesn't get any prettier. Also the method stays cumbersome for large(r) coefficient arrays. I can live with my workaround (spelling out every coefficient in the call) and put my hopes on future improvements on the conversion process. You indirectly answer my question: initial values are not supported - so my workaround of embedding the constant in a Signal can't work. Close but no cigar :) Regards, Josy |
From: Christopher F. <chr...@gm...> - 2015-03-17 18:26:11
|
<snip> > > I tried that code, and although it functions, the VHDL code doesn't get any > prettier. I am typically not too concerned with the "prettiness" of the converted code. I think reasonable steps should be taken to make it readable but not stand in the way. With that said I created and issue (enhancement request) for this particular item: https://github.com/jandecaluwe/myhdl/issues/34 Regards, Chris |
From: Josy B. <jos...@gm...> - 2015-03-17 18:51:59
|
Christopher Felton <chris.felton <at> gmail.com> writes: > > <snip> > > > > I tried that code, and although it functions, the VHDL code doesn't get any > > prettier. > > I am typically not too concerned with the "prettiness" of > the converted code. I think reasonable steps should be > taken to make it readable but not stand in the way. > > With that said I created and issue (enhancement request) > for this particular item: > https://github.com/jandecaluwe/myhdl/issues/34 > ><snip> Chris, To me the prettiness of the VHDL or Verilog code is an indication of how 'good' the MyHDL code was drafted/converted. From the workarounds the one with the spelled out constants looks best in both representations (MyHDL and VHDL) where the others bear the signs of being workarounds. But I welcome your MEP-lite. Just wish you hadn't used the word *rom*, I feel that name is a bit ambiguous to describe what is going on there. Regards, Josy |
From: Christopher F. <chr...@gm...> - 2015-03-17 19:05:57
|
> But I welcome your MEP-lite. Just wish you hadn't used the word *rom*, I > feel that name is a bit ambiguous to describe what is going on there. > It can be change and probably should. I agree, in this case we want a constant and not a read-only look-up. I will change it. Regards, Chris |
From: Josy B. <jos...@gm...> - 2015-03-18 07:36:47
|
Christopher Felton <chris.felton <at> gmail.com> writes: > > > > But I welcome your MEP-lite. Just wish you hadn't used the word *rom*, I > > feel that name is a bit ambiguous to describe what is going on there. > > > > It can be change and probably should. I agree, in this case > we want a constant and not a read-only look-up. I will change > it. > Chris, while you're at it, can you put the 'constants' in upper-case? A PEP8 recommendation ;) Regards, Josy |
From: Christopher F. <chr...@gm...> - 2015-03-18 13:07:20
|
On 3/18/15 2:36 AM, Josy Boelen wrote: > Christopher Felton <chris.felton <at> gmail.com> writes: > >> >> >>> But I welcome your MEP-lite. Just wish you hadn't used the word *rom*, > I >>> feel that name is a bit ambiguous to describe what is going on there. >>> >> >> It can be change and probably should. I agree, in this case >> we want a constant and not a read-only look-up. I will change >> it. >> > Chris, > > while you're at it, can you put the 'constants' in upper-case? A PEP8 > recommendation ;) > I disagree, we have a reference to a `tuple` not a module level constant. # module level constant from myhdl import * MAX_LEVELS = 4 # reference to a tuple coef = (1,2,3,4) Module-level constants are fairly rare. The python documentation [1] does not use cap names for references to tuples (can't say I have seen other that does) Regards, Chris [1] https://docs.python.org/2/tutorial/datastructures.html#tuples-and-sequences |
From: Josy B. <jos...@gm...> - 2015-03-18 16:23:05
|
> > while you're at it, can you put the 'constants' in upper-case? A PEP8 > > recommendation ;) > > > > I disagree, we have a reference to a `tuple` not a > module level constant. > > # module level constant > from myhdl import * > MAX_LEVELS = 4 > > # reference to a tuple > coef = (1,2,3,4) > > Module-level constants are fairly rare. The python > documentation [1] does not use cap names for references > to tuples (can't say I have seen other that does) > > Regards, > Chris > > [1] > https://docs.python.org/2/tutorial/datastructures.html#tuples-and- sequences Chris, I see your point. But I like to stress the fact that some arguments are *constant* (and I always use UPPER_CASE names for generic parameters in VHDL which is what I mimic in my MyHDL code). I'll let it digest. Regards, Josy |
From: Christopher F. <chr...@gm...> - 2015-03-18 13:19:53
|
On 3/17/15 1:51 PM, Josy Boelen wrote: > Christopher Felton <chris.felton <at> gmail.com> writes: > >> >> <snip> >>> >>> I tried that code, and although it functions, the VHDL code doesn't > get any >>> prettier. >> >> I am typically not too concerned with the "prettiness" of >> the converted code. I think reasonable steps should be >> taken to make it readable but not stand in the way. >> >> With that said I created and issue (enhancement request) >> for this particular item: >> https://github.com/jandecaluwe/myhdl/issues/34 >> >> <snip> > > Chris, > > To me the prettiness of the VHDL or Verilog code is an indication of how > 'good' the MyHDL code was drafted/converted. I strongly disagree, you are trading off readability and pythonics in the MyHDL code for "prettier" intermediate code. A similar example to yours is a straightfoward sum-of products x = [Signal(intbv(...) ...] b = tuple(map(int, [round(1/N*xmax) for ...])) @always_seq(clock.posedge, reset=reset) ssum = 0 for ii in range(N): c = b[ii] ssum = ssum + x[ii] * c y.next = ssum I wouldn't write the above any other way even though the converted code will have the case embedded in the process. Regards, Chris |
From: Josy B. <jos...@gm...> - 2015-03-18 16:36:23
|
> > To me the prettiness of the VHDL or Verilog code is an indication of how > > 'good' the MyHDL code was drafted/converted. > > I strongly disagree, you are trading off readability > and pythonics in the MyHDL code for "prettier" > intermediate code. > > A similar example to yours is a straightfoward > sum-of products > > x = [Signal(intbv(...) ...] > b = tuple(map(int, [round(1/N*xmax) for ...])) > > <at> always_seq(clock.posedge, reset=reset) > ssum = 0 > for ii in range(N): > c = b[ii] > ssum = ssum + x[ii] * c > y.next = ssum > > I wouldn't write the above any other way even though > the converted code will have the case embedded in the > process. > > <snip> You're quite right that for N being large the above approach is the only way. Imagine spelling out 64 constants all day long ... For the moment. When your (ours?) MEP_lite gets implemented, it will look like this x = [Signal(intbv(...) ...] b = tuple(map(int, [round(1/N*xmax) for ...])) def sop(N, x, b ): <at> always_seq(clock.posedge, reset=reset) def sop(): ssum = 0 for ii in range(N): ssum = ssum + x[ii] * b[ii] y.next = ssum return sop Which is definitely more pythonic (and the converted V* will be cleaner too) I refrained from making b upper-case :) Best regards, Josy |
From: Christopher F. <chr...@gm...> - 2015-03-18 16:39:50
|
On 3/18/15 11:36 AM, Josy Boelen wrote: > >>> To me the prettiness of the VHDL or Verilog code is an indication of > how >>> 'good' the MyHDL code was drafted/converted. >> >> I strongly disagree, you are trading off readability >> and pythonics in the MyHDL code for "prettier" >> intermediate code. >> >> A similar example to yours is a straightfoward >> sum-of products >> >> x = [Signal(intbv(...) ...] >> b = tuple(map(int, [round(1/N*xmax) for ...])) >> >> <at> always_seq(clock.posedge, reset=reset) >> ssum = 0 >> for ii in range(N): >> c = b[ii] >> ssum = ssum + x[ii] * c >> y.next = ssum >> >> I wouldn't write the above any other way even though >> the converted code will have the case embedded in the >> process. >> >> <snip> > > You're quite right that for N being large the above approach is the only > way. Imagine spelling out 64 constants all day long ... > For the moment. > When your (ours?) MEP_lite gets implemented, it will look like this > x = [Signal(intbv(...) ...] > b = tuple(map(int, [round(1/N*xmax) for ...])) > > def sop(N, x, b ): > <at> always_seq(clock.posedge, reset=reset) > def sop(): > ssum = 0 > for ii in range(N): > ssum = ssum + x[ii] * b[ii] > y.next = ssum > return sop > No, even with the enhancement request the above would not be converted because the index is not constant. For that to be convertible the loop would need to be unrolled. Regards, Chris |
From: Josy B. <jos...@gm...> - 2015-03-18 16:54:39
|
> > x = [Signal(intbv(...) ...] > > b = tuple(map(int, [round(1/N*xmax) for ...])) > > > > def sop(N, x, b ): > > <at> always_seq(clock.posedge, reset=reset) > > def sop(): > > ssum = 0 > > for ii in range(N): > > ssum = ssum + x[ii] * b[ii] > > y.next = ssum > > return sop > > > > No, even with the enhancement request the above would > not be converted because the index is not constant. > For that to be convertible the loop would need to be > unrolled. > <snip> Chris, did you trick me? :) I overlooked that fact. Aren't the two codes doing the same in the end? Or perhaps the second doesn't convert? In my code a loop would, almost always, be unrolled or rather be replaced by a, probably pipelined, tree-structure so the indexes would become constants. Best regards, Josy |
From: Christopher F. <chr...@gm...> - 2015-03-18 17:03:23
|
On 3/18/15 11:54 AM, Josy Boelen wrote: >>> x = [Signal(intbv(...) ...] >>> b = tuple(map(int, [round(1/N*xmax) for ...])) >>> >>> def sop(N, x, b ): >>> <at> always_seq(clock.posedge, reset=reset) >>> def sop(): >>> ssum = 0 >>> for ii in range(N): >>> ssum = ssum + x[ii] * b[ii] >>> y.next = ssum >>> return sop >>> >> >> No, even with the enhancement request the above would >> not be converted because the index is not constant. >> For that to be convertible the loop would need to be >> unrolled. >> <snip> > > Chris, > > did you trick me? :) Not intentionally. > I overlooked that fact. Aren't the two codes doing the same in the end? > Or perhaps the second doesn't convert? > In my code a loop would, almost always, be unrolled or rather be > replaced by a, probably pipelined, tree-structure so the indexes would > become constants. They are doing the same thing in the end from a code perspective but not from a hardware perspective. The only way to do the second is to unroll the loop with literals or with initial value support. You would need an array of signals in the converted code with an initial (constant) value. The initial value support is too inconsistent across tools (best of our knowledge). The safe method, is to set the initial values your self. def m_initial_values(coef, coef_init): assert isinstance(coef, list) assert isinstance(coef_init, tuple) N = len(coef) @always_comb def assign(): for ii in range(N): ival = coef_init[ii] coef.next = ival return assign Using a generic module like the above will let you use the second version of code directly and then you don't have to mess with the synthesis tools inconsistent support of initial values. Regards, Chris |
From: Christopher F. <chr...@gm...> - 2015-03-18 17:58:16
|
<snip> > > def m_initial_values(coef, coef_init): > assert isinstance(coef, list) > assert isinstance(coef_init, tuple) > N = len(coef) > @always_comb > def assign(): > for ii in range(N): > ival = coef_init[ii] > coef.next = ival > return assign > Of course it should be coef[ii].next = ival (should have written a test :) Regards, Chris |
From: Josy B. <jos...@gm...> - 2015-03-18 17:28:30
|
<snip> Thanks, Chris learned something today, again Regards, Josy |
From: Josy B. <jos...@gm...> - 2015-03-15 20:13:52
|
> > I Josy, my apologies but I'm running short of time this evening, but at > first glance is it not somewhat similar to this: > https://github.com/hgomersall/Veriutils/blob/master/veriutils/hdl_blocks.p y#L264 Henry, at first sight the operations look alike, but I believe the subtle difference between the _fixed_ index in my case and the _variable_ index in your case explains the behaviour: in your case MyHDL recognises this as a memory (RAM or ROM) access and generates the actual code. In my case it is an _ordinary_ list and is not supported. I tried to wrok around that by giving it a list Of Signals, which convert but with _no initialisation_ which makes all coefficients zero. I finally worked around the _problem_ by writing every coefficient as a single parameter in the function call. My ListOfConstants.py example remains for _exploration_ by the _volunteers_ Regards, Josy |
From: Christopher F. <chr...@gm...> - 2015-03-15 21:00:52
|
On 3/15/15 12:48 PM, Josy Boelen wrote: > 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: Correct, if the list of constants doesn't follow the ROM conversion examples [1] it will not convert. A work around is to use local references to the list-of-constants param1 = list_of_constants[0] param2 = list_of_constants[1] or to use an interface, object with constants instead of the list-of-constants. class Parameters: const1 = 9 const2 = 7 const3 = 'go' Hope that helps (didn't have a look at the example, commenting on the description), Chris [1] http://docs.myhdl.org/en/latest/manual/conversion_examples.html#rom-inference |
From: Josy B. <jos...@gm...> - 2015-03-15 22:18:44
|
> Correct, if the list of constants doesn't > follow the ROM conversion examples [1] it > will not convert. > > A work around is to use local references to > the list-of-constants > > param1 = list_of_constants[0] > param2 = list_of_constants[1] > > or to use an interface, object with constants > instead of the list-of-constants. > > class Parameters: > const1 = 9 > const2 = 7 > const3 = 'go' > > Hope that helps (didn't have a look at the > example, commenting on the description), > Chris > > [1] > http://docs.myhdl.org/en/latest/manual/conversion_examples.html#rom- inference > Chris, Any workaround is a good as any other workaround, but still is a workaround. I just added the 4 (in my actual module, not the example) constants in the call. None of the workarounds can be classified as _pythonic_, I'd say. What if I come up with a 8 * 8 constant matrix (like in JPEG DCT)? And it is well supported in VHDL ... I checked the _toVHDL code and process (I built a lot of tracing in my local copy), and it is not evident how to add support for this. But we'll discuss this another time? (It's bedtime over here ...) Best regards, Josy |
From: Christopher F. <chr...@gm...> - 2015-03-16 00:43:43
|
<snip> > > Any workaround is a good as any other workaround, but still is a > workaround. I just added the 4 (in my actual module, not the example) > constants in the call. Workaround was a poor choice or words in this case, it is an alternative method. Either way I don't think it was what you were looking for. > None of the workarounds can be classified as _pythonic_, I'd say. What > if I come up with a 8 * 8 constant matrix (like in JPEG DCT)? And it is > well supported in VHDL ... Either of those approaches, I would argue, are pythonic but they don't solve the problem you are trying to solve. A 1D list of constants should be convertible a 2D I believe is not. This (2D) is something that needs to be added. Regards, Chris |
From: Josy B. <jos...@gm...> - 2015-03-16 08:17:56
|
> <snip> > > > > Any workaround is a good as any other workaround, but still is a > > workaround. I just added the 4 (in my actual module, not the example) > > constants in the call. > > Workaround was a poor choice or words in this > case, it is an alternative method. Either > way I don't think it was what you were > looking for. > > > None of the workarounds can be classified as _pythonic_, I'd say. What > > if I come up with a 8 * 8 constant matrix (like in JPEG DCT)? And it is > > well supported in VHDL ... > > Either of those approaches, I would argue, are > pythonic but they don't solve the problem you > are trying to solve. > > A 1D list of constants should be convertible a > 2D I believe is not. This (2D) is something that > needs to be added. > <snip> Chris, Replace my wording 'not pythonic' by 'less beautiful'. And 'pythonic' by 'idiomatic Python' as coined by Jeff Knupp in his book (see http://www.jeffknupp.com) You may want to take a look at the example: https://gist.github.com/josyb/af6b7809d976b2559ae4 anyhow. The conversion passes if I send a list of Signals, but the VHDL signals are not initialised. My (final or main) question was whether I am wrong to expect to have those initialised in the VHDL code, as I have assigned in the Python code (and were used by the simulation). I can live with workaround but do believe in improving MyHDL. But I do understand that the current conversion process/code has its limits. Hopefully we can find a good candidate for the GSOC 'Refactor Conversion Code' project. To me the Python (annotated) AST to V* code in one step is perhaps better cut in two phases where we on a line-by-line basis introduce an intermediary representation which we finally translate into text output. The code in _toVHDL.py has seen a few flags introduced to steer the casting (I too did so to 'solve' issues #14 and #15). Maintenance just gets more and more difficult by each added flag? A side-question, how do we label an issue on Github as Enhancement or Question? I drew up this question in the issue page, but didn't post it as I realised it might be interpreted as a 'bug report' which certainly is not my intention. A 1D list of constants only converts in the 'special' case of inferring memory. As I explained to Henry (Gomersall) my code has a 'fixed' index and doesn't get recognised as such. The 2D array of constants was just an (unfortunate?) example to indicate how cumbersome and error-prone typing the 'alternatives' would become. Best regards, Josy |
From: Christopher F. <chr...@gm...> - 2015-03-17 04:25:14
|
<snip> > > A side-question, how do we label an issue on Github as Enhancement or > Question? I drew up this question in the issue page, but didn't post it > as I realised it might be interpreted as a 'bug report' which certainly > is not my intention. I think only the owner can, or only users given permission? At this point, I believe, only Jan can apply labels to issues. Regards, Chris |