Merge pull request #34 from antmicro/clock_detection
Enchance clock detection
diff --git a/tests/clocks/README.md b/tests/clocks/README.md
index e762b25..e969e29 100644
--- a/tests/clocks/README.md
+++ b/tests/clocks/README.md
@@ -1,14 +1,14 @@
# `clocks` tests
This directory contains test for the clock detection functionality for the
-`v2x_to_model.py` tool.
+`vlog_to_model.py` and `vlog_to_pbtype.py` tool.
## Detection of clock signals
- - [ ] Signal is named `clk`.
- - [ ] Signal has `clk` in the name.
- - [ ] Manually set via the `(* CLOCK *)` Verilog attribute.
+ - [ ] Signal name matches the regexp `[a-z_]*clk[a-z0-9]*$`
+ - [ ] Manually set via the `(* CLOCK *)` or `(* CLOCK=1 *)` Verilog attribute.
+ - [ ] Manually cleared via the `(* CLOCK=0 *)` Verilog attribute.
- [ ] Signal drives synchronous logic (IE flipflop).
- [ ] Detection in recursive module includes.
diff --git a/tests/clocks/input_attr_not_clock/block.sim.v b/tests/clocks/input_attr_not_clock/block.sim.v
new file mode 100644
index 0000000..f87f8b2
--- /dev/null
+++ b/tests/clocks/input_attr_not_clock/block.sim.v
@@ -0,0 +1,17 @@
+/*
+ * `input wire a` should be detected as a clock because it drives the flip
+ * flop. However, it has the attribute CLOCK set to 0 which should force it
+ * to be a regular input.
+ */
+module BLOCK(a, b, c);
+ (* CLOCK=0 *)
+ input wire a;
+ input wire b;
+ output wire c;
+
+ reg r;
+ always @ ( posedge a ) begin
+ r <= b;
+ end
+ assign c = r;
+endmodule
diff --git a/tests/clocks/input_attr_not_clock/golden.model.xml b/tests/clocks/input_attr_not_clock/golden.model.xml
new file mode 100644
index 0000000..51ff8df
--- /dev/null
+++ b/tests/clocks/input_attr_not_clock/golden.model.xml
@@ -0,0 +1,11 @@
+<models xmlns:xi="http://www.w3.org/2001/XInclude">
+ <model name="BLOCK">
+ <input_ports>
+ <port clock="a" combinational_sink_ports="c" name="a"/>
+ <port clock="a" name="b"/>
+ </input_ports>
+ <output_ports>
+ <port clock="a" name="c"/>
+ </output_ports>
+ </model>
+</models>
diff --git a/tests/clocks/input_attr_not_clock/golden.pb_type.xml b/tests/clocks/input_attr_not_clock/golden.pb_type.xml
new file mode 100644
index 0000000..c748dd3
--- /dev/null
+++ b/tests/clocks/input_attr_not_clock/golden.pb_type.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<pb_type xmlns:xi="http://www.w3.org/2001/XInclude" name="BLOCK" num_pb="1">
+ <blif_model>.subckt BLOCK</blif_model>
+ <input name="a" num_pins="1"/>
+ <input name="b" num_pins="1"/>
+ <output name="c" num_pins="1"/>
+</pb_type>
diff --git a/tests/clocks/input_named_regex/block.sim.v b/tests/clocks/input_named_regex/block.sim.v
new file mode 100644
index 0000000..3870d30
--- /dev/null
+++ b/tests/clocks/input_named_regex/block.sim.v
@@ -0,0 +1,14 @@
+(* whitebox *)
+module BLOCK(
+ input wire clk,
+ input wire Clk,
+ input wire CLK,
+ input wire clkX,
+ input wire clkBus,
+ input wire sys_clk,
+ input wire sys_clk10,
+ input wire regular_input,
+ output wire o
+);
+
+endmodule
diff --git a/tests/clocks/input_named_regex/golden.model.xml b/tests/clocks/input_named_regex/golden.model.xml
new file mode 100644
index 0000000..3f5edd2
--- /dev/null
+++ b/tests/clocks/input_named_regex/golden.model.xml
@@ -0,0 +1,17 @@
+<models xmlns:xi="http://www.w3.org/2001/XInclude">
+ <model name="BLOCK">
+ <input_ports>
+ <port is_clock="1" name="CLK"/>
+ <port is_clock="1" name="Clk"/>
+ <port is_clock="1" name="clk"/>
+ <port is_clock="1" name="clkBus"/>
+ <port is_clock="1" name="clkX"/>
+ <port name="regular_input"/>
+ <port is_clock="1" name="sys_clk"/>
+ <port is_clock="1" name="sys_clk10"/>
+ </input_ports>
+ <output_ports>
+ <port name="o"/>
+ </output_ports>
+ </model>
+</models>
diff --git a/tests/clocks/input_named_regex/golden.pb_type.xml b/tests/clocks/input_named_regex/golden.pb_type.xml
new file mode 100644
index 0000000..2edf093
--- /dev/null
+++ b/tests/clocks/input_named_regex/golden.pb_type.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='utf-8'?>
+<pb_type xmlns:xi="http://www.w3.org/2001/XInclude" name="BLOCK" num_pb="1">
+ <blif_model>.subckt BLOCK</blif_model>
+ <clock name="CLK" num_pins="1"/>
+ <clock name="Clk" num_pins="1"/>
+ <clock name="clk" num_pins="1"/>
+ <clock name="clkBus" num_pins="1"/>
+ <clock name="clkX" num_pins="1"/>
+ <clock name="sys_clk" num_pins="1"/>
+ <clock name="sys_clk10" num_pins="1"/>
+ <input name="regular_input" num_pins="1"/>
+ <output name="o" num_pins="1"/>
+</pb_type>
diff --git a/v2x/vlog_to_model.py b/v2x/vlog_to_model.py
index a4df516..3477de1 100755
--- a/v2x/vlog_to_model.py
+++ b/v2x/vlog_to_model.py
@@ -3,7 +3,9 @@
Convert a Verilog simulation model to a VPR `model.xml`
The following Verilog attributes are considered on ports:
- - `(* CLOCK *)` : force a given port to be a clock
+ - `(* CLOCK *)` or `(* CLOCK=1 *)` : force a given port to be a clock
+
+ - `(* CLOCK=0 *)` : force a given port not to be a clock
- `(* ASSOC_CLOCK="RDCLK" *)` : force a port's associated
clock to a given value
@@ -26,6 +28,7 @@
from .yosys import run
from .yosys.json import YosysJSON
+from .yosys import utils as utils
from .xmlinc import xmlinc
@@ -173,6 +176,13 @@
for name, width, bits, iodir in ports:
nocomb = tmod.net_attr(name, "NO_COMB")
+
+ is_clock = name in clocks or utils.is_clock_name(name)
+
+ port_attrs = tmod.port_attrs(name)
+ if "CLOCK" in port_attrs:
+ is_clock = int(port_attrs["CLOCK"]) != 0
+
attrs = dict(name=name)
sinks = run.get_combinational_sinks(infiles, top, name)
@@ -183,7 +193,7 @@
# FIXME: Check if ignoring clock for "combination_sink_ports"
# is a valid thing to do.
- if name in clocks or "clk" in name.lower():
+ if is_clock:
attrs["is_clock"] = "1"
else:
clks = list()
diff --git a/v2x/vlog_to_pbtype.py b/v2x/vlog_to_pbtype.py
index d109dfb..7146992 100755
--- a/v2x/vlog_to_pbtype.py
+++ b/v2x/vlog_to_pbtype.py
@@ -44,7 +44,9 @@
with this wire
The following are allowed on ports:
- - `(* CLOCK *)` : force a given port to be a clock
+ - `(* CLOCK *)` or `(* CLOCK=1 *)` : force a given port to be a clock
+
+ - `(* CLOCK=0 *)` : force a given port not to be a clock
- `(* ASSOC_CLOCK="RDCLK" *)` : force a port's associated clock to a
given value
@@ -68,6 +70,7 @@
from .yosys import run
from .yosys.json import YosysJSON
+from .yosys import utils as utils
from .xmlinc import xmlinc # noqa: E402
@@ -808,7 +811,28 @@
ET.SubElement(pb_type_xml, "pb_class", {}).text = pb_attrs["class"]
# Create the pins for this pb_type
- clocks = run.list_clocks(infiles, mod.name)
+ clocks = set(run.list_clocks(infiles, mod.name))
+
+ # Add extra clocks inferred from port names
+ # Mask out clocks with the attribute "CLOCK" not equal to 1
+ for name, width, bits, iodir in mod.ports:
+ port_attrs = mod.port_attrs(name)
+
+ is_clock = utils.is_clock_name(name)
+
+ # In pb_type "clock" ports can be only inputs. Clock outputs must
+ # be declared as "output".
+ if iodir == "output":
+ is_clock = False
+
+ if "CLOCK" in port_attrs:
+ is_clock = int(port_attrs["CLOCK"]) != 0
+
+ if is_clock:
+ clocks.add(name)
+ else:
+ clocks.discard(name)
+
make_ports(clocks, mod, pb_type_xml, "clocks")
make_ports(clocks, mod, pb_type_xml, "inputs")
make_ports(clocks, mod, pb_type_xml, "outputs")
diff --git a/v2x/yosys/run.py b/v2x/yosys/run.py
index 91e361d..ef2627e 100755
--- a/v2x/yosys/run.py
+++ b/v2x/yosys/run.py
@@ -287,7 +287,7 @@
"""
return do_select(
infiles, module,
- "c:* %x:+[CLK]:+[clk]:+[clock]:+[CLOCK] a:CLOCK=1 %u c:* %d x:* %i"
+ "c:* %x:+[CLK]:+[clk]:+[clock]:+[CLOCK] c:* %d x:* %i"
)
diff --git a/v2x/yosys/utils.py b/v2x/yosys/utils.py
index 722ac9b..f6c3597 100644
--- a/v2x/yosys/utils.py
+++ b/v2x/yosys/utils.py
@@ -1,11 +1,41 @@
#!/usr/bin/env python3
import re
-"""The JSON Yosys outputs isn't acutally compliant JSON, as it contains C-style
-comments. These must be stripped."""
+
+CLOCK_NAME_REGEX = re.compile(r"[a-z_]*clk[a-z0-9]*$")
def strip_yosys_json(text):
+ """The JSON Yosys outputs isn't acutally compliant JSON, as it contains C-style
+ comments. These must be stripped."""
stripped = re.sub(r'\\\n', '', text)
stripped = re.sub(r'//.*\n', '\n', stripped)
stripped = re.sub(r'/\*.*\*/', '', stripped)
return stripped
+
+
+def is_clock_name(name):
+ """
+ Returns true if the port name correspond to a clock according to arbitrary
+ regular expressions.
+
+ >>> is_clock_name("data")
+ False
+ >>> is_clock_name("clk")
+ True
+ >>> is_clock_name("Clk")
+ True
+ >>> is_clock_name("Clk_Rst0")
+ False
+ >>> is_clock_name("Data_clk")
+ True
+ >>> is_clock_name("clk99")
+ True
+ >>> is_clock_name("bus_clk99")
+ True
+ >>> is_clock_name("busclk15")
+ True
+ >>> is_clock_name("clkb")
+ True
+ """
+ match = CLOCK_NAME_REGEX.match(name.lower())
+ return match is not None