diff options
Diffstat (limited to 'backends/cxxrtl')
| -rw-r--r-- | backends/cxxrtl/cxxrtl.h | 107 | ||||
| -rw-r--r-- | backends/cxxrtl/cxxrtl_backend.cc | 167 | 
2 files changed, 161 insertions, 113 deletions
| diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h index 4552a0125..073921cc4 100644 --- a/backends/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/cxxrtl.h @@ -457,6 +457,42 @@ struct value : public expr_base<value<Bits>> {  		return shr<AmountBits, /*Signed=*/true>(amount);  	} +	template<size_t ResultBits, size_t SelBits> +	value<ResultBits> bmux(const value<SelBits> &sel) const { +		static_assert(ResultBits << SelBits == Bits, "invalid sizes used in bmux()"); +		size_t amount = sel.data[0] * ResultBits; +		size_t shift_chunks = amount / chunk::bits; +		size_t shift_bits   = amount % chunk::bits; +		value<ResultBits> result; +		chunk::type carry = 0; +		if (ResultBits % chunk::bits + shift_bits > chunk::bits) +			carry = data[result.chunks + shift_chunks] << (chunk::bits - shift_bits); +		for (size_t n = 0; n < result.chunks; n++) { +			result.data[result.chunks - 1 - n] = carry | (data[result.chunks + shift_chunks - 1 - n] >> shift_bits); +			carry = (shift_bits == 0) ? 0 +				: data[result.chunks + shift_chunks - 1 - n] << (chunk::bits - shift_bits); +		} +		return result; +	} + +	template<size_t ResultBits, size_t SelBits> +	value<ResultBits> demux(const value<SelBits> &sel) const { +		static_assert(Bits << SelBits == ResultBits, "invalid sizes used in demux()"); +		size_t amount = sel.data[0] * Bits; +		size_t shift_chunks = amount / chunk::bits; +		size_t shift_bits   = amount % chunk::bits; +		value<ResultBits> result; +		chunk::type carry = 0; +		for (size_t n = 0; n < chunks; n++) { +			result.data[shift_chunks + n] = (data[n] << shift_bits) | carry; +			carry = (shift_bits == 0) ? 0 +				: data[n] >> (chunk::bits - shift_bits); +		} +		if (Bits % chunk::bits + shift_bits > chunk::bits) +			result.data[shift_chunks + chunks] = carry; +		return result; +	} +  	size_t ctpop() const {  		size_t count = 0;  		for (size_t n = 0; n < chunks; n++) { @@ -722,50 +758,32 @@ std::ostream &operator<<(std::ostream &os, const wire<Bits> &val) {  template<size_t Width>  struct memory { -	std::vector<value<Width>> data; +	const size_t depth; +	std::unique_ptr<value<Width>[]> data; -	size_t depth() const { -		return data.size(); -	} - -	memory() = delete; -	explicit memory(size_t depth) : data(depth) {} +	explicit memory(size_t depth) : depth(depth), data(new value<Width>[depth]) {}  	memory(const memory<Width> &) = delete;  	memory<Width> &operator=(const memory<Width> &) = delete;  	memory(memory<Width> &&) = default; -	memory<Width> &operator=(memory<Width> &&) = default; - -	// The only way to get the compiler to put the initializer in .rodata and do not copy it on stack is to stuff it -	// into a plain array. You'd think an std::initializer_list would work here, but it doesn't, because you can't -	// construct an initializer_list in a constexpr (or something) and so if you try to do that the whole thing is -	// first copied on the stack (probably overflowing it) and then again into `data`. -	template<size_t Size> -	struct init { -		size_t offset; -		value<Width> data[Size]; -	}; - -	template<size_t... InitSize> -	explicit memory(size_t depth, const init<InitSize> &...init) : data(depth) { -		data.resize(depth); -		// This utterly reprehensible construct is the most reasonable way to apply a function to every element -		// of a parameter pack, if the elements all have different types and so cannot be cast to an initializer list. -		auto _ = {std::move(std::begin(init.data), std::end(init.data), data.begin() + init.offset)...}; -		(void)_; +	memory<Width> &operator=(memory<Width> &&other) { +		assert(depth == other.depth); +		data = std::move(other.data); +		write_queue = std::move(other.write_queue); +		return *this;  	}  	// An operator for direct memory reads. May be used at any time during the simulation.  	const value<Width> &operator [](size_t index) const { -		assert(index < data.size()); +		assert(index < depth);  		return data[index];  	}  	// An operator for direct memory writes. May only be used before the simulation is started. If used  	// after the simulation is started, the design may malfunction.  	value<Width> &operator [](size_t index) { -		assert(index < data.size()); +		assert(index < depth);  		return data[index];  	} @@ -790,7 +808,7 @@ struct memory {  	std::vector<write> write_queue;  	void update(size_t index, const value<Width> &val, const value<Width> &mask, int priority = 0) { -		assert(index < data.size()); +		assert(index < depth);  		// Queue up the write while keeping the queue sorted by priority.  		write_queue.insert(  			std::upper_bound(write_queue.begin(), write_queue.end(), priority, @@ -947,9 +965,9 @@ struct debug_item : ::cxxrtl_object {  		flags   = 0;  		width   = Width;  		lsb_at  = 0; -		depth   = item.data.size(); +		depth   = item.depth;  		zero_at = zero_offset; -		curr    = item.data.empty() ? nullptr : item.data[0].data; +		curr    = item.data ? item.data[0].data : nullptr;  		next    = nullptr;  		outline = nullptr;  	} @@ -1051,9 +1069,9 @@ struct debug_items {  	}  }; -// Tag class to disambiguate module move constructor and module constructor that takes black boxes -// out of another instance of the module. -struct adopt {}; +// Tag class to disambiguate the default constructor used by the toplevel module that calls reset(), +// and the constructor of interior modules that should not call it. +struct interior {};  struct module {  	module() {} @@ -1557,6 +1575,27 @@ value<BitsY> mod_ss(const value<BitsA> &a, const value<BitsB> &b) {  	return divmod_ss<BitsY>(a, b).second;  } +template<size_t BitsY, size_t BitsA, size_t BitsB> +CXXRTL_ALWAYS_INLINE +value<BitsY> modfloor_uu(const value<BitsA> &a, const value<BitsB> &b) { +	return divmod_uu<BitsY>(a, b).second; +} + +// GHDL Modfloor operator. Returns r=a mod b, such that r has the same sign as b and +// a=b*N+r where N is some integer +// In practical terms, when a and b have different signs and the remainder returned by divmod_ss is not 0 +// then return the remainder + b +template<size_t BitsY, size_t BitsA, size_t BitsB> +CXXRTL_ALWAYS_INLINE +value<BitsY> modfloor_ss(const value<BitsA> &a, const value<BitsB> &b) { +	value<BitsY> r; +	r = divmod_ss<BitsY>(a, b).second; +	if((b.is_neg() != a.is_neg()) && !r.is_zero()) +		return add_ss<BitsY>(b, r); +	return r; +} + +  // Memory helper  struct memory_index {  	bool valid; diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index ff28c20b3..62768bd33 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -185,7 +185,7 @@ bool is_binary_cell(RTLIL::IdString type)  		ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or),  		ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx),  		ID($eq), ID($ne), ID($eqx), ID($nex), ID($gt), ID($ge), ID($lt), ID($le), -		ID($add), ID($sub), ID($mul), ID($div), ID($mod)); +		ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor));  }  bool is_extending_cell(RTLIL::IdString type) @@ -198,7 +198,7 @@ bool is_extending_cell(RTLIL::IdString type)  bool is_inlinable_cell(RTLIL::IdString type)  {  	return is_unary_cell(type) || is_binary_cell(type) || type.in( -		ID($mux), ID($concat), ID($slice), ID($pmux)); +		ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux));  }  bool is_ff_cell(RTLIL::IdString type) @@ -934,11 +934,6 @@ struct CxxrtlWorker {  		f << "}";  	} -	void dump_const_init(const RTLIL::Const &data) -	{ -		dump_const_init(data, data.size()); -	} -  	void dump_const(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false)  	{  		f << "value<" << width << ">"; @@ -1159,6 +1154,22 @@ struct CxxrtlWorker {  			for (int part = 0; part < s_width; part++) {  				f << ")";  			} +		// Big muxes +		} else if (cell->type == ID($bmux)) { +			dump_sigspec_rhs(cell->getPort(ID::A), for_debug); +			f << ".bmux<"; +			f << cell->getParam(ID::WIDTH).as_int(); +			f << ">("; +			dump_sigspec_rhs(cell->getPort(ID::S), for_debug); +			f << ").val()"; +		// Demuxes +		} else if (cell->type == ID($demux)) { +			dump_sigspec_rhs(cell->getPort(ID::A), for_debug); +			f << ".demux<"; +			f << GetSize(cell->getPort(ID::Y)); +			f << ">("; +			dump_sigspec_rhs(cell->getPort(ID::S), for_debug); +			f << ").val()";  		// Concats  		} else if (cell->type == ID($concat)) {  			dump_sigspec_rhs(cell->getPort(ID::B), for_debug); @@ -1785,20 +1796,10 @@ struct CxxrtlWorker {  		} else {  			f << "<" << wire->width << ">";  		} -		f << " " << mangle(wire); -		if (wire_init.count(wire)) { -			f << " "; -			dump_const_init(wire_init.at(wire)); -		} -		f << ";\n"; +		f << " " << mangle(wire) << ";\n";  		if (edge_wires[wire]) {  			if (!wire_type.is_buffered()) { -				f << indent << "value<" << wire->width << "> prev_" << mangle(wire); -				if (wire_init.count(wire)) { -					f << " "; -					dump_const_init(wire_init.at(wire)); -				} -				f << ";\n"; +				f << indent << "value<" << wire->width << "> prev_" << mangle(wire) << ";\n";  			}  			for (auto edge_type : edge_types) {  				if (edge_type.first.wire == wire) { @@ -1848,38 +1849,67 @@ struct CxxrtlWorker {  		f << "value<" << wire->width << "> " << mangle(wire) << ";\n";  	} -	void dump_memory(Mem *mem) +	void dump_reset_method(RTLIL::Module *module)  	{ -		dump_attrs(mem); -		f << indent << "memory<" << mem->width << "> " << mangle(mem) -		            << " { " << mem->size << "u"; -		if (!GetSize(mem->inits)) { -			f << " };\n"; -		} else { -			f << ",\n"; -			inc_indent(); -				for (auto &init : mem->inits) { +		int mem_init_idx = 0; +		inc_indent(); +			for (auto wire : module->wires()) { +				const auto &wire_type = wire_types[wire]; +				if (!wire_type.is_named() || wire_type.is_local()) continue; +				if (!wire_init.count(wire)) continue; + +				f << indent << mangle(wire) << " = "; +				if (wire_types[wire].is_buffered()) { +					f << "wire<" << wire->width << ">"; +				} else { +					f << "value<" << wire->width << ">"; +				} +				dump_const_init(wire_init.at(wire), wire->width); +				f << ";\n"; + +				if (edge_wires[wire] && !wire_types[wire].is_buffered()) { +					f << indent << "prev_" << mangle(wire) << " = "; +					dump_const(wire_init.at(wire), wire->width); +					f << ";\n"; +				} +			} +			for (auto &mem : mod_memories[module]) { +				for (auto &init : mem.inits) {  					if (init.removed)  						continue;  					dump_attrs(&init); -					int words = GetSize(init.data) / mem->width; -					f << indent << "memory<" << mem->width << ">::init<" << words << "> { " -						    << stringf("%#x", init.addr.as_int()) << ", {"; +					int words = GetSize(init.data) / mem.width; +					f << indent << "static const value<" << mem.width << "> "; +					f << "mem_init_" << ++mem_init_idx << "[" << words << "] {";  					inc_indent();  						for (int n = 0; n < words; n++) {  							if (n % 4 == 0)  								f << "\n" << indent;  							else  								f << " "; -							dump_const(init.data, mem->width, n * mem->width, /*fixed_width=*/true); +							dump_const(init.data, mem.width, n * mem.width, /*fixed_width=*/true);  							f << ",";  						}  					dec_indent(); -					f << "\n" << indent << "}},\n"; +					f << "\n"; +					f << indent << "};\n"; +					f << indent << "std::copy(std::begin(mem_init_" << mem_init_idx << "), "; +					f << "std::end(mem_init_" << mem_init_idx << "), "; +					f << "&" << mangle(&mem) << ".data[" << stringf("%#x", init.addr.as_int()) << "]);\n";  				} -			dec_indent(); -			f << indent << "};\n"; -		} +			} +			for (auto cell : module->cells()) { +				if (is_internal_cell(cell->type)) +					continue; +				f << indent << mangle(cell); +				RTLIL::Module *cell_module = module->design->module(cell->type); +				if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) { +					f << "->reset();\n"; +				} else { +					f << ".reset();\n"; +				} +			} +		dec_indent();  	}  	void dump_eval_method(RTLIL::Module *module) @@ -2200,6 +2230,10 @@ struct CxxrtlWorker {  						dump_wire(wire, /*is_local=*/false);  				}  				f << "\n"; +				f << indent << "void reset() override {\n"; +				dump_reset_method(module); +				f << indent << "}\n"; +				f << "\n";  				f << indent << "bool eval() override {\n";  				dump_eval_method(module);  				f << indent << "}\n"; @@ -2248,7 +2282,9 @@ struct CxxrtlWorker {  					dump_debug_wire(wire, /*is_local=*/false);  				bool has_memories = false;  				for (auto &mem : mod_memories[module]) { -					dump_memory(&mem); +					dump_attrs(&mem); +					f << indent << "memory<" << mem.width << "> " << mangle(&mem) +					            << " { " << mem.size << "u };\n";  					has_memories = true;  				}  				if (has_memories) @@ -2269,52 +2305,20 @@ struct CxxrtlWorker {  						dump_metadata_map(cell->attributes);  						f << ");\n";  					} else { -						f << indent << mangle(cell_module) << " " << mangle(cell) << ";\n"; +						f << indent << mangle(cell_module) << " " << mangle(cell) << " {interior()};\n";  					}  					has_cells = true;  				}  				if (has_cells)  					f << "\n"; -				f << indent << mangle(module) << "() {}\n"; -				if (has_cells) { -					f << indent << mangle(module) << "(adopt, " << mangle(module) << " other) :\n"; -					bool first = true; -					for (auto cell : module->cells()) { -						if (is_internal_cell(cell->type)) -							continue; -						if (first) { -							first = false; -						} else { -							f << ",\n"; -						} -						RTLIL::Module *cell_module = module->design->module(cell->type); -						if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) { -							f << indent << "  " << mangle(cell) << "(std::move(other." << mangle(cell) << "))"; -						} else { -							f << indent << "  " << mangle(cell) << "(adopt {}, std::move(other." << mangle(cell) << "))"; -						} -					} -					f << " {\n"; -					inc_indent(); -						for (auto cell : module->cells()) { -							if (is_internal_cell(cell->type)) -								continue; -							RTLIL::Module *cell_module = module->design->module(cell->type); -							if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) -								f << indent << mangle(cell) << "->reset();\n"; -						} -					dec_indent(); -					f << indent << "}\n"; -				} else { -					f << indent << mangle(module) << "(adopt, " << mangle(module) << " other) {}\n"; -				} -				f << "\n"; -				f << indent << "void reset() override {\n"; +				f << indent << mangle(module) << "(interior) {}\n"; +				f << indent << mangle(module) << "() {\n";  				inc_indent(); -					f << indent << "*this = " << mangle(module) << "(adopt {}, std::move(*this));\n"; +					f << indent << "reset();\n";  				dec_indent(); -				f << indent << "}\n"; +				f << indent << "};\n";  				f << "\n"; +				f << indent << "void reset() override;\n";  				f << indent << "bool eval() override;\n";  				f << indent << "bool commit() override;\n";  				if (debug_info) { @@ -2341,6 +2345,10 @@ struct CxxrtlWorker {  	{  		if (module->get_bool_attribute(ID(cxxrtl_blackbox)))  			return; +		f << indent << "void " << mangle(module) << "::reset() {\n"; +		dump_reset_method(module); +		f << indent << "}\n"; +		f << "\n";  		f << indent << "bool " << mangle(module) << "::eval() {\n";  		dump_eval_method(module);  		f << indent << "}\n"; @@ -2898,15 +2906,16 @@ struct CxxrtlWorker {  								debug_wire_type = {WireType::INLINE, node->cell}; // wire replaced with cell  								break;  							case FlowGraph::Node::Type::CONNECT: -							  debug_wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig +								debug_wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig  								break;  							default: continue;  						}  						debug_live_nodes.erase(node); -					} else if (wire_type.is_member() || wire_type.is_local()) { +					} else if (wire_type.is_member() || wire_type.type == WireType::LOCAL) {  						debug_wire_type = wire_type; // wire not inlinable  					} else { -						log_assert(wire_type.type == WireType::UNUSED); +						log_assert(wire_type.type == WireType::INLINE || +						           wire_type.type == WireType::UNUSED);  						if (flow.wire_comb_defs[wire].size() == 0) {  							if (wire_init.count(wire)) { // wire never modified  								debug_wire_type = {WireType::CONST, wire_init.at(wire)}; | 
