/ constants / ecp5_platforms.py
ecp5_platforms.py
 1  from collections.abc import Callable, Iterable
 2  from itertools import chain
 3  from typing import TypeAlias
 4  from amaranth import *
 5  from amaranth.build.dsl import Subsignal
 6  from amaranth.vendor import LatticeECP5Platform
 7  from amaranth.build import Resource, Attrs, Pins, Clock, PinsN
 8  from amaranth.lib.wiring import Signature, Flow
 9  
10  from constants.ecp5_pinout import ecp5_bg756_pins, ecp5_bg756_pclk
11  
12  __all__ = ["make_ecp5_platform"]
13  
14  
15  def iterate_members(signature: Signature):
16      for hier_name, member in signature.members.flatten():
17          if not member.is_port:
18              continue
19          name = "__".join(str(x) for x in hier_name)
20          yield name, member
21  
22  
23  def SignatureResource(*args, signature: Signature, default_name: str, conn=None, **pinargs: str):  # noqa: N802
24      io = []
25  
26      for name, member in iterate_members(signature):
27          dir = "i" if member.flow == Flow.In else "o"
28          io.append(Subsignal(name, Pins(pinargs[name], dir=dir, conn=conn)))
29  
30      return Resource.family(*args, default_name=default_name, ios=io)
31  
32  
33  class PinManager:
34      def __init__(self, pins: Iterable[str]):
35          self.pin_bag = list(pins)
36  
37      def p(self, count: int = 1):
38          return " ".join([self.pin_bag.pop() for _ in range(count)])
39  
40      def named_pin(self, names: Iterable[str]):
41          for name in names:
42              if name in self.pin_bag:
43                  self.pin_bag.remove(name)
44                  return name
45          raise RuntimeError("Named pins %s not free" % ", ".join(names))
46  
47  
48  ResourceBuilder: TypeAlias = Callable[[PinManager], list[Resource]]
49  
50  
51  def signature_resources(signature: Signature, default_name: str, number: int):
52      def make_resources(pins: PinManager) -> list[Resource]:
53          pinargs = {name: pins.p(Shape.cast(member.shape).width) for name, member in iterate_members(signature)}
54          return [SignatureResource(number, signature=signature, default_name=default_name, **pinargs)]
55  
56      return make_resources
57  
58  
59  def append_resources(*args: ResourceBuilder):
60      def make_resources(pins: PinManager):
61          return list(chain.from_iterable(map(lambda f: f(pins), args)))
62  
63      return make_resources
64  
65  
66  def make_ecp5_platform(resource_builder: ResourceBuilder):
67      pins = PinManager(ecp5_bg756_pins)
68  
69      # Tutorial for synthesis in amaranth:
70      # https://github.com/RobertBaruch/amaranth-tutorial/blob/main/9_synthesis.md
71      class ECP5BG756Platform(LatticeECP5Platform):
72          device = "LFE5UM5G-85F"
73          package = "BG756"
74          speed = "8"
75          default_clk = "clk"
76          default_rst = "rst"
77  
78          clk_pin = pins.named_pin(ecp5_bg756_pclk)
79          resources = [
80              Resource("rst", 0, PinsN(pins.p(), dir="i"), Attrs(IO_TYPE="LVCMOS33")),
81              Resource("clk", 0, Pins(clk_pin, dir="i"), Clock(12e6), Attrs(IO_TYPE="LVCMOS33")),
82          ] + resource_builder(pins)
83  
84          connectors = []
85  
86          def toolchain_program(self, products, name, **kwargs):
87              pass
88  
89      return ECP5BG756Platform