vlog95: Add support for translating a SV function return statement
This commit is contained in:
parent
8b7dde0e41
commit
11696e23d1
|
|
@ -22,6 +22,8 @@
|
||||||
# include "vlog95_priv.h"
|
# include "vlog95_priv.h"
|
||||||
# include "ivl_alloc.h"
|
# include "ivl_alloc.h"
|
||||||
|
|
||||||
|
const char *func_rtn_name = 0;
|
||||||
|
|
||||||
static char*get_time_const(int time_value)
|
static char*get_time_const(int time_value)
|
||||||
{
|
{
|
||||||
switch (time_value) {
|
switch (time_value) {
|
||||||
|
|
@ -787,12 +789,117 @@ static void emit_specify(ivl_scope_t scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for a disable in the statement (function body) for this scope.
|
||||||
|
*/
|
||||||
|
static unsigned has_func_disable(ivl_scope_t scope, ivl_statement_t stmt)
|
||||||
|
{
|
||||||
|
unsigned idx, count, rtn = 0;
|
||||||
|
/* If there is a statement then look to see if it is or has a
|
||||||
|
* disable for this function scope. */
|
||||||
|
if (! stmt) return 0;
|
||||||
|
assert(ivl_scope_type(scope) == IVL_SCT_FUNCTION);
|
||||||
|
switch (ivl_statement_type(stmt)) {
|
||||||
|
/* These are not allowed in a function. */
|
||||||
|
case IVL_ST_ASSIGN_NB:
|
||||||
|
case IVL_ST_DELAY:
|
||||||
|
case IVL_ST_DELAYX:
|
||||||
|
case IVL_ST_FORK:
|
||||||
|
case IVL_ST_FORK_JOIN_ANY:
|
||||||
|
case IVL_ST_FORK_JOIN_NONE:
|
||||||
|
case IVL_ST_UTASK:
|
||||||
|
case IVL_ST_WAIT:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
|
/* These are allowed in a function and cannot have a disable. */
|
||||||
|
case IVL_ST_NOOP:
|
||||||
|
case IVL_ST_ALLOC:
|
||||||
|
case IVL_ST_ASSIGN:
|
||||||
|
case IVL_ST_CASSIGN:
|
||||||
|
case IVL_ST_DEASSIGN:
|
||||||
|
case IVL_ST_FORCE:
|
||||||
|
case IVL_ST_FREE:
|
||||||
|
case IVL_ST_RELEASE:
|
||||||
|
case IVL_ST_STASK:
|
||||||
|
case IVL_ST_TRIGGER:
|
||||||
|
break;
|
||||||
|
/* Look for a disable in each block statement. */
|
||||||
|
case IVL_ST_BLOCK:
|
||||||
|
count = ivl_stmt_block_count(stmt);
|
||||||
|
for (idx = 0; (idx < count) && ! rtn; idx += 1) {
|
||||||
|
rtn |= has_func_disable(scope,
|
||||||
|
ivl_stmt_block_stmt(stmt, idx));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* Look for a disable in each case branch. */
|
||||||
|
case IVL_ST_CASE:
|
||||||
|
case IVL_ST_CASER:
|
||||||
|
case IVL_ST_CASEX:
|
||||||
|
case IVL_ST_CASEZ:
|
||||||
|
count = ivl_stmt_case_count(stmt);
|
||||||
|
for (idx = 0; (idx < count) && ! rtn; idx += 1) {
|
||||||
|
rtn |= has_func_disable(scope,
|
||||||
|
ivl_stmt_case_stmt(stmt, idx));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* Either the true or false clause may have a disable. */
|
||||||
|
case IVL_ST_CONDIT:
|
||||||
|
rtn = has_func_disable(scope, ivl_stmt_cond_true(stmt));
|
||||||
|
if (! rtn) {
|
||||||
|
rtn = has_func_disable(scope, ivl_stmt_cond_false(stmt));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* These have a single sub-statement so look for a disable there. */
|
||||||
|
case IVL_ST_FOREVER:
|
||||||
|
case IVL_ST_REPEAT:
|
||||||
|
case IVL_ST_WHILE:
|
||||||
|
rtn = has_func_disable(scope, ivl_stmt_sub_stmt(stmt));
|
||||||
|
break;
|
||||||
|
/* The function has a disable if the disable scope matches the
|
||||||
|
* function scope. */
|
||||||
|
case IVL_ST_DISABLE:
|
||||||
|
rtn = scope == ivl_stmt_call(stmt);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s:%u: vlog95 error: Unknown statment type (%d) "
|
||||||
|
"in functin disable check.\n",
|
||||||
|
ivl_stmt_file(stmt),
|
||||||
|
ivl_stmt_lineno(stmt),
|
||||||
|
(int)ivl_statement_type(stmt));
|
||||||
|
|
||||||
|
vlog_errors += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the block name used when a SystemVerilog return is used in a
|
||||||
|
* function and the body does not already have an enclosing named block.
|
||||||
|
* This is needed since the actual function cannot be disabled.
|
||||||
|
*/
|
||||||
|
static char *get_func_return_name(ivl_scope_t scope)
|
||||||
|
{
|
||||||
|
const char *name_func = ivl_scope_basename(scope);
|
||||||
|
const char *name_head = "_ivl_";
|
||||||
|
const char *name_tail = "_return";
|
||||||
|
char *name_return;
|
||||||
|
name_return = (char *)malloc(strlen(name_head) +
|
||||||
|
strlen(name_func) +
|
||||||
|
strlen(name_tail) + 1);
|
||||||
|
name_return[0] = 0;
|
||||||
|
(void) strcpy(name_return, name_head);
|
||||||
|
(void) strcat(name_return, name_func);
|
||||||
|
(void) strcat(name_return, name_tail);
|
||||||
|
return name_return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This search method may be slow for a large structural design with a
|
* This search method may be slow for a large structural design with a
|
||||||
* large number of gate types. That's not what this converter was built
|
* large number of gate types. That's not what this converter was built
|
||||||
* for so this is probably OK. If this becomes an issue then we need a
|
* for so this is probably OK. If this becomes an issue then we need a
|
||||||
* better method/data structure.
|
* better method/data structure.
|
||||||
*/
|
*/
|
||||||
static const char **scopes_emitted = 0;
|
static const char **scopes_emitted = 0;
|
||||||
static unsigned num_scopes_emitted = 0;
|
static unsigned num_scopes_emitted = 0;
|
||||||
|
|
||||||
|
|
@ -968,9 +1075,8 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent)
|
||||||
emit_scope_variables(scope);
|
emit_scope_variables(scope);
|
||||||
|
|
||||||
if (sc_type == IVL_SCT_MODULE) {
|
if (sc_type == IVL_SCT_MODULE) {
|
||||||
unsigned count;
|
unsigned count = ivl_scope_lpms(scope);
|
||||||
/* Output the LPM devices. */
|
/* Output the LPM devices. */
|
||||||
count = ivl_scope_lpms(scope);
|
|
||||||
for (idx = 0; idx < count; idx += 1) {
|
for (idx = 0; idx < count; idx += 1) {
|
||||||
emit_lpm(scope, ivl_scope_lpm(scope, idx));
|
emit_lpm(scope, ivl_scope_lpm(scope, idx));
|
||||||
}
|
}
|
||||||
|
|
@ -995,10 +1101,53 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent)
|
||||||
ivl_design_process(design, (ivl_process_f)find_process, scope);
|
ivl_design_process(design, (ivl_process_f)find_process, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Output the function/task body. */
|
/* Output the function body. */
|
||||||
if (sc_type == IVL_SCT_TASK || sc_type == IVL_SCT_FUNCTION) {
|
if (sc_type == IVL_SCT_FUNCTION) {
|
||||||
emit_stmt(scope, ivl_scope_def(scope));
|
ivl_statement_t body = ivl_scope_def(scope);
|
||||||
|
assert(func_rtn_name == 0);
|
||||||
|
/* If the function disables itself then that is really a
|
||||||
|
* SystemVerilog return statement in disguise. A toplevel
|
||||||
|
* named begin is needed to make this work in standard Verilog
|
||||||
|
* so add one if it is needed. */
|
||||||
|
if (ivl_statement_type(body) == IVL_ST_BLOCK) {
|
||||||
|
ivl_scope_t blk_scope = ivl_stmt_block_scope(body);
|
||||||
|
if (blk_scope) {
|
||||||
|
func_rtn_name = ivl_scope_basename(blk_scope);
|
||||||
|
emit_stmt(scope, body);
|
||||||
|
func_rtn_name = 0;
|
||||||
|
} else if (has_func_disable(scope, body)) {
|
||||||
|
char *name_return = get_func_return_name(scope);
|
||||||
|
unsigned count = ivl_stmt_block_count(body);
|
||||||
|
fprintf(vlog_out, "%*cbegin: %s\n", indent, ' ',
|
||||||
|
name_return);
|
||||||
|
indent += indent_incr;
|
||||||
|
func_rtn_name = name_return;
|
||||||
|
for (idx = 0; idx < count; idx += 1) {
|
||||||
|
emit_stmt(scope, ivl_stmt_block_stmt(body, idx));
|
||||||
|
}
|
||||||
|
func_rtn_name = 0;
|
||||||
|
indent -= indent_incr;
|
||||||
|
fprintf(vlog_out, "%*cend /* %s */\n", indent, ' ',
|
||||||
|
name_return);
|
||||||
|
free(name_return);
|
||||||
|
} else emit_stmt(scope, body);
|
||||||
|
/* A non-block statment may need a named block for a return. */
|
||||||
|
} else if (has_func_disable(scope, body)) {
|
||||||
|
char *name_return = get_func_return_name(scope);
|
||||||
|
fprintf(vlog_out, "%*cbegin: %s\n", indent, ' ',
|
||||||
|
name_return);
|
||||||
|
indent += indent_incr;
|
||||||
|
func_rtn_name = name_return;
|
||||||
|
emit_stmt(scope, body);
|
||||||
|
func_rtn_name = 0;
|
||||||
|
indent -= indent_incr;
|
||||||
|
fprintf(vlog_out, "%*cend /* %s */\n", indent, ' ',
|
||||||
|
name_return);
|
||||||
|
free(name_return);
|
||||||
|
} else emit_stmt(scope, body);
|
||||||
}
|
}
|
||||||
|
/* Output the task body. */
|
||||||
|
if (sc_type == IVL_SCT_TASK) emit_stmt(scope, ivl_scope_def(scope));
|
||||||
|
|
||||||
/* Print any sub-scopes. */
|
/* Print any sub-scopes. */
|
||||||
ivl_scope_children(scope, (ivl_scope_f*) emit_scope, scope);
|
ivl_scope_children(scope, (ivl_scope_f*) emit_scope, scope);
|
||||||
|
|
|
||||||
|
|
@ -1051,11 +1051,28 @@ static void emit_stmt_delayx(ivl_scope_t scope, ivl_statement_t stmt)
|
||||||
emit_stmt(scope, ivl_stmt_sub_stmt(stmt));
|
emit_stmt(scope, ivl_stmt_sub_stmt(stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned is_func_disable(ivl_scope_t scope, ivl_scope_t disable_scope)
|
||||||
|
{
|
||||||
|
assert(func_rtn_name);
|
||||||
|
/* Find the enclosing function scope. */
|
||||||
|
while (ivl_scope_type(scope) != IVL_SCT_FUNCTION) {
|
||||||
|
scope = ivl_scope_parent(scope);
|
||||||
|
assert(scope);
|
||||||
|
}
|
||||||
|
/* If the function scope and the scope to be disabled match then this
|
||||||
|
* is a function disable (SystemVerilog return). */
|
||||||
|
return scope == disable_scope;
|
||||||
|
}
|
||||||
|
|
||||||
static void emit_stmt_disable(ivl_scope_t scope, ivl_statement_t stmt)
|
static void emit_stmt_disable(ivl_scope_t scope, ivl_statement_t stmt)
|
||||||
{
|
{
|
||||||
ivl_scope_t disable_scope = ivl_stmt_call(stmt);
|
ivl_scope_t disable_scope = ivl_stmt_call(stmt);
|
||||||
fprintf(vlog_out, "%*cdisable ", get_indent(), ' ');
|
fprintf(vlog_out, "%*cdisable ", get_indent(), ' ');
|
||||||
emit_scope_path(scope, disable_scope);
|
/* If this disable is in a function and it is disabling the function
|
||||||
|
* then emit the appropriate function return name. */
|
||||||
|
if (func_rtn_name && is_func_disable(scope, disable_scope)) {
|
||||||
|
fprintf(vlog_out, "%s", func_rtn_name);
|
||||||
|
} else emit_scope_path(scope, disable_scope);
|
||||||
fprintf(vlog_out, ";");
|
fprintf(vlog_out, ";");
|
||||||
emit_stmt_file_line(stmt);
|
emit_stmt_file_line(stmt);
|
||||||
fprintf(vlog_out, "\n");
|
fprintf(vlog_out, "\n");
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,12 @@ extern int sim_precision;
|
||||||
*/
|
*/
|
||||||
extern ivl_parameter_t emitting_param;
|
extern ivl_parameter_t emitting_param;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The statement code needs to know what name to use for a translated
|
||||||
|
* function return statement (disable).
|
||||||
|
*/
|
||||||
|
extern const char *func_rtn_name;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keep the current indent level.
|
* Keep the current indent level.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue