diff options
Diffstat (limited to 'frontends')
| -rw-r--r-- | frontends/ast/genrtlil.cc | 19 | ||||
| -rw-r--r-- | frontends/ast/simplify.cc | 129 | 
2 files changed, 143 insertions, 5 deletions
| diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index ed709aa33..4c25287ad 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -932,7 +932,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun  		if (children.at(0)->type != AST_CONSTANT)  			log_file_error(filename, location.first_line, "Static cast with non constant expression!\n");  		children.at(1)->detectSignWidthWorker(width_hint, sign_hint); -		width_hint = children.at(0)->bitsAsConst().as_int(); +		this_width = children.at(0)->bitsAsConst().as_int(); +		width_hint = max(width_hint, this_width);  		if (width_hint <= 0)  			log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n");  		break; @@ -1087,6 +1088,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun  			}  			break;  		} +		if (str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") { +			width_hint = 32; +			sign_hint = true; +			break; +		}  		if (current_scope.count(str))  		{  			// This width detection is needed for function calls which are @@ -1525,13 +1531,20 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)  	// changing the size of signal can be done directly using RTLIL::SigSpec  	case AST_CAST_SIZE: {  			RTLIL::SigSpec size = children[0]->genRTLIL(); -			RTLIL::SigSpec sig = children[1]->genRTLIL();  			if (!size.is_fully_const())  				log_file_error(filename, location.first_line, "Static cast with non constant expression!\n");  			int width = size.as_int();  			if (width <= 0)  				log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n"); -			sig.extend_u0(width, sign_hint); +			// determine the *signedness* of the expression +			int sub_width_hint = -1; +			bool sub_sign_hint = true; +			children[1]->detectSignWidth(sub_width_hint, sub_sign_hint); +			// generate the signal given the *cast's* size and the +			// *expression's* signedness +			RTLIL::SigSpec sig = children[1]->genWidthRTLIL(width, sub_sign_hint); +			// context may effect this node's signedness, but not that of the +			// casted expression  			is_signed = sign_hint;  			return sig;  		} diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 777f46bd7..18b1e1e11 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -673,6 +673,118 @@ void add_wire_for_ref(const RTLIL::Wire *ref, const std::string &str)  	current_scope[str] = wire;  } +enum class IdentUsage { +	NotReferenced, // target variable is neither read or written in the block +	Assigned, // target variable is always assigned before use +	SyncRequired, // target variable may be used before it has been assigned +}; + +// determines whether a local variable a block is always assigned before it is +// used, meaning the nosync attribute can automatically be added to that +// variable +static IdentUsage always_asgn_before_use(const AstNode *node, const std::string &target) +{ +	// This variable has been referenced before it has necessarily been assigned +	// a value in this procedure. +	if (node->type == AST_IDENTIFIER && node->str == target) +		return IdentUsage::SyncRequired; + +	// For case statements (which are also used for if/else), we check each +	// possible branch. If the variable is assigned in all branches, then it is +	// assigned, and a sync isn't required. If it used before assignment in any +	// branch, then a sync is required. +	if (node->type == AST_CASE) { +		bool all_defined = true; +		bool any_used = false; +		bool has_default = false; +		for (const AstNode *child : node->children) { +			if (child->type == AST_COND && child->children.at(0)->type == AST_DEFAULT) +				has_default = true; +			IdentUsage nested = always_asgn_before_use(child, target); +			if (nested != IdentUsage::Assigned && child->type == AST_COND) +				all_defined = false; +			if (nested == IdentUsage::SyncRequired) +				any_used = true; +		} +		if (any_used) +			return IdentUsage::SyncRequired; +		else if (all_defined && has_default) +			return IdentUsage::Assigned; +		else +			return IdentUsage::NotReferenced; +	} + +	// Check if this is an assignment to the target variable. For simplicity, we +	// don't analyze sub-ranges of the variable. +	if (node->type == AST_ASSIGN_EQ) { +		const AstNode *ident = node->children.at(0); +		if (ident->type == AST_IDENTIFIER && ident->str == target) +			return IdentUsage::Assigned; +	} + +	for (const AstNode *child : node->children) { +		IdentUsage nested = always_asgn_before_use(child, target); +		if (nested != IdentUsage::NotReferenced) +			return nested; +	} +	return IdentUsage::NotReferenced; +} + +static const std::string auto_nosync_prefix = "\\AutoNosync"; + +// mark a local variable in an always_comb block for automatic nosync +// consideration +static void mark_auto_nosync(AstNode *block, const AstNode *wire) +{ +	log_assert(block->type == AST_BLOCK); +	log_assert(wire->type == AST_WIRE); +	block->attributes[auto_nosync_prefix + wire->str] = AstNode::mkconst_int(1, +			false); +} + +// check a procedural block for auto-nosync markings, remove them, and add +// nosync to local variables as necessary +static void check_auto_nosync(AstNode *node) +{ +	std::vector<RTLIL::IdString> attrs_to_drop; +	for (const auto& elem : node->attributes) { +		// skip attributes that don't begin with the prefix +		if (elem.first.compare(0, auto_nosync_prefix.size(), +					auto_nosync_prefix.c_str())) +			continue; + +		// delete and remove the attribute once we're done iterating +		attrs_to_drop.push_back(elem.first); + +		// find the wire based on the attribute +		std::string wire_name = elem.first.substr(auto_nosync_prefix.size()); +		auto it = current_scope.find(wire_name); +		if (it == current_scope.end()) +			continue; + +		// analyze the usage of the local variable in this block +		IdentUsage ident_usage = always_asgn_before_use(node, wire_name); +		if (ident_usage != IdentUsage::Assigned) +			continue; + +		// mark the wire with `nosync` +		AstNode *wire = it->second; +		log_assert(wire->type == AST_WIRE); +		wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false); +	} + +	// remove the attributes we've "consumed" +	for (const RTLIL::IdString &str : attrs_to_drop) { +		auto it = node->attributes.find(str); +		delete it->second; +		node->attributes.erase(it); +	} + +	// check local variables in any nested blocks +	for (AstNode *child : node->children) +		check_auto_nosync(child); +} +  // convert the AST into a simpler AST that has all parameters substituted by their  // values, unrolled for-loops, expanded generate blocks, etc. when this function  // is done with an AST it can be converted into RTLIL using genRTLIL(). @@ -980,6 +1092,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  				}  			}  		} + +		for (AstNode *child : children) +			if (child->type == AST_ALWAYS && +					child->attributes.count(ID::always_comb)) +				check_auto_nosync(child);  	}  	// create name resolution entries for all objects with names @@ -1389,8 +1506,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	if (const_fold && type == AST_CASE)  	{ -		int width_hint; -		bool sign_hint;  		detectSignWidth(width_hint, sign_hint);  		while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { }  		if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) { @@ -2226,6 +2341,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,  	{  		expand_genblock(str + "."); +		// if this is an autonamed block is in an always_comb +		if (current_always && current_always->attributes.count(ID::always_comb) +				&& str.at(0) == '$') +			// track local variables in this block so we can consider adding +			// nosync once the block has been fully elaborated +			for (AstNode *child : children) +				if (child->type == AST_WIRE && +						!child->attributes.count(ID::nosync)) +					mark_auto_nosync(this, child); +  		std::vector<AstNode*> new_children;  		for (size_t i = 0; i < children.size(); i++)  			if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) { | 
