Source code for yellow_blocks.snap_adc

from .yellow_block import YellowBlock
from verilog import VerilogModule
from constraints import PortConstraint, ClockConstraint, RawConstraint
from .yellow_block_typecodes import *
import math, numpy as np

[docs]class snap_adc(YellowBlock):
[docs] def initialize(self): self.typecode = TYPECODE_ADC16CTRL # num_units is the number of ADC chips # board_count is the number of boards self.num_units = 3 self.num_clocks = 1 self.zdok_rev = 2 # no frame clocks (see adc16) self.n_inputs = self.snap_inputs // 3 #number of inputs per chip # self.adc_resolution, possible values are 8, 10, 12, 14, 16 # Currently only 8, 12, 16 are supported if self.adc_resolution <=8: self.adc_data_width = 8 elif self.adc_resolution >8 and self.adc_resolution<=16: self.adc_data_width = 16 else: self.adc_data_width = 8 self.LOG_USER_WIDTH = int(math.log(self.adc_data_width*4,2)) # An HMCAD1511 has 8 ADC cores and DDR transmission self.line_clock_freq = self.sample_rate/(8.0/self.n_inputs)*self.adc_resolution/2.0 self.add_source('adc16_interface') self.add_source('wb_adc16_controller') self.add_source('wb_bram') self.provides = ['adc0_clk','adc0_clk90', 'adc0_clk180', 'adc0_clk270'] self.requires = ['HAD1511_0', 'HAD1511_1', 'HAD1511_2']
[docs] def gen_children(self): """ The first instance of this adc adds the required clock controller module """ if self.i_am_the_first: lmx = YellowBlock.make_block({'tag':'xps:lmx2581', 'fullpath':'%s/lmx2581_from'%self.name, 'name':'lmx2581'}, self.platform) swreg = YellowBlock.make_block({'tag':'xps:sw_reg_sync', 'fullpath':'%s/adc16_use_synth'%self.name, 'io_dir':'From Processor', 'name':'adc16_use_synth'}, self.platform) return [lmx, swreg] else: return []
[docs] def modify_top(self,top): module = 'adc16_interface' inst = top.get_instance(entity=module, name=self.fullname) inst.add_parameter('G_NUM_CLOCKS', int(self.num_clocks)) inst.add_parameter('G_ZDOK_REV', int(self.zdok_rev)) inst.add_parameter('G_NUM_UNITS', int(self.num_units)) inst.add_parameter('ADC_RESOLUTION', int(self.adc_resolution)) inst.add_parameter('ADC_DATA_WIDTH', int(self.adc_data_width)) # ports which go to simulink inst.add_port('a1', self.fullname+'_a1', width=self.adc_data_width) inst.add_port('a2', self.fullname+'_a2', width=self.adc_data_width) inst.add_port('a3', self.fullname+'_a3', width=self.adc_data_width) inst.add_port('a4', self.fullname+'_a4', width=self.adc_data_width) inst.add_port('b1', self.fullname+'_b1', width=self.adc_data_width) inst.add_port('b2', self.fullname+'_b2', width=self.adc_data_width) inst.add_port('b3', self.fullname+'_b3', width=self.adc_data_width) inst.add_port('b4', self.fullname+'_b4', width=self.adc_data_width) inst.add_port('c1', self.fullname+'_c1', width=self.adc_data_width) inst.add_port('c2', self.fullname+'_c2', width=self.adc_data_width) inst.add_port('c3', self.fullname+'_c3', width=self.adc_data_width) inst.add_port('c4', self.fullname+'_c4', width=self.adc_data_width) # ports which go to the wb controller. Any ports which don't go to top level need # corresponding signals to be added to top.v. **Not anymore, this is now default behaviour!** inst.add_port('fabric_clk', 'adc0_clk') inst.add_port('fabric_clk_90', 'adc0_clk90') inst.add_port('fabric_clk_180', 'adc0_clk180') inst.add_port('fabric_clk_270', 'adc0_clk270') inst.add_port('reset', 'adc16_reset') inst.add_port('iserdes_bitslip', 'adc16_iserdes_bitslip', width=64) inst.add_port('delay_rst', 'adc16_delay_rst', width=64) inst.add_port('delay_tap', 'adc16_delay_tap', width=5) inst.add_port('snap_req', 'adc16_snap_req') inst.add_port('snap_we', 'adc16_snap_we') inst.add_port('snap_addr', 'adc16_snap_addr', width=10) inst.add_port('locked', 'adc16_locked', width=2) inst.add_port('demux_mode', 'adc16_demux_mode', width=2) # Now the external ports, which need corresponding ports adding to top.v inst.add_port('clk_frame_p', '0', parent_sig=False) inst.add_port('clk_frame_n', '0', parent_sig=False) inst.add_port('clk_line_p', 'adc16_clk_line_p', parent_port=True, dir='in', width=self.num_clocks) inst.add_port('clk_line_n', 'adc16_clk_line_n', parent_port=True, dir='in', width=self.num_clocks) inst.add_port('ser_a_p', 'adc16_ser_a_p', parent_port=True, dir='in', width=4*self.num_units) inst.add_port('ser_a_n', 'adc16_ser_a_n', parent_port=True, dir='in', width=4*self.num_units) inst.add_port('ser_b_p', 'adc16_ser_b_p', parent_port=True, dir='in', width=4*self.num_units) inst.add_port('ser_b_n', 'adc16_ser_b_n', parent_port=True, dir='in', width=4*self.num_units) # Clock switch. if self.i_am_the_first: top.add_port('clk_sel_a', dir='out', width=1) top.add_port('clk_sel_b', dir='out', width=1) top.assign_signal('clk_sel_a[0]', "%s_adc16_use_synth_user_data_out[0]"%self.name) top.assign_signal('clk_sel_b[0]', "~%s_adc16_use_synth_user_data_out[0]"%self.name) # ADC Power down and reset signals are wired to the FPGA, but hardwire them to match the adc16 card top.add_port('adc_rst_n', dir='out', width=3) top.add_port('adc_pd', dir='out', width=3) top.assign_signal('adc_rst_n', "3'b111") top.assign_signal('adc_pd', "3'b000") # wb controller wbctrl = top.get_instance(entity='wb_adc16_controller', name='wb_adc16_controller') wbctrl.add_parameter('G_ROACH2_REV', 0) wbctrl.add_parameter('G_ZDOK_REV', int(self.zdok_rev)) wbctrl.add_parameter('G_NUM_UNITS', int(self.num_units)) wbctrl.add_parameter('G_NUM_SCLK_LINES', 3) wbctrl.add_parameter('G_NUM_SDATA_LINES', 3) # These are top-level ports -- they don't need signal declarations, # but they do need ports added to the top-level wbctrl.add_port('adc0_adc3wire_csn1', 'adc0_adc3wire_csn1', dir='out', parent_port=True) wbctrl.add_port('adc0_adc3wire_csn2', 'adc0_adc3wire_csn2', dir='out', parent_port=True) wbctrl.add_port('adc0_adc3wire_csn3', 'adc0_adc3wire_csn3', dir='out', parent_port=True) wbctrl.add_port('adc0_adc3wire_csn4', '') wbctrl.add_port('adc0_adc3wire_sdata','adc0_adc3wire_sdata', width=3, dir='out', parent_port=True) wbctrl.add_port('adc0_adc3wire_sclk', 'adc0_adc3wire_sclk', width=3, dir='out', parent_port=True) wbctrl.add_port('adc1_adc3wire_csn1', '') wbctrl.add_port('adc1_adc3wire_csn2', '') wbctrl.add_port('adc1_adc3wire_csn3', '') wbctrl.add_port('adc1_adc3wire_csn4', '') wbctrl.add_port('adc1_adc3wire_sdata','') wbctrl.add_port('adc1_adc3wire_sclk', '') # internal connections to the adc controller. We have already declared the corresponding # signals earlier. wbctrl.add_port('adc16_reset','adc16_reset') wbctrl.add_port('adc16_iserdes_bitslip','adc16_iserdes_bitslip', width=64) wbctrl.add_port('adc16_delay_rst', 'adc16_delay_rst', width=64) wbctrl.add_port('adc16_delay_tap', 'adc16_delay_tap', width=5) wbctrl.add_port('adc16_snap_req', 'adc16_snap_req') wbctrl.add_port('adc16_locked', 'adc16_locked', width=2) wbctrl.add_port('adc16_demux_mode', 'adc16_demux_mode', width=2) # and finally the wb interface wbctrl.add_wb_interface(nbytes=2**8, regname='adc16_controller', mode='rw', typecode=self.typecode) snap_chan = ['a','b','c','d','e','f','g','h'] for k in range(self.num_units): # Embedded wb-RAM din = self.fullname+'_%s'%snap_chan[k] wbram = top.get_instance(entity='wb_bram', name='adc16_wb_ram%d'%k, comment='Embedded ADC16 bram') wbram.add_parameter('LOG_USER_WIDTH',self.LOG_USER_WIDTH) wbram.add_parameter('USER_ADDR_BITS','10') wbram.add_parameter('N_REGISTERS','2') wbram.add_wb_interface(regname='adc16_wb_ram%d'%k, mode='rw', nbytes=(self.adc_data_width//8)*4*2**10, typecode=TYPECODE_SWREG) wbram.add_port('user_clk','adc0_clk', parent_sig=False) wbram.add_port('user_addr','adc16_snap_addr', width=10) #wbram.add_port('user_din','{%s1, %s2, %s3, %s4}'%(din,din,din,din), parent_sig=False) wbram.add_port('user_din',self.reorder_ports([din+'1',din+'2',din+'3',din+'4']), parent_sig=False) wbram.add_port('user_we','adc16_snap_we') wbram.add_port('user_dout','')
[docs] def reorder_ports(self,port_list, wb_bitwidth=32): """ Reorder output ports of ADCs to arrange sampling data in correct order in wb_bram wb_bitwidth stands for the bit width of data in/out port of wishbone bus .. code-block:: python reorder_ports(['a1','a2','a3','a4']) when self.adc_data_width == 8, return {a1,a2,a3,a4} when self.adc_data_width == 16, return {a3,a4,a1,a2} when self.adc_data_width == 32, return {a4,a3,a2,a1} """ if not isinstance(port_list,list): raise ValueError("Parameter error") elif any([not isinstance(port,str) for port in port_list]): raise ValueError("Parameter error") r = wb_bitwidth // self.adc_data_width port_list = np.array(port_list).reshape(-1, r) port_list = port_list[::-1,:].reshape(-1).tolist() return '{' + ','.join(port_list) + '}'
[docs] def gen_constraints(self): cons = [] # ADC SPI interface cons.append(PortConstraint('adc0_adc3wire_csn1', 'adc_csn', iogroup_index=0)) cons.append(PortConstraint('adc0_adc3wire_csn2', 'adc_csn', iogroup_index=1)) cons.append(PortConstraint('adc0_adc3wire_csn3', 'adc_csn', iogroup_index=2)) cons.append(PortConstraint('adc0_adc3wire_sdata', 'adc_sdata', port_index=list(range(3)), iogroup_index=list(range(3)))) cons.append(PortConstraint('adc0_adc3wire_sclk', 'adc_sclk', port_index=list(range(3)), iogroup_index=list(range(3)))) cons.append(PortConstraint('adc16_clk_line_p', 'adc_lclkp', iogroup_index=0)) cons.append(PortConstraint('adc16_clk_line_n', 'adc_lclkn', iogroup_index=0)) # in 4 channel mode, the adc controller demuxes each a-b pair to make a stream # in 2 channel mode, the demux order should be a[2*n],a[2*n+1],b[2*n],b[2*n+1] for n in range(2) # in 1 channel mode, the demux order should be a[0],a[2],b[0],b[2],a[1],a[3],b[1],b[3] # In the board description file, adcX_out is a 16 element vector with a_p[0], a_n[0], b_p[0], b_n[0], a_p[1], ... b_n[15] # This is now configured dynamically in the gateware #if self.n_inputs == 4: # ap_index = [0, 4, 8, 12] # an_index = [1, 5, 9, 13] # bp_index = [2, 6, 10, 14] # bn_index = [3, 7, 11, 15] #elif self.n_inputs == 2: # ap_index = [0, 2, 8, 10] # an_index = [1, 3, 9, 11] # bp_index = [4, 6, 12, 14] # bn_index = [5, 7, 13, 15] #elif self.n_inputs == 1: # ap_index = [0, 2, 4, 6] # an_index = [1, 3, 5, 7] # bp_index = [8, 10, 12, 14] # bn_index = [9, 11, 13, 15] # ap_index = [0, 4, 8, 12] an_index = [1, 5, 9, 13] bp_index = [2, 6, 10, 14] bn_index = [3, 7, 11, 15] cons.append(PortConstraint('adc16_ser_a_p', 'adc0_out', port_index=list(range(4)), iogroup_index=ap_index)) cons.append(PortConstraint('adc16_ser_a_n', 'adc0_out', port_index=list(range(4)), iogroup_index=an_index)) cons.append(PortConstraint('adc16_ser_b_p', 'adc0_out', port_index=list(range(4)), iogroup_index=bp_index)) cons.append(PortConstraint('adc16_ser_b_n', 'adc0_out', port_index=list(range(4)), iogroup_index=bn_index)) cons.append(PortConstraint('adc16_ser_a_p', 'adc1_out', port_index=list(range(4,8)), iogroup_index=ap_index)) cons.append(PortConstraint('adc16_ser_a_n', 'adc1_out', port_index=list(range(4,8)), iogroup_index=an_index)) cons.append(PortConstraint('adc16_ser_b_p', 'adc1_out', port_index=list(range(4,8)), iogroup_index=bp_index)) cons.append(PortConstraint('adc16_ser_b_n', 'adc1_out', port_index=list(range(4,8)), iogroup_index=bn_index)) cons.append(PortConstraint('adc16_ser_a_p', 'adc2_out', port_index=list(range(8,12)), iogroup_index=ap_index)) cons.append(PortConstraint('adc16_ser_a_n', 'adc2_out', port_index=list(range(8,12)), iogroup_index=an_index)) cons.append(PortConstraint('adc16_ser_b_p', 'adc2_out', port_index=list(range(8,12)), iogroup_index=bp_index)) cons.append(PortConstraint('adc16_ser_b_n', 'adc2_out', port_index=list(range(8,12)), iogroup_index=bn_index)) if self.i_am_the_first: cons.append(PortConstraint('clk_sel_a', 'clk_sel_a', port_index=list(range(1)), iogroup_index=list(range(1)))) cons.append(PortConstraint('clk_sel_b', 'clk_sel_b', port_index=list(range(1)), iogroup_index=list(range(1)))) cons.append(PortConstraint('adc_rst_n', 'adc_rst_n', port_index=list(range(3)), iogroup_index=list(range(3)))) cons.append(PortConstraint('adc_pd', 'adc_pd', port_index=list(range(3)), iogroup_index=list(range(3)))) # clock constraint with variable period clkconst = ClockConstraint('adc16_clk_line_p', name='adc_clk', freq=self.line_clock_freq) cons.append(clkconst) cons.append(RawConstraint('set_clock_groups -name async_sysclk_adcclk -asynchronous -group [get_clocks -include_generated_clocks %s] -group [get_clocks -include_generated_clocks sys_clk0_dcm]' % clkconst.name)) cons.append(RawConstraint('set_multicycle_path -from [get_clocks -include_generated_clocks %s] -to [get_clocks -include_generated_clocks sys_clk0_dcm] 3' % clkconst.name)) cons.append(RawConstraint('set_multicycle_path -from [get_clocks -include_generated_clocks %s] -to [get_clocks -include_generated_clocks sys_clk0_dcm] -hold 2' % clkconst.name)) return cons