Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cap table support #566

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/hammer-tech/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,21 @@ def paths_func(lib: "Library") -> List[str]:
return LibraryFilter.new("qrc", "qrc RC corner tech file",
paths_func=paths_func, is_file=True)

@property
def cap_table_filter(self) -> LibraryFilter:
"""
Selecting cap table RC Corner tech files.
"""

def paths_func(lib: "Library") -> List[str]:
if lib.cap_table_file is not None:
return [lib.cap_table_file]
else:
return []

return LibraryFilter.new("cap_table", "cap table RC corner tech file",
paths_func=paths_func, is_file=True)

@property
def verilog_synth_filter(self) -> LibraryFilter:
"""
Expand Down
2 changes: 1 addition & 1 deletion src/hammer-tech/hammer_tech.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ def process_library_filter(self,
# Next, extract paths and prepend them to get the real paths.
def get_and_prepend_path(lib: Library) -> Tuple[Library, List[str]]:
paths = filt.paths_func(lib)
full_paths = list(map(lambda path: self.prepend_dir_path(path, lib), paths))
full_paths = list(map(lambda path: self.prepend_dir_path(path, lib) if path else '', paths))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change? Is this just a bugfix?

Copy link
Collaborator Author

@ErikFanderson ErikFanderson Mar 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepend_dir_path has an assertion that checks to make sure the path is not empty. I am not entirely sure why the assertion exists, but this was my solution to get an empty string to be returned

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, how do empty paths end up showing up to begin with? And how is this cap table specific?

Copy link
Collaborator Author

@ErikFanderson ErikFanderson Mar 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well because you only need a cap_table_file OR a qrc_techfile then 1 of them can be empty. Previously hammer would error if you did not supply a qrc_techfile even though the function "generate_mmmc_script" already assumed that if there was no supplied qrc_techfile that an empty string would be retrieved when calling "get_mmmc_qrc". I guess this raises the question of whether we actually want an error to be thrown if neither of these files are supplied

Copy link
Contributor

@harrisonliew harrisonliew Mar 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, seeing how other things use prepend_dir_path, I also don't see what the value of the len(path) > 0 assertion is. It is only used in certain cases (DRC/LVS decks, map files) but doesn't make whatever you specified in the tech json any more or less correct.

I think what we really should do is call an os.path.exists on the constructed path so that Python can catch bad paths in tech jsons before it's passed to the tool where sometimes at best only a warning is thrown.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are no qrc techfiles, then wouldn't get_mmmc_qrc() return an empty string since the read_libs call would return an empty list?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a side note, we really need to detach the QRC techfile from the libraries (#315).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can incorporate that larger change into this PR, so that we aren't creating more work we need to undo/fix later?

return lib, full_paths

libs_and_paths = list(map(get_and_prepend_path, filtered_libs)) # type: List[Tuple[Library, List[str]]]
Expand Down
2 changes: 2 additions & 0 deletions src/hammer-tech/library_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def spice_file(self) -> Optional[str]: pass
def verilog_synth(self) -> Optional[str]: pass
@property
def verilog_sim(self) -> Optional[str]: pass
@property
def cap_table_file(self) -> Optional[str]: pass

PathsFunctionType = Callable[["Library"], List[str]]

Expand Down
3 changes: 3 additions & 0 deletions src/hammer-tech/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@
"qrc techfile" : {
"type" : "string"
},
"cap table file" : {
"type" : "string"
},
"supplies" : {
"title" : "Supplies",
"type" : "object",
Expand Down
37 changes: 29 additions & 8 deletions src/hammer-vlsi/hammer_vlsi/hammer_vlsi_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1473,7 +1473,14 @@ def get_mmmc_qrc(self, corner: MMMCCorner) -> str:
lib_args = self.technology.read_libs([hammer_tech.filters.qrc_tech_filter],
hammer_tech.HammerTechnologyUtils.to_plain_item,
extra_pre_filters=[
self.filter_for_mmmc(voltage=corner.voltage, temp=corner.temp)])
self.filter_for_mmmc(voltage=corner.voltage, temp=corner.temp)], must_exist=False)
return " ".join(lib_args)

def get_mmmc_cap_table(self, corner: MMMCCorner) -> str:
lib_args = self.technology.read_libs([hammer_tech.filters.cap_table_filter],
hammer_tech.HammerTechnologyUtils.to_plain_item,
extra_pre_filters=[
self.filter_for_mmmc(voltage=corner.voltage, temp=corner.temp)], must_exist=False)
return " ".join(lib_args)

def get_qrc_tech(self) -> str:
Expand All @@ -1487,6 +1494,17 @@ def get_qrc_tech(self) -> str:
], hammer_tech.HammerTechnologyUtils.to_plain_item)
return " ".join(lib_args)

def get_cap_table(self) -> str:
"""
Helper function to get the list of cap table files in space separated format.

:return: List of cap table files separated by spaces
"""
lib_args = self.technology.read_libs([
hammer_tech.filters.cap_table_filter
], hammer_tech.HammerTechnologyUtils.to_plain_item)
return " ".join(lib_args)

def generate_mmmc_script(self) -> str:
"""
Output for the mmmc.tcl script.
Expand Down Expand Up @@ -1565,16 +1583,18 @@ def append_mmmc(cmd: str) -> None:
name="{n}.hold_cond".format(n=hold_corner.name),
list="{n}.hold_set".format(n=hold_corner.name)
))
# Next, create Innovus rc corners from qrc tech files
append_mmmc("create_rc_corner -name {name} -temperature {tempInCelsius} {qrc}".format(
# Next, create Innovus rc corners from qrc tech or cap table files
append_mmmc("create_rc_corner -name {name} -temperature {tempInCelsius} {qrc} {cap_table}".format(
name="{n}.setup_rc".format(n=setup_corner.name),
tempInCelsius=str(setup_corner.temp.value),
qrc="-qrc_tech {}".format(self.get_mmmc_qrc(setup_corner)) if self.get_mmmc_qrc(setup_corner) != '' else ''
qrc="-qrc_tech {}".format(self.get_mmmc_qrc(setup_corner)) if self.get_mmmc_qrc(setup_corner) != '' else '',
harrisonliew marked this conversation as resolved.
Show resolved Hide resolved
cap_table="-cap_table {}".format(self.get_mmmc_cap_table(setup_corner)) if self.get_mmmc_cap_table(setup_corner) != '' else ''
))
append_mmmc("create_rc_corner -name {name} -temperature {tempInCelsius} {qrc}".format(
append_mmmc("create_rc_corner -name {name} -temperature {tempInCelsius} {qrc} {cap_table}".format(
name="{n}.hold_rc".format(n=hold_corner.name),
tempInCelsius=str(hold_corner.temp.value),
qrc="-qrc_tech {}".format(self.get_mmmc_qrc(hold_corner)) if self.get_mmmc_qrc(hold_corner) != '' else ''
qrc="-qrc_tech {}".format(self.get_mmmc_qrc(hold_corner)) if self.get_mmmc_qrc(hold_corner) != '' else '',
cap_table="-cap_table {}".format(self.get_mmmc_cap_table(hold_corner)) if self.get_mmmc_cap_table(hold_corner) != '' else ''
))
# Next, create an Innovus delay corner.
append_mmmc(
Expand Down Expand Up @@ -1610,10 +1630,11 @@ def append_mmmc(cmd: str) -> None:
))
# extra junk: -opcond ...
rc_corner_name = "rc_cond"
append_mmmc("create_rc_corner -name {name} -temperature {tempInCelsius} {qrc}".format(
append_mmmc("create_rc_corner -name {name} -temperature {tempInCelsius} {qrc} {cap_table}".format(
name=rc_corner_name,
tempInCelsius=120, # TODO: this should come from tech config
qrc="-qrc_tech {}".format(self.get_qrc_tech()) if self.get_qrc_tech() != '' else ''
qrc="-qrc_tech {}".format(self.get_qrc_tech()) if self.get_qrc_tech() != '' else '',
cap_table="-cap_table {}".format(self.get_cap_table()) if self.get_cap_table() != '' else ''
))
# Next, create an Innovus delay corner.
delay_corner_name = "my_delay_corner"
Expand Down
31 changes: 31 additions & 0 deletions src/hammer-vlsi/tech_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,37 @@ def add_duplicates(in_dict: Dict[str, Any]) -> Dict[str, Any]:
# Cleanup
shutil.rmtree(tech_dir_base)

def test_cap_table_file_library_filter(self) -> None:
"""
Test that process_library_filter removes duplicates.
"""
import hammer_config

tech_dir, tech_dir_base = HammerToolTestHelpers.create_tech_dir("dummy28")
tech_json_filename = os.path.join(tech_dir, "dummy28.tech.json")

def add_cap_table(in_dict: Dict[str, Any]) -> Dict[str, Any]:
out_dict = deepdict(in_dict)
out_dict["libraries"].append({
"cap table file": "test/cap_table_file"
})
return out_dict

HammerToolTestHelpers.write_tech_json(tech_json_filename, add_cap_table)
tech = self.get_tech(hammer_tech.HammerTechnology.load_from_dir("dummy28", tech_dir))
tech.cache_dir = tech_dir

database = hammer_config.HammerDatabase()
tech.set_database(database)
outputs = tech.process_library_filter(pre_filts=[], filt=hammer_tech.filters.cap_table_filter,
must_exist=False,
output_func=lambda str, _: [str])

self.assertEqual(outputs, ["{0}/cap_table_file".format(tech_dir)])

# Cleanup
shutil.rmtree(tech_dir_base)

@staticmethod
def add_tarballs(in_dict: Dict[str, Any]) -> Dict[str, Any]:
"""
Expand Down