11#!/usr/bin/env python3
22
3- import re
43import sys
5- import csv
6- import argparse
74import importlib .util
85
9- from utils .timing_data import TimingData
10- from utils .memory_config import MemoryConfig
11- from utils .class_process import Process
12- from utils .run_utils import RunUtils
136from utils .rw_port_group import RWPortGroup
14- from utils .ss_port_creator import SSPortCreator
15- from utils .ss_port_organizer import SSPortOrganizer
16- from utils .single_port_ssram import SinglePortSSRAM
17-
18- # TODO
19- # support reg file
207
218#
229# Class to generate a single port RAM from customer-specific spreadsheet input
2310#
24- # Usage: spreadsheet_ram.py --config <fakeram_config> --physical <physical_csv>
25- # --mem_config <metrics_csv>
26- # --mapping <custom_mapping> --output_dir <output_dir>
27- #
28- # where
29- # fakeram_config - standard FakeRAM2.0 JSON config
30- # physical_csv - CSV file containing physical data such as size, pins (layer
31- # and rect) and obstructions
32- # metrics_csv - CSV file containing power and timing characteristics
33- # custom_mapping - Python3 file containing two mapping routines that are
34- # custom-specific (see below)
35- # output_dir - output directory name
36- #
3711
3812
3913class SSRAMGenerator :
4014 """Container class for generating a spreadsheet-based memory"""
4115
42- def __init__ (self , config_file , util_file ):
16+ def __init__ (self , util_file ):
4317 """Initializer"""
4418 self ._import_custom_mappings (util_file )
45- if config_file :
46- # Use config to get voltage
47- self ._process = Process (RunUtils .get_config (config_file ))
4819
4920 def _import_custom_mappings (self , file_name ):
5021 """
@@ -69,31 +40,6 @@ def _import_custom_mappings(self, file_name):
6940 "VDD": "power",
7041 }
7142
72- get_key_map - returns a dictionary that maps the CSV column to a
73- dictionary that helps the SSRAMGenerator properly handle
74- the data in its own structures. The dictionary recognizes
75- the following keys:
76-
77- key: name of the field in the functional or timing
78- data object
79- type: object type to convert into
80- conversion: optional field for unit conversion (csv
81- value is multiplied by the specified value
82-
83- Example:
84- key_map = {
85- "Num Words": { "key": "depth", "type": int },
86- "Num Bits": { "key": "width", "type": int },
87- "Num Banks": { "key": "banks", "type": int },
88- "Memory Name": { "key": "name", "type": str },
89- "cin (pf)":{ "key": "cap_input_pf", "type": float },
90- "corner.Ts (ns)": { "key": "t_setup_ns", "type": float },
91- "corner.Th (ns)": { "key": "t_hold_ns", "type": float },
92- "corner.Ta (ns)": { "key": "access_time_ns", "type": float },
93- "corner.Tc (ns)": { "key": "cycle_time_ns", "type": float },
94- "Static Power (uW)": { "key": "standby_leakage_per_bank_mW", "type": float, "conversion": 1e-3 },
95- "Dynamic Power (uW/MHz)": { "key": "pin_dynamic_power_mW", "type": float , "conversion": 1e-3},
96- }
9743 """
9844
9945 module_name = "spreadsheet_utils"
@@ -102,135 +48,6 @@ def _import_custom_mappings(self, file_name):
10248 sys .modules [module_name ] = self ._util_module
10349 spec .loader .exec_module (self ._util_module )
10450 self ._pin_type_map = self ._util_module .get_pin_type_map ()
105- self ._key_map = self ._util_module .get_key_map ()
106-
107- def create_memory (self , mem_config , physical ):
108- """Extracts the data from the CSV files and returns the memory object"""
109-
110- # Get the physical data and organize it
111- phys_data = self .read_physical_file (physical )
112- pin_org = SSPortOrganizer (self ._pin_type_map )
113- pin_org .organize_ports (phys_data )
114- num_pins = len (phys_data ["pin_data" ])
115-
116- # Get the metrics data and organize it
117- macro_metrics = self .read_metrics_file (mem_config , phys_data ["name" ])
118- timing_data = TimingData (macro_metrics )
119- mem_config = MemoryConfig .from_json (macro_metrics )
120-
121- mem = SinglePortSSRAM (mem_config , self ._process , timing_data , num_pins )
122- self .set_logical_pins (mem , pin_org )
123- port_creator = SSPortCreator (mem , self ._pin_type_map )
124- port_creator .create_ports (phys_data ["pin_data" ])
125- if "obs" in phys_data :
126- port_creator .create_obs (phys_data ["obs" ])
127- mem .get_physical_data ().set_extents (
128- float (phys_data ["width" ]), float (phys_data ["height" ])
129- )
130- # snap to grid to sync up the physical data fields
131- mem .get_physical_data ().snap_to_grid (1 , 1 )
132- return mem
133-
134- def read_physical_file (self , file_name ):
135- """
136- Reads the physical data CSV file and returns a dictionary that includes
137- the pin and obstruction data
138- """
139-
140- macro_data = {"pin_data" : {}, "obs" : []}
141- with open (file_name , "r" , encoding = "utf-8-sig" ) as csv_fh :
142- reader = csv .DictReader (csv_fh )
143- is_first = True
144- for row in reader :
145- # MACRO,SIZE_WIDTH,SIZE_HEIGHT,SOURCE,PIN,USE,LAYER,x1,y1,x2,y2
146- if is_first :
147- macro_data ["name" ] = row ["MACRO" ]
148- macro_data ["width" ] = row ["SIZE_WIDTH" ]
149- macro_data ["height" ] = row ["SIZE_HEIGHT" ]
150- is_first = False
151- source = row ["SOURCE" ]
152- if source == "PIN" :
153- pin_data = {
154- "name" : row ["PIN" ],
155- "use" : row ["USE" ],
156- "layer" : row ["LAYER" ],
157- "rect" : [
158- float (row ["x1" ]),
159- float (row ["y1" ]),
160- float (row ["x2" ]),
161- float (row ["y2" ]),
162- ],
163- }
164- if pin_data ["name" ] not in macro_data ["pin_data" ]:
165- macro_data ["pin_data" ][pin_data ["name" ]] = pin_data
166- else : # pragma: no cover
167- raise Exception (
168- "{} had multiple pin shapes" .format (pin_data ["name" ])
169- )
170- elif source == "OBS" :
171- obs_data = {
172- "layer" : row ["LAYER" ],
173- "layer_attr" : None ,
174- "rect" : [
175- float (row ["x1" ]),
176- float (row ["y1" ]),
177- float (row ["x2" ]),
178- float (row ["y2" ]),
179- ],
180- }
181- macro_data ["obs" ].append (obs_data )
182- else :
183- print (
184- "Skipping {} since source is {}" .format (
185- row ["PIN" ], row ["SOURCE" ]
186- )
187- )
188- return macro_data
189-
190- def get_size_keys (self ):
191- """Returns the keys that map to depth and width"""
192-
193- depth_key = width_key = None
194- for key , val in self ._key_map .items ():
195- if val .get ("key" ) == "depth" :
196- depth_key = key
197- elif val .get ("key" ) == "width" :
198- width_key = key
199- return (depth_key , width_key )
200-
201- def read_metrics_file (self , file_name , macro_name ):
202- """
203- Reads the metrics CSV file to extract the power and timing data.
204-
205- Returns a dictionary that has been normalized to our expected metrics
206- and Process/TimingData names
207-
208- The depth and width are present on the first row and apply to all
209- subsequent rows until they are set again on a subsequent row.
210- Effectively, the cells should have been merged, but weren't.
211- """
212- macro_metrics = {}
213- depth = width = None
214- depth_key , width_key = self .get_size_keys ()
215- with open (file_name , "r" , encoding = "utf-8-sig" ) as csv_fh :
216- reader = csv .DictReader (csv_fh )
217- for row in reader :
218- if row [depth_key ]:
219- depth = row [depth_key ]
220- if row [width_key ]:
221- width = row [width_key ]
222- if row ["memory_name" ] == macro_name :
223- row [depth_key ] = depth
224- row [width_key ] = width
225- for csv_key , metric_key_data in self ._key_map .items ():
226- if csv_key in row and row [csv_key ] != "N/A" :
227- metric_key = metric_key_data ["key" ]
228- metric_type_fn = metric_key_data ["type" ]
229- metric_conv_factor = metric_key_data .get ("conversion" , 1 )
230- macro_metrics [metric_key ] = (
231- metric_type_fn (row [csv_key ]) * metric_conv_factor
232- )
233- return macro_metrics
23451
23552 def set_logical_pins (self , mem , pin_org ):
23653 """Sets the pins to be used for Verilog and Liberty output"""
@@ -261,44 +78,3 @@ def set_logical_pins(self, mem, pin_org):
26178 for src in pin_org .get_misc_ports ():
26279 if src ["type" ] not in ["power" , "ground" ]:
26380 mem .add_misc_port (src ["name" ])
264-
265- @staticmethod
266- def main ():
267- """Main driver"""
268- parser = argparse .ArgumentParser (
269- description = "Create a set of memory colllateral from a spreadsheet input"
270- )
271- parser .add_argument (
272- "--config" ,
273- help = "Input configuration file containing technology parameters" ,
274- required = True ,
275- )
276- parser .add_argument (
277- "--mem_config" ,
278- help = "CSV file containing technical parameters such as size, timing, power" ,
279- required = True ,
280- )
281- parser .add_argument (
282- "--physical" ,
283- help = "CSV file containing physical data such as pin locations and layers" ,
284- required = True ,
285- )
286- parser .add_argument (
287- "--mapping" , help = "Custom Python mapping file" , required = True
288- )
289- parser .add_argument (
290- "--output_dir" ,
291- action = "store" ,
292- help = "Output directory " ,
293- required = False ,
294- default = "results" ,
295- )
296-
297- args = parser .parse_args ()
298- rep = SSRAMGenerator (args .config , args .mapping )
299- mem = rep .create_memory (args .mem_config , args .physical )
300- RunUtils .write_memory (mem , args .output_dir )
301-
302-
303- if __name__ == "__main__" :
304- SSRAMGenerator .main ()
0 commit comments