iverilog/elab_type.cc

492 lines
14 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2012-2020 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
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
# include "PExpr.h"
Improve handling of type identifier references Currently when referencing a typedef this gets replaced with the `data_type_t` that the typedef points to. This works for most cases, but there are some corner cases where it breaks down. E.g. it is possible to have a scoped type identifier which references a type defined in a package. For such type identifiers, only the data_type_t itself is remembered, but not the package scope. This will cause the type identifier to be elaborated in the wrong scope. Furthermore type identifiers of vector types used for module or task port might not be elaborated in the correct scope. Introduce a new `typeref_t` which has `data_type_t` as a base type and can be used as the data type for a signal. A new instance of a `typeref_t` is created when referencing a type identifier. The `typeref_t` remembers both the data type and the scope of the type identifier. When elaborating the `typeref_t` the elaboration is passed through to the referenced `data_type_t`. But special care is taken to lookup the right scope first. With the new approach also typedefs of typedefs are supported. This previously did not work because chained typedefs all reference the same `data_type_t`, but each typedef sets the `name` field of the `data_type_t`. So the second typedef overwrites the first typedef and a lookup of the scope of the first typedef by name will fail as it will return the scope of the second typedef. This refactoring also allows to define clear ownership of a data_type_t instance. This e.g. means that an array type owns its base type and the base type can be freed when the array type itself is freed. The same is true for signals and class properties, they now own their data type and the data type can be freed when the signal or property is freed. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-03-24 11:05:33 +01:00
# include "PScope.h"
# include "pform_types.h"
# include "netlist.h"
# include "netclass.h"
# include "netdarray.h"
# include "netenum.h"
# include "netqueue.h"
# include "netparray.h"
# include "netscalar.h"
# include "netstruct.h"
# include "netvector.h"
# include "netmisc.h"
# include <typeinfo>
# include "ivl_assert.h"
using namespace std;
/*
* Elaborations of types may vary depending on the scope that it is
* done in, so keep a per-scope cache of the results.
*/
ivl_type_t data_type_t::elaborate_type(Design*des, NetScope*scope)
{
Improve handling of type identifier references Currently when referencing a typedef this gets replaced with the `data_type_t` that the typedef points to. This works for most cases, but there are some corner cases where it breaks down. E.g. it is possible to have a scoped type identifier which references a type defined in a package. For such type identifiers, only the data_type_t itself is remembered, but not the package scope. This will cause the type identifier to be elaborated in the wrong scope. Furthermore type identifiers of vector types used for module or task port might not be elaborated in the correct scope. Introduce a new `typeref_t` which has `data_type_t` as a base type and can be used as the data type for a signal. A new instance of a `typeref_t` is created when referencing a type identifier. The `typeref_t` remembers both the data type and the scope of the type identifier. When elaborating the `typeref_t` the elaboration is passed through to the referenced `data_type_t`. But special care is taken to lookup the right scope first. With the new approach also typedefs of typedefs are supported. This previously did not work because chained typedefs all reference the same `data_type_t`, but each typedef sets the `name` field of the `data_type_t`. So the second typedef overwrites the first typedef and a lookup of the scope of the first typedef by name will fail as it will return the scope of the second typedef. This refactoring also allows to define clear ownership of a data_type_t instance. This e.g. means that an array type owns its base type and the base type can be freed when the array type itself is freed. The same is true for signals and class properties, they now own their data type and the data type can be freed when the signal or property is freed. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-03-24 11:05:33 +01:00
scope = find_scope(des, scope);
Definitions*use_definitions = scope;
map<Definitions*,ivl_type_t>::iterator pos = cache_type_elaborate_.lower_bound(use_definitions);
if (pos != cache_type_elaborate_.end() && pos->first == use_definitions)
return pos->second;
ivl_type_t tmp;
if (elaborating) {
des->errors++;
cerr << get_fileline() << ": error: "
<< "Circular type definition found involving `" << *this << "`."
<< endl;
// Try to recover
tmp = netvector_t::integer_type();
} else {
elaborating = true;
tmp = elaborate_type_raw(des, scope);
elaborating = false;
}
cache_type_elaborate_.insert(pos, pair<NetScope*,ivl_type_t>(scope, tmp));
return tmp;
}
Improve handling of type identifier references Currently when referencing a typedef this gets replaced with the `data_type_t` that the typedef points to. This works for most cases, but there are some corner cases where it breaks down. E.g. it is possible to have a scoped type identifier which references a type defined in a package. For such type identifiers, only the data_type_t itself is remembered, but not the package scope. This will cause the type identifier to be elaborated in the wrong scope. Furthermore type identifiers of vector types used for module or task port might not be elaborated in the correct scope. Introduce a new `typeref_t` which has `data_type_t` as a base type and can be used as the data type for a signal. A new instance of a `typeref_t` is created when referencing a type identifier. The `typeref_t` remembers both the data type and the scope of the type identifier. When elaborating the `typeref_t` the elaboration is passed through to the referenced `data_type_t`. But special care is taken to lookup the right scope first. With the new approach also typedefs of typedefs are supported. This previously did not work because chained typedefs all reference the same `data_type_t`, but each typedef sets the `name` field of the `data_type_t`. So the second typedef overwrites the first typedef and a lookup of the scope of the first typedef by name will fail as it will return the scope of the second typedef. This refactoring also allows to define clear ownership of a data_type_t instance. This e.g. means that an array type owns its base type and the base type can be freed when the array type itself is freed. The same is true for signals and class properties, they now own their data type and the data type can be freed when the signal or property is freed. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-03-24 11:05:33 +01:00
NetScope *data_type_t::find_scope(Design *, NetScope *scope) const
{
return scope;
}
ivl_type_t data_type_t::elaborate_type_raw(Design*des, NetScope*) const
{
cerr << get_fileline() << ": internal error: "
<< "Elaborate method not implemented for " << typeid(*this).name()
<< "." << endl;
des->errors += 1;
return 0;
}
Correctly handle separate port type declaration for `integer` and `time` When using non-ANSI style port declarations it is possible to declare the port direction and the data type for the port in separate statements. E.g. ``` input x; reg x; ``` When using packed array dimensions they must match for both declarations. E.g. ``` input [3:0] x; reg [3:0] x; ``` But this only applies for vector types, i.e. the packed dimension is explicitly declared. It does not apply to the `integer` and `time` types, which have an implicit packed dimension. The current implementation requires that even for `integer` and `time` types the implicit dimension needs to be explicitly declared in the port direction. E.g. the following will result in a elaboration error complaining about a packed dimension mismatch. ``` module test; output x; integer x; endmodule ``` Currently the parser creates a vector_type_t for `time` and `integer`. This means that e.g. `time` and `reg [63:0]` are indistinguishable during elaboration, even though they require different behavior. To fix let the atom2_type_t handle `integer` and `time`. Since it no longer exclusively handles 2-state types, rename it to atom_type_t. This also fixes a problem with the vlog95 target unit tests. The vlog95 target translates ``` module test(output integer x); endmodule ``` to ``` module test(x); output x; integer x; endmodule ``` which then fails when being elaborated again. There were some regression tests that were failing because of this that will now pass. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-03-12 21:10:19 +01:00
ivl_type_t atom_type_t::elaborate_type_raw(Design*des, NetScope*) const
{
switch (type_code) {
Correctly handle separate port type declaration for `integer` and `time` When using non-ANSI style port declarations it is possible to declare the port direction and the data type for the port in separate statements. E.g. ``` input x; reg x; ``` When using packed array dimensions they must match for both declarations. E.g. ``` input [3:0] x; reg [3:0] x; ``` But this only applies for vector types, i.e. the packed dimension is explicitly declared. It does not apply to the `integer` and `time` types, which have an implicit packed dimension. The current implementation requires that even for `integer` and `time` types the implicit dimension needs to be explicitly declared in the port direction. E.g. the following will result in a elaboration error complaining about a packed dimension mismatch. ``` module test; output x; integer x; endmodule ``` Currently the parser creates a vector_type_t for `time` and `integer`. This means that e.g. `time` and `reg [63:0]` are indistinguishable during elaboration, even though they require different behavior. To fix let the atom2_type_t handle `integer` and `time`. Since it no longer exclusively handles 2-state types, rename it to atom_type_t. This also fixes a problem with the vlog95 target unit tests. The vlog95 target translates ``` module test(output integer x); endmodule ``` to ``` module test(x); output x; integer x; endmodule ``` which then fails when being elaborated again. There were some regression tests that were failing because of this that will now pass. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-03-12 21:10:19 +01:00
case INTEGER:
return netvector_t::integer_type(signed_flag);
case TIME:
if (signed_flag)
return &netvector_t::time_signed;
else
return &netvector_t::time_unsigned;
case LONGINT:
if (signed_flag)
return &netvector_t::atom2s64;
else
return &netvector_t::atom2u64;
Correctly handle separate port type declaration for `integer` and `time` When using non-ANSI style port declarations it is possible to declare the port direction and the data type for the port in separate statements. E.g. ``` input x; reg x; ``` When using packed array dimensions they must match for both declarations. E.g. ``` input [3:0] x; reg [3:0] x; ``` But this only applies for vector types, i.e. the packed dimension is explicitly declared. It does not apply to the `integer` and `time` types, which have an implicit packed dimension. The current implementation requires that even for `integer` and `time` types the implicit dimension needs to be explicitly declared in the port direction. E.g. the following will result in a elaboration error complaining about a packed dimension mismatch. ``` module test; output x; integer x; endmodule ``` Currently the parser creates a vector_type_t for `time` and `integer`. This means that e.g. `time` and `reg [63:0]` are indistinguishable during elaboration, even though they require different behavior. To fix let the atom2_type_t handle `integer` and `time`. Since it no longer exclusively handles 2-state types, rename it to atom_type_t. This also fixes a problem with the vlog95 target unit tests. The vlog95 target translates ``` module test(output integer x); endmodule ``` to ``` module test(x); output x; integer x; endmodule ``` which then fails when being elaborated again. There were some regression tests that were failing because of this that will now pass. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-03-12 21:10:19 +01:00
case INT:
if (signed_flag)
return &netvector_t::atom2s32;
else
return &netvector_t::atom2u32;
Correctly handle separate port type declaration for `integer` and `time` When using non-ANSI style port declarations it is possible to declare the port direction and the data type for the port in separate statements. E.g. ``` input x; reg x; ``` When using packed array dimensions they must match for both declarations. E.g. ``` input [3:0] x; reg [3:0] x; ``` But this only applies for vector types, i.e. the packed dimension is explicitly declared. It does not apply to the `integer` and `time` types, which have an implicit packed dimension. The current implementation requires that even for `integer` and `time` types the implicit dimension needs to be explicitly declared in the port direction. E.g. the following will result in a elaboration error complaining about a packed dimension mismatch. ``` module test; output x; integer x; endmodule ``` Currently the parser creates a vector_type_t for `time` and `integer`. This means that e.g. `time` and `reg [63:0]` are indistinguishable during elaboration, even though they require different behavior. To fix let the atom2_type_t handle `integer` and `time`. Since it no longer exclusively handles 2-state types, rename it to atom_type_t. This also fixes a problem with the vlog95 target unit tests. The vlog95 target translates ``` module test(output integer x); endmodule ``` to ``` module test(x); output x; integer x; endmodule ``` which then fails when being elaborated again. There were some regression tests that were failing because of this that will now pass. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-03-12 21:10:19 +01:00
case SHORTINT:
if (signed_flag)
return &netvector_t::atom2s16;
else
return &netvector_t::atom2u16;
Correctly handle separate port type declaration for `integer` and `time` When using non-ANSI style port declarations it is possible to declare the port direction and the data type for the port in separate statements. E.g. ``` input x; reg x; ``` When using packed array dimensions they must match for both declarations. E.g. ``` input [3:0] x; reg [3:0] x; ``` But this only applies for vector types, i.e. the packed dimension is explicitly declared. It does not apply to the `integer` and `time` types, which have an implicit packed dimension. The current implementation requires that even for `integer` and `time` types the implicit dimension needs to be explicitly declared in the port direction. E.g. the following will result in a elaboration error complaining about a packed dimension mismatch. ``` module test; output x; integer x; endmodule ``` Currently the parser creates a vector_type_t for `time` and `integer`. This means that e.g. `time` and `reg [63:0]` are indistinguishable during elaboration, even though they require different behavior. To fix let the atom2_type_t handle `integer` and `time`. Since it no longer exclusively handles 2-state types, rename it to atom_type_t. This also fixes a problem with the vlog95 target unit tests. The vlog95 target translates ``` module test(output integer x); endmodule ``` to ``` module test(x); output x; integer x; endmodule ``` which then fails when being elaborated again. There were some regression tests that were failing because of this that will now pass. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-03-12 21:10:19 +01:00
case BYTE:
if (signed_flag)
return &netvector_t::atom2s8;
else
return &netvector_t::atom2u8;
default:
cerr << get_fileline() << ": internal error: "
Correctly handle separate port type declaration for `integer` and `time` When using non-ANSI style port declarations it is possible to declare the port direction and the data type for the port in separate statements. E.g. ``` input x; reg x; ``` When using packed array dimensions they must match for both declarations. E.g. ``` input [3:0] x; reg [3:0] x; ``` But this only applies for vector types, i.e. the packed dimension is explicitly declared. It does not apply to the `integer` and `time` types, which have an implicit packed dimension. The current implementation requires that even for `integer` and `time` types the implicit dimension needs to be explicitly declared in the port direction. E.g. the following will result in a elaboration error complaining about a packed dimension mismatch. ``` module test; output x; integer x; endmodule ``` Currently the parser creates a vector_type_t for `time` and `integer`. This means that e.g. `time` and `reg [63:0]` are indistinguishable during elaboration, even though they require different behavior. To fix let the atom2_type_t handle `integer` and `time`. Since it no longer exclusively handles 2-state types, rename it to atom_type_t. This also fixes a problem with the vlog95 target unit tests. The vlog95 target translates ``` module test(output integer x); endmodule ``` to ``` module test(x); output x; integer x; endmodule ``` which then fails when being elaborated again. There were some regression tests that were failing because of this that will now pass. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-03-12 21:10:19 +01:00
<< "atom_type_t type_code=" << type_code << "." << endl;
des->errors += 1;
return 0;
}
}
ivl_type_t class_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
{
return scope->find_class(des, name);
}
/*
* elaborate_type_raw for enumerations is actually mostly performed
* during scope elaboration so that the enumeration literals are
* available at the right time. At that time, the netenum_t* object is
* stashed in the scope so that I can retrieve it here.
*/
ivl_type_t enum_type_t::elaborate_type_raw(Design *des, NetScope *scope) const
{
ivl_type_t base = base_type->elaborate_type(des, scope);
const class netvector_t *vec_type = dynamic_cast<const netvector_t*>(base);
if (!vec_type && !dynamic_cast<const netparray_t*>(base)) {
cerr << get_fileline() << ": error: "
<< "Invalid enum base type `" << *base << "`."
<< endl;
des->errors++;
} else if (base->slice_dimensions().size() > 1) {
cerr << get_fileline() << ": error: "
<< "Enum type must not have more than 1 packed dimension."
<< endl;
des->errors++;
}
bool integer_flag = false;
if (vec_type)
integer_flag = vec_type->get_isint();
netenum_t *type = new netenum_t(base, names->size(), integer_flag);
type->set_line(*this);
scope->add_enumeration_set(this, type);
return type;
}
ivl_type_t vector_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
{
vector<netrange_t> packed;
if (pdims.get())
evaluate_ranges(des, scope, this, packed, *pdims);
netvector_t*tmp = new netvector_t(packed, base_type);
tmp->set_signed(signed_flag);
tmp->set_isint(integer_flag);
tmp->set_implicit(implicit_flag);
return tmp;
}
ivl_type_t real_type_t::elaborate_type_raw(Design*, NetScope*) const
{
switch (type_code_) {
case REAL:
return &netreal_t::type_real;
case SHORTREAL:
return &netreal_t::type_shortreal;
}
return 0;
}
ivl_type_t string_type_t::elaborate_type_raw(Design*, NetScope*) const
{
return &netstring_t::type_string;
}
ivl_type_t parray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
{
vector<netrange_t>packed;
if (dims.get())
evaluate_ranges(des, scope, this, packed, *dims);
ivl_type_t etype = base_type->elaborate_type(des, scope);
if (!etype->packed()) {
cerr << this->get_fileline() << " error: Packed array ";
cerr << "base-type `";
cerr << *base_type;
cerr << "` is not packed." << endl;
des->errors++;
}
return new netparray_t(packed, etype);
}
ivl_type_t struct_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
{
netstruct_t*res = new netstruct_t;
res->set_line(*this);
res->packed(packed_flag);
res->set_signed(signed_flag);
if (union_flag)
res->union_flag(true);
for (list<struct_member_t*>::iterator cur = members->begin()
; cur != members->end() ; ++ cur) {
// Elaborate the type of the member.
struct_member_t*curp = *cur;
ivl_type_t mem_vec = curp->type->elaborate_type(des, scope);
if (mem_vec == 0)
continue;
// There may be several names that are the same type:
// <data_type> name1, name2, ...;
// Process all the member, and give them a type.
for (list<decl_assignment_t*>::iterator cur_name = curp->names->begin()
; cur_name != curp->names->end() ; ++ cur_name) {
decl_assignment_t*namep = *cur_name;
netstruct_t::member_t memb;
memb.name = namep->name;
memb.net_type = elaborate_array_type(des, scope, *this,
mem_vec, namep->index);
res->append_member(des, memb);
}
}
return res;
}
static ivl_type_t elaborate_darray_check_type(Design *des, const LineInfo &li,
ivl_type_t type,
const char *darray_type)
{
if (dynamic_cast<const netvector_t*>(type) ||
dynamic_cast<const netparray_t*>(type) ||
dynamic_cast<const netreal_t*>(type) ||
dynamic_cast<const netstring_t*>(type))
return type;
cerr << li.get_fileline() << ": Sorry: "
<< darray_type << " of type `" << *type
<< "` is not yet supported." << endl;
des->errors++;
// Return something to recover
return new netvector_t(IVL_VT_LOGIC);
}
static ivl_type_t elaborate_queue_type(Design *des, NetScope *scope,
const LineInfo &li, ivl_type_t base_type,
PExpr *ridx)
{
base_type = elaborate_darray_check_type(des, li, base_type, "Queue");
long max_idx = -1;
if (ridx) {
NetExpr*tmp = elab_and_eval(des, scope, ridx, -1, true);
NetEConst*cv = dynamic_cast<NetEConst*>(tmp);
if (cv == 0) {
cerr << li.get_fileline() << ": error: "
<< "queue bound must be constant."
<< endl;
des->errors++;
} else {
verinum res = cv->value();
if (res.is_defined()) {
max_idx = res.as_long();
if (max_idx < 0) {
cerr << li.get_fileline() << ": error: "
<< "queue bound must be positive ("
<< max_idx << ")." << endl;
des->errors++;
max_idx = -1;
}
} else {
cerr << li.get_fileline() << ": error: "
<< "queue bound must be defined."
<< endl;
des->errors++;
}
}
delete cv;
}
return new netqueue_t(base_type, max_idx);
}
// If dims is not empty create a unpacked array type and clear dims, otherwise
// return the base type. Also check that we actually support the base type.
static ivl_type_t elaborate_static_array_type(Design *des, const LineInfo &li,
ivl_type_t base_type,
std::vector<netrange_t> &dims)
{
if (dims.empty())
return base_type;
if (dynamic_cast<const netqueue_t*>(base_type)) {
cerr << li.get_fileline() << ": sorry: "
<< "array of queue type is not yet supported."
<< endl;
des->errors++;
// Recover
base_type = new netvector_t(IVL_VT_LOGIC);
} else if (dynamic_cast<const netdarray_t*>(base_type)) {
cerr << li.get_fileline() << ": sorry: "
<< "array of dynamic array type is not yet supported."
<< endl;
des->errors++;
// Recover
base_type = new netvector_t(IVL_VT_LOGIC);
}
ivl_type_t type = new netuarray_t(dims, base_type);
dims.clear();
return type;
}
ivl_type_t elaborate_array_type(Design *des, NetScope *scope,
const LineInfo &li, ivl_type_t base_type,
const list<pform_range_t> &dims)
{
const long warn_dimension_size = 1 << 30;
std::vector<netrange_t> dimensions;
dimensions.reserve(dims.size());
ivl_type_t type = base_type;
for (list<pform_range_t>::const_iterator cur = dims.begin();
cur != dims.end() ; ++cur) {
PExpr *lidx = cur->first;
PExpr *ridx = cur->second;
if (lidx == 0 && ridx == 0) {
// Special case: If we encounter an undefined dimensions,
// then turn this into a dynamic array and put all the
// packed dimensions there.
type = elaborate_static_array_type(des, li, type, dimensions);
type = elaborate_darray_check_type(des, li, type, "Dynamic array");
type = new netdarray_t(type);
continue;
} else if (dynamic_cast<PENull*>(lidx)) {
// Special case: Detect the mark for a QUEUE declaration,
// which is the dimensions [null:max_idx].
type = elaborate_static_array_type(des, li, type, dimensions);
type = elaborate_queue_type(des, scope, li, type, ridx);
continue;
}
long index_l, index_r;
evaluate_range(des, scope, &li, *cur, index_l, index_r);
if (abs(index_r - index_l) > warn_dimension_size) {
cerr << li.get_fileline() << ": warning: "
<< "Array dimension is greater than "
<< warn_dimension_size << "."
<< endl;
}
dimensions.push_back(netrange_t(index_l, index_r));
}
return elaborate_static_array_type(des, li, type, dimensions);
}
ivl_type_t uarray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
{
ivl_type_t btype = base_type->elaborate_type(des, scope);
return elaborate_array_type(des, scope, *this, btype, *dims.get());
}
Improve handling of type identifier references Currently when referencing a typedef this gets replaced with the `data_type_t` that the typedef points to. This works for most cases, but there are some corner cases where it breaks down. E.g. it is possible to have a scoped type identifier which references a type defined in a package. For such type identifiers, only the data_type_t itself is remembered, but not the package scope. This will cause the type identifier to be elaborated in the wrong scope. Furthermore type identifiers of vector types used for module or task port might not be elaborated in the correct scope. Introduce a new `typeref_t` which has `data_type_t` as a base type and can be used as the data type for a signal. A new instance of a `typeref_t` is created when referencing a type identifier. The `typeref_t` remembers both the data type and the scope of the type identifier. When elaborating the `typeref_t` the elaboration is passed through to the referenced `data_type_t`. But special care is taken to lookup the right scope first. With the new approach also typedefs of typedefs are supported. This previously did not work because chained typedefs all reference the same `data_type_t`, but each typedef sets the `name` field of the `data_type_t`. So the second typedef overwrites the first typedef and a lookup of the scope of the first typedef by name will fail as it will return the scope of the second typedef. This refactoring also allows to define clear ownership of a data_type_t instance. This e.g. means that an array type owns its base type and the base type can be freed when the array type itself is freed. The same is true for signals and class properties, they now own their data type and the data type can be freed when the signal or property is freed. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-03-24 11:05:33 +01:00
ivl_type_t typeref_t::elaborate_type_raw(Design*des, NetScope*s) const
{
if (!s) {
// Try to recover
return new netvector_t(IVL_VT_LOGIC);
}
return type->elaborate_type(des, s);
}
NetScope *typeref_t::find_scope(Design *des, NetScope *s) const
{
// If a scope has been specified use that as a starting point for the
// search
if (scope)
s = des->find_package(scope->pscope_name());
return s;
}
ivl_type_t typedef_t::elaborate_type(Design *des, NetScope *scope)
{
if (!data_type.get()) {
cerr << get_fileline() << ": error: Undefined type `" << name << "`."
<< endl;
des->errors++;
// Try to recover
return netvector_t::integer_type();
}
// Search upwards from where the type was referenced
scope = scope->find_typedef_scope(des, this);
if (!scope) {
cerr << get_fileline() << ": sorry: "
<< "Can not find the scope type defintion `" << name << "`."
<< endl;
des->errors++;
// Try to recover
return netvector_t::integer_type();
}
ivl_type_t elab_type = data_type->elaborate_type(des, scope);
if (!elab_type)
return netvector_t::integer_type();
bool type_ok = true;
switch (basic_type) {
case ENUM:
type_ok = dynamic_cast<const netenum_t *>(elab_type);
break;
case STRUCT: {
const netstruct_t *struct_type = dynamic_cast<const netstruct_t *>(elab_type);
type_ok = struct_type && !struct_type->union_flag();
break;
}
case UNION: {
const netstruct_t *struct_type = dynamic_cast<const netstruct_t *>(elab_type);
type_ok = struct_type && struct_type->union_flag();
break;
}
case CLASS:
type_ok = dynamic_cast<const netclass_t *>(elab_type);
break;
default:
break;
}
if (!type_ok) {
cerr << data_type->get_fileline() << " error: "
<< "Unexpected type `" << *elab_type << "` for `" << name
<< "`. It was forward declared as `" << basic_type
<< "` at " << get_fileline() << "."
<< endl;
des->errors++;
}
return elab_type;
}
ivl_type_t type_parameter_t::elaborate_type_raw(Design *des, NetScope*scope) const
{
ivl_type_t type;
scope->get_parameter(des, name, type);
// Recover
if (!type)
return netvector_t::integer_type();
return type;
}