vendor.quicklogic: enable SoC clock configuration
[nmigen.git] / nmigen / vendor / quicklogic.py
1 from abc import abstractproperty
2
3 from ..hdl import *
4 from ..lib.cdc import ResetSynchronizer
5 from ..build import *
6
7
8 __all__ = ["QuicklogicPlatform"]
9
10
11 class QuicklogicPlatform(TemplatedPlatform):
12 """
13 Symbiflow toolchain
14 -------------------
15
16 Required tools:
17 * ``symbiflow_synth``
18 * ``symbiflow_pack``
19 * ``symbiflow_place``
20 * ``symbiflow_route``
21 * ``symbiflow_write_fasm``
22 * ``symbiflow_write_bitstream``
23
24 The environment is populated by running the script specified in the environment variable
25 ``NMIGEN_ENV_QLSymbiflow``, if present.
26
27 Available overrides:
28 * ``add_constraints``: inserts commands in XDC file.
29 """
30
31 device = abstractproperty()
32 package = abstractproperty()
33
34 # Since the QuickLogic version of SymbiFlow toolchain is not upstreamed yet
35 # we should distinguish the QuickLogic version from mainline one.
36 # QuickLogic toolchain: https://github.com/QuickLogic-Corp/quicklogic-fpga-toolchain/releases
37 toolchain = "QLSymbiflow"
38
39 required_tools = [
40 "symbiflow_synth",
41 "symbiflow_pack",
42 "symbiflow_place",
43 "symbiflow_route",
44 "symbiflow_write_fasm",
45 "symbiflow_write_bitstream",
46 "symbiflow_write_openocd",
47 ]
48 file_templates = {
49 **TemplatedPlatform.build_script_templates,
50 "{{name}}.v": r"""
51 /* {{autogenerated}} */
52 {{emit_verilog()}}
53 """,
54 "{{name}}.debug.v": r"""
55 /* {{autogenerated}} */
56 {{emit_debug_verilog()}}
57 """,
58 "{{name}}.pcf": r"""
59 # {{autogenerated}}
60 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
61 set_io {{port_name}} {{pin_name}}
62 {% endfor %}
63 """,
64 "{{name}}.xdc": r"""
65 # {{autogenerated}}
66 {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
67 {% for attr_name, attr_value in attrs.items() -%}
68 set_property {{attr_name}} {{attr_value}} [get_ports {{port_name|tcl_escape}} }]
69 {% endfor %}
70 {% endfor %}
71 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
72 """,
73 "{{name}}.sdc": r"""
74 # {{autogenerated}}
75 {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
76 {% if port_signal is not none -%}
77 create_clock -period {{100000000/frequency}} {{port_signal.name|ascii_escape}}
78 {% endif %}
79 {% endfor %}
80 """
81 }
82 command_templates = [
83 r"""
84 {{invoke_tool("symbiflow_synth")}}
85 -t {{name}}
86 -v {% for file in platform.iter_files(".v", ".sv", ".vhd", ".vhdl") -%} {{file}} {% endfor %} {{name}}.v
87 -d {{platform.device}}
88 -p {{name}}.pcf
89 -P {{platform.package}}
90 -x {{name}}.xdc
91 """,
92 r"""
93 {{invoke_tool("symbiflow_pack")}}
94 -e {{name}}.eblif
95 -d {{platform.device}}
96 -s {{name}}.sdc
97 """,
98 r"""
99 {{invoke_tool("symbiflow_place")}}
100 -e {{name}}.eblif
101 -d {{platform.device}}
102 -p {{name}}.pcf
103 -n {{name}}.net
104 -P {{platform.package}}
105 -s {{name}}.sdc
106 """,
107 r"""
108 {{invoke_tool("symbiflow_route")}}
109 -e {{name}}.eblif
110 -d {{platform.device}}
111 -s {{name}}.sdc
112 """,
113 r"""
114 {{invoke_tool("symbiflow_write_fasm")}}
115 -e {{name}}.eblif
116 -d {{platform.device}}
117 -s {{name}}.sdc
118 """,
119 r"""
120 {{invoke_tool("symbiflow_write_bitstream")}}
121 -f {{name}}.fasm
122 -d {{platform.device}}
123 -P {{platform.package}}
124 -b {{name}}.bit
125 """,
126 # This should be `invoke_tool("symbiflow_write_openocd")`, but isn't because of a bug in
127 # the QLSymbiflow v1.3.0 toolchain release.
128 r"""
129 python3 -m quicklogic_fasm.bitstream_to_openocd
130 {{name}}.bit
131 {{name}}.openocd
132 --osc-freq {{platform.osc_freq}}
133 --fpga-clk-divider {{platform.osc_div}}
134 """,
135 ]
136
137 # Common logic
138
139 @property
140 def default_clk_constraint(self):
141 if self.default_clk == "sys_clk0":
142 return Clock(self.osc_freq / self.osc_div)
143 return super().default_clk_constraint
144
145 def add_clock_constraint(self, clock, frequency):
146 super().add_clock_constraint(clock, frequency)
147 clock.attrs["keep"] = "TRUE"
148
149 def create_missing_domain(self, name):
150 if name == "sync" and self.default_clk is not None:
151 m = Module()
152 if self.default_clk == "sys_clk0":
153 if not hasattr(self, "osc_div"):
154 raise ValueError("OSC divider (osc_div) must be an integer between 2 "
155 "and 512")
156 if not isinstance(self.osc_div, int) or self.osc_div < 2 or self.osc_div > 512:
157 raise ValueError("OSC divider (osc_div) must be an integer between 2 "
158 "and 512, not {!r}"
159 .format(self.osc_div))
160 if not hasattr(self, "osc_freq"):
161 raise ValueError("OSC frequency (osc_freq) must be an integer between 2100000 "
162 "and 80000000")
163 if not isinstance(self.osc_freq, int) or self.osc_freq < 2100000 or self.osc_freq > 80000000:
164 raise ValueError("OSC frequency (osc_freq) must be an integer between 2100000 "
165 "and 80000000, not {!r}"
166 .format(self.osc_freq))
167 clk_i = Signal()
168 sys_clk0 = Signal()
169 m.submodules += Instance("qlal4s3b_cell_macro",
170 o_Sys_Clk0=sys_clk0)
171 m.submodules += Instance("gclkbuff",
172 o_A=sys_clk0,
173 o_Z=clk_i)
174 else:
175 clk_i = self.request(self.default_clk).i
176
177 if self.default_rst is not None:
178 rst_i = self.request(self.default_rst).i
179 else:
180 rst_i = Const(0)
181
182 m.domains += ClockDomain("sync")
183 m.d.comb += ClockSignal("sync").eq(clk_i)
184 m.submodules.reset_sync = ResetSynchronizer(rst_i, domain="sync")
185 return m