From 15ef6084533809894dd0b5200a65497047c2ccf8 Mon Sep 17 00:00:00 2001
From: Clifford Wolf <clifford@clifford.at>
Date: Fri, 19 Aug 2016 19:48:26 +0200
Subject: Added memory_memx pass, "memory -memx", and "prep -memx"

---
 passes/memory/Makefile.inc   |  1 +
 passes/memory/memory.cc      | 13 ++++++-
 passes/memory/memory_memx.cc | 92 ++++++++++++++++++++++++++++++++++++++++++++
 techlibs/common/prep.cc      | 19 ++++++++-
 4 files changed, 121 insertions(+), 4 deletions(-)
 create mode 100644 passes/memory/memory_memx.cc

diff --git a/passes/memory/Makefile.inc b/passes/memory/Makefile.inc
index aeff225d8..ad359c01b 100644
--- a/passes/memory/Makefile.inc
+++ b/passes/memory/Makefile.inc
@@ -6,4 +6,5 @@ OBJS += passes/memory/memory_collect.o
 OBJS += passes/memory/memory_unpack.o
 OBJS += passes/memory/memory_bram.o
 OBJS += passes/memory/memory_map.o
+OBJS += passes/memory/memory_memx.o
 
diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc
index e3c627607..947d598be 100644
--- a/passes/memory/memory.cc
+++ b/passes/memory/memory.cc
@@ -31,14 +31,15 @@ struct MemoryPass : public Pass {
 	{
 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 		log("\n");
-		log("    memory [-nomap] [-nordff] [-bram <bram_rules>] [selection]\n");
+		log("    memory [-nomap] [-nordff] [-memx] [-bram <bram_rules>] [selection]\n");
 		log("\n");
 		log("This pass calls all the other memory_* passes in a useful order:\n");
 		log("\n");
-		log("    memory_dff [-nordff]\n");
+		log("    memory_dff [-nordff]                (-memx implies -nordff)\n");
 		log("    opt_clean\n");
 		log("    memory_share\n");
 		log("    opt_clean\n");
+		log("    memory_memx                         (when called with -memx)\n");
 		log("    memory_collect\n");
 		log("    memory_bram -rules <bram_rules>     (when called with -bram)\n");
 		log("    memory_map                          (skipped if called with -nomap)\n");
@@ -51,6 +52,7 @@ struct MemoryPass : public Pass {
 	{
 		bool flag_nomap = false;
 		bool flag_nordff = false;
+		bool flag_memx = false;
 		string memory_bram_opts;
 
 		log_header(design, "Executing MEMORY pass.\n");
@@ -66,6 +68,11 @@ struct MemoryPass : public Pass {
 				flag_nordff = true;
 				continue;
 			}
+			if (args[argidx] == "-memx") {
+				flag_nordff = true;
+				flag_memx = true;
+				continue;
+			}
 			if (argidx+1 < args.size() && args[argidx] == "-bram") {
 				memory_bram_opts += " -rules " + args[++argidx];
 				continue;
@@ -77,6 +84,8 @@ struct MemoryPass : public Pass {
 		Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff");
 		Pass::call(design, "opt_clean");
 		Pass::call(design, "memory_share");
+		if (flag_memx)
+			Pass::call(design, "memory_memx");
 		Pass::call(design, "opt_clean");
 		Pass::call(design, "memory_collect");
 
diff --git a/passes/memory/memory_memx.cc b/passes/memory/memory_memx.cc
new file mode 100644
index 000000000..2b02e2490
--- /dev/null
+++ b/passes/memory/memory_memx.cc
@@ -0,0 +1,92 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/register.h"
+#include "kernel/log.h"
+#include <sstream>
+#include <set>
+#include <stdlib.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct MemoryMemxPass : public Pass {
+	MemoryMemxPass() : Pass("memory_memx", "emulate vlog sim behavior for mem ports") { }
+	virtual void help()
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("    memory_memx [selection]\n");
+		log("\n");
+		log("This pass adds additional circuitry that emulates the Verilog simulation\n");
+		log("behavior for out-of-bounds memory reads and writes.\n");
+		log("\n");
+	}
+	virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+		log_header(design, "Executing MEMORY_MEMX pass (converting $mem cells to logic and flip-flops).\n");
+		extra_args(args, 1, design);
+
+		for (auto module : design->selected_modules())
+		{
+			vector<Cell*> mem_port_cells;
+
+			for (auto cell : module->selected_cells())
+				if (cell->type.in("$memrd", "$memwr"))
+					mem_port_cells.push_back(cell);
+
+			for (auto cell : mem_port_cells)
+			{
+				IdString memid = cell->getParam("\\MEMID").decode_string();
+				RTLIL::Memory *mem = module->memories.at(memid);
+
+				int lowest_addr = mem->start_offset;
+				int highest_addr = mem->start_offset + mem->size - 1;
+
+				SigSpec addr = cell->getPort("\\ADDR");
+				addr.extend_u0(32);
+
+				SigSpec addr_ok = module->Nex(NEW_ID, module->ReduceXor(NEW_ID, addr), module->ReduceXor(NEW_ID, {addr, State::S1}));
+				if (lowest_addr != 0)
+					addr_ok = module->LogicAnd(NEW_ID, addr_ok, module->Ge(NEW_ID, addr, lowest_addr));
+				addr_ok = module->LogicAnd(NEW_ID, addr_ok, module->Le(NEW_ID, addr, highest_addr));
+
+				if (cell->type == "$memrd")
+				{
+					if (cell->getParam("\\CLK_ENABLE").as_bool())
+						log_error("Cell %s.%s (%s) has an enabled clock. Clocked $memrd cells are not supported by memory_memx!\n",
+								log_id(module), log_id(cell), log_id(cell->type));
+
+					SigSpec rdata = cell->getPort("\\DATA");
+					Wire *raw_rdata = module->addWire(NEW_ID, GetSize(rdata));
+					module->addMux(NEW_ID, SigSpec(State::Sx, GetSize(rdata)), raw_rdata, addr_ok, rdata);
+					cell->setPort("\\DATA", raw_rdata);
+				}
+
+				if (cell->type == "$memwr")
+				{
+					SigSpec en = cell->getPort("\\EN");
+					en = module->And(NEW_ID, en, addr_ok.repeat(GetSize(en)));
+					cell->setPort("\\EN", en);
+				}
+			}
+		}
+	}
+} MemoryMemxPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/common/prep.cc b/techlibs/common/prep.cc
index ac1d98fde..8accb7e28 100644
--- a/techlibs/common/prep.cc
+++ b/techlibs/common/prep.cc
@@ -53,6 +53,10 @@ struct PrepPass : public ScriptPass
 		log("        passed to 'proc'. uses verilog simulation behavior for verilog if/case\n");
 		log("        undef handling. this also prevents 'wreduce' from being run.\n");
 		log("\n");
+		log("    -memx\n");
+		log("        simulate verilog simulation behavior for out-of-bounds memory accesses\n");
+		log("        using the 'memory_memx' pass. This option implies -nordff.\n");
+		log("\n");
 		log("    -nordff\n");
 		log("        passed to 'memory_dff'. prohibits merging of FFs into memory read ports\n");
 		log("\n");
@@ -68,7 +72,7 @@ struct PrepPass : public ScriptPass
 	}
 
 	string top_module, fsm_opts, memory_opts;
-	bool autotop, flatten, ifxmode;
+	bool autotop, flatten, ifxmode, memxmode;
 
 	virtual void clear_flags() YS_OVERRIDE
 	{
@@ -78,6 +82,7 @@ struct PrepPass : public ScriptPass
 		autotop = false;
 		flatten = false;
 		ifxmode = false;
+		memxmode = false;
 	}
 
 	virtual void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -114,6 +119,11 @@ struct PrepPass : public ScriptPass
 				ifxmode = true;
 				continue;
 			}
+			if (args[argidx] == "-memx") {
+				memxmode = true;
+				memory_opts += " -nordff";
+				continue;
+			}
 			if (args[argidx] == "-nordff") {
 				memory_opts += " -nordff";
 				continue;
@@ -153,7 +163,10 @@ struct PrepPass : public ScriptPass
 
 		if (check_label("coarse"))
 		{
-			run(ifxmode ? "proc -ifx" : "proc");
+			if (help_mode)
+				run("proc [-ifx]");
+			else
+				run(ifxmode ? "proc -ifx" : "proc");
 			if (help_mode || flatten)
 				run("flatten", "(if -flatten)");
 			run("opt_expr -keepdc");
@@ -163,6 +176,8 @@ struct PrepPass : public ScriptPass
 			if (!ifxmode)
 				run("wreduce");
 			run("memory_dff" + (help_mode ? " [-nordff]" : memory_opts));
+			if (help_mode || memxmode)
+				run("memory_memx", "(if -memx)");
 			run("opt_clean");
 			run("memory_collect");
 			run("opt -keepdc -fast");
-- 
cgit v1.2.3