mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge pull request #1851 from YosysHQ/claire/bitselwrite
Improved rewrite code for writing to bit slice
This commit is contained in:
		
						commit
						9e1afde7a0
					
				
					 17 changed files with 1431 additions and 15 deletions
				
			
		|  | @ -218,6 +218,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch | |||
| 	realvalue = 0; | ||||
| 	id2ast = NULL; | ||||
| 	basic_prep = false; | ||||
| 	lookahead = false; | ||||
| 
 | ||||
| 	if (child1) | ||||
| 		children.push_back(child1); | ||||
|  | @ -310,6 +311,10 @@ void AstNode::dumpAst(FILE *f, std::string indent) const | |||
| 		fprintf(f, " reg"); | ||||
| 	if (is_signed) | ||||
| 		fprintf(f, " signed"); | ||||
| 	if (basic_prep) | ||||
| 		fprintf(f, " basic_prep"); | ||||
| 	if (lookahead) | ||||
| 		fprintf(f, " lookahead"); | ||||
| 	if (port_id > 0) | ||||
| 		fprintf(f, " port=%d", port_id); | ||||
| 	if (range_valid || range_left != -1 || range_right != 0) | ||||
|  |  | |||
|  | @ -202,6 +202,9 @@ namespace AST | |||
| 		// this is used by simplify to detect if basic analysis has been performed already on the node
 | ||||
| 		bool basic_prep; | ||||
| 
 | ||||
| 		// this is used for ID references in RHS expressions that should use the "new" value for non-blocking assignments
 | ||||
| 		bool lookahead; | ||||
| 
 | ||||
| 		// this is the original sourcecode location that resulted in this AST node
 | ||||
| 		// it is automatically set by the constructor using AST::current_filename and
 | ||||
| 		// the AST::get_line_num() callback function.
 | ||||
|  | @ -352,6 +355,7 @@ namespace AST_INTERNAL | |||
| 	extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child; | ||||
| 	extern AST::AstModule *current_module; | ||||
| 	extern bool current_always_clocked; | ||||
| 	struct LookaheadRewriter; | ||||
| 	struct ProcessGenerator; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -157,6 +157,126 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const | |||
| 	return wire; | ||||
| } | ||||
| 
 | ||||
| // helper class for rewriting simple lookahead references in AST always blocks
 | ||||
| struct AST_INTERNAL::LookaheadRewriter | ||||
| { | ||||
| 	dict<IdString, pair<AstNode*, AstNode*>> lookaheadids; | ||||
| 
 | ||||
| 	void collect_lookaheadids(AstNode *node) | ||||
| 	{ | ||||
| 		if (node->lookahead) { | ||||
| 			log_assert(node->type == AST_IDENTIFIER); | ||||
| 			if (!lookaheadids.count(node->str)) { | ||||
| 				AstNode *wire = new AstNode(AST_WIRE); | ||||
| 				for (auto c : node->id2ast->children) | ||||
| 					wire->children.push_back(c->clone()); | ||||
| 				wire->str = stringf("$lookahead%s$%d", node->str.c_str(), autoidx++); | ||||
| 				wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false); | ||||
| 				wire->is_logic = true; | ||||
| 				while (wire->simplify(true, false, false, 1, -1, false, false)) { } | ||||
| 				current_ast_mod->children.push_back(wire); | ||||
| 				lookaheadids[node->str] = make_pair(node->id2ast, wire); | ||||
| 				wire->genRTLIL(); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		for (auto child : node->children) | ||||
| 			collect_lookaheadids(child); | ||||
| 	} | ||||
| 
 | ||||
| 	bool has_lookaheadids(AstNode *node) | ||||
| 	{ | ||||
| 		if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) != 0) | ||||
| 			return true; | ||||
| 
 | ||||
| 		for (auto child : node->children) | ||||
| 			if (has_lookaheadids(child)) | ||||
| 				return true; | ||||
| 
 | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	bool has_nonlookaheadids(AstNode *node) | ||||
| 	{ | ||||
| 		if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) == 0) | ||||
| 			return true; | ||||
| 
 | ||||
| 		for (auto child : node->children) | ||||
| 			if (has_nonlookaheadids(child)) | ||||
| 				return true; | ||||
| 
 | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	void rewrite_lookaheadids(AstNode *node, bool lhs = false) | ||||
| 	{ | ||||
| 		if (node->type == AST_ASSIGN_LE) | ||||
| 		{ | ||||
| 			if (has_lookaheadids(node->children[0])) | ||||
| 			{ | ||||
| 				if (has_nonlookaheadids(node->children[0])) | ||||
| 					log_error("incompatible mix of lookahead and non-lookahead IDs in LHS expression.\n"); | ||||
| 
 | ||||
| 				rewrite_lookaheadids(node->children[0], true); | ||||
| 				node->type = AST_ASSIGN_EQ; | ||||
| 			} | ||||
| 
 | ||||
| 			rewrite_lookaheadids(node->children[1], lhs); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (node->type == AST_IDENTIFIER && (node->lookahead || lhs)) { | ||||
| 			AstNode *newwire = lookaheadids.at(node->str).second; | ||||
| 			node->str = newwire->str; | ||||
| 			node->id2ast = newwire; | ||||
| 			lhs = false; | ||||
| 		} | ||||
| 
 | ||||
| 		for (auto child : node->children) | ||||
| 			rewrite_lookaheadids(child, lhs); | ||||
| 	} | ||||
| 
 | ||||
| 	LookaheadRewriter(AstNode *top) | ||||
| 	{ | ||||
| 		// top->dumpAst(NULL, "REWRITE-BEFORE> ");
 | ||||
| 		// top->dumpVlog(NULL, "REWRITE-BEFORE> ");
 | ||||
| 
 | ||||
| 		AstNode *block = nullptr; | ||||
| 
 | ||||
| 		for (auto c : top->children) | ||||
| 			if (c->type == AST_BLOCK) { | ||||
| 				log_assert(block == nullptr); | ||||
| 				block = c; | ||||
| 			} | ||||
| 		log_assert(block != nullptr); | ||||
| 
 | ||||
| 		collect_lookaheadids(block); | ||||
| 		rewrite_lookaheadids(block); | ||||
| 
 | ||||
| 		for (auto it : lookaheadids) | ||||
| 		{ | ||||
| 			AstNode *ref_orig = new AstNode(AST_IDENTIFIER); | ||||
| 			ref_orig->str = it.second.first->str; | ||||
| 			ref_orig->id2ast = it.second.first; | ||||
| 			ref_orig->was_checked = true; | ||||
| 
 | ||||
| 			AstNode *ref_temp = new AstNode(AST_IDENTIFIER); | ||||
| 			ref_temp->str = it.second.second->str; | ||||
| 			ref_temp->id2ast = it.second.second; | ||||
| 			ref_temp->was_checked = true; | ||||
| 
 | ||||
| 			AstNode *init_assign = new AstNode(AST_ASSIGN_EQ, ref_temp->clone(), ref_orig->clone()); | ||||
| 			AstNode *final_assign = new AstNode(AST_ASSIGN_LE, ref_orig, ref_temp); | ||||
| 
 | ||||
| 			block->children.insert(block->children.begin(), init_assign); | ||||
| 			block->children.push_back(final_assign); | ||||
| 		} | ||||
| 
 | ||||
| 		// top->dumpAst(NULL, "REWRITE-AFTER> ");
 | ||||
| 		// top->dumpVlog(NULL, "REWRITE-AFTER> ");
 | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| // helper class for converting AST always nodes to RTLIL processes
 | ||||
| struct AST_INTERNAL::ProcessGenerator | ||||
| { | ||||
|  | @ -191,6 +311,9 @@ struct AST_INTERNAL::ProcessGenerator | |||
| 
 | ||||
| 	ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg) | ||||
| 	{ | ||||
| 		// rewrite lookahead references
 | ||||
| 		LookaheadRewriter la_rewriter(always); | ||||
| 
 | ||||
| 		// generate process and simple root case
 | ||||
| 		proc = new RTLIL::Process; | ||||
| 		proc->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); | ||||
|  | @ -338,7 +461,7 @@ struct AST_INTERNAL::ProcessGenerator | |||
| 		return chunks; | ||||
| 	} | ||||
| 
 | ||||
| 	// recursively traverse the AST an collect all assigned signals
 | ||||
| 	// recursively traverse the AST and collect all assigned signals
 | ||||
| 	void collect_lvalues(RTLIL::SigSpec ®, AstNode *ast, bool type_eq, bool type_le, bool run_sort_and_unify = true) | ||||
| 	{ | ||||
| 		switch (ast->type) | ||||
|  | @ -1010,7 +1133,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) | |||
| 			int add_undef_bits_msb = 0; | ||||
| 			int add_undef_bits_lsb = 0; | ||||
| 
 | ||||
| 			if (id2ast && id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) { | ||||
| 			log_assert(id2ast != nullptr); | ||||
| 
 | ||||
| 			if (id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) { | ||||
| 				RTLIL::Wire *wire = current_module->addWire(str); | ||||
| 				wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); | ||||
| 				wire->name = str; | ||||
|  | @ -1025,7 +1150,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) | |||
| 				chunk = RTLIL::Const(id2ast->children[0]->bits); | ||||
| 				goto use_const_chunk; | ||||
| 			} | ||||
| 			else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) { | ||||
| 			else if ((id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) { | ||||
| 				RTLIL::Wire *current_wire = current_module->wire(str); | ||||
| 				if (current_wire->get_bool_attribute(ID::is_interface)) | ||||
| 					is_interface = true; | ||||
|  |  | |||
|  | @ -1754,8 +1754,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 	} | ||||
| 
 | ||||
| 	// replace dynamic ranges in left-hand side expressions (e.g. "foo[bar] <= 1'b1;") with
 | ||||
| 	// a big case block that selects the correct single-bit assignment.
 | ||||
| 	if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) { | ||||
| 	// either a big case block that selects the correct single-bit assignment, or mask and
 | ||||
| 	// shift operations.
 | ||||
| 	if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) | ||||
| 	{ | ||||
| 		if (children[0]->type != AST_IDENTIFIER || children[0]->children.size() == 0) | ||||
| 			goto skip_dynamic_range_lvalue_expansion; | ||||
| 		if (children[0]->children[0]->range_valid || did_something) | ||||
|  | @ -1764,10 +1766,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 			goto skip_dynamic_range_lvalue_expansion; | ||||
| 		if (!children[0]->id2ast->range_valid) | ||||
| 			goto skip_dynamic_range_lvalue_expansion; | ||||
| 
 | ||||
| 		int source_width = children[0]->id2ast->range_left - children[0]->id2ast->range_right + 1; | ||||
| 		int result_width = 1; | ||||
| 
 | ||||
| 		AstNode *shift_expr = NULL; | ||||
| 		AstNode *range = children[0]->children[0]; | ||||
| 
 | ||||
| 		if (range->children.size() == 1) { | ||||
| 			shift_expr = range->children[0]->clone(); | ||||
| 		} else { | ||||
|  | @ -1780,19 +1785,72 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 				log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); | ||||
| 			result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; | ||||
| 		} | ||||
| 		did_something = true; | ||||
| 		newNode = new AstNode(AST_CASE, shift_expr); | ||||
| 		for (int i = 0; i < source_width; i++) { | ||||
| 			int start_bit = children[0]->id2ast->range_right + i; | ||||
| 			AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true)); | ||||
| 
 | ||||
| 		if (0) | ||||
| 		{ | ||||
| 			// big case block
 | ||||
| 
 | ||||
| 			did_something = true; | ||||
| 			newNode = new AstNode(AST_CASE, shift_expr); | ||||
| 			for (int i = 0; i < source_width; i++) { | ||||
| 				int start_bit = children[0]->id2ast->range_right + i; | ||||
| 				AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true)); | ||||
| 				AstNode *lvalue = children[0]->clone(); | ||||
| 				lvalue->delete_children(); | ||||
| 				int end_bit = std::min(start_bit+result_width,source_width) - 1; | ||||
| 				lvalue->children.push_back(new AstNode(AST_RANGE, | ||||
| 						mkconst_int(end_bit, true), mkconst_int(start_bit, true))); | ||||
| 				cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone()))); | ||||
| 				newNode->children.push_back(cond); | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			// mask and shift operations, disabled for now
 | ||||
| 
 | ||||
| 			AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true))); | ||||
| 			wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); | ||||
| 			wire_mask->attributes["\\nosync"] = AstNode::mkconst_int(1, false); | ||||
| 			wire_mask->is_logic = true; | ||||
| 			while (wire_mask->simplify(true, false, false, 1, -1, false, false)) { } | ||||
| 			current_ast_mod->children.push_back(wire_mask); | ||||
| 
 | ||||
| 			AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true))); | ||||
| 			wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); | ||||
| 			wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false); | ||||
| 			wire_data->is_logic = true; | ||||
| 			while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } | ||||
| 			current_ast_mod->children.push_back(wire_data); | ||||
| 
 | ||||
| 			did_something = true; | ||||
| 			newNode = new AstNode(AST_BLOCK); | ||||
| 
 | ||||
| 			AstNode *lvalue = children[0]->clone(); | ||||
| 			lvalue->delete_children(); | ||||
| 			int end_bit = std::min(start_bit+result_width,source_width) - 1; | ||||
| 			lvalue->children.push_back(new AstNode(AST_RANGE, | ||||
| 					mkconst_int(end_bit, true), mkconst_int(start_bit, true))); | ||||
| 			cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone()))); | ||||
| 			newNode->children.push_back(cond); | ||||
| 
 | ||||
| 			AstNode *ref_mask = new AstNode(AST_IDENTIFIER); | ||||
| 			ref_mask->str = wire_mask->str; | ||||
| 			ref_mask->id2ast = wire_mask; | ||||
| 			ref_mask->was_checked = true; | ||||
| 
 | ||||
| 			AstNode *ref_data = new AstNode(AST_IDENTIFIER); | ||||
| 			ref_data->str = wire_data->str; | ||||
| 			ref_data->id2ast = wire_data; | ||||
| 			ref_data->was_checked = true; | ||||
| 
 | ||||
| 			AstNode *old_data = lvalue->clone(); | ||||
| 			if (type == AST_ASSIGN_LE) | ||||
| 				old_data->lookahead = true; | ||||
| 
 | ||||
| 			AstNode *shamt = shift_expr; | ||||
| 
 | ||||
| 			newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, ref_mask->clone(), | ||||
| 					new AstNode(AST_SHIFT_LEFT, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), shamt->clone()))); | ||||
| 			newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, ref_data->clone(), | ||||
| 					new AstNode(AST_SHIFT_LEFT, new AstNode(AST_BIT_AND, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), children[1]->clone()), shamt))); | ||||
| 			newNode->children.push_back(new AstNode(type, lvalue, new AstNode(AST_BIT_OR, new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask)), ref_data))); | ||||
| 		} | ||||
| 
 | ||||
| 		goto apply_newNode; | ||||
| 	} | ||||
| skip_dynamic_range_lvalue_expansion:; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue