Thread: [myhdl-list] Restrictions for conversion
Brought to you by:
jandecaluwe
From: Oscar D. <osc...@gm...> - 2011-12-23 12:37:13
|
Hi I was doing some experiments with the conversion function, and I found a problem I'd like to discuss regarding access to members of data structures inside generators. Suppose you have a dict with some constants you need for the calculations: constants_list = {"myconst": 10} def trans_block(clk, din, dout, const_list): @always(clk.posedge) def proc(): dout.next = din + const_list["addconst"] return instances() conversion fails with: myhdl.ConversionError: in file simpletest.py, line 22: Object type is not supported in this context: const_list, <type 'dict'> Same occur if you have a list: myhdl.ConversionError: in file simpletest.py, line 22: Object type is not supported in this context: const_list, <type 'list'> Or, even if you use an object with members accessed as attributes, like this: dout.next = din + const_list.addconst conversion fails with: myhdl.ConversionError: in file simpletest.py, line 29: Unsupported attribute: addconst A workaround is to access data outside the generator, with another variable name: constants_list = {"myconst": 10} def trans_block(clk, din, dout, const_list): addconst = const_list["addconst"] @always(clk.posedge) def proc(): dout.next = din + addconst return instances() However, it's kind of annoying to do that when you have a lot of variables to access. Something similar occurs when you access signals. What are your thoughts? Is it good to allow access data members whatever way you want or is better to have a fixed coding style? By the way, I think these kind of details should be explained in the conversion documentation. Best regards -- Oscar Díaz Key Fingerprint = 904B 306C C3C2 7487 650B BFAC EDA2 B702 90E9 9964 gpg --keyserver subkeys.pgp.net --recv-keys 90E99964 I recommend using OpenDocument Format for daily use and exchange of documents. http://www.fsf.org/campaigns/opendocument |
From: Christopher F. <chr...@gm...> - 2011-12-23 13:56:04
|
You are correct, this currently is a limitation. Adding support for this is part on the MEP we recently created. Regard, Chris ** sent from mobile device ** On Dec 23, 2011 6:37 AM, "Oscar Diaz" <osc...@gm...> wrote: > Hi > > I was doing some experiments with the conversion function, and I found > a problem I'd like to discuss regarding access to members of data > structures inside generators. Suppose you have a dict with some > constants you need for the calculations: > > constants_list = {"myconst": 10} > def trans_block(clk, din, dout, const_list): > @always(clk.posedge) > def proc(): > dout.next = din + const_list["addconst"] > return instances() > > conversion fails with: > myhdl.ConversionError: in file simpletest.py, line 22: > Object type is not supported in this context: const_list, <type 'dict'> > > Same occur if you have a list: > myhdl.ConversionError: in file simpletest.py, line 22: > Object type is not supported in this context: const_list, <type 'list'> > > Or, even if you use an object with members accessed as attributes, like > this: > dout.next = din + const_list.addconst > conversion fails with: > myhdl.ConversionError: in file simpletest.py, line 29: > Unsupported attribute: addconst > > A workaround is to access data outside the generator, with another > variable name: > > constants_list = {"myconst": 10} > def trans_block(clk, din, dout, const_list): > addconst = const_list["addconst"] > @always(clk.posedge) > def proc(): > dout.next = din + addconst > return instances() > > However, it's kind of annoying to do that when you have a lot of > variables to access. Something similar occurs when you access signals. > > What are your thoughts? Is it good to allow access data members > whatever way you want or is better to have a fixed coding style? By > the way, I think these kind of details should be explained in the > conversion documentation. > > Best regards > > -- > Oscar Díaz > Key Fingerprint = 904B 306C C3C2 7487 650B BFAC EDA2 B702 90E9 9964 > gpg --keyserver subkeys.pgp.net --recv-keys 90E99964 > > I recommend using OpenDocument Format > for daily use and exchange of documents. > > http://www.fsf.org/campaigns/opendocument > > > ------------------------------------------------------------------------------ > Write once. Port to many. > Get the SDK and tools to simplify cross-platform app development. Create > new or port existing apps to sell to consumers worldwide. Explore the > Intel AppUpSM program developer opportunity. appdeveloper.intel.com/join > http://p.sf.net/sfu/intel-appdev > _______________________________________________ > myhdl-list mailing list > myh...@li... > https://lists.sourceforge.net/lists/listinfo/myhdl-list > |
From: Norbo <Nor...@gm...> - 2012-02-18 21:06:04
|
About: (More general support of indexed constants) http://www.myhdl.org/doku.php/dev:tasks ok this post maybe gets a bit long, but it may be helpful to further eliminate this conversion limitation: i first present a simple myhdl example which is victim to such an conversion limitation. then i show three slightly different ways how it can be implemented in vhdl (i dont no verilog). the myhdl example: ########################################################################## from myhdl import * def somedut(inData,out_data,data_index,LENGTH): TWIDDEL_WIDTH=10 real_Vals=[Signal(intbv(0,min=-2000,max=2000)) for i in range(LENGTH)] Table=tuple([i for i in range(LENGTH)]) @always_comb def comb_logic(): for i in range(LENGTH): real_Vals[i].next=inData*Table[i] ###this line cannot be converted to vhdl @always_comb def output_logic(): out_data.next=real_Vals[data_index] return comb_logic,output_logic #real_alu_insts,imag_alu_insts, comb_logic,seq_logic def TESTBENCH_XX(): LENGTH=100 clk=Signal(bool(0)) inData=Signal(intbv(0,min=-10,max=10)) out0=Signal(intbv(0,min=-2000,max=2000)) index=Signal(intbv(0,min=0,max=100)) toVHDL(somedut,inData,out0,index,LENGTH) dut_inst=somedut(inData,out0,index,LENGTH) @always(delay(10)) def clkgen(): clk.next = not clk @instance def stimulus(): index.next=0 inData.next=0 inData.next=1 for i in range(LENGTH): yield clk index.next=i inData.next=2 for i in range(LENGTH): yield clk index.next=i raise StopSimulation @instance def Monitor(): #print "\t\tPortC:",PORTC_OUT,"Binary:" ,bin(PORTC_OUT,WORD_SZ) while 1: yield out0 print "out0=",out0 return dut_inst,Monitor,stimulus,clkgen sim = Simulation(TESTBENCH_XX()) sim.run() ########################################################################## The Error is: myhdl.ConversionError: in file general_rom.py, line 13: Object type is not supported in this context: Table, <type 'tuple'> ########################################################################## Possible (by altera quartus synthesis able) ways it could be implemented in vhdl: The one with the function is proposed on http://www.myhdl.org/doku.php/dev:tasks (More general support of indexed constants) ############################################################################## -- File: TESTBENCH.vhd -- Generated by MyHDL 0.7 -- Date: Sat Feb 18 19:09:25 2012 library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; use std.textio.all; use work.pck_myhdl_07.all; entity TESTBENCH is port ( clk : in std_logic; inData: in signed (4 downto 0); out_data: out signed (7 downto 0); data_index: in signed (7 downto 0) ); end entity TESTBENCH; architecture MyHDL of TESTBENCH is type t_array_dut_inst_real_Vals is array(0 to 16) of signed (15 downto 0); signal dut_inst_real_Vals: t_array_dut_inst_real_Vals; type t_array_dut_test is array(0 to 16) of signed (4 downto 0); ---- 1. solution (solution 1 and 2 is basically the same but in sulotion 1 the content of the array is built ---- programtically) function init_mem return t_array_dut_test is variable temp_mem : t_array_dut_test; begin for i in t_array_dut_test'range loop temp_mem(i) := to_signed(i, 5); end loop; return temp_mem; end; signal dut_inst_test: t_array_dut_test:=init_mem; --or signal dut_inst_test: t_array_dut_test:=init_mem; ---- 2. solution, directly initialize the array signal dut_inst_test2: t_array_dut_test:=(0=> "00000", 1=> "00001", 2=> "00010", 3=> "00011", 4=> "00100", 5=> "00101", 6=> "00110", 7=> "00111", 8=> "01000", 9=> "01001", 10=> "01010", 11=> "01011", 12=> "01100", 13=> "01101", 14=> "01110", 15=> "01111", others => "10000"); ---- 3. solution same as Rom in myhdl, but from a function function ValueSelect(dut_inst_RomAddr: in integer) return signed is variable dut_inst_RomData : signed (4 downto 0); begin case dut_inst_RomAddr is when 0 => dut_inst_RomData := "00000"; --attention : instead of <= when 1 => dut_inst_RomData := "00001"; when 2 => dut_inst_RomData := "00010"; when 3 => dut_inst_RomData := "00011"; when 4 => dut_inst_RomData := "00100"; when 5 => dut_inst_RomData := "00101"; when 6 => dut_inst_RomData := "00110"; when 7 => dut_inst_RomData := "00111"; when 8 => dut_inst_RomData := "01000"; when 9 => dut_inst_RomData := "01001"; when 10 => dut_inst_RomData := "01010"; when 11 => dut_inst_RomData := "01011"; when 12 => dut_inst_RomData := "01100"; when 13 => dut_inst_RomData := "01101"; when 14 => dut_inst_RomData := "01110"; when 15 => dut_inst_RomData := "01111"; when others => dut_inst_RomData := "10000"; end case; return dut_inst_RomData; end; begin -- combinatorial TESTBENCH_DUT_INST_COMB_LOGIC: process ( inData,dut_inst_test) is begin for i in 0 to 16 loop dut_inst_real_Vals(i) <= (resize(inData, 11) * dut_inst_test(i)); --dut_inst_test2(i)); or ValueSelect(i)); end loop; end process TESTBENCH_DUT_INST_COMB_LOGIC; -- clocked --TESTBENCH_DUT_INST_COMB_LOGIC: process ( clk) is --begin --if rising_edge(clk) then -- for i in 0 to 16 loop -- dut_inst_real_Vals(i) <= (resize(inData, 11) * ValueSelect(i)); --dut_inst_test2(i)); or ValueSelect(i)); -- end loop; -- end if; --end process TESTBENCH_DUT_INST_COMB_LOGIC; out_data <= resize(dut_inst_real_Vals(to_integer(data_index)), 8); end architecture MyHDL; ############################################################################## greetings Norbo |
From: Christopher F. <chr...@gm...> - 2012-02-20 12:03:48
|
On 2/18/2012 3:05 PM, Norbo wrote: > About: (More general support of indexed constants) > http://www.myhdl.org/doku.php/dev:tasks > ok this post maybe gets a bit long, but it may be helpful to further > eliminate this conversion limitation: > > i first present a simple myhdl example which is victim to such an > conversion limitation. > then i show three slightly different ways how it can be implemented in > vhdl (i dont no verilog). > > the myhdl example: > > ########################################################################## > from myhdl import * > > def somedut(inData,out_data,data_index,LENGTH): > TWIDDEL_WIDTH=10 > real_Vals=[Signal(intbv(0,min=-2000,max=2000)) for i in range(LENGTH)] > Table=tuple([i for i in range(LENGTH)]) > > @always_comb > def comb_logic(): > for i in range(LENGTH): > real_Vals[i].next=inData*Table[i] ###this line cannot be > converted to vhdl > @always_comb > def output_logic(): > out_data.next=real_Vals[data_index] > > return comb_logic,output_logic #real_alu_insts,imag_alu_insts, > comb_logic,seq_logic > Norbo, For this particular example, the description can be changed so that it does convert. I do not understand why the realVals array (list of signals) is used since only one element is used at a time. def SomeDut(inData, outData, dataIndex, LENGTH=4): Table = tuple([ii for ii in range(LENGTH)]) tableVal = Signal(intbv(0, min=-2000, max=2000)) @always_comb def hdl_get_table_value(): tableVal.next = Table[int(dataIndex)] @always_comb def hld_output(): outData.next = inData*tableVal return hld_output, hdl_get_table_value I believe the limitation you are trying to demonstrate is when the tuple (ROM) access is mixed in other statements, instead of being its own statement, i.e. rather than a separate generator to describe the ROM it is embedded in the statements directly, e.g. @always_comb def hld_output(): outData.next = inData*Table[int(dataIndex)] For this second case to be convertible the converting code needs to do some extra work which it does not and it is not clear if it should. The conversion code will need to do the same conversion as the previous (above) example, hence it will have to infer more. This has been debated in the past (level of inference) and there are different opinions on the level of inference that should be supported. Regards, Chris |
From: Norbo <Nor...@gm...> - 2012-02-21 20:12:58
|
> I do not understand why the realVals array (list ofsignals) is used > since only one element is used at a time. yeah it deosnt realy makes sense if only one element used at a time, except for the case where you probably want to have read access to a constant mixed into another expression and then at some time later overide this data with some other value. (which would actually be like a ram access where the ram content is initialized, i have seen a post where jan wrote that he already had this code but he removed it because not every synthesis tool supported it at that time, or is there a limitation with verilog, i think vhdl should do fine now) > I believe the limitation you are trying to demonstrate is when the tuple > (ROM) access is mixed in other statements, instead of being its own > statement, i.e. rather than a separate generator to describe the ROM it > is embedded in the statements directly, e.g. > @always_comb > def hld_output(): > outData.next = inData*Table[int(dataIndex)] > For this second case to be convertible the converting code needs to do > some extra work which it does not and it is not clear if it should. The > conversion code will need to do the same conversion as the previous > (above) example, hence it will have to infer more. This has beendebated > in the past (level of inference) and there are differentopinions on the > level of inference that should be supported. Another way to do this in myhdl to avoid an "deeper level of inference" would be to use instead of the Table[] which is a tuple of ints a explicit declared signal array where the initial values of the signal array are set to the valus of the tuple, like this: signal_array=[Signal(intbv(Table[i],min=min(Table),max=max(Table)+1)) for i in range(len(Table))] an then use the signal_array in every statement where the access is mixed with other statements, e.g. @always_comb def hld_output(): outData.next = inData*signal_array[int(dataIndex)] + signal_array[int(dataIndex+1)] #.....etc This is actually working fine in myhdl simulation. The conversion to vhdl and verilog succeeds also without error or warning. but the signal_array in the vhdl code doesnt gets initialized with the Table values and outData will never get the corresponding data. So the actual workaround i am currently using is adding this code, but i think this could be a obstacle for some synthesis tools and for me it doesn't seams very neat, since it could be simply made obsolete. @always_comb def comb_logic(): for i in range(len(Table)): signal_array[int(i)].next=Table[int(i)] Ok here is the full code where i am using this workaround: (a ultra generic fir filter) ################################################################################################################ from myhdl import * def Combinatorical_adder(out,in1,in2): @always_comb def cc_logic(): out.next=in1+in2 return cc_logic ## NR_PARALLELS must be a natural divider of len(COEFFS) def Generic_FIR_filter(clk,rst,inData,inData_enable,outData,outData_valid,NR_PARALLELS,COEFFS): NR_COEFFS=len(COEFFS) coeffs_signal_array=[Signal(intbv(COEFFS[i],min=min(COEFFS),max=max(COEFFS)+1)) for i in range(NR_COEFFS)] reg_Shift_array=[Signal(intbv(0,min=inData.min,max=inData.max)) for i in range(NR_COEFFS)] reg_count=Signal(intbv(0,min=0,max=len(COEFFS))) add_conection=[Signal(intbv(0,min=outData.min,max=outData.max)) for i in range(NR_PARALLELS)] #TODO data range reg_mult_result=[Signal(intbv(0,min=outData.min,max=outData.max)) for i in range(NR_PARALLELS)] #TODO data range reg_accumulation=Signal(intbv(0,min=outData.min,max=outData.max)) reg_run_enable=Signal(bool(0)) Data_valid=Signal(bool(0)) reg_next_run_enable=Signal(bool(0)) addup_insts=[None]*NR_PARALLELS for i in range(NR_PARALLELS): if i==(NR_PARALLELS-1): addup_insts[i]=Combinatorical_adder(add_conection[i],reg_mult_result[i],0) else: addup_insts[i]=Combinatorical_adder(add_conection[i],reg_mult_result[i],add_conection[i+1]) @always(clk.posedge,rst.negedge) def seq_logic(): if rst==0: reg_count.next=0 for i in range(NR_COEFFS): reg_Shift_array[i].next=0 for i in range(NR_PARALLELS): reg_mult_result[i].next=0 reg_run_enable.next=0 Data_valid.next=0 else: outData_valid.next=Data_valid if inData_enable: #should not be enabled before data is valid reg_Shift_array[0].next=inData for i in range(NR_COEFFS-1): reg_Shift_array[int(i+1)].next=reg_Shift_array[int(i)] reg_run_enable.next=True reg_accumulation.next=0 Data_valid.next=False outData_valid.next=False if reg_run_enable: for i in range(NR_PARALLELS): reg_mult_result[int(i)].next=reg_Shift_array[int(i+reg_count)]*coeffs_signal_array[int(i+reg_count)] if reg_count==(NR_COEFFS-NR_PARALLELS): reg_count.next=0 Data_valid.next=True reg_run_enable.next=False else: reg_count.next=reg_count+NR_PARALLELS Data_valid.next=False if reg_next_run_enable: reg_accumulation.next=reg_accumulation+add_conection[0] reg_next_run_enable.next=reg_run_enable @always_comb def output_logic(): outData.next=reg_accumulation #### current workaround, ###### for i in range(NR_COEFFS): coeffs_signal_array[int(i)].next=COEFFS[int(i)] ############################################## return seq_logic,addup_insts ,output_logic #real_alu_insts,imag_alu_insts, comb_logic,seq_logic from pylab import * import scipy.signal as signal def TESTBENCH_XX(): ## Filter design COEFF_WIDTH=10 NR_COEFFS = 60 a = signal.firwin(NR_COEFFS, cutoff = 0.4, window = "hamming") filter_coefficients=tuple([int(i*2**COEFF_WIDTH) for i in a]) ## instanciation clk=Signal(bool(0)) rst=Signal(bool(0)) inData_enable=Signal(bool(0)) outData_valid=Signal(bool(0)) inData=Signal(intbv(0,min=-10,max=10)) outData=Signal(intbv(0,min=-20000,max=20000)) toVerilog(Generic_FIR_filter,clk,rst,inData,inData_enable,outData,outData_valid,NR_PARALLELS=3,COEFFS=filter_coefficients) dut_inst=Generic_FIR_filter(clk,rst,inData,inData_enable,outData,outData_valid,NR_PARALLELS=3,COEFFS=filter_coefficients) step_response=[] clock_counter=Signal(intbv(0)) @always(delay(10)) def clkgen(): clk.next = not clk clock_counter.next=clock_counter+1 @instance def stimulus(): rst.next=0 yield clk yield clk rst.next=1 yield clk yield clk #test step responce inData.next=0 inData_enable.next=1 for i in range(NR_COEFFS): yield clk.posedge inData_enable.next=0 yield clk.posedge print "wait for outData_valid" yield outData_valid step_response.append(int(outData)) yield clk.negedge inData.next=1 inData_enable.next=1 subplot(211) plot(step_response) ##step_response of HDL filter subplot(212) plot(cumsum(filter_coefficients)) ##step_response of the real filter print "Testbench Cycles:",clock_counter/2 show() raise StopSimulation return stimulus,clkgen,dut_inst sim = Simulation(TESTBENCH_XX()) sim.run() ########################################################################################### greetings Norbo |
From: Norbo <Nor...@gm...> - 2012-02-21 20:15:20
|
> I do not understand why the realVals array (list ofsignals) is used > since only one element is used at a time. yeah it deosnt realy makes sense if only one element is used at a time, except for the case where you want to have read access to a constant then some time later override this data with some other value. (which would actually be like a ram access where the ram content is initialized, i have seen a post where jan wrote that he already had this code but he removed it because not every synthesis tool supported it at that time, or is there a limitation with verilog, i think vhdl should do fine now) > I believe the limitation you are trying to demonstrate is when the tuple > (ROM) access is mixed in other statements, instead of being its own > statement, i.e. rather than a separate generator to describe the ROM it > is embedded in the statements directly, e.g. > @always_comb > def hld_output(): > outData.next = inData*Table[int(dataIndex)] > For this second case to be convertible the converting code needs to do > some extra work which it does not and it is not clear if it should. The > conversion code will need to do the same conversion as the previous > (above) example, hence it will have to infer more. This has beendebated > in the past (level of inference) and there are differentopinions on the > level of inference that should be supported. Another way to do this in myhdl to avoid an "deeper level of inference" would be to use, instead of the Table[] (which is a tuple of ints), a explicit declared signal array where the initial values of the signal array are set to the values of the tuple, like this: signal_array=[Signal(intbv(Table[i],min=min(Table),max=max(Table)+1)) for i in range(len(Table))] an then use the signal_array in every statement where the access is mixed with other statements, e.g. @always_comb def hld_output(): outData.next = inData*signal_array[int(dataIndex)] + signal_array[int(dataIndex+1)] #.....etc This is actually working fine in myhdl simulation. The conversion to vhdl and verilog succeeds also without error or warning. but the signal_array in the vhdl code doesnt gets initialized with the Table values and outData will never get the corresponding data. So the actual workaround i am currently using is adding this code, but i think this could be a obstacle for some synthesis tools and for me it doesn't seams very neat, since it could be simply made obsolete. @always_comb def comb_logic(): for i in range(len(Table)): signal_array[int(i)].next=Table[int(i)] Ok here is the full code where i am using this workaround: (a ultra generic fir filter) ################################################################################################################ from myhdl import * def Combinatorical_adder(out,in1,in2): @always_comb def cc_logic(): out.next=in1+in2 return cc_logic ## NR_PARALLELS must be a natural divider of len(COEFFS) def Generic_FIR_filter(clk,rst,inData,inData_enable,outData,outData_valid,NR_PARALLELS,COEFFS): NR_COEFFS=len(COEFFS) coeffs_signal_array=[Signal(intbv(COEFFS[i],min=min(COEFFS),max=max(COEFFS)+1)) for i in range(NR_COEFFS)] reg_Shift_array=[Signal(intbv(0,min=inData.min,max=inData.max)) for i in range(NR_COEFFS)] reg_count=Signal(intbv(0,min=0,max=len(COEFFS))) add_conection=[Signal(intbv(0,min=outData.min,max=outData.max)) for i in range(NR_PARALLELS)] #TODO data range reg_mult_result=[Signal(intbv(0,min=outData.min,max=outData.max)) for i in range(NR_PARALLELS)] #TODO data range reg_accumulation=Signal(intbv(0,min=outData.min,max=outData.max)) reg_run_enable=Signal(bool(0)) Data_valid=Signal(bool(0)) reg_next_run_enable=Signal(bool(0)) addup_insts=[None]*NR_PARALLELS for i in range(NR_PARALLELS): if i==(NR_PARALLELS-1): addup_insts[i]=Combinatorical_adder(add_conection[i],reg_mult_result[i],0) else: addup_insts[i]=Combinatorical_adder(add_conection[i],reg_mult_result[i],add_conection[i+1]) @always(clk.posedge,rst.negedge) def seq_logic(): if rst==0: reg_count.next=0 for i in range(NR_COEFFS): reg_Shift_array[i].next=0 for i in range(NR_PARALLELS): reg_mult_result[i].next=0 reg_run_enable.next=0 Data_valid.next=0 else: outData_valid.next=Data_valid if inData_enable: #should not be enabled before data is valid reg_Shift_array[0].next=inData for i in range(NR_COEFFS-1): reg_Shift_array[int(i+1)].next=reg_Shift_array[int(i)] reg_run_enable.next=True reg_accumulation.next=0 Data_valid.next=False outData_valid.next=False if reg_run_enable: for i in range(NR_PARALLELS): reg_mult_result[int(i)].next=reg_Shift_array[int(i+reg_count)]*coeffs_signal_array[int(i+reg_count)] if reg_count==(NR_COEFFS-NR_PARALLELS): reg_count.next=0 Data_valid.next=True reg_run_enable.next=False else: reg_count.next=reg_count+NR_PARALLELS Data_valid.next=False if reg_next_run_enable: reg_accumulation.next=reg_accumulation+add_conection[0] reg_next_run_enable.next=reg_run_enable @always_comb def output_logic(): outData.next=reg_accumulation #### current workaround, ###### for i in range(NR_COEFFS): coeffs_signal_array[int(i)].next=COEFFS[int(i)] ############################################## return seq_logic,addup_insts ,output_logic #real_alu_insts,imag_alu_insts, comb_logic,seq_logic from pylab import * import scipy.signal as signal def TESTBENCH_XX(): ## Filter design COEFF_WIDTH=10 NR_COEFFS = 60 a = signal.firwin(NR_COEFFS, cutoff = 0.4, window = "hamming") filter_coefficients=tuple([int(i*2**COEFF_WIDTH) for i in a]) ## instanciation clk=Signal(bool(0)) rst=Signal(bool(0)) inData_enable=Signal(bool(0)) outData_valid=Signal(bool(0)) inData=Signal(intbv(0,min=-10,max=10)) outData=Signal(intbv(0,min=-20000,max=20000)) toVerilog(Generic_FIR_filter,clk,rst,inData,inData_enable,outData,outData_valid,NR_PARALLELS=3,COEFFS=filter_coefficients) dut_inst=Generic_FIR_filter(clk,rst,inData,inData_enable,outData,outData_valid,NR_PARALLELS=3,COEFFS=filter_coefficients) step_response=[] clock_counter=Signal(intbv(0)) @always(delay(10)) def clkgen(): clk.next = not clk clock_counter.next=clock_counter+1 @instance def stimulus(): rst.next=0 yield clk yield clk rst.next=1 yield clk yield clk #test step responce inData.next=0 inData_enable.next=1 for i in range(NR_COEFFS): yield clk.posedge inData_enable.next=0 yield clk.posedge print "wait for outData_valid" yield outData_valid step_response.append(int(outData)) yield clk.negedge inData.next=1 inData_enable.next=1 subplot(211) plot(step_response) ##step_response of HDL filter subplot(212) plot(cumsum(filter_coefficients)) ##step_response of the real filter print "Testbench Cycles:",clock_counter/2 show() raise StopSimulation return stimulus,clkgen,dut_inst sim = Simulation(TESTBENCH_XX()) sim.run() ########################################################################################### greetings Norbo |
From: Norbo <Nor...@gm...> - 2012-04-20 13:16:54
Attachments:
ArrayInit.patch
|
I Considered the following example: from myhdl import * def TOP(out1,out2): aListSig=[Signal(intbv(i)[8:]) for i in range(10)] @always_comb def comb_logic(): out1.next=aListSig[3] out2.next=aListSig[9] return comb_logic def test_bench(): sig1=Signal(intbv(0)[8:]) sig2=Signal(intbv(0)[8:]) instanc_top=TOP(sig1,sig2) interval = delay(10) @always(interval) def stimulus(): print "Value1 is: ",sig1," Value2 is: ",sig2 return stimulus,instanc_top sim = Simulation(test_bench()) sim.run(10) toVHDL(TOP,Signal(intbv(0)[8:]),Signal(intbv(0)[8:] As expected the output of the simulation is: >> Value1 is: 3 Value2 is: 9 The genereted VHDL code however is: library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; use std.textio.all; use work.pck_myhdl_07.all; entity TOP is port ( out1: out unsigned(7 downto 0); out2: out unsigned(7 downto 0) ); end entity TOP; architecture MyHDL of TOP is type t_array_aListSig is array(0 to 10-1) of unsigned(7 downto 0); signal aListSig: t_array_aListSig; begin out1 <= aListSig(3); out2 <= aListSig(9); end architecture MyHDL; this code obviously would not show these values at the ouput ports. if in the myhdl code seperated signal instead of the list of signals is used, then the myhdl code would look like this: def TOP(out1,out2): #aListSig=[Signal(intbv(i)[8:]) for i in range(10)] aSig1=Signal(intbv(3)[8:]) aSig2=Signal(intbv(9)[8:]) @always_comb def comb_logic(): out1.next=aSig1 #aListSig[3] out2.next=aSig2 #aListSig[9] return comb_logic Then the generated in vhdl code looks like this: architecture MyHDL of TOP is signal aSig1: unsigned(7 downto 0); signal aSig2: unsigned(7 downto 0); begin aSig1 <= to_unsigned(3, 8); aSig2 <= to_unsigned(9, 8); out1 <= aSig1; out2 <= aSig2; end architecture MyHDL; In this example the values (3 and 9) are put on the output ports. Do you see where i want to point at? Now consider the case of a synchron Ram with pre-initialized values which you want to infere, there the memory-list needs to have initial values. from myhdl import * def sync_RAM(dout, din, addr, we, clk, CONTENT=None): """sync Ram model """ mem = [Signal(intbv(CONTENT[i],min=dout.min,max=dout.max)) for i in range(len(CONTENT))] @always(clk.posedge) def read_write(): if we: mem[addr].next = din dout.next = mem[addr] return read_write def TESTBENCH_XX(): we=Signal(bool()) clk=Signal(bool()) MemoryContent=[2,121,43,8,32]+range(3,10) addr=Signal(intbv(0,min=0,max=len(MemoryContent))) din=Signal(intbv(min(MemoryContent),min=min(MemoryContent),max=max(MemoryContent)+1 )) dout=Signal(intbv(min(MemoryContent),min=min(MemoryContent),max=max(MemoryContent)+1 )) toVHDL(sync_RAM,dout,din,addr,we,clk,MemoryContent) toVerilog(sync_RAM,dout,din,addr,we,clk,MemoryContent) sync_RAM_inst=sync_RAM(dout,din,addr,we,clk,CONTENT=MemoryContent) @always(delay(10)) def clkgen(): clk.next = not clk @instance def stimulus(): for i,data in enumerate(MemoryContent): addr.next=i yield clk.negedge print "Data is:", dout raise StopSimulation return sync_RAM_inst,clkgen,stimulus sim = Simulation(TESTBENCH_XX()) sim.run() this is bassicaly very similar to the examples above where the simulation has the initial values but the generated code doesnt have them and therfore behaves different than the simulation. Another thing is that you probably dont want to have every list of signal which you declare in myhdl to be initialized in the generated code. For that i used the "None" keyword of python e.g: mem = [Signal(intbv(None)[8:]) for i in range(255)] So for me i basically changed the myhdl sources so that list of Signals gets initialzed in the generated code, so the the above synchron RAM descriptions gets converted to the following vhdl code: library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; use std.textio.all; use work.pck_myhdl_07.all; entity sync_RAM is port ( dout: out unsigned(6 downto 0); din: in unsigned(6 downto 0); addr: in unsigned(3 downto 0); we: in std_logic; clk: in std_logic ); end entity sync_RAM; -- Ram model architecture MyHDL of sync_RAM is type t_array_mem is array(0 to 12-1) of unsigned(6 downto 0); signal mem: t_array_mem :=(0=>"0000010", 1=>"1111001", 2=>"0101011", 3=>"0001000", 4=>"0100000", 5=>"0000011", 6=>"0000100", 7=>"0000101", 8=>"0000110", 9=>"0000111", 10=>"0001000", 11=>"0001001"); begin SYNC_RAM_READ_WRITE: process (clk) is begin if rising_edge(clk) then if to_boolean(we) then mem(to_integer(addr)) <= din; end if; dout <= mem(to_integer(addr)); end if; end process SYNC_RAM_READ_WRITE; end architecture MyHDL; And the generated Verilog code looks like this: `timescale 1ns/10ps module sync_RAM ( dout, din, addr, we, clk ); // Ram model output [6:0] dout; reg [6:0] dout; input [6:0] din; input [3:0] addr; input we; input clk; reg [6:0] mem [0:12-1]; initial begin : INIT_mem mem[0]=2; mem[1]=121; mem[2]=43; mem[3]=8; mem[4]=32; mem[5]=3; mem[6]=4; mem[7]=5; mem[8]=6; mem[9]=7; mem[10]=8; mem[11]=9; end always @(posedge clk) begin: SYNC_RAM_READ_WRITE if (we) begin mem[addr] <= din; end dout <= mem[addr]; end endmodule I synthesised this syncron ram description in symplify and alter quartus in vhdl and verilog and from the first view they both create the ram with initial values succesfully if i change the the line: mem = [Signal(intbv(CONTENT[i],min=dout.min,max=dout.max)) for i in range(len(CONTENT))] to: mem = [Signal(intbv(None,min=dout.min,max=dout.max)) for i in range(len(CONTENT))] then the code gets converted like normal, and in order to make the boundchecks of the intbv succesfull i set the initial value of the intbv to the lower bound value (min). if this value is not given i set it to zero. What you think about this? Have i overseen something important? Any other suggestions? (if you want to try this, the patchfile should be appended) greeting Norbo |
From: Christopher F. <chr...@gm...> - 2012-04-20 16:23:16
|
As you are aware the use cases supported by MyHDL are RAM and ROM and examples for each and descriptions can be found here: http://www.myhdl.org/doc/0.7/manual/conversion_examples.html#ram-inference http://www.myhdl.org/doc/0.7/manual/conversion_examples.html#rom-inference What you are looking for is pre-initialize RAM, that is most notably used in FPGA designs. If you are only looking for ROM using a tuple of ints should get you what you want. It will load the correct examples. Your example below using the ROM template would convert to what you expect. # MyHDL description from myhdl import * def TOP(clk,out1,out2): rom = tuple([ii for ii in range(10)]) @always(clk.posedge) def rom_logic(): out1.next=rom[3] out2.next=rom[9] return rom_logic -- Converted VHDL TOP_ROM_LOGIC: process (clk) is begin if rising_edge(clk) then case 3 is when 0 => out1 <= "00000000"; when 1 => out1 <= "00000001"; when 2 => out1 <= "00000010"; when 3 => out1 <= "00000011"; when 4 => out1 <= "00000100"; when 5 => out1 <= "00000101"; when 6 => out1 <= "00000110"; when 7 => out1 <= "00000111"; when 8 => out1 <= "00001000"; when others => out1 <= "00001001"; end case; case 9 is when 0 => out2 <= "00000000"; when 1 => out2 <= "00000001"; when 2 => out2 <= "00000010"; when 3 => out2 <= "00000011"; when 4 => out2 <= "00000100"; when 5 => out2 <= "00000101"; when 6 => out2 <= "00000110"; when 7 => out2 <= "00000111"; when 8 => out2 <= "00001000"; when others => out2 <= "00001001"; end case; end if; end process TOP_ROM_LOGIC; In the past discussions the Altera recommended guidelines for RAM and ROM instantiation has been referenced. http://www.altera.com/literature/hb/qts/qts_qii51007.pdf. From my experience tool specific pragmas or init files are used to pre-init RAMS in the FPGA vendor tools. The guidelines for "specifying initial memory contents at power-up" is described start at section 11-32, which seem to suggest the same (.mif file). But it does go on to describe a method for initializing, using an initial block in Verilog and a function in VHDL. To get to an actual synthesizable approach it appears the initial values would not be enough? One of the reasons why initial values has not been implemented (it is on the todo list, http://www.myhdl.org/doku.php/dev:tasks#initial_values_suppot) is that it was observed that Quartus did not support initial value support in Verilog. There would be a mis-match between the Verilog conversion and VHDL conversion. It is not clear to me at this point what is the best path forward. I believe adding initial value support is possible just need to test with a bunch of tools, this is doable. If you actual goal is synthesiable pre-init RAM this path might not get you there. Regards, Chris On 4/20/12 8:16 AM, Norbo wrote: > I Considered the following example: > > from myhdl import * > > def TOP(out1,out2): > aListSig=[Signal(intbv(i)[8:]) for i in range(10)] > > @always_comb > def comb_logic(): > out1.next=aListSig[3] > out2.next=aListSig[9] > > return comb_logic > > > def test_bench(): > sig1=Signal(intbv(0)[8:]) > sig2=Signal(intbv(0)[8:]) > instanc_top=TOP(sig1,sig2) > > interval = delay(10) > @always(interval) > def stimulus(): > print "Value1 is: ",sig1," Value2 is: ",sig2 > > return stimulus,instanc_top > > > sim = Simulation(test_bench()) > sim.run(10) > toVHDL(TOP,Signal(intbv(0)[8:]),Signal(intbv(0)[8:] > > > > As expected the output of the simulation is: >>> Value1 is: 3 Value2 is: 9 > The genereted VHDL code however is: > > library IEEE; > use IEEE.std_logic_1164.all; > use IEEE.numeric_std.all; > use std.textio.all; > > use work.pck_myhdl_07.all; > > entity TOP is > port ( > out1: out unsigned(7 downto 0); > out2: out unsigned(7 downto 0) > ); > end entity TOP; > > architecture MyHDL of TOP is > > type t_array_aListSig is array(0 to 10-1) of unsigned(7 downto 0); > signal aListSig: t_array_aListSig; > > begin > > > > > > out1 <= aListSig(3); > out2 <= aListSig(9); > > end architecture MyHDL; > > this code obviously would not show these values at the ouput ports. > if in the myhdl code seperated signal instead of the list of signals is > used, > then the myhdl code would look like this: > > def TOP(out1,out2): > #aListSig=[Signal(intbv(i)[8:]) for i in range(10)] > aSig1=Signal(intbv(3)[8:]) > aSig2=Signal(intbv(9)[8:]) > > @always_comb > def comb_logic(): > out1.next=aSig1 #aListSig[3] > out2.next=aSig2 #aListSig[9] > > return comb_logic > > > > Then the generated in vhdl code looks like this: > > architecture MyHDL of TOP is > > signal aSig1: unsigned(7 downto 0); > signal aSig2: unsigned(7 downto 0); > > begin > > aSig1 <= to_unsigned(3, 8); > aSig2 <= to_unsigned(9, 8); > > > > > out1 <= aSig1; > out2 <= aSig2; > > end architecture MyHDL; > > In this example the values (3 and 9) are put on the output ports. > Do you see where i want to point at? > > > Now consider the case of a synchron Ram with pre-initialized values > which you want to infere, there the memory-list needs to have initial > values. > > from myhdl import * > > def sync_RAM(dout, din, addr, we, clk, CONTENT=None): > """sync Ram model """ > mem = [Signal(intbv(CONTENT[i],min=dout.min,max=dout.max)) for i in > range(len(CONTENT))] > > @always(clk.posedge) > def read_write(): > if we: > mem[addr].next = din > dout.next = mem[addr] > > return read_write > > def TESTBENCH_XX(): > we=Signal(bool()) > clk=Signal(bool()) > MemoryContent=[2,121,43,8,32]+range(3,10) > addr=Signal(intbv(0,min=0,max=len(MemoryContent))) > din=Signal(intbv(min(MemoryContent),min=min(MemoryContent),max=max(MemoryContent)+1 > )) > dout=Signal(intbv(min(MemoryContent),min=min(MemoryContent),max=max(MemoryContent)+1 > )) > > toVHDL(sync_RAM,dout,din,addr,we,clk,MemoryContent) > toVerilog(sync_RAM,dout,din,addr,we,clk,MemoryContent) > sync_RAM_inst=sync_RAM(dout,din,addr,we,clk,CONTENT=MemoryContent) > > @always(delay(10)) > def clkgen(): > clk.next = not clk > > @instance > def stimulus(): > for i,data in enumerate(MemoryContent): > addr.next=i > yield clk.negedge > print "Data is:", dout > > raise StopSimulation > > return sync_RAM_inst,clkgen,stimulus > > sim = Simulation(TESTBENCH_XX()) > sim.run() > > this is bassicaly very similar to the examples above where the > simulation has the initial values but the generated code > doesnt have them and therfore behaves different than the simulation. > Another thing is that you probably dont want to have every list of > signal which you declare in myhdl to be initialized in the generated code. > For that i used the "None" keyword of python e.g: > mem = [Signal(intbv(None)[8:]) for i in range(255)] > > So for me i basically changed the myhdl sources so that list of Signals > gets initialzed in the generated code, so the the above synchron RAM > descriptions gets converted to the following vhdl code: > > > library IEEE; > use IEEE.std_logic_1164.all; > use IEEE.numeric_std.all; > use std.textio.all; > > use work.pck_myhdl_07.all; > > entity sync_RAM is > port ( > dout: out unsigned(6 downto 0); > din: in unsigned(6 downto 0); > addr: in unsigned(3 downto 0); > we: in std_logic; > clk: in std_logic > ); > end entity sync_RAM; > -- Ram model > > architecture MyHDL of sync_RAM is > > type t_array_mem is array(0 to 12-1) of unsigned(6 downto 0); > signal mem: t_array_mem :=(0=>"0000010", > 1=>"1111001", > 2=>"0101011", > 3=>"0001000", > 4=>"0100000", > 5=>"0000011", > 6=>"0000100", > 7=>"0000101", > 8=>"0000110", > 9=>"0000111", > 10=>"0001000", > 11=>"0001001"); > > begin > > > > > SYNC_RAM_READ_WRITE: process (clk) is > begin > if rising_edge(clk) then > if to_boolean(we) then > mem(to_integer(addr)) <= din; > end if; > dout <= mem(to_integer(addr)); > end if; > end process SYNC_RAM_READ_WRITE; > > end architecture MyHDL; > > > > > > > > > And the generated Verilog code looks like this: > > > > > `timescale 1ns/10ps > > module sync_RAM ( > dout, > din, > addr, > we, > clk > ); > // Ram model > > output [6:0] dout; > reg [6:0] dout; > input [6:0] din; > input [3:0] addr; > input we; > input clk; > > > reg [6:0] mem [0:12-1]; > > initial > begin : INIT_mem > mem[0]=2; > mem[1]=121; > mem[2]=43; > mem[3]=8; > mem[4]=32; > mem[5]=3; > mem[6]=4; > mem[7]=5; > mem[8]=6; > mem[9]=7; > mem[10]=8; > mem[11]=9; > end > > > > > always @(posedge clk) begin: SYNC_RAM_READ_WRITE > if (we) begin > mem[addr] <= din; > end > dout <= mem[addr]; > end > > endmodule > > > I synthesised this syncron ram description in symplify and alter quartus > in vhdl and verilog and from the first view they both create the ram > with initial values succesfully > > if i change the the line: > mem = [Signal(intbv(CONTENT[i],min=dout.min,max=dout.max)) for i in > range(len(CONTENT))] > to: > mem = [Signal(intbv(None,min=dout.min,max=dout.max)) for i in > range(len(CONTENT))] > > then the code gets converted like normal, and in order to make the > boundchecks of the intbv succesfull i > set the initial value of the intbv to the lower bound value (min). if > this value is not given i set it to zero. > > > What you think about this? Have i overseen something important? Any > other suggestions? (if you want to try this, the patchfile should be > appended) > > greeting > Norbo > > > ------------------------------------------------------------------------------ > For Developers, A Lot Can Happen In A Second. > Boundary is the first to Know...and Tell You. > Monitor Your Applications in Ultra-Fine Resolution. Try it FREE! > http://p.sf.net/sfu/Boundary-d2dvs2 > > > > _______________________________________________ > myhdl-list mailing list > myh...@li... > https://lists.sourceforge.net/lists/listinfo/myhdl-list |
From: Norbo <Nor...@gm...> - 2012-04-22 13:28:49
Attachments:
ArrayInit.patch
|
Just wanted to provide a more highlighting example about the more general support of indexed constants if the array is initialized in the verilog or vhdl code, especially about using multiple indices in the same expression: from myhdl import * def TOP(out1,in_data,in_addr): aListSig=[Signal(intbv(i)[8:]) for i in range(10)] @always_comb def comb_logic(): out1.next=aListSig[3]*3+aListSig[in_addr]*in_data return comb_logic def test_bench(): sig1=Signal(intbv(0)[8:]) in_data=Signal(intbv(0)[8:]) in_addr=Signal(intbv(0)[8:]) instanc_top=TOP(sig1,in_data,in_addr) #interval = delay(10) @instance def stimulus(): in_data.next=0 in_addr.next=0 yield delay(1) print "Value1 is: ",sig1," Value2 is: ",in_data in_data.next=2 in_addr.next=6 yield delay(1) print "Value1 is: ",sig1," Value2 is: ",in_data raise StopSimulation return stimulus,instanc_top sim = Simulation(test_bench()) sim.run(20) a,b,c = [Signal(intbv(0)[8:]) for i in range(3)] toVHDL(TOP,a,b,c) toVerilog(TOP,a,b,c) Of curse this doesnt get mapped into ram, but the code is synthesisable and the netlist-viewer seems to show a good result. In the patch i posted previously i noticed an error i made: The verilog code is not synthesisable if the list of signals is only used in reading, because then the toVerilog conversion defines the list of signals with the "wire" keyword ( in the case above "wire [7:0] aListSig [0:10-1];" ) but it is not possible to use the "initial block" with the wire keyword. so i changed it in this case to "reg" (in the case above "reg [7:0] aListSig [0:10-1];") I just appended the new patch. greetings Norbo |
From: Christopher F. <chr...@gm...> - 2012-04-24 03:57:33
|
> > > Of curse this doesnt get mapped into ram, but the code is synthesisable > and the netlist-viewer seems to show > a good result. > In the patch i posted previously i noticed an error i made: > The verilog code is not synthesisable if the list of signals is only > used in reading, because then the toVerilog conversion > defines the list of signals with the "wire" keyword ( in the case above > "wire [7:0] aListSig [0:10-1];" ) > but it is not possible to use the "initial block" with the wire keyword. > so i changed it in this case to "reg" (in the case above "reg [7:0] > aListSig [0:10-1];") > I just appended the new patch. > > > > greetings > > Norbo > > I was taking a look at your patch, your patch is not based on the latest 0.8-dev branch. There are enough changes that it doesn't auto import to the 0.8-dev branch. I don't know if the _doinit flag is actually needed? I believe the conversion code can determine if the value should be written or not without modifying the Signal or intbv objects. As your modified intbv does, it can always create the initial value unless the Signal/intbv init value is None. Lastly, the changes do break the unit-tests. As mentioned, the patch didn't auto import. I believe I got all the changes manually but possibly something was missed in the manual addition of the changes. I think removing the _doinit will fix the failures (but I didn't really look that closely). Regards, Chris |
From: Norbo <Nor...@gm...> - 2012-05-01 18:06:12
Attachments:
ArrayInit.patch
|
> I was taking a look at your patch, your patch is not based on the latest > 0.8-dev branch. There are enough changes that it doesn't auto import to > the 0.8-dev branch. > > I don't know if the _doinit flag is actually needed? I believe the > conversion code can determine if the value should be written or not > without modifying the Signal or intbv objects. As your modified intbv > does, it can always create the initial value unless the Signal/intbv > init value is None. Yes this would be possible if the value (._val) of the intbv is set to None. But there are several things that raise, exceptions if the value is set to None. (Boundcheck, __getitem__, etc..) so i added the _doinit flag and set the (._val) to 0 (or to the min value if defined), and thereby avoided the error-prone changing of these functions which wouldnt work with the None value. > Lastly, the changes do break the unit-tests. As mentioned, the patch > didn't auto import. I believe I got all the changes manually but > possibly something was missed in the manual addition of the changes. I > think removing the _doinit will fix the failures (but I didn't really > look that closely). The now appended patch file is based on the 0.8-dev branch, the core test passes but until now i wasnt able to run the conversion tests. The initial values in this patch for the array signal are now written in the following casess: for VHDL and Verilog: 1. reading it in a combinatorical process --- initial values are written 2. reading it synchron (clocked) --- initial values are written (could be used to describe inited ROM) 3. reading it synchron (clocked) and writing it synchron --- initial values are written (could be used to describe pre-inited RAM, or even pre-inited dual ported RAM) 4. reading it synchron (clocked) and writing it in a combinatorical process --- initial values are not ! written (because it is syntactically not possible in verilog) 5. reading and writing in a combinatorical process --- initial values are not ! written (because it is syntactically not possible in verilog) would this be acceptable/desirable? greetings Norbo |
From: Christopher F. <chr...@gm...> - 2012-05-05 04:49:26
|
On 5/1/12 1:00 PM, Norbo wrote: >> I was taking a look at your patch, your patch is not based on the latest >> 0.8-dev branch. There are enough changes that it doesn't auto import to >> the 0.8-dev branch. >> >> I don't know if the _doinit flag is actually needed? I believe the >> conversion code can determine if the value should be written or not >> without modifying the Signal or intbv objects. As your modified intbv >> does, it can always create the initial value unless the Signal/intbv >> init value is None. > Yes this would be possible if the value (._val) of the intbv is set to > None. > But there are several things that raise, exceptions if the value is set to > None. (Boundcheck, __getitem__, etc..) > so i added the _doinit flag and set the (._val) to 0 (or to the min value > if defined), and thereby avoided the error-prone changing of these > functions which wouldnt work with the None value. Why do you need "None"? It would be my understanding that the conversion process will always create the init values in the converted code. You don't need the _doinit flag and you don't need None. > > >> Lastly, the changes do break the unit-tests. As mentioned, the patch >> didn't auto import. I believe I got all the changes manually but >> possibly something was missed in the manual addition of the changes. I >> think removing the _doinit will fix the failures (but I didn't really >> look that closely). > The now appended patch file is based on the 0.8-dev branch, the core test > passes but until now i wasnt able to > run the conversion tests. > > The initial values in this patch for the array signal are now written in > the following casess: for VHDL and Verilog: > > 1. reading it in a combinatorical process --- initial values are written > 2. reading it synchron (clocked) --- initial values are > written (could be used to describe inited ROM) > 3. reading it synchron (clocked) and writing it synchron --- initial > values are written (could be used to describe pre-inited RAM, or even > pre-inited dual ported RAM) > 4. reading it synchron (clocked) and writing it in a combinatorical > process --- initial values are not ! written (because it is syntactically > not possible in verilog) > 5. reading and writing in a combinatorical process --- initial > values are not ! written (because it is syntactically not possible in > verilog) > > > would this be acceptable/desirable? Lost me. I don't understand why it matters how the Signal variable is used to determine if the initial value should be written. As mentioned, shouldn't the initial values always be written? Note: these are only simulation and or "special" synthesis mapping initial values. Reset values are still reset values. The only thing that needs to be determined is if the initial block needs to be added. That should simply be based on list of signals or not. Regards, Chris |
From: Norbo <Nor...@gm...> - 2012-05-05 09:00:59
|
Am 05.05.2012, 06:49 Uhr, schrieb Christopher Felton <chr...@gm...>: > On 5/1/12 1:00 PM, Norbo wrote: >>> I was taking a look at your patch, your patch is not based on the >>> latest >>> 0.8-dev branch. There are enough changes that it doesn't auto import to >>> the 0.8-dev branch. >>> >>> I don't know if the _doinit flag is actually needed? I believe the >>> conversion code can determine if the value should be written or not >>> without modifying the Signal or intbv objects. As your modified intbv >>> does, it can always create the initial value unless the Signal/intbv >>> init value is None. >> Yes this would be possible if the value (._val) of the intbv is set to >> None. >> But there are several things that raise, exceptions if the value is set >> to >> None. (Boundcheck, __getitem__, etc..) >> so i added the _doinit flag and set the (._val) to 0 (or to the min >> value >> if defined), and thereby avoided the error-prone changing of these >> functions which wouldnt work with the None value. > > Why do you need "None"? It would be my understanding that the > conversion process will always create the init values in the converted > code. You don't need the _doinit flag and you don't need None. My thought on this was that it may be usefull to have explicit controll on whether the initial values are written ot not. For example if a big ram is described and you don't need the initial values, the converted file would be filled up with a huge number of basically useless values. -> Or in the words of "import this" in python: Explicit is better than implicit >>> Lastly, the changes do break the unit-tests. As mentioned, the patch >>> didn't auto import. I believe I got all the changes manually but >>> possibly something was missed in the manual addition of the changes. I >>> think removing the _doinit will fix the failures (but I didn't really >>> look that closely). >> The now appended patch file is based on the 0.8-dev branch, the core >> test >> passes but until now i wasnt able to >> run the conversion tests. >> >> The initial values in this patch for the array signal are now written in >> the following casess: for VHDL and Verilog: >> >> 1. reading it in a combinatorical process --- initial values are written >> 2. reading it synchron (clocked) --- initial values are >> written (could be used to describe inited ROM) >> 3. reading it synchron (clocked) and writing it synchron --- initial >> values are written (could be used to describe pre-inited RAM, or even >> pre-inited dual ported RAM) >> 4. reading it synchron (clocked) and writing it in a combinatorical >> process --- initial values are not ! written (because it is >> syntactically >> not possible in verilog) >> 5. reading and writing in a combinatorical process --- initial >> values are not ! written (because it is syntactically not possible in >> verilog) >> >> >> would this be acceptable/desirable? > > Lost me. I don't understand why it matters how the Signal variable is > used to determine if the initial value should be written. As mentioned, > shouldn't the initial values always be written? For example for the case 5. "reading and writing in a combinatorical process" a example in for verilog could look like this: Myhdl code: ------------------------------------------------------ def TOP(out1,in_data): aListSig=[Signal(intbv(i)[8:]) for i in range(10)] @always_comb def comb_logic(): out1.next=aListSig[3] @always_comb def comb2_logic(): aListSig[3].next=in_data return comb2_logic ,comb_log Verilog code: --------------------------------------------------- module TOP ( out1, in_data ); output [7:0] out1; wire [7:0] out1; input [7:0] in_data; wire [7:0] aListSig [0:10-1]; //hypothetical inital block would be placed here //but if a initial block is placed here //the code is no longer valid because "aListSig" is defined as wire. //but it needs to be defined as wire because somewhere in the code someone //writes to the variable "aListSig" in combinatorically assignment. //therfore the initial signal cant be written in the case when a combinatoricall //assignment to the variable "aListSig" takes place somewhere in the code. //Anyway a initialvalue is not needed when a combinatorical assignment to the variable "aListSig" is made, //because the "aListSig" then gets its value imidiatley at the beginning of the simulation by this //assignment.(or after some delta cycles with zero delay). //In verilog the complete signal array is defined as wire, so if only the variable //with index 3 is (as below aListSig[3]) used combinatorically then the complete array //needs to be defined as "wire" and therfore no initialvalues are possible assign aListSig[3] = in_data; assign out1 = aListSig[3]; endmodule greetings Norbo |
From: Christopher F. <chr...@gm...> - 2012-05-05 10:41:27
|
On 5/5/12 4:00 AM, Norbo wrote: > Am 05.05.2012, 06:49 Uhr, schrieb Christopher Felton > <chr...@gm...>: > >> On 5/1/12 1:00 PM, Norbo wrote: >>>> I was taking a look at your patch, your patch is not based on the >>>> latest >>>> 0.8-dev branch. There are enough changes that it doesn't auto import to >>>> the 0.8-dev branch. >>>> >>>> I don't know if the _doinit flag is actually needed? I believe the >>>> conversion code can determine if the value should be written or not >>>> without modifying the Signal or intbv objects. As your modified intbv >>>> does, it can always create the initial value unless the Signal/intbv >>>> init value is None. >>> Yes this would be possible if the value (._val) of the intbv is set to >>> None. >>> But there are several things that raise, exceptions if the value is set >>> to >>> None. (Boundcheck, __getitem__, etc..) >>> so i added the _doinit flag and set the (._val) to 0 (or to the min >>> value >>> if defined), and thereby avoided the error-prone changing of these >>> functions which wouldnt work with the None value. >> >> Why do you need "None"? It would be my understanding that the >> conversion process will always create the init values in the converted >> code. You don't need the _doinit flag and you don't need None. > > My thought on this was that it may be usefull to have explicit controll on > whether the initial values are written ot not. For example if a big ram is > described > and you don't need the initial values, the converted file would be filled > up with a > huge number of basically useless values. -> Or in the words of "import > this" in python: > Explicit is better than implicit > To me this is *not* explicit, you don't have direct control over _doinit you have implicit control if the initial values are generated or not (which I have even forgotten what the control is). There are a set of rules that decide for you if initial values are to be generated or not. I would think explicit would be the use of None, you are directly indicating no init values. I hear the argument for the large inclusive of init values. And with FPGA embedded memories and ASIC available ROM/RAM growing these could be long init lists. But I am not totally convinced the inconvenience of long inits are bad because it breaks the earlier argument "MyHDL to Verilog/VHDL mismatch". I think the question; "Is the absence of initial values detrimental because it causes a co-simulation mismatch"? If it decided this is a bad thing then I think we need to live with the long init list or support the None initial values on the MyHDL side. > >>>> Lastly, the changes do break the unit-tests. As mentioned, the patch >>>> didn't auto import. I believe I got all the changes manually but >>>> possibly something was missed in the manual addition of the changes. I >>>> think removing the _doinit will fix the failures (but I didn't really >>>> look that closely). >>> The now appended patch file is based on the 0.8-dev branch, the core >>> test >>> passes but until now i wasnt able to >>> run the conversion tests. >>> >>> The initial values in this patch for the array signal are now written in >>> the following casess: for VHDL and Verilog: >>> >>> 1. reading it in a combinatorical process --- initial values are written >>> 2. reading it synchron (clocked) --- initial values are >>> written (could be used to describe inited ROM) >>> 3. reading it synchron (clocked) and writing it synchron --- initial >>> values are written (could be used to describe pre-inited RAM, or even >>> pre-inited dual ported RAM) >>> 4. reading it synchron (clocked) and writing it in a combinatorical >>> process --- initial values are not ! written (because it is >>> syntactically >>> not possible in verilog) >>> 5. reading and writing in a combinatorical process --- initial >>> values are not ! written (because it is syntactically not possible in >>> verilog) >>> >>> >>> would this be acceptable/desirable? >> >> Lost me. I don't understand why it matters how the Signal variable is >> used to determine if the initial value should be written. As mentioned, >> shouldn't the initial values always be written? > > For example for the case 5. "reading and writing in a combinatorical > process" > > assign aListSig[3] = in_data; > assign out1 = aListSig[3]; > > endmodule > This might be the path of least resistance right now. But I don't think there is a reason that the converted couldn't generate reg [7:0] out1 = 8'd0; reg [7:0] aListSig [0:10-1]; // initial blocks ... @always_comb begin aListSig[3] = in_data out1 = aListSig[3] end This is only an issue in Verilog and not VHDL. Maybe a behavioral statement should be written instead of a continuous assignment. To me it appears a lot of complexity would be added which could be avoided by simply converting the Verilog similar to how the VHDL is converted. Regards, Chris |
From: Norbo <Nor...@gm...> - 2012-05-05 15:35:53
|
Am 05.05.2012, 12:41 Uhr, schrieb Christopher Felton <chr...@gm...>: > On 5/5/12 4:00 AM, Norbo wrote: >> Am 05.05.2012, 06:49 Uhr, schrieb Christopher Felton >> <chr...@gm...>: >> >>> On 5/1/12 1:00 PM, Norbo wrote: >>>>> I was taking a look at your patch, your patch is not based on the >>>>> latest >>>>> 0.8-dev branch. There are enough changes that it doesn't auto import >>>>> to >>>>> the 0.8-dev branch. >>>>> >>>>> I don't know if the _doinit flag is actually needed? I believe the >>>>> conversion code can determine if the value should be written or not >>>>> without modifying the Signal or intbv objects. As your modified intbv >>>>> does, it can always create the initial value unless the Signal/intbv >>>>> init value is None. >>>> Yes this would be possible if the value (._val) of the intbv is set to >>>> None. >>>> But there are several things that raise, exceptions if the value is >>>> set >>>> to >>>> None. (Boundcheck, __getitem__, etc..) >>>> so i added the _doinit flag and set the (._val) to 0 (or to the min >>>> value >>>> if defined), and thereby avoided the error-prone changing of these >>>> functions which wouldnt work with the None value. >>> >>> Why do you need "None"? It would be my understanding that the >>> conversion process will always create the init values in the converted >>> code. You don't need the _doinit flag and you don't need None. >> >> My thought on this was that it may be usefull to have explicit controll >> on >> whether the initial values are written ot not. For example if a big ram >> is >> described >> and you don't need the initial values, the converted file would be >> filled >> up with a >> huge number of basically useless values. -> Or in the words of "import >> this" in python: >> Explicit is better than implicit >> > > To me this is *not* explicit, you don't have direct control over _doinit > you have implicit control if the initial values are generated or not > (which I have even forgotten what the control is). The idea of the controll was if you write: aList=[Signal(intbv(None)) for i in range(10) ] --> then the initial values are not written no matter what. aList=[Signal(intbv(i)) for i in range(10) ] --> the initial values are written in verilog and vhdl when there is no combinatorical assignment ("as a path of least resistance right now") > I hear the argument for the large inclusive of init values. And with > FPGA embedded memories and ASIC available ROM/RAM growing these could be > long init lists. But I am not totally convinced the inconvenience of > long inits are bad because it breaks the earlier argument "MyHDL to > Verilog/VHDL mismatch". I didn't get that. > This might be the path of least resistance right now. But I don't think > there is a reason that the converted couldn't generate > > reg [7:0] out1 = 8'd0; > reg [7:0] aListSig [0:10-1]; > // initial blocks ... > @always_comb begin nice: @always_comb begin a myhdl artefact ;) > aListSig[3] = in_data > out1 = aListSig[3] > end > > This is only an issue in Verilog and not VHDL. Maybe a behavioral > statement should be written instead of a continuous assignment. To me > it appears a lot of complexity would be added which could be avoided by > simply converting the Verilog similar to how the VHDL is converted. Damm, i overlooked that. The synthesis of the follwing is succesfully. .... reg [7:0] aListSig [0:10-1]; initial begin : INIT_aListSig aListSig[0]=0; aListSig[1]=1; aListSig[2]=2; aListSig[3]=3; aListSig[4]=4; aListSig[5]=5; aListSig[6]=6; aListSig[7]=7; aListSig[8]=8; aListSig[9]=9; end always @(in_data,aListSig[3]) begin // or always @* begin aListSig[3] = in_data; out1 = aListSig[3]; end summary: this means the "assign" in the verilog conversion needs to be replaced (by always @* begin ... end) and also if it gets declared as "wire" would need to be defined as "reg". another point is that i think it is a good thing to have all the variables by default not to be initialized with values in the converted code. Initialisation should only occour if you really want to describe (pre-init RAM) or ROM. because for example in VHDL all the std_logic signals are set at startup of a vhdl simulation to the value 'U' (stands for uninitialized) if there is no other initial value given. After the reset all values should have changed from this 'U' to something usefull. This allows you to see imidiatly if you have messed up or missed something in the reset code. If all values are set by default to something usefull you can easyl overlook some resets, you couldnt even check it with a testbench at startup if the reset is done properly, when the initial value has the same value as the reset value you have forgotten in the code. The point is, by default no initial value should be written in the converted code, only when you need it explicit to describe something. Maybe a default None value in the intbv? grettings Norbo |
From: Christopher F. <chr...@gm...> - 2012-05-07 15:13:15
|
> >> This might be the path of least resistance right now. But I don't think >> there is a reason that the converted couldn't generate >> >> reg [7:0] out1 = 8'd0; >> reg [7:0] aListSig [0:10-1]; >> // initial blocks ... >> @always_comb begin > > nice: @always_comb begin a myhdl artefact ;) > >> aListSig[3] = in_data >> out1 = aListSig[3] >> end >> Yes, typo should not have included the '@'. Note: *always_comb* and *always_ff* are new key words in later version of Verilog and support by most Sysnthesis tools, even the free ones. Or it is fine to use always @(*) or explicitly include the sensitivity list as the VHDL conversion does. Regards, Chris |
From: Christopher F. <chr...@gm...> - 2012-05-10 02:06:42
|
On 5/5/12 10:20 AM, Norbo wrote: > Am 05.05.2012, 12:41 Uhr, schrieb Christopher Felton > <chr...@gm...>: <snip> >>>>>> >>>>>> I don't know if the _doinit flag is actually needed? I believe the >>>>>> conversion code can determine if the value should be written or not >>>>>> without modifying the Signal or intbv objects. As your modified intbv >>>>>> does, it can always create the initial value unless the Signal/intbv >>>>>> init value is None. >>>>> Yes this would be possible if the value (._val) of the intbv is set to >>>>> None. >>>>> But there are several things that raise, exceptions if the value is >>>>> set >>>>> to >>>>> None. (Boundcheck, __getitem__, etc..) >>>>> so i added the _doinit flag and set the (._val) to 0 (or to the min >>>>> value >>>>> if defined), and thereby avoided the error-prone changing of these >>>>> functions which wouldnt work with the None value. >>>> >>>> Why do you need "None"? It would be my understanding that the >>>> conversion process will always create the init values in the converted >>>> code. You don't need the _doinit flag and you don't need None. >>> >>> My thought on this was that it may be usefull to have explicit controll >>> on >>> whether the initial values are written ot not. For example if a big ram >>> is >>> described >>> and you don't need the initial values, the converted file would be >>> filled >>> up with a >>> huge number of basically useless values. -> Or in the words of "import >>> this" in python: >>> Explicit is better than implicit >>> >> >> To me this is *not* explicit, you don't have direct control over _doinit >> you have implicit control if the initial values are generated or not >> (which I have even forgotten what the control is). > The idea of the controll was if you write: > aList=[Signal(intbv(None)) for i in range(10) ] --> then the initial > values are not written no matter what. > aList=[Signal(intbv(i)) for i in range(10) ] --> the initial values > are written in verilog and vhdl when there is > no combinatorical assignment ("as a path of least resistance right > now") > > I think we are both in agreement, now, that the None can be used for the explicit control (which means more changes in the implementation). But I don't think I agree that there are cases when the values should *not* be written. I think they should always be written (when not None). I think we can work through the implementation changes. If None is used, that should mean the _doinit is not required. > > >> I hear the argument for the large inclusive of init values. And with >> FPGA embedded memories and ASIC available ROM/RAM growing these could be >> long init lists. But I am not totally convinced the inconvenience of >> long inits are bad because it breaks the earlier argument "MyHDL to >> Verilog/VHDL mismatch". > > I didn't get that. It has been brought up in the past that if MyHDL simulation has initial values and the Verilog/VHDL does not have initial values. Then you have a simulation mismatch at time 0 through time N (most cases, until the reset occurs). I think we have a good design plan to cover the initial values that includes the pre-init RAMS (nice addition). We should probably summarize in a separate post (new thread, not buried). I boils down to adding support for None and then creating initial values in Verilog/VHDL if the intbv initial value is not None. Regards, Chris |
From: Jan D. <ja...@ja...> - 2012-05-22 13:12:55
|
On 05/05/2012 05:20 PM, Norbo wrote: > another point is that i think it is a good thing to have all the variables > by default not to be initialized > with values in the converted code. No, they should, for the simple reason that the intent of conversion is to generate Verilog/VHDL which matches the MyHDL behavior exactly. The only reason why they are not written currently (once they were) is a practical one: some synthesis tools didn't support this. But as soon we are sure they all do, I will want to change that. That would solve all kinds of issues at simulation time 0. > The point is, by default no initial value should be written in the > converted code, only when you need it explicit to describe something. > Maybe a default None value in the intbv? If you do something special for the intbv, then that can only give a half-hearted solution. I would like to encourage the use of high-level types such as bool and enum. -- Jan Decaluwe - Resources bvba - http://www.jandecaluwe.com Python as a HDL: http://www.myhdl.org VHDL development, the modern way: http://www.sigasi.com World-class digital design: http://www.easics.com |
From: Tom D. <td...@di...> - 2012-05-22 13:29:37
|
On 05/22/2012 08:12 AM, Jan Decaluwe wrote: > On 05/05/2012 05:20 PM, Norbo wrote: > > >> another point is that i think it is a good thing to have all the variables >> by default not to be initialized >> with values in the converted code. > No, they should, for the simple reason that the intent of conversion > is to generate Verilog/VHDL which matches the MyHDL behavior exactly. > > The only reason why they are not written currently (once they were) is > a practical one: some synthesis tools didn't support this. But as soon > we are sure they all do, I will want to change that. That would solve all > kinds of issues at simulation time 0. It can be annoying not to have a value at time 0 since you can get some warnings but I don't think all synthesis tools will take care of this for you. Depending on what you are synthesizing to it might not even be possible. I think the only way to guarantee initial values is the have a set/reset that is activated and use it in your logic to properly initialize anything that matters. Now maybe we are trying to deal with special cases in FPGAs? Such as initializing memory? I am coming late to this discussion. |
From: Jan D. <ja...@ja...> - 2012-05-22 13:52:04
|
On 05/22/2012 03:29 PM, Tom Dillon wrote: > On 05/22/2012 08:12 AM, Jan Decaluwe wrote: >> On 05/05/2012 05:20 PM, Norbo wrote: >> >> >>> another point is that i think it is a good thing to have all the variables >>> by default not to be initialized >>> with values in the converted code. >> No, they should, for the simple reason that the intent of conversion >> is to generate Verilog/VHDL which matches the MyHDL behavior exactly. >> >> The only reason why they are not written currently (once they were) is >> a practical one: some synthesis tools didn't support this. But as soon >> we are sure they all do, I will want to change that. That would solve all >> kinds of issues at simulation time 0. > > It can be annoying not to have a value at time 0 since you can get some > warnings but I don't think all synthesis tools will take care of this > for you. Depending on what you are synthesizing to it might not even be > possible. I am not saying synthesis should do anything with these values. It should just support the syntax. What this gives us is that at least in simulation, we would get a full match between MyHDL and the converted Verilog/VHDL, which is the primary goal of conversion. Now we sometimes don't at time 0, and it's always confusing. > I think the only way to guarantee initial values is the have a set/reset > that is activated and use it in your logic to properly initialize > anything that matters. At the gate level, of course. > Now maybe we are trying to deal with special cases in FPGAs? Such as > initializing memory? I am coming late to this discussion. Me also :-) I would also like to hear a simple explanation of this. -- Jan Decaluwe - Resources bvba - http://www.jandecaluwe.com Python as a HDL: http://www.myhdl.org VHDL development, the modern way: http://www.sigasi.com World-class digital design: http://www.easics.com |
From: Tom D. <td...@di...> - 2012-05-22 13:59:56
|
On 05/22/2012 08:51 AM, Jan Decaluwe wrote: > On 05/22/2012 03:29 PM, Tom Dillon wrote: >> On 05/22/2012 08:12 AM, Jan Decaluwe wrote: >>> On 05/05/2012 05:20 PM, Norbo wrote: >>> >>> >>>> another point is that i think it is a good thing to have all the variables >>>> by default not to be initialized >>>> with values in the converted code. >>> No, they should, for the simple reason that the intent of conversion >>> is to generate Verilog/VHDL which matches the MyHDL behavior exactly. >>> >>> The only reason why they are not written currently (once they were) is >>> a practical one: some synthesis tools didn't support this. But as soon >>> we are sure they all do, I will want to change that. That would solve all >>> kinds of issues at simulation time 0. >> It can be annoying not to have a value at time 0 since you can get some >> warnings but I don't think all synthesis tools will take care of this >> for you. Depending on what you are synthesizing to it might not even be >> possible. > I am not saying synthesis should do anything with these values. It > should just support the syntax. > > What this gives us is that at least in simulation, we would get a full > match between MyHDL and the converted Verilog/VHDL, which is the > primary goal of conversion. Now we sometimes don't at time 0, and it's > always confusing. Yes, that makes good sense. |
From: Christopher F. <chr...@gm...> - 2012-05-22 15:35:00
|
On 5/22/2012 8:51 AM, Jan Decaluwe wrote: > On 05/22/2012 03:29 PM, Tom Dillon wrote: >> On 05/22/2012 08:12 AM, Jan Decaluwe wrote: >>> On 05/05/2012 05:20 PM, Norbo wrote: >>> >>> >>>> another point is that i think it is a good thing to have all the variables >>>> by default not to be initialized >>>> with values in the converted code. >>> No, they should, for the simple reason that the intent of conversion >>> is to generate Verilog/VHDL which matches the MyHDL behavior exactly. >>> >>> The only reason why they are not written currently (once they were) is >>> a practical one: some synthesis tools didn't support this. But as soon >>> we are sure they all do, I will want to change that. That would solve all >>> kinds of issues at simulation time 0. >> >> It can be annoying not to have a value at time 0 since you can get some >> warnings but I don't think all synthesis tools will take care of this >> for you. Depending on what you are synthesizing to it might not even be >> possible. > > I am not saying synthesis should do anything with these values. It > should just support the syntax. > > What this gives us is that at least in simulation, we would get a full > match between MyHDL and the converted Verilog/VHDL, which is the > primary goal of conversion. Now we sometimes don't at time 0, and it's > always confusing. > >> I think the only way to guarantee initial values is the have a set/reset >> that is activated and use it in your logic to properly initialize >> anything that matters. > > At the gate level, of course. > >> Now maybe we are trying to deal with special cases in FPGAs? Such as >> initializing memory? I am coming late to this discussion. > > Me also :-) I would also like to hear a simple explanation of this. > > You had to add the "simple" qualifier :) FPGAs support pre-init RAM. Synthesis will extract the array initial values from the HDL and during configuration program the BRAM (internal FPGA RAM). When the FPGA comes out of reset (not logic reset, FPGA post config reset) the RAM will contain the initial values. The RAM is a RAM so the values can be overridden by the logic, i.e not a ROM. This is only possible in an FPGA and is supported by X&A synthesis tools. I don't know a reasonable method to achieve the pre-init RAM, currently. Theoretically, this would be supported with initial value support; such that the lower HDL matches the MyHDL at time 0. I don't see a reason why the memory initial value shouldn't be synthesizable, as well, if the tools support it. The question is for large memories would you want to be able to disable initial values for the memory array? Would the overhead in simulation and/or synthesis be too costly to always include the memory initial values? And yes, we should have some information on the cost of large memory initial values before proclaiming we need a method to disable. I have not run any experiments or run into this in the past. Anyone have any data points if large pre-init RAM structures are costly in simulation or synthesis? Summary, two items. One synthesizable initial value support (ivs) for RAM memories. Second, does there need to be a method to disable ivs for RAM memories? Regards, Chris |
From: Jan D. <ja...@ja...> - 2012-05-22 21:32:23
|
On 05/22/2012 05:34 PM, Christopher Felton wrote: > > FPGAs support pre-init RAM. Synthesis will extract the array initial > values from the HDL and during configuration program the BRAM (internal > FPGA RAM). When the FPGA comes out of reset (not logic reset, FPGA post > config reset) the RAM will contain the initial values. The RAM is a RAM > so the values can be overridden by the logic, i.e not a ROM. This is > only possible in an FPGA and is supported by X&A synthesis tools. > > I don't know a reasonable method to achieve the pre-init RAM, currently. Do you mean no reasonable method from MyHDL, or no reasonable method at all? And if there is a reasonable method, how does it work? -- Jan Decaluwe - Resources bvba - http://www.jandecaluwe.com Python as a HDL: http://www.myhdl.org VHDL development, the modern way: http://www.sigasi.com World-class digital design: http://www.easics.com |
From: Christopher F. <chr...@gm...> - 2012-05-23 02:37:34
|
On 5/22/12 4:32 PM, Jan Decaluwe wrote: > On 05/22/2012 05:34 PM, Christopher Felton wrote: > >> >> FPGAs support pre-init RAM. Synthesis will extract the array initial >> values from the HDL and during configuration program the BRAM (internal >> FPGA RAM). When the FPGA comes out of reset (not logic reset, FPGA post >> config reset) the RAM will contain the initial values. The RAM is a RAM >> so the values can be overridden by the logic, i.e not a ROM. This is >> only possible in an FPGA and is supported by X&A synthesis tools. >> >> I don't know a reasonable method to achieve the pre-init RAM, currently. > > Do you mean no reasonable method from MyHDL, or no reasonable method > at all? That was somewhat missed phrased, "... reasonable method ...". Currently initial values are not supported (enabled), for basic types or arrays (memories). Currently you cannot do either. I should have said, pre-init RAM is not supported in MyHDL. Known, reasonable, RAM pre-init methods are available for Verilog/VHDL. It seems reasonable to include with the initial value support. Norbo prototyped this and it all seemed good but we have been discussing the overall behavior. I think this is where we sit right now. 1. Initial value support should be added, it resolves the time 0 mismatch. It has been verified with the latest versions of Quartus and ISE. 2. Intial value support for arrays should be included (all types are initialized). Verdict is still out if the ivs for arrays should be "synthesizable". 3. Unresolved, should there be a method to disable ivs. The current proposed method is to use None. > > And if there is a reasonable method, how does it work? > For Verilog it is an initial block, like the following initial begin for (i = 0; i < 32; i = i + 1) mem[i] = i[7:0]; end VHDL it is similar but uses a function, TYPE MEM IS ARRAY(31 DOWNTO 0) OF unsigned(7 DOWNTO 0); FUNCTION initialize_ram return MEM is variable result : MEM; BEGIN FOR i IN 31 DOWNTO 0 LOOP result(i) := to_unsigned(natural(i), natural'(8)); END LOOP; RETURN result; END initialize_ram; What I don't recall (what needs to be verified) if the above "templates" are supported by most synthesis tools, pretty sure simulation will support the above for arrays (memories). Regards, Chris |
From: Christopher F. <chr...@gm...> - 2012-05-23 10:47:11
|
On 5/23/12 4:01 AM, Jan Decaluwe wrote: > On 05/23/2012 04:37 AM, Christopher Felton wrote: >> On 5/22/12 4:32 PM, Jan Decaluwe wrote: >>> On 05/22/2012 05:34 PM, Christopher Felton wrote: >>> >>>> >>>> FPGAs support pre-init RAM. Synthesis will extract the array initial >>>> values from the HDL and during configuration program the BRAM (internal >>>> FPGA RAM). When the FPGA comes out of reset (not logic reset, FPGA post >>>> config reset) the RAM will contain the initial values. The RAM is a RAM >>>> so the values can be overridden by the logic, i.e not a ROM. This is >>>> only possible in an FPGA and is supported by X&A synthesis tools. >>>> >>>> I don't know a reasonable method to achieve the pre-init RAM, currently. >>> >>> Do you mean no reasonable method from MyHDL, or no reasonable method >>> at all? >> >> That was somewhat missed phrased, "... reasonable method ...". >> Currently initial values are not supported (enabled), for basic types or >> arrays (memories). Currently you cannot do either. I should have >> said, pre-init RAM is not supported in MyHDL. >> >> Known, reasonable, RAM pre-init methods are available for Verilog/VHDL. >> It seems reasonable to include with the initial value support. > > Yes. Now, from the altera literature it seems they also support > $readmemb, which would make it possible to initialize a RAM with > arbitrary values. But I don't know how to do that with VHDL. The Verilog initial and the VHDL function just get ugly to the human reader. Nothing says you can explicitly list each array element. This is where the concern to disable arose. initial begin mem[0] = 123; mem[1] = 78; ... mem[31] = 9; end FUNCTION ... BEGIN result(0) := to_unsigned(natural(123), natural'(8) result(1) := to_unsigned(natural(78), natural'(8) ... result(31) := to_unsigned(natural(9), natural'(8) RETURN result; END This is what was prototyped. > > Would it be sufficient to support memory initialization to > an identical value for each location? > >> Norbo prototyped this and it all seemed good but we have been discussing >> the overall behavior. I think this is where we sit right now. >> >> 1. Initial value support should be added, it resolves >> the time 0 mismatch. It has been verified with >> the latest versions of Quartus and ISE. > > Yes. The one implementation detail we ran into, was that the continuous assign statements in Verilog will need to be replaced with an @always block. > >> 2. Intial value support for arrays should be included >> (all types are initialized). Verdict is still out >> if the ivs for arrays should be "synthesizable". > > Yes. > > In summary: I think initial values should be written, regardless of > what synthesis does with it, because of the exact match in > simulation. > > >> 3. Unresolved, should there be a method to disable >> ivs. The current proposed method is to use None. > > As a fallback method, I think it would be wise to be able > to disable initial value writing by a control parameter > to toVerilog/toVHDL. A separate control for plain signals > and arrays probably. But this would be purely because some > synthesis tools may not support the syntax. > > I don't see a value in adding support for None to intbv, > as discussed before. > Sounds fine to me, to disable you would need to explicitly send the argument to the conversion for plain signals and/or for arrays. Regards, Chris |