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