Source code for util.sram_configuration_protocol

"""
Details the SRAM configuration protocol

.. rst-class:: ascii

::

         +--------+--------+--------+--------+
         |        |        |        |        |
    W[3] |        |        |        |        |
         |        |        |        |        |
         +-----------------------------------+
         |        |        |        |        |
    W[2] |        |        |        |        |
         |        |        |        |        |
         +-----------------------------------+
         |        |        |        |        |
    W[1] |        |        |        |        |
         |        |        |        |        |
         +-----------------------------------+
         |        |        |        |        |
    W[0] |        |        |        |        |
         |        |        |        |        |
         +--------+--------+--------+--------+
            b[0]     b[1]     b[2]     b[3]

"""

import logging
import math
from copy import deepcopy

import spydrnet as sdn
from spydrnet_physical.util.shell import launch_shell
from spydrnet_physical.util import OpenFPGA_Config_Generator

logger = logging.getLogger('spydrnet_logs')


[docs]class sram_configuration(OpenFPGA_Config_Generator): """ This example demonstrate how configuration chain can be restructured after the tile tranformation. This method is better suited while creating a configuration after the physical tranformation. However mapping the sequence back to the original sequence could require complex scripting. """ word_line_rows = [] """Stores number of word lines in each row """ bit_line_cols = [] """Stores number of bit lines in each column """ _config_bits_matrix = [] def __init__(self, grid, netlist, library, top_module): super().__init__(grid, netlist, library, top_module) w_lines = next(self._top_module.get_ports("wl*")) b_lines = next(self._top_module.get_ports("bl*")) logger.debug("Found total %d bits", w_lines.size) assert w_lines.size == b_lines.size, "Mismatch WL and BL size" # self._config_bits_matrix = [[0]*self.fpga_size[0]]*self.fpga_size[0] self._config_bits_matrix = [[0 for _ in range(self.fpga_size[0])] for _ in range(self.fpga_size[1])] self.word_line_rows = [0] * self.fpga_size[1] self.bit_line_cols = [0] * self.fpga_size[0] self.annotate_configuration_bits()
[docs] def print_configuration_bit_matrix(self, matrix=None): """ Print the configuration bits matrix extracted from the fabric """ matrix = matrix or self._config_bits_matrix for y_pt in range(self.fpga_size[1]-1, -1, -1): for x_pt in range(self.fpga_size[0]): bits = matrix[y_pt][x_pt] print(f"{bits:4}", end=" ") print()
[docs] def get_tile(self, x_pt, y_pt): """Returns the instance associated with the specific x and y cordinate""" return next(self._top_module.get_instances(f"*_{x_pt}__{y_pt}_*"))
[docs] def annotate_configuration_bits(self): ''' Adds number of configuration bit information to the each modules property ''' for each_def in self._top_module.get_definitions("*"): w_lines = next(each_def.get_ports("wl*")) b_lines = next(each_def.get_ports("bl*")) assert w_lines.size == b_lines.size, "Mismatch WL and BL size" logger.debug("%20s %d", each_def.name, w_lines.size) each_def.properties["CONFIG_BITS"] = w_lines.size for x_pt in range(self.fpga_size[0]): for y_pt in range(self.fpga_size[1]): inst = self.get_tile(x_pt+1, y_pt+1) self._config_bits_matrix[y_pt][x_pt] = \ inst.reference.properties.get( "CONFIG_BITS", 0)
[docs] def add_configuration_scheme(self): ''' Creates configuration chain ''' logger.debug("Adding memory configuration protocol") self._top_module.remove_port(next(self._top_module.get_ports("wl*"))) self._top_module.remove_port(next(self._top_module.get_ports("bl*"))) # TODO This part should be copied to the remove_cables method wl_cable = next(self._top_module.get_cables("wl*")) for each_wire in wl_cable.wires: for pins in each_wire.pins[::-1]: each_wire.disconnect_pin(pins) bl_cable = next(self._top_module.get_cables("bl*")) for each_wire in bl_cable.wires: for pins in each_wire.pins[::-1]: each_wire.disconnect_pin(pins) self._top_module.remove_cable(next(self._top_module.get_cables("wl"))) self._top_module.remove_cable(next(self._top_module.get_cables("bl"))) self._create_wl_ports() self._create_bl_ports() self._create_wl_connection() self._create_bl_connection() logger.debug(self.fpga_size)
def remove_bl_wl_lines(self): for module in self._top_module.get_definitions("*"): wl_port = next(module.get_ports("wl"), None) if wl_port: module.remove_port(wl_port) bl_port = next(module.get_ports("bl"), None) if bl_port: module.remove_port(bl_port) def _create_wl_connection(self): top = self._top_module wl_lines = top.create_cable("wl_in", wires=sum(self.word_line_rows)) # wl_out = top.create_cable("wl_out", wires=sum(self.word_line_rows)) for y_pt in range(self.fpga_size[1]): width = self.word_line_rows[y_pt] pre_instance = None for x_pt in range(self.fpga_size[1]): instance = self.get_tile(x_pt+1, y_pt+1) iname = instance.name cable = top.create_cable(f"{iname}_wl_in", wires=width) port = next(instance.get_ports("wl_in")) cable.connect_instance_port(instance, port) if pre_instance: port = next(pre_instance.get_ports("wl_out")) cable.connect_instance_port(pre_instance, port) else: cable.assign_cable(wl_lines, sum(self.word_line_rows[:y_pt+1]), sum(self.word_line_rows[:y_pt]), reverse=True) pre_instance = instance out_cable = top.create_cable(f"{iname}_wl_out", wires=width) port = next(instance.get_ports("wl_out")) out_cable.connect_instance_port(instance, port) def _create_bl_connection(self): top = self._top_module bl_lines = top.create_cable("bl_in", wires=sum(self.bit_line_cols)) # bl_out = top.create_cable("bl_out", wires=sum(self.bit_line_cols)) for x_pt in range(self.fpga_size[1]): width = self.bit_line_cols[x_pt] pre_instance = None for y_pt in range(self.fpga_size[1]): instance = self.get_tile(x_pt+1, y_pt+1) iname = instance.name cable = top.create_cable(f"{iname}_bl_in", wires=width) port = next(instance.get_ports("bl_in")) cable.connect_instance_port(instance, port) if pre_instance: port = next(pre_instance.get_ports("bl_out")) cable.connect_instance_port(pre_instance, port) else: cable.assign_cable(bl_lines, sum(self.bit_line_cols[:x_pt+1]), sum(self.bit_line_cols[:x_pt]), reverse=True) pre_instance = instance out_cable = top.create_cable(f"{iname}_bl_out", wires=width) port = next(instance.get_ports("bl_out")) out_cable.connect_instance_port(instance, port) def _create_bl_ports(self): """ Create BL lines in each row TODO: Change method to identify the port size on eachmodule and then create """ for x_pt in range(self.fpga_size[1]): width = self.bit_line_cols[x_pt] for y_pt in range(self.fpga_size[1]): module = self.get_tile(x_pt+1, y_pt+1).reference # Make sure bl_in port exist bl_in = next(module.get_ports("bl_in"), None) if not bl_in: module.create_port("bl_in", direction=sdn.IN, pins=width) else: while bl_in.size < width: bl_in.create_pin() # Make sure bl_out port exist bl_out = next(module.get_ports("bl_out"), None) if not bl_out: module.create_port("bl_out", direction=sdn.OUT, pins=width) else: while bl_out.size < width: bl_out.create_pin() # Make sure crosponding cable exists bl_in_cables = next(module.get_cables("bl_in"), None) if not bl_in_cables: bl_in_cables = module.create_cable("bl_in", wires=width) else: while bl_in_cables.size < width: bl_in_cables.create_wire() # Make sure crosponding cable exists bl_out_cables = next(module.get_cables("bl_out"), None) if not bl_out_cables: bl_out_cables = module.create_cable("bl_out", wires=width) else: while bl_out_cables.size < width: bl_out_cables.create_wire() assignement = next(module.get_instances( "bl_in_bl_out_assign"), None) if assignement: module.remove_child(assignement) bl_in_cables.assign_cable(bl_out_cables) def _create_wl_ports(self): """ Create WL lines in each row TODO: Change method to identify the port size on eachmodule and then create """ for y_pt in range(self.fpga_size[1]): width = self.word_line_rows[y_pt] for x_pt in range(self.fpga_size[1]): module = self.get_tile(x_pt+1, y_pt+1).reference # Make sure wl_in port exist wl_in = next(module.get_ports("wl_in"), None) if not wl_in: module.create_port("wl_in", direction=sdn.IN, pins=width) else: while wl_in.size < width: wl_in.create_pin() # Make sure wl_out port exist wl_out = next(module.get_ports("wl_out"), None) if not wl_out: module.create_port("wl_out", direction=sdn.OUT, pins=width) else: while wl_out.size < width: wl_out.create_pin() # Make sure crosponding cable exists wl_in_cables = next(module.get_cables("wl_in"), None) if not wl_in_cables: wl_in_cables = module.create_cable("wl_in", wires=width) else: while wl_in_cables.size < width: wl_in_cables.create_wire() # Make sure crosponding cable exists wl_out_cables = next(module.get_cables("wl_out"), None) if not wl_out_cables: wl_out_cables = module.create_cable("wl_out", wires=width) else: while wl_out_cables.size < width: wl_out_cables.create_wire() assignement = next(module.get_instances( "wl_in_wl_out_assign"), None) if assignement: module.remove_child(assignement) wl_in_cables.assign_cable(wl_out_cables)
[docs] def set_wl_distribution(self, lines): """ Sets fixed number of word lines for each row of the FPGA grid. Args: lines (list): List of integer indicating lines in each row """ self.word_line_rows = lines for x_pt in range(self.fpga_size[0]): bits = [self._config_bits_matrix[i][x_pt] for i in range(self.fpga_size[1])] self.bit_line_cols[x_pt] = math.ceil(max(bits)/lines[x_pt]) logger.debug(self.bit_line_cols)
def set_bl_distribution(self, lines): pass
[docs] def write_fabric_key(self): ''' This will be extendned in the class ''' return
def _create_inter_tiles(self): inst_list = [] for y in range(self.fpga_size[1], 0, -1): for x in sorted(range(self.fpga_size[0], 0, -1), reverse=(y+1) % 2): print(f"*{x}_{y}*") inst = next(self._top_module.get_instances(f"*{x}__{y}*")) inst_list.append(inst) self._connect_instances(self._top_module, inst_list) head_cable = next(self._top_module.get_cables(self.head)) first_head = next(inst_list[0].get_port_pins(self.head)) head_cable.wires[0].connect_pin(first_head) tail_cable = next(self._top_module.get_cables(self.tail)) last_tail = next(inst_list[-1].get_port_pins(self.tail)) tail_cable.wires[0].connect_pin(last_tail) def _create_intra_tiles(self): for each in ["top_right_tile", "top_tile", "top_left_tile", "left_tile", "tile", "right_tile", "bottom_right_tile", "bottom_left_tile"]: tile = next(self._library.get_definitions(each)) # Remove ccff related cables and port from tile for cable in list(tile.get_cables("ccff*")): for pin in list(cable.wires[0].pins): pin.wire.disconnect_pin(pin) tile.remove_cable(cable) for port in list(tile.get_ports("ccff*")): tile.remove_port(port) # Create chain inst_list = sorted([inst for inst in tile.get_instances()], key=(lambda x: self.order.index(x.reference.name))) self._connect_instances(tile, inst_list) # Create ccff_head port tile.create_port(self._head, direction=sdn.IN, pins=1) ccff_head_wire = tile.create_cable(self._head, wires=1).wires[0] ccff_head_wire.connect_pin( next(inst_list[0].get_port_pins(self.head))) # Create ccff_tail port tile.create_port(self._tail, direction=sdn.OUT, pins=1) ccff_tail_wire = tile.create_cable(self._tail, wires=1).wires[0] ccff_tail_wire.connect_pin( next(inst_list[-1].get_port_pins(self.tail)))