Skip to content

Added gatemate vendor and Updated init file #1460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
4 changes: 4 additions & 0 deletions amaranth/vendor/__init__.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
__all__ = [
"AlteraPlatform",
"AMDPlatform",
"GateMatePlatform"
"GowinPlatform",
"IntelPlatform",
"LatticeECP5Platform",
Expand All @@ -30,6 +31,9 @@ def __getattr__(name):
if name == "GowinPlatform":
from ._gowin import GowinPlatform
return GowinPlatform
if name == "GateMatePlatform":
from ._gatemate import GateMatePlatform
return GateMatePlatform
if name in ("LatticePlatform", "LatticeECP5Platform", "LatticeMachXO2Platform",
"LatticeMachXO3LPlatform"):
from ._lattice import LatticePlatform
Expand Down
120 changes: 120 additions & 0 deletions amaranth/vendor/_gatemate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from abc import abstractmethod
from amaranth import *
from amaranth.build import *
from amaranth.lib.cdc import ResetSynchronizer

__all__ = ["GateMatePlatform"]

class GateMatePlatform(TemplatedPlatform):
"""
.. rubric:: GateMate FPGA toolchain

Required tools:
* ``yosys``
* ``p_r``

The environment is populated by setting the ``CC_TOOL`` environment variable to point to the toolchain directory.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how this would happen as nothing in this file touches CC_TOOL. There is a default behavior that will use the platform.toolchain property (defined here to be GateMate) to derive AMARANTH_ENV_GATEMATE from it.

If you wanted to use CC_TOOL to populale the environment you have to write a custom template for the shell and batch scripts, which is possible but finicky and requires extensive testing and especially careful coding.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed CC_TOOL to AMARANTH_ENV_GATEMATE

"""

device = property(abstractmethod(lambda: None))
package = property(abstractmethod(lambda: None))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that nothing in the platform file is using these variables. In that case they should be removed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted those lines


toolchain = "GateMate"

required_tools = [
"yosys",
"p_r",
]

file_templates = {
**TemplatedPlatform.build_script_templates,
"{{name}}.v": r"""
/* {{autogenerated}} */
{{emit_verilog()}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a Yosys-based platform, you should output a .il file, not a .v one.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

        "{{name}}.il": r"""
            # {{autogenerated}}
            {{emit_rtlil()}}
        """,

Instead of

        "{{name}}.v": r"""
            /* {{autogenerated}} */
            {{emit_verilog()}}
        """,

""",
"{{name}}.debug.v": r"""
/* {{autogenerated}} */
{{emit_debug_verilog()}}
""",
"{{name}}.ccf": r"""
# {{autogenerated}}
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
Net "{{port_name}}" Loc = "{{pin_name}}"
{%- set constraints = [
"SCHMITT_TRIGGER", "PULLUP", "PULLDOWN", "KEEPER", "SLEW", "DRIVE",
"DELAY_OBF", "DELAY_IBF", "FF_IBF", "FF_OBF", "LVDS_BOOST", "LVDS_TERM"
] -%}
{%- for constraint in constraints if constraint in attrs -%}
| {{constraint}}={{attrs[constraint]}}
{%- endfor -%};
{% endfor %}
""",
"{{name}}.sdc": r"""
# {{autogenerated}}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no iter_clock_constraints function in the main branch (you can grep it with zero hits), how did you test sdc generation functionality?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't test the .sdc function, to be honest. I based this part of the on the _quicklogic.py vendor.

What do I need to do here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to make sure that a clock constraint, as specified in your board file at least, is actually applied. Whether a clock constraint is applied or not can be seen in the report of the PNR tool, in a format described in the vendor's documentation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I searched through the documentation and couldn't find a clock constraint file anywhere.
This is the workflow.
image

I will ask a question on our github about the clock constraints

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That has a clock constraint file in the top right, as I would expect.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is their pin constraint file, the .ccf file?

{% if port_signal is not none -%}
create_clock -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
{% endif %}
{% endfor %}
""",
}

command_templates = [
r"""
mkdir -p log;
mkdir -p net;
{{invoke_tool("yosys")}}
-ql log/synth.log -p 'read -sv {{name}}.v; synth_gatemate -top {{name}} -nomx8 -vlog net/{{name}}_synth.v'
Copy link
Member

@whitequark whitequark Jul 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Yosys script should be another generated file.

-q should only be given if requested by the end user.

You can copy the Yosys invocation (both the file template and the command template) from the SiliconBlue platform and modify it to fit GateMate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tried this now, but I get some errors that the top module is missing. I will try to resolve tomorrow.

""",
r"""

{{invoke_tool("p_r")}}
-v
-i net/{{name}}_synth.v
-o {{name}}
-ccf {{name}}.ccf
-cCP > log/impl.log
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no log directory any more. The redirection should probably be something like this:

Suggested change
-cCP > log/impl.log
-cCP
> {{name}}.tim

The .rpt/.tim extension is something we've been using for Yosys based platforms and isn't set in stone, but it's nice to have consistency.

Copy link
Author

@tarik-hamedovic tarik-hamedovic Jul 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to

        r"""
        {{invoke_tool("p_r")}}
            {{verbose("-v")}}
            -i {{name}}_synth.v
            -o {{name}}
            -ccf {{name}}.ccf
            -cCP 
            > {{name}}.tim
        """,

""",
]

# Common logic

@property
def default_clk_constraint(self):
if self.default_clk == "sys_clk0":
return Clock(self.osc_freq / self.osc_div)
return super().default_clk_constraint

def add_clock_constraint(self, clock, frequency):
super().add_clock_constraint(clock, frequency)
clock.attrs["keep"] = "TRUE"

def create_missing_domain(self, name):
if name == "sync" and self.default_clk is not None:
m = Module()
if self.default_clk == "sys_clk0":
if not hasattr(self, "osc_div"):
raise ValueError("OSC divider (osc_div) must be an integer between 2 and 512")
if not isinstance(self.osc_div, int) or self.osc_div < 2 or self.osc_div > 512:
raise ValueError("OSC divider (osc_div) must be an integer between 2 and 512, not {!r}".format(self.osc_div))
if not hasattr(self, "osc_freq"):
raise ValueError("OSC frequency (osc_freq) must be an integer between 2100000 and 80000000")
if not isinstance(self.osc_freq, int) or self.osc_freq < 2100000 or self.osc_freq > 80000000:
raise ValueError("OSC frequency (osc_freq) must be an integer between 2100000 and 80000000, not {!r}".format(self.osc_freq))
clk_i = Signal()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look right--you aren't using osc_div and osc_freq anywhere in the instantiation and qlal4s3b_cell_macro is a QuickLogic primitive. Do GateMate FPGAs have any internal oscillator at all, like RC oscillator or ring oscillator? If not then the create_missing_domain function should only use an async reset synchronizer, or possibly just be absent.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I have read on their documentation for the CCGM1A1 Chip, there are no oscillators(ring or RC) on the chip.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you don't need any implementation of create_missing_domain or default_clk_constraint at all.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed create_missing_domain and default_clk_constraint.

sys_clk0 = Signal()
m.submodules += Instance("qlal4s3b_cell_macro", o_Sys_Clk0=sys_clk0)
m.submodules += Instance("gclkbuff", o_A=sys_clk0, o_Z=clk_i)
else:
clk_i = self.request(self.default_clk).i

if self.default_rst is not None:
rst_i = self.request(self.default_rst).i
else:
rst_i = Const(0)

m.domains += ClockDomain("sync")
m.d.comb += ClockSignal("sync").eq(clk_i)
m.submodules.reset_sync = ResetSynchronizer(rst_i, domain="sync")
return m