1 /*
2  * CModel.cc
3  *
4  * Copyright 2014-2018 D. Mitch Bailey  cvc at shuharisystem dot com
5  *
6  * This file is part of cvc.
7  *
8  * cvc is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * cvc is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with cvc.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  * You can download cvc from https://github.com/d-m-bailey/cvc.git
22  */
23 
24 #include "CModel.hh"
25 
26 #include "CCondition.hh"
27 #include "CParameterMap.hh"
28 #include "CDevice.hh"
29 #include "CCircuit.hh"
30 #include "CPower.hh"
31 #include <regex>
32 
CModelCheck(string theCheck,string theParameter,string theInclusiveMin,string theExclusiveMin,string theInclusiveMax,string theExclusiveMax)33 CModelCheck::CModelCheck(string theCheck, string theParameter, string theInclusiveMin, string theExclusiveMin, string theInclusiveMax, string theExclusiveMax ) {
34 	check = theCheck;
35 	parameter = theParameter;
36 	isVoltage = (theParameter[0] == 'V');
37 	minInclusiveText = theInclusiveMin;
38 	minExclusiveText = theExclusiveMin;
39 	maxInclusiveText = theInclusiveMax;
40 	maxExclusiveText = theExclusiveMax;
41 	SetValue(minInclusiveText, minInclusiveVoltage, minInclusiveValue);
42 	SetValue(maxInclusiveText, maxInclusiveVoltage, maxInclusiveValue);
43 	SetValue(minExclusiveText, minExclusiveVoltage, minExclusiveValue);
44 	SetValue(maxExclusiveText, maxExclusiveVoltage, maxExclusiveValue);
45 }
46 
SetValue(string theParameter,voltage_t & theVoltage,float & theValue)47 void CModelCheck::SetValue(string theParameter, voltage_t &theVoltage, float &theValue) {
48 	if ( ! IsEmpty(theParameter) ) {
49 		if ( isVoltage && IsValidVoltage_(theParameter) ) {
50 			theVoltage = round(from_string<float>(theParameter) * VOLTAGE_SCALE + 0.1);
51 		} else {
52 			theValue = from_string<float>(theParameter);
53 		}
54 	}
55 }
56 
CModel(string theParameterString)57 CModel::CModel(string theParameterString) {
58 // parameter string example
59 // NMOS nch Vth=0.500 Vgs=0.3 Vds=0.5 Vbs=0.4 condition=(L<0.4u w>=1.2u)
60 // RESISTOR fuse Vds=0.4 R=1000 condition=(r<10) model=switch_on
61 // BOX res1 model=resistor diode=(1-3,2-3)
62 	string myParameterName;
63 	string myParameterValue;
64 	size_t	myEqualIndex;
65 	size_t	myStringBegin = theParameterString.find_first_not_of(" \t");
66 	size_t	myStringEnd = theParameterString.find_first_of(" \t", myStringBegin);
67 	definition = theParameterString.substr(myStringBegin, myStringEnd - myStringBegin);
68 	type = gModelTypeStringMap.at(theParameterString.substr(myStringBegin, myStringEnd - myStringBegin));
69 	baseType = gBaseModelTypePrefixMap.at(type);
70 	if ( IsMos_(type) ) {
71 		resistanceDefinition = DEFAULT_MOS_RESISTANCE;
72 	} else if ( type == RESISTOR ) {
73 		resistanceDefinition = DEFAULT_RESISTANCE;
74 	}
75 	myStringBegin = theParameterString.find_first_not_of(" \t", myStringEnd);
76 	myStringEnd = theParameterString.find_first_of(" \t", myStringBegin);
77 	name = theParameterString.substr(myStringBegin, myStringEnd - myStringBegin);
78 	definition = definition + " " + name;
79 
80 	myStringBegin = theParameterString.find_first_not_of(" \t", myStringEnd);
81 	while ( myStringBegin < theParameterString.length() ) {
82 		myStringEnd = theParameterString.find_first_of(" \t", myStringBegin);
83 		definition = definition + " " + theParameterString.substr(myStringBegin, myStringEnd - myStringBegin);
84 
85 		myEqualIndex = theParameterString.find("=", myStringBegin);
86 		myParameterName = theParameterString.substr(myStringBegin, myEqualIndex - myStringBegin);
87 		myParameterValue = theParameterString.substr(myEqualIndex + 1, myStringEnd - (myEqualIndex + 1));
88 		if (myParameterName == "Vth") {
89 			vthDefinition = myParameterValue; // String_to_Voltage(myParameterValue);
90 		} else if (myParameterName == "Vgs") {
91 			maxVgsDefinition = myParameterValue; // String_to_Voltage(myParameterValue);
92 		} else if (myParameterName == "Vds") {
93 			maxVdsDefinition = myParameterValue; // String_to_Voltage(myParameterValue);
94 		} else if (myParameterName == "Vbs") {
95 			maxVbsDefinition = myParameterValue; // String_to_Voltage(myParameterValue);
96 		} else if (myParameterName == "Vbg") {
97 			maxVbgDefinition = myParameterValue; // String_to_Voltage(myParameterValue);
98 		} else if (myParameterName == "R") {
99 			resistanceDefinition = myParameterValue;
100 		} else if (myParameterName == "model") {
101 			if ( baseType != "M" && baseType != "R" && baseType != "C" && baseType != "X" ) throw EModelError("basetype " + baseType + "cannot be overriden as " + myParameterValue);
102 			if ( baseType == "M" && ( myParameterValue != "fuse_on" && myParameterValue != "fuse_off" ) ) throw EModelError("mosfet cannot be overriden as " + myParameterValue);
103 			if ( baseType != "X" && myParameterValue != "switch_on" && myParameterValue != "switch_off" && myParameterValue != "fuse_on" && myParameterValue != "fuse_off" ) throw EModelError("invalid override " + myParameterValue);
104 			type = gModelTypeStringMap.at(myParameterValue);
105 		} else if (myParameterName == "condition") {
106 			CreateConditions(myParameterValue);
107 		} else if (myParameterName == "diode") {
108 			SetDiodes(myParameterValue);
109 		} else {
110 			throw EModelError("unknown parameter '" + theParameterString.substr(myStringBegin, myStringEnd - myStringBegin) + "' in " + definition);
111 		}
112 		myStringBegin = theParameterString.find_first_not_of(" \t", myStringEnd);
113 	}
114 	if ( diodeList.empty() ) {
115 		if ( IsNmos_(type) ) {
116 			diodeList.push_back(make_pair(4,1));
117 			diodeList.push_back(make_pair(4,3));
118 		} else if ( IsPmos_(type) ) {
119 			diodeList.push_back(make_pair(1,4));
120 			diodeList.push_back(make_pair(3,4));
121 		} else if ( type == DIODE ) {
122 			diodeList.push_back(make_pair(1,2));
123 		}
124 	}
125 }
126 
ParameterMatch(CParameterMap & theParameterMap,text_t theCellName)127 bool CModel::ParameterMatch(CParameterMap& theParameterMap, text_t theCellName) {
128 	CNormalValue myCheckValue;
129 
130 	for (CConditionPtrList::iterator condition_ppit = conditionPtrList.begin(); condition_ppit != conditionPtrList.end(); condition_ppit++) {
131 		try {
132 			myCheckValue = theParameterMap.at((*condition_ppit)->parameter);
133 			if ( ! (*condition_ppit)->CheckCondition(myCheckValue) ) return (false);
134 		}
135 		catch (const out_of_range& oor_exception) {
136 			throw EFatalError("missing parameter " + (*condition_ppit)->parameter + " in " + name);
137 		}
138 	}
139 	if ( cellFilterRegex_p ) {
140 		if ( ! regex_match(theCellName, *cellFilterRegex_p) ) return false;
141 	}
142 	return (true);
143 }
144 
CreateConditions(string theConditionString)145 void CModel::CreateConditions (string theConditionString) {
146 // condition string example
147 // condition=(L<0.4u w>=1.2u)
148 // condition=(r<10)
149 	string myConditionName;
150 	string myConditionRelation;
151 	string myConditionValue;
152 	size_t	myRelationIndex;
153 	size_t  myValueIndex;
154 	size_t	myStringBegin = theConditionString.find_first_not_of("( \t");
155 	size_t	myStringEnd = theConditionString.find_first_of(", )", myStringBegin);
156 	while ( myStringEnd < theConditionString.length() ) {
157 		myRelationIndex = theConditionString.find_first_of("<=>", myStringBegin);
158 		myValueIndex = theConditionString.find_first_not_of("<=>", myRelationIndex);
159 		myConditionName = trim_(theConditionString.substr(myStringBegin, myRelationIndex - myStringBegin));
160 		myConditionRelation = trim_(theConditionString.substr(myRelationIndex, myValueIndex - myRelationIndex));
161 		myConditionValue = trim_(theConditionString.substr(myValueIndex, myStringEnd - myValueIndex));
162 		toupper_(myConditionName);
163 		if ( myConditionName == "CELL" && myConditionRelation == "=" ) {
164 			cellFilter = myConditionValue;
165 			cellFilterRegex_p = new regex(FuzzyFilter(myConditionValue));
166 		} else {
167 			conditionPtrList.push_back(new CCondition(myConditionName, myConditionRelation, myConditionValue));
168 		}
169 		myStringBegin = theConditionString.find_first_not_of("), \t", myStringEnd);
170 		myStringEnd = theConditionString.find_first_of(", )", myStringBegin);
171 	}
172 }
173 
SetDiodes(string theDiodeString)174 void CModel::SetDiodes (string theDiodeString) {
175 // diode string example
176 // diode=(1-3,2-3)
177 // diode=(3-2)
178 	if ( ! diodeList.empty() ) {
179 		diodeList.clear();
180 	}
181 	int myAnode;
182 	int myCathode;
183 	size_t	myDelimterIndex;
184 	size_t	myStringBegin = theDiodeString.find_first_not_of("(, \t");
185 	size_t	myStringEnd = theDiodeString.find_first_of("), \t", myStringBegin);
186 	while ( myStringEnd < theDiodeString.length() ) {
187 		myDelimterIndex = theDiodeString.find_first_of("-", myStringBegin);
188 		myAnode = from_string<int>(trim_(theDiodeString.substr(myStringBegin, myDelimterIndex - myStringBegin)));
189 		myCathode = from_string<int>(trim_(theDiodeString.substr(myDelimterIndex+1, myStringEnd - myDelimterIndex - 1)));
190 		diodeList.push_back(make_pair(myAnode, myCathode));
191 		myStringBegin = theDiodeString.find_first_not_of("), \t", myStringEnd);
192 		myStringEnd = theDiodeString.find_first_of("), \t", myStringBegin);
193 	}
194 }
195 
Clear()196 void CModel::Clear() {
197 	while ( ! conditionPtrList.empty() ) {
198 		delete conditionPtrList.front();
199 		conditionPtrList.pop_front();
200 	}
201 }
202 
ModelCount()203 size_t CModel::ModelCount() {
204 	size_t myModelCount = 0;
205 	for (CDevice * myDevice_p = firstDevice_p; myDevice_p; myDevice_p = myDevice_p->nextDevice_p) {
206 		myModelCount += myDevice_p->parent_p->instanceCount;
207 	}
208 	return myModelCount;
209 }
210 
Print(ostream & theLogFile,bool thePrintDeviceListFlag,string theIndentation)211 void CModel::Print(ostream & theLogFile, bool thePrintDeviceListFlag, string theIndentation) {
212 	string myIndentation = theIndentation + " ";
213 	theLogFile << theIndentation << "Model> "<< setw(10) << left << name;
214 	theLogFile << right << setw(10) << ModelCount();
215 	theLogFile << " " << baseType << "->" << setw(10) << left << gModelTypeMap[type];
216 	theLogFile << " Parameters>";
217 	switch (type) {
218 		case NMOS: case PMOS: case LDDN: case LDDP: {
219 			theLogFile << " Vth=" << PrintToleranceParameter(vthDefinition, Vth, VOLTAGE_SCALE);
220 			if ( ! IsEmpty(maxVdsDefinition) ) theLogFile << " Vds=" << PrintToleranceParameter(maxVdsDefinition, maxVds, VOLTAGE_SCALE);
221 			if ( ! IsEmpty(maxVgsDefinition) ) theLogFile << " Vgs=" << PrintToleranceParameter(maxVgsDefinition, maxVgs, VOLTAGE_SCALE);
222 			if ( ! IsEmpty(maxVbgDefinition) ) theLogFile << " Vbg=" << PrintToleranceParameter(maxVbgDefinition, maxVbg, VOLTAGE_SCALE);
223 			if ( ! IsEmpty(maxVbsDefinition) ) theLogFile << " Vbs=" << PrintToleranceParameter(maxVbsDefinition, maxVbs, VOLTAGE_SCALE);
224 			theLogFile << " R=" << resistanceDefinition;
225 			break; }
226 		case RESISTOR: {
227 			if ( ! IsEmpty(maxVdsDefinition) ) theLogFile << " Vds=" << PrintToleranceParameter(maxVdsDefinition, maxVds, VOLTAGE_SCALE);
228 			if ( ! IsEmpty(resistanceDefinition) ) theLogFile << " R=" << resistanceDefinition;
229 			break; }
230 		case FUSE_ON:
231 		case FUSE_OFF:
232 		case CAPACITOR:
233 		case DIODE: {
234 			if ( ! IsEmpty(maxVdsDefinition) ) theLogFile << " Vds=" << PrintToleranceParameter(maxVdsDefinition, maxVds, VOLTAGE_SCALE);
235 			break; }
236 		case BIPOLAR:
237 		case SWITCH_ON:
238 		case SWITCH_OFF: {
239 			break; }
240 		default: {
241 			theLogFile << " Unknown type:";
242 		}
243 	}
244 	if ( ! conditionPtrList.empty() ) {
245 		theLogFile << " Conditions>";
246 		for (CConditionPtrList::iterator condition_ppit = conditionPtrList.begin(); condition_ppit != conditionPtrList.end(); condition_ppit++) {
247 			(*condition_ppit)->Print(theLogFile, "same-line");
248 		}
249 	}
250 	if ( cellFilter != "" ) {
251 		theLogFile << " Cell filter>" << cellFilter;
252 	}
253 	if ( ! diodeList.empty() ) {
254 		theLogFile << " Diodes>";
255 		for ( auto diode_pit = diodeList.begin(); diode_pit != diodeList.end(); diode_pit++ ) {
256 			theLogFile << " " << diode_pit->first << "-" << diode_pit->second;
257 		}
258 	}
259 	theLogFile << endl;
260 	if ( firstDevice_p && thePrintDeviceListFlag ) {
261 		theLogFile << myIndentation << "DeviceList> start" << endl;
262 		for ( CDevice * myDevice_p = firstDevice_p; myDevice_p != NULL; myDevice_p = myDevice_p->nextDevice_p ) {
263 			theLogFile << myIndentation << " " << myDevice_p->parent_p->name << "/" << myDevice_p->name << endl;
264 		}
265 		theLogFile << myIndentation << "DeviceList> end" << endl;
266 	}
267 }
268 
ConditionString()269 string CModel::ConditionString() {
270 	stringstream myConditionString;
271 	if ( ! conditionPtrList.empty() ) {
272 		myConditionString << " Conditions>";
273 		for (CConditionPtrList::iterator condition_ppit = conditionPtrList.begin(); condition_ppit != conditionPtrList.end(); condition_ppit++) {
274 			(*condition_ppit)->Print(myConditionString, "same-line");
275 		}
276 	}
277 	if ( cellFilter != "" ) {
278 		myConditionString << " Cell filter>" << cellFilter;
279 	}
280 	return(myConditionString.str());
281 }
282 
Clear()283 void CModelListMap::Clear() {
284 	while ( ! empty() ) {
285 		CModelList& myModelList = begin()->second;
286 		while ( ! myModelList.empty() ) {
287 			myModelList.front().Clear();
288 			myModelList.pop_front();
289 		}
290 		erase(begin());
291 	}
292 }
293 
AddModel(string theParameterString)294 void CModelListMap::AddModel(string theParameterString) {
295 	try {
296 		CModel	myNewModel(theParameterString);
297 		string myModelKey = myNewModel.baseType + " " + myNewModel.name;
298 		try {
299 			this->at(myModelKey).push_back(myNewModel);
300 			if ( (*this)[myModelKey].vthDefinition != myNewModel.vthDefinition ) {
301 				cout << "Vth mismatch for " << myNewModel.name << " ignored. ";
302 				cout << (*this)[myModelKey].vthDefinition << "!=" << myNewModel.vthDefinition << endl;
303 			}
304 		}
305 		catch (const out_of_range& oor_exception) {
306 			(*this)[myModelKey].push_back(myNewModel);
307 			(*this)[myModelKey].vthDefinition = myNewModel.vthDefinition;
308 		}
309 	}
310 	catch (EModelError & myError) {
311 		cout << myError.what() << endl;
312 		cout << "Invalid model format: " << theParameterString << endl;
313 		hasError = true;
314 	}
315 	catch (...) {
316 		cout << "Invalid model format: " << theParameterString << endl;
317 		hasError = true;
318 	}
319 }
320 
FindModel(text_t theCellName,text_t theParameterText,CTextResistanceMap & theParameterResistanceMap,ostream & theLogFile)321 CModel * CModelListMap::FindModel(text_t theCellName, text_t theParameterText, CTextResistanceMap& theParameterResistanceMap, ostream& theLogFile) {
322 	// FindModel: Set the model type based on theParameterText. Also adds entry to theParameterResistanceMap.
323 	string	myParameterString = trim_(string(theParameterText));
324 	string	myModelKey = myParameterString.substr(0, myParameterString.find(" ", 2));
325 	try {
326 		CParameterMap myParameterMap;
327 		if ( myParameterString.length() > myModelKey.length() ) {
328 			// if there are parameters besides the model name
329 			myParameterMap.CreateParameterMap(myParameterString.substr(myModelKey.length() + 1));
330 		}
331 		CModelList::iterator myLastModel = this->at(myModelKey).end();
332 		for (CModelList::iterator model_pit = this->at(myModelKey).begin(); model_pit != myLastModel; model_pit++) {
333 			if ( model_pit->ParameterMatch(myParameterMap, theCellName) ) {
334 				switch (model_pit->type) {
335 				// TODO: do not recalculate if already exists
336 					case NMOS: case PMOS: case LDDN: case LDDP: {
337 						theParameterResistanceMap[theParameterText] = myParameterMap.CalculateResistance(model_pit->resistanceDefinition);
338 						if ( theParameterResistanceMap[theParameterText] == MAX_RESISTANCE ) {
339 							theLogFile << "WARNING: resistance for " << theParameterText << " exceeds maximum" << endl;
340 						}
341 						break; }
342 					case RESISTOR: {
343 						theParameterResistanceMap[theParameterText] = myParameterMap.CalculateResistance(model_pit->resistanceDefinition);
344 						if ( theParameterResistanceMap[theParameterText] == MAX_RESISTANCE ) {
345 							theLogFile << "WARNING: resistance for " << theParameterText << " exceeds maximum" << endl;
346 						}
347 						break; }
348 					default: theParameterResistanceMap[theParameterText] = 1;
349 				}
350 
351 				return &(*model_pit);
352 			}
353 		}
354 		return (NULL);
355 	}
356 	catch (const out_of_range& oor_exception) {
357 		return (NULL);
358 	}
359 }
360 
FindModelList(string theModelName)361 CModelList * CModelListMap::FindModelList(string theModelName) {
362 	// FindModelList: Return a pointer to the first model list for theModelName
363 	//
364 	// Warning: is 2 different types of models have the same name, only returns the first.
365 	for (auto mapPair_pit = begin(); mapPair_pit != end(); mapPair_pit++) {
366 		if ( mapPair_pit->first.substr(2) == theModelName ) {
367 			return &mapPair_pit->second;
368 
369 		}
370 	}
371 	return NULL;
372 }
373 
Print(ostream & theLogFile,string theIndentation)374 void CModelListMap::Print(ostream & theLogFile, string theIndentation) {
375 	string myIndentation = theIndentation + " ";
376 	theLogFile << endl << theIndentation << "ModelList> filename " << filename << endl;
377 	for (CModelListMap::iterator modelList_pit = begin(); modelList_pit != end(); modelList_pit++) {
378 		for (CModelList::iterator model_pit = modelList_pit->second.begin(); model_pit != modelList_pit->second.end(); model_pit++) {
379 			model_pit->Print(theLogFile, PRINT_DEVICE_LIST_OFF, myIndentation);
380 		}
381 	}
382 	theLogFile << theIndentation << "ModelList> end" << endl << endl;
383 }
384 
DebugPrint(string theIndentation)385 void CModelListMap::DebugPrint(string theIndentation) {
386 	string myIndentation = theIndentation + " ";
387 	cout << theIndentation << "ModelList> start" << endl;
388 	for (CModelListMap::iterator modelList_pit = begin(); modelList_pit != end(); modelList_pit++) {
389 		for (CModelList::iterator model_pit = modelList_pit->second.begin(); model_pit != modelList_pit->second.end(); model_pit++) {
390 			model_pit->Print(cout, PRINT_DEVICE_LIST_ON, myIndentation);
391 		}
392 	}
393 	cout << theIndentation << "ModelList> end" << endl << endl;
394 }
395 
396 #define PERMIT_UNDEFINED true
SetVoltageTolerances(teestream & theReportFile,CPowerPtrMap & thePowerMacroPtrMap)397 returnCode_t CModelListMap::SetVoltageTolerances(teestream & theReportFile, CPowerPtrMap & thePowerMacroPtrMap) {
398 	theReportFile << "Setting model tolerances..." << endl;
399 	bool myToleranceErrorFlag = false;
400 	for (CModelListMap::iterator modelList_pit = begin(); modelList_pit != end(); modelList_pit++) {
401 		for (CModelList::iterator model_pit = modelList_pit->second.begin(); model_pit != modelList_pit->second.end(); model_pit++) {
402 			try {
403 				if ( ! IsEmpty(model_pit->vthDefinition) ) {
404 					model_pit->Vth = thePowerMacroPtrMap.CalculateVoltage(model_pit->vthDefinition, SIM_POWER, (*this), PERMIT_UNDEFINED);
405 					if ( model_pit->Vth == UNKNOWN_VOLTAGE ) model_pit->validModel = false;
406 				}
407 				if ( ! IsEmpty(model_pit->maxVbgDefinition) ) {
408 					model_pit->maxVbg = thePowerMacroPtrMap.CalculateVoltage(model_pit->maxVbgDefinition, SIM_POWER, (*this), PERMIT_UNDEFINED);
409 					if ( model_pit->maxVbg == UNKNOWN_VOLTAGE ) model_pit->validModel = false;
410 				}
411 				if ( ! IsEmpty(model_pit->maxVbsDefinition) ) {
412 					model_pit->maxVbs = thePowerMacroPtrMap.CalculateVoltage(model_pit->maxVbsDefinition, SIM_POWER, (*this), PERMIT_UNDEFINED);
413 					if ( model_pit->maxVbs == UNKNOWN_VOLTAGE ) model_pit->validModel = false;
414 				}
415 				if ( ! IsEmpty(model_pit->maxVdsDefinition) ) {
416 					model_pit->maxVds = thePowerMacroPtrMap.CalculateVoltage(model_pit->maxVdsDefinition, SIM_POWER, (*this), PERMIT_UNDEFINED);
417 					if ( model_pit->maxVds == UNKNOWN_VOLTAGE ) model_pit->validModel = false;
418 				}
419 				if ( ! IsEmpty(model_pit->maxVgsDefinition) ) {
420 					model_pit->maxVgs = thePowerMacroPtrMap.CalculateVoltage(model_pit->maxVgsDefinition, SIM_POWER, (*this), PERMIT_UNDEFINED);
421 					if ( model_pit->maxVgs == UNKNOWN_VOLTAGE ) model_pit->validModel = false;
422 				}
423 			}
424 			catch (EPowerError & myException) {
425 				theReportFile << "ERROR: Model tolerance " << myException.what() << endl;
426 				myToleranceErrorFlag = true;
427 			}
428 		}
429 	}
430 	return (myToleranceErrorFlag) ? FAIL : OK;
431 }
432 
433 
434