Exemple #1
0
 def start(self, tag, attrs, nsmap=None):
     if self.filectx is None:
         self.filectx = self.xmlfile.__enter__(
         )  # we have to use context manager manually
     while tag == 'block' and len(self.stack) == 1:
         mapped = io_bindings.get(attrs['name'])
         if mapped is None:
             break
         x, y, subblock = mapped
         tile = get_hierarchical_tile(self.context.top, (x, y))
         if tile is None:
             break
         tile = tile[-1].model
         if not tile.block.module_class.is_io_block or subblock < 0 or subblock >= tile.block.capacity:
             raise PRGAAPIError(
                 "No IO block found at position ({}, {}, {})".format(
                     x, y, subblock))
         matched = _reprog_instance.match(attrs['instance'])
         old_tile = context.tiles[matched.group('name')]
         if old_tile.block is not tile.block:
             raise PRGAAPIError(
                 "Tile '{}' at position ({}, {}) is not compatible with tile '{}'"
                 .format(tile.name, x, y, old_tile.name))
         attrs['instance'] = tile.name + matched.group('index')
         self.remapping = (old_tile.name, tile.name)
         break
     ctx = self.filectx.element(tag, attrs)
     self.stack.append((tag, ctx))
     ctx.__enter__()
Exemple #2
0
    def create_global(self,
                      name,
                      width=1,
                      is_clock=False,
                      bind_to_position=None,
                      bind_to_subblock=None):
        """Create a global wire.

        Args:
            name (:obj:`str`): Name of the global wire
            width (:obj:`int`): Number of bits in the global wire
            is_clock (:obj:`bool`): If this global wire is a clock. A global clock must be 1-bit wide
            bind_to_position (:obj:`Position`): Assign the IOB at the position as the driver of this global wire. If
                not specified, use `Global.bind` to bind later
            bind_to_subblock (:obj:`int`): Assign the IOB with the sub-block ID as the driver of this global wire. If
                ``bind_to_position`` is specified, ``bind_to_subblock`` is ``0`` by default

        Returns:
            `Global`: The created global wire
        """
        if name in self._globals:
            raise PRGAAPIError(
                "Global wire named '{}' is already created".format(name))
        elif width != 1:
            raise PRGAAPIError(
                "Only 1-bit wide global wires are supported now")
        global_ = self._globals.setdefault(name, Global(name, width, is_clock))
        if bind_to_position is not None:
            global_.bind(bind_to_position, uno(bind_to_subblock, 0))
        return global_
Exemple #3
0
def find_verilog_top(files, top = None):
    """Find and parse the top-level module in a list of Verilog files.

    Args:
        files (:obj:`Sequence` [:obj:`str` ]): Verilog files
        top (:obj:`str`): the name of the top-level module if there are more than one modules in the Verilog
            files

    Returns:
        `VerilogModule`:
    """
    mods = {x.name : x for f in files for x in Vex().extract_objects(f)}
    mod = next(iter(itervalues(mods)))
    if len(mods) > 1:
        if top is not None:
            try:
                mod = mods[top]
            except KeyError:
                raise PRGAAPIError("Module '{}' is not found in the file(s)")
        else:
            raise PRGAAPIError('Multiple modules found in the file(s) but no top is specified')
    ports = {}
    for port in mod.ports:
        matched = _reprog_width.match(port.data_type)
        direction = port.mode.strip()
        if direction == 'input':
            direction = PortDirection.input_
        elif direction == 'output':
            direction = PortDirection.output
        else:
            raise PRGAAPIError("Unknown port direction '{}'".format(direction))
        low, high = None, None
        matched = _reprog_width.match(port.data_type)
        if matched is not None:
            start, end = map(int, matched.group('start', 'end'))
            if start > end:
                low, high = end, start + 1
            elif end > start:
                low, high = start, end + 1
            else:
                low, high = start, start + 1
        ports[port.name] = VerilogPort(port.name, direction, low, high)
    return VerilogModule(mod.name, ports)
Exemple #4
0
    def create_cluster(self, name):
        """Create a cluster.

        Args:
            name (:obj:`str`): Name of the cluster

        Returns:
            `Cluster`: The created cluster
        """
        if name in self._modules:
            raise PRGAAPIError("Module '{}' is already created".format(name))
        return self._modules.setdefault(name, Cluster(name))
Exemple #5
0
    def create_io_block(self, name, capacity, input_=True, output=True):
        """Create an IO block.

        Args:
            name (:obj:`str`): Name of the IO block
            capacity (:obj:`int`): Number of block instances in one tile
            input_ (:obj:`bool`): If set, the IOB can be used as an external input
            output (:obj:`bool`): If set, the IOB can be used as an external output

        Returns:
            `IOBlock`: The created IO block
        """
        if name in self._modules:
            raise PRGAAPIError("Module '{}' is already created".format(name))
        io_primitive = ((self.primitives['iopad'] if output else
                         self.primitives['inpad']) if input_ else
                        (self.primitives['outpad'] if output else None))
        if io_primitive is None:
            raise PRGAAPIError(
                "At least one of 'input' and 'output' must be True")
        return self._modules.setdefault(name,
                                        IOBlock(name, io_primitive, capacity))
Exemple #6
0
    def create_direct_tunnel(self, name, source, sink, offset):
        """Create a direct inter-block tunnel.

        Args:
            name (:obj:`str`): Name of this direct inter-block tunnel
            source (`AbstractBlockPort`): Source of the tunnel
            sink (`AbstractBlockPort`): Sink of the tunnel
            offset (:obj:`tuple` [:obj:`int`, :obj:`int` ]): Position of the source block relative to the sink block.
                This definition is the opposite of how VPR defines a ``<direct>`` tag. In addition, ``offset`` is
                defined based on the position of the blocks, not the ports
        """
        if name in self._directs:
            raise PRGAAPIError(
                "Direct inter-block tunnel named '{}' is already created".
                format(name))
        elif not (source.parent.module_class.is_logic_block
                  and sink.parent.module_class.is_logic_block):
            raise PRGAAPIError(
                "Direct inter-block tunnel can only be created between logic block ports"
            )
        return self._directs.setdefault(
            name, DirectTunnel(name, source, sink, Position(*offset)))
Exemple #7
0
    def create_tile(self, name, block, orientation=Orientation.auto):
        """Create a tile.

        Args:
            name (:obj:`str`): Name of the tile
            block (`IOBlock` or `LogicBlock`): Block in this tile
            orientation (`Orientation`): On which side of the top-level array should the tile be placed on. Required
                and only used if ``block`` is an `IOBlock`

        Returns:
            `Tile`: The created tile

        Note:
            Sadly, unlike VPR, each tile must have a unique name DIFFERENT from the name of the block
        """
        if name in self._modules:
            raise PRGAAPIError("Module '{}' is already created".format(name))
        elif block.module_class.is_io_block and orientation in (
                None, Orientation.auto):
            raise PRGAAPIError(
                "Non-auto 'orientation' are required since '{}' is an IO block"
                .format(block))
        return self._modules.setdefault(name, Tile(name, block, orientation))
Exemple #8
0
    def create_logic_block(self, name, width=1, height=1):
        """Create a logic block.

        Args:
            name (:obj:`str`): Name of the logic block
            width (:obj:`int`): Width of the logic block in terms of tiles
            height (:obj:`int`): Height of the logic block in terms of tiles

        Returns:
            `LogicBlock`: The created logic block
        """
        if name in self._modules:
            raise PRGAAPIError("Module '{}' is already created".format(name))
        return self._modules.setdefault(name, LogicBlock(name, width, height))
Exemple #9
0
    def create_segment(self, name, width, length):
        """Create a wire segment prototype.

        Args:
            name (:obj:`str`): Name of the segment
            width (:obj:`int`): Number of wire segments per routing channel, per direction. If the wire segments are
                longer than 1 tile, this is the number of wire segments originated from the channel
            length (:obj:`int`): Length of the wire segments, in terms of tiles

        Returns:
            `Segment`: The created segment prototype
        """
        if name in self._segments:
            raise PRGAAPIError(
                "Wire segment named '{}' is already created".format(name))
        return self._segments.setdefault(name, Segment(name, width, length))
Exemple #10
0
    def create_array(self,
                     name,
                     width,
                     height,
                     coverage=ChannelCoverage(),
                     inner_coverage=ChannelCoverage(True, True, True, True)):
        """Create a (sub-)array.

        Args:
            name (:obj:`str`): Name of the array
            width (:obj:`int`): Number of tiles in the X axis
            height (:obj:`int`): Number of tiles in the Y axis
            coverage (`ChannelCoverage`): Coverage of routing channels surrounding the array. No routing channels are
                covered by default
            inner_coverage (`ChannelCoverage`): Coverage of the channels on the edges of this array. All edges are
                covered by default

        Returns:
            `Array`: The created array
        """
        if name in self._modules:
            raise PRGAAPIError("Module '{}' is already created".format(name))
        return self._modules.setdefault(
            name, Array(name, width, height, coverage, inner_coverage))
Exemple #11
0
def iobind(context, mod_top, fixed = None):
    """Generate IO assignment.

    Args:
        context (`ArchitectureContext`): The architecture context of the custom FPGA
        mod_top (`VerilogModule`): Top-level module of target design
        fixed (:obj:`Mapping` [:obj:`str`, :obj:`tuple` [:obj:`int`, :obj:`int`, :obj:`int` ]]): Manually assigned IOs
    """
    # prepare assignment map
    assignments = [[([], []) for _0 in range(context.top.height)] for _1 in range(context.top.width)]
    for x, y in product(range(context.top.width), range(context.top.height)):
        tile = get_hierarchical_tile(context.top, (x, y))
        if tile is not None and hierarchical_position(tile) == (x, y):
            tile = tile[-1].model
            if tile.block.module_class.is_io_block:
                i, o = map(tile.block.physical_ports.get, ("exti", "exto"))
                if i is not None:
                    i = [None] * tile.block.capacity
                else:
                    i = []
                if o is not None:
                    o = [None] * tile.block.capacity
                else:
                    o = []
                assignments[x][y] = i, o
    # process fixed assignments
    processed = {}
    for name, (x, y, subblock) in iteritems(uno(fixed, {})):
        direction = PortDirection.input_
        if name.startswith('out:'):
            name = name[4:]
            direction = PortDirection.output
        matched = _reprog_bit.match(name)
        port_name, index = matched.group('name', 'index')
        index = None if index is None else int(index)
        port = mod_top.ports.get(port_name)
        if port is None:
            raise PRGAAPIError("Port '{}' not found in module '{}'"
                    .format(port_name, mod_top.name))
        elif port.direction is not direction:
            raise PRGAAPIError("Direction mismatch: port '{}' is {} in behavioral model but {} in IO bindings"
                    .format(port_name, port.direction.name, direction))
        elif index is None and port.low is not None:
            raise PRGAAPIError("Port '{}' is a bus and requires an index"
                    .format(port_name))
        elif index is not None and (port.low is None or index < port.low or index >= port.high):
            raise PRGAAPIError("Bit index '{}' is not in port '{}'"
                    .format(index, port_name))
        try:
            if assignments[x][y][0][subblock] is not None or assignments[x][y][1][subblock] is not None:
                raise PRGAAPIError("Conflicting assignment at ({}, {}, {})"
                        .format(x, y, subblock))
            assignments[x][y][port.direction.case(0, 1)][subblock] = name
            processed[port.direction.case("", "out:") + name] = x, y, subblock
        except (IndexError, TypeError):
            raise PRGAAPIError("Cannot assign port '{}' to ({}, {}, {})"
                    .format(name, x, y, subblock))
    # assign IOs
    next_io = {d: None for d in PortDirection}
    for port_name, port in iteritems(mod_top.ports):
        key = port.direction.case("", "out:") + port_name
        if port.low is None:
            if key in processed:
                continue
            io = next_io[port.direction] = _find_next_available_io(assignments,
                    next_io[port.direction], port.direction)
            if io is None:
                raise PRGAAPIError("Ran out of IOs when assigning '{}'".format(port_name))
            x, y, subblock = io
            assignments[x][y][port.direction.case(0, 1)][subblock] = key
            processed[key] = io
        else:
            for i in range(port.low, port.high):
                bit_name = '{}[{}]'.format(key, i)
                if bit_name in processed:
                    continue
                io = next_io[port.direction] = _find_next_available_io(assignments,
                        next_io[port.direction], port.direction)
                if io is None:
                    raise PRGAAPIError("Ran out of IOs when assigning '{}'".format(bit_name))
                x, y, subblock = io
                assignments[x][y][port.direction.case(0, 1)][subblock] = bit_name
                processed[bit_name] = io
    return processed
Exemple #12
0
 def run(self, context):
     """Run the flow."""
     # 1. resolve dependences/conflicts
     passes = OrderedDict()
     while self._passes:
         updated = None
         for i, pass_ in enumerate(self._passes):
             # 1.1 is the exact same pass already run?
             if pass_.key in passes:
                 raise PRGAAPIError("Pass '{}' is added twice".format(
                     pass_.key))
             # 1.2 is there any invalid pass keys?
             try:
                 duplicate = next(
                     key for key in passes
                     if not self.__key_is_irrelevent(pass_.key, key))
                 raise PRGAAPIError(
                     "Pass '{}' and '{}' are duplicate, or one is the sub-pass of another"
                     .format(duplicate, pass_.key))
             except StopIteration:
                 pass
             # 1.3 is any pass key in conflict with this key?
             try:
                 conflict = next(key for key in passes if any(
                     self.__key_is_prefix(rule, key)
                     for rule in pass_.conflicts))
                 raise PRGAAPIError("Pass '{}' conflicts with '{}'".format(
                     conflict, pass_.key))
             except StopIteration:
                 pass
             # 1.4 are all the dependences satisfied?
             if all(
                     any(self.__key_is_prefix(rule, key) for key in passes)
                     for rule in pass_.dependences):
                 passes[pass_.key] = pass_
                 updated = i
                 break
         if updated is None:
             missing = {
                 pass_.key: tuple(rule for rule in pass_.dependences if all(
                     not self.__key_is_prefix(rule, key) for key in passes))
                 for pass_ in self._passes
             }
             raise PRGAAPIError("Missing passes:" + "\n\t".join(
                 map(
                     lambda kv: "{} required by {}".format(
                         ', '.join(kv[1]), kv[0]), iteritems(missing))))
         else:
             del self._passes[i]
     passes = tuple(itervalues(passes))
     # 2. order passes
     # 2.1 build a graph
     g = nx.DiGraph()
     g.add_nodes_from(range(len(passes)))
     for i, pass_ in enumerate(passes):
         for j, other in enumerate(passes):
             if i == j:
                 continue
             if (any(
                     self.__key_is_prefix(rule, other.key)
                     for rule in pass_.passes_before_self) or any(
                         self.__key_is_prefix(rule, other.key)
                         for rule in pass_.dependences)):
                 # ``other`` should be executed before ``pass_``
                 g.add_edge(j, i)
             if any(
                     self.__key_is_prefix(rule, other.key)
                     for rule in pass_.passes_after_self):
                 # ``other`` should be executed after ``pass_``
                 g.add_edge(i, j)
     try:
         passes = [passes[i] for i in nx.topological_sort(g)]
     except nx.exception.NetworkXUnfeasible:
         raise PRGAAPIError(
             "Cannot determine a feasible order of the passes")
     # 3. run passes
     if self._drop_cache_before_start:
         context._cache = {}
     for pass_ in passes:
         _logger.info("running pass '%s'", pass_.key)
         t = time.time()
         pass_.run(context)
         # context._passes_applied.add(pass_.key)
         _logger.info("pass '%s' took %f seconds", pass_.key,
                      time.time() - t)
     if self._drop_cache_after_end:
         context._cache = {}
Exemple #13
0
         help="Name of the generated design-specific synthesis script. 'synth.ys' by default")
 
 args = parser.parse_args()
 enable_stdout_logging(__name__, logging.INFO)
 
 # get verilog template
 env = jj.Environment(loader=jj.FileSystemLoader(
     os.path.join(os.path.abspath(os.path.dirname(__file__)), 'templates'), ))
 
 _logger.info("Unpickling architecture context: {}".format(args.context))
 context = ArchitectureContext.unpickle(args.context)
 channel_width = 2 * sum(sgmt.width * sgmt.length for sgmt in itervalues(context.segments))
 
 _logger.info("Reading target design ...")
 if args.model is None:
     raise PRGAAPIError("Source file(s) for target design not given")
 model_top = find_verilog_top(args.model, args.model_top)
 model_top.parameters = parse_parameters(args.model_parameters) 
 
 _logger.info("Reading testbench ...")
 if args.testbench is None:
     raise PRGAAPIError("Source file(s) for testbench not given")
 testbench_top = find_verilog_top(args.testbench, args.testbench_top)
 testbench_top.parameters = parse_parameters(args.testbench_parameters) 
 
 _logger.info("Assigning IO ...")
 io_assignments = iobind(context, model_top, 
         parse_io_bindings(args.io) if args.io is not None else {})
 ostream = open('io.pads', 'w')
 for name, (x, y, subblock) in iteritems(io_assignments):
     ostream.write("{} {} {} {}\n".format(name, x, y, subblock))
Exemple #14
0
def generate_testbench_wrapper(context, template, ostream, tb_top, behav_top, io_bindings):
    """Generate simulation testbench wrapper.

    Args:
        context (`ArchitectureContext`): The architecture context of the custom FPGA
        template (Jinja2 template):
        ostream (file-like object): Output file
        tb_top (`VerilogModule`): Top-level module of the testbench of the behavioral model
        behav_top (`VerilogModule`): Top-level module of the behavioral model
        io_bindings (:obj:`Mapping` [:obj:`str` ], :obj:`tuple` [:obj:`int`, :obj:`int`, :obj:`int`]): Mapping from
           port name in the behavioral model to \(x, y, subblock\)
    """
    # configuration bits
    config_info = {}
    total_config_bits = config_info['bs_total_size'] = context.config_circuitry_delegate.total_config_bits
    last_bit_index = config_info['bs_last_bit_index'] = (total_config_bits - 1) % 64
    num_qwords = total_config_bits // 64
    if last_bit_index != 63:
        num_qwords += 1
    config_info['bs_num_qwords'] = num_qwords

    # extract io bindings
    impl_info = {'name': context.top.name, 'config': []}
    ports = impl_info['ports'] = {}
    for name, (x, y, subblock) in iteritems(io_bindings):
        direction = PortDirection.input_
        if name.startswith('out:'):
            name = name[4:]
            direction = PortDirection.output
        matched = _reprog_bit.match(name)
        port_name = matched.group('name')
        index = matched.group('index')
        if index is not None:
            index = int(index)
        # 1. is it a valid port in the behavioral model?
        behav_port = behav_top.ports.get(port_name)
        if behav_port is None:
            raise PRGAAPIError("Port '{}' is not found in design '{}'"
                    .format(port_name, behav_top.name))
        elif behav_port.direction is not direction:
            raise PRGAAPIError("Direction mismatch: port '{}' is {} in behavioral model but {} in IO bindings"
                    .format(port_name, behav_port.direction.name, direction))
        elif index is None and behav_port.low is not None:
            raise PRGAAPIError("Port '{}' is a bus and requires an index"
                    .format(port_name))
        elif index is not None and (behav_port.low is None or index < behav_port.low or index >= behav_port.high):
            raise PRGAAPIError("Bit index '{}' is not in port '{}'"
                    .format(index, port_name))
        # 2. is (x, y, subblock) an IO block?
        port = get_external_port(context.top, (x, y), subblock, direction)
        if port is None:
            raise PRGAAPIError("No {} IO port found at position ({}, {}, {})"
                    .format(direction, x, y, subblock))
        # 3. bind
        behav_port = copy(behav_port)
        behav_port.name = name
        ports[port.name] = behav_port

    # configuration info
    _update_config_list(context, context.top, impl_info['config'])

    # generate testbench wrapper
    template.stream({
        "config": config_info,
        "behav": behav_top,
        "tb": tb_top,
        "impl": impl_info,
        "iteritems": iteritems,
        "itervalues": itervalues,
        }).dump(ostream)