aboutsummaryrefslogtreecommitdiff
path: root/nmigen_boards/ulx3s.py
blob: d4e1c88b33a61b35480790e27abac8b266ac75a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import os
import argparse
import subprocess
import shutil

from nmigen.build import *
from nmigen.vendor.lattice_ecp5 import *
from .resources import *


__all__ = [
    "ULX3S_12F_Platform", "ULX3S_25F_Platform",
    "ULX3S_45F_Platform", "ULX3S_85F_Platform"
]


class _ULX3SPlatform(LatticeECP5Platform):
    package                = "BG381"
    speed                  = "6"
    default_clk            = "clk25"

    resources = [
        Resource("clk25", 0, Pins("G2", dir="i"), Clock(25e6), Attrs(IO_TYPE="LVCMOS33")),

        # Used to reload FPGA configuration.
        Resource("program", 0, PinsN("M4", dir="o"), Attrs(IO_TYPE="LVCMOS33", PULLMODE="UP")),

        *LEDResources(pins="B2 C2 C1 D2 D1 E2 E1 H3",
            attrs=Attrs(IO_TYPE="LVCMOS33", DRIVE="4")),
        *ButtonResources(pins="R1 T1 R18 V1 U1 H16",
            attrs=Attrs(IO_TYPE="LVCMOS33", PULLMODE="DOWN")
        ),
        *ButtonResources("switch", pins="E8 D8 D7 E7",
            attrs=Attrs(IO_TYPE="LVCMOS33", PULLMODE="DOWN")
        ),

        # Semantic aliases by button label.
        Resource("button_pwr",   0, PinsN("D6", dir="i"), Attrs(IO_TYPE="LVCMOS33", PULLMODE="UP")),
        Resource("button_fire",  0, Pins("R1",  dir="i"), Attrs(IO_TYPE="LVCMOS33", PULLMODE="DOWN")),
        Resource("button_fire",  1, Pins("T1",  dir="i"), Attrs(IO_TYPE="LVCMOS33", PULLMODE="DOWN")),
        Resource("button_up",    0, Pins("R18", dir="i"), Attrs(IO_TYPE="LVCMOS33", PULLMODE="DOWN")),
        Resource("button_down",  0, Pins("V1",  dir="i"), Attrs(IO_TYPE="LVCMOS33", PULLMODE="DOWN")),
        Resource("button_left",  0, Pins("U1",  dir="i"), Attrs(IO_TYPE="LVCMOS33", PULLMODE="DOWN")),
        Resource("button_right", 0, Pins("H16", dir="i"), Attrs(IO_TYPE="LVCMOS33", PULLMODE="DOWN")),

        # FTDI connection.
        UARTResource(0, 
            rx="M1", tx="L4", rts="M3", dtr="N1", role="dce",
            attrs=Attrs(IO_TYPE="LVCMOS33")
        ),
        Resource("uart_tx_enable", 0, Pins("L3", dir="o"), Attrs(IO_TYPE="LVCMOS33")),

        *SDCardResources(0,
            clk="J1", cmd="J3", dat0="K2", dat1="K1", dat2="H2", dat3="H1",
            attrs=Attrs(IO_TYPE="LVCMOS33", SLEW="FAST")
        ),

        # SPI Flash clock is accessed via USR_MCLK instance.
        Resource("spi_flash", 0,
            Subsignal("cs",   PinsN("R2", dir="o")),
            Subsignal("copi", Pins("W2", dir="o")),
            Subsignal("cipo", Pins("V2", dir="i")),
            Subsignal("hold", PinsN("W1", dir="o")),
            Subsignal("wp",   PinsN("Y2", dir="o")),
            Attrs(PULLMODE="NONE", DRIVE="4", IO_TYPE="LVCMOS33")
        ),

        SDRAMResource(0,
            clk="F19", cke="F20", cs="P20", we="T20", cas="T19", ras="R20", dqm="U19 E20",
            ba="P19 N20", a="M20 M19 L20 L19 K20 K19 K18 J20 J19 H20 N19 G20 G19",
            dq="J16 L18 M18 N18 P18 T18 T17 U20 E19 D20 D19 C20 E18 F18 J18 J17",
            attrs=Attrs(PULLMODE="NONE", DRIVE="4", SLEWRATE="FAST", IO_TYPE="LVCMOS33")
        ),

        # SPI bus for ADC.
        SPIResource("adc", cs="R17", copi="R16", cipo="U16", clk="P17",
            attrs=Attrs(IO_TYPE="LVCMOS33", PULLMODE="UP")),

        # TRRS audio jack
        Resource("audio", 0,
            Subsignal("l", Pins("E4 D3 C3 B3", dir="o")),
            Subsignal("r", Pins("A3 B5 D5 C5", dir="o")),
            Subsignal("ring2", Pins("A3 B5 D5 C5", dir="o")), # extra ring out for video adapters
        ),

        # ESP-32 connections
        Resource("esp32", 0,
            Subsignal("en",     Pins("F1", dir="o"), Attrs(PULLMODE="UP")),
            Subsignal("tx",     Pins("K3", dir="o"), Attrs(PULLMODE="UP")),
            Subsignal("rx",     Pins("K4", dir="i"), Attrs(PULLMODE="UP")),
            Subsignal("gpio0",  Pins("L2"),          Attrs(PULLMODE="UP")),
            Subsignal("gpio5",  Pins("N4")),
            Subsignal("gpio16", Pins("L1"),          Attrs(PULLMODE="UP")),
            Subsignal("gpio17", Pins("N3"),          Attrs(PULLMODE="UP")),
            Attrs(IO_TYPE="LVCMOS33", DRIVE="4")
        ),

        # PCB antenna, tuned to 433MHz
        Resource("ant", 0, Pins("G1", dir="o"), Attrs(IO_TYPE="LVCMOS33")),

        # Differential versions of our connector I/O.
        Resource("diff_gpio", 0, DiffPairs("B11", "C11"), Attrs(IO_TYPE="LVCMOS33")),
        Resource("diff_gpio", 1, DiffPairs("A10", "A11"), Attrs(IO_TYPE="LVCMOS33")),
        Resource("diff_gpio", 2, DiffPairs("A9", "B10"), Attrs(IO_TYPE="LVCMOS33")),
        Resource("diff_gpio", 3, DiffPairs("B9", "C10"),  Attrs(IO_TYPE="LVCMOS33")),

        DirectUSBResource(0,
            d_p="D15", d_n="E15", pullup="B12",
            attrs=Attrs(IO_TYPE="LVCMOS33")
        )
    ]

    connectors = [
        Connector("gpio", 0, {
            "0+": "B11",  "0-":  "C11", "1+":  "A10", "1-":  "A11",
            "2+": "A9",   "2-":  "B10", "3+":  "B9",  "3-":  "C10",
            "4+": "A7",   "4-":  "A8",  "5+":  "C8",  "5-":  "B8",
            "6+": "C6",   "6-":  "C7",  "7+":  "A6",  "7-":  "B6",
            "8+": "A4",   "8-":  "A5",  "9+":  "A2",  "9-":  "B1",
            "10+": "C4",  "10-": "B4",  "11+": "F4",  "11-": "E3",
            "12+": "G3",  "12-": "F3",  "13+": "H4",  "13-": "G5",
            "14+": "U18", "14-": "U17", "15+": "N17", "15-": "P16",
            "16+": "N16", "16-": "M17", "17+": "L16", "17-": "L17",
            "18+": "H18", "18-": "H17", "19+": "F17", "19-": "G18",
            "20+": "D18", "20-": "E17", "21+": "C18", "21-": "D17",
            "22+": "B15", "22-": "C15", "23+": "B17", "23-": "C17",
            "24+": "C16", "24-": "D16", "25+": "D14", "25-": "E14",
            "26+": "B13", "26-": "C13", "27+": "D13", "27-": "E13",
        })
    ]

    @property
    def required_tools(self):
        return super().required_tools + [
            "openFPGALoader"
        ]

    def toolchain_prepare(self, fragment, name, **kwargs):
        overrides = dict(ecppack_opts="--compress")
        overrides.update(kwargs)
        return super().toolchain_prepare(fragment, name, **overrides)

    def toolchain_program(self, products, name):
        tool = os.environ.get("OPENFPGALOADER", "openFPGALoader")
        with products.extract("{}.bit".format(name)) as bitstream_filename:
            subprocess.check_call([tool, "-b", "ulx3s", '-m', bitstream_filename])


class ULX3S_12F_Platform(_ULX3SPlatform):
    device                 = "LFE5U-12F"


class ULX3S_25F_Platform(_ULX3SPlatform):
    device                 = "LFE5U-25F"


class ULX3S_45F_Platform(_ULX3SPlatform):
    device                 = "LFE5U-45F"


class ULX3S_85F_Platform(_ULX3SPlatform):
    device                 = "LFE5U-85F"


if __name__ == "__main__":
    from .test.blinky import *
    
    variants = {
        '12F': ULX3S_12F_Platform,
        '25F': ULX3S_25F_Platform,
        '45F': ULX3S_45F_Platform,
        '85F': ULX3S_85F_Platform
    }
    
    # Figure out which FPGA variant we want to target...
    parser = argparse.ArgumentParser()
    parser.add_argument('variant', choices=variants.keys())
    args = parser.parse_args()

    # ... and run Blinky on it.
    platform = variants[args.variant]
    platform().build(Blinky(), do_program=True)