Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better FMC support #269

Open
kaolpr opened this issue Nov 25, 2022 · 1 comment
Open

Better FMC support #269

kaolpr opened this issue Nov 25, 2022 · 1 comment

Comments

@kaolpr
Copy link
Contributor

kaolpr commented Nov 25, 2022

For FMC TDC and Shuttler debugging, I've created some helpers for dealing with FMC that I'd like to submit to your critical review ;-).

Observations

  • FMC standard offers selectable VADJ and VIO_B_M2C voltages.
  • VADJ is set by the carrier (preferably based on EEPROM content).
  • VIO_B_M2C should be set by FMC (but it is not always obeyed by the carriers, see FMC VIO_B_M2C not connected sinara-hw/FMC_Shuttler#32).
  • According to specification, FPGA IO banks serving FMC Bank B IO pins should be powered by VIO_B_M2C.
  • For EEM modules ARTIQ use iostandard parameter, that is a function returning required IO standard. For EEM modules, it is always LVDS.

The problem

It seems to be beneficial to provide some decoupling between FMC module and platform specific code as it is done with e.g. differential inputs or synchronizers.

LA and HA banks can have different IO standards than HB (both single ended and differential). Those standards can be both platform-specific and FMC specific. Not all carriers support VADJ adjustment (e.g. FMC2 @ AFCK), and some do not accept VIO_B_M2C (e.g. EEM FMC Carrier). FMC also not always follow specification (e.g. Shuttler and VIO_B_M2C).

So for FMC modules using all 3 FMC banks and mixing single-ended and differential signals, a number of different IO standards can be in use. Hard-coding them in FMC module will impact decoupling from particular platform.

Passing them to the io function may be a solution, but it seems a bit cumbersome. The developer would then need to match signals with appropriate voltage levels. This can be easily performed inside platform class.

Suggested solution

For applications, I've come across, mostly two types of signals are used (excluding MGT and clocking that uses CLKx lines): single-ended IOs with appropriate voltage standard and LVDS. For that reason, I assumed that those two should be supported in the first place.

As bank voltages can not be really deducted due to different implementations, I assumed they have to be given on per gateware design basis. In my opinion, instantiation of the platform is a good place to pass desired voltage configuration.

The platform would then be able to provide appropriate IO standard for the given bank.

For example: defining a single-ended signal on bank LA using a platform initialized with VADJ=1.8V would lead to, in the case of Xilinx platform, LVCMOS18.

On the other hand, attempt to define differential signal (by default in LVDS standard) on VADJ=3.3V configured Kintex 7 platform would rise an exception, as such configuration is not supported.

Designer intent to use a specific type of IO standard (e.g. single-ended or differential) would be expressed by using an "IO standard keyword". Such keyword would be used when instantiating IOStandard class. During execution of add_extension method, the platform would replace "keyword IO standards" with those applicable to the given platform configuration.

In the example below, two keywords are used: single and diff, expressing will of using single-ended and differential LVDS types, respectively. More keywords can be implemented, if needed.

If one wants to force a specific IO standard (thereby breaking FMC-platform decoupling), instead of a keyword a desired IO standard prefixed with force:<IO standard> can be used. In the example, this feature is illustrated with IOStandard("force:FANCY_HARDCODED_STD").

Implementation example:

from migen import *
from migen.build.generic_platform import *
from migen.build.fmc import _fmc_pin
from migen.build.platforms.digilent_genesys2 import Platform as Genesys2


class DemoFMC:
    @classmethod
    def io(cls, fmc):
        return [
            (f"fmc{fmc}_single_ended", 0,
                Pins(_fmc_pin(fmc, "HB", 0, "p")),
                IOStandard("single")
            ),
            (f"fmc{fmc}_differential", 0,
                Subsignal("p", Pins(_fmc_pin(fmc, "LA", 0, "p"))),
                Subsignal("n", Pins(_fmc_pin(fmc, "LA", 0, "n"))),
                IOStandard("diff")
            ),
            (f"fmc{fmc}_fancy", 0,
                Pins(_fmc_pin(fmc, "HB", 2, "p")),
                IOStandard("force:FANCY_HARDCODED_STD")
            ),
        ]
    
    @classmethod
    def add_std(cls, target, fmc):
        target.platform.add_extension(cls.io(fmc))

        target.platform.request(f"fmc{fmc}_single_ended")
        target.platform.request(f"fmc{fmc}_differential")
        target.platform.request(f"fmc{fmc}_fancy")


class DemoTarget(Module):
    def __init__(self, platform):
        self.platform = platform


platform = Genesys2(fmc1_vadj=2.5, fmc1_vio_b_m2c=1.8)
target = DemoTarget(platform)
DemoFMC.add_std(target, 1)

platform.build(target, run=False, build_dir="./build")

Genesys2 has its IO standards defined in the following way:

iostd = {
  1.2: {"diff": "LVDS", "single": "LVCMOS12"},
  1.8: {"diff": "LVDS_25", "single": "LVCMOS18"},
  2.5: {"diff": "LVDS_25", "single": "LVCMOS25"},
  3.3: {"diff": None, "single": "LVCMOS33"},
}

Therefore, example code generates the following constraints:

 ## fmc1_single_ended:0
set_property LOC G13 [get_ports {fmc1_single_ended}]
set_property IOSTANDARD LVCMOS18 [get_ports {fmc1_single_ended}]
 ## fmc1_differential:0.p
set_property LOC D27 [get_ports {fmc1_differential_p}]
set_property IOSTANDARD LVDS_25 [get_ports {fmc1_differential_p}]
 ## fmc1_differential:0.n
set_property LOC C27 [get_ports {fmc1_differential_n}]
set_property IOSTANDARD LVDS_25 [get_ports {fmc1_differential_n}]
 ## fmc1_fancy:0
set_property LOC L15 [get_ports {fmc1_fancy}]
set_property IOSTANDARD FANCY_HARDCODED_STD [get_ports {fmc1_fancy}]

(...)
@sbourdeauducq
Copy link
Member

sbourdeauducq commented Nov 30, 2022

This could be generalized beyond FMC by simply defining platform-independent I/O standards, e.g. some Enum with common signaling standards that could be used in place of IOStandard. It seems that is the main point of this proposal. I would not bother with VADJ/VIO_B_M2C and leave it to the user to sort out at the Migen application level, it seems everybody has their own sauce there anyway.

On the other hand, attempt to define differential signal (by default in LVDS standard) on VADJ=3.3V configured Kintex 7 platform would rise an exception, as such configuration is not supported.

The rules there are complicated and best left to the vendor tools.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants