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.
This commit is contained in:
Martin Whitaker 2016-03-25 21:46:04 +00:00
parent b5324c7ba2
commit dc1c3a4043
7 changed files with 137 additions and 31 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2012 Stephen Williams <steve@icarus.com>
* Copyright (c) 1998-2016 Stephen Williams <steve@icarus.com>
* 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)
{
}

View File

@ -1,7 +1,7 @@
#ifndef IVL_PExpr_H
#define IVL_PExpr_H
/*
* Copyright (c) 1998-2014 Stephen Williams <steve@icarus.com>
* Copyright (c) 1998-2016 Stephen Williams <steve@icarus.com>
* 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_;
};

View File

@ -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
@ -2517,22 +2517,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<NetEConst*>(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)

View File

@ -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);

View File

@ -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<NetEConst*>(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<NetEConst*>(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

33
parse.y
View File

@ -613,7 +613,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector<Statement*>
%type <gates> gate_instance_list
%type <pform_name> hierarchy_identifier implicit_class_handle
%type <expr> assignment_pattern expression expr_primary expr_mintypmax
%type <expr> assignment_pattern expression expr_mintypmax
%type <expr> expr_primary_or_typename expr_primary
%type <expr> class_new dynamic_array_new
%type <expr> inc_or_dec_expression inside_expression lpvalue
%type <expr> branch_probe_expression streaming_concatenation
@ -3083,7 +3084,7 @@ branch_probe_expression
;
expression
: expr_primary
: expr_primary_or_typename
{ $$ = $1; }
| inc_or_dec_expression
{ $$ = $1; }
@ -3378,6 +3379,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);
@ -3419,15 +3434,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 */
@ -3731,12 +3737,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.");

View File

@ -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 << ")";
}