aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--frontends/ast/ast.h3
-rw-r--r--frontends/ast/genrtlil.cc35
-rw-r--r--frontends/ast/simplify.cc100
-rw-r--r--frontends/json/jsonparse.cc12
-rw-r--r--frontends/verilog/preproc.cc11
-rw-r--r--kernel/rtlil.h3
-rw-r--r--passes/sat/assertpmux.cc2
-rw-r--r--techlibs/machxo2/Makefile.inc5
-rw-r--r--techlibs/machxo2/cells_map.v34
-rw-r--r--techlibs/machxo2/cells_sim.v212
-rw-r--r--techlibs/machxo2/synth_machxo2.cc248
-rw-r--r--tests/arch/machxo2/.gitignore2
-rw-r--r--tests/arch/machxo2/add_sub.ys8
-rw-r--r--tests/arch/machxo2/dffs.ys19
-rw-r--r--tests/arch/machxo2/fsm.ys15
-rw-r--r--tests/arch/machxo2/logic.ys8
-rw-r--r--tests/arch/machxo2/mux.ys40
-rw-r--r--tests/arch/machxo2/run-test.sh4
-rw-r--r--tests/arch/machxo2/shifter.ys10
-rw-r--r--tests/arch/machxo2/tribuf.ys10
-rw-r--r--tests/sat/bug2595.ys18
-rw-r--r--tests/various/fib_tern.v70
-rw-r--r--tests/various/fib_tern.ys6
-rw-r--r--tests/verilog/macro_unapplied.ys17
-rw-r--r--tests/verilog/macro_unapplied_newline.ys5
26 files changed, 875 insertions, 23 deletions
diff --git a/Makefile b/Makefile
index 1f255a5ea..50f4f52b7 100644
--- a/Makefile
+++ b/Makefile
@@ -810,6 +810,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
+cd tests/arch/ice40 && bash run-test.sh $(SEEDOPT)
+cd tests/arch/xilinx && bash run-test.sh $(SEEDOPT)
+cd tests/arch/ecp5 && bash run-test.sh $(SEEDOPT)
+ +cd tests/arch/machxo2 && bash run-test.sh $(SEEDOPT)
+cd tests/arch/efinix && bash run-test.sh $(SEEDOPT)
+cd tests/arch/anlogic && bash run-test.sh $(SEEDOPT)
+cd tests/arch/gowin && bash run-test.sh $(SEEDOPT)
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index b793c6c2d..df2700831 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -276,6 +276,9 @@ namespace AST
bool is_simple_const_expr();
std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint);
+ bool is_recursive_function() const;
+ std::pair<AstNode*, AstNode*> get_tern_choice();
+
// create a human-readable text representation of the AST (for debugging)
void dumpAst(FILE *f, std::string indent) const;
void dumpVlog(FILE *f, std::string indent) const;
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index 24f5e1bef..713e34eb1 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -944,6 +944,41 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
}
break;
}
+ if (current_scope.count(str))
+ {
+ // This width detection is needed for function calls which are
+ // unelaborated, which currently only applies to calls to recursive
+ // functions reached by unevaluated ternary branches.
+ const AstNode *func = current_scope.at(str);
+ if (func->type != AST_FUNCTION)
+ log_file_error(filename, location.first_line, "Function call to %s resolved to something that isn't a function!\n", RTLIL::unescape_id(str).c_str());
+ const AstNode *wire = nullptr;
+ for (const AstNode *child : func->children)
+ if (child->str == func->str) {
+ wire = child;
+ break;
+ }
+ log_assert(wire && wire->type == AST_WIRE);
+ sign_hint = wire->is_signed;
+ width_hint = 1;
+ if (!wire->children.empty())
+ {
+ log_assert(wire->children.size() == 1);
+ const AstNode *range = wire->children.at(0);
+ log_assert(range->type == AST_RANGE && range->children.size() == 2);
+ AstNode *left = range->children.at(0)->clone();
+ AstNode *right = range->children.at(1)->clone();
+ while (left->simplify(true, false, false, 1, -1, false, true)) { }
+ while (right->simplify(true, false, false, 1, -1, false, true)) { }
+ if (left->type != AST_CONSTANT || right->type != AST_CONSTANT)
+ log_file_error(filename, location.first_line, "Function %s has non-constant width!",
+ RTLIL::unescape_id(str).c_str());
+ width_hint = abs(int(left->asInt(true) - right->asInt(true)));
+ delete left;
+ delete right;
+ }
+ break;
+ }
YS_FALLTHROUGH
// everything should have been handled above -> print error if not.
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index df7c9282c..ae42d7ec6 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -575,6 +575,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
deep_recursion_warning = false;
}
+ static bool unevaluated_tern_branch = false;
+
AstNode *newNode = NULL;
bool did_something = false;
@@ -1091,7 +1093,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
break;
case AST_TERNARY:
- detect_width_simple = true;
child_0_is_self_determined = true;
break;
@@ -1124,6 +1125,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
detectSignWidth(width_hint, sign_hint);
if (type == AST_TERNARY) {
+ if (width_hint < 0) {
+ while (!children[0]->basic_prep && children[0]->simplify(true, false, in_lvalue, stage, -1, false, in_param))
+ did_something = true;
+
+ bool backup_unevaluated_tern_branch = unevaluated_tern_branch;
+ AstNode *chosen = get_tern_choice().first;
+
+ unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[2];
+ while (!children[1]->basic_prep && children[1]->simplify(false, false, in_lvalue, stage, -1, false, in_param))
+ did_something = true;
+
+ unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[1];
+ while (!children[2]->basic_prep && children[2]->simplify(false, false, in_lvalue, stage, -1, false, in_param))
+ did_something = true;
+
+ unevaluated_tern_branch = backup_unevaluated_tern_branch;
+ detectSignWidth(width_hint, sign_hint);
+ }
int width_hint_left, width_hint_right;
bool sign_hint_left, sign_hint_right;
bool found_real_left, found_real_right;
@@ -1187,6 +1206,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
for (size_t i = 0; i < children.size(); i++) {
bool did_something_here = true;
bool backup_flag_autowire = flag_autowire;
+ bool backup_unevaluated_tern_branch = unevaluated_tern_branch;
if ((type == AST_GENFOR || type == AST_FOR) && i >= 3)
break;
if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1)
@@ -1199,6 +1219,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
break;
if (type == AST_DEFPARAM && i == 0)
flag_autowire = true;
+ if (type == AST_TERNARY && i > 0 && !unevaluated_tern_branch) {
+ AstNode *chosen = get_tern_choice().first;
+ unevaluated_tern_branch = chosen && chosen != children[i];
+ }
while (did_something_here && i < children.size()) {
bool const_fold_here = const_fold, in_lvalue_here = in_lvalue;
int width_hint_here = width_hint;
@@ -1238,6 +1262,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
did_something = true;
}
flag_autowire = backup_flag_autowire;
+ unevaluated_tern_branch = backup_unevaluated_tern_branch;
}
for (auto &attr : attributes) {
while (attr.second->simplify(true, false, false, stage, -1, false, true))
@@ -3177,6 +3202,8 @@ skip_dynamic_range_lvalue_expansion:;
std::string prefix = sstr.str();
AstNode *decl = current_scope[str];
+ if (unevaluated_tern_branch && decl->is_recursive_function())
+ goto replace_fcall_later;
decl = decl->clone();
decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion
decl->expand_genblock(prefix);
@@ -3612,24 +3639,9 @@ replace_fcall_later:;
case AST_TERNARY:
if (children[0]->isConst())
{
- bool found_sure_true = false;
- bool found_maybe_true = false;
-
- if (children[0]->type == AST_CONSTANT)
- for (auto &bit : children[0]->bits) {
- if (bit == RTLIL::State::S1)
- found_sure_true = true;
- if (bit > RTLIL::State::S1)
- found_maybe_true = true;
- }
- else
- found_sure_true = children[0]->asReal(sign_hint) != 0;
-
- AstNode *choice = NULL, *not_choice = NULL;
- if (found_sure_true)
- choice = children[1], not_choice = children[2];
- else if (!found_maybe_true)
- choice = children[2], not_choice = children[1];
+ auto pair = get_tern_choice();
+ AstNode *choice = pair.first;
+ AstNode *not_choice = pair.second;
if (choice != NULL) {
if (choice->type == AST_CONSTANT) {
@@ -4859,4 +4871,54 @@ void AstNode::allocateDefaultEnumValues()
}
}
+bool AstNode::is_recursive_function() const
+{
+ std::set<const AstNode *> visited;
+ std::function<bool(const AstNode *node)> visit = [&](const AstNode *node) {
+ if (visited.count(node))
+ return node == this;
+ visited.insert(node);
+ if (node->type == AST_FCALL) {
+ auto it = current_scope.find(node->str);
+ if (it != current_scope.end() && visit(it->second))
+ return true;
+ }
+ for (const AstNode *child : node->children) {
+ if (visit(child))
+ return true;
+ }
+ return false;
+ };
+
+ log_assert(type == AST_FUNCTION);
+ return visit(this);
+}
+
+std::pair<AstNode*, AstNode*> AstNode::get_tern_choice()
+{
+ if (!children[0]->isConst())
+ return {};
+
+ bool found_sure_true = false;
+ bool found_maybe_true = false;
+
+ if (children[0]->type == AST_CONSTANT)
+ for (auto &bit : children[0]->bits) {
+ if (bit == RTLIL::State::S1)
+ found_sure_true = true;
+ if (bit > RTLIL::State::S1)
+ found_maybe_true = true;
+ }
+ else
+ found_sure_true = children[0]->asReal(true) != 0;
+
+ AstNode *choice = nullptr, *not_choice = nullptr;
+ if (found_sure_true)
+ choice = children[1], not_choice = children[2];
+ else if (!found_maybe_true)
+ choice = children[2], not_choice = children[1];
+
+ return {choice, not_choice};
+}
+
YOSYS_NAMESPACE_END
diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc
index 1b34aaf3a..312f6d3be 100644
--- a/frontends/json/jsonparse.cc
+++ b/frontends/json/jsonparse.cc
@@ -72,10 +72,17 @@ struct JsonNode
break;
}
- if ('0' <= ch && ch <= '9')
+ if (('0' <= ch && ch <= '9') || ch == '-')
{
+ bool negative = false;
type = 'N';
- data_number = ch - '0';
+ if (ch == '-') {
+ data_number = 0;
+ negative = true;
+ } else {
+ data_number = ch - '0';
+ }
+
data_string += ch;
while (1)
@@ -97,6 +104,7 @@ struct JsonNode
data_string += ch;
}
+ data_number = negative ? -data_number : data_number;
data_string = "";
break;
diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc
index c451c4c20..de707593f 100644
--- a/frontends/verilog/preproc.cc
+++ b/frontends/verilog/preproc.cc
@@ -477,7 +477,16 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok)
std::string name = tok.substr(1);
std::string skipped_spaces = skip_spaces();
tok = next_token(false);
- if (tok == "(" && body->has_args) {
+ if (body->has_args) {
+ if (tok != "(") {
+ if (tok.size() == 1 && iscntrl(tok[0])) {
+ char buf[5];
+ snprintf(buf, sizeof(buf), "\\x%02x", tok[0]);
+ tok = buf;
+ }
+ log_error("Expected to find '(' to begin macro arguments for '%s', but instead found '%s'\n",
+ name.c_str(), tok.c_str());
+ }
std::vector<std::string> args;
bool done = false;
while (!done) {
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index 4dad3c428..6170ea55e 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -735,6 +735,7 @@ struct RTLIL::SigChunk
RTLIL::SigChunk extract(int offset, int length) const;
inline int size() const { return width; }
+ inline bool is_wire() const { return wire != NULL; }
bool operator <(const RTLIL::SigChunk &other) const;
bool operator ==(const RTLIL::SigChunk &other) const;
@@ -760,6 +761,8 @@ struct RTLIL::SigBit
SigBit(const RTLIL::SigBit &sigbit) = default;
RTLIL::SigBit &operator =(const RTLIL::SigBit &other) = default;
+ inline bool is_wire() const { return wire != NULL; }
+
bool operator <(const RTLIL::SigBit &other) const;
bool operator ==(const RTLIL::SigBit &other) const;
bool operator !=(const RTLIL::SigBit &other) const;
diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc
index e9a10465e..f31b78804 100644
--- a/passes/sat/assertpmux.cc
+++ b/passes/sat/assertpmux.cc
@@ -88,7 +88,7 @@ struct AssertpmuxWorker
{
SigSpec output;
- for (auto muxuser : sigbit_muxusers.at(bit))
+ for (auto muxuser : sigbit_muxusers[bit])
{
Cell *cell = std::get<0>(muxuser);
int portidx = std::get<1>(muxuser);
diff --git a/techlibs/machxo2/Makefile.inc b/techlibs/machxo2/Makefile.inc
new file mode 100644
index 000000000..6f6f6ce94
--- /dev/null
+++ b/techlibs/machxo2/Makefile.inc
@@ -0,0 +1,5 @@
+
+OBJS += techlibs/machxo2/synth_machxo2.o
+
+$(eval $(call add_share_file,share/machxo2,techlibs/machxo2/cells_map.v))
+$(eval $(call add_share_file,share/machxo2,techlibs/machxo2/cells_sim.v))
diff --git a/techlibs/machxo2/cells_map.v b/techlibs/machxo2/cells_map.v
new file mode 100644
index 000000000..82eb10d95
--- /dev/null
+++ b/techlibs/machxo2/cells_map.v
@@ -0,0 +1,34 @@
+module \$lut (A, Y);
+ parameter WIDTH = 0;
+ parameter LUT = 0;
+ input [WIDTH-1:0] A;
+ output Y;
+
+ localparam rep = 1<<(4-WIDTH);
+ wire [3:0] I;
+
+ generate
+ if(WIDTH == 1) begin
+ assign I = {1'b0, 1'b0, 1'b0, A[0]};
+ end else if(WIDTH == 2) begin
+ assign I = {1'b0, 1'b0, A[1], A[0]};
+ end else if(WIDTH == 3) begin
+ assign I = {1'b0, A[2], A[1], A[0]};
+ end else if(WIDTH == 4) begin
+ assign I = {A[3], A[2], A[1], A[0]};
+ end else begin
+ wire _TECHMAP_FAIL_ = 1;
+ end
+ endgenerate
+
+ LUT4 #(.INIT({rep{LUT}})) _TECHMAP_REPLACE_ (.A(I[0]), .B(I[1]), .C(I[2]), .D(I[3]), .Z(Y));
+endmodule
+
+// DFFs
+module \$_DFF_P_ (input D, C, output Q); FACADE_FF #(.CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(1'b0), .DI(D), .Q(Q)); endmodule
+
+// IO- "$__" cells for the iopadmap pass.
+module \$__FACADE_OUTPAD (input I, output O); FACADE_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.PAD(O), .I(I), .T(1'b0)); endmodule
+module \$__FACADE_INPAD (input I, output O); FACADE_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.PAD(I), .O(O)); endmodule
+module \$__FACADE_TOUTPAD (input I, OE, output O); FACADE_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.PAD(O), .I(I), .T(~OE)); endmodule
+module \$__FACADE_TINOUTPAD (input I, OE, output O, inout B); FACADE_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.PAD(B), .I(I), .O(O), .T(~OE)); endmodule
diff --git a/techlibs/machxo2/cells_sim.v b/techlibs/machxo2/cells_sim.v
new file mode 100644
index 000000000..161ddfe2e
--- /dev/null
+++ b/techlibs/machxo2/cells_sim.v
@@ -0,0 +1,212 @@
+module LUT4 #(
+ parameter [15:0] INIT = 0
+) (
+ input A, B, C, D,
+ output Z
+);
+ // This form of LUT propagates as few x's as possible.
+ wire [7:0] s3 = D ? INIT[15:8] : INIT[7:0];
+ wire [3:0] s2 = C ? s3[ 7:4] : s3[3:0];
+ wire [1:0] s1 = B ? s2[ 3:2] : s2[1:0];
+ assign Z = A ? s1[1] : s1[0];
+endmodule
+
+module FACADE_FF #(
+ parameter GSR = "ENABLED",
+ parameter CEMUX = "1",
+ parameter CLKMUX = "0",
+ parameter LSRMUX = "LSR",
+ parameter LSRONMUX = "LSRMUX",
+ parameter SRMODE = "LSR_OVER_CE",
+ parameter REGSET = "SET",
+ parameter REGMODE = "FF"
+) (
+ input CLK, DI, LSR, CE,
+ output reg Q
+);
+
+ wire muxce;
+ generate
+ case (CEMUX)
+ "1": assign muxce = 1'b1;
+ "0": assign muxce = 1'b0;
+ "INV": assign muxce = ~CE;
+ default: assign muxce = CE;
+ endcase
+ endgenerate
+
+ wire muxlsr = (LSRMUX == "INV") ? ~LSR : LSR;
+ wire muxlsron = (LSRONMUX == "LSRMUX") ? muxlsr : 1'b0;
+ wire muxclk = (CLKMUX == "INV") ? ~CLK : CLK;
+ wire srval = (REGSET == "SET") ? 1'b1 : 1'b0;
+
+ initial Q = srval;
+
+ generate
+ if (REGMODE == "FF") begin
+ if (SRMODE == "ASYNC") begin
+ always @(posedge muxclk, posedge muxlsron)
+ if (muxlsron)
+ Q <= srval;
+ else if (muxce)
+ Q <= DI;
+ end else begin
+ always @(posedge muxclk)
+ if (muxlsron)
+ Q <= srval;
+ else if (muxce)
+ Q <= DI;
+ end
+ end else if (REGMODE == "LATCH") begin
+ ERROR_UNSUPPORTED_FF_MODE error();
+ end else begin
+ ERROR_UNKNOWN_FF_MODE error();
+ end
+ endgenerate
+endmodule
+
+/* For consistency with ECP5; represents F0/F1 => OFX0 mux in a slice. */
+module PFUMX (input ALUT, BLUT, C0, output Z);
+ assign Z = C0 ? ALUT : BLUT;
+endmodule
+
+/* For consistency with ECP5; represents FXA/FXB => OFX1 mux in a slice. */
+module L6MUX21 (input D0, D1, SD, output Z);
+ assign Z = SD ? D1 : D0;
+endmodule
+
+/* For consistency, input order matches TRELLIS_SLICE even though the BELs in
+prjtrellis were filled in clockwise order from bottom left. */
+module FACADE_SLICE #(
+ parameter MODE = "LOGIC",
+ parameter GSR = "ENABLED",
+ parameter SRMODE = "LSR_OVER_CE",
+ parameter CEMUX = "1",
+ parameter CLKMUX = "0",
+ parameter LSRMUX = "LSR",
+ parameter LSRONMUX = "LSRMUX",
+ parameter LUT0_INITVAL = 16'hFFFF,
+ parameter LUT1_INITVAL = 16'hFFFF,
+ parameter REGMODE = "FF",
+ parameter REG0_SD = "1",
+ parameter REG1_SD = "1",
+ parameter REG0_REGSET = "SET",
+ parameter REG1_REGSET = "SET",
+ parameter CCU2_INJECT1_0 = "YES",
+ parameter CCU2_INJECT1_1 = "YES",
+ parameter WREMUX = "INV"
+) (
+ input A0, B0, C0, D0,
+ input A1, B1, C1, D1,
+ input M0, M1,
+ input FCI, FXA, FXB,
+
+ input CLK, LSR, CE,
+ input DI0, DI1,
+
+ input WD0, WD1,
+ input WAD0, WAD1, WAD2, WAD3,
+ input WRE, WCK,
+
+ output F0, Q0,
+ output F1, Q1,
+ output FCO, OFX0, OFX1,
+
+ output WDO0, WDO1, WDO2, WDO3,
+ output WADO0, WADO1, WADO2, WADO3
+);
+
+ generate
+ if (MODE == "LOGIC") begin
+ L6MUX21 FXMUX (.D0(FXA), .D1(FXB), .SD(M1), .Z(OFX1));
+
+ wire k0;
+ wire k1;
+ PFUMX K0K1MUX (.ALUT(k1), .BLUT(k0), .C0(M0), .Z(OFX0));
+
+ LUT4 #(.INIT(LUT0_INITVAL)) LUT_0 (.A(A0), .B(B0), .C(C0), .D(D0), .Z(k0));
+ LUT4 #(.INIT(LUT1_INITVAL)) LUT_1 (.A(A0), .B(B0), .C(C0), .D(D0), .Z(k1));
+
+ assign F0 = k0;
+ assign F1 = k1;
+ end else if (MODE == "CCU2") begin
+ ERROR_UNSUPPORTED_SLICE_MODE error();
+ end else if (MODE == "DPRAM") begin
+ ERROR_UNSUPPORTED_SLICE_MODE error();
+ end else begin
+ ERROR_UNKNOWN_SLICE_MODE error();
+ end
+ endgenerate
+
+ /* Reg can be fed either by M, or DI inputs; DI inputs muxes OFX and F
+ outputs (in other words, feeds back into FACADE_SLICE). */
+ wire di0 = (REG0_SD == "1") ? DI0 : M0;
+ wire di1 = (REG1_SD == "1") ? DI1 : M1;
+
+ FACADE_FF#(.GSR(GSR), .CEMUX(CEMUX), .CLKMUX(CLKMUX), .LSRMUX(LSRMUX),
+ .LSRONMUX(LSRONMUX), .SRMODE(SRMODE), .REGSET(REG0_REGSET),
+ .REGMODE(REGMODE)) REG_0 (.CLK(CLK), .DI(di0), .LSR(LSR), .CE(CE), .Q(Q0));
+ FACADE_FF#(.GSR(GSR), .CEMUX(CEMUX), .CLKMUX(CLKMUX), .LSRMUX(LSRMUX),
+ .LSRONMUX(LSRONMUX), .SRMODE(SRMODE), .REGSET(REG1_REGSET),
+ .REGMODE(REGMODE)) REG_1 (.CLK(CLK), .DI(di1), .LSR(LSR), .CE(CE), .Q(Q1));
+endmodule
+
+module FACADE_IO #(
+ parameter DIR = "INPUT"
+) (
+ inout PAD,
+ input I, T,
+ output O
+);
+ generate
+ if (DIR == "INPUT") begin
+ assign O = PAD;
+ end else if (DIR == "OUTPUT") begin
+ assign PAD = T ? 1'bz : I;
+ end else if (DIR == "BIDIR") begin
+ assign PAD = T ? 1'bz : I;
+ assign O = PAD;
+ end else begin
+ ERROR_UNKNOWN_IO_MODE error();
+ end
+ endgenerate
+endmodule
+
+(* blackbox *)
+module OSCH #(
+ parameter NOM_FREQ = "2.08"
+) (
+ input STDBY,
+ output OSC,
+ output SEDSTDBY
+);
+endmodule
+
+(* blackbox *)
+module DCCA (
+ input CLKI,
+ input CE,
+ output CLKO
+);
+endmodule
+
+(* blackbox *)
+module DCMA (
+ input CLK0,
+ input CLK1,
+ input SEL,
+ output DCMOUT
+);
+endmodule
+
+// IO- "$__" cells for the iopadmap pass. These are temporary cells not meant
+// to be instantiated by the end user. They are required in this file for
+// attrmvcp to work.
+(* blackbox *)
+module \$__FACADE_OUTPAD (input I, output O); endmodule
+(* blackbox *)
+module \$__FACADE_INPAD (input I, output O); endmodule
+(* blackbox *)
+module \$__FACADE_TOUTPAD (input I, OE, output O); endmodule
+(* blackbox *)
+module \$__FACADE_TINOUTPAD (input I, OE, output O, inout B); endmodule
diff --git a/techlibs/machxo2/synth_machxo2.cc b/techlibs/machxo2/synth_machxo2.cc
new file mode 100644
index 000000000..bd56fbba9
--- /dev/null
+++ b/techlibs/machxo2/synth_machxo2.cc
@@ -0,0 +1,248 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 William D. Jones <wjones@wdj-consulting.com>
+ *
+ * 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/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SynthMachXO2Pass : public ScriptPass
+{
+ SynthMachXO2Pass() : ScriptPass("synth_machxo2", "synthesis for MachXO2 FPGAs. This work is experimental.") { }
+
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" synth_machxo2 [options]\n");
+ log("\n");
+ log("This command runs synthesis for MachXO2 FPGAs.\n");
+ log("\n");
+ log(" -top <module>\n");
+ log(" use the specified module as top module\n");
+ log("\n");
+ log(" -blif <file>\n");
+ log(" write the design to the specified BLIF file. writing of an output file\n");
+ log(" is omitted if this parameter is not specified.\n");
+ log("\n");
+ log(" -edif <file>\n");
+ log(" write the design to the specified EDIF file. writing of an output file\n");
+ log(" is omitted if this parameter is not specified.\n");
+ log("\n");
+ log(" -json <file>\n");
+ log(" write the design to the specified JSON file. writing of an output file\n");
+ log(" is omitted if this parameter is not specified.\n");
+ log("\n");
+ log(" -run <from_label>:<to_label>\n");
+ log(" only run the commands between the labels (see below). an empty\n");
+ log(" from label is synonymous to 'begin', and empty to label is\n");
+ log(" synonymous to the end of the command list.\n");
+ log("\n");
+ log(" -noflatten\n");
+ log(" do not flatten design before synthesis\n");
+ log("\n");
+ log(" -noiopad\n");
+ log(" do not insert IO buffers\n");
+ log("\n");
+ log(" -vpr\n");
+ log(" generate an output netlist (and BLIF file) suitable for VPR\n");
+ log(" (this feature is experimental and incomplete)\n");
+ log("\n");
+ log("\n");
+ log("The following commands are executed by this synthesis command:\n");
+ help_script();
+ log("\n");
+ }
+
+ string top_opt, blif_file, edif_file, json_file;
+ bool flatten, vpr, noiopad;
+
+ void clear_flags() override
+ {
+ top_opt = "-auto-top";
+ blif_file = "";
+ edif_file = "";
+ json_file = "";
+ flatten = true;
+ vpr = false;
+ noiopad = false;
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ string run_from, run_to;
+ clear_flags();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ top_opt = "-top " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-blif" && argidx+1 < args.size()) {
+ blif_file = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-edif" && argidx+1 < args.size()) {
+ edif_file = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-json" && argidx+1 < args.size()) {
+ json_file = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-run" && argidx+1 < args.size()) {
+ size_t pos = args[argidx+1].find(':');
+ if (pos == std::string::npos)
+ break;
+ run_from = args[++argidx].substr(0, pos);
+ run_to = args[argidx].substr(pos+1);
+ continue;
+ }
+ if (args[argidx] == "-flatten") {
+ flatten = true;
+ continue;
+ }
+ if (args[argidx] == "-noflatten") {
+ flatten = false;
+ continue;
+ }
+ if (args[argidx] == "-noiopad") {
+ noiopad = true;
+ continue;
+ }
+ if (args[argidx] == "-vpr") {
+ vpr = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!design->full_selection())
+ log_cmd_error("This command only operates on fully selected designs!\n");
+
+ log_header(design, "Executing SYNTH_MACHXO2 pass.\n");
+ log_push();
+
+ run_script(design, run_from, run_to);
+
+ log_pop();
+ }
+
+ void script() override
+ {
+ if (check_label("begin"))
+ {
+ run("read_verilog -lib -icells +/machxo2/cells_sim.v");
+ run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
+ }
+
+ if (check_label("flatten", "(unless -noflatten)"))
+ {
+ if (flatten || help_mode) {
+ run("proc");
+ run("flatten");
+ run("tribuf -logic");
+ run("deminout");
+ }
+ }
+
+ if (check_label("coarse"))
+ {
+ run("synth -run coarse");
+ }
+
+ if (check_label("fine"))
+ {
+ run("memory_map");
+ run("opt -full");
+ run("techmap -map +/techmap.v");
+ run("opt -fast");
+ }
+
+ if (check_label("map_ios", "(unless -noiopad)"))
+ {
+ if (!noiopad || help_mode)
+ {
+ run("iopadmap -bits -outpad $__FACADE_OUTPAD I:O -inpad $__FACADE_INPAD O:I -toutpad $__FACADE_TOUTPAD OE:I:O -tinoutpad $__FACADE_TINOUTPAD OE:O:I:B A:top");
+ run("attrmvcp -attr src -attr LOC t:$__FACADE_OUTPAD %x:+[O] t:$__FACADE_TOUTPAD %x:+[O] t:$__FACADE_TINOUTPAD %x:+[B]");
+ run("attrmvcp -attr src -attr LOC -driven t:$__FACADE_INPAD %x:+[I]");
+ }
+ }
+
+ if (check_label("map_ffs"))
+ {
+ run("dfflegalize -cell $_DFF_P_ 0");
+ }
+
+ if (check_label("map_luts"))
+ {
+ run("abc -lut 4 -dress");
+ run("clean");
+ }
+
+ if (check_label("map_cells"))
+ {
+ run("techmap -map +/machxo2/cells_map.v");
+ run("clean");
+ }
+
+ if (check_label("check"))
+ {
+ run("hierarchy -check");
+ run("stat");
+ }
+
+ if (check_label("blif"))
+ {
+ if (!blif_file.empty() || help_mode) {
+ if (vpr || help_mode) {
+ run(stringf("opt_clean -purge"),
+ " (vpr mode)");
+ run(stringf("write_blif -attr -cname -conn -param %s",
+ help_mode ? "<file-name>" : blif_file.c_str()),
+ " (vpr mode)");
+ }
+ if (!vpr)
+ run(stringf("write_blif -gates -attr -param %s",
+ help_mode ? "<file-name>" : blif_file.c_str()),
+ " (non-vpr mode)");
+ }
+ }
+
+ if (check_label("edif"))
+ {
+ if (!edif_file.empty() || help_mode)
+ run(stringf("write_edif %s", help_mode ? "<file-name>" : edif_file.c_str()));
+ }
+
+ if (check_label("json"))
+ {
+ if (!json_file.empty() || help_mode)
+ run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str()));
+ }
+ }
+} SynthMachXO2Pass;
+
+PRIVATE_NAMESPACE_END
diff --git a/tests/arch/machxo2/.gitignore b/tests/arch/machxo2/.gitignore
new file mode 100644
index 000000000..1d329c933
--- /dev/null
+++ b/tests/arch/machxo2/.gitignore
@@ -0,0 +1,2 @@
+*.log
+/run-test.mk
diff --git a/tests/arch/machxo2/add_sub.ys b/tests/arch/machxo2/add_sub.ys
new file mode 100644
index 000000000..d9497b818
--- /dev/null
+++ b/tests/arch/machxo2/add_sub.ys
@@ -0,0 +1,8 @@
+read_verilog ../common/add_sub.v
+hierarchy -top top
+proc
+equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # 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 10 t:LUT4
+select -assert-none t:LUT4 t:FACADE_IO %% t:* %D
diff --git a/tests/arch/machxo2/dffs.ys b/tests/arch/machxo2/dffs.ys
new file mode 100644
index 000000000..83a79a9d6
--- /dev/null
+++ b/tests/arch/machxo2/dffs.ys
@@ -0,0 +1,19 @@
+read_verilog ../common/dffs.v
+design -save read
+
+hierarchy -top dff
+proc
+equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd dff # Constrain all select calls below inside the top module
+select -assert-count 1 t:FACADE_FF
+select -assert-none t:FACADE_FF t:FACADE_IO %% t:* %D
+
+design -load read
+hierarchy -top dffe
+proc
+equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd dffe # Constrain all select calls below inside the top module
+select -assert-count 2 t:FACADE_FF t:LUT4
+select -assert-none t:FACADE_FF t:LUT4 t:FACADE_IO %% t:* %D
diff --git a/tests/arch/machxo2/fsm.ys b/tests/arch/machxo2/fsm.ys
new file mode 100644
index 000000000..847a61161
--- /dev/null
+++ b/tests/arch/machxo2/fsm.ys
@@ -0,0 +1,15 @@
+read_verilog ../common/fsm.v
+hierarchy -top fsm
+proc
+flatten
+
+equiv_opt -run :prove -map +/machxo2/cells_sim.v synth_machxo2
+miter -equiv -make_assert -flatten gold gate miter
+sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip 1 miter
+
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd fsm # Constrain all select calls below inside the top module
+
+select -assert-max 16 t:LUT4
+select -assert-count 6 t:FACADE_FF
+select -assert-none t:FACADE_FF t:LUT4 t:FACADE_IO %% t:* %D
diff --git a/tests/arch/machxo2/logic.ys b/tests/arch/machxo2/logic.ys
new file mode 100644
index 000000000..bf93ab128
--- /dev/null
+++ b/tests/arch/machxo2/logic.ys
@@ -0,0 +1,8 @@
+read_verilog ../common/logic.v
+hierarchy -top top
+proc
+equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # 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 9 t:LUT4
+select -assert-none t:LUT4 t:FACADE_IO %% t:* %D
diff --git a/tests/arch/machxo2/mux.ys b/tests/arch/machxo2/mux.ys
new file mode 100644
index 000000000..6c8aa857c
--- /dev/null
+++ b/tests/arch/machxo2/mux.ys
@@ -0,0 +1,40 @@
+read_verilog ../common/mux.v
+design -save read
+
+hierarchy -top mux2
+proc
+equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd mux2 # Constrain all select calls below inside the top module
+select -assert-count 1 t:LUT4
+select -assert-none t:LUT4 t:FACADE_IO %% t:* %D
+
+design -load read
+hierarchy -top mux4
+proc
+equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd mux4 # Constrain all select calls below inside the top module
+select -assert-count 2 t:LUT4
+
+select -assert-none t:LUT4 t:FACADE_IO %% t:* %D
+
+design -load read
+hierarchy -top mux8
+proc
+equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # 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 5 t:LUT4
+
+select -assert-none t:LUT4 t:FACADE_IO %% t:* %D
+
+design -load read
+hierarchy -top mux16
+proc
+equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # 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 11 t:LUT4
+
+select -assert-none t:LUT4 t:FACADE_IO %% t:* %D
diff --git a/tests/arch/machxo2/run-test.sh b/tests/arch/machxo2/run-test.sh
new file mode 100644
index 000000000..4be4b70ae
--- /dev/null
+++ b/tests/arch/machxo2/run-test.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+set -eu
+source ../../gen-tests-makefile.sh
+run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
diff --git a/tests/arch/machxo2/shifter.ys b/tests/arch/machxo2/shifter.ys
new file mode 100644
index 000000000..87fdab0fa
--- /dev/null
+++ b/tests/arch/machxo2/shifter.ys
@@ -0,0 +1,10 @@
+read_verilog ../common/shifter.v
+hierarchy -top top
+proc
+flatten
+equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # 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 8 t:FACADE_FF
+select -assert-none t:FACADE_FF t:FACADE_IO %% t:* %D
diff --git a/tests/arch/machxo2/tribuf.ys b/tests/arch/machxo2/tribuf.ys
new file mode 100644
index 000000000..9c00a8bcf
--- /dev/null
+++ b/tests/arch/machxo2/tribuf.ys
@@ -0,0 +1,10 @@
+read_verilog ../common/tribuf.v
+hierarchy -top tristate
+proc
+flatten
+equiv_opt -assert -map +/machxo2/cells_sim.v synth_machxo2 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd tristate # Constrain all select calls below inside the top module
+select -assert-count 3 t:FACADE_IO
+select -assert-count 1 t:$not
+select -assert-none t:FACADE_IO t:$not %% t:* %D
diff --git a/tests/sat/bug2595.ys b/tests/sat/bug2595.ys
new file mode 100644
index 000000000..f668fd747
--- /dev/null
+++ b/tests/sat/bug2595.ys
@@ -0,0 +1,18 @@
+read_ilang <<EOT
+module \top
+ wire input 3 \A
+ wire width 2 input 2 \B
+ wire width 2 input 1 \S
+ wire \Y
+ cell $pmux \my_pmux
+ parameter signed \S_WIDTH 2
+ parameter signed \WIDTH 1
+ connect \A \A
+ connect \B \B
+ connect \S \S
+ connect \Y \Y
+ end
+end
+EOT
+
+assertpmux
diff --git a/tests/various/fib_tern.v b/tests/various/fib_tern.v
new file mode 100644
index 000000000..fefde74ce
--- /dev/null
+++ b/tests/various/fib_tern.v
@@ -0,0 +1,70 @@
+module gate(
+ off, fib0, fib1, fib2, fib3, fib4, fib5, fib6, fib7, fib8, fib9
+);
+ input wire signed [31:0] off;
+
+ function automatic blah(
+ input x
+ );
+ blah = x;
+ endfunction
+
+ function automatic integer fib(
+ input integer k
+ );
+ fib = k == 0
+ ? 0
+ : k == 1
+ ? 1
+ : fib(k - 1) + fib(k - 2);
+ endfunction
+
+ function automatic integer fib_wrap(
+ input integer k,
+ output integer o
+ );
+ o = off + fib(k);
+ endfunction
+
+ output integer fib0;
+ output integer fib1;
+ output integer fib2;
+ output integer fib3;
+ output integer fib4;
+ output integer fib5;
+ output integer fib6;
+ output integer fib7;
+ output integer fib8;
+ output integer fib9;
+
+ initial begin : blk
+ integer unused;
+ unused = fib_wrap(0, fib0);
+ unused = fib_wrap(1, fib1);
+ unused = fib_wrap(2, fib2);
+ unused = fib_wrap(3, fib3);
+ unused = fib_wrap(4, fib4);
+ unused = fib_wrap(5, fib5);
+ unused = fib_wrap(6, fib6);
+ unused = fib_wrap(7, fib7);
+ unused = fib_wrap(8, fib8);
+ unused = fib_wrap(9, fib9);
+ end
+endmodule
+
+module gold(
+ off, fib0, fib1, fib2, fib3, fib4, fib5, fib6, fib7, fib8, fib9
+);
+ input wire signed [31:0] off;
+
+ output integer fib0 = off + 0;
+ output integer fib1 = off + 1;
+ output integer fib2 = off + 1;
+ output integer fib3 = off + 2;
+ output integer fib4 = off + 3;
+ output integer fib5 = off + 5;
+ output integer fib6 = off + 8;
+ output integer fib7 = off + 13;
+ output integer fib8 = off + 21;
+ output integer fib9 = off + 34;
+endmodule
diff --git a/tests/various/fib_tern.ys b/tests/various/fib_tern.ys
new file mode 100644
index 000000000..e5bf186e1
--- /dev/null
+++ b/tests/various/fib_tern.ys
@@ -0,0 +1,6 @@
+read_verilog fib_tern.v
+hierarchy
+proc
+equiv_make gold gate equiv
+equiv_simple
+equiv_status -assert
diff --git a/tests/verilog/macro_unapplied.ys b/tests/verilog/macro_unapplied.ys
new file mode 100644
index 000000000..81eb10b8b
--- /dev/null
+++ b/tests/verilog/macro_unapplied.ys
@@ -0,0 +1,17 @@
+logger -expect-no-warnings
+read_verilog -sv <<EOT
+`define MACRO(a = 1, b = 2) initial $display("MACRO(a = %d, b = %d)", a, b)
+module top;
+ `MACRO();
+endmodule
+EOT
+
+design -reset
+
+logger -expect error "Expected to find '\(' to begin macro arguments for 'MACRO', but instead found ';'" 1
+read_verilog -sv <<EOT
+`define MACRO(a = 1, b = 2) initial $display("MACRO(a = %d, b = %d)", a, b)
+module top;
+ `MACRO;
+endmodule
+EOT
diff --git a/tests/verilog/macro_unapplied_newline.ys b/tests/verilog/macro_unapplied_newline.ys
new file mode 100644
index 000000000..a3f88d5b4
--- /dev/null
+++ b/tests/verilog/macro_unapplied_newline.ys
@@ -0,0 +1,5 @@
+logger -expect error "Expected to find '\(' to begin macro arguments for 'foo', but instead found '\\x0a'" 1
+read_verilog -sv <<EOT
+`define foo(a=1) (a)
+`foo
+EOT