/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: SCSettings.java
* Written by Jonathan Gainsley, Sun Microsystems.
*
* Copyright (c) 2009 Sun Microsystems and Static Free Software
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.simulation.sctiming;
import com.sun.electric.database.text.TextUtils;
import java.util.List;
import java.util.ArrayList;
/**
* Library-wide settings
* User: gainsley
* Date: Nov 16, 2006
*/
public class SCSettings {
String simulator = "hspice";
public String libName = "sclib";
public String commonHeaderFile = "";
String bufferCell = null;
String bufferCellStrengthParam = null;
String bufferCellInputPort = null;
String bufferCellOutputPort = null;
String bufferCellSweep = null;
String bufferCellSweepMinTime = null;
String bufferCellSweepExcludeFromAveraging = null;
String loadCell = null;
String loadCellStrengthParam = null;
String loadCellPort = null;
String loadCellSweepExcludeFromAveraging = null;
String loadCellSweep = null;
String loadCellSweepMinTime = null;
String loadCellSweepForSetupHold = null;
String loadCellSweepForSetupHoldMinTime = null;
String clkBufferCell = null;
String clkBufferCellStrengthParam = null;
String clkBufferCellInputPort = null;
String clkBufferCellOutputPort = null;
String clkBufferCellSweep = null;
String clkBufferCellSweepMinTime = null;
String operatingPointName = null;
public double vdd = 0;
double temp = 25;
double tech = 1;
double inputRampTimePS = 50;
double simResolutionPS = 1;
double simTimePS = 10000;
double timeStartPS = 200;
double periodPS = 1000;
double inputLow = 0.2;
double inputHigh = 0.8;
double inputDelayThresh = 0.5;
double outputLow = 0.2;
double outputHigh = 0.8;
double outputDelayThresh = 0.5;
double edgePercentForCapStart = 0.05;
double edgePercentForCapEnd = 0.55;
double holdGlitchHighPercent = 0.8;
double holdGlitchLowPercent = 0.2;
double tmsetupMinGuessPS = 0;
double tmsetupMaxGuessPS = 300;
double tmsetupGuessPS = 100;
double clk2qpushout = 20;
double capUnit = 1e-12; // if you change the cap or time units, change them below as well
double timeUnit = 1e-9;
boolean simpleSequentialCharacterization = false;
/**
* Set the simulator (typically hspice). If the simulator is not on your
* PATH, you should specify the path here as well.
* @param pathandname full path to executable
*/
public void setSimulator(String pathandname) { this.simulator = pathandname; }
/**
* Set the common header file to be included in all characterization netlists.
* This should include the spice model file .lib statement and the buffer,
* clock buffer, and output load subckts.
* @param pathandname full path to file
*/
public void setCommonHeaderFile(String pathandname) { this.commonHeaderFile = pathandname; }
/**
* Set VDD for the simulation
* @param name a name of this operating point
* @param vdd the voltage
* @param temp the temperature
*/
public void setOperatingPoint(String name, double vdd, double temp) {
this.operatingPointName = name;
this.vdd = vdd;
this.temp = temp;
}
/**
* Get Vdd setting
* @return vdd setting
*/
public double getVdd() { return vdd; }
/**
* Set the name of library. This only affects the library name
* used when writing the Liberty file.
* @param libName name of library
*/
public void setLibrary(String libName) { this.libName = libName; }
/**
* Set the buffer cell used to drive the input. This should be a
* buffer (non-inverting), with one input and one output, and
* a parameter used to vary its drive strength.
* All unspecified pins are tied to ground.
* @param cellname the name of the subckt
* @param strengthParam the name of the strength parameter
* @param inputPort the name of the input port
* @param outputPort the name of the output port
*/
public void setBufferCell(String cellname, String strengthParam,
String inputPort, String outputPort) {
this.bufferCell = cellname;
this.bufferCellStrengthParam = strengthParam;
this.bufferCellInputPort = inputPort;
this.bufferCellOutputPort = outputPort;
}
/**
* Set the clock buffer cell used to drive the clock pin input
* (and clock false input if specified on the arc).
* This should be a buffer (non-inverting), with one input and
* one output, and a parameter used to vary its drive strength.
* All unspecified pins are tied to ground.
* @param cellname the name of the subckt
* @param strengthParam the name of the strength parameter
* @param inputPort the name of the input port
* @param outputPort the name of the output port
*/
public void setClkBufferCell(String cellname, String strengthParam, String inputPort, String outputPort) {
this.clkBufferCell = cellname;
this.clkBufferCellStrengthParam = strengthParam;
this.clkBufferCellInputPort = inputPort;
this.clkBufferCellOutputPort = outputPort;
}
/**
* Set the load cell used to load the output of the test cell.
* There should be one input pin, and a parameter used to
* vary its drive strength.
* All unspecified pins are tied to ground.
* @param cellname the name of the subckt
* @param strengthParam the name of the strength parameter
* @param loadPort the name of the load port
*/
public void setLoadCell(String cellname, String strengthParam, String loadPort) {
this.loadCell = cellname;
this.loadCellStrengthParam = strengthParam;
this.loadCellPort = loadPort;
}
/**
* Set the time the simulation will run, and the resolution in ps.
* Typically the resolution should be 1ps, and the duration should
* be 1000ps.
* @param resolutionPS the resolution in ps
* @param durationPS the duration in ps
*/
public void setSimulationTime(double resolutionPS, double durationPS) {
this.simResolutionPS = resolutionPS;
this.simTimePS = durationPS;
}
/**
* Set the input buffer sweep. This will set the values of
* the input buffer strength param. The string argument should
* be a space separated list of the form "1 2 4 8".
* @param sweep the list of strengths to sweep
*/
public void setInputBufferSweep(String sweep) {
bufferCellSweep = sweep;
}
/**
* Set the input buffer sweep for min time. This will set the values of
* the input buffer strength param. The string argument should
* be a space separated list of the form "1 2 4 8".
* @param sweep the list of strengths to sweep
*/
public void setInputBufferSweepMinTime(String sweep) {
bufferCellSweepMinTime = sweep;
}
/**
* Set the load cell sweep. This will set the values of
* the load cell strength param. The string argument should
* be a space separated list of the form "1 2 4 8".
* @param sweep the list of strengths to sweep
*/
public void setLoadSweep(String sweep) {
loadCellSweep = sweep;
if (loadCellSweepForSetupHold == null)
loadCellSweepForSetupHold = sweep;
}
/**
* Set the load cell sweep for min time. This will set the values of
* the load cell strength param. The string argument should
* be a space separated list of the form "1 2 4 8".
* @param sweep the list of strengths to sweep
*/
public void setLoadSweepMinTime(String sweep) {
loadCellSweepMinTime = sweep;
if (loadCellSweepForSetupHoldMinTime == null)
loadCellSweepForSetupHoldMinTime = sweep;
}
/**
* Set the load cell sweep for setup and hold, otherwise
* it defaults to whatever you set for the normal load cell sweep
* (used for clk2q in sequential tests).
* The string argument should
* be a space separated list of the form "1 2 4 8".
* @param sweep the list of strengths to sweep
*/
public void setLoadSweepForSetupHold(String sweep) {
loadCellSweepForSetupHold = sweep;
}
/**
* Set the load cell sweep for setup and hold (min time), otherwise
* it defaults to whatever you set for the normal load cell sweep
* (used for clk2q in sequential tests).
* The string argument should
* be a space separated list of the form "1 2 4 8".
* @param sweep the list of strengths to sweep
*/
public void setLoadSweepForSetupHoldMinTime(String sweep) {
loadCellSweepForSetupHoldMinTime = sweep;
}
/**
* Set the clock buffer size. This will set the value of
* the clock buffer strength param.
* Normally, for the clock, only one value is used,
* as the timing analyzer does not analyze the clock
* tree (assumes ideal clock). Therefore, it cannot
* predict the clock edge rate, so it assumes
* the clock tree is designed to produce this
* edge rate everywhere.
* @param size the size of the clock buffer
*/
public void setClkBufferSize(String size) {
clkBufferCellSweep = size;
}
/**
* Set the clock buffer size for min time. This will set the value of
* the clock buffer strength param.
* Normally, for the clock, only one value is used,
* as the timing analyzer does not analyze the clock
* tree (assumes ideal clock). Therefore, it cannot
* predict the clock edge rate, so it assumes
* the clock tree is designed to produce this
* edge rate everywhere.
* @param size the size of the clock buffer
*/
public void setClkBufferSizeMinTime(String size) {
clkBufferCellSweepMinTime = size;
}
/**
* Set the input low, high, and delay measurement thresholds,
* as a percentage of vdd. These numbers must be between 0 and 1.
* @param low low level threshold (usually 0.2)
* @param high high level threshold (usually 0.8)
* @param delay delay level threshold (usually 0.5)
*/
public void setInputThresholds(double low, double high, double delay) {
this.inputLow = low;
this.inputHigh = high;
this.inputDelayThresh = delay;
}
/**
* Set the output low, high, and delay measurement thresholds,
* as a percentage of vdd. These numbers must be between 0 and 1.
* @param low low level threshold (usually 0.2)
* @param high high level threshold (usually 0.8)
* @param delay delay level threshold (usually 0.5)
*/
public void setOutputThresholds(double low, double high, double delay) {
this.outputLow = low;
this.outputHigh = high;
this.outputDelayThresh = delay;
}
/**
* Set the ramp time used by input and clock edges of
* PWL voltage sources used to drive input and clock
* buffers.
* @param inputRampTimePS the edge rate, 0 to 100%, in ps.
*/
public void setInputRampTimePS(double inputRampTimePS) {
this.inputRampTimePS = inputRampTimePS;
}
/**
* Set the setup time range when doing sequential characterization.
* Note that characterization will run faster if the range is smaller.
* Values are in picoseconds. Default is 0, 100ps, 300ps.
* @param low minimum possible value (typically 0 or negative)
* @param guess a guess as to the actual value (though it will vary wrt to input drive strength)
* @param high maximum possible value
*/
public void setSetupTimeRangePS(double low, double guess, double high) {
tmsetupGuessPS = guess;
tmsetupMaxGuessPS = high;
tmsetupMinGuessPS = low;
}
/**
* Set the values of the buffer cell sweep to
* ignore for clock-to-Q times. Clk-to-Q is assumed to be independent
* of input rise/fall times, but that is not strictly true - especially
* if the range of input rise/fall times used is quite large. This method allows
* you to mask out the outliers to get a more reasonable clock-to-q value.
* <P>
* The string passed in should be a space separated set of values, such
* as "1 2 3 4".
* @param values the values to exclude
*/
public void setInputBufferSweepExcludeFromAveraging(String values) {
this.bufferCellSweepExcludeFromAveraging = values;
}
/**
* Set the values of the load cell sweep to exclude from the calculation of
* setup and hold times. Setup and Hold times
* are assumed to be independent of output load, but that is not strictly true -
* especially if the range of loads is quite large. This method allows you
* to mask out the outliers to get a more reasonable setup and hold value.
* <P>
* The string passed in should be a space separated set of values, such
* as "1 2 3 4".
* @param values the values to exclude
*/
public void setLoadSweepExcludeFromAveraging(String values) {
this.loadCellSweepExcludeFromAveraging = values;
}
/**
* Set the setup, hold, and clock-2-q characterization to simple
* mode (matches Sun Microelectronics). Rather than optimizing
* for a minimal setup+clk2q delay, this measures clk2q on a
* static input, and then decreases the setup time until the clk2q
* delay gets pushed out (moved) by an amount specified.
* @param b true to set to simple mode
*/
public void setSimpleSequentialCharacterization(boolean b) {
this.simpleSequentialCharacterization = b;
}
/**
* For simple setup sequential characterization mode, set the amount
* of clk2q push out (degredation) that defines the setup time.
* @param ps the amount of push out in ps
*/
public void setClk2QPushOut(double ps) {
this.clk2qpushout = ps;
}
/**
* Set the percentages of vdd at which the hold test considers the signal
* to be glitched - high for a glitch on vdd, and low for a glitch on gnd.
* @param high the high value glitch (eg 0.8 for 80% of vdd)
* @param low the low value glitch (eg 0.2 for 20% of vdd)
*/
public void setHoldTimeGlitchPercentages(double high, double low) {
this.holdGlitchHighPercent = high;
this.holdGlitchLowPercent = low;
}
void checkSettings(boolean sequentialTest) throws SCTimingException {
err(libName == null, "Library name not specified");
err(bufferCell == null, "Buffer cell not specified");
err(bufferCellInputPort == null, "Buffer cell input port not specified");
err(bufferCellOutputPort == null, "Buffer cell output port not specified");
err(bufferCellStrengthParam == null, "Buffer strength param not specified");
err(bufferCellSweep == null, "Sweep for input buffer cell not specified");
err(loadCell == null, "Load cell not specified");
err(loadCellPort == null, "Load cell load port not specified");
err(loadCellStrengthParam == null, "Load cell strength param not specified");
err(loadCellSweep == null, "Sweep for load cell not specified");
err(loadCellSweepForSetupHold == null, "Sweep for load cell not specified for setup and hold");
err(operatingPointName == null, "Operating point (vdd, temp) not specified");
if (sequentialTest) {
if (bufferCellSweepMinTime == null) bufferCellSweepMinTime = bufferCellSweep;
if (loadCellSweepMinTime == null) loadCellSweepMinTime = loadCellSweep;
if (clkBufferCellSweepMinTime == null) clkBufferCellSweepMinTime = clkBufferCellSweep;
if (loadCellSweepForSetupHoldMinTime == null) loadCellSweepForSetupHoldMinTime = loadCellSweepForSetupHold;
err(clkBufferCell == null, "Clock buffer cell not specified");
err(clkBufferCellInputPort == null, "Clock buffer input port not specfieid");
err(clkBufferCellOutputPort == null, "Clock buffer output port not specfieid");
err(clkBufferCellStrengthParam == null, "Clock buffer strength param not specified");
err(clkBufferCellSweep == null, "Sweep for clock buffer cell not specified");
if (simpleSequentialCharacterization) {
}
}
}
void err(boolean print, String msg) throws SCTimingException {
if (print) {
//System.out.println("SCTiming: Error: "+msg);
throw new SCTimingException(msg);
}
}
/* *******************************************************
* Liberty file data
* *******************************************************/
String tableSlewVsLoads = "tableInputSlewVsCLoad";
String tableSetupHold = "tableInputSlewVsClkSlew";
String tableClk2Q = "tableInputClkSlewVsCLoad";
int numClkSlews = 0;
/**
* Get a Liberty Data Group that is the Library with
* all settings used for this characterization.
* @return a Liberty Data Group
*/
public LibData.Group getLibrary() {
LibData.Group library = new LibData.Group("library", libName, null);
// parameters and units etc
library.putAttributeComplex("technology", "cmos");
library.putAttribute("delay_model", "table_lookup");
List<LibData.Value> attrList = new ArrayList<LibData.Value>();
attrList.add(new LibData.Value(LibData.ValueType.INT, new Integer(1)));
attrList.add(new LibData.Value(LibData.ValueType.STRING, getUnitScale(capUnit)+"f"));
LibData.Head h = new LibData.Head("capacitive_load_unit", attrList);
library.putAttribute(new LibData.Attribute("capacitive_load_unit", h));
library.putAttribute("time_unit", "\"1"+ getUnitScale(timeUnit)+"s\"");
library.putAttribute("voltage_unit", "\"1V\"");
library.putAttribute("current_unit", "\"1A\"");
library.putAttribute("input_threshold_pct_rise", inputDelayThresh*100);
library.putAttribute("input_threshold_pct_fall", inputDelayThresh*100);
library.putAttribute("output_threshold_pct_rise", outputDelayThresh*100);
library.putAttribute("output_threshold_pct_fall", outputDelayThresh*100);
library.putAttribute("slew_lower_threshold_pct_rise", inputLow*100);
library.putAttribute("slew_upper_threshold_pct_rise", inputHigh*100);
library.putAttribute("slew_lower_threshold_pct_fall", inputLow*100);
library.putAttribute("slew_upper_threshold_pct_fall", inputHigh*100);
library.putAttribute("pulling_resistance_unit", "\"1ohm\"");
library.putAttribute("default_fanout_load", "1.0");
library.putAttribute("default_inout_pin_cap", "1.0");
library.putAttribute("default_input_pin_cap", "1.0");
library.putAttribute("default_output_pin_cap", "0.0");
LibData.Group op = new LibData.Group("operating_conditions", operatingPointName, library);
op.putAttribute("voltage", vdd);
op.putAttribute("temperature", temp);
op.putAttribute("process", 1.0);
// sc timing specific (not liberty supported) options
/*
library.putAttribute("simulator", simulator);
library.putAttribute("commonHeaderFile", commonHeaderFile);
library.putAttribute("bufferCell", bufferCell);
library.putAttribute("bufferCellStrengthParam", bufferCellStrengthParam);
library.putAttribute("bufferCellInputPort", bufferCellInputPort);
library.putAttribute("bufferCellOutputPort", bufferCellOutputPort);
library.putAttribute("bufferCellSweep", bufferCellSweep);
library.putAttribute("loadCell", loadCell);
library.putAttribute("loadCellStrengthParam", loadCellStrengthParam);
library.putAttribute("loadCellPort", loadCellPort);
library.putAttribute("loadCellSweep", loadCellSweep);
library.putAttribute("clkBufferCell", clkBufferCell);
library.putAttribute("clkBufferCellStrengthParam", clkBufferCellStrengthParam);
library.putAttribute("clkBufferCellInputPort", clkBufferCellInputPort);
library.putAttribute("clkBufferCellOutputPort", clkBufferCellOutputPort);
library.putAttribute("clkBufferCellSweep", clkBufferCellSweep);
library.putAttribute("vdd", vdd);
library.putAttribute("inputRampTimePS", inputRampTimePS);
library.putAttribute("simTimePS", simTimePS);
library.putAttribute("simResolutionPS", simResolutionPS);
library.putAttribute("tmsetupMinGuessPS", tmsetupMinGuessPS);
library.putAttribute("tmsetupGuessPS", tmsetupGuessPS);
library.putAttribute("tmsetupMaxGuessPS", tmsetupMaxGuessPS);
*/
// table definitions
// table slew vs loads
int numSlews = bufferCellSweep.trim().split("\\s+").length;
int numLoads = loadCellSweep.trim().split("\\s+").length;
StringBuffer slewsCount = new StringBuffer("\"");
for (int i=1; i<=numSlews; i++) slewsCount.append(i+" ");
slewsCount.append("\"");
StringBuffer loadsCount = new StringBuffer("\"");
for (int i=1; i<=numLoads; i++) loadsCount.append(i+" ");
loadsCount.append("\"");
LibData.Group luTableSlewVsLoads = new LibData.Group("lu_table_template", tableSlewVsLoads, library);
luTableSlewVsLoads.putAttribute("variable_1", "input_net_transition");
luTableSlewVsLoads.putAttribute("variable_2", "total_output_net_capacitance");
luTableSlewVsLoads.putAttributeComplex("index_1", slewsCount.toString());
luTableSlewVsLoads.putAttributeComplex("index_2", loadsCount.toString());
// table setup and hold
numClkSlews = clkBufferCellSweep.trim().split("\\s+").length;
StringBuffer clksCount = new StringBuffer("\"");
for (int i=1; i<=numClkSlews; i++) clksCount.append(i+" ");
clksCount.append("\"");
LibData.Group luTableSetupHold = new LibData.Group("lu_table_template", tableSetupHold, library);
luTableSetupHold.putAttribute("variable_1", "constrained_pin_transition");
if (numClkSlews > 1)
luTableSetupHold.putAttribute("variable_2", "related_pin_transition");
luTableSetupHold.putAttributeComplex("index_1", slewsCount.toString());
if (numClkSlews > 1)
luTableSetupHold.putAttributeComplex("index_2", clksCount.toString());
// table clock to Q
LibData.Group luTableClk2Q = new LibData.Group("lu_table_template", tableClk2Q, library);
if (numClkSlews > 1) {
luTableClk2Q.putAttribute("variable_1", "input_net_transition");
luTableClk2Q.putAttribute("variable_2", "total_output_net_capacitance");
luTableClk2Q.putAttributeComplex("index_1", clksCount.toString());
luTableClk2Q.putAttributeComplex("index_2", loadsCount.toString());
} else {
luTableClk2Q.putAttribute("variable_1", "total_output_net_capacitance");
luTableClk2Q.putAttributeComplex("index_1", loadsCount.toString());
}
return library;
}
/**
* Set the settings of this object from some liberty data
* @param data
*/
public void getSettingsFromLibData(LibData data) {
if (data == null) return;
LibData.Group library = data.getLibrary();
if (library == null) return;
inputDelayThresh = getDoubleAttribute(library, "input_threshold_pct_rise", inputDelayThresh*100)/100;
outputDelayThresh = getDoubleAttribute(library, "output_threshold_pct_rise", outputDelayThresh*100)/100;
inputLow = getDoubleAttribute(library, "slew_lower_threshold_rise", inputLow*100)/100;
inputHigh = getDoubleAttribute(library, "slew_upper_threshold_rise", inputHigh*100)/100;
simulator = getStringAttribute(library, "simulator", simulator);
commonHeaderFile = getStringAttribute(library, "commonHeaderFile", commonHeaderFile);
bufferCell = getStringAttribute(library, "bufferCell", bufferCell);
bufferCellStrengthParam = getStringAttribute(library, "bufferCellStrengthParam", bufferCellStrengthParam);
bufferCellInputPort = getStringAttribute(library, "bufferCellInputPort", bufferCellInputPort);
bufferCellOutputPort = getStringAttribute(library, "bufferCellOutputPort", bufferCellOutputPort);
bufferCellSweep = getStringAttribute(library, "bufferCellSweep", bufferCellSweep);
loadCell = getStringAttribute(library, "loadCell", loadCell);
loadCellStrengthParam = getStringAttribute(library, "loadCellStrengthParam", loadCellStrengthParam);
loadCellPort = getStringAttribute(library, "loadCellPort", loadCellPort);
loadCellSweep = getStringAttribute(library, "loadCellSweep", loadCellSweep);
clkBufferCell = getStringAttribute(library, "clkBufferCell", clkBufferCell);
clkBufferCellStrengthParam = getStringAttribute(library, "clkBufferCellStrengthParam", clkBufferCellStrengthParam);
clkBufferCellInputPort = getStringAttribute(library, "clkBufferCellInputPort", clkBufferCellInputPort);
clkBufferCellOutputPort = getStringAttribute(library, "clkBufferCellOutputPort", clkBufferCellOutputPort);
clkBufferCellSweep = getStringAttribute(library, "clkBufferCellSweep", clkBufferCellSweep);
vdd = getDoubleAttribute(library, "vdd", vdd);
inputRampTimePS = getDoubleAttribute(library, "inputRampTimePS", inputRampTimePS);
simTimePS = getDoubleAttribute(library, "simTimePS", simTimePS);
simResolutionPS = getDoubleAttribute(library, "simResolutionPS", simResolutionPS);
tmsetupGuessPS = getDoubleAttribute(library, "tmsetupGuessPS", tmsetupGuessPS);
tmsetupMinGuessPS = getDoubleAttribute(library, "tmsetupMinGuessPS", tmsetupMinGuessPS);
tmsetupMaxGuessPS = getDoubleAttribute(library, "tmsetupMaxGuessPS", tmsetupMaxGuessPS);
}
private static String getStringAttribute(LibData.Group group, String name, String defaultVal) {
LibData.Attribute attr = group.getAttribute(name);
if (attr == null) return defaultVal;
String val = attr.getString();
if (val == null) return defaultVal;
return val;
}
private static double getDoubleAttribute(LibData.Group group, String name, double defaultVal) {
LibData.Attribute attr = group.getAttribute(name);
if (attr == null) return defaultVal;
Double val = attr.getDouble();
if (val == null) {
// try converting string
String sval = attr.getString();
if (sval == null) return defaultVal;
try {
return Double.parseDouble(sval);
} catch (NumberFormatException e) {
return defaultVal;
}
}
return val.doubleValue();
}
private static String getUnitScale(double d) {
String s = TextUtils.formatDoublePostFix(d);
for (int i=0; i<s.length(); i++) {
char c = s.charAt(i);
if (Character.isLetter(c))
return String.valueOf(c);
}
return "";
}
}