Exemple #1
0
    def __init__(self, sim_core, name):
        self.sim_core = sim_core
        rfnocsim.SimComp.__init__(self, sim_core, name,
                                  rfnocsim.comptype.hardware)
        # Max resources from Virtex7 datasheet
        self.max_resources = rfnocsim.HwRsrcs()
        self.max_resources.add('DSP', 3600)
        self.max_resources.add('BRAM_18kb', 2940)
        self.resources = rfnocsim.HwRsrcs()
        # Each FPGA has 80 SERDES lanes
        self.max_io = 80
        self.serdes_i = dict()
        self.serdes_o = dict()
        # Each lane can carry at most 10GB/s
        # Each SERDES needs to have some buffering. We assume elastic buffering (50% full on avg).
        io_buff_size = (self.IO_LN_BW *
                        self.IO_LN_LATENCY) / self.ELASTIC_BUFF_FULLNESS
        # Worst case lane latency
        lane_latency = self.IO_LN_LATENCY * self.get_tick_rate()
        for i in range(self.max_io):
            self.serdes_i[i] = rfnocsim.Channel(sim_core,
                                                self.__ioln_name(i) + '/I',
                                                self.IO_LN_BW,
                                                lane_latency / 2)
            self.serdes_o[i] = rfnocsim.Channel(sim_core,
                                                self.__ioln_name(i) + '/O',
                                                self.IO_LN_BW,
                                                lane_latency / 2)
            self.resources.add('BRAM_18kb', 1 + math.ceil(
                io_buff_size / self.BRAM_BYTES))  #input buffering per lane
            self.resources.add('BRAM_18kb', 1)  #output buffering per lane
        # Other resources
        self.resources.add('BRAM_18kb', 72)  # BPS infrastructure + microblaze
        self.resources.add('BRAM_18kb', 128)  # 2 MIGs

        self.functions = dict()
Exemple #2
0
    def config_bitstream(cls, bee7fpga, app_settings, in_chans, out_chans, total_num_chans, is_radio_node):
        if len(in_chans) != 64:
            raise bee7fpga.SimCompError('in_chans must be 64 channels wide. Got ' + str(len(in_chans)))
        if len(out_chans) != 16:
            raise bee7fpga.SimCompError('out_chans must be 16 channels wide. Got ' + str(len(out_chans)))
        GRP_LEN = 16 / 2   # 2 radio channesl per USRP

        # Broadcast raw data streams to all internal and external FPGAs
        for i in range(GRP_LEN):
            in_ln = bee7fpga.EXT_IO_LANES[bee7fpga.BP_BASE+i]
            bee7fpga.sim_core.connect(bee7fpga.serdes_i[in_ln], 0, bee7fpga.serdes_o[bee7fpga.EW_IO_LANES[i]], 0)
            bee7fpga.sim_core.connect(bee7fpga.serdes_i[in_ln], 0, bee7fpga.serdes_o[bee7fpga.NS_IO_LANES[i]], 0)
            bee7fpga.sim_core.connect(bee7fpga.serdes_i[in_ln], 0, bee7fpga.serdes_o[bee7fpga.XX_IO_LANES[i]], 0)
            bee7fpga.sim_core.connect(bee7fpga.serdes_i[in_ln], 0, bee7fpga.serdes_o[bee7fpga.EXT_IO_LANES[bee7fpga.BP_BASE+8+i]], 0)
        # Create an internal bus to hold the generated partial products
        bee7fpga.pp_bus = dict()
        for i in range(GRP_LEN):
            bee7fpga.pp_bus[i] = rfnocsim.Channel(bee7fpga.sim_core, '%s/_INTERNAL_PP_%02d' % (bee7fpga.name,i))
        # We need to compute partial products of the data that is broadcast to us
        # pp_input_lanes represents the IO lanes that hold this data
        pp_input_lanes = bee7fpga.EXT_IO_LANES[bee7fpga.BP_BASE:bee7fpga.BP_BASE+GRP_LEN] + \
            bee7fpga.EW_IO_LANES[0:GRP_LEN] + bee7fpga.NS_IO_LANES[0:GRP_LEN] + bee7fpga.XX_IO_LANES[0:GRP_LEN]
        # The function that computes the partial products
        func = PartialContribComputer(
            sim_core=bee7fpga.sim_core, name=bee7fpga.name + '/pp_computer/', size=len(pp_input_lanes),
            dst_chans=out_chans,
            items_per_stream=2, app_settings=app_settings)
        for i in range(len(pp_input_lanes)):
            bee7fpga.sim_core.connect(bee7fpga.serdes_i[pp_input_lanes[i]], 0, func, i)
        for i in range(GRP_LEN): #Outputs of function
            bee7fpga.sim_core.connect(func, i, bee7fpga.pp_bus[i], 0)
        bee7fpga.add_function(func)
        # Add a function combine all partial products (one per IO lane)
        for i in range(GRP_LEN):
            func = PartialContribCombiner(
                sim_core=bee7fpga.sim_core, name=bee7fpga.name + '/pp_combiner_%d/' % (i),
                radix=2, app_settings=app_settings, reducer_filter=(list(range(total_num_chans)), 'tx'))
            # Partial products generated internally have to be added to a partial
            # sum coming from outside
            bee7fpga.sim_core.connect(bee7fpga.serdes_i[bee7fpga.EXT_IO_LANES[bee7fpga.FP_BASE+i]], 0, func, 0)
            bee7fpga.sim_core.connect(bee7fpga.pp_bus[i], 0, func, 1)
            # If this FPGA is hooked up to the radio then send partial products
            # back to when samples came from. Otherwise send it out to the PP output bus
            if is_radio_node:
                bee7fpga.sim_core.connect(func, 0, bee7fpga.serdes_o[bee7fpga.EXT_IO_LANES[bee7fpga.BP_BASE+i]], 0)
            else:
                bee7fpga.sim_core.connect(func, 0, bee7fpga.serdes_o[bee7fpga.EXT_IO_LANES[bee7fpga.FP_BASE+8+i]], 0)
            bee7fpga.add_function(func)
Exemple #3
0
    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)