amaranth-lang / amaranth-boards

Board definitions for Amaranth HDL
BSD 2-Clause "Simplified" License
108 stars 110 forks source link

Test module that configure a given pin of a connector as output, and makes it blink -plug a LED on it- #204

Open sporniket opened 2 years ago

sporniket commented 2 years ago

followup of my comment for #128

typical use (adapted from https://github.com/sporniket/amaranth_sandbox/tree/686b90d42eee8e360d615373728c1ebc0c570ce7/board--colorlight-i9 )

# imports for writing the new platform
import os
import subprocess

from amaranth.build import *
from amaranth.vendor.lattice_ecp5 import *
from amaranth_boards.resources import *  # from .resources import *

# imports for writing the test
from blinky import *
from blinky_gpio import *

# === the platform ===
class Colorlight_I9_V7_2_Platform(LatticeECP5Platform):
    """See board info at https://github.com/wuxx/Colorlight-FPGA-Projects/blob/master/colorlight_i9_v7.2.md"""

    device = "LFE5U-45F"
    package = "BG381"
    speed = "6"
    default_clk = "clk25"

    resources = [
        Resource(
            "clk25", 0, Pins("P3", dir="i"), Clock(25e6), Attrs(IO_TYPE="LVCMOS33")
        ),
        *LEDResources(
            pins="L2", attrs=Attrs(IO_TYPE="LVCMOS33", DRIVE="4")
        ),  # the sample use LVCMOS25, but this pins is also accessible out of the board
    ]

    # no connectors for now
    connectors = [
        # 'P[2..6] connectors, 2x15 pins ; pins are listed row by row ; meaning first 2x6 pmod is from pin 1 to 12, second 2x6 pmod is from pin 18
        Connector(
            "p",
            2,
            "- - - - K18  L2 T18 C18 R17 R18 M17 P17 " #first pmod
            "U18 T17 - - P18 U17 " 
            "- - - - N18 N17 L20 M18 K20 L18 G20 J20" # second pmod
        ),
    ]

    @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, "-c", "cmsisdap", "-m", bitstream_filename])

# === the test ===

def askContinue():
    action = input("Continue ? (y/n) :")
    if action == "n":
        print("Bye.")
        exit()

if __name__ == "__main__":
    print("========================[ START testing onboard LED ]============================")
    Colorlight_I9_V7_2_Platform().build(Blinky(), do_program=True)
    askContinue()

    # list of addressable pins on the first row
    row_one = (5,7,9,11,13,17,23,25,27,29)
    row_two = (6,8,10,12,14,18,24,26,28,30)

    conn_index = 2 # provision for future loop
    print(f"========================[START testing connector 'p'#{conn_index} -- row 1]============================")
    print(f"INSTALL test rig on first row of connector {conn_index}")
    askContinue()
    for pin in row_one:
        print(f"------------------------> Pin Under Test : #{pin}")
        Colorlight_I9_V7_2_Platform().build(BlinkyGpio("p",2,pin), do_program=True)
        askContinue()

    print(f"========================[START testing connector 'p'#{conn_index} -- row 2]============================")
    print(f"INSTALL test rig on second row of connector {conn_index}")
    askContinue()
    for pin in row_two:
        print(f"------------------------> Pin Under Test : #{pin}")
        Colorlight_I9_V7_2_Platform().build(BlinkyGpio("p",2,pin), do_program=True)
        askContinue()

    print(f"========================[END testing connector 'p'#{conn_index}]============================")

    print("ALL DONE.")

Edit : see the script running : https://www.youtube.com/watch?v=eU3kbz5UF2Q

josuah commented 1 year ago

Some other method I discovered on someone else's project was building some scan chain:

connect physically all vertical pins of a PMOD, and then use RTL to connect them horizontally in a way that makes all pin connected into a single wire across the whole board:


   ┌───┬───┬───┬───┬───┬───┐   ┌───┬───┬───┬───┬───┬───┐   ┌───┬───┬───┬───┬───┬───┐
IN...........o │ o...o │ o...............o │ o...o │ o...............o │ o...o │ o...OUT
   ├───┼───┼─│─┼─│─┼─│─┼─│─┤   ├───┼───┼─│─┼─│─┼─│─┼─│─┤   ├───┼───┼─│─┼─│─┼─│─┼─│─┤
   │   │   │ o...o │ o...o │   │   │   │ o...o │ o...o │   │   │   │ o...o │ o...o │
   └───┴───┴───┴───┴───┴───┘   └───┴───┴───┴───┴───┴───┘   └───┴───┴───┴───┴───┴───┘

If the OUT signal follows the IN signal, then all pins are connected.

This does not prevent the presence of shorts between two pins, which would go undetected with that scheme.

Maybe some alternative pinout would be rquired to support alternative connectors.

josuah commented 1 year ago

Would that not be something to integrate on each project in their own way rather than upstream? Maybe there are more projects doing this style of testing than I thought...