diff --git a/tools/FakeRAM2.0/test/memory_test.py b/tools/FakeRAM2.0/test/memory_test.py index bea42cb229..ee586299d4 100755 --- a/tools/FakeRAM2.0/test/memory_test.py +++ b/tools/FakeRAM2.0/test/memory_test.py @@ -77,6 +77,53 @@ def test_memory(self): self.assertEqual(timing_data.cycle_time_ns, 0.1566) self.assertEqual(timing_data.fo4_ps, 9.0632) + def _create_sp_ram(self, process, width, depth): + mem_config = MemoryConfig.from_json( + {"name": "sample", "width": width, "depth": depth, "banks": 1} + ) + return MemoryFactory.create(mem_config, "RAM", "SP", process, TimingData()) + + def _pin_height(self, process, memory): + return 2 * process.y_offset + memory.get_num_pins() * process.pin_pitch_um + + def test_shallow_memory_is_tall_enough_for_its_pins(self): + """ + A 4x25 RAM's bitcells are 1.296 um tall, too short for its 55 pins; + it is made as tall as its pins need, snapped up to 2.8 um + """ + + memory = self._create_sp_ram(self._process, 25, 4) + physical = memory.get_physical_data() + self.assertEqual(memory.get_num_pins(), 55) + self.assertGreaterEqual( + physical.get_height(), self._pin_height(self._process, memory) + ) + self.assertAlmostEqual(physical.get_height(), 2.8, delta=self._delta) + + def test_deep_memory_keeps_bitcell_height(self): + """A memory whose bitcells already fit its pins is not made taller""" + + memory = self._create_sp_ram(self._process, 32, 64) + _, height = self._process.get_macro_dimensions(32, 64, 1, 0) + self.assertGreater(height, self._pin_height(self._process, memory)) + self.assertAlmostEqual( + memory.get_physical_data().get_height(), 21.0, delta=self._delta + ) + + def test_pins_fit_exactly_without_snapping(self): + """ + With a 1 nm snap the height is exactly what the pins need, and + every pin still gets a track + """ + + process_data = TestUtils.get_base_process_data().copy() + process_data["snap_height_nm"] = 1 + process = Process(process_data) + for width in range(1, 300): + # constructing the memory raises if its pins do not fit + memory = self._create_sp_ram(process, width, 4) + self.assertGreaterEqual(memory.get_physical_data().get_pin_pitch(), 0) + if __name__ == "__main__": unittest.main() diff --git a/tools/FakeRAM2.0/utils/class_memory.py b/tools/FakeRAM2.0/utils/class_memory.py index 33d81f9d94..b6631a7aba 100644 --- a/tools/FakeRAM2.0/utils/class_memory.py +++ b/tools/FakeRAM2.0/utils/class_memory.py @@ -42,11 +42,18 @@ def __init__(self, mem_config, process, timing_data): width_um, height_um = self.process.get_macro_dimensions( self.width_in_bits, self.depth, self.num_banks, self.additional_height ) + # Signal pins are stacked one per track up the left edge, inset + # by y_offset at both ends, so the macro must be at least that + # tall; a shallow memory with many pins is otherwise too short + num_pins = self.get_num_pins() + height_um = max( + height_um, + 2 * self.process.y_offset + num_pins * self.process.pin_pitch_um, + ) self.physical.set_extents(width_um, height_um) self.physical.snap_to_grid( self.process.snap_width_nm, self.process.snap_height_nm ) - num_pins = self.get_num_pins() self.physical.set_pin_pitches( self.get_name(), num_pins, diff --git a/tools/FakeRAM2.0/utils/class_process.py b/tools/FakeRAM2.0/utils/class_process.py index c11dae3d6a..487a853e6a 100644 --- a/tools/FakeRAM2.0/utils/class_process.py +++ b/tools/FakeRAM2.0/utils/class_process.py @@ -119,11 +119,6 @@ def get_macro_dimensions(self, width_in_bits, depth, num_banks, additional_heigh total_height += additional_height - min_height = max( - 3.5, self.snap_height_nm / 1000.0 if self.snap_height_nm else 0 - ) - total_height = max(total_height, min_height) - return (total_width, total_height) def get_tech_nm(self): diff --git a/tools/FakeRAM2.0/utils/physical_data.py b/tools/FakeRAM2.0/utils/physical_data.py index 5c2aaefdfd..06ab390baa 100644 --- a/tools/FakeRAM2.0/utils/physical_data.py +++ b/tools/FakeRAM2.0/utils/physical_data.py @@ -92,7 +92,11 @@ def set_pin_pitches(self, name, num_pins, min_pin_pitch, y_offset): raise Exception( f"Error: attempting to set pin pitches before height ({name})" ) - number_of_tracks_available = math.floor((h - 2 * y_offset) / min_pin_pitch) + # Rounded before flooring: a height that fits its pins exactly would + # otherwise lose a track to float error (e.g. 46.99999 -> 46) + number_of_tracks_available = math.floor( + round((h - 2 * y_offset) / min_pin_pitch, 6) + ) number_of_spare_tracks = number_of_tracks_available - num_pins if number_of_spare_tracks < 0: