def __init__(self, sim_core, index, app_settings): rfnocsim.SimComp.__init__(self, sim_core, name='USRP_%03d' % (index), ctype=rfnocsim.comptype.hardware) # USRP i carries data for radio 2i and 2i+1 interleaved into one stream self.index = index items = [ rfnocsim.DataStream.submatrix_gen('rx', [2 * index]), rfnocsim.DataStream.submatrix_gen('rx', [2 * index + 1]) ] # Samples are 4 bytes I and Q latency = (self.RADIO_LATENCY + self.IO_LATENCY / 2) * self.get_tick_rate() if app_settings['domain'] == 'frequency': # Max latency per direction depends on the FFT size and sample rate latency += self.__get_fft_latency(app_settings['fft_size'], app_settings['samp_rate'], self.get_tick_rate()) # An X310 Radio has two producers (RX data) and consumers (TX data) (i.e. two ethernet ports) # Both ports can carry data from both radio frontends self.sources = ([ rfnocsim.Producer(sim_core, self.name + '/TX0', self.BPI, items, self.MAX_SAMP_RATE, latency), rfnocsim.Producer(sim_core, self.name + '/TX1', self.BPI, items, self.MAX_SAMP_RATE, latency) ]) self.sinks = ([ rfnocsim.Consumer(sim_core, self.name + '/RX0', self.BPI * self.MAX_SAMP_RATE, latency), rfnocsim.Consumer(sim_core, self.name + '/RX1', self.BPI * self.MAX_SAMP_RATE, latency) ]) # The actual sample rate depends over the wire depends on the radio sample rate, # the FFT size and FFT overlap for src in self.sources: if app_settings['domain'] == 'frequency': src.set_rate(app_settings['samp_rate'] * (1.0 + (float(app_settings['fft_overlap']) / app_settings['fft_size']))) else: src.set_rate(app_settings['samp_rate'])
def __init__(self, sim_core, index, num_coeffs, switch_ports, app_settings): rfnocsim.SimComp.__init__(self, sim_core, name='MGMT_HOST_%03d' % (index), ctype=rfnocsim.comptype.other) if app_settings['domain'] == 'frequency': k = app_settings['fft_size'] else: k = app_settings['fir_taps'] self.sources = dict() self.sinks = dict() for l in range(switch_ports): self.sources[l] = rfnocsim.Producer( sim_core, '%s/COEFF_%d' % (self.name, l), 4, ['coeff_%03d[%d]' % (index, l)], (10e9 / 8) / switch_ports, 0) self.sinks[l] = rfnocsim.Consumer( sim_core, self.name + '%s/ACK%d' % (self.name, l)) self.sources[l].set_rate(k * num_coeffs * app_settings['coherence_rate'])
def config_bitstream(cls, bee7fpga, app_settings, fpga_addr): """ Defines the FPGA behavior for the current FPGA. This function will make create the necessary simulation functions, connect them to IO lanes and define the various utilization metrics for the image. config_bitstream(bee7fpga, app_settings, fpga_addr): - bee7fpga: The FPGA simulation object being configured - fpga_addr: Address of the FPGA in 3-D space - app_settings: Application information """ if len(fpga_addr) != 3: raise bee7fpga.SimCompError('fpga_addr must be 3-dimensional. Got ' + str(len(fpga_addr))) # Map that stores lane indices for all neighbors of this node (router_map, terminal_map) = cls.get_portmap(fpga_addr) # USRPs are connected in the X dimension (RTM) because it has SFP+ ports base_usrp_lane = terminal_map['X'] DIM_WIDTH = 4 # Dimension size for the 3-D network MAX_USRPS = 4 # Max USRPs that can possibly be connected to each FPGA NUM_USRPS = 2 # Number of USRPs actually connected to each FPGA CHANS_PER_USRP = 2 # How many radio channels does each USRP have ALL_CHANS = list(range(pow(DIM_WIDTH, 3) * NUM_USRPS * CHANS_PER_USRP)) # Each FPGA will forward the sample stream from each USRP to all of its # X-axis neighbors for ri in router_map['X']: for li in range(MAX_USRPS): # li = GT Lane index bee7fpga.sim_core.connect(bee7fpga.serdes_i[base_usrp_lane + li], 0, bee7fpga.serdes_o[router_map['X'][ri] + li], 0) # Consequently, this FPGA will receive the USRP sample streams from each of # its X-axis neighbors. Define an internal bus to aggregate all the neighbor # streams with the native ones. Order the streams such that each FPGA sees the # same data streams. bee7fpga.int_samp_bus = dict() for i in range(DIM_WIDTH): for li in range(MAX_USRPS): # li = GT Lane index bee7fpga.int_samp_bus[(MAX_USRPS*i) + li] = rfnocsim.Channel( bee7fpga.sim_core, '%s/_INT_SAMP_%02d' % (bee7fpga.name,(MAX_USRPS*i) + li)) ln_base = base_usrp_lane if i == fpga_addr['X'] else router_map['X'][i] bee7fpga.sim_core.connect(bee7fpga.serdes_i[ln_base + li], 0, bee7fpga.int_samp_bus[(MAX_USRPS*i) + li], 0) # Forward the X-axis aggregated sample streams to all Y-axis neighbors for ri in router_map['Y']: for li in range(DIM_WIDTH*DIM_WIDTH): # li = GT Lane index bee7fpga.sim_core.connect(bee7fpga.int_samp_bus[li], 0, bee7fpga.serdes_o[router_map['Y'][ri] + li], 0) # What partial products will this FPGA compute? # Generate channel list to compute partial products pp_chans = list() for cg in range(DIM_WIDTH): # cg = Channel group for r in range(NUM_USRPS): radio_num = cls.get_radio_num({'X':fpga_addr['X'], 'Y':fpga_addr['Y'], 'Z':cg}, r, NUM_USRPS) for ch in range(CHANS_PER_USRP): pp_chans.append(radio_num*CHANS_PER_USRP + ch) # Instantiate partial product computer bee7fpga.func_pp_comp = PartialContribComputer( sim_core=bee7fpga.sim_core, name=bee7fpga.name+'/pp_computer/', size=DIM_WIDTH*DIM_WIDTH*NUM_USRPS, dst_chans=pp_chans, items_per_stream=CHANS_PER_USRP, app_settings=app_settings) bee7fpga.add_function(bee7fpga.func_pp_comp) # Partial product computer takes inputs from all Y-axis links for sg in range(DIM_WIDTH): # sg = Group of sexdectects for qi in range(DIM_WIDTH): # qi = GT Quad index for li in range(NUM_USRPS): func_inln = (sg * DIM_WIDTH * NUM_USRPS) + (qi * NUM_USRPS) + li if sg == fpga_addr['Y']: bee7fpga.sim_core.connect(bee7fpga.int_samp_bus[(qi * DIM_WIDTH) + li], 0, bee7fpga.func_pp_comp, func_inln) else: bee7fpga.sim_core.connect(bee7fpga.serdes_i[router_map['Y'][sg] + (qi * DIM_WIDTH) + li], 0, bee7fpga.func_pp_comp, func_inln) # Internal bus to hold aggregated partial products bee7fpga.pp_bus = dict() for i in range(DIM_WIDTH*NUM_USRPS): bee7fpga.pp_bus[i] = rfnocsim.Channel(bee7fpga.sim_core, '%s/_INT_PP_%02d' % (bee7fpga.name,i)) bee7fpga.sim_core.connect(bee7fpga.func_pp_comp, i, bee7fpga.pp_bus[i], 0) # Forward partial products to Z-axis neighbors for ri in router_map['Z']: for li in range(NUM_USRPS): # li = GT Lane index bee7fpga.sim_core.connect(bee7fpga.pp_bus[ri*NUM_USRPS + li], 0, bee7fpga.serdes_o[router_map['Z'][ri] + li], 0) # Instantiate partial product adder bee7fpga.func_pp_comb = dict() for i in range(NUM_USRPS): bee7fpga.func_pp_comb[i] = PartialContribCombiner( sim_core=bee7fpga.sim_core, name=bee7fpga.name + '/pp_combiner_%d/'%(i), radix=DIM_WIDTH, app_settings=app_settings, reducer_filter=(ALL_CHANS, 'tx'), items_per_stream=CHANS_PER_USRP) bee7fpga.add_function(bee7fpga.func_pp_comb[i]) # Aggregate partial products from Z-axis neighbors for u in range(NUM_USRPS): for ri in range(DIM_WIDTH): if ri in router_map['Z']: bee7fpga.sim_core.connect(bee7fpga.serdes_i[router_map['Z'][ri] + u], 0, bee7fpga.func_pp_comb[u], ri) else: bee7fpga.sim_core.connect(bee7fpga.pp_bus[ri*NUM_USRPS + u], 0, bee7fpga.func_pp_comb[u], ri) # Instantiate partial product adder for u in range(NUM_USRPS): bee7fpga.sim_core.connect(bee7fpga.func_pp_comb[u], 0, bee7fpga.serdes_o[base_usrp_lane + u], 0) # Coefficient consumer bee7fpga.coeff_sink = rfnocsim.Consumer(bee7fpga.sim_core, bee7fpga.name + '/coeff_sink', 10e9/8, 0.0) bee7fpga.sim_core.connect(bee7fpga.serdes_i[terminal_map['X'] + NUM_USRPS], 0, bee7fpga.coeff_sink, 0)