From 04ae07f03c0a89d94f9a358bbce135c4dd7636be Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Fri, 25 Mar 2016 21:46:04 +0000 Subject: [PATCH] Fix for GitHub issue #94 - enhance support for SystemVerilog size casting. Allow the size expression to be any constant expression. Also ensure that the expression width and type are correctly calculated and applied. (cherry picked from commit dc1c3a4043f222250ce49b47d6d90072737749fb) --- PExpr.cc | 4 ++-- PExpr.h | 6 +++--- elab_expr.cc | 48 +++++++++++++++++++++++++++++++++++++++-------- netmisc.h | 23 ++++++++++++++++++++++- pad_to_width.cc | 50 ++++++++++++++++++++++++++++++++++++++++++++++++- parse.y | 33 ++++++++++++++++++-------------- pform_dump.cc | 4 ++-- 7 files changed, 137 insertions(+), 31 deletions(-) diff --git a/PExpr.cc b/PExpr.cc index 7e0e3693f..bce998419 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2012 Stephen Williams + * Copyright (c) 1998-2016 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -137,7 +137,7 @@ bool PEBinary::has_aa_term(Design*des, NetScope*scope) const return left_->has_aa_term(des, scope) || right_->has_aa_term(des, scope); } -PECastSize::PECastSize(unsigned si, PExpr*b) +PECastSize::PECastSize(PExpr*si, PExpr*b) : size_(si), base_(b) { } diff --git a/PExpr.h b/PExpr.h index 55f0fb237..adf17a137 100644 --- a/PExpr.h +++ b/PExpr.h @@ -1,7 +1,7 @@ #ifndef IVL_PExpr_H #define IVL_PExpr_H /* - * Copyright (c) 1998-2014 Stephen Williams + * Copyright (c) 1998-2016 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -971,7 +971,7 @@ class PECallFunction : public PExpr { class PECastSize : public PExpr { public: - explicit PECastSize(unsigned expr_wid, PExpr*base); + explicit PECastSize(PExpr*size, PExpr*base); ~PECastSize(); void dump(ostream &out) const; @@ -984,7 +984,7 @@ class PECastSize : public PExpr { width_mode_t&mode); private: - unsigned size_; + PExpr* size_; PExpr* base_; }; diff --git a/elab_expr.cc b/elab_expr.cc index 9e38a134c..2f92afa27 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -2521,22 +2521,54 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, unsigned PECastSize::test_width(Design*des, NetScope*scope, width_mode_t&) { - expr_width_ = size_; + ivl_assert(*this, size_); + ivl_assert(*this, base_); + + NetExpr*size_ex = elab_and_eval(des, scope, size_, -1, true); + NetEConst*size_ce = dynamic_cast(size_ex); + expr_width_ = size_ce ? size_ce->value().as_ulong() : 0; + delete size_ex; + if (expr_width_ == 0) { + cerr << get_fileline() << ": error: Cast size expression " + "must be constant and greater than zero." << endl; + des->errors += 1; + return 0; + } width_mode_t tmp_mode = PExpr::SIZED; base_->test_width(des, scope, tmp_mode); - return size_; + if (!type_is_vectorable(base_->expr_type())) { + cerr << get_fileline() << ": error: Cast base expression " + "must be a vector type." << endl; + des->errors += 1; + return 0; + } + + expr_type_ = base_->expr_type(); + min_width_ = expr_width_; + signed_flag_ = base_->has_sign(); + + return expr_width_; } NetExpr* PECastSize::elaborate_expr(Design*des, NetScope*scope, - unsigned, unsigned) const + unsigned expr_wid, unsigned flags) const { - NetExpr*sub = base_->elaborate_expr(des, scope, base_->expr_width(), NO_FLAGS); - NetESelect*sel = new NetESelect(sub, 0, size_); - sel->set_line(*this); + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag - return sel; + ivl_assert(*this, size_); + ivl_assert(*this, base_); + + NetExpr*sub = base_->elaborate_expr(des, scope, base_->expr_width(), flags); + + // Perform the cast. The extension method (zero/sign), if needed, + // depends on the type of the base expression. + NetExpr*tmp = cast_to_width(sub, expr_width_, base_->has_sign(), *this); + + // Pad up to the expression width. The extension method (zero/sign) + // depends on the type of enclosing expression. + return pad_to_width(tmp, expr_wid, signed_flag_, *this); } unsigned PECastType::test_width(Design*des, NetScope*scope, width_mode_t&wid) diff --git a/netmisc.h b/netmisc.h index 4123d4f61..2e2297b15 100644 --- a/netmisc.h +++ b/netmisc.h @@ -1,7 +1,7 @@ #ifndef IVL_netmisc_H #define IVL_netmisc_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 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 @@ -66,6 +66,27 @@ inline NetScope* symbol_search(const LineInfo*li, * enough. */ extern NetExpr*pad_to_width(NetExpr*expr, unsigned wid, const LineInfo&info); + +/* + * This function transforms an expression by either zero or sign extending + * the high bits until the expression has the desired width. This may mean + * not transforming the expression at all, if it is already wide enough. + * The extension method and the returned expression type is determined by + * signed_flag. + */ +extern NetExpr*pad_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info); + +/* + * This function transforms an expression by either zero or sign extending + * or discarding the high bits until the expression has the desired width. + * This may mean not transforming the expression at all, if it is already + * the correct width. The extension method (if needed) and the returned + * expression type is determined by signed_flag. + */ +extern NetExpr*cast_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info); + extern NetNet*pad_to_width(Design*des, NetNet*n, unsigned w, const LineInfo&info); diff --git a/pad_to_width.cc b/pad_to_width.cc index 32ba6c61d..85f8d74e4 100644 --- a/pad_to_width.cc +++ b/pad_to_width.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 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 @@ -51,6 +51,54 @@ NetExpr*pad_to_width(NetExpr*expr, unsigned wid, const LineInfo&info) return tmp; } +NetExpr*pad_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info) +{ + if (wid <= expr->expr_width()) { + expr->cast_signed(signed_flag); + return expr; + } + + /* If the expression is a const, then replace it with a wider + const. This is a more efficient result. */ + if (NetEConst*tmp = dynamic_cast(expr)) { + verinum oval = tmp->value(); + oval.has_sign(signed_flag); + oval = pad_to_width(oval, wid); + tmp = new NetEConst(oval); + tmp->set_line(info); + delete expr; + return tmp; + } + + NetESelect*tmp = new NetESelect(expr, 0, wid); + tmp->cast_signed(signed_flag); + tmp->set_line(info); + return tmp; +} + +NetExpr*cast_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info) +{ + /* If the expression is a const, then replace it with a new + const. This is a more efficient result. */ + if (NetEConst*tmp = dynamic_cast(expr)) { + tmp->cast_signed(signed_flag); + if (wid != tmp->expr_width()) { + tmp = new NetEConst(verinum(tmp->value(), wid)); + tmp->set_line(info); + delete expr; + } + return tmp; + } + + NetESelect*tmp = new NetESelect(expr, 0, wid); + tmp->cast_signed(signed_flag); + tmp->set_line(info); + + return tmp; +} + /* * Pad a NetNet to the desired vector width by concatenating a * NetConst of constant zeros. Use a NetConcat node to do the diff --git a/parse.y b/parse.y index 806b58713..739bf9b73 100644 --- a/parse.y +++ b/parse.y @@ -607,7 +607,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type gate_instance_list %type hierarchy_identifier implicit_class_handle -%type assignment_pattern expression expr_primary expr_mintypmax +%type assignment_pattern expression expr_mintypmax +%type expr_primary_or_typename expr_primary %type class_new dynamic_array_new %type inc_or_dec_expression inside_expression lpvalue %type branch_probe_expression streaming_concatenation @@ -3033,7 +3034,7 @@ branch_probe_expression ; expression - : expr_primary + : expr_primary_or_typename { $$ = $1; } | inc_or_dec_expression { $$ = $1; } @@ -3328,6 +3329,20 @@ expression_list_proper } ; +expr_primary_or_typename + : expr_primary + + /* There are a few special cases (notably $bits argument) where the + expression may be a type name. Let the elaborator sort this out. */ + | TYPE_IDENTIFIER + { PETypename*tmp = new PETypename($1.type); + FILE_NAME(tmp,@1); + $$ = tmp; + delete[]$1.text; + } + + ; + expr_primary : number { assert($1); @@ -3369,15 +3384,6 @@ expr_primary delete[]$1; } - /* There are a few special cases (notably $bits argument) where the - expression may be a type name. Let the elaborator sort this out. */ - | TYPE_IDENTIFIER - { PETypename*tmp = new PETypename($1.type); - FILE_NAME(tmp,@1); - $$ = tmp; - delete[]$1.text; - } - /* The hierarchy_identifier rule matches simple identifiers as well as indexed arrays and part selects */ @@ -3681,12 +3687,11 @@ expr_primary /* Cast expressions are primaries */ - | DEC_NUMBER '\'' '(' expression ')' + | expr_primary '\'' '(' expression ')' { PExpr*base = $4; if (gn_system_verilog()) { - PECastSize*tmp = new PECastSize($1->as_ulong(), base); + PECastSize*tmp = new PECastSize($1, base); FILE_NAME(tmp, @1); - delete $1; $$ = tmp; } else { yyerror(@1, "error: Size cast requires SystemVerilog."); diff --git a/pform_dump.cc b/pform_dump.cc index 4cbd994ec..afd2ddc27 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2016 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 @@ -318,7 +318,7 @@ void PECallFunction::dump(ostream &out) const void PECastSize::dump(ostream &out) const { - out << size_ << "'("; + out << *size_ << "'("; base_->dump(out); out << ")"; }