2015-01-21 21:58:35 +01:00
|
|
|
/*******************************************************************************
|
2021-01-09 14:26:49 +01:00
|
|
|
* Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
|
2015-01-21 21:58:35 +01:00
|
|
|
* All rights reserved. This program and the accompanying materials
|
|
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
* which accompanies this distribution, and is available at
|
|
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
*
|
|
|
|
* Contributors:
|
|
|
|
* MINRES Technologies GmbH - initial API and implementation
|
|
|
|
*******************************************************************************/
|
2015-01-06 17:14:16 +01:00
|
|
|
package com.minres.scviewer.database.vcd;
|
|
|
|
|
|
|
|
import java.io.*;
|
2020-11-29 10:25:48 +01:00
|
|
|
import java.text.ParseException;
|
2015-01-06 17:14:16 +01:00
|
|
|
import java.util.*;
|
|
|
|
|
2018-11-05 18:21:54 +01:00
|
|
|
import com.minres.scviewer.database.BitValue;
|
2015-01-09 09:16:40 +01:00
|
|
|
import com.minres.scviewer.database.BitVector;
|
|
|
|
|
2015-01-06 17:14:16 +01:00
|
|
|
class VCDFileParser {
|
|
|
|
private StreamTokenizer tokenizer;
|
|
|
|
private IVCDDatabaseBuilder traceBuilder;
|
2020-11-29 10:25:48 +01:00
|
|
|
private HashMap<String, Integer> nameToNetMap = new HashMap<>();
|
2015-01-06 17:14:16 +01:00
|
|
|
private long picoSecondsPerIncrement;
|
|
|
|
private boolean stripNetWidth;
|
2019-03-14 20:51:02 +01:00
|
|
|
private boolean replaceColon;
|
2015-01-06 17:14:16 +01:00
|
|
|
long currentTime;
|
|
|
|
|
|
|
|
public VCDFileParser(boolean stripNetWidth) {
|
|
|
|
this.stripNetWidth=stripNetWidth;
|
2019-03-14 20:51:02 +01:00
|
|
|
this.replaceColon=false;
|
2015-01-06 17:14:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean load(InputStream is, IVCDDatabaseBuilder builder) {
|
|
|
|
tokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(is)));
|
|
|
|
tokenizer.resetSyntax();
|
|
|
|
tokenizer.wordChars(33, 126);
|
|
|
|
tokenizer.whitespaceChars('\r', '\r');
|
|
|
|
tokenizer.whitespaceChars('\n', '\n');
|
|
|
|
tokenizer.whitespaceChars(' ', ' ');
|
|
|
|
tokenizer.whitespaceChars('\t', '\t');
|
|
|
|
try {
|
|
|
|
traceBuilder = builder;
|
|
|
|
currentTime=0;
|
|
|
|
while (parseDefinition());
|
|
|
|
while (parseTransition());
|
|
|
|
return true;
|
|
|
|
} catch (Exception exc) {
|
|
|
|
exc.printStackTrace();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-29 10:25:48 +01:00
|
|
|
private void parseScope() throws IOException, ParseException {
|
2015-01-06 17:14:16 +01:00
|
|
|
nextToken(); // Scope type (ignore)
|
|
|
|
nextToken();
|
|
|
|
traceBuilder.enterModule(tokenizer.sval);
|
|
|
|
match("$end");
|
|
|
|
}
|
|
|
|
|
2020-11-29 10:25:48 +01:00
|
|
|
private void parseUpscope() throws IOException, ParseException {
|
2015-01-06 17:14:16 +01:00
|
|
|
match("$end");
|
|
|
|
traceBuilder.exitModule();
|
|
|
|
}
|
|
|
|
|
2020-11-29 10:25:48 +01:00
|
|
|
private void parseVar() throws IOException {
|
2015-01-06 17:14:16 +01:00
|
|
|
nextToken(); // type
|
2018-10-11 11:20:36 +02:00
|
|
|
String type = tokenizer.sval;
|
2015-01-06 17:14:16 +01:00
|
|
|
nextToken(); // size
|
|
|
|
int width = Integer.parseInt(tokenizer.sval);
|
2018-10-11 11:20:36 +02:00
|
|
|
if("real".equals(type))
|
2018-11-05 18:21:54 +01:00
|
|
|
width=0;
|
2015-01-06 17:14:16 +01:00
|
|
|
nextToken();
|
|
|
|
String id = tokenizer.sval;
|
|
|
|
nextToken();
|
2020-11-29 10:25:48 +01:00
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
sb.append(tokenizer.sval);
|
2015-01-06 17:14:16 +01:00
|
|
|
while (nextToken() && !tokenizer.sval.equals("$end")) {
|
2020-11-29 10:25:48 +01:00
|
|
|
sb.append(tokenizer.sval);
|
2015-01-06 17:14:16 +01:00
|
|
|
}
|
2020-11-29 10:25:48 +01:00
|
|
|
String netName = sb.toString();
|
2015-01-06 17:14:16 +01:00
|
|
|
Integer net = nameToNetMap.get(id);
|
2019-03-14 20:51:02 +01:00
|
|
|
if (net == null) { // We've never seen this net before
|
|
|
|
int openBracket = netName.indexOf('[');
|
2015-01-06 17:14:16 +01:00
|
|
|
if(stripNetWidth){
|
|
|
|
if (openBracket != -1) netName = netName.substring(0, openBracket);
|
2019-03-14 20:51:02 +01:00
|
|
|
openBracket = -1;
|
|
|
|
}
|
|
|
|
if(replaceColon) {
|
|
|
|
if (openBracket != -1) {
|
2020-11-29 10:25:48 +01:00
|
|
|
netName = netName.substring(0, openBracket).replace(":", ".")+netName.substring(openBracket);
|
2019-03-14 20:51:02 +01:00
|
|
|
} else
|
2020-11-29 10:25:48 +01:00
|
|
|
netName=netName.replace(":", ".");
|
2015-01-06 17:14:16 +01:00
|
|
|
}
|
|
|
|
nameToNetMap.put(id, traceBuilder.newNet(netName, -1, width));
|
|
|
|
} else {
|
|
|
|
// Shares data with existing net. Add as clone.
|
|
|
|
traceBuilder.newNet(netName, net, width);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-29 10:25:48 +01:00
|
|
|
private void parseComment() throws IOException {
|
2019-03-14 20:51:02 +01:00
|
|
|
nextToken();
|
2020-11-29 10:25:48 +01:00
|
|
|
StringBuilder s = new StringBuilder();
|
|
|
|
s.append(tokenizer.sval);
|
2019-03-14 20:51:02 +01:00
|
|
|
nextToken();
|
|
|
|
while(!tokenizer.sval.equals("$end")){
|
2020-11-29 10:25:48 +01:00
|
|
|
s.append(" ").append(tokenizer.sval);
|
2019-03-14 20:51:02 +01:00
|
|
|
nextToken();
|
|
|
|
}
|
2020-11-29 10:25:48 +01:00
|
|
|
replaceColon|=s.toString().contains("ARTERIS Architecture");
|
2019-03-14 20:51:02 +01:00
|
|
|
}
|
|
|
|
|
2020-11-29 10:25:48 +01:00
|
|
|
private void parseTimescale() throws IOException {
|
2015-01-06 17:14:16 +01:00
|
|
|
nextToken();
|
2020-11-29 10:25:48 +01:00
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
sb.append(tokenizer.sval);
|
2015-01-06 17:14:16 +01:00
|
|
|
nextToken();
|
|
|
|
while(!tokenizer.sval.equals("$end")){
|
2020-11-29 10:25:48 +01:00
|
|
|
sb.append(" ").append(tokenizer.sval);
|
2015-01-06 17:14:16 +01:00
|
|
|
nextToken();
|
|
|
|
}
|
2020-11-29 10:25:48 +01:00
|
|
|
String s = sb.toString();
|
2015-01-06 17:14:16 +01:00
|
|
|
switch (s.charAt(s.length() - 2)){
|
|
|
|
case 'p': // Nano-seconds
|
|
|
|
picoSecondsPerIncrement = 1;
|
|
|
|
s = s.substring(0, s.length() - 2).trim();
|
|
|
|
break;
|
|
|
|
case 'n': // Nano-seconds
|
|
|
|
picoSecondsPerIncrement = 1000;
|
|
|
|
s = s.substring(0, s.length() - 2).trim();
|
|
|
|
break;
|
|
|
|
case 'u': // Microseconds
|
|
|
|
picoSecondsPerIncrement = 1000000;
|
|
|
|
s = s.substring(0, s.length() - 2).trim();
|
|
|
|
break;
|
|
|
|
case 'm': // Microseconds
|
|
|
|
picoSecondsPerIncrement = 1000000000;
|
|
|
|
s = s.substring(0, s.length() - 2).trim();
|
|
|
|
break;
|
|
|
|
default: // Seconds
|
|
|
|
picoSecondsPerIncrement = 1000000000000L;
|
|
|
|
s = s.substring(0, s.length() - 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
picoSecondsPerIncrement *= Long.parseLong(s);
|
|
|
|
}
|
|
|
|
|
2020-11-29 10:25:48 +01:00
|
|
|
private boolean parseDefinition() throws IOException, ParseException {
|
2015-01-06 17:14:16 +01:00
|
|
|
nextToken();
|
|
|
|
if (tokenizer.sval.equals("$scope"))
|
|
|
|
parseScope();
|
|
|
|
else if (tokenizer.sval.equals("$var"))
|
|
|
|
parseVar();
|
|
|
|
else if (tokenizer.sval.equals("$upscope"))
|
|
|
|
parseUpscope();
|
|
|
|
else if (tokenizer.sval.equals("$timescale"))
|
|
|
|
parseTimescale();
|
2019-03-14 20:51:02 +01:00
|
|
|
else if (tokenizer.sval.equals("$comment"))
|
|
|
|
parseComment();
|
2015-01-06 17:14:16 +01:00
|
|
|
else if (tokenizer.sval.equals("$enddefinitions")) {
|
|
|
|
match("$end");
|
|
|
|
return false;
|
2021-01-09 20:10:58 +01:00
|
|
|
} else do {
|
|
|
|
if (!nextToken()) return false;
|
|
|
|
} while (!tokenizer.sval.equals("$end"));
|
2015-01-06 17:14:16 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-29 10:25:48 +01:00
|
|
|
private boolean parseTransition() throws IOException {
|
2015-01-06 17:14:16 +01:00
|
|
|
if (!nextToken()) return false;
|
|
|
|
if (tokenizer.sval.charAt(0) == '#') { // If the line begins with a #, this is a timestamp.
|
|
|
|
currentTime = Long.parseLong(tokenizer.sval.substring(1)) * picoSecondsPerIncrement;
|
|
|
|
} else {
|
|
|
|
if(tokenizer.sval.equals("$comment")){
|
|
|
|
do {
|
|
|
|
if (!nextToken()) return false;
|
|
|
|
} while (!tokenizer.sval.equals("$end"));
|
|
|
|
return true;
|
|
|
|
}
|
2018-10-11 11:20:36 +02:00
|
|
|
if (tokenizer.sval.equals("$dumpvars") || tokenizer.sval.equals("$end"))
|
|
|
|
return true;
|
2020-11-29 10:25:48 +01:00
|
|
|
String value;
|
|
|
|
String id;
|
|
|
|
if (tokenizer.sval.charAt(0) == 'b' || tokenizer.sval.charAt(0) == 'r') {
|
|
|
|
// Multiple value net. Value appears first, followed by space, then identifier
|
2015-01-06 17:14:16 +01:00
|
|
|
value = tokenizer.sval.substring(1);
|
|
|
|
nextToken();
|
|
|
|
id = tokenizer.sval;
|
|
|
|
} else {
|
|
|
|
// Single value net. identifier first, then value, no space.
|
|
|
|
value = tokenizer.sval.substring(0, 1);
|
|
|
|
id = tokenizer.sval.substring(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Integer net = nameToNetMap.get(id);
|
2020-11-29 10:25:48 +01:00
|
|
|
if (net == null)
|
2015-01-06 17:14:16 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
int netWidth = traceBuilder.getNetWidth(net);
|
2018-11-05 18:21:54 +01:00
|
|
|
if(netWidth==0) {
|
2018-10-15 09:13:41 +02:00
|
|
|
if("nan".equals(value))
|
|
|
|
traceBuilder.appendTransition(net, currentTime, Double.NaN);
|
|
|
|
else
|
|
|
|
traceBuilder.appendTransition(net, currentTime, Double.parseDouble(value));
|
2015-01-06 17:14:16 +01:00
|
|
|
} else {
|
2018-10-11 11:20:36 +02:00
|
|
|
BitVector decodedValues = new BitVector(netWidth);
|
|
|
|
if (value.equals("z") && netWidth > 1) {
|
|
|
|
for (int i = 0; i < netWidth; i++)
|
2018-11-05 18:21:54 +01:00
|
|
|
decodedValues.setValue(i, BitValue.Z);
|
2018-10-11 11:20:36 +02:00
|
|
|
} else if (value.equals("x") && netWidth > 1) {
|
|
|
|
for (int i = 0; i < netWidth; i++)
|
2018-11-05 18:21:54 +01:00
|
|
|
decodedValues.setValue(i, BitValue.X);
|
2018-10-11 11:20:36 +02:00
|
|
|
} else {
|
|
|
|
int stringIndex = 0;
|
2018-11-05 18:21:54 +01:00
|
|
|
for (int convertedIndex = netWidth -1; convertedIndex >=0; convertedIndex--) {
|
|
|
|
if(convertedIndex<value.length()) {
|
|
|
|
switch (value.charAt(stringIndex++)) {
|
|
|
|
case 'z':
|
|
|
|
decodedValues.setValue(convertedIndex, BitValue.Z);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '1':
|
|
|
|
decodedValues.setValue(convertedIndex, BitValue.ONE);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '0':
|
|
|
|
decodedValues.setValue(convertedIndex, BitValue.ZERO);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'x':
|
|
|
|
decodedValues.setValue(convertedIndex, BitValue.X);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
decodedValues.setValue(convertedIndex, BitValue.X);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
decodedValues.setValue(convertedIndex, BitValue.ZERO);
|
2018-10-11 11:20:36 +02:00
|
|
|
}
|
2015-01-06 17:14:16 +01:00
|
|
|
}
|
|
|
|
}
|
2018-11-05 18:21:54 +01:00
|
|
|
traceBuilder.appendTransition(net, currentTime, decodedValues);
|
2015-01-06 17:14:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-29 10:25:48 +01:00
|
|
|
private void match(String value) throws ParseException, IOException {
|
2015-01-06 17:14:16 +01:00
|
|
|
nextToken();
|
|
|
|
if (!tokenizer.sval.equals(value))
|
2020-11-29 10:25:48 +01:00
|
|
|
throw new ParseException("Line "+tokenizer.lineno()+": parse error, expected "+value+" got "+tokenizer.sval, tokenizer.lineno());
|
2015-01-06 17:14:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean nextToken() throws IOException {
|
|
|
|
return tokenizer.nextToken() != StreamTokenizer.TT_EOF;
|
|
|
|
}
|
|
|
|
|
2020-11-29 10:25:48 +01:00
|
|
|
}
|