| #!/usr/bin/env python3 |
| # Copyright 2020 Efabless Corporation |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """ |
| |
| """ |
| |
| import argparse |
| import os |
| import odb |
| |
| parser = argparse.ArgumentParser( |
| description="Places macros in positions and orientations specified by a config file" |
| ) |
| |
| parser.add_argument( |
| "--lef", |
| "-l", |
| nargs="+", |
| type=str, |
| default=None, |
| required=True, |
| help="Input LEF file(s)", |
| ) |
| |
| parser.add_argument( |
| "--input-def", |
| "-id", |
| required=True, |
| help="DEF view of the design that needs to have its instances placed", |
| ) |
| |
| parser.add_argument("--output-def", "-o", required=True, help="Output placed DEF file") |
| |
| parser.add_argument("--config", "-c", required=True, help="Configuration file") |
| |
| parser.add_argument( |
| "--fixed", |
| "-f", |
| action="store_true", |
| default=False, |
| help="a flag to signal whether the placement should be fixed or placed", |
| ) |
| |
| args = parser.parse_args() |
| input_lef_file_names = args.lef |
| input_def_file_name = args.input_def |
| output_def_file_name = args.output_def |
| config_file_name = args.config |
| fixed_flag = args.fixed |
| |
| LEF2OA_MAP = { |
| "N": "R0", |
| "S": "R180", |
| "W": "R90", |
| "E": "R270", |
| "FN": "MY", |
| "FS": "MX", |
| "FW": "MXR90", |
| "FE": "MYR90", |
| } |
| |
| |
| def lef_rot_to_oa_rot(rot): |
| if rot in LEF2OA_MAP: |
| return LEF2OA_MAP[rot] |
| else: |
| assert rot in [item[1] for item in LEF2OA_MAP.items()], rot |
| return rot |
| |
| |
| def gridify(n, f): |
| """ |
| e.g., (1.1243, 0.005) -> 1.120 |
| """ |
| return round(n / f) * f |
| |
| |
| # read config |
| macros = {} |
| with open(config_file_name, "r") as config_file: |
| for line in config_file: |
| # Discard comments and empty lines |
| line = line.split("#")[0].strip() |
| if not line: |
| continue |
| line = line.split() |
| macros[line[0]] = [ |
| str(int(float(line[1]) * 1000)), |
| str(int(float(line[2]) * 1000)), |
| line[3], |
| ] |
| |
| print("Placing the following macros:") |
| print(macros) |
| |
| db_design = odb.dbDatabase.create() |
| |
| for lef in input_lef_file_names: |
| odb.read_lef(db_design, lef) |
| odb.read_def(db_design, input_def_file_name) |
| |
| chip_design = db_design.getChip() |
| block_design = chip_design.getBlock() |
| top_design_name = block_design.getName() |
| print("Design name:", top_design_name) |
| |
| macros_cnt = len(macros) |
| for inst in block_design.getInsts(): |
| inst_name = inst.getName() |
| if inst.isFixed(): |
| assert inst_name not in macros, inst_name |
| continue |
| if inst_name in macros: |
| print("Placing", inst_name) |
| macro_data = macros[inst_name] |
| x = gridify(int(macro_data[0]), 5) |
| y = gridify(int(macro_data[1]), 5) |
| inst.setOrient(lef_rot_to_oa_rot(macro_data[2])) |
| inst.setLocation(x, y) |
| if fixed_flag: |
| inst.setPlacementStatus("FIRM") |
| else: |
| inst.setPlacementStatus("PLACED") |
| del macros[inst_name] |
| |
| assert not macros, ("Macros not found:", macros) |
| |
| print("Successfully placed", macros_cnt, "instances") |
| |
| odb.write_def(block_design, output_def_file_name) |
| assert os.path.exists(output_def_file_name), "Output not written successfully" |