import os import subprocess from amaranth.build import Resource, Pins, Attrs, Clock, DiffPairs, Subsignal, Connector from amaranth.vendor import XilinxPlatform __all__ = ["AlchitryPtV2Platform"] # Based on pinouts published by Alchitry Labs, latest commit 7a6bd06d133ee2033c34f2e89a751889329da12b # https://github.com/alchitry/Alchitry-Labs-V2/blob/master/src/main/kotlin/com/alchitry/labs2/hardware/pinout/PtV2TopPin.kt # https://github.com/alchitry/Alchitry-Labs-V2/blob/master/src/main/kotlin/com/alchitry/labs2/hardware/pinout/PtV2AlphaTopPin.kt # https://github.com/alchitry/Alchitry-Labs-V2/blob/master/src/main/kotlin/com/alchitry/labs2/hardware/pinout/PtV2BottomPin.kt class AlchitryPtV2Platform(XilinxPlatform): device = "xc7a100t" package = "fgg484" speed = "2" default_clk = "clk100" resources = [ Resource( "clk100", 0, Pins("W19", dir="i"), Clock(100e6), Attrs(IOSTANDARD="LVCMOS33"), ), Resource("rst_n", 0, Pins("N15", dir="i"), Attrs(IOSTANDARD="LVCMOS33")), Resource("led", 0, Pins("P19", dir="o"), Attrs(IOSTANDARD="LVCMOS33")), Resource("led", 1, Pins("P20", dir="o"), Attrs(IOSTANDARD="LVCMOS33")), Resource("led", 2, Pins("T21", dir="o"), Attrs(IOSTANDARD="LVCMOS33")), Resource("led", 3, Pins("R19", dir="o"), Attrs(IOSTANDARD="LVCMOS33")), Resource("led", 4, Pins("V22", dir="o"), Attrs(IOSTANDARD="LVCMOS33")), Resource("led", 5, Pins("U21", dir="o"), Attrs(IOSTANDARD="LVCMOS33")), Resource("led", 6, Pins("T20", dir="o"), Attrs(IOSTANDARD="LVCMOS33")), Resource("led", 7, Pins("W20", dir="o"), Attrs(IOSTANDARD="LVCMOS33")), # FTDI USB Interface Resource( "usb", 0, Subsignal("rx", Pins("AA20", dir="i")), Subsignal("tx", Pins("AA21", dir="o")), # Note: The following D pins have different banks Subsignal("d", Pins("W21 W22 Y21 Y22 F15 U7", dir="io")), # D2-D7 Subsignal("rxf_n", Pins("F21", dir="o")), # RXF# Subsignal("txe_n", Pins("T3", dir="o")), # TXE# Subsignal("rd_n", Pins("T16", dir="o")), # RD# Subsignal("wr_n", Pins("U16", dir="o")), # WR# Subsignal("siwua", Pins("Y17", dir="o")), # SIWUA Attrs(IOSTANDARD="LVCMOS33"), ), # QSPI Flash Resource( "spi_flash", 0, Subsignal("cs_n", Pins("T19", dir="o")), # CLK is on a dedicated clock pin, but used as a standard IO for SPI Subsignal("clk", Pins("L12", dir="o")), Subsignal("dq", Pins("P22 R22 P21 R21", dir="io")), # D0, D1, D2, D3 Attrs(IOSTANDARD="LVCMOS33"), ), # Analog differential pair (e.g., for XADC) Resource("vp_vn", 0, DiffPairs("L10", "M9", dir="i")), # DDR3 SDRAM Resource( "ddr3", 0, Subsignal( "a", Pins( "K14 M15 N18 K16 L14 K18 M13 L18 L13 M18 K13 L15 M16 L16", dir="o" ), ), Subsignal("ba", Pins("K19 N20 M20", dir="o")), Subsignal("cas_n", Pins("N22", dir="o")), Subsignal("cke", Pins("M22", dir="o")), Subsignal("clk", DiffPairs("K17", "J17", dir="o")), Subsignal("cs_n", Pins("N19", dir="o")), Subsignal("dm", Pins("H22 G13", dir="o")), Subsignal( "dq", Pins( "J22 M21 L21 J20 H20 G20 J21 H19 H13 G18 J15 H17 G15 G17 G16 H15", dir="io", ), ), Subsignal( "dqs", Subsignal("p", Pins("K21 J14", dir="io")), Subsignal("n", Pins("K22 H14", dir="io")), ), Subsignal("odt", Pins("M17", dir="o")), Subsignal("ras_n", Pins("L20", dir="o")), Subsignal("reset_n", Pins("J19", dir="o")), Subsignal("we_n", Pins("L19", dir="o")), Attrs(IOSTANDARD="SSTL135", SLEW="FAST"), Attrs(IOSTANDARD="DIFF_SSTL135", SLEW="FAST", on="clk"), Attrs(IOSTANDARD="DIFF_SSTL135", SLEW="FAST", on="dqs"), ), ] # --- IO Connectors --- # Note on IOSTANDARD: # Bank 13 can be powered by 3.3V, 2.5V, or 1.8V. # All other IO banks (14, 16, 34, 35) are fixed at 3.3V. # Defaulting to LVCMOS33 is safe, but for Bank 13, you may need to # override the IOSTANDARD for your specific application if using a different VCC. connectors = [ Connector( "top_a", 0, { "A3": "AB22", "A4": "AB18", "A5": "AB21", "A6": "AA18", "A9": "E3", "A10": "N2", "A11": "F3", "A12": "P2", "A15": "M2", "A16": "L1", "A17": "M3", "A18": "M1", "A21": "J6", "A22": "D2", "A23": "K6", "A24": "E2", "A27": "M5", "A28": "L4", "A29": "M6", "A30": "L5", "A33": "P4", "A34": "N5", "A35": "P5", "A36": "P6", "A39": "G4", "A40": "G3", "A41": "H4", "A42": "H3", "A45": "J4", "A46": "K3", "A47": "K4", "A48": "L3", "A51": "P1", "A52": "N3", "A53": "R1", "A54": "N4", "A57": "B2", "A58": "A1", "A59": "C2", "A60": "B1", "A63": "F1", "A64": "D1", "A65": "G1", "A66": "E1", "A69": "H5", "A70": "G2", "A71": "J5", "A72": "H2", "A75": "J1", "A76": "J2", "A77": "K1", "A78": "K2", }, ), Connector( "top_c", 0, { "C29": "U18", "C30": "Y1", "C31": "U17", "C32": "W1", "C33": "V2", "C34": "N14", "C35": "U2", "C36": "N13", }, ), Connector( "bottom_a", 0, { "A3": "AB1", "A4": "AA3", "A5": "AA1", "A6": "Y3", "A9": "D16", "A10": "A16", "A11": "E16", "A12": "A15", "A15": "A19", "A16": "A21", "A17": "A18", "A18": "B21", "A21": "C20", "A22": "D21", "A23": "D20", "A24": "E21", "A27": "B22", "A28": "D22", "A29": "C22", "A30": "E22", "A33": "B13", "A34": "A14", "A35": "C13", "A36": "A13", "A39": "C17", "A40": "B18", "A41": "D17", "A42": "B17", "A45": "C19", "A46": "D19", "A47": "C18", "A48": "E19", "A51": "F14", "A52": "B16", "A53": "F13", "A54": "B15", "A57": "E17", "A58": "F20", "A59": "F16", "A60": "F19", "A63": "E18", "A64": "A20", "A65": "F18", "A66": "B20", "A69": "G22", "A70": "C15", "A71": "G21", "A72": "C14", "A75": "D15", "A76": "E14", "A77": "D14", "A78": "E13", }, ), Connector( "bottom_b", 0, { "B3": "Y2", "B4": "AB6", "B5": "W2", "B6": "AB7", "B9": "Y7", "B10": "AB8", "B11": "Y8", "B12": "AA8", "B15": "AA6", "B16": "AB5", "B17": "Y6", "B18": "AA5", "B21": "AB2", "B22": "Y9", "B23": "AB3", "B24": "W9", "B27": "T6", "B28": "W7", "B29": "R6", "B30": "V7", "B33": "V8", "B34": "V5", "B35": "V9", "B36": "U6", "B39": "W4", "B40": "AA4", "B41": "V4", "B42": "Y4", "B45": "T4", "B46": "U5", "B47": "R4", "B48": "T5", "B51": "E10", "B52": "E6", "B53": "F10", "B54": "F6", "B57": "C7", "B58": "C11", "B59": "D7", "B60": "D11", "B63": "A6", "B64": "A10", "B65": "B6", "B66": "B10", "B69": "C5", "B70": "C9", "B71": "D5", "B72": "D9", "B75": "A4", "B76": "A8", "B77": "B4", "B78": "B8", }, ), Connector( "bottom_c", 0, { "C29": "U1", "C30": "W5", "C31": "T1", "C32": "W6", "C33": "R2", "C34": "V3", "C35": "R3", "C36": "U3", }, ), ] # The Top B connector differs between the Alpha and final V2 release. # We handle this by dynamically adding the correct connector based on the revision. _top_b_pins_v2 = { "B3": "R14", "B4": "R16", "B5": "P14", "B6": "P15", "B9": "R17", "B10": "P17", "B11": "P16", "B12": "N17", "B15": "W17", "B16": "T18", "B17": "V17", "B18": "R18", "B21": "AB20", "B22": "V19", "B23": "AA19", "B24": "V18", "B27": "Y19", "B28": "V20", "B29": "Y18", "B30": "U20", "B33": "AB10", "B34": "AB15", "B35": "AA9", "B36": "AA15", "B39": "W12", "B40": "Y12", "B41": "W11", "B42": "Y11", "B45": "V14", "B46": "V15", "B47": "V13", "B48": "U15", "B51": "W10", "B52": "AB17", "B53": "V10", "B54": "AB16", "B57": "AB12", "B58": "AA16", "B59": "AB11", "B60": "Y16", "B63": "AA11", "B64": "T15", "B65": "AA10", "B66": "T14", "B69": "AB13", "B70": "Y14", "B71": "AA13", "B72": "W14", "B75": "AA14", "B76": "W16", "B77": "Y13", "B78": "W15", } _top_b_pins_alpha = { "B3": "AB13", "B4": "AA11", "B5": "AA13", "B6": "AA10", "B9": "AB12", "B10": "Y14", "B11": "AB11", "B12": "W14", "B15": "AB15", "B16": "W16", "B17": "AA15", "B18": "W15", "B21": "AB17", "B22": "V19", "B23": "AB16", "B24": "V18", "B27": "Y19", "B28": "V20", "B29": "Y18", "B30": "U20", "B33": "AB10", "B34": "W10", "B35": "AA9", "B36": "V10", "B39": "W12", "B40": "Y12", "B41": "W11", "B42": "Y11", "B45": "V14", "B46": "V15", "B47": "V13", "B48": "U15", "B51": "AB20", "B52": "AA16", "B53": "AA19", "B54": "Y16", "B57": "W17", "B58": "T15", "B59": "V17", "B60": "T14", "B63": "AA14", "B64": "R16", "B65": "Y13", "B66": "P15", "B69": "R17", "B70": "T18", "B71": "P16", "B72": "R18", "B75": "N14", "B76": "P17", "B77": "N13", "B78": "N17", } def __init__(self, *, revision="v2", **kwargs): if revision.lower() == "v2": self.connectors.append(Connector("top_b", 0, self._top_b_pins_v2)) elif revision.lower() in ("pt_alpha", "alpha"): self.connectors.append(Connector("top_b", 0, self._top_b_pins_alpha)) else: raise ValueError(f"Unknown revision '{revision}', expected 'v2' or 'alpha'") super().__init__(**kwargs) def toolchain_program(self, products, name, **kwargs): with products.extract(f"{name}.bit") as bitstream_filename: # Create TCL script for programming tcl_content = f""" open_hw_manager connect_hw_server open_hw_target set_property PROGRAM.FILE {{{bitstream_filename}}} [get_hw_devices xc7a100t_0] set_property PROBES.FILE {{}} [get_hw_devices xc7a100t_0] set_property FULL_PROBES.FILE {{}} [get_hw_devices xc7a100t_0] current_hw_device [get_hw_devices xc7a100t_0] program_hw_devices [get_hw_devices xc7a100t_0] close_hw_manager exit """ tcl_script = "program_fpga.tcl" tcl_full_path = os.path.join("build", tcl_script) with open(tcl_full_path, "w") as f: f.write(tcl_content) # Run vivado to program the FPGA subprocess.run(["vivado", "-mode", "batch", "-source", tcl_script], cwd="build", check=True) if __name__ == "__main__": # Example usage: p = AlchitryPtV2Platform() # To use the alpha board revision: # p = AlchitryPtV2Platform(revision="alpha") # Requesting a single LED led = p.request("led", 0) print(f"LED 0: {led.pins}") # Requesting a pin from a connector by name a3_pin = p.request("top_a", conn_name="A3") print(f"Top A3 pin: {a3_pin.pins}") # Requesting the DDR resource and accessing a subsignal ddr = p.request("ddr3") print(f"DDR DQS0 (p): {ddr.dqs.p[0].pin}") print(f"DDR DQS0 (n): {ddr.dqs.n[0].pin}")