Skip to content
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

Added gatemate vendor and Updated init file #1460

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ Amaranth can be used to target any FPGA or ASIC process that accepts behavioral
* AMD 7-series (toolchains: Vivado, ISE);
* AMD UltraScale, UltraScale+ (toolchains: Vivado);
* Altera (toolchains: Quartus);
* Quicklogic EOS S3 (toolchains: **Yosys+VPR**).
* Quicklogic EOS S3 (toolchains: **Yosys+VPR**);
* GateMate (toolchains: **Yosys**+GateMate p_r).

FOSS toolchains are listed in **bold**.

Expand Down
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 @@ -27,6 +28,9 @@ def __getattr__(name):
if name in ("AlteraPlatform", "IntelPlatform"):
from ._altera import AlteraPlatform
return AlteraPlatform
if name == "GateMatePlatform":
from ._gatemate import GateMatePlatform
return GateMatePlatform
whitequark marked this conversation as resolved.
Show resolved Hide resolved
if name == "GowinPlatform":
from ._gowin import GowinPlatform
return GowinPlatform
Expand Down
87 changes: 87 additions & 0 deletions amaranth/vendor/_gatemate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from abc import abstractmethod
from amaranth import *
from amaranth.build import *
from amaranth.lib.cdc import ResetSynchronizer


__all__ = ["GateMatePlatform"]

whitequark marked this conversation as resolved.
Show resolved Hide resolved

class GateMatePlatform(TemplatedPlatform):
"""
Required tools:
* ``yosys``
* ``p_r``

The environment is populated by running the script specified in the environment variable
``AMARANTH_ENV_GATEMATE``, if present.
"""


toolchain = "GateMate"

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

file_templates = {
**TemplatedPlatform.build_script_templates,
"{{name}}.il": r"""
# {{autogenerated}}
{{emit_rtlil()}}
""",
"{{name}}.debug.v": r"""
/* {{autogenerated}} */
{{emit_debug_verilog()}}
""",
"{{name}}.ys": r"""
# {{autogenerated}}
{% for file in platform.iter_files(".v") -%}
read_verilog {{get_override("read_verilog_opts")|options}} {{file}}
{% endfor %}
{% for file in platform.iter_files(".sv") -%}
read_verilog -sv {{get_override("read_verilog_opts")|options}} {{file}}
{% endfor %}
{% for file in platform.iter_files(".il") -%}
read_ilang {{file}}
{% endfor %}
read_ilang {{name}}.il
{{get_override("script_after_read")|default("# (script_after_read placeholder)")}}
synth_gatemate {{get_override("synth_opts")|options}} -top {{name}} -vlog {{name}}_synth.v
{{get_override("script_after_synth")|default("# (script_after_synth placeholder)")}}
""",
"{{name}}.ccf": r"""
# {{autogenerated}}
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
Net "{{port_name}}" Loc = "{{pin_name}}"
{%- for constraint, value in attrs.items() -%}
| {{constraint}}={{value}}
{%- endfor -%};
{% endfor %}
""",
}

command_templates = [
r"""
{{invoke_tool("yosys")}}
{{quiet("-q")}}
{{get_override("yosys_opts")|options}}
-l {{name}}.rpt
{{name}}.ys
""",
r"""
{{invoke_tool("p_r")}}
{{verbose("-v")}}
-i {{name}}_synth.v
-o {{name}}
-ccf {{name}}.ccf
-cCP > {{name}}.tim
""",
]

# Common logic

def add_clock_constraint(self, clock, frequency):
super().add_clock_constraint(clock, frequency)
clock.attrs["keep"] = "TRUE"
whitequark marked this conversation as resolved.
Show resolved Hide resolved
whitequark marked this conversation as resolved.
Show resolved Hide resolved
132 changes: 132 additions & 0 deletions examples/Blinky.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/env python3

from amaranth import *
from amaranth.sim import *
from amaranth.back import verilog

import argparse
import subprocess
import importlib
import os
import shutil

top_name = "Blinky"

class Blinky(Elaboratable):

def __init__(self, num_leds=1, clock_divider=21):
self.num_leds = num_leds
self.clock_divider = clock_divider
self.leds = Signal(num_leds)

def elaborate(self, platform):
# Create a new Amaranth module
m = Module()

# This is a local signal, which will not be accessible from outside.
count = Signal(self.clock_divider)

# If the platform is not defined then it is simulation
if platform is not None:

# ULX3S
#leds = [platform.request("led", i) for i in range(self.num_leds)]
#m.d.comb += [led.o.eq(self.leds[i]) for i, led in enumerate(leds)]

# Olimex GateMate
led = platform.request("led", 0)
m.d.comb += led.o.eq(self.leds)

# In the sync domain all logic is clocked at the positive edge of
# the implicit clk signal.
m.d.sync += count.eq(count + 1)
with m.If(count == (2**self.clock_divider - 1)):
m.d.sync += [
self.leds.eq(~self.leds),
count.eq(0)
]

return m


def clean():
files_to_remove = [f"{top_name}.vcd", f"{top_name}.gtkw", f"{top_name}.v"]
build_dir = "build"

for file in files_to_remove:
try:
os.remove(file)
print(f"Removed {file}")
except FileNotFoundError:
print(f"{file} not found, skipping")

if os.path.isdir(build_dir):
try:
shutil.rmtree(build_dir)
print(f"Removed {build_dir} directory")
except OSError as e:
print(f"Error removing {build_dir}: {e}")

if __name__ == "__main__":

parser = argparse.ArgumentParser()
parser.add_argument("-s", "--simulate", action="store_true", help="Simulate Blinky Example")
parser.add_argument("-b", "--build", action="store_true", help="Build The Blinky Example")
parser.add_argument("-v", "--verilog", action="store_true", help="Generate Verilog for Blinky Example")
parser.add_argument("-p", "--platform", type=str, required=False, help="Platform module (e.g., amaranth_boards.ulx3s.ULX3S_85F_Platform)")
parser.add_argument("-n", "--num-leds", type=int, default=1, help="Number of LEDs")
parser.add_argument("-cd", "--clock-divider", type=int, default=21, help="Clock divider (bit width of the counter)")
parser.add_argument("-cf", "--clock-frequency", type=float, default=1.0, help="Clock frequency in MHz")
parser.add_argument("-rt", "--runtime", type=int, default=30000, help="Testbench runtime in clock cycles")
parser.add_argument("-c", "--clean", action="store_true", help="Clean generated files and build directory")
parser.add_argument("-dp", "--do-program", action="store_true", help="Program the device after building")
parser.add_argument("-gw", "--gtkwave", action="store_true", help="Open GTKWave after simulation")

args = parser.parse_args()

if args.clean:
clean()

else:
num_leds = args.num_leds if args.num_leds is not None else 1
clock_divider = args.clock_divider if args.clock_divider is not None else 21
clock_frequency = args.clock_frequency if args.clock_frequency is not None else 1.0
runtime = args.runtime if args.runtime is not None else 30000
do_program = args.do_program

if args.simulate:

def testbench():
for _ in range(runtime):
yield Tick()

# Instantiate the Blinky module
dut = Blinky(num_leds, clock_divider)

# Create a simulator
sim = Simulator(dut)
sim.add_clock(1e-6 / clock_frequency)
sim.add_process(testbench)
with sim.write_vcd(f"{top_name}.vcd", f"{top_name}.gtkw", traces=[dut.leds]):
sim.run()

# Open GTKWave with the generated VCD file if --gtkwave is set
if args.gtkwave:
subprocess.run(["gtkwave", f"{top_name}.vcd"])

elif args.build:
if args.platform is None:
raise ValueError("Platform must be specified for building")
platform_module_name, platform_class_name = args.platform.rsplit(".", 1)
platform_module = importlib.import_module(platform_module_name)
platform_class = getattr(platform_module, platform_class_name)

plat = platform_class()
plat.build(Blinky(num_leds, clock_divider), do_program=do_program)

elif args.verilog:
dut = Blinky(num_leds, clock_divider)
with open(f"{top_name}.v", "w") as f:
f.write(verilog.convert(dut, ports=[dut.leds]))

# TODO: Maybe write an additional file where all of the amaranth_boards with their vendors are specified so that you can type ulx3s -85F instead of amaranth_boards.ulx3s.ULX3S_85F_Platform