teroshdl
Version:
Backend for terosHDL IDE
781 lines (727 loc) • 26.9 kB
JavaScript
/* eslint-disable no-empty */
// Copyright 2020 Teros Technology
//
// Ismael Perez Rojo
// Carlos Alberto Ruiz Naranjo
// Alfredo Saez
//
// This file is part of Colibri.
//
// Colibri 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.
//
// Colibri 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 Colibri. If not, see <https://www.gnu.org/licenses/>.
const stm_base = require('./stm_base_parser');
const Path = require('path');
class Paser_stm_verilog extends stm_base.Parser_stm_base {
constructor(comment_symbol, parser) {
super();
this.set_symbol(comment_symbol);
if (parser !== undefined){
this.parser = parser;
this.loaded_wasm = true;
}
}
set_comment_symbol(comment_symbol) {
this.set_symbol(comment_symbol);
}
async init() {
if (this.loaded_wasm !== true){
try{
const Parser = require('web-tree-sitter');
await Parser.init();
this.parser = new Parser();
let Lang = await Parser.Language.load(Path.join(__dirname, Path.sep +
"parsers" + Path.sep + "tree-sitter-verilog.wasm"));
this.parser.setLanguage(Lang);
this.loaded_wasm = true;
}
catch(e){}
}
}
async get_svg_sm(code, comment_symbol) {
this.set_symbol(comment_symbol);
let process;
try {
const tree = this.parser.parse(code);
process = this.get_process(tree);
}
catch (e) {
return { 'svg': [], 'stm': [] };
}
let stm = [];
let svg = [];
for (let i = 0; i < process.length; ++i) {
let states;
try {
states = this.get_process_info(process[i]);
}
catch (e) {
states = undefined;
}
if (states !== undefined) {
for (let j = 0; j < states.length; ++j) {
if (this.check_stm(states[j]) === true) {
stm.push(states[j]);
let svg_tmp = this.json_to_svg(states[j]);
let stm_tmp = {
'svg': svg_tmp,
'description': states[j].description
};
svg.push(stm_tmp);
}
}
}
}
return { 'svg': svg, 'stm': stm };
}
get_process(tree) {
let process_array = [];
let arch_body = this.get_architecture_body(tree);
let cursor = arch_body.walk();
let comments = '';
// Process
cursor.gotoFirstChild();
do {
if (cursor.nodeType === 'module_or_generate_item') {
cursor.gotoFirstChild();
do {
if (cursor.nodeType === 'always_construct') {
let process = {
'code': this.get_deep_process(cursor.currentNode()),
'comments': comments.trim()
};
process_array.push(process);
comments = '';
}
else {
comments = '';
}
}
while (cursor.gotoNextSibling() !== false);
cursor.gotoParent();
}
else if (cursor.nodeType === 'comment') {
comments += this.get_comment(cursor.nodeText);
}
else {
comments = '';
}
}
while (cursor.gotoNextSibling() !== false);
return process_array;
}
get_deep_process(p) {
let statement = this.get_item_from_childs(p, 'statement');
let statement_item = this.get_item_from_childs(statement, 'statement_item');
let procedural_timing_control_statement =
this.get_item_from_childs(statement_item, 'procedural_timing_control_statement');
if (procedural_timing_control_statement === undefined){
let seq_block = this.get_item_from_childs(statement_item, 'seq_block');
return seq_block;
}
let statement_or_null = this.get_item_from_childs(procedural_timing_control_statement, 'statement_or_null');
let statement_2 = this.get_item_from_childs(statement_or_null, 'statement');
let statement_item_2 = this.get_item_from_childs(statement_2, 'statement_item');
let seq_block = this.get_item_from_childs(statement_item_2, 'seq_block');
if (seq_block === undefined){
let cond_statement = this.get_item_from_childs(statement_item_2, 'conditional_statement');
return cond_statement;
}
return seq_block;
}
get_architecture_body(p) {
let break_p = false;
let arch_body = undefined;
let cursor = p.walk();
cursor.gotoFirstChild();
do {
if (cursor.nodeType === 'module_declaration') {
arch_body = cursor.currentNode();
break_p = true;
}
}
while (cursor.gotoNextSibling() === true && break_p === false);
return arch_body;
}
get_process_info(proc) {
let stms = [];
let p = proc.code;
let name = this.get_process_label(p);
let case_statements = this.get_case_process(p);
for (let i = 0; i < case_statements.length; ++i) {
let description = proc.comments;
let p_info = {
'description': description.replace('fsm_extract', ''),
'name': '',
'state_variable_name': '',
'states': []
};
p_info.name = name;
if (case_statements !== undefined && case_statements.length !== 0) {
p_info.state_variable_name = this.get_state_variable_name(case_statements[i]);
p_info.states = this.get_states(case_statements[i], p_info.state_variable_name);
let check = this.check_empty_states_transitions(p_info.states);
if (check === true) {
let result = this.force_case_stm(case_statements[i]);
p_info.state_variable_name = result.variable_name;
p_info.states = result.states;
}
stms.push(p_info);
}
}
return stms;
}
//////////////////////////////////////////////////////////////////////////////
// Force
//////////////////////////////////////////////////////////////////////////////
force_case_stm(p) {
let state_names = this.get_state_names_from_case(p);
let state_name_candidate = this.search_state_variable_candidates(p, state_names);
let states = this.get_states(p, state_name_candidate);
return { 'variable_name': state_name_candidate, 'states': states };
}
get_state_names_from_case(p) {
let state_names = [];
let state_names_case = this.search_multiple_in_tree(p, 'case_item_expression');
for (let i = 0; i < state_names_case.length; ++i) {
state_names.push(state_names_case[i].text);
}
return state_names;
}
search_state_variable_candidates(p, state_names) {
let candidates = [];
let signals = this.search_multiple_in_tree(p, 'blocking_assignment');
for (let i = 0; i < signals.length; ++i) {
let rigth = this.get_rigth_simple_waveform_assignment(signals[i]);
if (rigth !== undefined) {
let left = this.get_left_simple_waveform_assignment(signals[i]);
if (state_names.includes(rigth) === true) {
candidates.push(left);
}
}
}
let variables = this.search_multiple_in_tree(p, 'nonblocking_assignment');
for (let i = 0; i < variables.length; ++i) {
let rigth = this.get_rigth_simple_variable_assignment(variables[i]);
if (rigth !== undefined) {
let left = this.get_rigth_simple_variable_assignment(variables[i]);
if (state_names.includes(rigth) === true) {
candidates.push(left);
}
}
}
let unique = this.mode(candidates);
return unique;
}
mode(array) {
if (array.length == 0)
return null;
var mode_map = {};
var max_el = array[0], max_count = 1;
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (mode_map[el] == null)
mode_map[el] = 1;
else
mode_map[el]++;
if (mode_map[el] > max_count) {
max_el = el;
max_count = mode_map[el];
}
}
return max_el;
}
//////////////////////////////////////////////////////////////////////////////
get_states(p, state_variable_name) {
let case_items = this.get_item_multiple_from_childs(p, 'case_item');
let case_state = [];
for (let i = 0; i < case_items.length; ++i) {
let state = {
'name': '',
'transitions': [],
'start_position': [],
'end_position': []
};
let result = this.get_item_from_childs(case_items[i], 'case_item_expression');
if (result !== undefined && result.text !== 'default') {
state.name = result.text;
state.start_position = [result.startPosition.row, result.startPosition.column];
state.end_position = [result.endPosition.row, result.endPosition.column];
state.transitions = this.get_transitions(case_items[i], state_variable_name);
case_state.push(state);
}
}
return case_state;
}
get_transitions(p, state_variable_name, metacondition) {
let assign_transitions = [];
let if_transitions = [];
let last_transitions = [];
let transitions = [];
let skip = false;
let last = 0;
let statement_or_null;
if (p.type !== 'statement_or_null') {
statement_or_null = this.get_item_from_childs(p, 'statement_or_null');
}
else {
statement_or_null = p.walk().currentNode();
}
let statement = this.get_item_from_childs(statement_or_null, 'statement');
let statement_item = this.get_item_from_childs(statement, 'statement_item');
let seq_block = this.get_item_from_childs(statement_item, 'seq_block');
let itera_item = [];
if (seq_block === undefined) {
itera_item = [statement_item];
skip = true;
}
else {
itera_item = this.get_item_multiple_from_childs(seq_block, 'statement_or_null');
}
for (let i = 0; i < itera_item.length; ++i) {
let statement_item_2 = itera_item[i];
if (skip === false) {
let statement_2 = this.get_item_from_childs(itera_item[i], 'statement');
statement_item_2 = this.get_item_from_childs(statement_2, 'statement_item');
}
//Search if
let type;
let block;
let if_statement = this.get_item_from_childs(statement_item_2, 'conditional_statement');
if (if_statement === undefined) {
//Search assignment
let assign_statement = this.get_item_from_childs(statement_item_2, 'blocking_assignment');
if (assign_statement !== undefined) {
type = 'simple_waveform_assignment';
block = assign_statement;
}
else {
let nonassign_statement = this.get_item_from_childs(statement_item_2, 'nonblocking_assignment');
if (nonassign_statement !== undefined) {
type = 'simple_waveform_assignment';
block = nonassign_statement;
}
}
}
else {
type = 'if_statement';
block = if_statement;
}
if (type === 'if_statement') {
let tmp_transitions = this.get_if_transitions(block, state_variable_name, metacondition);
if_transitions = if_transitions.concat(tmp_transitions);
last = 0;
}
else if (type === 'simple_waveform_assignment') {
let tmp_transitions = this.get_assignament_transitions(block, state_variable_name, metacondition);
if (tmp_transitions.length !== 0 && tmp_transitions !== undefined) {
assign_transitions = tmp_transitions;
last_transitions = tmp_transitions;
last = 1;
}
}
}
if (last === 1) {
transitions = last_transitions;
}
else {
transitions = if_transitions.concat(assign_transitions);
}
return transitions;
}
get_if_transitions(p, state_variable_name, metacondition) {
let transitions = [];
let ifs = this.get_if_elsif_else(p);
//Set else condition
let conditions = [];
let else_condition = '';
for (let i = 0; i < ifs.length; ++i) {
let condition = ifs[i].condition;
if (condition !== '' && conditions.includes(condition) === false) {
else_condition += `not (${condition})\n`;
}
else {
let tmp_condition = else_condition.slice(0, -1);
//Remove duplicate conditions
let current_conditions = tmp_condition.split('\n');
let unique = current_conditions.filter(this.only_unique);
let condition_tmp = '';
for (let i = 0; i < unique.length - 1; ++i) {
condition_tmp += unique[i] + '\n';
}
condition_tmp += unique[unique.length - 1] + '\n';
condition = condition_tmp;
ifs[i].condition = condition;
}
conditions.push(condition);
}
for (let i = 0; i < ifs.length; ++i) {
let transition = this.get_transition(ifs[i], state_variable_name, metacondition);
if (transition !== undefined) {
transitions = transitions.concat(transition);
}
}
return transitions;
}
get_if_elsif_else(p) {
let ifs = [];
let cursor = p.walk();
cursor.gotoFirstChild();
do {
if (cursor.nodeType === 'else') {
let break_p = false;
while (break_p === false && cursor.gotoNextSibling() !== false) {
if (cursor.nodeType === 'statement_or_null') {
let item = this.get_item_from_childs(cursor.currentNode(), 'statement');
let statement_item = this.get_item_from_childs(item, 'statement_item');
let block_item = this.get_item_from_childs(statement_item, 'seq_block');
if (block_item !== undefined) {
item = this.get_item_from_childs(block_item, 'statement_or_null');
item = this.get_item_from_childs(item, 'statement');
statement_item = this.get_item_from_childs(item, 'statement_item');
}
item = this.get_item_from_childs(statement_item, 'conditional_statement');
if (item !== undefined) {
let tmp_ifs = this.get_if_elsif_else(item);
ifs = ifs.concat(tmp_ifs);
}
else {
let if_item_else = {
'condition': '',
'code': '',
'start_position': '',
'end_position': ''
};
let blocking_assignment = this.get_item_from_childs(statement_item, 'blocking_assignment');
if (blocking_assignment !== undefined) {
if (block_item !== undefined) {
if_item_else.code = block_item;
// if_item_else.start_position = start_position;
// if_item_else.end_position = end_position;
}
else {
if_item_else.code = statement_item;
// if_item_else.start_position = start_position;
// if_item_else.end_position = end_position;
}
ifs.push(if_item_else);
}
else {
let nonblocking_assignment = this.get_item_from_childs(statement_item, 'nonblocking_assignment');
if (nonblocking_assignment !== undefined) {
if (block_item !== undefined) {
if_item_else.code = block_item;
// if_item_else.start_position = start_position;
// if_item_else.end_position = end_position;
}
else {
if_item_else.code = statement_item;
// if_item_else.start_position = start_position;
// if_item_else.end_position = end_position;
}
ifs.push(if_item_else);
}
}
}
}
}
}
else if (cursor.nodeType === 'if') {
let break_p = false;
let if_item = {
'condition': '',
'code': ''
};
while (break_p === false && cursor.gotoNextSibling() !== false) {
if (cursor.nodeType === 'cond_predicate') {
let item = this.get_item_from_childs(cursor.currentNode(), 'expression_or_cond_pattern');
if (item !== undefined) {
if_item.condition = item.text;
if_item.start_position = item.startPosition;
if_item.end_position = item.endPosition;
}
}
else if (cursor.nodeType === 'statement_or_null') {
let item = this.get_item_from_childs(cursor.currentNode(), 'statement');
item = this.get_item_from_childs(item, 'statement_item');
if (this.get_item_from_childs(item, 'seq_block') !== undefined) {
item = this.get_item_from_childs(item, 'seq_block');
if_item.start_position = item.startPosition;
if_item.end_position = item.endPosition;
// item = this.get_item_from_childs(item, 'statement_or_null');
// item = this.get_item_from_childs(item, 'statement');
// item = this.get_item_from_childs(item, 'statement_item');
}
if_item.code = item;
break_p = true;
ifs.push(if_item);
}
}
}
}
while (cursor.gotoNextSibling() !== false);
return ifs;
}
get_assignament_transitions(p, state_variable_name, metacondition) {
let transitions = [];
let tmp_destination = this.check_get_simple_waveform_assignment(p, state_variable_name);
if (tmp_destination !== undefined) {
let s_position = p.startPosition;
let e_position = p.endPosition;
let start_position = [s_position.row, e_position.column - 1];
let end_position = [e_position.row, e_position.column];
let condition = '';
if (metacondition !== '' && metacondition !== undefined) {
condition = metacondition;
}
let destination = tmp_destination;
let transition = {
'condition': condition,
'destination': destination,
'start_position': start_position,
'end_position': end_position
};
transitions.push(transition);
}
return transitions;
}
get_transition(p, state_variable_name, metacondition) {
let condition = p.condition;
let tmp_start_position = p.start_position;
let tmp_end_position = p.end_position;
let start_position = [tmp_start_position.row, tmp_start_position.column];
let end_position = [tmp_end_position.row, tmp_end_position.column];
let transitions = this.get_transitions_in_if(p.code, state_variable_name,
condition, start_position, end_position, metacondition);
return transitions;
}
get_start_position_array(p) {
let tmp_position = p.code.startPosition;
return tmp_position;
}
get_end_position_array(p) {
let tmp_position = p.code.endPosition;
return tmp_position;
}
get_transitions_in_if(p, state_variable_name, condition, start_position, end_position, metacondition) {
let last = 0;
let last_transitions = [];
//if transitions
let if_transitions = [];
//assign transitions
let assign_transitions = [];
let transitions = [];
let destination = undefined;
let cursor = p.walk();
cursor.gotoFirstChild();
do {
if (cursor.nodeType === 'blocking_assignment' || cursor.nodeType === 'nonblocking_assignment') {
let tmp_destination = this.check_get_simple_waveform_assignment(cursor.currentNode(), state_variable_name);
if (tmp_destination !== undefined) {
destination = tmp_destination;
if (condition !== undefined && destination !== undefined) {
let transition = {
'condition': '',
'destination': '',
'start_position': start_position,
'end_position': end_position
};
if (metacondition !== undefined && metacondition !== '') {
condition += `\n${metacondition}`;
let current_conditions = condition.split('\n');
let unique = current_conditions.filter(this.only_unique);
let condition_tmp = '';
for (let i = 0; i < unique.length - 1; ++i) {
condition_tmp += unique[i] + '\n';
}
condition_tmp += unique[unique.length - 1] + '\n';
condition = condition_tmp;
}
transition.condition = condition;
transition.destination = destination;
last = 1;
assign_transitions = [transition];
last_transitions = [transition];
}
}
}
else if (cursor.nodeType === 'simple_variable_assignment') {
let tmp_destination = this.check_get_simple_variable_assignment(cursor.currentNode(), state_variable_name);
if (tmp_destination !== undefined) {
destination = tmp_destination;
if (condition !== undefined && destination !== undefined) {
let transition = {
'condition': '',
'destination': '',
'start_position': start_position,
'end_position': end_position
};
if (metacondition !== undefined && metacondition !== '') {
condition += `\n${metacondition}`;
let current_conditions = condition.split('\n');
let unique = current_conditions.filter(this.only_unique);
let condition_tmp = '';
for (let i = 0; i < unique.length - 1; ++i) {
condition_tmp += unique[i] + '\n';
}
condition_tmp += unique[unique.length - 1] + '\n';
condition = condition_tmp;
}
transition.condition = condition;
transition.destination = destination;
last = 1;
assign_transitions = [transition];
last_transitions = [transition];
}
}
}
else if (cursor.nodeType === 'conditional_statement') {
last = 0;
let if_transitions_tmp = this.get_if_transitions(cursor.currentNode(), state_variable_name, condition);
if_transitions = if_transitions.concat(if_transitions_tmp);
}
else if (cursor.nodeType === 'statement_or_null') {
last = 0;
//check assignement
let item = this.get_item_from_childs(cursor.currentNode(), 'statement');
item = this.get_item_from_childs(item, 'statement_item');
let item_0 = this.get_item_from_childs(item, 'blocking_assignment');
let item_1 = this.get_item_from_childs(item, 'nonblocking_assignment');
let if_item = true;
if (item_0 !== undefined || item_1 !== undefined) {
if_item = false;
}
if (metacondition !== undefined && metacondition !== '') {
condition += `\n${metacondition}`;
let current_conditions = condition.split('\n');
let unique = current_conditions.filter(this.only_unique);
let condition_tmp = '';
for (let i = 0; i < unique.length - 1; ++i) {
condition_tmp += unique[i] + '\n';
}
condition_tmp += unique[unique.length - 1] + '\n';
condition = condition_tmp;
}
let if_transitions_tmp = [];
//check block if
let item_block_if = this.get_item_from_childs(item, 'conditional_statement');
if (item_block_if === undefined) {
if_transitions_tmp = this.get_transitions(cursor.currentNode(), state_variable_name, condition);
}
else {
if_transitions_tmp = this.get_if_transitions(item_block_if, state_variable_name, condition);
}
if (if_item === false) {
if (if_transitions_tmp.length !== 0) {
assign_transitions = if_transitions_tmp;
last_transitions = if_transitions_tmp;
}
}
else {
if_transitions = if_transitions.concat(if_transitions_tmp);
}
}
}
while (cursor.gotoNextSibling() !== false);
if (last !== 0) {
transitions = last_transitions;
}
else {
transitions = if_transitions.concat(assign_transitions);
}
return transitions;
}
check_get_simple_waveform_assignment(p, state_variable_name) {
let destination = undefined;
let left = this.get_left_simple_waveform_assignment(p);
if (left === state_variable_name) {
destination = this.get_rigth_simple_waveform_assignment(p);
}
return destination;
}
check_get_simple_variable_assignment(p, state_variable_name) {
let destination = undefined;
let left = this.get_left_simple_waveform_assignment(p);
if (left === state_variable_name) {
destination = this.get_rigth_simple_variable_assignment(p);
}
return destination;
}
get_left_simple_waveform_assignment(p) {
let left = '';
let item = this.get_item_from_childs(p, 'operator_assignment');
item = this.get_item_from_childs(item, 'variable_lvalue');
if (item !== undefined) {
left = item.text;
}
if (left === '') {
item = this.get_item_from_childs(p, 'variable_lvalue');
if (item !== undefined) {
left = item.text;
}
}
return left;
}
get_rigth_simple_waveform_assignment(p) {
let rigth = undefined;
let item = this.get_item_from_childs(p, 'operator_assignment');
item = this.get_item_from_childs(item, 'expression');
if (item !== undefined) {
rigth = item.text;
}
if (rigth === undefined) {
item = this.get_item_from_childs(p, 'expression');
if (item !== undefined) {
rigth = item.text;
}
}
return rigth;
}
get_rigth_simple_variable_assignment(p) {
let rigth = undefined;
let cursor = p.walk();
cursor.gotoFirstChild();
do {
if (cursor.nodeType === 'simple_name') {
rigth = cursor.nodeText;
}
}
while (cursor.gotoNextSibling() !== false);
return rigth;
}
get_state_variable_name(p) {
let state_variable_name = undefined;
let case_expression = this.get_item_from_childs(p, 'case_expression');
if (case_expression !== undefined) {
state_variable_name = case_expression.text;
}
return state_variable_name;
}
get_case_process(p) {
let case_statement = this.search_multiple_in_tree(p, 'case_statement');
return case_statement;
}
get_process_label(p) {
let label_txt = '';
let label = this.get_item_from_childs(p, "block_identifier");
if (label === undefined) {
label_txt = ''
}
else {
label_txt = label.text;
}
return label_txt;
}
}
module.exports = {
Paser_stm_verilog: Paser_stm_verilog
};