nmigen icon indicating copy to clipboard operation
nmigen copied to clipboard

Support silkscreen references (and other information) in platform definitions

Open mithro opened this issue 5 years ago • 19 comments

The new Atlys platform definition in #15 does the following;

Resource("user_led", 0, Pins("U18",  dir="o"), Attrs(IOSTANDARD="LVCMOS33")),       # LD0
Resource("user_led", 1, Pins("M14",  dir="o"), Attrs(IOSTANDARD="LVCMOS33")),       # LD1
Resource("user_led", 2, Pins("N14",  dir="o"), Attrs(IOSTANDARD="LVCMOS33")),       # LD2
Resource("user_led", 3, Pins("L14",  dir="o"), Attrs(IOSTANDARD="LVCMOS33")),       # LD3
Resource("user_led", 4, Pins("M13",  dir="o"), Attrs(IOSTANDARD="LVCMOS33")),       # LD4
Resource("user_led", 5, Pins("D4",   dir="o"), Attrs(IOSTANDARD="LVCMOS33")),       # LD5
Resource("user_led", 6, Pins("P16",  dir="o"), Attrs(IOSTANDARD="LVCMOS33")),       # LD6
Resource("user_led", 7, Pins("N12",  dir="o"), Attrs(IOSTANDARD=bank2_iostandard)), # LD7

Resource("user_btn", 0, PinsN("T15", dir="i"), Attrs(IOSTANDARD=bank2_iostandard)), # RESET
Resource("reset"   , 0, PinsN("T15", dir="i"), Attrs(IOSTANDARD=bank2_iostandard)), # RESET
Resource("user_btn", 1, Pins("N4",   dir="i"), Attrs(IOSTANDARD="LVCMOS18")),       # BTNU
Resource("user_btn", 2, Pins("P4",   dir="i"), Attrs(IOSTANDARD="LVCMOS18")),       # BTNL
Resource("user_btn", 3, Pins("P3",   dir="i"), Attrs(IOSTANDARD="LVCMOS18")),       # BTND
Resource("user_btn", 4, Pins("F6",   dir="i"), Attrs(IOSTANDARD="LVCMOS18")),       # BTNR
Resource("user_btn", 5, Pins("F5",   dir="i"), Attrs(IOSTANDARD="LVCMOS18")),       # BTNC

Resource("user_sw" , 0, Pins("A10",  dir="i"), Attrs(IOSTANDARD="LVCMOS33")),       # SW0
Resource("user_sw" , 1, Pins("D14",  dir="i"), Attrs(IOSTANDARD="LVCMOS33")),       # SW1
Resource("user_sw" , 2, Pins("C14",  dir="i"), Attrs(IOSTANDARD="LVCMOS33")),       # SW2
Resource("user_sw" , 3, Pins("P15",  dir="i"), Attrs(IOSTANDARD="LVCMOS33")),       # SW3
Resource("user_sw" , 4, Pins("P12",  dir="i"), Attrs(IOSTANDARD=bank2_iostandard)), # SW4
Resource("user_sw" , 5, Pins("R5",   dir="i"), Attrs(IOSTANDARD=bank2_iostandard)), # SW5
Resource("user_sw" , 6, Pins("T5",   dir="i"), Attrs(IOSTANDARD=bank2_iostandard)), # SW6
Resource("user_sw" , 7, Pins("E4",   dir="i"), Attrs(IOSTANDARD="LVCMOS18")),       # SW7

UARTResource(0, rx="A16", tx="B16", attrs=Attrs(IOSTANDARD="LVCMOS33")),            # J17/UART

It would be really useful to expose the information in the comments to a user.

I think this makes the most sense for LEDs and switches, but can work for connectors too.

With this information you could even then write something which lets you specify silk screen reference values in a console and have them mapped to the right switch / led.

For the complicated boards like the Digilent Atlys we went even further and provided the following;

_hdmi_infos = {
    "HDMI_IN0_MNEMONIC": "J1",
    "HDMI_IN0_DESCRIPTION" : (
      "  Type A connector, marked as J1, on side with USB connectors.\\r\\n"
      "  To use J1, make sure:\\r\\n"
      "   * JP4 has a jumper (it connects / disconnects 5V to HDMI pin 18).\\r\\n"
      "   * JP2 (marked only as SDA/SCL - not to be confused with JP6 and JP6)\\r\\n"
      "     has *two* jumpers (horizontally).\\r\\n"
      "   * JP5 has a jumper (it enables the HDMI input buffer).\\r\\n"
    ),


    "HDMI_IN1_MNEMONIC": "J3",
    "HDMI_IN1_DESCRIPTION" : (
      "  Type A connector, marked as J3, between audio connectors and\\r\\n"
      "  Ethernet RJ45 connector.\\r\\n"
      "  To use J3, make sure:\\r\\n"
      "  * JP8 has a jumper (it connects / disconnects 5V to HDMI pin 18)\\r\\n"
      "  * JP6 and JP7 do *not* have any jumpers (it connect J3's and J2's\\r\\n"
      "    EDID lines together).\\r\\n"
    ),


    "HDMI_OUT0_MNEMONIC": "J2",
    "HDMI_OUT0_DESCRIPTION" : (
      "  Type A connector, marked as J2, next to the power connector.\\r\\n"
      "  To use J2, make sure:\\r\\n"
      "  * JP8 has a jumper (it connects / disconnects 5V to HDMI pin 18)\\r\\n"
      "  * JP6 and JP7 do *not* have any jumpers (it connect J3's and J2's\\r\\n"
      "    EDID lines together).\\r\\n"
    ),


    "HDMI_OUT1_MNEMONIC": "JB",
    "HDMI_OUT1_DESCRIPTION" : (
      "  Micro-D connector, marked as JB, on the same side as switches\\r\\n"
      "  + LEDs but on the underside of the board below MOD connector.\\r\\n"
      "  Works as either output or input because it isn't buffered.\\r\\n"
      "  Also often referred to as 'JA'.\\r\\n"
    )
}

I feel like this information should be in some type of docstring associated with connectors / resources?

mithro avatar Jul 09 '19 00:07 mithro

How would it be presented to the user?

whitequark avatar Jul 09 '19 00:07 whitequark

I would suggest that the;

  • Resource object have an optional attribute called silkscreen_reference which takes an arbitrary string.
  • Resource objects have an optional attribute called location_information which takes an arbitrary string which can be used to describe to a human were to find this resource on the development board.
  • Resource objects can be given a __doc__ string which can just have arbitrary human information about the resource which doesn't fit elsewhere.

Then you could do something like;

  leds = {}
  while True:
    l = platform.get_resource("user_led")
    if not l:
      break
    leds[l.silkscreen_reference or 'led{}'.format(len(leds))] = l

   self.gpio = GPIO(len(leds))
   for i, (name, l) in enumerate(sort(leds.items())):
     m.d.sync += l.eq(self.gpio.reg[i])
     if l.silkscreen_reference:
       m.define('USER_LED{}'.format(i), l.silkscreen_reference)

mithro avatar Jul 09 '19 00:07 mithro

What is m.define?

whitequark avatar Jul 09 '19 02:07 whitequark

Was thinking something like add_constant

class BaseSoC(SoCSDRAM):
    def __init__(self, platform, **kwargs):
        self.add_constant("SPIFLASH_PAGE_SIZE", platform.spiflash_page_size)
        self.add_constant("SPIFLASH_SECTOR_SIZE", platform.spiflash_sector_size)

mithro avatar Jul 09 '19 02:07 mithro

That doesn't explain what it does.

whitequark avatar Jul 09 '19 03:07 whitequark

Provides a define in the C header file. CSRConstant could be another option.

Or you could write out a CSV or other configuration file?

mithro avatar Jul 09 '19 03:07 mithro

Writing C header files is definitely not something that should be a part of core nMigen.

whitequark avatar Jul 09 '19 03:07 whitequark

I agree that the output side probably doesn't make sense in nMigen's core -- was just trying to provide some type of example of how it might end up getting used.

As this information tends to end up in comments in the board / platform file anyway, it seems a good idea to allow a more structured format that other tools can then use / depend on?

I also can't think of a logical way for this information to be provided in an external file / package while still being kept in sync with the board files. Do you have any ideas?

My thinking is kind of like how type hints don't /do/ anything in Python directly but other tools can reuse them. Type hints kind of came out of people putting the information in their numpy / Google style docstrings too...

Open to alternative suggestions / proposals.....

mithro avatar Jul 09 '19 03:07 mithro

We should definitely have the ability to add refres information to resources. Perhaps Resource(..., Refdes("U3"))? Or maybe Silk("U3").

I am not so sure about the __doc__ strings and how to best handle them.

whitequark avatar Jul 09 '19 03:07 whitequark

I like Silk over Refdes (I assume Reference Designator)?

Something like a freeform __doc__, help or doc would be a good thing to have? Python docstrings are kind of a superpower....

mithro avatar Jul 09 '19 04:07 mithro

Something like a freeform __doc__, help or doc would be a good thing to have? Python docstrings are kind of a superpower....

The main problem here is to decide what can they be attached, what is the syntax for that, and how should they be retrieved. It's hard to see this just from hypotheticals, and without a concrete use case.

whitequark avatar Jul 09 '19 04:07 whitequark

These example comments in the litex-buildenv Opsis platform definition would be candidates to end up in docstring type stuff in my opinion;

    ## Opsis I2C Bus
    # Connected to both the EEPROM and the FX2.
    #
    ## 24AA02E48 - component U23
    # 2 Kbit Electrically Erasable PROM
    # Pre-programmed Globally Unique, 48-bit Node Address
    # The device is organized as two blocks of 128 x 8-bit memory with a 2-wire serial interface.
    ## \/ Strongly pulled (2k) to VCC3V3 via R34
    #NET "eeprom_scl"           LOC =     "G6"       |IOSTANDARD =             I2C;     #                      (/Ethernet/MAC_SCL)
    #NET "eeprom_sda"           LOC =     "C1"       |IOSTANDARD =             I2C;     #                      (/Ethernet/MAC_SDA)
    ("opsis_i2c", 0,
        Subsignal("scl", Pins("G6"), IOStandard("I2C")),
        Subsignal("sda", Pins("C1"), IOStandard("I2C")),
    ),

    ## DDR3
    # MT41J128M16JT-125:K - 16 Meg x 16 x 8 Banks - DDR3-1600 11-11-11
    # FBGA Code: D9PSL, Part Number: MT41J128M16 - http://www.micron.com/support/fbga
    ("ddram_clock", 0,
        Subsignal("p", Pins("K4")),
        Subsignal("n", Pins("K3")),
        IOStandard("DIFF_SSTL15_II"), Misc("IN_TERM=NONE")
    ),
    ("ddram", 0,
        Subsignal("cke", Pins("F2"), IOStandard("SSTL15_II")),
        Subsignal("ras_n", Pins("M5"), IOStandard("SSTL15_II")),
        Subsignal("cas_n", Pins("M4"), IOStandard("SSTL15_II")),
        Subsignal("we_n", Pins("H2"), IOStandard("SSTL15_II")),
        Subsignal("ba", Pins("J3 J1 H1"), IOStandard("SSTL15_II")),
        Subsignal("a", Pins("K2 K1 K5 M6 H3 L4 M3 K6 G3 G1 J4 E1 F1 J6 H5"), IOStandard("SSTL15_II")),
        Subsignal("dq", Pins(
                    "R3 R1 P2 P1 L3 L1 M2 M1",
                    "T2 T1 U3 U1 W3 W1 Y2 Y1"), IOStandard("SSTL15_II")),
        Subsignal("dqs", Pins("N3 V2"), IOStandard("DIFF_SSTL15_II")),
        Subsignal("dqs_n", Pins("N1 V1"), IOStandard("DIFF_SSTL15_II")),
        Subsignal("dm", Pins("N4 P3"), IOStandard("SSTL15_II")),
        Subsignal("odt", Pins("L6"), IOStandard("SSTL15_II")),
        Subsignal("reset_n", Pins("E3"), IOStandard("LVCMOS15")),
        Misc("SLEW=FAST"),
        Misc("VCCAUX_IO=HIGH")
    ),

    ## onboard HDMI IN1
    ## HDMI - connector J5 - Direction RX
    ("hdmi_in", 0,
        Subsignal("clk_p", Pins("L20"), IOStandard("TMDS_33")),
        Subsignal("clk_n", Pins("L22"), IOStandard("TMDS_33")),
        Subsignal("data0_p", Pins("M21"), IOStandard("TMDS_33")),
        Subsignal("data0_n", Pins("M22"), IOStandard("TMDS_33")),
        Subsignal("data1_p", Pins("N20"), IOStandard("TMDS_33")),
        Subsignal("data1_n", Pins("N22"), IOStandard("TMDS_33")),
        Subsignal("data2_p", Pins("P21"), IOStandard("TMDS_33")),
        Subsignal("data2_n", Pins("P22"), IOStandard("TMDS_33")),
        Subsignal("scl", Pins("T21"), IOStandard("LVCMOS33")),
        Subsignal("sda", Pins("R22"), IOStandard("LVCMOS33")),
        Subsignal("hpd_en", Pins("R20"), IOStandard("LVCMOS33"))
    ),

    ## onboard HDMI IN2
    ## HDMI - connector J4 - Direction RX
    ("hdmi_in", 1,
        Subsignal("clk_p", Pins("M20"), IOStandard("TMDS_33")),
        Subsignal("clk_n", Pins("M19"), IOStandard("TMDS_33")),
        Subsignal("data0_p", Pins("J20"), IOStandard("TMDS_33")),
        Subsignal("data0_n", Pins("J22"), IOStandard("TMDS_33")),
        Subsignal("data1_p", Pins("H21"), IOStandard("TMDS_33")),
        Subsignal("data1_n", Pins("H22"), IOStandard("TMDS_33")),
        Subsignal("data2_p", Pins("K20"), IOStandard("TMDS_33")),
        Subsignal("data2_n", Pins("L19"), IOStandard("TMDS_33")),
        Subsignal("scl", Pins("L17"), IOStandard("LVCMOS33")),
        Subsignal("sda", Pins("T18"), IOStandard("LVCMOS33")),
        Subsignal("hpd_en", Pins("V19"), IOStandard("LVCMOS33"))
    ),

The _hdmi_infos[XXX_DESCRIPTION] fields would also be candidates...

Maybe we should do some more conversion of the the Opsis / Atlys board files and see if we can shake something out? The Atlys board porting is already were the idea of Silk / Designators kind of came from.

mithro avatar Jul 09 '19 04:07 mithro

Alright. I'll see what I can do about this.

whitequark avatar Jul 09 '19 04:07 whitequark

Not directly silkscreen data but another idea I had is to provide possibility of having a picture of a board (attached to|included in) a Platform class. This would allow IDEs to present this picture to users in the platform selection window. Connectors could also provide coordinates of the connectors on the picture.

Fatsie avatar Jul 09 '19 19:07 Fatsie

@Fatsie That is something I have wanted to do for a long time too.

However, I feel like that is better done through creating a file format which is something like a collection of image files + XML/JSON description of were things are. If that file format also had silk references it would be easy to connect the nMigen platform and the file together in a GUI / emulation environment?

mithro avatar Jul 09 '19 19:07 mithro

@mithro Having this more in-depth documentation in another repo and have nmigen-boards as submodule is an alternative. From the other side, having this data inside python environment makes it is also accessible from jupyter and the like. One of my dreams is to have some (fancy) Jupyter workbooks using nMigen online as our cloud fpga/ASIC development platform.

Fatsie avatar Jul 09 '19 20:07 Fatsie

@Fatsie - I think the images + connector (+button+led) locations information would be useful for tools like @renode and QEmu -- not just nmigen. It is also likely that only a small number of boards which are used in nmigen will ever get this information -- hence why I'm thinking they should be separate....

mithro avatar Jul 09 '19 21:07 mithro

FYI - @mgielda - @pgielda

mithro avatar Jul 09 '19 23:07 mithro

Having this more in-depth documentation in another repo and have nmigen-boards as submodule is an alternative.

That seems extremely prone to desynchronization.

whitequark avatar Jul 10 '19 04:07 whitequark