mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge branch 'YosysHQ:master' into rtlworks/script-stack/issue/3594
This commit is contained in:
		
						commit
						6da26921d8
					
				
					 35 changed files with 1399 additions and 203 deletions
				
			
		
							
								
								
									
										15
									
								
								CHANGELOG
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								CHANGELOG
									
										
									
									
									
								
							|  | @ -2,9 +2,22 @@ | |||
| List of major changes and improvements between releases | ||||
| ======================================================= | ||||
| 
 | ||||
| Yosys 0.29 .. Yosys 0.29-dev | ||||
| Yosys 0.30 .. Yosys 0.31-dev | ||||
| -------------------------- | ||||
| 
 | ||||
| Yosys 0.29 .. Yosys 0.30 | ||||
| -------------------------- | ||||
|  * New commands and options | ||||
|     - Added "recover_names" pass to recover names post-mapping. | ||||
| 
 | ||||
|  * Gowin support | ||||
|     - Added remaining primitives blackboxes. | ||||
| 
 | ||||
|  * Various | ||||
|     - "show -colorattr" will now color the cells, wires, and  | ||||
|       connection arrows. | ||||
|     - "show -viewer none" will not execute viewer. | ||||
| 
 | ||||
| Yosys 0.28 .. Yosys 0.29 | ||||
| -------------------------- | ||||
|  * New commands and options | ||||
|  |  | |||
							
								
								
									
										23
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										23
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -141,7 +141,7 @@ LDLIBS += -lrt | |||
| endif | ||||
| endif | ||||
| 
 | ||||
| YOSYS_VER := 0.29+44 | ||||
| YOSYS_VER := 0.30+48 | ||||
| 
 | ||||
| # Note: We arrange for .gitcommit to contain the (short) commit hash in
 | ||||
| # tarballs generated with git-archive(1) using .gitattributes. The git repo
 | ||||
|  | @ -157,7 +157,7 @@ endif | |||
| OBJS = kernel/version_$(GIT_REV).o | ||||
| 
 | ||||
| bumpversion: | ||||
| 	sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 9c5a60e.. | wc -l`/;" Makefile | ||||
| 	sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline f7a8284.. | wc -l`/;" Makefile | ||||
| 
 | ||||
| # set 'ABCREV = default' to use abc/ as it is
 | ||||
| #
 | ||||
|  | @ -165,7 +165,7 @@ bumpversion: | |||
| # is just a symlink to your actual ABC working directory, as 'make mrproper'
 | ||||
| # will remove the 'abc' directory and you do not want to accidentally
 | ||||
| # delete your work on ABC..
 | ||||
| ABCREV = 2c1c83f | ||||
| ABCREV = bb64142 | ||||
| ABCPULL = 1 | ||||
| ABCURL ?= https://github.com/YosysHQ/abc | ||||
| ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) | ||||
|  | @ -523,6 +523,7 @@ CXXFLAGS += -I$(GHDL_INCLUDE_DIR) -DYOSYS_ENABLE_GHDL | |||
| LDLIBS += $(GHDL_LIB_DIR)/libghdl.a $(file <$(GHDL_LIB_DIR)/libghdl.link) | ||||
| endif | ||||
| 
 | ||||
| LDLIBS_VERIFIC = | ||||
| ifeq ($(ENABLE_VERIFIC),1) | ||||
| VERIFIC_DIR ?= /usr/local/src/verific_lib | ||||
| VERIFIC_COMPONENTS ?= verilog database util containers hier_tree | ||||
|  | @ -548,9 +549,9 @@ CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS | |||
| endif | ||||
| CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC | ||||
| ifeq ($(OS), Darwin) | ||||
| LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)) -lz | ||||
| LDLIBS_VERIFIC += $(foreach comp,$(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)),-Wl,-force_load $(comp)) -lz | ||||
| else | ||||
| LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -lz | ||||
| LDLIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -Wl,--no-whole-archive -lz | ||||
| endif | ||||
| endif | ||||
| 
 | ||||
|  | @ -740,13 +741,13 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS)) | |||
| endif | ||||
| 
 | ||||
| $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) | ||||
| 	$(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) | ||||
| 	$(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) $(LDLIBS_VERIFIC) | ||||
| 
 | ||||
| libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) | ||||
| ifeq ($(OS), Darwin) | ||||
| 	$(P) $(LD) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) | ||||
| 	$(P) $(LD) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC) | ||||
| else | ||||
| 	$(P) $(LD) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) | ||||
| 	$(P) $(LD) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC) | ||||
| endif | ||||
| 
 | ||||
| %.o: %.cc | ||||
|  | @ -797,13 +798,13 @@ ifneq ($(ABCREV),default) | |||
| 	$(Q) if test -d abc && test -d abc/.git && ! git -C abc diff-index --quiet HEAD; then \
 | ||||
| 		echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \
 | ||||
| 	fi | ||||
| 	$(Q) if test -d abc && ! test -d abc/.git && ! test "`cat abc/.gitcommit | cut -c1-7`" == "$(ABCREV)"; then \
 | ||||
| 	$(Q) if test -d abc && ! test -d abc/.git && ! test "`cat abc/.gitcommit | cut -c1-7`" = "$(ABCREV)"; then \
 | ||||
| 		echo 'REEBE: Qbjaybnqrq NOP irefvbaf qbrf abg zngpu! Qbjaybnq sebz:' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; echo $(ABCURL)/archive/$(ABCREV).tar.gz; false; \
 | ||||
| 	fi | ||||
| # set a variable so the test fails if git fails to run - when comparing outputs directly, empty string would match empty string
 | ||||
| 	$(Q) if test -d abc && ! test -d abc/.git && test "`cat abc/.gitcommit | cut -c1-7`" == "$(ABCREV)"; then \
 | ||||
| 	$(Q) if test -d abc && ! test -d abc/.git && test "`cat abc/.gitcommit | cut -c1-7`" = "$(ABCREV)"; then \
 | ||||
| 		echo "Compiling local copy of ABC"; \
 | ||||
| 	elif ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" == "$$rev"); then \
 | ||||
| 	elif ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" = "$$rev"); then \
 | ||||
| 		test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \
 | ||||
| 		echo "Pulling ABC from $(ABCURL):"; set -x; \
 | ||||
| 		test -d abc || git clone $(ABCURL) abc; \
 | ||||
|  |  | |||
|  | @ -736,6 +736,9 @@ struct AigerWriter | |||
| 				auto sig_qy = cell->getPort(cell->type.in(ID($anyconst), ID($anyseq)) ? ID::Y : ID::Q); | ||||
| 				SigSpec sig = sigmap(sig_qy); | ||||
| 
 | ||||
| 				if (cell->get_bool_attribute(ID(clk2fflogic))) | ||||
| 					sig_qy = cell->getPort(ID::D); // For a clk2fflogic $_FF_ the named signal is the D input not the Q output
 | ||||
| 
 | ||||
| 				for (int i = 0; i < GetSize(sig_qy); i++) { | ||||
| 					if (sig_qy[i].wire == nullptr || sig[i].wire == nullptr) | ||||
| 						continue; | ||||
|  |  | |||
|  | @ -728,7 +728,10 @@ struct BtorWorker | |||
| 			else | ||||
| 				btorf("%d state %d %s\n", nid, sid, log_id(symbol)); | ||||
| 
 | ||||
| 			ywmap_state(sig_q); | ||||
| 			if (cell->get_bool_attribute(ID(clk2fflogic))) | ||||
| 				ywmap_state(cell->getPort(ID::D)); // For a clk2fflogic FF the named signal is the D input not the Q output
 | ||||
| 			else | ||||
| 				ywmap_state(sig_q); | ||||
| 
 | ||||
| 			if (nid_init_val >= 0) { | ||||
| 				int nid_init = next_nid++; | ||||
|  |  | |||
|  | @ -1595,6 +1595,25 @@ value<BitsY> modfloor_ss(const value<BitsA> &a, const value<BitsB> &b) { | |||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| template<size_t BitsY, size_t BitsA, size_t BitsB> | ||||
| CXXRTL_ALWAYS_INLINE | ||||
| value<BitsY> divfloor_uu(const value<BitsA> &a, const value<BitsB> &b) { | ||||
| 	return divmod_uu<BitsY>(a, b).first; | ||||
| } | ||||
| 
 | ||||
| // Divfloor. Similar to above: returns q=a//b, where q has the sign of a*b and a=b*q+N.
 | ||||
| // In other words, returns (truncating) a/b, except if a and b have different signs
 | ||||
| // and there's non-zero remainder, subtract one more towards floor.
 | ||||
| template<size_t BitsY, size_t BitsA, size_t BitsB> | ||||
| CXXRTL_ALWAYS_INLINE | ||||
| value<BitsY> divfloor_ss(const value<BitsA> &a, const value<BitsB> &b) { | ||||
| 	value<BitsY> q, r; | ||||
| 	std::tie(q, r) = divmod_ss<BitsY>(a, b); | ||||
| 	if ((b.is_neg() != a.is_neg()) && !r.is_zero()) | ||||
| 		return sub_uu<BitsY>(q, value<1> { 1u }); | ||||
| 	return q; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // Memory helper
 | ||||
| struct memory_index { | ||||
|  |  | |||
|  | @ -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($modfloor)); | ||||
| 		ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor), ID($divfloor)); | ||||
| } | ||||
| 
 | ||||
| bool is_extending_cell(RTLIL::IdString type) | ||||
|  |  | |||
|  | @ -120,6 +120,9 @@ struct EdifBackend : public Backend { | |||
| 		log("        sets the delimiting character for module port rename clauses to\n"); | ||||
| 		log("        parentheses, square brackets, or angle brackets.\n"); | ||||
| 		log("\n"); | ||||
| 		log("    -lsbidx\n"); | ||||
| 		log("        use index 0 for the LSB bit of a net or port instead of MSB.\n"); | ||||
| 		log("\n"); | ||||
| 		log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n"); | ||||
| 		log("command generates EDIF files for the Xilinx place&route tools. It might be\n"); | ||||
| 		log("necessary to make small modifications to this command when a different tool\n"); | ||||
|  | @ -132,6 +135,7 @@ struct EdifBackend : public Backend { | |||
| 		std::string top_module_name; | ||||
| 		bool port_rename = false; | ||||
| 		bool attr_properties = false; | ||||
| 		bool lsbidx = false; | ||||
| 		std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports; | ||||
| 		bool nogndvcc = false, gndvccy = false, keepmode = false; | ||||
| 		CellTypes ct(design); | ||||
|  | @ -173,6 +177,10 @@ struct EdifBackend : public Backend { | |||
| 				} | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-lsbidx") { | ||||
| 				lsbidx = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(f, filename, args, argidx); | ||||
|  | @ -184,6 +192,14 @@ struct EdifBackend : public Backend { | |||
| 
 | ||||
| 		for (auto module : design->modules()) | ||||
| 		{ | ||||
| 			lib_cell_ports[module->name]; | ||||
| 
 | ||||
| 			for (auto port : module->ports) | ||||
| 			{ | ||||
| 				Wire *wire = module->wire(port); | ||||
| 				lib_cell_ports[module->name][port] = std::max(lib_cell_ports[module->name][port], GetSize(wire)); | ||||
| 			} | ||||
| 
 | ||||
| 			if (module->get_blackbox_attribute()) | ||||
| 				continue; | ||||
| 
 | ||||
|  | @ -200,7 +216,7 @@ struct EdifBackend : public Backend { | |||
| 				if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) { | ||||
| 					lib_cell_ports[cell->type]; | ||||
| 					for (auto p : cell->connections()) | ||||
| 						lib_cell_ports[cell->type][p.first] = GetSize(p.second); | ||||
| 						lib_cell_ports[cell->type][p.first] = std::max(lib_cell_ports[cell->type][p.first], GetSize(p.second)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | @ -437,7 +453,7 @@ struct EdifBackend : public Backend { | |||
| 					*f << ")\n"; | ||||
| 					for (int i = 0; i < wire->width; i++) { | ||||
| 						RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i)); | ||||
| 						net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1), wire->port_input)); | ||||
| 						net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), lsbidx ? i : GetSize(wire)-i-1), wire->port_input)); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | @ -468,13 +484,13 @@ struct EdifBackend : public Backend { | |||
| 							log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n", | ||||
| 									i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i])); | ||||
| 						else { | ||||
| 							int member_idx = GetSize(sig)-i-1; | ||||
| 							int member_idx = lsbidx ? i : GetSize(sig)-i-1; | ||||
| 							auto m = design->module(cell->type); | ||||
| 							int width = sig.size(); | ||||
| 							if (m) { | ||||
| 								auto w = m->wire(p.first); | ||||
| 								if (w) { | ||||
| 									member_idx = GetSize(w)-i-1; | ||||
| 									member_idx = lsbidx ? i : GetSize(w)-i-1; | ||||
| 									width = GetSize(w); | ||||
| 								} | ||||
| 							} | ||||
|  |  | |||
|  | @ -626,8 +626,9 @@ struct Smt2Worker | |||
| 				} | ||||
| 
 | ||||
| 				bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst)); | ||||
| 				bool clk2fflogic = cell->type == ID($anyinit) && cell->get_bool_attribute(ID(clk2fflogic)); | ||||
| 				int smtoffset = 0; | ||||
| 				for (auto chunk : cell->getPort(QY).chunks()) { | ||||
| 				for (auto chunk : cell->getPort(clk2fflogic ? ID::D : QY).chunks()) { | ||||
| 					if (chunk.is_wire()) | ||||
| 						decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire, smtoffset)); | ||||
| 					smtoffset += chunk.width; | ||||
|  | @ -772,7 +773,7 @@ struct Smt2Worker | |||
| 			int arrayid = idcounter++; | ||||
| 			memarrays[mem] = arrayid; | ||||
| 
 | ||||
| 			int abits = ceil_log2(mem->size); | ||||
| 			int abits = max(1, ceil_log2(mem->size)); | ||||
| 
 | ||||
| 			bool has_sync_wr = false; | ||||
| 			bool has_async_wr = false; | ||||
|  | @ -1219,7 +1220,7 @@ struct Smt2Worker | |||
| 			{ | ||||
| 				int arrayid = memarrays.at(mem); | ||||
| 
 | ||||
| 				int abits = ceil_log2(mem->size);; | ||||
| 				int abits = max(1, ceil_log2(mem->size)); | ||||
| 
 | ||||
| 				bool has_sync_wr = false; | ||||
| 				bool has_async_wr = false; | ||||
|  |  | |||
|  | @ -174,6 +174,8 @@ def help(): | |||
|         further failed assertions. To output multiple traces | ||||
|         covering all found failed assertions, the character '%' is | ||||
|         replaced in all dump filenames with an increasing number. | ||||
|         In cover mode, don't stop when a cover trace contains a failed | ||||
|         assertion. | ||||
| 
 | ||||
|     --check-witness | ||||
|         check that the used witness file contains sufficient | ||||
|  | @ -1739,7 +1741,7 @@ elif covermode: | |||
|             smt_pop() | ||||
|             smt.write("(define-fun covers_%d ((state |%s_s|)) (_ BitVec %d) (bvand (covers_%d state) #b%s))" % (coveridx, topmod, len(cover_desc), coveridx-1, cover_mask)) | ||||
| 
 | ||||
|         if found_failed_assert: | ||||
|         if found_failed_assert and not keep_going: | ||||
|             break | ||||
| 
 | ||||
|         if "1" not in cover_mask: | ||||
|  |  | |||
|  | @ -768,7 +768,7 @@ class SmtIo: | |||
| 
 | ||||
|             if self.timeinfo: | ||||
|                 i = 0 | ||||
|                 s = "/-\|" | ||||
|                 s = r"/-\|" | ||||
| 
 | ||||
|                 count = 0 | ||||
|                 num_bs = 0 | ||||
|  | @ -1171,7 +1171,7 @@ class MkVcd: | |||
| 
 | ||||
|     def escape_name(self, name): | ||||
|         name = re.sub(r"\[([0-9a-zA-Z_]*[a-zA-Z_][0-9a-zA-Z_]*)\]", r"<\1>", name) | ||||
|         if re.match("[\[\]]", name) and name[0] != "\\": | ||||
|         if re.match(r"[\[\]]", name) and name[0] != "\\": | ||||
|             name = "\\" + name | ||||
|         return name | ||||
| 
 | ||||
|  |  | |||
|  | @ -194,7 +194,7 @@ def aiw2yw(input, mapfile, output): | |||
| 
 | ||||
|         values = WitnessValues() | ||||
|         for i, v in enumerate(inline): | ||||
|             if v == "x" or outyw.t > 0 and i in aiger_map.init_inputs: | ||||
|             if outyw.t > 0 and i in aiger_map.init_inputs: | ||||
|                 continue | ||||
| 
 | ||||
|             try: | ||||
|  |  | |||
|  | @ -571,7 +571,7 @@ The ``$mem_v2`` cell has the following ports: | |||
| 	signals for the read ports. | ||||
| 
 | ||||
| ``\RD_DATA`` | ||||
| 	This input is ``\RD_PORTS*\WIDTH`` bits wide, containing all data | ||||
| 	This output is ``\RD_PORTS*\WIDTH`` bits wide, containing all data | ||||
| 	signals for the read ports. | ||||
| 
 | ||||
| ``\RD_ARST`` | ||||
|  |  | |||
							
								
								
									
										654
									
								
								docs/source/CHAPTER_Memorymap.rst
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										654
									
								
								docs/source/CHAPTER_Memorymap.rst
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,654 @@ | |||
| .. _chapter:memorymap: | ||||
| 
 | ||||
| Memory mapping | ||||
| ============== | ||||
| 
 | ||||
| Documentation for the Yosys ``memory_libmap`` memory mapper.  Note that not all supported patterns | ||||
| are included in this document, of particular note is that combinations of multiple patterns should | ||||
| generally work.  For example, `Write port with byte enables`_ could be used in conjunction with any | ||||
| of the simple dual port (SDP) models.  In general if a hardware memory definition does not support a | ||||
| given configuration, additional logic will be instantiated to guarantee behaviour is consistent with | ||||
| simulation. | ||||
| 
 | ||||
| See also: `passes/memory/memlib.md <https://github.com/YosysHQ/yosys/blob/master/passes/memory/memlib.md>`_ | ||||
| 
 | ||||
| Additional notes | ||||
| ---------------- | ||||
| 
 | ||||
| Memory kind selection | ||||
| ~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| The memory inference code will automatically pick target memory primitive based on memory geometry | ||||
| and features used.  Depending on the target, there can be up to four memory primitive classes | ||||
| available for selection: | ||||
| 
 | ||||
| - FF RAM (aka logic): no hardware primitive used, memory lowered to a bunch of FFs and multiplexers | ||||
| 
 | ||||
|   - Can handle arbitrary number of write ports, as long as all write ports are in the same clock domain | ||||
|   - Can handle arbitrary number and kind of read ports | ||||
| 
 | ||||
| - LUT RAM (aka distributed RAM): uses LUT storage as RAM | ||||
|    | ||||
|   - Supported on most FPGAs (with notable exception of ice40) | ||||
|   - Usually has one synchronous write port, one or more asynchronous read ports | ||||
|   - Small | ||||
|   - Will never be used for ROMs (lowering to plain LUTs is always better) | ||||
| 
 | ||||
| - Block RAM: dedicated memory tiles | ||||
| 
 | ||||
|   - Supported on basically all FPGAs | ||||
|   - Supports only synchronous reads | ||||
|   - Two ports with separate clocks | ||||
|   - Usually supports true dual port (with notable exception of ice40 that only supports SDP) | ||||
|   - Usually supports asymmetric memories and per-byte write enables | ||||
|   - Several kilobits in size | ||||
| 
 | ||||
| - Huge RAM: | ||||
| 
 | ||||
|   - Only supported on several targets: | ||||
|      | ||||
|     - Some Xilinx UltraScale devices (UltraRAM) | ||||
| 
 | ||||
|       - Two ports, both with mutually exclusive synchronous read and write | ||||
|       - Single clock | ||||
|       - Initial data must be all-0 | ||||
| 
 | ||||
|     - Some ice40 devices (SPRAM) | ||||
| 
 | ||||
|       - Single port with mutually exclusive synchronous read and write | ||||
|       - Does not support initial data | ||||
| 
 | ||||
|     - Nexus (large RAM) | ||||
|        | ||||
|       - Two ports, both with mutually exclusive synchronous read and write | ||||
|       - Single clock | ||||
| 
 | ||||
|   - Will not be automatically selected by memory inference code, needs explicit opt-in via | ||||
|     ram_style attribute | ||||
| 
 | ||||
| In general, you can expect the automatic selection process to work roughly like this: | ||||
| 
 | ||||
| - If any read port is asynchronous, only LUT RAM (or FF RAM) can be used. | ||||
| - If there is more than one write port, only block RAM can be used, and this needs to be a | ||||
|   hardware-supported true dual port pattern | ||||
| 
 | ||||
|   - … unless all write ports are in the same clock domain, in which case FF RAM can also be used, | ||||
|     but this is generally not what you want for anything but really small memories | ||||
| 
 | ||||
| - Otherwise, either FF RAM, LUT RAM, or block RAM will be used, depending on memory size | ||||
| 
 | ||||
| This process can be overridden by attaching a ram_style attribute to the memory: | ||||
| 
 | ||||
| - `(* ram_style = "logic" *)` selects FF RAM | ||||
| - `(* ram_style = "distributed" *)` selects LUT RAM | ||||
| - `(* ram_style = "block" *)` selects block RAM | ||||
| - `(* ram_style = "huge" *)` selects huge RAM | ||||
| 
 | ||||
| It is an error if this override cannot be realized for the given target. | ||||
| 
 | ||||
| Many alternate spellings of the attribute are also accepted, for compatibility with other software. | ||||
| 
 | ||||
| Initial data | ||||
| ~~~~~~~~~~~~ | ||||
| 
 | ||||
| Most FPGA targets support initializing all kinds of memory to user-provided values.  If explicit | ||||
| initialization is not used the initial memory value is undefined.  Initial data can be provided by | ||||
| either initial statements writing memory cells one by one of ``$readmemh`` or ``$readmemb`` system | ||||
| tasks.  For an example pattern, see `Synchronous read port with initial value`_. | ||||
| 
 | ||||
| Write port with byte enables | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Byte enables can be used with any supported pattern | ||||
| - To ensure that multiple writes will be merged into one port, they need to have disjoint bit | ||||
|   ranges, have the same address, and the same clock | ||||
| - Any write enable granularity will be accepted (down to per-bit write enables), but using smaller | ||||
|   granularity than natively supported by the target is very likely to be inefficient (eg. using | ||||
|   4-bit bytes on ECP5 will result in either padding the bytes with 5 dummy bits to native 9-bit | ||||
|   units or splitting the RAM into two block RAMs) | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [31 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable[0]) | ||||
| 			mem[write_addr][7:0] <= write_data[7:0]; | ||||
| 		if (write_enable[1]) | ||||
| 			mem[write_addr][15:8] <= write_data[15:8]; | ||||
| 		if (write_enable[2]) | ||||
| 			mem[write_addr][23:16] <= write_data[23:16]; | ||||
| 		if (write_enable[3]) | ||||
| 			mem[write_addr][31:24] <= write_data[31:24]; | ||||
| 		if (read_enable) | ||||
| 			read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| Simple dual port (SDP) memory patterns | ||||
| -------------------------------------- | ||||
| 
 | ||||
| Asynchronous-read SDP | ||||
| ~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - This will result in LUT RAM on supported targets | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 	always @(posedge clk) | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 	assign read_data = mem[read_addr]; | ||||
| 
 | ||||
| Synchronous SDP with clock domain crossing | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Will result in block RAM or LUT RAM depending on size | ||||
| - No behavior guarantees in case of simultaneous read and write to the same address | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge write_clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 	end | ||||
| 
 | ||||
| 	always @(posedge read_clk) begin | ||||
| 		if (read_enable) | ||||
| 			read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| Synchronous SDP read first | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - The read and write parts can be in the same or different processes. | ||||
| - Will result in block RAM or LUT RAM depending on size | ||||
| - As long as the same clock is used for both, yosys will ensure read-first behavior.  This may | ||||
|   require extra circuitry on some targets for block RAM.  If this is not necessary, use one of the | ||||
|   patterns below. | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 		if (read_enable) | ||||
| 			read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| Synchronous SDP with undefined collision behavior | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Like above, but the read value is undefined when read and write ports target the same address in | ||||
|   the same cycle | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 
 | ||||
| 		if (read_enable) begin | ||||
| 			read_data <= mem[read_addr]; | ||||
| 		 | ||||
| 		// 👇 this if block 👇 | ||||
| 		if (write_enable && read_addr == write_addr) | ||||
| 			read_data <= 'x; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| - Or below, using the no_rw_check attribute | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	(* no_rw_check *) | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 
 | ||||
| 		if (read_enable)  | ||||
| 			read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| Synchronous SDP with write-first behavior | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Will result in block RAM or LUT RAM depending on size | ||||
| - May use additional circuitry for block RAM if write-first is not natively supported. Will always | ||||
|   use additional circuitry for LUT RAM. | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 
 | ||||
| 		if (read_enable) begin | ||||
| 			read_data <= mem[read_addr]; | ||||
| 			if (write_enable && read_addr == write_addr) | ||||
| 				read_data <= write_data; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| Synchronous SDP with write-first behavior (alternate pattern) | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - This pattern is supported for compatibility, but is much less flexible than the above | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 		read_addr_reg <= read_addr; | ||||
| 	end | ||||
| 
 | ||||
| 	assign read_data = mem[read_addr_reg]; | ||||
| 
 | ||||
| Single-port RAM memory patterns | ||||
| ------------------------------- | ||||
| 
 | ||||
| Asynchronous-read single-port RAM | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Will result in single-port LUT RAM on supported targets | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 	always @(posedge clk) | ||||
| 		if (write_enable) | ||||
| 			mem[addr] <= write_data; | ||||
| 	assign read_data = mem[addr]; | ||||
| 
 | ||||
| Synchronous single-port RAM with mutually exclusive read/write | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Will result in single-port block RAM or LUT RAM depending on size | ||||
| - This is the correct pattern to infer ice40 SPRAM (with manual ram_style selection) | ||||
| - On targets that don't support read/write block RAM ports (eg. ice40), will result in SDP block RAM instead | ||||
| - For block RAM, will use "NO_CHANGE" mode if available | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[addr] <= write_data; | ||||
| 		else if (read_enable) | ||||
| 			read_data <= mem[addr]; | ||||
| 	end | ||||
| 
 | ||||
| Synchronous single-port RAM with read-first behavior | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Will only result in single-port block RAM when read-first behavior is natively supported; | ||||
|   otherwise, SDP RAM with additional circuitry will be used | ||||
| - Many targets (Xilinx, ECP5, …) can only natively support read-first/write-first single-port RAM | ||||
|   (or TDP RAM) where the write_enable signal implies the read_enable signal (ie. can never write | ||||
|   without reading). The memory inference code will run a simple SAT solver on the control signals to | ||||
|   determine if this is the case, and insert emulation circuitry if it cannot be easily proven. | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[addr] <= write_data; | ||||
| 		if (read_enable) | ||||
| 			read_data <= mem[addr]; | ||||
| 	end | ||||
| 
 | ||||
| Synchronous single-port RAM with write-first behavior | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Will result in single-port block RAM or LUT RAM when supported | ||||
| - Block RAMs will require extra circuitry if write-first behavior not natively supported | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[addr] <= write_data; | ||||
| 		if (read_enable) | ||||
| 			if (write_enable) | ||||
| 				read_data <= write_data; | ||||
| 			else  | ||||
| 				read_data <= mem[addr]; | ||||
| 	end | ||||
| 
 | ||||
| Synchronous read port with initial value | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Initial read port values can be combined with any other supported pattern | ||||
| - If block RAM is used and initial read port values are not natively supported by the target, small | ||||
|   emulation circuit will be inserted | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 	reg [DATA_WIDTH - 1 : 0] read_data; | ||||
| 	initial read_data = 'h1234; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 		if (read_enable) | ||||
| 			read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| Read register reset patterns | ||||
| ---------------------------- | ||||
| 
 | ||||
| Resets can be combined with any other supported pattern (except that synchronous reset and | ||||
| asynchronous reset cannot both be used on a single read port).  If block RAM is used and the | ||||
| selected reset (synchronous or asynchronous) is used but not natively supported by the target, small | ||||
| emulation circuitry will be inserted. | ||||
| 
 | ||||
| Synchronous reset, reset priority over enable | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 
 | ||||
| 		if (read_reset) | ||||
| 			read_data <= {sval}; | ||||
| 		else if (read_enable) | ||||
| 			read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| Synchronous reset, enable priority over reset | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 		if (read_enable) | ||||
| 			if (read_reset) | ||||
| 				read_data <= 'h1234; | ||||
| 			else | ||||
| 				read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| Synchronous read port with asynchronous reset | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 	end | ||||
| 
 | ||||
| 	always @(posedge clk, posedge reset_read) begin | ||||
| 		if (reset_read) | ||||
| 			read_data <= 'h1234; | ||||
| 		else if (read_enable) | ||||
| 			read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| Asymmetric memory patterns | ||||
| -------------------------- | ||||
| 
 | ||||
| To construct an asymmetric memory (memory with read/write ports of differing widths): | ||||
| 
 | ||||
| - Declare the memory with the width of the narrowest intended port | ||||
| - Split all wide ports into multiple narrow ports | ||||
| - To ensure the wide ports will be correctly merged: | ||||
| 
 | ||||
|   - For the address, use a concatenation of actual address in the high bits and a constant in the | ||||
|     low bits | ||||
|   - Ensure the actual address is identical for all ports belonging to the wide port | ||||
|   - Ensure that clock is identical | ||||
|   - For read ports, ensure that enable/reset signals are identical (for write ports, the enable | ||||
|     signal may vary — this will result in using the byte enable functionality) | ||||
| 
 | ||||
| Asymmetric memory is supported on all targets, but may require emulation circuitry where not | ||||
| natively supported.  Note that when the memory is larger than the underlying block RAM primitive, | ||||
| hardware asymmetric memory support is likely not to be used even if present as it is more expensive. | ||||
| 
 | ||||
| Wide synchronous read port | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [7:0] mem [0:255]; | ||||
| 	wire [7:0] write_addr; | ||||
| 	wire [5:0] read_addr; | ||||
| 	wire [7:0] write_data; | ||||
| 	reg [31:0] read_data; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 		if (read_enable) begin | ||||
| 			read_data[7:0] <= mem[{read_addr, 2'b00}]; | ||||
| 			read_data[15:8] <= mem[{read_addr, 2'b01}]; | ||||
| 			read_data[23:16] <= mem[{read_addr, 2'b10}]; | ||||
| 			read_data[31:24] <= mem[{read_addr, 2'b11}]; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| Wide asynchronous read port | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Note: the only target natively supporting this pattern is Xilinx UltraScale | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [7:0] mem [0:511]; | ||||
| 	wire [8:0] write_addr; | ||||
| 	wire [5:0] read_addr; | ||||
| 	wire [7:0] write_data; | ||||
| 	wire [63:0] read_data; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 	end | ||||
| 
 | ||||
| 	assign read_data[7:0] = mem[{read_addr, 3'b000}]; | ||||
| 	assign read_data[15:8] = mem[{read_addr, 3'b001}]; | ||||
| 	assign read_data[23:16] = mem[{read_addr, 3'b010}]; | ||||
| 	assign read_data[31:24] = mem[{read_addr, 3'b011}]; | ||||
| 	assign read_data[39:32] = mem[{read_addr, 3'b100}]; | ||||
| 	assign read_data[47:40] = mem[{read_addr, 3'b101}]; | ||||
| 	assign read_data[55:48] = mem[{read_addr, 3'b110}]; | ||||
| 	assign read_data[63:56] = mem[{read_addr, 3'b111}]; | ||||
| 
 | ||||
| Wide write port | ||||
| ~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [7:0] mem [0:255]; | ||||
| 	wire [5:0] write_addr; | ||||
| 	wire [7:0] read_addr; | ||||
| 	wire [31:0] write_data; | ||||
| 	reg [7:0] read_data; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable[0]) | ||||
| 			mem[{write_addr, 2'b00}] <= write_data[7:0]; | ||||
| 		if (write_enable[1]) | ||||
| 			mem[{write_addr, 2'b01}] <= write_data[15:8]; | ||||
| 		if (write_enable[2]) | ||||
| 			mem[{write_addr, 2'b10}] <= write_data[23:16]; | ||||
| 		if (write_enable[3]) | ||||
| 			mem[{write_addr, 2'b11}] <= write_data[31:24]; | ||||
| 		if (read_enable) | ||||
| 			read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| True dual port (TDP) patterns | ||||
| ----------------------------- | ||||
| 
 | ||||
| - Many different variations of true dual port memory can be created by combining two single-port RAM | ||||
|   patterns on the same memory | ||||
| - When TDP memory is used, memory inference code has much less maneuver room to create requested | ||||
|   semantics compared to individual single-port patterns (which can end up lowered to SDP memory | ||||
|   where necessary) — supported patterns depend strongly on the target | ||||
| - In particular, when both ports have the same clock, it's likely that "undefined collision" mode | ||||
|   needs to be manually selected to enable TDP memory inference | ||||
| - The examples below are non-exhaustive — many more combinations of port types are possible | ||||
| - Note: if two write ports are in the same process, this defines a priority relation between them | ||||
|   (if both ports are active in the same clock, the later one wins). On almost all targets, this will | ||||
|   result in a bit of extra circuitry to ensure the priority semantics. If this is not what you want, | ||||
|   put them in separate processes. | ||||
| 
 | ||||
|   - Priority is not supported when using the verific front end and any priority semantics are ignored. | ||||
| 
 | ||||
| TDP with different clocks, exclusive read/write | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk_a) begin | ||||
| 		if (write_enable_a) | ||||
| 			mem[addr_a] <= write_data_a; | ||||
| 		else if (read_enable_a) | ||||
| 			read_data_a <= mem[addr_a]; | ||||
| 	end | ||||
| 
 | ||||
| 	always @(posedge clk_b) begin | ||||
| 		if (write_enable_b) | ||||
| 			mem[addr_b] <= write_data_b; | ||||
| 		else if (read_enable_b) | ||||
| 			read_data_b <= mem[addr_b]; | ||||
| 	end | ||||
| 
 | ||||
| TDP with same clock, read-first behavior | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - This requires hardware inter-port read-first behavior, and will only work on some targets (Xilinx, Nexus) | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable_a) | ||||
| 			mem[addr_a] <= write_data_a; | ||||
| 		if (read_enable_a) | ||||
| 			read_data_a <= mem[addr_a]; | ||||
| 	end | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable_b) | ||||
| 			mem[addr_b] <= write_data_b; | ||||
| 		if (read_enable_b) | ||||
| 			read_data_b <= mem[addr_b]; | ||||
| 	end | ||||
| 
 | ||||
| TDP with multiple read ports | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - The combination of a single write port with an arbitrary amount of read ports is supported on all | ||||
|   targets — if a multi-read port primitive is available (like Xilinx RAM64M), it'll be used as | ||||
|   appropriate.  Otherwise, the memory will be automatically split into multiple primitives. | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [31:0] mem [0:31]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] <= write_data; | ||||
| 	end | ||||
| 
 | ||||
| 	assign read_data_a = mem[read_addr_a]; | ||||
| 	assign read_data_b = mem[read_addr_b]; | ||||
| 	assign read_data_c = mem[read_addr_c]; | ||||
| 
 | ||||
| Not yet supported patterns | ||||
| -------------------------- | ||||
| 
 | ||||
| Synchronous SDP with write-first behavior via blocking assignments | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Would require modifications to the Yosys Verilog frontend. | ||||
| - Use `Synchronous SDP with write-first behavior`_ instead | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] = write_data; | ||||
| 
 | ||||
| 		if (read_enable) | ||||
| 			read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| Asymmetric memories via part selection | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Would require major changes to the Verilog frontend. | ||||
| - Build wide ports out of narrow ports instead (see `Wide synchronous read port`_) | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [31:0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	wire [1:0] byte_lane; | ||||
| 	wire [7:0] write_data; | ||||
| 
 | ||||
| 	always @(posedge clk) begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr][byte_lane * 8 +: 8] <= write_data; | ||||
| 
 | ||||
| 		if (read_enable) | ||||
| 			read_data <= mem[read_addr]; | ||||
| 	end | ||||
| 
 | ||||
| 
 | ||||
| Undesired patterns | ||||
| ------------------ | ||||
| 
 | ||||
| Asynchronous writes | ||||
| ~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| - Not supported in modern FPGAs | ||||
| - Not supported in yosys code anyhow | ||||
| 
 | ||||
| .. code:: verilog | ||||
| 
 | ||||
| 	reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; | ||||
| 
 | ||||
| 	always @* begin | ||||
| 		if (write_enable) | ||||
| 			mem[write_addr] = write_data; | ||||
| 	end | ||||
| 
 | ||||
| 	assign read_data = mem[read_addr]; | ||||
| 
 | ||||
|  | @ -42,6 +42,7 @@ Yosys manual | |||
| 	CHAPTER_Verilog.rst | ||||
| 	CHAPTER_Optimize.rst | ||||
| 	CHAPTER_Techmap.rst | ||||
| 	CHAPTER_Memorymap.rst | ||||
| 	CHAPTER_Eval.rst | ||||
| 
 | ||||
| .. raw:: latex | ||||
|  |  | |||
|  | @ -112,15 +112,26 @@ void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefil | |||
| 	string message = linefile ? stringf("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile)) : ""; | ||||
| 	message += vstringf(msg, args); | ||||
| 
 | ||||
| 	if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR) | ||||
| 		log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str()); | ||||
| 	else | ||||
| 		log("%s%s\n", message_prefix.c_str(), message.c_str()); | ||||
| 
 | ||||
| 	if (log_verific_callback) { | ||||
| 		string full_message = stringf("%s%s\n", message_prefix.c_str(), message.c_str()); | ||||
| 		log_verific_callback(int(msg_type), message_id, LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile), full_message.c_str()); | ||||
| 	} else { | ||||
| 		if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR) | ||||
| 			log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str()); | ||||
| 		else | ||||
| 			log("%s%s\n", message_prefix.c_str(), message.c_str()); | ||||
| 	} | ||||
| 	if (verific_error_msg.empty() && (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_PROGRAM_ERROR)) | ||||
| 		verific_error_msg = message; | ||||
| } | ||||
| 
 | ||||
| void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg)) | ||||
| { | ||||
| 	Message::SetConsoleOutput(0); | ||||
| 	Message::RegisterCallBackMsg(msg_func); | ||||
| 	log_verific_callback = cb; | ||||
| } | ||||
| 
 | ||||
| string get_full_netlist_name(Netlist *nl) | ||||
| { | ||||
| 	if (nl->NumOfRefs() == 1) { | ||||
|  | @ -1638,6 +1649,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma | |||
| 				cell->parameters[ID::TRANSPARENT] = false; | ||||
| 				cell->parameters[ID::ABITS] = GetSize(addr); | ||||
| 				cell->parameters[ID::WIDTH] = GetSize(data); | ||||
| 				import_attributes(cell->attributes, inst); | ||||
| 				cell->setPort(ID::CLK, RTLIL::State::Sx); | ||||
| 				cell->setPort(ID::EN, RTLIL::State::Sx); | ||||
| 				cell->setPort(ID::ADDR, addr); | ||||
|  | @ -1667,6 +1679,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma | |||
| 				cell->parameters[ID::PRIORITY] = 0; | ||||
| 				cell->parameters[ID::ABITS] = GetSize(addr); | ||||
| 				cell->parameters[ID::WIDTH] = GetSize(data); | ||||
| 				import_attributes(cell->attributes, inst); | ||||
| 				cell->setPort(ID::EN, RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data))); | ||||
| 				cell->setPort(ID::CLK, RTLIL::State::S0); | ||||
| 				cell->setPort(ID::ADDR, addr); | ||||
|  | @ -1994,7 +2007,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma | |||
| 					initval[i] = State::Sx; | ||||
| 			} | ||||
| 
 | ||||
| 			if (initval.is_fully_undef()) | ||||
| 			if (wire->port_input) { | ||||
| 				wire->attributes[ID::defaultvalue] = Const(initval); | ||||
| 				wire->attributes.erase(ID::init); | ||||
| 			} else if (initval.is_fully_undef()) | ||||
| 				wire->attributes.erase(ID::init); | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -59,6 +59,7 @@ bool log_quiet_warnings = false; | |||
| int log_verbose_level; | ||||
| string log_last_error; | ||||
| void (*log_error_atexit)() = NULL; | ||||
| void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg) = NULL; | ||||
| 
 | ||||
| int log_make_debug = 0; | ||||
| int log_force_debug = 0; | ||||
|  |  | |||
|  | @ -132,6 +132,9 @@ void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(for | |||
| void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); | ||||
| void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); | ||||
| 
 | ||||
| void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg)); | ||||
| extern void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg); | ||||
| 
 | ||||
| // Log with filename to report a problem in a source file.
 | ||||
| void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); | ||||
| void log_file_info(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); | ||||
|  |  | |||
|  | @ -178,6 +178,8 @@ class WType: | |||
| 		t.cont = None | ||||
| 		t.attr_type = attr_types.default | ||||
| 		if str_def.find("<") != -1:# and str_def.find("<") < str_def.find(" "): | ||||
| 			str_def = str_def.replace("const ", "") | ||||
| 
 | ||||
| 			candidate = WContainer.from_string(str_def, containing_file, line_number) | ||||
| 			if candidate == None: | ||||
| 				return None | ||||
|  | @ -203,8 +205,12 @@ class WType: | |||
| 
 | ||||
| 		prefix = "" | ||||
| 
 | ||||
| 		if str.startswith(str_def, "const "): | ||||
| 			if "char_p" in str_def: | ||||
| 				prefix = "const " | ||||
| 			str_def = str_def[6:] | ||||
| 		if str.startswith(str_def, "unsigned "): | ||||
| 			prefix = "unsigned " | ||||
| 			prefix = "unsigned " + prefix | ||||
| 			str_def = str_def[9:] | ||||
| 		while str.startswith(str_def, "long "): | ||||
| 			prefix= "long " + prefix | ||||
|  | @ -1285,7 +1291,7 @@ class WFunction: | |||
| 		prefix = "" | ||||
| 		i = 0 | ||||
| 		for part in parts: | ||||
| 			if part in ["unsigned", "long", "short"]: | ||||
| 			if part in ["unsigned", "long", "short", "const"]: | ||||
| 				prefix += part + " " | ||||
| 				i += 1 | ||||
| 			else: | ||||
|  | @ -1361,10 +1367,17 @@ class WFunction: | |||
| 			func.args.append(parsed) | ||||
| 		return func | ||||
| 
 | ||||
| 	@property | ||||
| 	def mangled_name(self): | ||||
| 		mangled_typename = lambda code: code.replace("::", "_").replace("<","_").replace(">","_") \ | ||||
| 										   .replace(" ","").replace("*","").replace(",","") | ||||
| 
 | ||||
| 		return self.name + "".join( | ||||
| 			f"__{mangled_typename(arg.wtype.gen_text_cpp())}" for arg in self.args | ||||
| 		) | ||||
| 
 | ||||
| 	def gen_alias(self): | ||||
| 		self.alias = self.name | ||||
| 		for arg in self.args: | ||||
| 			self.alias += "__" + arg.wtype.gen_text_cpp().replace("::", "_").replace("<","_").replace(">","_").replace(" ","").replace("*","").replace(",","") | ||||
| 		self.alias = self.mangled_name | ||||
| 
 | ||||
| 	def gen_decl(self): | ||||
| 		if self.duplicate: | ||||
|  | @ -2190,12 +2203,15 @@ def clean_duplicates(): | |||
| 			for fun in class_.found_funs: | ||||
| 				if fun.gen_decl_hash_py() in known_decls: | ||||
| 					debug("Multiple declarations of " + fun.gen_decl_hash_py(),3) | ||||
| 
 | ||||
| 					other = known_decls[fun.gen_decl_hash_py()] | ||||
| 					other.gen_alias() | ||||
| 					fun.gen_alias() | ||||
| 					if fun.gen_decl_hash_py() == other.gen_decl_hash_py(): | ||||
| 					if fun.mangled_name == other.mangled_name: | ||||
| 						fun.duplicate = True | ||||
| 						debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3) | ||||
| 						continue | ||||
| 
 | ||||
| 					other.gen_alias() | ||||
| 					fun.gen_alias() | ||||
| 				else: | ||||
| 					known_decls[fun.gen_decl_hash_py()] = fun | ||||
| 			known_decls = [] | ||||
|  |  | |||
|  | @ -112,11 +112,10 @@ struct CheckPass : public Pass { | |||
| 				for (size_t i = 0; i < all_cases.size(); i++) { | ||||
| 					for (auto action : all_cases[i]->actions) { | ||||
| 						for (auto bit : sigmap(action.first)) | ||||
| 							if (bit.wire) { | ||||
| 								wire_drivers[bit].push_back( | ||||
| 									stringf("action %s <= %s (case rule) in process %s", | ||||
| 									        log_signal(action.first), log_signal(action.second), log_id(proc_it.first))); | ||||
| 							} | ||||
| 							wire_drivers[bit].push_back( | ||||
| 								stringf("action %s <= %s (case rule) in process %s", | ||||
| 										log_signal(action.first), log_signal(action.second), log_id(proc_it.first))); | ||||
| 
 | ||||
| 						for (auto bit : sigmap(action.second)) | ||||
| 							if (bit.wire) used_wires.insert(bit); | ||||
| 					} | ||||
|  | @ -134,10 +133,9 @@ struct CheckPass : public Pass { | |||
| 						if (bit.wire) used_wires.insert(bit); | ||||
| 					for (auto action : sync->actions) { | ||||
| 						for (auto bit : sigmap(action.first)) | ||||
| 							if (bit.wire) | ||||
| 								wire_drivers[bit].push_back( | ||||
| 									stringf("action %s <= %s (sync rule) in process %s", | ||||
| 									        log_signal(action.first), log_signal(action.second), log_id(proc_it.first))); | ||||
| 							wire_drivers[bit].push_back( | ||||
| 								stringf("action %s <= %s (sync rule) in process %s", | ||||
| 										log_signal(action.first), log_signal(action.second), log_id(proc_it.first))); | ||||
| 						for (auto bit : sigmap(action.second)) | ||||
| 							if (bit.wire) used_wires.insert(bit); | ||||
| 					} | ||||
|  | @ -176,7 +174,8 @@ struct CheckPass : public Pass { | |||
| 							if (logic_cell) | ||||
| 								topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)), | ||||
| 										stringf("wire %s", log_signal(sig[i]))); | ||||
| 							if (sig[i].wire) | ||||
| 
 | ||||
| 							if (sig[i].wire || !cell->input(conn.first)) | ||||
| 								wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", | ||||
| 										log_id(conn.first), i, log_id(cell), log_id(cell->type))); | ||||
| 						} | ||||
|  | @ -192,7 +191,8 @@ struct CheckPass : public Pass { | |||
| 				if (wire->port_input) { | ||||
| 					SigSpec sig = sigmap(wire); | ||||
| 					for (int i = 0; i < GetSize(sig); i++) | ||||
| 						wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i)); | ||||
| 						if (sig[i].wire || !wire->port_output) | ||||
| 							wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i)); | ||||
| 				} | ||||
| 				if (wire->port_output) | ||||
| 					for (auto bit : sigmap(wire)) | ||||
|  | @ -212,6 +212,15 @@ struct CheckPass : public Pass { | |||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			for (auto state : {State::S0, State::S1, State::Sx}) | ||||
| 				if (wire_drivers.count(state)) { | ||||
| 					string message = stringf("Drivers conflicting with a constant %s driver:\n", log_signal(state)); | ||||
| 					for (auto str : wire_drivers[state]) | ||||
| 						message += stringf("    %s\n", str.c_str()); | ||||
| 					log_warning("%s", message.c_str()); | ||||
| 					counter++; | ||||
| 				} | ||||
| 
 | ||||
| 			for (auto it : wire_drivers) | ||||
| 				if (wire_drivers_count[it.first] > 1) { | ||||
| 					string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first)); | ||||
|  |  | |||
|  | @ -139,7 +139,12 @@ static bool rename_witness(RTLIL::Design *design, dict<RTLIL::Module *, int> &ca | |||
| 
 | ||||
| 		if (cell->type.in(ID($anyconst), ID($anyseq), ID($anyinit), ID($allconst), ID($allseq))) { | ||||
| 			has_witness_signals = true; | ||||
| 			auto QY = cell->type == ID($anyinit) ? ID::Q : ID::Y; | ||||
| 			IdString QY; | ||||
| 			bool clk2fflogic = false; | ||||
| 			if (cell->type == ID($anyinit)) | ||||
| 				QY = (clk2fflogic = cell->get_bool_attribute(ID(clk2fflogic))) ? ID::D : ID::Q; | ||||
| 			else | ||||
| 				QY = ID::Y; | ||||
| 			auto sig_out = cell->getPort(QY); | ||||
| 
 | ||||
| 			for (auto chunk : sig_out.chunks()) { | ||||
|  | @ -151,7 +156,10 @@ static bool rename_witness(RTLIL::Design *design, dict<RTLIL::Module *, int> &ca | |||
| 					auto new_id = module->uniquify("\\_witness_." + name); | ||||
| 					auto new_wire = module->addWire(new_id, GetSize(sig_out)); | ||||
| 					new_wire->set_hdlname_attribute({ "_witness_", strstr(new_id.c_str(), ".") + 1 }); | ||||
| 					module->connect({sig_out, new_wire}); | ||||
| 					if (clk2fflogic) | ||||
| 						module->connect({new_wire, sig_out}); | ||||
| 					else | ||||
| 						module->connect({sig_out, new_wire}); | ||||
| 					cell->setPort(QY, new_wire); | ||||
| 					break; | ||||
| 				} | ||||
|  |  | |||
|  | @ -84,7 +84,7 @@ struct ShowWorker | |||
| 	std::string nextColor() | ||||
| 	{ | ||||
| 		if (currentColor == 0) | ||||
| 			return "color=\"black\""; | ||||
| 			return "color=\"black\", fontcolor=\"black\""; | ||||
| 		return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", currentColor%8+1, currentColor%8+1); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -97,19 +97,16 @@ struct ShowWorker | |||
| 
 | ||||
| 	std::string nextColor(RTLIL::SigSpec sig, std::string defaultColor) | ||||
| 	{ | ||||
| 		sig.sort_and_unify(); | ||||
| 		for (auto &c : sig.chunks()) { | ||||
| 			if (c.wire != nullptr) | ||||
| 				for (auto &s : color_selections) | ||||
| 					if (s.second.selected_members.count(module->name) > 0 && s.second.selected_members.at(module->name).count(c.wire->name) > 0) | ||||
| 						return stringf("color=\"%s\"", s.first.c_str()); | ||||
| 		} | ||||
| 		std::string color = findColor(sig); | ||||
| 		if (!color.empty()) return color; | ||||
| 		return defaultColor; | ||||
| 	} | ||||
| 
 | ||||
| 	std::string nextColor(const RTLIL::SigSig &conn, std::string defaultColor) | ||||
| 	{ | ||||
| 		return nextColor(conn.first, nextColor(conn.second, defaultColor)); | ||||
| 		std::string color = findColor(conn); | ||||
| 		if (!color.empty()) return color; | ||||
| 		return defaultColor; | ||||
| 	} | ||||
| 
 | ||||
| 	std::string nextColor(const RTLIL::SigSpec &sig) | ||||
|  | @ -131,12 +128,28 @@ struct ShowWorker | |||
| 		return stringf("style=\"setlinewidth(3)\", label=\"<%d>\"", bits); | ||||
| 	} | ||||
| 
 | ||||
| 	const char *findColor(std::string member_name) | ||||
| 	std::string findColor(RTLIL::SigSpec sig) | ||||
| 	{ | ||||
| 		sig.sort_and_unify(); | ||||
| 		for (auto &c : sig.chunks()) { | ||||
| 			if (c.wire != nullptr) | ||||
| 				return findColor(c.wire->name); | ||||
| 		} | ||||
| 		return ""; | ||||
| 	} | ||||
| 
 | ||||
| 	std::string findColor(const RTLIL::SigSig &conn) | ||||
| 	{ | ||||
| 		std::string firstColor = findColor(conn.first); | ||||
| 		if (findColor(conn.second) == firstColor) return firstColor; | ||||
| 		return ""; | ||||
| 	} | ||||
| 
 | ||||
| 	std::string findColor(IdString member_name) | ||||
| 	{ | ||||
| 		for (auto &s : color_selections) | ||||
| 			if (s.second.selected_member(module->name, member_name)) { | ||||
| 				dot_escape_store.push_back(stringf(", color=\"%s\"", s.first.c_str())); | ||||
| 				return dot_escape_store.back().c_str(); | ||||
| 				return stringf("color=\"%s\", fontcolor=\"%s\"", s.first.c_str(), s.first.c_str()); | ||||
| 			} | ||||
| 
 | ||||
| 		RTLIL::Const colorattr_value; | ||||
|  | @ -155,8 +168,7 @@ struct ShowWorker | |||
| 			colorattr_cache[colorattr_value] = (next_id % 8) + 1; | ||||
| 		} | ||||
| 
 | ||||
| 		dot_escape_store.push_back(stringf(", colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", colorattr_cache.at(colorattr_value), colorattr_cache.at(colorattr_value))); | ||||
| 		return dot_escape_store.back().c_str(); | ||||
| 		return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", colorattr_cache.at(colorattr_value), colorattr_cache.at(colorattr_value)); | ||||
| 	} | ||||
| 
 | ||||
| 	const char *findLabel(std::string member_name) | ||||
|  | @ -189,6 +201,12 @@ struct ShowWorker | |||
| 		if (id[0] == '\\') | ||||
| 			id = id.substr(1); | ||||
| 
 | ||||
| 		// TODO: optionally include autoname + print correspondence in case of ambiguity
 | ||||
| 		size_t max_label_len = abbreviateIds ? 256 : 16384; | ||||
| 		if (id.size() > max_label_len) { | ||||
| 			id = id.substr(0,max_label_len-3) + "..."; | ||||
| 		} | ||||
| 
 | ||||
| 		std::string str; | ||||
| 		for (char ch : id) { | ||||
| 			if (ch == '\\') { | ||||
|  | @ -196,7 +214,7 @@ struct ShowWorker | |||
| 				str += "╲"; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (ch == '"') | ||||
| 			if (ch == '"' || ch == '<' || ch == '>') | ||||
| 				str += "\\"; | ||||
| 			str += ch; | ||||
| 		} | ||||
|  | @ -317,7 +335,7 @@ struct ShowWorker | |||
| 			} | ||||
| 
 | ||||
| 			code += stringf("x%d [ shape=record, style=rounded, label=\"", dot_idx) \ | ||||
| 					+ join_label_pieces(label_pieces) + "\" ];\n"; | ||||
| 					+ join_label_pieces(label_pieces) + stringf("\", %s ];\n", nextColor(sig).c_str()); | ||||
| 
 | ||||
| 			if (!port.empty()) { | ||||
| 				currentColor = xorshift32(currentColor); | ||||
|  | @ -414,9 +432,9 @@ struct ShowWorker | |||
| 			if (wire->port_input || wire->port_output) | ||||
| 				shape = "octagon"; | ||||
| 			if (wire->name.isPublic()) { | ||||
| 				fprintf(f, "n%d [ shape=%s, label=\"%s\", %s, fontcolor=\"black\" ];\n", | ||||
| 				fprintf(f, "n%d [ shape=%s, label=\"%s\", %s ];\n", | ||||
| 						id2num(wire->name), shape, findLabel(wire->name.str()), | ||||
| 						nextColor(RTLIL::SigSpec(wire), "color=\"black\"").c_str()); | ||||
| 						nextColor(RTLIL::SigSpec(wire), "color=\"black\", fontcolor=\"black\"").c_str()); | ||||
| 				if (wire->port_input) | ||||
| 					all_sources.insert(stringf("n%d", id2num(wire->name))); | ||||
| 				else if (wire->port_output) | ||||
|  | @ -481,11 +499,11 @@ struct ShowWorker | |||
| #ifdef CLUSTER_CELLS_AND_PORTBOXES | ||||
| 			if (!code.empty()) | ||||
| 				fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s ];\n%s}\n", | ||||
| 						id2num(cell->name), id2num(cell->name), label_string.c_str(), findColor(cell->name), code.c_str()); | ||||
| 						id2num(cell->name), id2num(cell->name), label_string.c_str(), color.c_str(), code.c_str()); | ||||
| 			else | ||||
| #endif | ||||
| 				fprintf(f, "c%d [ shape=record, label=\"%s\"%s ];\n%s", | ||||
| 						id2num(cell->name), label_string.c_str(), findColor(cell->name.str()), code.c_str()); | ||||
| 				fprintf(f, "c%d [ shape=record, label=\"%s\", %s ];\n%s", | ||||
| 						id2num(cell->name), label_string.c_str(), findColor(cell->name).c_str(), code.c_str()); | ||||
| 		} | ||||
| 
 | ||||
| 		for (auto &it : module->processes) | ||||
|  | @ -555,9 +573,9 @@ struct ShowWorker | |||
| 				} else if (right_node[0] == 'x') { | ||||
| 					net_conn_map[left_node].out.insert({right_node, GetSize(conn.first)}); | ||||
| 				} else { | ||||
| 					net_conn_map[right_node].in.insert({stringf("x%d:e", single_idx_count), GetSize(conn.first)}); | ||||
| 					net_conn_map[left_node].out.insert({stringf("x%d:w", single_idx_count), GetSize(conn.first)}); | ||||
| 					fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\"];\n", single_idx_count++); | ||||
| 					net_conn_map[right_node].in.insert({stringf("x%d", single_idx_count), GetSize(conn.first)}); | ||||
| 					net_conn_map[left_node].out.insert({stringf("x%d", single_idx_count), GetSize(conn.first)}); | ||||
| 					fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\", %s];\n", single_idx_count++, findColor(conn).c_str()); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | @ -643,6 +661,7 @@ struct ShowPass : public Pass { | |||
| 		log("    -viewer <viewer>\n"); | ||||
| 		log("        Run the specified command with the graphics file as parameter.\n"); | ||||
| 		log("        On Windows, this pauses yosys until the viewer exits.\n"); | ||||
| 		log("        Use \"-viewer none\" to not run any command.\n"); | ||||
| 		log("\n"); | ||||
| 		log("    -format <format>\n"); | ||||
| 		log("        Generate a graphics file in the specified format. Use 'dot' to just\n"); | ||||
|  | @ -903,28 +922,30 @@ struct ShowPass : public Pass { | |||
| 		#if defined(YOSYS_DISABLE_SPAWN) | ||||
| 			log_assert(viewer_exe.empty() && !format.empty()); | ||||
| 		#else | ||||
| 		if (!viewer_exe.empty()) { | ||||
| 			#ifdef _WIN32 | ||||
| 				// system()/cmd.exe does not understand single quotes nor
 | ||||
| 				// background tasks on Windows. So we have to pause yosys
 | ||||
| 				// until the viewer exits.
 | ||||
| 				std::string cmd = stringf("%s \"%s\"", viewer_exe.c_str(), out_file.c_str()); | ||||
| 			#else | ||||
| 				std::string cmd = stringf("%s '%s' %s", viewer_exe.c_str(), out_file.c_str(), background.c_str()); | ||||
| 			#endif | ||||
| 			log("Exec: %s\n", cmd.c_str()); | ||||
| 			if (run_command(cmd) != 0) | ||||
| 				log_cmd_error("Shell command failed!\n"); | ||||
| 		} else | ||||
| 		if (format.empty()) { | ||||
| 			#ifdef __APPLE__ | ||||
| 			std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' %s", getuid(), dot_file.c_str(), dot_file.c_str(), background.c_str()); | ||||
| 			#else | ||||
| 			std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' %s", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), background.c_str()); | ||||
| 			#endif | ||||
| 			log("Exec: %s\n", cmd.c_str()); | ||||
| 			if (run_command(cmd) != 0) | ||||
| 				log_cmd_error("Shell command failed!\n"); | ||||
| 		if (viewer_exe != "none") { | ||||
| 			if (!viewer_exe.empty()) { | ||||
| 				#ifdef _WIN32 | ||||
| 					// system()/cmd.exe does not understand single quotes nor
 | ||||
| 					// background tasks on Windows. So we have to pause yosys
 | ||||
| 					// until the viewer exits.
 | ||||
| 					std::string cmd = stringf("%s \"%s\"", viewer_exe.c_str(), out_file.c_str()); | ||||
| 				#else | ||||
| 					std::string cmd = stringf("%s '%s' %s", viewer_exe.c_str(), out_file.c_str(), background.c_str()); | ||||
| 				#endif | ||||
| 				log("Exec: %s\n", cmd.c_str()); | ||||
| 				if (run_command(cmd) != 0) | ||||
| 					log_cmd_error("Shell command failed!\n"); | ||||
| 			} else | ||||
| 			if (format.empty()) { | ||||
| 				#ifdef __APPLE__ | ||||
| 				std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' %s", getuid(), dot_file.c_str(), dot_file.c_str(), background.c_str()); | ||||
| 				#else | ||||
| 				std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' %s", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), background.c_str()); | ||||
| 				#endif | ||||
| 				log("Exec: %s\n", cmd.c_str()); | ||||
| 				if (run_command(cmd) != 0) | ||||
| 					log_cmd_error("Shell command failed!\n"); | ||||
| 			} | ||||
| 		} | ||||
| 		#endif | ||||
| 
 | ||||
|  |  | |||
|  | @ -292,10 +292,12 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos | |||
| 	if (!purge_mode) | ||||
| 		for (auto &it : module->cells_) { | ||||
| 			RTLIL::Cell *cell = it.second; | ||||
| 			if (ct_reg.cell_known(cell->type)) | ||||
| 			if (ct_reg.cell_known(cell->type)) { | ||||
| 				bool clk2fflogic = cell->get_bool_attribute(ID(clk2fflogic)); | ||||
| 				for (auto &it2 : cell->connections()) | ||||
| 					if (ct_reg.cell_output(cell->type, it2.first)) | ||||
| 					if (clk2fflogic ? it2.first == ID::D : ct_reg.cell_output(cell->type, it2.first)) | ||||
| 						register_signals.add(it2.second); | ||||
| 			} | ||||
| 			for (auto &it2 : cell->connections()) | ||||
| 				connected_signals.add(it2.second); | ||||
| 		} | ||||
|  |  | |||
|  | @ -91,7 +91,7 @@ struct PruneWorker | |||
| 			if (GetSize(new_lhs) == 0) { | ||||
| 				if (GetSize(conn_lhs) == 0) | ||||
| 					removed_count++; | ||||
| 				cs->actions.erase((it++).base() - 1); | ||||
| 				it = decltype(cs->actions)::reverse_iterator(cs->actions.erase(it.base() - 1)); | ||||
| 			} else { | ||||
| 				it->first = new_lhs; | ||||
| 				it->second = new_rhs; | ||||
|  |  | |||
|  | @ -80,15 +80,27 @@ struct Clk2fflogicPass : public Pass { | |||
| 		return module->Eqx(NEW_ID, {sampled_sig, sig}, polarity ? SigSpec {State::S0, State::S1} : SigSpec {State::S1, State::S0}); | ||||
| 	} | ||||
| 	// Sampled and current value of a data signal.
 | ||||
| 	SampledSig sample_data(Module *module, SigSpec sig, RTLIL::Const init, bool is_fine) { | ||||
| 	SampledSig sample_data(Module *module, SigSpec sig, RTLIL::Const init, bool is_fine, bool set_attribute = false) { | ||||
| 		std::string sig_str = log_signal(sig); | ||||
| 		sig_str.erase(std::remove(sig_str.begin(), sig_str.end(), ' '), sig_str.end()); | ||||
| 
 | ||||
| 
 | ||||
| 		Wire *sampled_sig = module->addWire(NEW_ID_SUFFIX(stringf("%s#sampled", sig_str.c_str())), GetSize(sig)); | ||||
| 		sampled_sig->attributes[ID::init] = init; | ||||
| 
 | ||||
| 		Cell *cell; | ||||
| 		if (is_fine) | ||||
| 			module->addFfGate(NEW_ID, sig, sampled_sig); | ||||
| 			cell = module->addFfGate(NEW_ID, sig, sampled_sig); | ||||
| 		else | ||||
| 			module->addFf(NEW_ID, sig, sampled_sig); | ||||
| 			cell = module->addFf(NEW_ID, sig, sampled_sig); | ||||
| 
 | ||||
| 		if (set_attribute) { | ||||
| 			for (auto &chunk : sig.chunks()) | ||||
| 				if (chunk.wire != nullptr) | ||||
| 					chunk.wire->set_bool_attribute(ID::keep); | ||||
| 			cell->set_bool_attribute(ID(clk2fflogic)); | ||||
| 		} | ||||
| 
 | ||||
| 		return {sampled_sig, sig}; | ||||
| 	} | ||||
| 	SigSpec mux(Module *module, SigSpec a, SigSpec b, SigSpec s, bool is_fine) { | ||||
|  | @ -213,7 +225,7 @@ struct Clk2fflogicPass : public Pass { | |||
| 					if (ff.has_clk) | ||||
| 						ff.unmap_ce_srst(); | ||||
| 
 | ||||
| 					auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine).sampled; | ||||
| 					auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine, true).sampled; | ||||
| 
 | ||||
| 					if (ff.has_clk) { | ||||
| 						// The init value for the sampled d is never used, so we can set it to fixed zero, reducing uninit'd FFs
 | ||||
|  |  | |||
|  | @ -140,6 +140,7 @@ struct SimInstance | |||
| 	dict<SigBit, pool<Wire*>> upd_outports; | ||||
| 
 | ||||
| 	dict<SigBit, SigBit> in_parent_drivers; | ||||
| 	dict<SigBit, SigBit> clk2fflogic_drivers; | ||||
| 
 | ||||
| 	pool<SigBit> dirty_bits; | ||||
| 	pool<Cell*> dirty_cells; | ||||
|  | @ -270,6 +271,11 @@ struct SimInstance | |||
| 				ff.past_srst = State::Sx; | ||||
| 				ff.data = ff_data; | ||||
| 				ff_database[cell] = ff; | ||||
| 
 | ||||
| 				if (cell->get_bool_attribute(ID(clk2fflogic))) { | ||||
| 					for (int i = 0; i < ff_data.width; i++) | ||||
| 						clk2fflogic_drivers.emplace(sigmap(ff_data.sig_d[i]), sigmap(ff_data.sig_q[i])); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if (cell->is_mem_cell()) | ||||
|  | @ -389,6 +395,10 @@ struct SimInstance | |||
| 			auto sigbit = sig[i]; | ||||
| 			auto sigval = value[i]; | ||||
| 
 | ||||
| 			auto clk2fflogic_driver = clk2fflogic_drivers.find(sigbit); | ||||
| 			if (clk2fflogic_driver != clk2fflogic_drivers.end()) | ||||
| 				sigbit = clk2fflogic_driver->second; | ||||
| 
 | ||||
| 			auto in_parent_driver = in_parent_drivers.find(sigbit); | ||||
| 			if (in_parent_driver == in_parent_drivers.end()) | ||||
| 				set_state(sigbit, sigval); | ||||
|  | @ -589,7 +599,7 @@ struct SimInstance | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	bool update_ph2(bool gclk) | ||||
| 	bool update_ph2(bool gclk, bool stable_past_update = false) | ||||
| 	{ | ||||
| 		bool did_something = false; | ||||
| 
 | ||||
|  | @ -600,7 +610,7 @@ struct SimInstance | |||
| 
 | ||||
| 			Const current_q = get_state(ff.data.sig_q); | ||||
| 
 | ||||
| 			if (ff_data.has_clk) { | ||||
| 			if (ff_data.has_clk && !stable_past_update) { | ||||
| 				// flip-flops
 | ||||
| 				State current_clk = get_state(ff_data.sig_clk)[0]; | ||||
| 				if (ff_data.pol_clk ? (ff.past_clk == State::S0 && current_clk != State::S0) : | ||||
|  | @ -621,7 +631,7 @@ struct SimInstance | |||
| 			if (ff_data.has_aload) { | ||||
| 				State current_aload = get_state(ff_data.sig_aload)[0]; | ||||
| 				if (current_aload == (ff_data.pol_aload ? State::S1 : State::S0)) { | ||||
| 					current_q = ff_data.has_clk ? ff.past_ad : get_state(ff.data.sig_ad); | ||||
| 					current_q = ff_data.has_clk && !stable_past_update ? ff.past_ad : get_state(ff.data.sig_ad); | ||||
| 				} | ||||
| 			} | ||||
| 			// async reset
 | ||||
|  | @ -672,6 +682,8 @@ struct SimInstance | |||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					if (stable_past_update) | ||||
| 						continue; | ||||
| 					if (port.clk_polarity ? | ||||
| 							(mdb.past_wr_clk[port_idx] == State::S1 || get_state(port.clk) != State::S1) : | ||||
| 							(mdb.past_wr_clk[port_idx] == State::S0 || get_state(port.clk) != State::S0)) | ||||
|  | @ -701,7 +713,7 @@ struct SimInstance | |||
| 		} | ||||
| 
 | ||||
| 		for (auto it : children) | ||||
| 			if (it.second->update_ph2(gclk)) { | ||||
| 			if (it.second->update_ph2(gclk, stable_past_update)) { | ||||
| 				dirty_children.insert(it.second); | ||||
| 				did_something = true; | ||||
| 			} | ||||
|  | @ -1197,9 +1209,21 @@ struct SimWorker : SimShared | |||
| 
 | ||||
| 	void initialize_stable_past() | ||||
| 	{ | ||||
| 		if (debug) | ||||
| 			log("\n-- ph1 (initialize) --\n"); | ||||
| 		top->update_ph1(); | ||||
| 
 | ||||
| 		while (1) | ||||
| 		{ | ||||
| 			if (debug) | ||||
| 				log("\n-- ph1 (initialize) --\n"); | ||||
| 
 | ||||
| 			top->update_ph1(); | ||||
| 
 | ||||
| 			if (debug) | ||||
| 				log("\n-- ph2 (initialize) --\n"); | ||||
| 
 | ||||
| 			if (!top->update_ph2(false, true)) | ||||
| 				break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (debug) | ||||
| 			log("\n-- ph3 (initialize) --\n"); | ||||
| 		top->update_ph3(true); | ||||
|  |  | |||
|  | @ -131,70 +131,3 @@ module CC_USR_RSTN ( | |||
| 	output USR_RSTN | ||||
| ); | ||||
| endmodule | ||||
| 
 | ||||
| (* blackbox *) | ||||
| module CC_FIFO_40K ( | ||||
| 	output A_ECC_1B_ERR, | ||||
| 	output B_ECC_1B_ERR, | ||||
| 	output A_ECC_2B_ERR, | ||||
| 	output B_ECC_2B_ERR, | ||||
| 	// FIFO pop port
 | ||||
| 	output [39:0] A_DO, | ||||
| 	output [39:0] B_DO, | ||||
| 	(* clkbuf_sink *) | ||||
| 	input  A_CLK, | ||||
| 	input  A_EN, | ||||
| 	// FIFO push port
 | ||||
| 	input  [39:0] A_DI, | ||||
| 	input  [39:0] B_DI, | ||||
| 	input  [39:0] A_BM, | ||||
| 	input  [39:0] B_BM, | ||||
| 	(* clkbuf_sink *) | ||||
| 	input  B_CLK, | ||||
| 	input  B_EN, | ||||
| 	input  B_WE, | ||||
| 	// FIFO control
 | ||||
| 	input  F_RST_N, | ||||
| 	input  [12:0] F_ALMOST_FULL_OFFSET, | ||||
| 	input  [12:0] F_ALMOST_EMPTY_OFFSET, | ||||
| 	// FIFO status signals
 | ||||
| 	output F_FULL, | ||||
| 	output F_EMPTY, | ||||
| 	output F_ALMOST_FULL, | ||||
| 	output F_ALMOST_EMPTY, | ||||
| 	output F_RD_ERROR, | ||||
| 	output F_WR_ERROR, | ||||
| 	output [15:0] F_RD_PTR, | ||||
| 	output [15:0] F_WR_PTR | ||||
| ); | ||||
| 	// Location format: D(0..N-1)X(0..3)Y(0..7) or UNPLACED
 | ||||
| 	parameter LOC = "UNPLACED"; | ||||
| 
 | ||||
| 	// Offset configuration
 | ||||
| 	parameter [12:0] ALMOST_FULL_OFFSET = 12'b0; | ||||
| 	parameter [12:0] ALMOST_EMPTY_OFFSET = 12'b0; | ||||
| 
 | ||||
| 	// Port Widths
 | ||||
| 	parameter A_WIDTH = 0; | ||||
| 	parameter B_WIDTH = 0; | ||||
| 
 | ||||
| 	// RAM and Write Modes
 | ||||
| 	parameter RAM_MODE = "SDP"; // "TPD" or "SDP"
 | ||||
| 	parameter FIFO_MODE = "SYNC"; // "ASYNC" or "SYNC"
 | ||||
| 
 | ||||
| 	// Inverting Control Pins
 | ||||
| 	parameter A_CLK_INV = 1'b0; | ||||
| 	parameter B_CLK_INV = 1'b0; | ||||
| 	parameter A_EN_INV = 1'b0; | ||||
| 	parameter B_EN_INV = 1'b0; | ||||
| 	parameter A_WE_INV = 1'b0; | ||||
| 	parameter B_WE_INV = 1'b0; | ||||
| 
 | ||||
| 	// Output Register
 | ||||
| 	parameter A_DO_REG = 1'b0; | ||||
| 	parameter B_DO_REG = 1'b0; | ||||
| 
 | ||||
| 	// Error Checking and Correction
 | ||||
| 	parameter A_ECC_EN  = 1'b0; | ||||
| 	parameter B_ECC_EN  = 1'b0; | ||||
| endmodule | ||||
|  |  | |||
|  | @ -733,13 +733,12 @@ module CC_BRAM_20K ( | |||
| 			// SDP read port
 | ||||
| 			always @(posedge clkb) | ||||
| 			begin | ||||
| 				// "NO_CHANGE" only
 | ||||
| 				for (k=0; k < B_RD_WIDTH; k=k+1) begin | ||||
| 					if (k < 20) begin | ||||
| 						if (enb && !wea) A_DO_out[k] <= memory[addrb+k]; | ||||
| 						if (enb) A_DO_out[k] <= memory[addrb+k]; | ||||
| 					end | ||||
| 					else begin // use both ports
 | ||||
| 						if (enb && !wea) B_DO_out[k-20] <= memory[addrb+k]; | ||||
| 						if (enb) B_DO_out[k-20] <= memory[addrb+k]; | ||||
| 					end | ||||
| 				end | ||||
| 			end | ||||
|  | @ -1274,13 +1273,12 @@ module CC_BRAM_40K ( | |||
| 			// SDP read port
 | ||||
| 			always @(posedge clkb) | ||||
| 			begin | ||||
| 				// "NO_CHANGE" only
 | ||||
| 				for (k=0; k < B_RD_WIDTH; k=k+1) begin | ||||
| 					if (k < 40) begin | ||||
| 						if (enb && !wea) A_DO_out[k] <= memory[addrb+k]; | ||||
| 						if (enb) A_DO_out[k] <= memory[addrb+k]; | ||||
| 					end | ||||
| 					else begin // use both ports
 | ||||
| 						if (enb && !wea) B_DO_out[k-40] <= memory[addrb+k]; | ||||
| 						if (enb) B_DO_out[k-40] <= memory[addrb+k]; | ||||
| 					end | ||||
| 				end | ||||
| 			end | ||||
|  | @ -1412,6 +1410,393 @@ module CC_BRAM_40K ( | |||
| 	endgenerate | ||||
| endmodule | ||||
| 
 | ||||
| module CC_FIFO_40K ( | ||||
| 	output A_ECC_1B_ERR, | ||||
| 	output B_ECC_1B_ERR, | ||||
| 	output A_ECC_2B_ERR, | ||||
| 	output B_ECC_2B_ERR, | ||||
| 	// FIFO pop port
 | ||||
| 	output [39:0] A_DO, | ||||
| 	output [39:0] B_DO, | ||||
| 	(* clkbuf_sink *) | ||||
| 	input  A_CLK, | ||||
| 	input  A_EN, | ||||
| 	// FIFO push port
 | ||||
| 	input  [39:0] A_DI, | ||||
| 	input  [39:0] B_DI, | ||||
| 	input  [39:0] A_BM, | ||||
| 	input  [39:0] B_BM, | ||||
| 	(* clkbuf_sink *) | ||||
| 	input  B_CLK, | ||||
| 	input  B_EN, | ||||
| 	input  B_WE, | ||||
| 	// FIFO control
 | ||||
| 	input  F_RST_N, | ||||
| 	input  [14:0] F_ALMOST_FULL_OFFSET, | ||||
| 	input  [14:0] F_ALMOST_EMPTY_OFFSET, | ||||
| 	// FIFO status signals
 | ||||
| 	output F_FULL, | ||||
| 	output F_EMPTY, | ||||
| 	output F_ALMOST_FULL, | ||||
| 	output F_ALMOST_EMPTY, | ||||
| 	output F_RD_ERROR, | ||||
| 	output F_WR_ERROR, | ||||
| 	output [15:0] F_RD_PTR, | ||||
| 	output [15:0] F_WR_PTR | ||||
| ); | ||||
| 	// Location format: D(0..N-1)X(0..3)Y(0..7) or UNPLACED
 | ||||
| 	parameter LOC = "UNPLACED"; | ||||
| 
 | ||||
| 	// Offset configuration
 | ||||
| 	parameter DYN_STAT_SELECT = 1'b0; | ||||
| 	parameter [14:0] ALMOST_FULL_OFFSET = 15'b0; | ||||
| 	parameter [14:0] ALMOST_EMPTY_OFFSET = 15'b0; | ||||
| 
 | ||||
| 	// Port Widths
 | ||||
| 	parameter A_WIDTH = 0; | ||||
| 	parameter B_WIDTH = 0; | ||||
| 
 | ||||
| 	// RAM and Write Modes
 | ||||
| 	parameter RAM_MODE = "TDP"; // "TDP" or "SDP"
 | ||||
| 	parameter FIFO_MODE = "SYNC"; // "ASYNC" or "SYNC"
 | ||||
| 
 | ||||
| 	// Inverting Control Pins
 | ||||
| 	parameter A_CLK_INV = 1'b0; | ||||
| 	parameter B_CLK_INV = 1'b0; | ||||
| 	parameter A_EN_INV = 1'b0; | ||||
| 	parameter B_EN_INV = 1'b0; | ||||
| 	parameter A_WE_INV = 1'b0; | ||||
| 	parameter B_WE_INV = 1'b0; | ||||
| 
 | ||||
| 	// Output Register
 | ||||
| 	parameter A_DO_REG = 1'b0; | ||||
| 	parameter B_DO_REG = 1'b0; | ||||
| 
 | ||||
| 	// Error Checking and Correction
 | ||||
| 	parameter A_ECC_EN  = 1'b0; | ||||
| 	parameter B_ECC_EN  = 1'b0; | ||||
| 
 | ||||
| 	integer i, k; | ||||
| 
 | ||||
| 	// 512 x 80 bit
 | ||||
| 	reg [40959:0] memory = 40960'b0; | ||||
| 
 | ||||
| 	reg [15:0] counter_max; | ||||
| 	reg [15:0] sram_depth; | ||||
| 	localparam tp = (A_WIDTH ==  1) ? 15 : | ||||
| 					(A_WIDTH ==  2) ? 14 : | ||||
| 					(A_WIDTH ==  5) ? 13 : | ||||
| 					(A_WIDTH == 10) ? 12 : | ||||
| 					(A_WIDTH == 20) ? 11 : | ||||
| 					(A_WIDTH == 40) ? 10 : 9; | ||||
| 
 | ||||
| 	initial begin | ||||
| 		// Check parameters
 | ||||
| 		if ((RAM_MODE != "SDP") && (RAM_MODE != "TDP")) begin | ||||
| 			$display("ERROR: Illegal RAM MODE %d.", RAM_MODE); | ||||
| 			$finish(); | ||||
| 		end | ||||
| 		if ((FIFO_MODE != "ASYNC") && (FIFO_MODE != "SYNC")) begin | ||||
| 			$display("ERROR: Illegal FIFO MODE %d.", FIFO_MODE); | ||||
| 			$finish(); | ||||
| 		end | ||||
| 		if ((RAM_MODE == "SDP") && (DYN_STAT_SELECT == 1)) begin | ||||
| 			$display("ERROR: Dynamic offset configuration is not supported in %s mode.", RAM_MODE); | ||||
| 			$finish(); | ||||
| 		end | ||||
| 		if ((RAM_MODE == "SDP") && ((A_WIDTH != 80) || (B_WIDTH != 80))) begin | ||||
| 			$display("ERROR: SDP is ony supported in 80 bit mode."); | ||||
| 			$finish(); | ||||
| 		end | ||||
| 		if ((A_WIDTH == 80) && (RAM_MODE == "TDP")) begin | ||||
| 			$display("ERROR: Port A width of 80 bits is only supported in SDP mode."); | ||||
| 			$finish(); | ||||
| 		end | ||||
| 		if ((B_WIDTH == 80) && (RAM_MODE == "TDP")) begin | ||||
| 			$display("ERROR: Port B width of 80 bits is only supported in SDP mode."); | ||||
| 			$finish(); | ||||
| 		end | ||||
| 		if ((A_WIDTH != 80) && (A_WIDTH != 40) && (A_WIDTH != 20) && (A_WIDTH != 10) && | ||||
| 			(A_WIDTH != 5)  && (A_WIDTH != 2)  && (A_WIDTH != 1) && (A_WIDTH != 0)) begin | ||||
| 			$display("ERROR: Illegal %s Port A width configuration %d.", RAM_MODE, A_WIDTH); | ||||
| 			$finish(); | ||||
| 		end | ||||
| 		if ((B_WIDTH != 80) && (B_WIDTH != 40) && (B_WIDTH != 20) && (B_WIDTH != 10) && | ||||
| 			(B_WIDTH != 5)  && (B_WIDTH != 2)  && (B_WIDTH != 1) && (B_WIDTH != 0)) begin | ||||
| 			$display("ERROR: Illegal %s Port B width configuration %d.", RAM_MODE, B_WIDTH); | ||||
| 			$finish(); | ||||
| 		end | ||||
| 		if (A_WIDTH != B_WIDTH) begin | ||||
| 			$display("ERROR: The values of A_WIDTH and B_WIDTH must be equal."); | ||||
| 		end | ||||
| 		if ((A_ECC_EN == 1'b1) && (RAM_MODE != "SDP") && (A_WIDTH != 40)) begin | ||||
| 			$display("ERROR: Illegal ECC Port A configuration. ECC mode requires TDP >=40 bit or SDP 80 bit, but is %s %d.", RAM_MODE, A_WIDTH); | ||||
| 			$finish(); | ||||
| 		end | ||||
| 		// Set local parameters
 | ||||
| 		if (A_WIDTH == 1) begin // A_WIDTH=B_WIDTH
 | ||||
| 			counter_max = 2 * 32*1024 - 1; | ||||
| 			sram_depth  =     32*1024; | ||||
| 		end | ||||
| 		else if (A_WIDTH == 2) begin | ||||
| 			counter_max = 2 * 16*1024 - 1; | ||||
| 			sram_depth  =     16*1024; | ||||
| 		end | ||||
| 		else if (A_WIDTH == 5) begin | ||||
| 			counter_max = 2 * 8*1024 - 1; | ||||
| 			sram_depth  =     8*1024; | ||||
| 		end | ||||
| 		else if (A_WIDTH == 10) begin | ||||
| 			counter_max = 2 * 4*1024 - 1; | ||||
| 			sram_depth  =     4*1024; | ||||
| 		end | ||||
| 		else if (A_WIDTH == 20) begin | ||||
| 			counter_max = 2 * 2*1024 - 1; | ||||
| 			sram_depth  =     2*1024; | ||||
| 		end | ||||
| 		else if (A_WIDTH == 40) begin | ||||
| 			counter_max = 2 * 1*1024 - 1; | ||||
| 			sram_depth  =     1*1024; | ||||
| 		end | ||||
| 		else begin // 80 bit SDP
 | ||||
| 			counter_max = 2 * 512 - 1; | ||||
| 			sram_depth  =     512; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	// Internal signals
 | ||||
| 	wire fifo_rdclk = A_CLK ^ A_CLK_INV; | ||||
| 	wire fifo_wrclk = (FIFO_MODE == "ASYNC") ? (B_CLK ^ B_CLK_INV) : (A_CLK ^ A_CLK_INV); | ||||
| 	wire [15:0] almost_full_offset  = DYN_STAT_SELECT ? F_ALMOST_FULL_OFFSET  : ALMOST_FULL_OFFSET; | ||||
| 	wire [15:0] almost_empty_offset = DYN_STAT_SELECT ? F_ALMOST_EMPTY_OFFSET : ALMOST_EMPTY_OFFSET; | ||||
| 	reg  [39:0] A_DO_out = 0, A_DO_reg = 0; | ||||
| 	reg  [39:0] B_DO_out = 0, B_DO_reg = 0; | ||||
| 
 | ||||
| 	// Status signals
 | ||||
| 	reg fifo_full; | ||||
| 	reg fifo_empty; | ||||
| 	reg fifo_almost_full; | ||||
| 	reg fifo_almost_empty; | ||||
| 	assign F_FULL         = fifo_full; | ||||
| 	assign F_EMPTY        = fifo_empty; | ||||
| 	assign F_ALMOST_FULL  = fifo_almost_full; | ||||
| 	assign F_ALMOST_EMPTY = fifo_almost_empty; | ||||
| 	assign F_WR_ERROR     = (F_FULL && (B_EN ^ B_EN_INV) && (B_WE ^ B_WE_INV)); | ||||
| 	assign F_RD_ERROR     = (F_EMPTY && (A_EN ^ A_EN_INV)); | ||||
| 	wire ram_we = (~F_FULL  && (B_EN ^ B_EN_INV) && (B_WE ^ B_WE_INV)); | ||||
| 	wire ram_en = (~F_EMPTY && (A_EN ^ A_EN_INV)); | ||||
| 
 | ||||
| 	// Reset synchronizers
 | ||||
| 	reg  [1:0] aclk_reset_q, bclk_reset_q; | ||||
| 	wire fifo_sync_rstn    = aclk_reset_q; | ||||
| 	wire fifo_async_wrrstn = bclk_reset_q; | ||||
| 	wire fifo_async_rdrstn = aclk_reset_q; | ||||
| 
 | ||||
| 	always @(posedge fifo_rdclk or negedge F_RST_N) | ||||
| 	begin | ||||
| 		if (F_RST_N == 1'b0) begin | ||||
| 			aclk_reset_q <= 2'b0; | ||||
| 		end | ||||
| 		else begin | ||||
| 			aclk_reset_q[1] <= aclk_reset_q[0]; | ||||
| 			aclk_reset_q[0] <= 1'b1; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	always @(posedge fifo_wrclk or negedge F_RST_N) | ||||
| 	begin | ||||
| 		if (F_RST_N == 1'b0) begin | ||||
| 			bclk_reset_q <= 2'b0; | ||||
| 		end | ||||
| 		else begin | ||||
| 			bclk_reset_q[1] <= bclk_reset_q[0]; | ||||
| 			bclk_reset_q[0] <= 1'b1; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	// Push/pop pointers
 | ||||
| 	reg  [15:0] rd_pointer, rd_pointer_int; | ||||
| 	reg  [15:0] wr_pointer, wr_pointer_int; | ||||
| 	reg  [15:0] rd_pointer_cmp, wr_pointer_cmp; | ||||
| 	wire [15:0] rd_pointer_nxt; | ||||
| 	wire [15:0] wr_pointer_nxt; | ||||
| 	reg  [15:0] fifo_rdaddr, rdaddr; | ||||
| 	reg  [15:0] fifo_wraddr, wraddr; | ||||
| 	assign F_RD_PTR = fifo_rdaddr; | ||||
| 	assign F_WR_PTR = fifo_wraddr; | ||||
| 
 | ||||
| 	always @(posedge fifo_rdclk or negedge F_RST_N) | ||||
| 	begin | ||||
| 		if (F_RST_N == 1'b0) begin | ||||
| 			rd_pointer <= 0; | ||||
| 			rd_pointer_int <= 0; | ||||
| 		end | ||||
| 		else if (ram_en) begin | ||||
| 			rd_pointer <= rd_pointer_nxt; | ||||
| 			rd_pointer_int <= rd_pointer_nxt[15:1] ^ rd_pointer_nxt[14:0]; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	assign rd_pointer_nxt = (rd_pointer == counter_max) ? (0) : (rd_pointer + 1'b1); | ||||
| 
 | ||||
| 	always @(posedge fifo_wrclk or negedge F_RST_N) | ||||
| 	begin | ||||
| 		if (F_RST_N == 1'b0) begin | ||||
| 			wr_pointer <= 0; | ||||
| 			wr_pointer_int <= 0; | ||||
| 		end | ||||
| 		else if (ram_we) begin | ||||
| 			wr_pointer <= wr_pointer_nxt; | ||||
| 			wr_pointer_int <= wr_pointer_nxt[15:1] ^ wr_pointer_nxt[14:0]; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	assign wr_pointer_nxt = (wr_pointer == counter_max) ? (0) : (wr_pointer + 1'b1); | ||||
| 
 | ||||
| 	// Address synchronizers
 | ||||
| 	reg [15:0] rd_pointer_sync, wr_pointer_sync; | ||||
| 	reg [15:0] rd_pointer_sync_0, rd_pointer_sync_1; | ||||
| 	reg [15:0] wr_pointer_sync_0, wr_pointer_sync_1; | ||||
| 
 | ||||
| 	always @(posedge fifo_rdclk or negedge F_RST_N) | ||||
| 	begin | ||||
| 		if (F_RST_N == 1'b0) begin | ||||
| 			wr_pointer_sync_0 <= 0; | ||||
| 			wr_pointer_sync_1 <= 0; | ||||
| 		end | ||||
| 		else begin | ||||
| 			wr_pointer_sync_0 <= wraddr; | ||||
| 			wr_pointer_sync_1 <= wr_pointer_sync_0; | ||||
| 		end | ||||
| 	 end | ||||
| 
 | ||||
| 	always @(posedge fifo_wrclk or negedge F_RST_N) | ||||
| 	 begin | ||||
| 		if (F_RST_N == 1'b0) begin | ||||
| 			rd_pointer_sync_0 <= 0; | ||||
| 			rd_pointer_sync_1 <= 0; | ||||
| 		end | ||||
| 		else begin | ||||
| 			rd_pointer_sync_0 <= rdaddr; | ||||
| 			rd_pointer_sync_1 <= rd_pointer_sync_0; | ||||
| 		end | ||||
| 	 end | ||||
| 
 | ||||
| 	always @(*) begin | ||||
| 		fifo_wraddr = {wr_pointer[tp-1:0], {(15-tp){1'b0}}}; | ||||
| 		fifo_rdaddr = {rd_pointer[tp-1:0], {(15-tp){1'b0}}}; | ||||
| 
 | ||||
| 		rdaddr = {rd_pointer[tp], rd_pointer_int[tp-1:0]}; | ||||
| 		wraddr = {{(15-tp){1'b0}}, wr_pointer[tp], wr_pointer_int[tp:0]}; | ||||
| 
 | ||||
| 		if (FIFO_MODE == "ASYNC") | ||||
| 			fifo_full = (wraddr[tp-2:0] == rd_pointer_sync_1[tp-2:0] ) && (wraddr[tp] != rd_pointer_sync_1[tp] ) && ( wraddr[tp-1] != rd_pointer_sync_1[tp-1] ); | ||||
| 		else | ||||
| 			fifo_full = (wr_pointer[tp-1:0] == rd_pointer[tp-1:0]) && (wr_pointer[tp] ^ rd_pointer[tp]); | ||||
| 
 | ||||
| 		if (FIFO_MODE == "ASYNC") | ||||
| 			fifo_empty = (wr_pointer_sync_1[tp:0] == rdaddr[tp:0]); | ||||
| 		else | ||||
| 			fifo_empty = (wr_pointer[tp:0] == rd_pointer[tp:0]); | ||||
| 
 | ||||
| 		rd_pointer_cmp = (FIFO_MODE == "ASYNC") ? rd_pointer_sync : rd_pointer; | ||||
| 		if (wr_pointer[tp] == rd_pointer_cmp[tp]) | ||||
| 			fifo_almost_full = ((wr_pointer[tp-1:0] - rd_pointer_cmp[tp-1:0]) >= (sram_depth - almost_full_offset)); | ||||
| 		else | ||||
| 			fifo_almost_full = ((rd_pointer_cmp[tp-1:0] - wr_pointer[tp-1:0]) <= almost_full_offset); | ||||
| 
 | ||||
| 		wr_pointer_cmp = (FIFO_MODE == "ASYNC") ? wr_pointer_sync : wr_pointer; | ||||
| 		if (wr_pointer_cmp[tp] == rd_pointer[tp]) | ||||
| 			fifo_almost_empty = ((wr_pointer_cmp[tp-1:0] - rd_pointer[tp-1:0]) <= almost_empty_offset); | ||||
| 		else | ||||
| 			fifo_almost_empty = ((rd_pointer[tp-1:0] - wr_pointer_cmp[tp-1:0]) >= (sram_depth - almost_empty_offset)); | ||||
| 	end | ||||
| 
 | ||||
| 	generate | ||||
| 		always @(*) begin | ||||
| 			wr_pointer_sync = 0; | ||||
| 			rd_pointer_sync = 0; | ||||
| 			for (i=tp; i >= 0; i=i-1) begin | ||||
| 				if (i == tp) begin | ||||
| 					wr_pointer_sync[i] = wr_pointer_sync_1[i]; | ||||
| 					rd_pointer_sync[i] = rd_pointer_sync_1[i]; | ||||
| 				end | ||||
| 				else begin | ||||
| 					wr_pointer_sync[i] = wr_pointer_sync_1[i] ^ wr_pointer_sync[i+1]; | ||||
| 					rd_pointer_sync[i] = rd_pointer_sync_1[i] ^ rd_pointer_sync[i+1]; | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 		if (RAM_MODE == "SDP") begin | ||||
| 			// SDP push ports A+B
 | ||||
| 			always @(posedge fifo_wrclk) | ||||
| 			begin | ||||
| 				for (k=0; k < A_WIDTH; k=k+1) begin | ||||
| 					if (k < 40) begin | ||||
| 						if (ram_we && A_BM[k]) memory[fifo_wraddr+k] <= A_DI[k]; | ||||
| 					end | ||||
| 					else begin // use both ports
 | ||||
| 						if (ram_we && B_BM[k-40]) memory[fifo_wraddr+k] <= B_DI[k-40]; | ||||
| 					end | ||||
| 				end | ||||
| 			end | ||||
| 			// SDP pop ports A+B
 | ||||
| 			always @(posedge fifo_rdclk) | ||||
| 			begin | ||||
| 				for (k=0; k < B_WIDTH; k=k+1) begin | ||||
| 					if (k < 40) begin | ||||
| 						if (ram_en) A_DO_out[k] <= memory[fifo_rdaddr+k]; | ||||
| 					end | ||||
| 					else begin // use both ports
 | ||||
| 						if (ram_en) B_DO_out[k-40] <= memory[fifo_rdaddr+k]; | ||||
| 					end | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 		else if (RAM_MODE == "TDP") begin | ||||
| 			// TDP pop port A
 | ||||
| 			always @(posedge fifo_rdclk) | ||||
| 			begin | ||||
| 				for (i=0; i < A_WIDTH; i=i+1) begin | ||||
| 					if (ram_en) begin | ||||
| 						A_DO_out[i] <= memory[fifo_rdaddr+i]; | ||||
| 					end | ||||
| 				end | ||||
| 			end | ||||
| 			// TDP push port B
 | ||||
| 			always @(posedge fifo_wrclk) | ||||
| 			begin | ||||
| 				for (i=0; i < B_WIDTH; i=i+1) begin | ||||
| 					if (ram_we && B_BM[i]) | ||||
| 						memory[fifo_wraddr+i] <= B_DI[i]; | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 	endgenerate | ||||
| 
 | ||||
| 	// Optional output register
 | ||||
| 	generate | ||||
| 		if (A_DO_REG) begin | ||||
| 			always @(posedge fifo_rdclk) begin | ||||
| 				A_DO_reg <= A_DO_out; | ||||
| 			end | ||||
| 			assign A_DO = A_DO_reg; | ||||
| 		end | ||||
| 		else begin | ||||
| 			assign A_DO = A_DO_out; | ||||
| 		end | ||||
| 		if (B_DO_REG) begin | ||||
| 			always @(posedge fifo_rdclk) begin | ||||
| 				B_DO_reg <= B_DO_out; | ||||
| 			end | ||||
| 			assign B_DO = B_DO_reg; | ||||
| 		end | ||||
| 		else begin | ||||
| 			assign B_DO = B_DO_out; | ||||
| 		end | ||||
| 	endgenerate | ||||
| endmodule | ||||
| 
 | ||||
| // Models of the LUT2 tree primitives
 | ||||
| module CC_L2T4( | ||||
| 	output O, | ||||
|  |  | |||
|  | @ -26,10 +26,12 @@ proc | |||
| equiv_opt -assert -map +/anlogic/cells_sim.v synth_anlogic # equivalency check | ||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||
| cd mux8 # Constrain all select calls below inside the top module | ||||
| select -assert-count 3 t:AL_MAP_LUT4 | ||||
| select -assert-count 1 t:AL_MAP_LUT6 | ||||
| select -assert-max 3 t:AL_MAP_LUT3 | ||||
| select -assert-max 3 t:AL_MAP_LUT4 | ||||
| select -assert-max 1 t:AL_MAP_LUT5 | ||||
| select -assert-max 1 t:AL_MAP_LUT6 | ||||
| 
 | ||||
| select -assert-none t:AL_MAP_LUT4 t:AL_MAP_LUT6 %% t:* %D | ||||
| select -assert-none t:AL_MAP_LUT3 t:AL_MAP_LUT4 t:AL_MAP_LUT5 t:AL_MAP_LUT6 %% t:* %D | ||||
| 
 | ||||
| design -load read | ||||
| hierarchy -top mux16 | ||||
|  |  | |||
|  | @ -28,8 +28,8 @@ equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check | |||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||
| cd mux8 # Constrain all select calls below inside the top module | ||||
| select -assert-max 1 t:L6MUX21 | ||||
| select -assert-max 7 t:LUT4 | ||||
| select -assert-max 2 t:PFUMX | ||||
| select -assert-max 8 t:LUT4 | ||||
| select -assert-max 3 t:PFUMX | ||||
| 
 | ||||
| select -assert-none t:LUT4 t:L6MUX21 t:PFUMX %% t:* %D | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,6 +15,6 @@ cd fsm # Constrain all select calls below inside the top module | |||
| select -assert-count 1 t:CC_BUFG | ||||
| select -assert-count 6 t:CC_DFF | ||||
| select -assert-max 5 t:CC_LUT2 | ||||
| select -assert-max 4 t:CC_LUT3 | ||||
| select -assert-max 6 t:CC_LUT3 | ||||
| select -assert-max 9 t:CC_LUT4 | ||||
| select -assert-none t:CC_BUFG t:CC_DFF t:CC_LUT2 t:CC_LUT3 t:CC_LUT4 %% t:* %D | ||||
|  |  | |||
|  | @ -4,5 +4,5 @@ flatten | |||
| equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check | ||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||
| cd top # Constrain all select calls below inside the top module | ||||
| select -assert-count 5 t:SB_LUT4 | ||||
| select -assert-max 6 t:SB_LUT4 | ||||
| select -assert-none t:SB_LUT4 %% t:* %D | ||||
|  |  | |||
|  | @ -69,7 +69,7 @@ proc | |||
| equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cyclonev -noiopad -noclkbuf # equivalency check | ||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||
| cd mux16 # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:MISTRAL_ALUT3 | ||||
| select -assert-max 1 t:MISTRAL_ALUT3 | ||||
| select -assert-max 2 t:MISTRAL_ALUT5 | ||||
| select -assert-max 5 t:MISTRAL_ALUT6 | ||||
| select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D | ||||
|  | @ -81,8 +81,8 @@ proc | |||
| equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cyclone10gx -noiopad -noclkbuf # equivalency check | ||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||
| cd mux16 # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:MISTRAL_ALUT3 | ||||
| select -assert-count 2 t:MISTRAL_ALUT5 | ||||
| select -assert-count 4 t:MISTRAL_ALUT6 | ||||
| select -assert-max 1 t:MISTRAL_ALUT3 | ||||
| select -assert-max 2 t:MISTRAL_ALUT5 | ||||
| select -assert-max 5 t:MISTRAL_ALUT6 | ||||
| 
 | ||||
| select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D | ||||
|  |  | |||
|  | @ -36,8 +36,7 @@ proc | |||
| equiv_opt -assert -map +/nexus/cells_sim.v synth_nexus # equivalency check | ||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||
| cd mux16 # Constrain all select calls below inside the top module | ||||
| select -assert-min 11 t:LUT4 | ||||
| select -assert-max 12 t:LUT4 | ||||
| select -assert-count 1 t:WIDEFN9 | ||||
| select -assert-max 2 t:WIDEFN9 | ||||
| 
 | ||||
| select -assert-none t:IB t:OB t:VLO t:VHI t:LUT4 t:WIDEFN9 %% t:* %D | ||||
|  |  | |||
|  | @ -30,12 +30,13 @@ proc | |||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc3se -noiopad # equivalency check | ||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||
| cd mux8 # Constrain all select calls below inside the top module | ||||
| select -assert-count 4 t:LUT1 | ||||
| select -assert-count 3 t:LUT4 | ||||
| select -assert-count 2 t:MUXF5 | ||||
| select -assert-max 5 t:LUT1 | ||||
| select -assert-max 3 t:LUT3 | ||||
| select -assert-max 3 t:LUT4 | ||||
| select -assert-max 3 t:MUXF5 | ||||
| select -assert-count 1 t:MUXF6 | ||||
| 
 | ||||
| select -assert-none t:LUT1 t:LUT4 t:MUXF5 t:MUXF6 %% t:* %D | ||||
| select -assert-none t:LUT1 t:LUT3 t:LUT4 t:MUXF5 t:MUXF6 %% t:* %D | ||||
| 
 | ||||
| 
 | ||||
| design -load read | ||||
|  |  | |||
							
								
								
									
										51
									
								
								tests/various/constant_drive_conflict.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								tests/various/constant_drive_conflict.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | |||
| read_verilog <<EOT | ||||
| module top(input A, output Y); | ||||
| 	assign A = 1; | ||||
| 
 | ||||
| 	assign Y = A; | ||||
| endmodule | ||||
| EOT | ||||
| 
 | ||||
| hierarchy -top top; proc | ||||
| 
 | ||||
| logger -expect warning "Drivers conflicting with a constant" 1 | ||||
| logger -expect log "Found and reported 1 problems." 1 | ||||
| check | ||||
| logger -check-expected | ||||
| 
 | ||||
| design -reset | ||||
| read_verilog <<EOT | ||||
| module top(input A, output Y); | ||||
| 	buffer some_buffer(A, Y); | ||||
| 	assign Y = 1; | ||||
| endmodule | ||||
| module buffer(input A, output Y); | ||||
| 	assign Y = A; | ||||
| endmodule | ||||
| EOT | ||||
| 
 | ||||
| hierarchy -top top; proc | ||||
| 
 | ||||
| logger -expect warning "Drivers conflicting with a constant" 1 | ||||
| logger -expect log "Found and reported 1 problems." 1 | ||||
| check | ||||
| logger -check-expected | ||||
| 
 | ||||
| design -reset | ||||
| read_verilog <<EOT | ||||
| module top(input clk, input A, output Y); | ||||
| 	reg Q; | ||||
| 	always @(posedge clk) Q <= A; | ||||
| 
 | ||||
| 	assign Q = 1; | ||||
| 
 | ||||
| 	assign Y = A; | ||||
| endmodule | ||||
| EOT | ||||
| 
 | ||||
| hierarchy -top top | ||||
| 
 | ||||
| logger -expect warning "Drivers conflicting with a constant" 1 | ||||
| logger -expect log "Found and reported 1 problems." 1 | ||||
| check | ||||
| logger -check-expected | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue