379 lines
12 KiB
C
379 lines
12 KiB
C
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "vcd_parser.h"
|
|
|
|
#ifndef VCDWASM
|
|
#include <node_api.h>
|
|
#else
|
|
#include "wasm_main.hpp"
|
|
#endif
|
|
|
|
#ifdef VCDWASM
|
|
typedef void* napi_env;
|
|
#endif
|
|
|
|
// #define LOGSPAN
|
|
// #define LOGSPAN printf("%s\n", __FUNCTION__);
|
|
|
|
#define ASSERT(val, expr) \
|
|
if (expr != napi_ok) { \
|
|
napi_throw(env, val); \
|
|
}
|
|
|
|
void strcopy(const unsigned char* p, const unsigned char* endp,
|
|
unsigned char* dst) {
|
|
const unsigned char* src;
|
|
src = p;
|
|
while (src < (endp - 1)) {
|
|
*dst = *src;
|
|
src++;
|
|
dst++;
|
|
}
|
|
*dst = 0;
|
|
}
|
|
|
|
void strconcat(const unsigned char* p, const unsigned char* endp,
|
|
unsigned char* dst) {
|
|
// printf("<len:%d>", endp - p);
|
|
dst += strlen((char*)dst); // go to the end of string
|
|
while (p < endp) {
|
|
*dst = *p;
|
|
p++;
|
|
dst++;
|
|
}
|
|
*dst = 0;
|
|
}
|
|
|
|
// FIXME use a better structure to match strings
|
|
int stringEq(const unsigned char* i, // search pattern
|
|
const unsigned char* p, const unsigned char* endp) {
|
|
if (*i == 0) {
|
|
return 1;
|
|
}
|
|
const unsigned char* j;
|
|
while (1) {
|
|
j = p;
|
|
// printf("(%s|%s)", (char *)i, (char *)j);
|
|
if (*i == 0) { // end of search pattern
|
|
return 0;
|
|
}
|
|
while (*i == *j) { // follow matching trail
|
|
if (*i == 0 && *j == 0) { // match zeros
|
|
return 1;
|
|
}
|
|
i++;
|
|
j++;
|
|
}
|
|
while (*i != 0) { // skip to the end of pattern word
|
|
i++;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
int commandSpan(vcd_parser_t* state, const unsigned char* p,
|
|
const unsigned char* endp) {
|
|
const uint8_t command = state->command;
|
|
#ifndef VCDWASM
|
|
napi_env env = state->napi_env;
|
|
|
|
// if (command == 5) { // $upscope
|
|
// state->stackPointer -= 1;
|
|
// return 0;
|
|
// }
|
|
|
|
if ((command == 1) || // $comment
|
|
(command == 2) || // $date
|
|
(command == 3) || // $scope
|
|
(command == 4) || // $timescale
|
|
(command == 5) || // $upscope
|
|
(command == 6) || // $var
|
|
(command == 7) // $version
|
|
) {
|
|
char* key;
|
|
switch (command) {
|
|
case 1:
|
|
key = "comment";
|
|
break;
|
|
case 2:
|
|
key = "date";
|
|
break;
|
|
case 4:
|
|
key = "timescale";
|
|
break;
|
|
// case 6: key = "var"; break;
|
|
case 7:
|
|
key = "version";
|
|
break;
|
|
}
|
|
|
|
napi_value val;
|
|
ASSERT(val,
|
|
napi_create_string_latin1(env, (char*)p, (endp - p - 4), &val))
|
|
ASSERT(state->info, napi_set_named_property(env, state->info, key, val))
|
|
return 0;
|
|
}
|
|
#else
|
|
if ((command > 0) && (command < 8)) {
|
|
const int len = endp - p;
|
|
int tailLen = 3;
|
|
if (len < 4) {
|
|
tailLen = len;
|
|
}
|
|
strcopy(p, endp - tailLen, state->tmpStr);
|
|
// set_property_string(key, state->tmpStr);
|
|
on_command(state->tmpStr, command);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if (command == 8) { // $enddefinitions
|
|
*(char*)state->idStr = 0;
|
|
*(char*)state->timeStampStr = 0;
|
|
#ifndef VCDWASM
|
|
napi_value status, undefined, eventName, eventPayload, return_val;
|
|
ASSERT(status, napi_create_string_latin1(env, "simulation",
|
|
NAPI_AUTO_LENGTH, &status))
|
|
ASSERT(state->info,
|
|
napi_set_named_property(env, state->info, "status", status))
|
|
ASSERT(undefined, napi_get_undefined(env, &undefined))
|
|
ASSERT(eventName,
|
|
napi_create_string_latin1(env, "$enddefinitions",
|
|
NAPI_AUTO_LENGTH, &eventName))
|
|
// ASSERT(eventPayload, napi_create_string_latin1(env, "payload",
|
|
// NAPI_AUTO_LENGTH, &eventPayload))
|
|
napi_value* argv[] = {&eventName}; // , &eventPayload };
|
|
ASSERT(state->lifee, napi_call_function(env, undefined, state->lifee, 1,
|
|
*argv, &return_val))
|
|
#else
|
|
set_property_string("status", "simulation");
|
|
emit_lifee("$enddefinitions");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int scopeIdentifierSpan(vcd_parser_t* state, const unsigned char* p,
|
|
const unsigned char* endp) {
|
|
strcopy(p, endp, state->tmpStr); // load the value into temp string 1
|
|
#ifndef VCDWASM
|
|
napi_env env = state->napi_env;
|
|
napi_value obj, stack, top;
|
|
ASSERT(obj, napi_create_object(env, &obj))
|
|
ASSERT(state->info,
|
|
napi_get_named_property(env, state->info, "stack", &stack))
|
|
|
|
// get the top of the stack in top
|
|
ASSERT(top, napi_get_element(env, stack, state->stackPointer, &top))
|
|
|
|
// set top.prop to new object
|
|
ASSERT(top, napi_set_named_property(env, top, state->tmpStr, obj))
|
|
|
|
state->stackPointer += 1;
|
|
ASSERT(top, napi_set_element(env, stack, state->stackPointer, obj))
|
|
#else
|
|
// set stack[sp].`tmpStr` to {}
|
|
snprintf(state->tmpStr2, 4096, "stack.%d.%s", state->stackPointer,
|
|
(char*)state->tmpStr);
|
|
new_object_path(state->tmpStr2);
|
|
|
|
// bump
|
|
state->stackPointer += 1;
|
|
|
|
// set stack[sp+1] to the same object as stack[sp].`tmpStr`
|
|
snprintf(state->tmpStr, 4096, "stack.%d", state->stackPointer);
|
|
|
|
set_path_to_path(state->tmpStr, state->tmpStr2);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int varSizeSpan(vcd_parser_t* state, const unsigned char* p,
|
|
const unsigned char* endp) {
|
|
const int32_t size = strtol((const char*)p, (char**)&endp, 10);
|
|
state->size = size;
|
|
#ifndef VCDWASM
|
|
#else
|
|
set_property_int("varType", state->type);
|
|
set_property_int("varSize", size);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int varIdSpan(vcd_parser_t* state, const unsigned char* p,
|
|
const unsigned char* endp) {
|
|
#ifndef VCDWASM
|
|
napi_env env = state->napi_env;
|
|
napi_value varId;
|
|
ASSERT(varId,
|
|
napi_create_string_latin1(env, (char*)p, (endp - p - 1), &varId))
|
|
ASSERT(state->info,
|
|
napi_set_named_property(env, state->info, "varId", varId))
|
|
#else
|
|
strcopy(p, endp, state->tmpStr);
|
|
set_property_string("varId", state->tmpStr);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int varNameSpan(vcd_parser_t* state, const unsigned char* p,
|
|
const unsigned char* endp) {
|
|
#ifndef VCDWASM
|
|
napi_env env = state->napi_env;
|
|
// *(endp - 1) = 0; // FIXME NULL termination of ASCII string
|
|
strcopy(p, endp, state->tmpStr);
|
|
napi_value stack, top, varId;
|
|
ASSERT(state->info,
|
|
napi_get_named_property(env, state->info, "stack", &stack))
|
|
ASSERT(top, napi_get_element(env, stack, state->stackPointer, &top))
|
|
ASSERT(state->info,
|
|
napi_get_named_property(env, state->info, "varId", &varId))
|
|
ASSERT(state->info, napi_set_named_property(env, top, state->tmpStr, varId))
|
|
#else
|
|
strcopy(p, endp, state->tmpStr);
|
|
// set
|
|
// info.stack[sp].`tmpStr` = info.varId
|
|
snprintf(state->tmpStr2, 4096, "stack.%d.%s", state->stackPointer,
|
|
(char*)state->tmpStr);
|
|
set_path_to_path(state->tmpStr2, "varType,varSize,varId");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int idSpan(vcd_parser_t* state, const unsigned char* p,
|
|
const unsigned char* endp) {
|
|
strconcat(p, endp, state->idStr);
|
|
// printf("<idSpan|%s>\n", (char *)state->idStr);
|
|
return 0;
|
|
}
|
|
|
|
int onId(vcd_parser_t* state, const unsigned char* _p,
|
|
const unsigned char* _endp) {
|
|
#ifndef VCDWASM
|
|
napi_env env = state->napi_env;
|
|
#endif
|
|
const unsigned char* p = (unsigned char*)state->idStr;
|
|
const unsigned int plen = strlen((char*)p) - 1;
|
|
*(char*)(p + plen) = 0; // null instead of space
|
|
const unsigned char* endp = p + plen - 1;
|
|
// printf("<onId|%s>\n", (char *)state->idStr);
|
|
|
|
const int valueWords = (state->digitCount + 63) >> 6;
|
|
const int maskWords = (state->maskCount + 63) >> 6;
|
|
uint64_t* value = state->value;
|
|
uint64_t* mask = state->mask;
|
|
if (stringEq((state->trigger), p, endp)) {
|
|
const uint8_t command = state->command;
|
|
// printf("{id:'%s',cmd:%d}", (char *)p, command);
|
|
// if (command == 14) {
|
|
// value[0] = 0;
|
|
// mask[0] = 0;
|
|
// } else
|
|
// if (command == 15) {
|
|
// value[0] = 1;
|
|
// mask[0] = 0;
|
|
// }
|
|
#ifndef VCDWASM
|
|
napi_value undefined, eventName, aTime, aCommand, aValue, aMask,
|
|
return_val;
|
|
ASSERT(undefined, napi_get_undefined(env, &undefined))
|
|
ASSERT(eventName, napi_create_string_latin1(
|
|
env, (char*)p, NAPI_AUTO_LENGTH, &eventName))
|
|
ASSERT(aTime, napi_create_int64(env, state->time, &aTime))
|
|
ASSERT(aCommand, napi_create_int32(env, command, &aCommand))
|
|
ASSERT(aValue,
|
|
napi_create_bigint_words(env, 0, valueWords, value, &aValue))
|
|
ASSERT(aMask, napi_create_bigint_words(env, 0, maskWords, mask, &aMask))
|
|
napi_value* argv[] = {&eventName, &aTime, &aCommand, &aValue, &aMask};
|
|
ASSERT(state->triee, napi_call_function(env, undefined, state->triee, 5,
|
|
*argv, &return_val))
|
|
// printf("<id='%s'>", (char *)p);
|
|
#else
|
|
// strcopy(p, endp, state->tmpStr);
|
|
emit_triee((char*)p, state->time, command, valueWords, value, maskWords,
|
|
mask);
|
|
#endif
|
|
}
|
|
for (int i = 0; i < valueWords; i++) {
|
|
value[i] = 0;
|
|
}
|
|
for (int i = 0; i < maskWords; i++) {
|
|
mask[i] = 0;
|
|
}
|
|
state->digitCount = 0;
|
|
state->maskCount = 0;
|
|
*(char*)state->idStr = 0;
|
|
return 0;
|
|
}
|
|
|
|
// 匹配 value id 这样的格式
|
|
int onDigit(vcd_parser_t* state, const unsigned char* _p,
|
|
const unsigned char* _endp, int digit) {
|
|
unsigned int valueCin = (digit & 1);
|
|
unsigned int maskCin = ((digit >> 1) & 1);
|
|
|
|
if ((valueCin != 0) || (state->digitCount != 0)) {
|
|
unsigned int valueCout;
|
|
uint64_t* value = state->value;
|
|
const int valueWordsMinus = (state->digitCount >> 6);
|
|
for (int i = 0; i <= valueWordsMinus; i++) {
|
|
valueCout = value[i] >> 63;
|
|
value[i] = (value[i] << 1) + valueCin;
|
|
valueCin = valueCout;
|
|
}
|
|
state->digitCount += 1;
|
|
}
|
|
if ((maskCin != 0) || (state->maskCount != 0)) {
|
|
unsigned int maskCout;
|
|
uint64_t* mask = state->mask;
|
|
const int maskWordsMinus = (state->maskCount >> 6);
|
|
for (int i = 0; i <= maskWordsMinus; i++) {
|
|
maskCout = mask[i] >> 63;
|
|
mask[i] = (mask[i] << 1) + maskCin;
|
|
maskCin = maskCout;
|
|
}
|
|
state->maskCount += 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int onRecover(vcd_parser_t* state, const unsigned char* p,
|
|
const unsigned char* endp, int digit) {
|
|
state->digitCount = 0;
|
|
state->maskCount = 0;
|
|
return 0;
|
|
}
|
|
|
|
int timeSpan(vcd_parser_t* state, const unsigned char* p,
|
|
const unsigned char* endp) {
|
|
strconcat(p, endp, state->timeStampStr);
|
|
// printf("<timeSpan|%s>\n", (char *)state->timeStampStr);
|
|
return 0;
|
|
}
|
|
|
|
int onTime(vcd_parser_t* state, const unsigned char* _p,
|
|
const unsigned char* _endp) {
|
|
char* end;
|
|
const int64_t time = strtoul(state->timeStampStr, &end, 10);
|
|
// printf("<onTime|%lu>\n", time);
|
|
if (state->time == INT64_MAX) {
|
|
#ifndef VCDWASM
|
|
napi_env env = state->napi_env;
|
|
napi_value val;
|
|
ASSERT(val, napi_create_int64(env, time, &val))
|
|
ASSERT(state->info,
|
|
napi_set_named_property(env, state->info, "t0", val))
|
|
#else
|
|
set_property_int("t0", time);
|
|
#endif
|
|
}
|
|
state->time = time;
|
|
*(char*)state->timeStampStr = 0;
|
|
return 0;
|
|
}
|