Add tests, perform more transformations
diff --git a/passes/pmgen/xilinx_dff.cc b/passes/pmgen/xilinx_dff.cc
index bf0c735..c09b272 100644
--- a/passes/pmgen/xilinx_dff.cc
+++ b/passes/pmgen/xilinx_dff.cc
@@ -54,7 +54,8 @@
 
 		for (auto module : design->selected_modules()) {
 			xilinx_dff_pm pm(module, module->selected_cells());
-			pm.run_xilinx_dff();
+			pm.run_xilinx_r();
+			pm.run_xilinx_ce();
 		}
 	}
 } XilinxDffPass;
diff --git a/passes/pmgen/xilinx_dff.pmg b/passes/pmgen/xilinx_dff.pmg
index 4de25c3..9ca8731 100644
--- a/passes/pmgen/xilinx_dff.pmg
+++ b/passes/pmgen/xilinx_dff.pmg
@@ -1,9 +1,10 @@
-pattern xilinx_dff
+pattern xilinx_r
 
 match fd
-	select fd->type.in(\FDRE)
+	select fd->type.in(\FDRE, \FDRE_1)
 	select port(fd, \R).is_fully_zero()
 	select port(fd, \CE).is_fully_ones()
+	select !param(fd, \IS_R_INVERTED, State::S0).as_bool() /* TODO */
 endmatch
 
 match lut
@@ -12,15 +13,73 @@
 endmatch
 
 code
-	if (lut->type == \LUT2) {
-		if (param(lut, \INIT) == Const::from_string("0010")) {
-			fd->setPort(\D, port(lut, \I0));
-			fd->setPort(\R, port(lut, \I1));
-		}
-		else if (param(lut, \INIT) == Const::from_string("0100")) {
-			fd->setPort(\R, port(lut, \I0));
-			fd->setPort(\D, port(lut, \I1));
-		}
+	if (param(lut, \INIT) == Const::from_string("0010")) {
+		fd->setPort(\D, port(lut, \I0));
+		fd->setPort(\R, port(lut, \I1));
 	}
-	else log_abort();
+	else if (param(lut, \INIT) == Const::from_string("0100")) {
+		fd->setPort(\R, port(lut, \I0));
+		fd->setPort(\D, port(lut, \I1));
+	}
+	else if (param(lut, \INIT) == Const::from_string("1110")) {
+		if (fd->type == \FDRE) {
+			fd->type = \FDSE;
+			fd->unsetParam(\IS_R_INVERTED);
+			fd->setParam(\IS_S_INVERTED, State::S0);
+		}
+		else if (fd->type == \FDRE_1) {
+			fd->type = \FDSE_1;
+		}
+		else log_abort();
+		fd->setPort(\D, port(lut, \I0));
+		fd->setPort(\S, port(lut, \I1));
+		fd->unsetParam(\IS_R_INVERTED);
+		fd->unsetPort(\R);
+	}
+endcode
+
+
+pattern xilinx_ce
+
+match fd
+	select fd->type.in(\FDRE, \FDRE_1, \FDSE, \FDSE_1, \FDCE, \FDCE_1, \FDPE, \FDPE_1)
+	select port(fd, \CE).is_fully_ones()
+endmatch
+
+match lut
+	select lut->type.in(\LUT3)
+	index <SigSpec> port(lut, \O) === port(fd, \D)
+endmatch
+
+code
+	if (param(lut, \INIT) == Const::from_string("11011000")) {
+		fd->setPort(\CE, port(lut, \I0));
+		fd->setPort(\D, port(lut, \I1));
+		fd->setPort(\Q, port(lut, \I2));
+	}
+	else if (param(lut, \INIT) == Const::from_string("11100100")) {
+		fd->setPort(\CE, port(lut, \I0));
+		fd->setPort(\Q, port(lut, \I1));
+		fd->setPort(\D, port(lut, \I2));
+	}
+	else if (param(lut, \INIT) == Const::from_string("11100010")) {
+		fd->setPort(\Q, port(lut, \I0));
+		fd->setPort(\CE, port(lut, \I1));
+		fd->setPort(\D, port(lut, \I2));
+	}
+	else if (param(lut, \INIT) == Const::from_string("11001010")) {
+		fd->setPort(\Q, port(lut, \I0));
+		fd->setPort(\D, port(lut, \I1));
+		fd->setPort(\CE, port(lut, \I2));
+	}
+	else if (param(lut, \INIT) == Const::from_string("10101100")) {
+		fd->setPort(\D, port(lut, \I0));
+		fd->setPort(\Q, port(lut, \I1));
+		fd->setPort(\CE, port(lut, \I2));
+	}
+	else if (param(lut, \INIT) == Const::from_string("10111000")) {
+		fd->setPort(\D, port(lut, \I0));
+		fd->setPort(\CE, port(lut, \I1));
+		fd->setPort(\Q, port(lut, \I2));
+	}
 endcode
diff --git a/tests/arch/xilinx/xilinx_dff.ys b/tests/arch/xilinx/xilinx_dff.ys
new file mode 100644
index 0000000..695c28b
--- /dev/null
+++ b/tests/arch/xilinx/xilinx_dff.ys
@@ -0,0 +1,62 @@
+read_verilog <<EOT
+module top(input c, d, r, output reg q);
+always @(posedge c)
+    if (r) q <= 1'b0;
+    else q <= d;
+endmodule
+EOT
+proc
+equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx
+design -load postopt
+cd top
+select -assert-count 1 t:FDRE
+select -assert-none t:* t:FDRE %d t:BUFG %d
+
+
+design -reset
+read_verilog <<EOT
+module top(input c, d, r, e, output reg q);
+always @(posedge c) begin
+    if (e) q <= d;
+    if (r) q <= 1'b0;
+end
+endmodule
+EOT
+proc
+equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx
+design -load postopt
+cd top
+select -assert-count 1 t:FDRE
+select -assert-none t:* t:FDRE %d t:BUFG %d
+
+
+design -reset
+read_verilog <<EOT
+module top(input c, d, s, output reg q);
+always @(posedge c)
+    if (s) q <= 1'b1;
+    else q <= d;
+endmodule
+EOT
+proc
+equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx
+design -load postopt
+cd top
+select -assert-count 1 t:FDSE
+select -assert-none t:* t:FDSE %d t:BUFG %d
+
+
+design -reset
+read_verilog <<EOT
+module top(input c, d, e, clr, output reg q);
+always @(posedge c or posedge clr)
+    if (clr) q <= 1'b0;
+    else if (e) q <= d;
+endmodule
+EOT
+proc
+equiv_opt -assert -map +/xilinx/cells_sim.v -multiclock synth_xilinx
+design -load postopt
+cd top
+select -assert-count 1 t:FDCE
+select -assert-none t:* t:FDCE %d t:BUFG %d