diff --git a/driver/iverilog.man.in b/driver/iverilog.man.in index 0f49fea4f..464a6acbf 100644 --- a/driver/iverilog.man.in +++ b/driver/iverilog.man.in @@ -190,9 +190,13 @@ prefixed by "I " and other files are prefixed by "M ". .B -m\fImodule\fP Add this module to the list of VPI modules to be loaded by the simulation. Many modules can be specified, and all will be loaded, in -the order specified. The system module is implicit and always included. -If a System Function Table file (.sft) exists for the module it -will be loaded automatically. +the order specified. The system module is implicit and always included +(and loaded last). If a System Function Table file (.sft) +exists for the module it will be loaded automatically. + +If the specified name includes at least one directory character, it is +assumed to be prefixed by the path to the module, otherwise the module +is assumed to be located in the \fIiverilog\fP base directory. .TP 8 .B -N\fIpath\fP This is used for debugging the compiler proper. Dump the final netlist diff --git a/driver/main.c b/driver/main.c index 56228637a..427c9d499 100644 --- a/driver/main.c +++ b/driver/main.c @@ -864,7 +864,14 @@ static void add_sft_file(const char *module) char *file; file = (char *) malloc(strlen(base)+1+strlen(module)+4+1); - sprintf(file, "%s%c%s.sft", base, sep, module); + // If the module name has at least one directory character + // in it, assume it includes the path, otherwise look in + // the base directory. + if (strchr(module, sep)) + sprintf(file, "%s.sft", module); + else + sprintf(file, "%s%c%s.sft", base, sep, module); + if (access(file, R_OK) == 0) fprintf(iconfig_file, "sys_func:%s\n", file); free(file); diff --git a/elab_expr.cc b/elab_expr.cc index 65c1dc714..fbfcea532 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -2752,13 +2752,15 @@ NetExpr* PECastSize::elaborate_expr(Design*des, NetScope*scope, ivl_assert(*this, size_); ivl_assert(*this, base_); - // When changing size, a cast behaves exactly like an assignment, - // so the result size affects the final expression width. + // A cast behaves exactly like an assignment to a temporary variable, + // so the temporary result size may affect the sub-expression width. unsigned cast_width = base_->expr_width(); if (cast_width < expr_width_) cast_width = expr_width_; NetExpr*sub = base_->elaborate_expr(des, scope, cast_width, flags); + if (sub == 0) + return 0; // Perform the cast. The extension method (zero/sign), if needed, // depends on the type of the base expression. @@ -2769,33 +2771,33 @@ NetExpr* PECastSize::elaborate_expr(Design*des, NetScope*scope, return pad_to_width(tmp, expr_wid, signed_flag_, *this); } -unsigned PECastType::test_width(Design*des, NetScope*scope, width_mode_t&wid) +unsigned PECastType::test_width(Design*des, NetScope*scope, width_mode_t&) { ivl_type_t t = target_->elaborate_type(des, scope); - base_->test_width(des, scope, wid); - if(const netdarray_t*use_darray = dynamic_cast (t)) { + width_mode_t tmp_mode = PExpr::SIZED; + base_->test_width(des, scope, tmp_mode); + + if (const netdarray_t*use_darray = dynamic_cast(t)) { expr_type_ = use_darray->element_base_type(); expr_width_ = use_darray->element_width(); - } - else if(const netstring_t*use_string = dynamic_cast (t)) { + } else if (const netstring_t*use_string = dynamic_cast(t)) { expr_type_ = use_string->base_type(); expr_width_ = 8; - } - else { + } else { expr_type_ = t->base_type(); expr_width_ = t->packed_width(); } - + min_width_ = expr_width_; signed_flag_ = t->get_signed(); - min_width_ = expr_width_; + return expr_width_; } NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope, - ivl_type_t type, unsigned) const + ivl_type_t type, unsigned flags) const { const netdarray_t*darray = NULL; const netvector_t*vector = NULL; @@ -2824,60 +2826,68 @@ NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope, } // Fallback - return elaborate_expr(des, scope, (unsigned) 0, 0); + return elaborate_expr(des, scope, (unsigned) 0, flags); } NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope, - unsigned, unsigned) const + unsigned expr_wid, unsigned flags) const { - NetExpr*expr = base_->elaborate_expr(des, scope, base_->expr_width(), NO_FLAGS); + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag - if(dynamic_cast(target_)) { - return cast_to_real(expr); + // A cast behaves exactly like an assignment to a temporary variable, + // so the temporary result size may affect the sub-expression width. + unsigned cast_width = base_->expr_width(); + if (type_is_vectorable(base_->expr_type()) && (cast_width < expr_width_)) + cast_width = expr_width_; + + NetExpr*sub = base_->elaborate_expr(des, scope, cast_width, flags); + if (sub == 0) + return 0; + + if (dynamic_cast(target_)) { + return cast_to_real(sub); } - if(const atom2_type_t*atom = dynamic_cast(target_)) { - if(base_->expr_width() > expr_width_) { - cerr << get_fileline() << ": cast type is not wide enough to store the result." << endl; - ivl_assert(*this, 0); - } + NetExpr*tmp = 0; + if (dynamic_cast(target_)) { + tmp = cast_to_int2(sub, expr_width_); + } + if (const vector_type_t*vec = dynamic_cast(target_)) { + switch (vec->base_type) { + case IVL_VT_BOOL: + tmp = cast_to_int2(sub, expr_width_); + break; - if(base_->has_sign() != atom->signed_flag) { - cerr << get_fileline() << ": cast type and subject differ in signedness." << endl; - ivl_assert(*this, 0); - } + case IVL_VT_LOGIC: + tmp = cast_to_int4(sub, expr_width_); + break; - // That is how you both resize & cast to integers - return new NetECast('2', expr, expr_width_, expr->has_sign()); + default: + break; + } + } + if (tmp) { + if (tmp == sub) { + // We already had the correct base type, so we just need to + // fix the size. Note that even if the size is already correct, + // we still need to isolate the sub-expression from changes in + // the signedness pushed down from the main expression. + tmp = cast_to_width(sub, expr_width_, sub->has_sign(), *this); + } + return pad_to_width(tmp, expr_wid, signed_flag_, *this); } - if(const vector_type_t*vec = dynamic_cast(target_)) { - switch(vec->base_type) { - case IVL_VT_BOOL: - return cast_to_int2(expr, expr_width_); + if (dynamic_cast(target_)) { + if (base_->expr_type() == IVL_VT_STRING) + return sub; // no conversion - case IVL_VT_LOGIC: - return cast_to_int4(expr, expr_width_); - - default: - break; /* Suppress warnings */ - } - } - - else if(dynamic_cast(target_)) { - if(base_->expr_type() == IVL_VT_STRING) - return expr; // no conversion - - if((base_->expr_type() != IVL_VT_BOOL) && - (base_->expr_type() != IVL_VT_LOGIC)) { - cerr << get_fileline() << ": cannot be cast to a string." << endl; - ivl_assert(*this, false); - } - - return expr; + if (base_->expr_type() == IVL_VT_LOGIC + || base_->expr_type() == IVL_VT_BOOL) + return sub; // handled by the target as special cases } cerr << get_fileline() << ": sorry: This cast operation is not yet supported." << endl; + des->errors += 1; return 0; } diff --git a/sys_funcs.cc b/sys_funcs.cc index bb1ccd8bc..41aaa2adb 100644 --- a/sys_funcs.cc +++ b/sys_funcs.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-2019 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -44,11 +44,24 @@ struct sfunc_return_type_cell : sfunc_return_type { struct sfunc_return_type_cell*next; }; -static struct sfunc_return_type_cell*sfunc_stack = 0; +static struct sfunc_return_type_cell*sfunc_list_head = 0; +static struct sfunc_return_type_cell*sfunc_list_tail = 0; + +void append_to_list(struct sfunc_return_type_cell*cell) +{ + if (sfunc_list_tail) { + sfunc_list_tail->next = cell; + sfunc_list_tail = cell; + } else { + sfunc_list_head = cell; + sfunc_list_tail = cell; + } + cell->next = 0; +} void cleanup_sys_func_table() { - struct sfunc_return_type_cell *next, *cur = sfunc_stack; + struct sfunc_return_type_cell *next, *cur = sfunc_list_head; while (cur) { next = cur->next; delete cur; @@ -58,8 +71,8 @@ void cleanup_sys_func_table() const struct sfunc_return_type* lookup_sys_func(const char*name) { - /* First, try to find the name in the function stack. */ - struct sfunc_return_type_cell*cur = sfunc_stack; + /* First, try to find the name in the function list. */ + struct sfunc_return_type_cell*cur = sfunc_list_head; while (cur) { if (strcmp(cur->name, name) == 0) return cur; @@ -77,7 +90,7 @@ const struct sfunc_return_type* lookup_sys_func(const char*name) idx += 1; } - /* No luck finding, so return the trailer, which give a + /* No luck finding, so return the trailer, which gives a default description. */ return sfunc_table + idx; } @@ -87,6 +100,10 @@ const struct sfunc_return_type* lookup_sys_func(const char*name) * format: * * [] + * + * The driver passes us user-provided tables first, so we add new entries + * to the end of the list. This allows user-defined functions to override + * built-in functions. */ int load_sys_func_table(const char*path) { @@ -136,8 +153,7 @@ int load_sys_func_table(const char*path) cell->type = IVL_VT_REAL; cell->wid = 1; cell->signed_flag = true; - cell->next = sfunc_stack; - sfunc_stack = cell; + append_to_list(cell); continue; } @@ -147,8 +163,7 @@ int load_sys_func_table(const char*path) cell->type = IVL_VT_LOGIC; cell->wid = 32; cell->signed_flag = true; - cell->next = sfunc_stack; - sfunc_stack = cell; + append_to_list(cell); continue; } @@ -188,8 +203,7 @@ int load_sys_func_table(const char*path) cell->type = IVL_VT_LOGIC; cell->wid = width; cell->signed_flag = signed_flag; - cell->next = sfunc_stack; - sfunc_stack = cell; + append_to_list(cell); continue; } @@ -199,8 +213,7 @@ int load_sys_func_table(const char*path) cell->type = IVL_VT_VOID; cell->wid = 0; cell->signed_flag = false; - cell->next = sfunc_stack; - sfunc_stack = cell; + append_to_list(cell); continue; } @@ -210,8 +223,7 @@ int load_sys_func_table(const char*path) cell->type = IVL_VT_STRING; cell->wid = 0; // string is a dynamic length type cell->signed_flag = false; - cell->next = sfunc_stack; - sfunc_stack = cell; + append_to_list(cell); continue; }