Params plugin: Add PLL test

Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
diff --git a/Makefile b/Makefile
index aff09e8..fe3f92d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-PLUGIN_LIST := fasm xdc
+PLUGIN_LIST := fasm xdc params
 PLUGINS := $(foreach plugin,$(PLUGIN_LIST),$(plugin).so)
 PLUGINS_INSTALL := $(foreach plugin,$(PLUGIN_LIST),install_$(plugin))
 PLUGINS_CLEAN := $(foreach plugin,$(PLUGIN_LIST),clean_$(plugin))
diff --git a/params-plugin/tests/Makefile b/params-plugin/tests/Makefile
new file mode 100644
index 0000000..2ab247e
--- /dev/null
+++ b/params-plugin/tests/Makefile
@@ -0,0 +1,39 @@
+TESTS = pll
+
+pll_verify = $(call compare_json,pll)
+
+all: $(TESTS)
+
+compare_json = python compare_output_json.py --json $(1)/$(1).json --golden $(1)/$(1).golden.json
+
+define test_tpl =
+$(1): $(1)/$(1).json
+	$$($(1)_verify)
+	RETVAL=$$$$? ; \
+	if [ $$$$RETVAL -eq 0 ]; then \
+		echo "$(1) PASS"; \
+		true; \
+	else \
+		echo "$(1) FAIL"; \
+		false; \
+	fi
+
+$(1)/$(1).json: $(1)/$(1).v
+	cd $(1); \
+	PART_JSON=../xc7a35tcsg324-1.json \
+	OUT_JSON=$(1).json \
+	INPUT_XDC_FILE=$(1).xdc \
+	yosys -p "tcl $(1).tcl" $(1).v -l yosys.log
+
+update_$(1): $(1)/$(1).json
+	@python compare_output_json.py --json $$< --golden $(1)/$(1).golden.json --update
+
+endef
+
+$(foreach test,$(TESTS),$(eval $(call test_tpl,$(test))))
+
+update: $(foreach test,$(TESTS),update_$(test))
+
+
+clean:
+	rm -rf $(foreach test,$(TESTS),$(test)/$(test).json $(test)/$(test).eblif $(test)/$(test).txt $(test)/yosys.log)
diff --git a/params-plugin/tests/compare_output_json.py b/params-plugin/tests/compare_output_json.py
new file mode 100644
index 0000000..3b437e8
--- /dev/null
+++ b/params-plugin/tests/compare_output_json.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+"""
+
+This script extracts the top module cells and their corresponding parameters
+from json files produced by Yosys.
+The return code of this script is used to check if the output is equivalent.
+"""
+
+import sys
+import json
+import argparse
+
+parameters = ["CLKFBOUT_CLKOUT1_HIGH_TIME"]
+
+def read_cells(json_file):
+    with open(json_file) as f:
+        data = json.load(f)
+    f.close()
+    cells = data['modules']['top']['cells']
+    cells_parameters = dict()
+    for cell, opts in cells.items():
+        attributes = opts['parameters']
+        if len(attributes.keys()):
+            if any([x in parameters for x in attributes.keys()]):
+                cells_parameters[cell] = attributes
+    return cells_parameters
+
+
+def main(args):
+    cells = read_cells(args.json)
+    if args.update:
+        with open(args.golden, 'w') as f:
+            json.dump(cells, f, indent=2)
+    else:
+        with open(args.golden) as f:
+            cells_golden = json.load(f)
+            if cells == cells_golden:
+                exit(0)
+            else:
+                print(json.dumps(cells, indent=4))
+                json.dump(cells, open(args.json + ".fail", 'w'), indent=2)
+                print("VS")
+                print(json.dumps(cells_golden, indent=4))
+                exit(1)
+    f.close()
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--json', help = 'JSON to compare', required = True)
+    parser.add_argument('--golden', help = 'Golden JSON file', required = True)
+    parser.add_argument('--update', action = 'store_true', help = 'Update golden reference')
+    args = parser.parse_args()
+    main(args)
diff --git a/params-plugin/tests/pll/pll.golden.json b/params-plugin/tests/pll/pll.golden.json
new file mode 100644
index 0000000..e3bf97d
--- /dev/null
+++ b/params-plugin/tests/pll/pll.golden.json
@@ -0,0 +1,65 @@
+{
+  "PLLE2_ADV": {
+    "CLKFBOUT_CLKOUT1_HIGH_TIME": "000110",
+    "CLKFBOUT_CLKOUT1_LOW_TIME": "000110",
+    "CLKFBOUT_CLKOUT1_OUTPUT_ENABLE": "1",
+    "CLKFBOUT_CLKOUT1_PHASE_MUX": "000",
+    "CLKFBOUT_CLKOUT2_DELAY_TIME": "000000",
+    "CLKFBOUT_CLKOUT2_EDGE": "0",
+    "CLKFBOUT_CLKOUT2_NO_COUNT": "0",
+    "CLKOUT0_CLKOUT1_HIGH_TIME": "001010",
+    "CLKOUT0_CLKOUT1_LOW_TIME": "001010",
+    "CLKOUT0_CLKOUT1_OUTPUT_ENABLE": "1",
+    "CLKOUT0_CLKOUT1_PHASE_MUX": "000",
+    "CLKOUT0_CLKOUT2_DELAY_TIME": "000000",
+    "CLKOUT0_CLKOUT2_EDGE": "0",
+    "CLKOUT0_CLKOUT2_NO_COUNT": "0",
+    "CLKOUT1_CLKOUT1_HIGH_TIME": "000010",
+    "CLKOUT1_CLKOUT1_LOW_TIME": "000011",
+    "CLKOUT1_CLKOUT1_OUTPUT_ENABLE": "1",
+    "CLKOUT1_CLKOUT1_PHASE_MUX": "000",
+    "CLKOUT1_CLKOUT2_DELAY_TIME": "000000",
+    "CLKOUT1_CLKOUT2_EDGE": "1",
+    "CLKOUT1_CLKOUT2_NO_COUNT": "0",
+    "CLKOUT2_CLKOUT1_HIGH_TIME": "000010",
+    "CLKOUT2_CLKOUT1_LOW_TIME": "000011",
+    "CLKOUT2_CLKOUT1_OUTPUT_ENABLE": "0",
+    "CLKOUT2_CLKOUT1_PHASE_MUX": "010",
+    "CLKOUT2_CLKOUT2_DELAY_TIME": "000001",
+    "CLKOUT2_CLKOUT2_EDGE": "1",
+    "CLKOUT2_CLKOUT2_NO_COUNT": "0",
+    "CLKOUT3_CLKOUT1_HIGH_TIME": "000011",
+    "CLKOUT3_CLKOUT1_LOW_TIME": "000011",
+    "CLKOUT3_CLKOUT1_OUTPUT_ENABLE": "0",
+    "CLKOUT3_CLKOUT1_PHASE_MUX": "000",
+    "CLKOUT3_CLKOUT2_DELAY_TIME": "000000",
+    "CLKOUT3_CLKOUT2_EDGE": "0",
+    "CLKOUT3_CLKOUT2_NO_COUNT": "0",
+    "CLKOUT4_CLKOUT1_HIGH_TIME": "000001",
+    "CLKOUT4_CLKOUT1_LOW_TIME": "000001",
+    "CLKOUT4_CLKOUT1_OUTPUT_ENABLE": "0",
+    "CLKOUT4_CLKOUT1_PHASE_MUX": "000",
+    "CLKOUT4_CLKOUT2_DELAY_TIME": "000000",
+    "CLKOUT4_CLKOUT2_EDGE": "0",
+    "CLKOUT4_CLKOUT2_NO_COUNT": "1",
+    "CLKOUT5_CLKOUT1_HIGH_TIME": "000001",
+    "CLKOUT5_CLKOUT1_LOW_TIME": "000001",
+    "CLKOUT5_CLKOUT1_OUTPUT_ENABLE": "0",
+    "CLKOUT5_CLKOUT1_PHASE_MUX": "000",
+    "CLKOUT5_CLKOUT2_DELAY_TIME": "000000",
+    "CLKOUT5_CLKOUT2_EDGE": "0",
+    "CLKOUT5_CLKOUT2_NO_COUNT": "1",
+    "DIVCLK_DIVCLK_EDGE": "0",
+    "DIVCLK_DIVCLK_HIGH_TIME": "000001",
+    "DIVCLK_DIVCLK_LOW_TIME": "000001",
+    "DIVCLK_DIVCLK_NO_COUNT": "1",
+    "FILTREG1_RESERVED": "000000001000",
+    "INV_CLKINSEL": "0",
+    "LKTABLE": "1111111111110011100111111010010000000001",
+    "LOCKREG3_RESERVED": "1",
+    "STARTUP_WAIT": "0",
+    "TABLE": "1111110100",
+    "ZINV_PWRDWN": "1",
+    "ZINV_RST": "0"
+  }
+}
\ No newline at end of file
diff --git a/params-plugin/tests/pll/pll.tcl b/params-plugin/tests/pll/pll.tcl
new file mode 100644
index 0000000..c2238d1
--- /dev/null
+++ b/params-plugin/tests/pll/pll.tcl
@@ -0,0 +1,31 @@
+yosys -import
+plugin -i xdc
+plugin -i params
+#Import the commands from the plugins to the tcl interpreter
+yosys -import
+
+read_verilog -specify -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v
+read_verilog -lib +/xilinx/cells_xtra.v
+hierarchy -check -auto-top
+set phase [get_param CLKOUT2_PHASE top/PLLE2_ADV]
+puts "Phase before: $phase"
+setparam -set CLKOUT2_PHASE [expr $phase * 1000] top/PLLE2_ADV
+puts "Phase after: [get_param CLKOUT2_PHASE top/PLLE2_ADV]"
+# Start flow after library reading
+synth_xilinx -vpr -flatten -abc9 -nosrl -noclkbuf -nodsp -iopad -run prepare:check
+
+# Map Xilinx tech library to 7-series VPR tech library.
+read_verilog -lib ../techmaps/cells_sim.v
+techmap -map  ../techmaps/cells_map.v
+
+# opt_expr -undriven makes sure all nets are driven, if only by the $undef
+# net.
+opt_expr -undriven
+opt_clean
+
+setundef -zero -params
+stat
+
+# Write the design in JSON format.
+write_json $::env(OUT_JSON)
+write_blif -attr -param -cname -conn pll.eblif
diff --git a/params-plugin/tests/pll/pll.v b/params-plugin/tests/pll/pll.v
new file mode 100644
index 0000000..a1eeb38
--- /dev/null
+++ b/params-plugin/tests/pll/pll.v
@@ -0,0 +1,38 @@
+module top(
+	(* dont_touch = "true" *)	input clk100,
+	input cpu_reset,
+	output [2:0] led
+);
+
+wire [2:0] led;
+wire builder_pll_fb;
+
+assign led[0] = main_locked;
+assign led[1] = main_clkout0;
+assign led[2] = main_clkout1;
+
+PLLE2_ADV #(
+	.CLKFBOUT_MULT(4'd12),
+	.CLKIN1_PERIOD(10.0),
+	.CLKOUT0_DIVIDE(5'd20),
+	.CLKOUT0_PHASE(1'd0),
+	.CLKOUT1_DIVIDE(3'd5),
+	.CLKOUT1_PHASE(1'd0),
+	.CLKOUT2_DIVIDE(3'd5),
+	.CLKOUT2_PHASE(7'd90),
+	.CLKOUT3_DIVIDE(3'd6),
+	.CLKOUT3_PHASE(1'd0),
+	.DIVCLK_DIVIDE(1'd1),
+	.REF_JITTER1(0.01),
+	.STARTUP_WAIT("FALSE")
+) PLLE2_ADV (
+	.CLKFBIN(builder_pll_fb),
+	.CLKIN1(clk100),
+	.RST(cpu_reset),
+	.CLKFBOUT(builder_pll_fb),
+	.CLKOUT0(main_clkout0),
+	.CLKOUT1(main_clkout1),
+	.LOCKED(main_locked)
+);
+
+endmodule
diff --git a/params-plugin/tests/pll/pll.xdc b/params-plugin/tests/pll/pll.xdc
new file mode 100644
index 0000000..2f4eba3
--- /dev/null
+++ b/params-plugin/tests/pll/pll.xdc
@@ -0,0 +1,9 @@
+# ## clk100:0
+set_property LOC E3 [get_ports clk100]
+set_property IOSTANDARD LVCMOS33 [get_ports clk100]
+# ## cpu_reset:0
+set_property LOC C2 [get_ports cpu_reset]
+set_property IOSTANDARD LVCMOS33 [get_ports cpu_reset]
+set_property LOC H5 [get_ports {led[0]}]
+set_property LOC J5 [get_ports {led[1]}]
+set_property LOC T9 [get_ports {led[2]}]
diff --git a/params-plugin/tests/techmaps/cells_map.v b/params-plugin/tests/techmaps/cells_map.v
new file mode 100644
index 0000000..772f588
--- /dev/null
+++ b/params-plugin/tests/techmaps/cells_map.v
@@ -0,0 +1,866 @@
+// ============================================================================
+// CMT
+
+`define PLL_FRAC_PRECISION  10
+`define PLL_FIXED_WIDTH     32
+
+// Rounds a fixed point number to a given precision
+function [`PLL_FIXED_WIDTH:1] pll_round_frac
+(
+input [`PLL_FIXED_WIDTH:1] decimal,
+input [`PLL_FIXED_WIDTH:1] precision
+);
+
+ if (decimal[(`PLL_FRAC_PRECISION - precision)] == 1'b1) begin
+   pll_round_frac = decimal + (1'b1 << (`PLL_FRAC_PRECISION - precision));
+ end else begin
+   pll_round_frac = decimal;
+ end
+
+endfunction
+
+// Computes content of the PLLs divider registers
+function [13:0] pll_divider_regs
+(
+input [ 7:0] divide,      // Max divide is 128
+input [31:0] duty_cycle   // Duty cycle is multiplied by 100,000
+);
+
+  reg [`PLL_FIXED_WIDTH:1] duty_cycle_fix;
+  reg [`PLL_FIXED_WIDTH:1] duty_cycle_min;
+  reg [`PLL_FIXED_WIDTH:1] duty_cycle_max;
+
+  reg [6:0] high_time;
+  reg [6:0] low_time;
+  reg       w_edge;
+  reg       no_count;
+
+  reg [`PLL_FIXED_WIDTH:1] temp;
+
+  if (divide >= 64) begin
+      duty_cycle_min = ((divide - 64) * 100_000) / divide;
+      duty_cycle_max = (645 / divide) * 100_00;
+      if (duty_cycle > duty_cycle_max)
+        duty_cycle = duty_cycle_max;
+      if (duty_cycle < duty_cycle_min)
+        duty_cycle = duty_cycle_min;
+  end
+
+  duty_cycle_fix = (duty_cycle << `PLL_FRAC_PRECISION) / 100_000;
+
+  if (divide == 7'h01) begin
+      high_time = 7'h01;
+      w_edge    = 1'b0;
+      low_time  = 7'h01;
+      no_count  = 1'b1;
+
+  end else begin
+      temp = pll_round_frac(duty_cycle_fix*divide, 1);
+
+      high_time = temp[`PLL_FRAC_PRECISION+7:`PLL_FRAC_PRECISION+1];
+      w_edge    = temp[`PLL_FRAC_PRECISION];
+
+      if (high_time == 7'h00) begin
+         high_time = 7'h01;
+         w_edge    = 1'b0;
+      end
+
+      if (high_time == divide) begin
+         high_time = divide - 1;
+         w_edge    = 1'b1;
+      end
+
+      low_time = divide - high_time;
+      no_count = 1'b0;
+  end
+
+  pll_divider_regs = {w_edge, no_count, high_time[5:0], low_time[5:0]};
+endfunction
+
+// Computes the PLLs phase shift registers
+function [10:0] pll_phase_regs
+(
+input        [ 7:0] divide,
+input signed [31:0] phase
+);
+
+  reg [`PLL_FIXED_WIDTH:1] phase_in_cycles;
+  reg [`PLL_FIXED_WIDTH:1] phase_fixed;
+  reg [1:0] mx;
+  reg [5:0] delay_time;
+  reg [2:0] phase_mux;
+
+  reg [`PLL_FIXED_WIDTH:1] temp;
+
+  if(phase < 0) begin
+      phase_fixed = ((phase + 360000) << `PLL_FRAC_PRECISION) / 1000;
+  end else begin
+      phase_fixed = (phase << `PLL_FRAC_PRECISION) / 1000;
+  end
+
+ phase_in_cycles = (phase_fixed * divide) / 360;
+ temp = pll_round_frac(phase_in_cycles, 3);
+
+ mx         =  2'b00;
+ phase_mux  =  temp[`PLL_FRAC_PRECISION:`PLL_FRAC_PRECISION-2];
+ delay_time =  temp[`PLL_FRAC_PRECISION+6:`PLL_FRAC_PRECISION+1];
+
+ pll_phase_regs = {mx, phase_mux, delay_time};
+endfunction
+
+
+// Given PLL/MMCM divide, duty_cycle and phase calculates content of the
+// CLKREG1 and CLKREG2.
+function [37:0] pll_clkregs
+(
+input [7:0]         divide,     // Max divide is 128
+input [31:0]        duty_cycle, // Multiplied by 100,000
+input signed [31:0] phase       // Phase is given in degrees (-360,000 to 360,000)
+);
+
+  reg [13:0] pll_div;   // EDGE, NO_COUNT, HIGH_TIME[5:0], LOW_TIME[5:0]
+  reg [10:0] pll_phase; // MX, PHASE_MUX[2:0], DELAY_TIME[5:0]
+
+  pll_div = pll_divider_regs(divide, duty_cycle);
+  pll_phase = pll_phase_regs(divide, phase);
+
+  pll_clkregs = {
+    // CLKREG2: RESERVED[6:0], MX[1:0], EDGE, NO_COUNT, DELAY_TIME[5:0]
+    6'h00, pll_phase[10:9], pll_div[13:12], pll_phase[5:0],
+    // CLKREG1: PHASE_MUX[3:0], RESERVED, HIGH_TIME[5:0], LOW_TIME[5:0]
+    pll_phase[8:6], 1'b0, pll_div[11:0]
+  };
+
+endfunction
+
+// This function takes the divide value and outputs the necessary lock values
+function [39:0] pll_lktable_lookup
+(
+input [6:0] divide // Max divide is 64
+);
+
+  reg [2559:0] lookup;
+
+  lookup = {
+    // This table is composed of:
+    // LockRefDly_LockFBDly_LockCnt_LockSatHigh_UnlockCnt
+    40'b00110_00110_1111101000_1111101001_0000000001,
+    40'b00110_00110_1111101000_1111101001_0000000001,
+    40'b01000_01000_1111101000_1111101001_0000000001,
+    40'b01011_01011_1111101000_1111101001_0000000001,
+    40'b01110_01110_1111101000_1111101001_0000000001,
+    40'b10001_10001_1111101000_1111101001_0000000001,
+    40'b10011_10011_1111101000_1111101001_0000000001,
+    40'b10110_10110_1111101000_1111101001_0000000001,
+    40'b11001_11001_1111101000_1111101001_0000000001,
+    40'b11100_11100_1111101000_1111101001_0000000001,
+    40'b11111_11111_1110000100_1111101001_0000000001,
+    40'b11111_11111_1100111001_1111101001_0000000001,
+    40'b11111_11111_1011101110_1111101001_0000000001,
+    40'b11111_11111_1010111100_1111101001_0000000001,
+    40'b11111_11111_1010001010_1111101001_0000000001,
+    40'b11111_11111_1001110001_1111101001_0000000001,
+    40'b11111_11111_1000111111_1111101001_0000000001,
+    40'b11111_11111_1000100110_1111101001_0000000001,
+    40'b11111_11111_1000001101_1111101001_0000000001,
+    40'b11111_11111_0111110100_1111101001_0000000001,
+    40'b11111_11111_0111011011_1111101001_0000000001,
+    40'b11111_11111_0111000010_1111101001_0000000001,
+    40'b11111_11111_0110101001_1111101001_0000000001,
+    40'b11111_11111_0110010000_1111101001_0000000001,
+    40'b11111_11111_0110010000_1111101001_0000000001,
+    40'b11111_11111_0101110111_1111101001_0000000001,
+    40'b11111_11111_0101011110_1111101001_0000000001,
+    40'b11111_11111_0101011110_1111101001_0000000001,
+    40'b11111_11111_0101000101_1111101001_0000000001,
+    40'b11111_11111_0101000101_1111101001_0000000001,
+    40'b11111_11111_0100101100_1111101001_0000000001,
+    40'b11111_11111_0100101100_1111101001_0000000001,
+    40'b11111_11111_0100101100_1111101001_0000000001,
+    40'b11111_11111_0100010011_1111101001_0000000001,
+    40'b11111_11111_0100010011_1111101001_0000000001,
+    40'b11111_11111_0100010011_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001,
+    40'b11111_11111_0011111010_1111101001_0000000001
+    };
+
+  pll_lktable_lookup = lookup[ ((64-divide)*40) +: 40];
+endfunction
+
+// This function takes the divide value and the bandwidth setting of the PLL
+// and outputs the digital filter settings necessary.
+function [9:0] pll_table_lookup
+(
+input [6:0]   divide, // Max divide is 64
+input [8*9:0] BANDWIDTH
+);
+
+  reg [639:0] lookup_low;
+  reg [639:0] lookup_high;
+  reg [639:0] lookup_optimized;
+
+  reg [9:0] lookup_entry;
+
+  lookup_low = {
+    // CP_RES_LFHF
+    10'b0010_1111_00,
+    10'b0010_1111_00,
+    10'b0010_0111_00,
+    10'b0010_1101_00,
+    10'b0010_0101_00,
+    10'b0010_0101_00,
+    10'b0010_1001_00,
+    10'b0010_1110_00,
+    10'b0010_1110_00,
+    10'b0010_0001_00,
+    10'b0010_0001_00,
+    10'b0010_0110_00,
+    10'b0010_0110_00,
+    10'b0010_0110_00,
+    10'b0010_0110_00,
+    10'b0010_1010_00,
+    10'b0010_1010_00,
+    10'b0010_1010_00,
+    10'b0010_1010_00,
+    10'b0010_1100_00,
+    10'b0010_1100_00,
+    10'b0010_1100_00,
+    10'b0010_1100_00,
+    10'b0010_1100_00,
+    10'b0010_1100_00,
+    10'b0010_1100_00,
+    10'b0010_1100_00,
+    10'b0010_1100_00,
+    10'b0010_1100_00,
+    10'b0010_1100_00,
+    10'b0010_0010_00,
+    10'b0010_0010_00,
+    10'b0010_0010_00,
+    10'b0010_0010_00,
+    10'b0010_0010_00,
+    10'b0010_0010_00,
+    10'b0010_0010_00,
+    10'b0010_0010_00,
+    10'b0010_0010_00,
+    10'b0010_0010_00,
+    10'b0011_1100_00,
+    10'b0011_1100_00,
+    10'b0011_1100_00,
+    10'b0011_1100_00,
+    10'b0011_1100_00,
+    10'b0011_1100_00,
+    10'b0011_1100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00
+  };
+
+  lookup_high = {
+    // CP_RES_LFHF
+    10'b0011_0111_00,
+    10'b0011_0111_00,
+    10'b0101_1111_00,
+    10'b0111_1111_00,
+    10'b0111_1011_00,
+    10'b1101_0111_00,
+    10'b1110_1011_00,
+    10'b1110_1101_00,
+    10'b1111_1101_00,
+    10'b1111_0111_00,
+    10'b1111_1011_00,
+    10'b1111_1101_00,
+    10'b1111_0011_00,
+    10'b1110_0101_00,
+    10'b1111_0101_00,
+    10'b1111_0101_00,
+    10'b1111_0101_00,
+    10'b1111_0101_00,
+    10'b0111_0110_00,
+    10'b0111_0110_00,
+    10'b0111_0110_00,
+    10'b0111_0110_00,
+    10'b0101_1100_00,
+    10'b0101_1100_00,
+    10'b0101_1100_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b0100_0010_00,
+    10'b0100_0010_00,
+    10'b0100_0010_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0011_0100_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00
+  };
+
+  lookup_optimized = {
+    // CP_RES_LFHF
+    10'b0011_0111_00,
+    10'b0011_0111_00,
+    10'b0101_1111_00,
+    10'b0111_1111_00,
+    10'b0111_1011_00,
+    10'b1101_0111_00,
+    10'b1110_1011_00,
+    10'b1110_1101_00,
+    10'b1111_1101_00,
+    10'b1111_0111_00,
+    10'b1111_1011_00,
+    10'b1111_1101_00,
+    10'b1111_0011_00,
+    10'b1110_0101_00,
+    10'b1111_0101_00,
+    10'b1111_0101_00,
+    10'b1111_0101_00,
+    10'b1111_0101_00,
+    10'b0111_0110_00,
+    10'b0111_0110_00,
+    10'b0111_0110_00,
+    10'b0111_0110_00,
+    10'b0101_1100_00,
+    10'b0101_1100_00,
+    10'b0101_1100_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b1100_0001_00,
+    10'b0100_0010_00,
+    10'b0100_0010_00,
+    10'b0100_0010_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0011_0100_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0010_1000_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0100_1100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00,
+    10'b0010_0100_00
+  };
+
+  if (BANDWIDTH == "LOW") begin
+    pll_table_lookup = lookup_low[((64-divide)*10) +: 10];
+  end else if (BANDWIDTH == "HIGH") begin
+    pll_table_lookup = lookup_high[((64-divide)*10) +: 10];
+  end else if (BANDWIDTH == "OPTIMIZED") begin
+    pll_table_lookup = lookup_optimized[((64-divide)*10) +: 10];
+  end
+
+endfunction
+
+// ............................................................................
+// IMPORTANT NOTE: Due to lack of support for real type parameters in Yosys
+// the PLL parameters that define duty cycles and phase shifts have to be
+// provided as integers! The DUTY_CYCLE is expressed as % of high time times
+// 1000 whereas the PHASE is expressed in degrees times 1000.
+
+// PLLE2_ADV
+module PLLE2_ADV
+(
+input         CLKFBIN,
+input         CLKIN1,
+input         CLKIN2,
+input         CLKINSEL,
+
+output        CLKFBOUT,
+output        CLKOUT0,
+output        CLKOUT1,
+output        CLKOUT2,
+output        CLKOUT3,
+output        CLKOUT4,
+output        CLKOUT5,
+
+input         PWRDWN,
+input         RST,
+output        LOCKED,
+
+input         DCLK,
+input         DEN,
+input         DWE,
+output        DRDY,
+input  [ 6:0] DADDR,
+input  [15:0] DI,
+output [15:0] DO
+);
+
+  parameter _TECHMAP_CONSTMSK_CLKINSEL_ = 0;
+  parameter _TECHMAP_CONSTVAL_CLKINSEL_ = 0;
+
+  parameter _TECHMAP_CONSTMSK_RST_      = 0;
+  parameter _TECHMAP_CONSTVAL_RST_      = 0;
+  parameter _TECHMAP_CONSTMSK_PWRDWN_   = 0;
+  parameter _TECHMAP_CONSTVAL_PWRDWN_   = 0;
+
+  parameter _TECHMAP_CONSTMSK_CLKFBOUT_ = 0;
+  parameter _TECHMAP_CONSTVAL_CLKFBOUT_ = 0;
+  parameter _TECHMAP_CONSTMSK_CLKOUT0_  = 0;
+  parameter _TECHMAP_CONSTVAL_CLKOUT0_  = 0;
+  parameter _TECHMAP_CONSTMSK_CLKOUT1_  = 0;
+  parameter _TECHMAP_CONSTVAL_CLKOUT1_  = 0;
+  parameter _TECHMAP_CONSTMSK_CLKOUT2_  = 0;
+  parameter _TECHMAP_CONSTVAL_CLKOUT2_  = 0;
+  parameter _TECHMAP_CONSTMSK_CLKOUT3_  = 0;
+  parameter _TECHMAP_CONSTVAL_CLKOUT3_  = 0;
+  parameter _TECHMAP_CONSTMSK_CLKOUT4_  = 0;
+  parameter _TECHMAP_CONSTVAL_CLKOUT4_  = 0;
+  parameter _TECHMAP_CONSTMSK_CLKOUT5_  = 0;
+  parameter _TECHMAP_CONSTVAL_CLKOUT5_  = 0;
+
+  parameter _TECHMAP_CONSTMSK_DCLK_     = 0;
+  parameter _TECHMAP_CONSTVAL_DCLK_     = 0;
+  parameter _TECHMAP_CONSTMSK_DEN_      = 0;
+  parameter _TECHMAP_CONSTVAL_DEN_      = 0;
+  parameter _TECHMAP_CONSTMSK_DWE_      = 0;
+  parameter _TECHMAP_CONSTVAL_DWE_      = 0;
+
+  parameter IS_CLKINSEL_INVERTED = 1'b0;
+  parameter IS_RST_INVERTED = 1'b0;
+  parameter IS_PWRDWN_INVERTED = 1'b0;
+
+  parameter BANDWIDTH = "OPTIMIZED";
+  parameter STARTUP_WAIT = "FALSE";
+  parameter COMPENSATION = "ZHOLD";
+
+  parameter CLKIN1_PERIOD = 0.0;
+  parameter REF_JITTER1 = 0.01;
+  parameter CLKIN2_PERIOD = 0.0;
+  parameter REF_JITTER2 = 0.01;
+
+  parameter [5:0] DIVCLK_DIVIDE = 1;
+
+  parameter [5:0] CLKFBOUT_MULT = 1;
+  parameter CLKFBOUT_PHASE = 0;
+
+  parameter [6:0] CLKOUT0_DIVIDE = 1;
+  parameter CLKOUT0_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT0_PHASE = 0;
+
+  parameter [6:0] CLKOUT1_DIVIDE = 1;
+  parameter CLKOUT1_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT1_PHASE = 0;
+
+  parameter [6:0] CLKOUT2_DIVIDE = 1;
+  parameter CLKOUT2_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT2_PHASE = 0;
+
+  parameter [6:0] CLKOUT3_DIVIDE = 1;
+  parameter CLKOUT3_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT3_PHASE = 0;
+
+  parameter [6:0] CLKOUT4_DIVIDE = 1;
+  parameter CLKOUT4_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT4_PHASE = 0;
+
+  parameter [6:0] CLKOUT5_DIVIDE = 1;
+  parameter CLKOUT5_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT5_PHASE = 0;
+
+  // Compute PLL's registers content
+  localparam CLKFBOUT_REGS = pll_clkregs(CLKFBOUT_MULT, 50000, CLKFBOUT_PHASE);
+  localparam DIVCLK_REGS   = pll_clkregs(DIVCLK_DIVIDE, 50000, 0);
+
+  localparam CLKOUT0_REGS  = pll_clkregs(CLKOUT0_DIVIDE, CLKOUT0_DUTY_CYCLE, CLKOUT0_PHASE);
+  localparam CLKOUT1_REGS  = pll_clkregs(CLKOUT1_DIVIDE, CLKOUT1_DUTY_CYCLE, CLKOUT1_PHASE);
+  localparam CLKOUT2_REGS  = pll_clkregs(CLKOUT2_DIVIDE, CLKOUT2_DUTY_CYCLE, CLKOUT2_PHASE);
+  localparam CLKOUT3_REGS  = pll_clkregs(CLKOUT3_DIVIDE, CLKOUT3_DUTY_CYCLE, CLKOUT3_PHASE);
+  localparam CLKOUT4_REGS  = pll_clkregs(CLKOUT4_DIVIDE, CLKOUT4_DUTY_CYCLE, CLKOUT4_PHASE);
+  localparam CLKOUT5_REGS  = pll_clkregs(CLKOUT5_DIVIDE, CLKOUT5_DUTY_CYCLE, CLKOUT5_PHASE);
+
+  // Handle inputs that should have certain logic levels when left unconnected
+  generate if (_TECHMAP_CONSTMSK_CLKINSEL_ == 1) begin
+    localparam INV_CLKINSEL = !_TECHMAP_CONSTVAL_CLKINSEL_;
+    wire clkinsel = 1'b1;
+  end else if (_TECHMAP_CONSTVAL_CLKINSEL_ == 0) begin
+    localparam INV_CLKINSEL = IS_CLKINSEL_INVERTED;
+    wire clkinsel = 1'b1;
+  end else begin
+    localparam INV_CLKINSEL = IS_CLKINSEL_INVERTED;
+    wire clkinsel = CLKINSEL;
+  end endgenerate
+
+  generate if (_TECHMAP_CONSTMSK_PWRDWN_ == 1) begin
+    localparam INV_PWRDWN =  !_TECHMAP_CONSTVAL_PWRDWN_;
+    wire pwrdwn = 1'b1;
+  end else if (_TECHMAP_CONSTVAL_PWRDWN_ == 0) begin
+    localparam INV_PWRDWN = ~IS_PWRDWN_INVERTED;
+    wire pwrdwn = 1'b1;
+  end else begin
+    localparam INV_PWRDWN =  IS_PWRDWN_INVERTED;
+    wire pwrdwn = PWRDWN;
+  end endgenerate
+
+  generate if (_TECHMAP_CONSTMSK_RST_ == 1) begin
+    localparam INV_RST =  !_TECHMAP_CONSTVAL_PWRDWN_;
+    wire rst = 1'b1;
+  end else if (_TECHMAP_CONSTVAL_RST_ == 0) begin
+    localparam INV_RST = ~IS_RST_INVERTED;
+    wire rst = 1'b1;
+  end else begin
+    localparam INV_RST =  IS_RST_INVERTED;
+    wire rst = RST;
+  end endgenerate
+
+  generate if (_TECHMAP_CONSTMSK_DCLK_ == 1)
+    wire dclk = _TECHMAP_CONSTVAL_DCLK_;
+  else if (_TECHMAP_CONSTVAL_DCLK_ == 0)
+    wire dclk = 1'b0;
+  else
+    wire dclk = DCLK;
+  endgenerate
+  
+  generate if (_TECHMAP_CONSTMSK_DEN_ == 1)
+    wire den = _TECHMAP_CONSTVAL_DEN_;
+  else if (_TECHMAP_CONSTVAL_DEN_ == 0)
+    wire den = 1'b0;
+  else
+    wire den = DEN;
+  endgenerate
+
+  generate if (_TECHMAP_CONSTMSK_DWE_ == 1)
+    wire dwe = _TECHMAP_CONSTVAL_DWE_;
+  else if (_TECHMAP_CONSTVAL_DWE_ == 0)
+    wire dwe = 1'b0;
+  else
+    wire dwe = DWE;
+  endgenerate
+
+  // The substituted cell
+  PLLE2_ADV_VPR #
+  (
+  // Inverters
+  .INV_CLKINSEL(INV_CLKINSEL),
+  .ZINV_PWRDWN (INV_PWRDWN),
+  .ZINV_RST    (INV_RST),
+
+  // Straight mapped parameters
+  .STARTUP_WAIT(STARTUP_WAIT == "TRUE"),
+
+  // Lookup tables
+  .LKTABLE(pll_lktable_lookup(CLKFBOUT_MULT)),
+  .TABLE(pll_table_lookup(CLKFBOUT_MULT, BANDWIDTH)),
+
+  // FIXME: How to compute values the two below ?
+  .FILTREG1_RESERVED(12'b0000_00001000),
+  .LOCKREG3_RESERVED(1'b1),
+
+  // Clock feedback settings
+  .CLKFBOUT_CLKOUT1_HIGH_TIME   (CLKFBOUT_REGS[11:6]),
+  .CLKFBOUT_CLKOUT1_LOW_TIME    (CLKFBOUT_REGS[5:0]),
+  .CLKFBOUT_CLKOUT1_PHASE_MUX   (CLKFBOUT_REGS[15:13]),
+  .CLKFBOUT_CLKOUT2_DELAY_TIME  (CLKFBOUT_REGS[21:16]),
+  .CLKFBOUT_CLKOUT2_EDGE        (CLKFBOUT_REGS[23]),
+  .CLKFBOUT_CLKOUT2_NO_COUNT    (CLKFBOUT_REGS[22]),
+
+  // Internal VCO divider settings
+  .DIVCLK_DIVCLK_HIGH_TIME      (DIVCLK_REGS[11:6]),
+  .DIVCLK_DIVCLK_LOW_TIME       (DIVCLK_REGS[5:0]),
+  .DIVCLK_DIVCLK_NO_COUNT       (DIVCLK_REGS[22]),
+  .DIVCLK_DIVCLK_EDGE           (DIVCLK_REGS[23]),
+
+  // CLKOUT0
+  .CLKOUT0_CLKOUT1_HIGH_TIME    (CLKOUT0_REGS[11:6]),
+  .CLKOUT0_CLKOUT1_LOW_TIME     (CLKOUT0_REGS[5:0]),
+  .CLKOUT0_CLKOUT1_PHASE_MUX    (CLKOUT0_REGS[15:13]),
+  .CLKOUT0_CLKOUT2_DELAY_TIME   (CLKOUT0_REGS[21:16]),
+  .CLKOUT0_CLKOUT2_EDGE         (CLKOUT0_REGS[23]),
+  .CLKOUT0_CLKOUT2_NO_COUNT     (CLKOUT0_REGS[22]),
+
+  // CLKOUT1
+  .CLKOUT1_CLKOUT1_HIGH_TIME    (CLKOUT1_REGS[11:6]),
+  .CLKOUT1_CLKOUT1_LOW_TIME     (CLKOUT1_REGS[5:0]),
+  .CLKOUT1_CLKOUT1_PHASE_MUX    (CLKOUT1_REGS[15:13]),
+  .CLKOUT1_CLKOUT2_DELAY_TIME   (CLKOUT1_REGS[21:16]),
+  .CLKOUT1_CLKOUT2_EDGE         (CLKOUT1_REGS[23]),
+  .CLKOUT1_CLKOUT2_NO_COUNT     (CLKOUT1_REGS[22]),
+
+  // CLKOUT2
+  .CLKOUT2_CLKOUT1_HIGH_TIME    (CLKOUT2_REGS[11:6]),
+  .CLKOUT2_CLKOUT1_LOW_TIME     (CLKOUT2_REGS[5:0]),
+  .CLKOUT2_CLKOUT1_PHASE_MUX    (CLKOUT2_REGS[15:13]),
+  .CLKOUT2_CLKOUT2_DELAY_TIME   (CLKOUT2_REGS[21:16]),
+  .CLKOUT2_CLKOUT2_EDGE         (CLKOUT2_REGS[23]),
+  .CLKOUT2_CLKOUT2_NO_COUNT     (CLKOUT2_REGS[22]),
+
+  // CLKOUT3
+  .CLKOUT3_CLKOUT1_HIGH_TIME    (CLKOUT3_REGS[11:6]),
+  .CLKOUT3_CLKOUT1_LOW_TIME     (CLKOUT3_REGS[5:0]),
+  .CLKOUT3_CLKOUT1_PHASE_MUX    (CLKOUT3_REGS[15:13]),
+  .CLKOUT3_CLKOUT2_DELAY_TIME   (CLKOUT3_REGS[21:16]),
+  .CLKOUT3_CLKOUT2_EDGE         (CLKOUT3_REGS[23]),
+  .CLKOUT3_CLKOUT2_NO_COUNT     (CLKOUT3_REGS[22]),
+
+  // CLKOUT4
+  .CLKOUT4_CLKOUT1_HIGH_TIME    (CLKOUT4_REGS[11:6]),
+  .CLKOUT4_CLKOUT1_LOW_TIME     (CLKOUT4_REGS[5:0]),
+  .CLKOUT4_CLKOUT1_PHASE_MUX    (CLKOUT4_REGS[15:13]),
+  .CLKOUT4_CLKOUT2_DELAY_TIME   (CLKOUT4_REGS[21:16]),
+  .CLKOUT4_CLKOUT2_EDGE         (CLKOUT4_REGS[23]),
+  .CLKOUT4_CLKOUT2_NO_COUNT     (CLKOUT4_REGS[22]),
+
+  // CLKOUT5
+  .CLKOUT5_CLKOUT1_HIGH_TIME    (CLKOUT5_REGS[11:6]),
+  .CLKOUT5_CLKOUT1_LOW_TIME     (CLKOUT5_REGS[5:0]),
+  .CLKOUT5_CLKOUT1_PHASE_MUX    (CLKOUT5_REGS[15:13]),
+  .CLKOUT5_CLKOUT2_DELAY_TIME   (CLKOUT5_REGS[21:16]),
+  .CLKOUT5_CLKOUT2_EDGE         (CLKOUT5_REGS[23]),
+  .CLKOUT5_CLKOUT2_NO_COUNT     (CLKOUT5_REGS[22]),
+
+  // Clock output enable controls
+  .CLKFBOUT_CLKOUT1_OUTPUT_ENABLE(_TECHMAP_CONSTVAL_CLKFBOUT_ === 1'bX),
+
+  .CLKOUT0_CLKOUT1_OUTPUT_ENABLE(_TECHMAP_CONSTVAL_CLKOUT0_ === 1'bX),
+  .CLKOUT1_CLKOUT1_OUTPUT_ENABLE(_TECHMAP_CONSTVAL_CLKOUT1_ === 1'bX),
+  .CLKOUT2_CLKOUT1_OUTPUT_ENABLE(_TECHMAP_CONSTVAL_CLKOUT2_ === 1'bX),
+  .CLKOUT3_CLKOUT1_OUTPUT_ENABLE(_TECHMAP_CONSTVAL_CLKOUT3_ === 1'bX),
+  .CLKOUT4_CLKOUT1_OUTPUT_ENABLE(_TECHMAP_CONSTVAL_CLKOUT4_ === 1'bX),
+  .CLKOUT5_CLKOUT1_OUTPUT_ENABLE(_TECHMAP_CONSTVAL_CLKOUT5_ === 1'bX)
+  )
+  _TECHMAP_REPLACE_
+  (
+  .CLKFBIN(CLKFBIN),
+  .CLKIN1(CLKIN1),
+  .CLKIN2(CLKIN2),
+  .CLKFBOUT(CLKFBOUT),
+  .CLKOUT0(CLKOUT0),
+  .CLKOUT1(CLKOUT1),
+  .CLKOUT2(CLKOUT2),
+  .CLKOUT3(CLKOUT3),
+  .CLKOUT4(CLKOUT4),
+  .CLKOUT5(CLKOUT5),
+
+  .CLKINSEL (clkinsel),
+
+  .PWRDWN   (pwrdwn),
+  .RST      (rst),
+  .LOCKED   (LOCKED),
+
+  .DCLK (dclk),
+  .DEN  (den),
+  .DWE  (dwe),
+  .DRDY (DRDY),
+  .DADDR(DADDR),
+  .DI   (DI),
+  .DO   (DO)
+  );
+
+endmodule
+
+// PLLE2_BASE
+module PLLE2_BASE
+(
+input         CLKFBIN,
+input         CLKIN,
+
+output        CLKFBOUT,
+output        CLKOUT0,
+output        CLKOUT1,
+output        CLKOUT2,
+output        CLKOUT3,
+output        CLKOUT4,
+output        CLKOUT5,
+
+input         RST,
+output        LOCKED
+);
+
+  parameter IS_CLKINSEL_INVERTED = 1'b0;
+  parameter IS_RST_INVERTED = 1'b0;
+
+  parameter BANDWIDTH = "OPTIMIZED";
+  parameter STARTUP_WAIT = "FALSE";
+
+  parameter CLKIN1_PERIOD = 0.0;
+  parameter REF_JITTER1 = 0.1;
+
+  parameter [5:0] DIVCLK_DIVIDE = 1;
+
+  parameter [5:0] CLKFBOUT_MULT = 1;
+  parameter signed CLKFBOUT_PHASE = 0;
+
+  parameter [6:0] CLKOUT0_DIVIDE = 1;
+  parameter CLKOUT0_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT0_PHASE = 0;
+
+  parameter [6:0] CLKOUT1_DIVIDE = 1;
+  parameter CLKOUT1_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT1_PHASE = 0;
+
+  parameter [6:0] CLKOUT2_DIVIDE = 1;
+  parameter CLKOUT2_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT2_PHASE = 0;
+
+  parameter [6:0] CLKOUT3_DIVIDE = 1;
+  parameter CLKOUT3_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT3_PHASE = 0;
+
+  parameter [6:0] CLKOUT4_DIVIDE = 1;
+  parameter CLKOUT4_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT4_PHASE = 0;
+
+  parameter [6:0] CLKOUT5_DIVIDE = 1;
+  parameter CLKOUT5_DUTY_CYCLE = 50000;
+  parameter signed CLKOUT5_PHASE = 0;
+
+  // The substituted cell
+  PLLE2_ADV #
+  (
+  .IS_CLKINSEL_INVERTED(IS_CLKINSEL_INVERTED),
+  .IS_RST_INVERTED(IS_RST_INVERTED),
+  .IS_PWRDWN_INVERTED(1'b0),
+
+  .BANDWIDTH(BANDWIDTH),
+  .STARTUP_WAIT(STARTUP_WAIT),
+
+  .CLKIN1_PERIOD(CLKIN1_PERIOD),
+  .REF_JITTER1(REF_JITTER1),
+
+  .DIVCLK_DIVIDE(DIVCLK_DIVIDE),
+
+  .CLKFBOUT_MULT(CLKFBOUT_MULT),
+  .CLKFBOUT_PHASE(CLKFBOUT_PHASE),
+
+  .CLKOUT0_DIVIDE(CLKOUT0_DIVIDE),
+  .CLKOUT0_DUTY_CYCLE(CLKOUT0_DUTY_CYCLE),
+  .CLKOUT0_PHASE(CLKOUT0_PHASE),
+
+  .CLKOUT1_DIVIDE(CLKOUT1_DIVIDE),
+  .CLKOUT1_DUTY_CYCLE(CLKOUT1_DUTY_CYCLE),
+  .CLKOUT1_PHASE(CLKOUT1_PHASE),
+
+  .CLKOUT2_DIVIDE(CLKOUT2_DIVIDE),
+  .CLKOUT2_DUTY_CYCLE(CLKOUT2_DUTY_CYCLE),
+  .CLKOUT2_PHASE(CLKOUT2_PHASE),
+
+  .CLKOUT3_DIVIDE(CLKOUT3_DIVIDE),
+  .CLKOUT3_DUTY_CYCLE(CLKOUT3_DUTY_CYCLE),
+  .CLKOUT3_PHASE(CLKOUT3_PHASE),
+
+  .CLKOUT4_DIVIDE(CLKOUT4_DIVIDE),
+  .CLKOUT4_DUTY_CYCLE(CLKOUT4_DUTY_CYCLE),
+  .CLKOUT4_PHASE(CLKOUT4_PHASE),
+
+  .CLKOUT5_DIVIDE(CLKOUT5_DIVIDE),
+  .CLKOUT5_DUTY_CYCLE(CLKOUT5_DUTY_CYCLE),
+  .CLKOUT5_PHASE(CLKOUT5_PHASE)
+  )
+  _TECHMAP_REPLACE_
+  (
+  .CLKFBIN(CLKFBIN),
+  .CLKIN1(CLKIN),
+  .CLKINSEL(1'b1),
+
+  .CLKFBOUT(CLKFBOUT),
+  .CLKOUT0(CLKOUT0),
+  .CLKOUT1(CLKOUT1),
+  .CLKOUT2(CLKOUT2),
+  .CLKOUT3(CLKOUT3),
+  .CLKOUT4(CLKOUT4),
+  .CLKOUT5(CLKOUT5),
+
+  .PWRDWN(1'b0),
+  .RST(RST),
+  .LOCKED(LOCKED),
+
+  .DCLK(1'b0),
+  .DEN(1'b0),
+  .DWE(1'b0),
+  .DRDY(),
+  .DADDR(7'd0),
+  .DI(16'd0),
+  .DO()
+  );
+
+endmodule
diff --git a/params-plugin/tests/techmaps/cells_sim.v b/params-plugin/tests/techmaps/cells_sim.v
new file mode 100644
index 0000000..607f98b
--- /dev/null
+++ b/params-plugin/tests/techmaps/cells_sim.v
@@ -0,0 +1,145 @@
+// ============================================================================
+// CMT
+
+// PLLE2_ADV_VPR
+(* blackbox *)
+module PLLE2_ADV_VPR
+(
+input         CLKFBIN,
+input         CLKIN1,
+input         CLKIN2,
+input         CLKINSEL,
+
+output        CLKFBOUT,
+output        CLKOUT0,
+output        CLKOUT1,
+output        CLKOUT2,
+output        CLKOUT3,
+output        CLKOUT4,
+output        CLKOUT5,
+
+input         PWRDWN,
+input         RST,
+output        LOCKED,
+
+input         DCLK,
+input         DEN,
+input         DWE,
+output        DRDY,
+input  [ 6:0] DADDR,
+input  [15:0] DI,
+output [15:0] DO
+);
+
+  parameter [0:0] INV_CLKINSEL = 1'd0;
+  parameter [0:0] ZINV_PWRDWN = 1'd0;
+  parameter [0:0] ZINV_RST = 1'd1;
+
+  parameter [0:0] STARTUP_WAIT = 1'd0;
+
+  // Tables
+  parameter [9:0] TABLE = 10'd0;
+  parameter [39:0] LKTABLE = 40'd0;
+  parameter [15:0] POWER_REG = 16'd0;
+  parameter [11:0] FILTREG1_RESERVED = 12'd0;
+  parameter [9:0] FILTREG2_RESERVED = 10'd0;
+  parameter [5:0] LOCKREG1_RESERVED = 6'd0;
+  parameter [0:0] LOCKREG2_RESERVED = 1'b0;
+  parameter [0:0] LOCKREG3_RESERVED = 1'b0;
+
+  // DIVCLK
+  parameter [5:0] DIVCLK_DIVCLK_HIGH_TIME = 6'd0;
+  parameter [5:0] DIVCLK_DIVCLK_LOW_TIME = 6'd0;
+  parameter [0:0] DIVCLK_DIVCLK_NO_COUNT = 1'b1;
+  parameter [0:0] DIVCLK_DIVCLK_EDGE = 1'b0;
+
+  // CLKFBOUT
+  parameter [5:0] CLKFBOUT_CLKOUT1_HIGH_TIME = 6'd0;
+  parameter [5:0] CLKFBOUT_CLKOUT1_LOW_TIME = 6'd0;
+  parameter [0:0] CLKFBOUT_CLKOUT1_OUTPUT_ENABLE = 1'b0;
+  parameter [2:0] CLKFBOUT_CLKOUT1_PHASE_MUX = 3'd0;
+  parameter [5:0] CLKFBOUT_CLKOUT2_DELAY_TIME = 6'd0;
+  parameter [0:0] CLKFBOUT_CLKOUT2_EDGE = 1'b0;
+  parameter [2:0] CLKFBOUT_CLKOUT2_FRAC = 3'd0;
+  parameter [0:0] CLKFBOUT_CLKOUT2_FRAC_EN = 1'b0;
+  parameter [0:0] CLKFBOUT_CLKOUT2_FRAC_WF_R = 1'b0;
+  parameter [0:0] CLKFBOUT_CLKOUT2_NO_COUNT = 1'b1;
+
+  // CLKOUT0
+  parameter [5:0] CLKOUT0_CLKOUT1_HIGH_TIME = 6'd0;
+  parameter [5:0] CLKOUT0_CLKOUT1_LOW_TIME = 6'd0;
+  parameter [0:0] CLKOUT0_CLKOUT1_OUTPUT_ENABLE = 1'b0;
+  parameter [2:0] CLKOUT0_CLKOUT1_PHASE_MUX = 3'd0;
+  parameter [5:0] CLKOUT0_CLKOUT2_DELAY_TIME = 6'd0;
+  parameter [0:0] CLKOUT0_CLKOUT2_EDGE = 1'b0;
+  parameter [2:0] CLKOUT0_CLKOUT2_FRAC = 3'd0;
+  parameter [0:0] CLKOUT0_CLKOUT2_FRAC_EN = 1'b0;
+  parameter [0:0] CLKOUT0_CLKOUT2_FRAC_WF_R = 1'b0;
+  parameter [0:0] CLKOUT0_CLKOUT2_NO_COUNT = 1'b1;
+
+  // CLKOUT1
+  parameter [5:0] CLKOUT1_CLKOUT1_HIGH_TIME = 6'd0;
+  parameter [5:0] CLKOUT1_CLKOUT1_LOW_TIME = 6'd0;
+  parameter [0:0] CLKOUT1_CLKOUT1_OUTPUT_ENABLE = 1'b0;
+  parameter [2:0] CLKOUT1_CLKOUT1_PHASE_MUX = 3'd0;
+  parameter [5:0] CLKOUT1_CLKOUT2_DELAY_TIME = 6'd0;
+  parameter [0:0] CLKOUT1_CLKOUT2_EDGE = 1'b0;
+  parameter [2:0] CLKOUT1_CLKOUT2_FRAC = 3'd0;
+  parameter [0:0] CLKOUT1_CLKOUT2_FRAC_EN = 1'b0;
+  parameter [0:0] CLKOUT1_CLKOUT2_FRAC_WF_R = 1'b0;
+  parameter [0:0] CLKOUT1_CLKOUT2_NO_COUNT = 1'b1;
+
+  // CLKOUT2
+  parameter [5:0] CLKOUT2_CLKOUT1_HIGH_TIME = 6'd0;
+  parameter [5:0] CLKOUT2_CLKOUT1_LOW_TIME = 6'd0;
+  parameter [0:0] CLKOUT2_CLKOUT1_OUTPUT_ENABLE = 1'b0;
+  parameter [2:0] CLKOUT2_CLKOUT1_PHASE_MUX = 3'd0;
+  parameter [5:0] CLKOUT2_CLKOUT2_DELAY_TIME = 6'd0;
+  parameter [0:0] CLKOUT2_CLKOUT2_EDGE = 1'b0;
+  parameter [2:0] CLKOUT2_CLKOUT2_FRAC = 3'd0;
+  parameter [0:0] CLKOUT2_CLKOUT2_FRAC_EN = 1'b0;
+  parameter [0:0] CLKOUT2_CLKOUT2_FRAC_WF_R = 1'b0;
+  parameter [0:0] CLKOUT2_CLKOUT2_NO_COUNT = 1'b1;
+
+  // CLKOUT3
+  parameter [5:0] CLKOUT3_CLKOUT1_HIGH_TIME = 6'd0;
+  parameter [5:0] CLKOUT3_CLKOUT1_LOW_TIME = 6'd0;
+  parameter [0:0] CLKOUT3_CLKOUT1_OUTPUT_ENABLE = 1'b0;
+  parameter [2:0] CLKOUT3_CLKOUT1_PHASE_MUX = 3'd0;
+  parameter [5:0] CLKOUT3_CLKOUT2_DELAY_TIME = 6'd0;
+  parameter [0:0] CLKOUT3_CLKOUT2_EDGE = 1'b0;
+  parameter [2:0] CLKOUT3_CLKOUT2_FRAC = 3'd0;
+  parameter [0:0] CLKOUT3_CLKOUT2_FRAC_EN = 1'b0;
+  parameter [0:0] CLKOUT3_CLKOUT2_FRAC_WF_R = 1'b0;
+  parameter [0:0] CLKOUT3_CLKOUT2_NO_COUNT = 1'b1;
+
+  // CLKOUT4
+  parameter [5:0] CLKOUT4_CLKOUT1_HIGH_TIME = 6'd0;
+  parameter [5:0] CLKOUT4_CLKOUT1_LOW_TIME = 6'd0;
+  parameter [0:0] CLKOUT4_CLKOUT1_OUTPUT_ENABLE = 1'b0;
+  parameter [2:0] CLKOUT4_CLKOUT1_PHASE_MUX = 3'd0;
+  parameter [5:0] CLKOUT4_CLKOUT2_DELAY_TIME = 6'd0;
+  parameter [0:0] CLKOUT4_CLKOUT2_EDGE = 1'b0;
+  parameter [2:0] CLKOUT4_CLKOUT2_FRAC = 3'd0;
+  parameter [0:0] CLKOUT4_CLKOUT2_FRAC_EN = 1'b0;
+  parameter [0:0] CLKOUT4_CLKOUT2_FRAC_WF_R = 1'b0;
+  parameter [0:0] CLKOUT4_CLKOUT2_NO_COUNT = 1'b1;
+
+  // CLKOUT5
+  parameter [5:0] CLKOUT5_CLKOUT1_HIGH_TIME = 6'd0;
+  parameter [5:0] CLKOUT5_CLKOUT1_LOW_TIME = 6'd0;
+  parameter [0:0] CLKOUT5_CLKOUT1_OUTPUT_ENABLE = 1'b0;
+  parameter [2:0] CLKOUT5_CLKOUT1_PHASE_MUX = 3'd0;
+  parameter [5:0] CLKOUT5_CLKOUT2_DELAY_TIME = 6'd0;
+  parameter [0:0] CLKOUT5_CLKOUT2_EDGE = 1'b0;
+  parameter [2:0] CLKOUT5_CLKOUT2_FRAC = 3'd0;
+  parameter [0:0] CLKOUT5_CLKOUT2_FRAC_EN = 1'b0;
+  parameter [0:0] CLKOUT5_CLKOUT2_FRAC_WF_R = 1'b0;
+  parameter [0:0] CLKOUT5_CLKOUT2_NO_COUNT = 1'b1;
+
+
+  // TODO: Compensation parameters
+
+  // TODO: How to simulate a PLL in verilog (i.e. the VCO) ???
+
+endmodule
diff --git a/params-plugin/tests/xc7a35tcsg324-1.json b/params-plugin/tests/xc7a35tcsg324-1.json
new file mode 100644
index 0000000..602b949
--- /dev/null
+++ b/params-plugin/tests/xc7a35tcsg324-1.json
@@ -0,0 +1,10 @@
+{
+    "iobanks": {
+        "0": "X1Y78",
+        "14": "X1Y26",
+        "15": "X1Y78",
+        "16": "X1Y130",
+        "34": "X113Y26",
+        "35": "X113Y78"
+    }
+}