|
| 1 | +import os |
| 2 | +import subprocess |
| 3 | +import textwrap |
| 4 | +import unittest |
| 5 | + |
| 6 | +from amaranth.build import * |
| 7 | +from amaranth.vendor import XilinxPlatform |
| 8 | +from .resources import * |
| 9 | + |
| 10 | + |
| 11 | +__all__ = ["NitefuryIIPlatform", "LitefuryPlatform"] |
| 12 | + |
| 13 | + |
| 14 | +class _BasePlatform(XilinxPlatform): |
| 15 | + speed = "2" |
| 16 | + default_clk = "clk200" |
| 17 | + |
| 18 | + resources = [ |
| 19 | + Resource("clk200", 0, DiffPairs(p="J19", n="H19", dir="i"), |
| 20 | + Clock(200e6), Attrs(IOSTANDARD="DIFF_SSTL15")), |
| 21 | + # 4 Programmable LEDs |
| 22 | + *LEDResources(pins="G3 H3 G4 H4", attrs=Attrs(IOSTANDARD="LVCMOS33")), |
| 23 | + # SPIFlash |
| 24 | + *SPIFlashResources(0, |
| 25 | + cs_n="T19", clk="L16", copi="P22", cipo="R22", wp_n="P21", hold_n="R21", |
| 26 | + attrs=Attrs(IOSTANDARD="LVCMOS33") |
| 27 | + ), |
| 28 | + # 1 M.2 indicator LED |
| 29 | + Resource("m2led", 0, Pins("M1"), Attrs(IOSTANDARD="LVCMOS33")), |
| 30 | + # PCIE |
| 31 | + Resource("pcie", 0, |
| 32 | + Subsignal("clkreq", PinsN("G1", dir="o"), Attrs(IOSTANDARD="LVCMOS33")), |
| 33 | + Subsignal("rst", PinsN("J1", dir="o"), Attrs(IOSTANDARD="LVCMOS33", PULLUP=1)), |
| 34 | + Subsignal("clk", DiffPairs(p="F6", n="E6", dir="i"), Attrs(IOSTANDARD="DIFF_SSTL15")), |
| 35 | + Subsignal("rx", DiffPairs(p="B10 B8 D11 D9", n="A10 A8 C11 C9", dir="i")), |
| 36 | + Subsignal("tx", DiffPairs(p="B6 B4 D5 D7", n="A6 A4 C5 C7", dir="o")), |
| 37 | + ), |
| 38 | + # DDR |
| 39 | + Resource("ddr3", 0, |
| 40 | + Subsignal("rst", PinsN("K16", dir="o"), Attrs(IOStandard="LVCMOS15")), |
| 41 | + Subsignal("clk", DiffPairs(p="K17", n="J17", dir="o")), |
| 42 | + Subsignal("clk_en", Pins("H22", dir="o")), |
| 43 | + # Subsignal("cs", PinsN("U8", dir="o")), |
| 44 | + Subsignal("we", PinsN("L16", dir="o")), |
| 45 | + Subsignal("ras", PinsN("H20", dir="o")), |
| 46 | + Subsignal("cas", PinsN("K18", dir="o")), |
| 47 | + Subsignal("a", Pins("M15 L21 M16 L18 K21 M18 M21 N20 M20 N19 J21 M22 K22 N18 N22 J22", dir="o")), |
| 48 | + Subsignal("ba", Pins("L19 J20 L20", dir="o")), |
| 49 | + Subsignal("dqs", DiffPairs(p="F18 B21", n="E18 A21", dir="io"), |
| 50 | + Attrs(IOSTANDARD="DIFF_SSTL135")), |
| 51 | + Subsignal("dq", Pins("D19 B20 E19 A20 F19 C19 F20 C18 E22 G21 D20 E21 C22 D21 B22 D22", dir="io"), |
| 52 | + Attrs(IN_TERM="UNTUNED_SPLIT_50")), |
| 53 | + Subsignal("dm", Pins("A19 G22", dir="o")), |
| 54 | + Subsignal("odt", Pins("K19", dir="o")), |
| 55 | + Attrs(IOSTANDARD="SSTL15", SLEW="FAST"), |
| 56 | + ), |
| 57 | + ] |
| 58 | + connectors = [] |
| 59 | + |
| 60 | + def toolchain_prepare(self, fragment, name, **kwargs): |
| 61 | + overrides = { |
| 62 | + "script_before_bitstream": |
| 63 | + """ |
| 64 | + set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design] |
| 65 | + set_property BITSTREAM.CONFIG.CONFIGRATE 16 [current_design] |
| 66 | + set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design] |
| 67 | + """, |
| 68 | + "script_after_bitstream": |
| 69 | + "write_cfgmem -force -format bin -interface spix4 -size 16 " |
| 70 | + "-loadbit \"up 0x0 {name}.bit\" -file {name}.bin".format(name=name), |
| 71 | + "add_constraints": |
| 72 | + """ |
| 73 | + set_property INTERNAL_VREF 0.675 [get_iobanks 34] |
| 74 | + set_property CFGBVS VCCO [current_design] |
| 75 | + set_property CONFIG_VOLTAGE 3.3 [current_design] |
| 76 | + """ |
| 77 | + } |
| 78 | + return super().toolchain_prepare(fragment, name, **overrides, **kwargs) |
| 79 | + |
| 80 | + def toolchain_program(self, product, name, *, programmer="openfpgaloader", flash=True): |
| 81 | + assert programmer in ("vivado", "openfpgaloader") |
| 82 | + |
| 83 | + if programmer == "vivado": |
| 84 | + if flash: |
| 85 | + # It does not appear possible to reset the FPGA via TCL after |
| 86 | + # flash programming. |
| 87 | + with product.extract("{}.bin".format(name)) as bitstream_filename: |
| 88 | + cmd = textwrap.dedent(""" |
| 89 | + open_hw_manager |
| 90 | + connect_hw_server |
| 91 | + open_hw_target |
| 92 | + current_hw_device [lindex [get_hw_devices xc7a*] 0]] |
| 93 | + create_hw_cfgmem -hw_device [current_hw_device] s25fl256sxxxxxx0-spi-x1_x2_x4 |
| 94 | + set_property PROGRAM.FILES {{{}}} [current_hw_cfgmem] |
| 95 | + set_property PROGRAM.ADDRESS_RANGE {{use_file}} [current_hw_cfgmem] |
| 96 | + set_property PROGRAM.BLANK_CHECK 1 [current_hw_cfgmem] |
| 97 | + set_property PROGRAM.ERASE 1 [current_hw_cfgmem] |
| 98 | + set_property PROGRAM.CFG_PROGRAM 1 [current_hw_cfgmem] |
| 99 | + set_property PROGRAM.VERIFY 1 [current_hw_cfgmem] |
| 100 | + create_hw_bitstream -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM_BITFILE [current_hw_device]] |
| 101 | + program_hw_devices |
| 102 | + program_hw_cfgmem |
| 103 | + close_hw_manager |
| 104 | + puts "Vivado TCL cannot reset boards. Reset or power-cycle your board now." |
| 105 | + """).format(bitstream_filename).encode("utf-8") |
| 106 | + subprocess.run(["vivado", "-nolog", "-nojournal", "-mode", "tcl"], input=cmd, check=True) |
| 107 | + else: |
| 108 | + with product.extract("{}.bit".format(name)) as bitstream_filename: |
| 109 | + cmd = textwrap.dedent(""" |
| 110 | + open_hw_manager |
| 111 | + connect_hw_server |
| 112 | + open_hw_target |
| 113 | + current_hw_device [lindex [get_hw_devices] 0] |
| 114 | + set_property PROGRAM.FILE {{{}}} [current_hw_device] |
| 115 | + program_hw_devices |
| 116 | + close_hw_manager |
| 117 | + """).format(bitstream_filename).encode("utf-8") |
| 118 | + subprocess.run(["vivado", "-nolog", "-nojournal", "-mode", "tcl"], input=cmd, check=True) |
| 119 | + else: |
| 120 | + # openfpgaloader |
| 121 | + openfpgaloader = os.environ.get("OPENFPGALOADER", "openFPGALoader") |
| 122 | + with product.extract("{}.bin".format(name)) as fn: |
| 123 | + # TODO: @timkpaine has digilent_hs3 cable |
| 124 | + subprocess.check_call([openfpgaloader, "-c", "digilent_hs3", fn]) |
| 125 | + |
| 126 | + |
| 127 | +class LitefuryPlatform(_BasePlatform): |
| 128 | + device = "xc7a100t" |
| 129 | + package = "fgg484" |
| 130 | + |
| 131 | + |
| 132 | +class NitefuryIIPlatform(_BasePlatform): |
| 133 | + device = "xc7a200t" |
| 134 | + package = "fbg484" |
| 135 | + |
| 136 | + |
| 137 | +class TestCase(unittest.TestCase): |
| 138 | + def test_smoke(self): |
| 139 | + from .test.blinky import Blinky |
| 140 | + NitefuryIIPlatform().build(Blinky(), do_build=False) |
| 141 | + |
| 142 | + |
| 143 | +if __name__ == "__main__": |
| 144 | + from .test.blinky import * |
| 145 | + NitefuryIIPlatform().build(Blinky(), do_program=True) |
0 commit comments