Jump to content
  • 0

DVI-to-RGB (DVI2RGB) differential signal polarity issues - how to invert?


tschesnok

Question

I'm using a Digilent FMC-HDMI card on a Trenz FPGA board (Artix-7) and the FMC connector has a few inverted differential signals (All but TMDS Data2 are inverted)

The DVI-to-RGB IP has no way to invert signals and I think there is no way to do it but to make changes in the IP code. I'm a Verilog guy at best.. and it is written in VHDL.

I see a file called InputSERDES.vhd which seems to contain the IBUFDS:

InputBuffer: IBUFDS
   generic map (
      DIFF_TERM  => FALSE,
      IOSTANDARD => "TMDS_33")
   port map (
      I          => sDataIn_p,
      IB         => sDataIn_n,
      O          => sDataIn);

And I know this is where I can swap the _p and the _n.

But the input is an array.. and this would swap them all I assume. I'm guessing this code here gets instantiated 3x some place.. and I would have to create a second file - one that is different.. and make a lot of changes to make it all work?

Is there a simple step by step way? Has anyone dealt with this before? VHDL gives me a headache.. especially top end stuff like decoding video.

Many thanks! 

Link to comment
Share on other sites

15 answers to this question

Recommended Posts

19 hours ago, tschesnok said:

And I know this is where I can swap the _p and the _n.

Well, not necessarily. Sometimes people laying out traces on a PCB will swap _n and _p pins to improve routing. I've run into this on numerous occasions.

When trying to use an FMC mezzanine card on a carrier that doesn't have a working demo for that pair you need to exercise caution and do your due diligence. This means tracing every signal on both schematics right through to the FPGA pin names ( fortunately Digilent shows these on their FPGA board schematics which makes the user's life a lot easier. Don't assume that any FMC mezzanine card will work with any FPGA carrier board having a similarly pinned (LPC/HPC) FMC connector.

I understand your issues with VHDL. 80% if what I do is VHDL and, because you can write very terse and obscure Verilog my headaches generally come from Verilog code written by people who like to impress themselves with their Verilog knowledge. If you spend any significant length of time doing FPGA development you will eventually realize that it isn't a VHDL verses Verilog world ( ignoring System Verilog and C-like HDLs ). You need at least a working knowledge of both and any complex designs are likely to use both, When it makes sense I generally just instantiate Verilog modules in my VHD code. Sometimes this doesn't work as well as it should because the Verilog module takes advantage of Verilog features that the tools trip up on, Usually, though it works out fine. You can always write a toplevel wrapper in your preferred HDL. Usually, this route takes an understanding of the most basic keywords and concepts of either HDL.

My guess is that your problem will be discovered in figuring out the complete signal paths rather than looking at the code.

The point I'm making as a general statement is that choosing a random FMC mezzanine card, even if it has supporting HW code for a specific FPGA carrier board, and expecting it to work on any other FPGA carrier board, even with significant effort, is a dangerous way to live. It's all about IO banks, clocking, and FPGA pin assignments; except where an FMC signal doesn't have connectivity on both boards. I've run into that too. And don't forget to pay attention to the power and ground signals.

[edit] If you are convinced that there really is a polarity swap of differential signals on the physical traces of your board setup relative to the one that your code used it might be a simple matter of swapping pin location assignments in the constraints file.

Link to comment
Share on other sites

Hi Zygot,

Thanks for taking time to answer.

Yes, the reason I'm swapping pins is because I traced them. I don't think you can just swap pins in the constraint file. (but If I'm wrong I would like to know since that would make life easy!) It is my experience (and understanding) that Vivado will complain when generating the bitstream when it finds a mismatch between pin polarity and IBUFDS. 

Of course now I'm thinking.. how does it know.. I'm guessing it understands the pin names assigned.. I don't think I can trick it.. but would love to hear suggestions.

A

Link to comment
Share on other sites

Oh.. and since I have you.. how precisely would you unrole this for loop below:

note: I will need to create a new file for TMDS_Decoder called "TMDS_Decoder_Inverse" which links to another changed file "SERDES_Decode_Inverse".. and then use these for iCh "2". - I mean I can't believe what an amazing pain it is to change pin polarity in an array of differential signals.. in a 3rd party IP. WTF :) 

 

-- Three data channel decoders
DataDecoders: for iCh in 2 downto 0 generate
   DecoderX: entity work.TMDS_Decoder
      generic map (
         kCtlTknCount => kMinTknCntForBlank, --how many subsequent control tokens make a valid blank detection (DVI spec)
         kTimeoutMs => kBlankTimeoutMs, --what is the maximum time interval for a blank to be detected (DVI spec)
         kRefClkFrqMHz => 200, --what is the RefClk frequency
         kIDLY_TapValuePs => kIDLY_TapValuePs, --delay in ps per tap
         kIDLY_TapWidth => kIDLY_TapWidth) --number of bits for IDELAYE2 tap counter   
      port map (
         aRst                    => pLockLostRst,               
         PixelClk                => PixelClk_int,
         SerialClk               => SerialClk_int,   
         RefClk                  => RefClk,          
         pRst                    => pRst_int,
         sDataIn_p               => TMDS_Data_p(iCh),                           
         sDataIn_n               => TMDS_Data_n(iCh),                                       
         pOtherChRdy(1 downto 0) => pRdy((iCh+1) mod 3) & pRdy((iCh+2) mod 3), -- tie channels together for channel de-skew
         pOtherChVld(1 downto 0) => pVld((iCh+1) mod 3) & pVld((iCh+2) mod 3), -- tie channels together for channel de-skew
         
         pC0                     => pC0(iCh),
         pC1                     => pC1(iCh),                    
         pMeRdy                  => pRdy(iCh),                
         pMeVld                  => pVld(iCh),                
         pVde                    => pDE(iCh),                  
         pDataIn(7 downto 0)     => pDataIn(iCh),   
         dbg_pEyeSize            => dbg_pEyeSize(iCh),
         dbg_pAlignErr           => dbg_pAlignErr(iCh),       
         dbg_pBitslip            => dbg_pBitslip(iCh)
      );
      
--end generate DataDecoders;
 

 

Link to comment
Share on other sites

2 hours ago, tschesnok said:

It is my experience (and understanding) that Vivado will complain when generating the bitstream when it finds a mismatch between pin polarity and IBUFDS.

I can't confirm or deny that for VIvado, but I'm pretty sure that I did this for an ISE project a few years ago. I don't know why the tools would care if you swapped _n and _p pins; there's no reason that I'm aware of, from a logic type specification, why this can't be done. Theses are differential inputs after all. I always read tool warnings, the bulk of which are often from the tool generated code, but have learned that not all of them require action on my part. Warnings are an indication that the tools find your sources confusing and are asking if you really want to get what you are asking for. Sometimes, in these cases it's a good idea to check to make sure that what you want really is what the synthesis tool created. Errors, of course are another matter.

You can always swap or reassign constrain pin names in your toplevel entity or module. My first instincts when faced with a problem like you are having is not to start changing code in the lower hierarchical levels unless absolutely necessary. It's had to anticipate how big a hole you'll end up with if you just start digging.

Sometimes I'm faced with a dilemma; do I want to change the pin names in a master constraints file or do I want to use those names in my HDL design when they don;t really make sense for the project. Connector pin names like JAx, LAx, etc. are typical. Sometimes I just end up creating new signal names in my toplevel entity and assign the port names to these so that at least the names make more sense when I'm reading it. This might be a solution to your second question as well. But VHDL is the language that I'm more comfortable in so I'm used to typing... My general approach to resolving these types of problems is to try reasonable, but quick and easy, solutions first. Editing location constraints would be the easiest, and least difficult to undo ( if you keep the original constraint file saved somewhere safe ), way of resolving differential signal polarity issues that I know of.

I really resist the temptation to add logic to get the polarity that I want as this can introduce new problems that might not be obvious.

Link to comment
Share on other sites

3 hours ago, tschesnok said:

Of course now I'm thinking.. how does it know.. I'm guessing it understands the pin names assigned..

Often for both Intel and Xilinx provide code that I've seen there isn't even an attempt to assign _n pins a location as the tools do know what _p is and that there is a corresponding _n pin which it also seems to know the location of from the explicit device part number specified in the project settings. Tools from all FPGA vendors try to infer stuff like structures and functionality unless you tell them not to. Usually this is good as your code gets replaced with something better that has the same behavior. This is not always the case however and users should not be afraid to modify the default synthesis and implementation settings when appropriate. It would be nice if all of this could be done in HDL source code, but alas, this is not the case. You have to hunt down which settings have HDL keywords and which ones need to be done in the constraints file or using TCL. The people who do FPGA seriously don't use GUI tools. Their projects are all a mixture of TCL, PERL, Make and other scripting languages because that's really the only way to control and maintain long lived project code.

Link to comment
Share on other sites

From what I read, the hardware is fixed up until the IBUFDS. From there you can do what you want. P had to attache to IBUFDS p since it is a physical link. 

 

Please: I need some VHDL help in unrolling the for loop above. Specifically.. originally it creates 3 instances called DataDecoders[x].DecoderX where the small x is the instance number. How do I recreate that when unrolling it? If I simply rename the DecoderX as DecoderX_0 (_1, _2 etc) I get timing errors since the SERDES are no longer "associated".. 
 

I would be so grateful if you could write it out. (don't worry about replacing any of the "iCh" in the block.

Do I declare DataDecoders(2 downto 0) in some way and then??  AHHH.. headache :)

Link to comment
Share on other sites

Well, even though I'd advise against your approach that's up to you.

Since I don't have all of the information I'm not comfortable meeting your request in the way that you want it. I think that you have to work this out fro yourself and take some aspirin.

The generate statement is just a nice way to put repetitive statements in a more terse form and make the text easier to read, You are correct that it creates 3 instances of whatever is in the generate - end generate statement; in this case a component instantiation. You can replace that by commenting out the generate statements and replicating the contents three times, replacing the iCh index with the actual std_logic_vector element numbers; that is 2,1 and 0. You are just connecting std_logic_vector elements to the appropriate signals in the  TMDS_Decoder component.

When I bump into Verilog code that I don't fully understand I accept that as an opportunity to learn a new skill. Not the answer you are looking for but the one that I think is in you best interests.

You could also cross you fingers and hope that someone else wants to be more accommodating. I suspect that you'll resolve this faster with a little research.

Link to comment
Share on other sites

From what I gather, the only way to swap polarity on a differential pair is AFTER  IBUFDS. (that seems to be confirmed all over the internet).

In this particular case we have the code in the above FOR loop instantiating TMDS_Decoder 3x which in turn is instantiating inputSERDES 3x (not shown). inputSERDES contains the IBUFDS.  So.. the only way to change polarity is to change inputSERDES for only one of the 3 data channels. This means that the three TMDS_Decoders can NOT be identical.. and can't be called in the FOR loop. 

When I write it as 3 code blocks Vivdo complains about timing not being associated.. so something is different. Somehow the For loop is connecting them?? They are all elements in an array called DataDecoders.

How do I create something like DataDecoders(Index)DecoderX: entity work.TMDS_Decoder  where index is 2:0.??

 

OR

Is it possible to but conditions inside a For / Generate loop?

 

DataDecoders: for iCh in 2 downto 0 generate
   If (iCh == 2)
          DecoderX: entity work.TMDS_Decoder_Inv
         generic map (
         --maps
         port map (
         --maps
          );

  else

        DecoderX: entity work.TMDS_Decoder
         generic map (
         --maps
         port map (
         --maps
          );

 

probably not, right?

Many thanks.

 

Link to comment
Share on other sites

Here's an example of doing a conditional generate from the code that I posted in my differential pmod challenge in the Digilent Project Vault:

constant USE_DIFFERENTIAL_IO                      : natural range 1 downto 0 := 1; -- SourcePMOD/SinkPMOD use TMDS_33 when 1

  -- SourcePMOD output buffers
  -- If using eight PMOD LVCMOS33 IO you need to modify the following to generate 8 OBUFs and
  -- connect all *_n and *_p pins  
  GEN_obuf : for i in 3 downto 0  generate
    -- SourcePMOD LVCMOS33 output buffers
    -- OBUF: Single-ended Output Buffer
    -- 7 Series
    -- Xilinx HDL Libraries Guide, version 13.4   
    GEN_obufs : if USE_DIFFERENTIAL_IO = 0 generate
      OBUF_inst : OBUF
      generic map (
        DRIVE       => 16,
        IOSTANDARD  => "LVCMOS33",
        SLEW        => "FAST")
      port map (
        O => pmod_dsource_p(i), -- Buffer output (connect directly to top-level port)
        I => source_data_out(i) -- Buffer input
      );  
    end generate;
    -- SourcePMOD LVCMOS33 output buffers
    -- OBUFDS: Differential Output Buffer
    -- 7 Series
    -- Xilinx HDL Libraries Guide, version 13.4   
    GEN_obufd : if USE_DIFFERENTIAL_IO = 1 generate
      OBUFDS_inst : OBUFDS
      generic map (
        IOSTANDARD  => "TMDS_33", -- Specify the output I/O standard
        SLEW        => "FAST")    -- Specify the output slew rate as "SLOW" or "FAST"
      port map (
        O     => pmod_dsource_p(i),   -- Diff_p output (connect directly to top-level port)
        OB    => pmod_dsource_n(i),   -- Diff_n output (connect directly to top-level port)
        I     => source_data_out(i)   -- Buffer input
      );
    end generate;    
  end generate;

Really, I think that you are making this harder than it has to be. If manually assigning signals to 3 instances of a component instantiation is creating problems then something is wrong. I would agree that using the generate loop to instantiate a component 3 times isn;t the best way to go.. especially if you aren't sure of the syntax.

But you don' seem to like my suggestions and I don't seem to think that your choices are worth my time so perhaps we're at an impasse.

Another way to go, which I've done on many occasions is to just figure out the code I'm looking at and re-write it to suit my comfort level. I've done this for VHDL written in a way that I''m not fond of as well.

I realize that you really just want to get your board working. I'm betting that Trenz isn't providing much in the way of assistance. I'm doubtful that anyone from Digilent wants to get involved either. Sometimes you just have to accept the extra effort and take a positive attitude toward it. When avoiding something starts becoming more work than actually doing the work that makes sense it's time for a bit of reflection on one's life choices.

 

 

 

Link to comment
Share on other sites

5 hours ago, tschesnok said:

From what I read, the hardware is fixed up until the IBUFDS.

I'm having a hard time believing this. The tools don't know how to interpret hardware signal polarity from the names in location constraints, except for differential signal names like xxx_p and xxx_n. So try changing xxx_p and xxx_n to pxxx and nxxx.. or something else.  I've never done this but it seems like a simple trick to try, if time was an enemy. I think that the tools are aware of differential naming  conventions but doubt that they are that smart. Again, it that doesn't work you can always create new names in the toplevel entity and connect the port names names to these.

Where's the aspirin?

Link to comment
Share on other sites

That would be nice.. but it does not seem so.

Intel does it the right way.. You handle polarity and conversion the the pin level with the pin planner. No "code" in Verilog/VHLD deals with differential signals.

It seems with Xilinx you do it in text from in code via I/OBUFDS. 

IBUFDS directly controls how the signal is used.. and unfortunately because it is code.. and not a pin setting.. it can be hidden inside other peoples designs / IP. That is the problem here.. and when I make a change I'm now triggering timing issues in viviado unrelated to code functionality. (i.e. code works as intended.. but it needs timing constraints that the original design sidestepped. 

 

A

Link to comment
Share on other sites

14 hours ago, tschesnok said:

Intel does it the right way.. You handle polarity and conversion the the pin level with the pin planner. No "code" in Verilog/VHLD deals with differential signals.

Wrong. I don't use the GUI tools in either Quartus or Vivado to assign constraints. I just don't find them to be conducive to efficient project management. I prefer writing  my constraints in text form For Quartus these are in the .qsf file for pin locations and feature assignment or various .sdc timing constraint files; for Vivado this is one or more .xdc files. Quartus is just less willing to cooperate with users who want to control constraints for themselves. The 'code' in your Verilog/VHDL that deals with things like IOSTANDARDs, differential or single-ended, are where you instantiate buffer primitives. The designer is responsible for understanding every aspect of their design. As I mentioned previously all FPGA vendor tools support an inconsistent mishmash of constraints that can be inserted into HDL text, or perhaps constraints files, or perhaps needs to be in a TCL script or from the tool 'IDE' GUI. There just isn't any consistency or order to it. It's a hit or miss proposition for the user. Complain to your vendor not me. Unfortunately, I care and your vendor doesn't; so we have a common problem.

By the way, if you look hard enough you can find every constraint in a Vivado design. I'm not sure where you get the idea that any are 'hidden'.

The designer has to understand what the timing constraints for a given design needs to be. Changing the source code might require a change in timing constraints depending on how buffers drive and receive pin signals and how the clocking structure is set up. Conceivably, for IOSTANDARD logic where rise and fall times are different simply swapping polarity could require a alteration to the timing constraints. If you are going to muck with a design you are responsible for figuring this all out. If you are hoping to use an add-on card on an FPGA development board that doesn't support it and hope use code meant for another platform to work without needed adjustment then you are likely to be frustrated, particularly when you know from your due diligence that signal polarities are being swapped in the trace routing.

All FPGA vendors provide tools in a form that pushes users to adopt a design flow that is convenient, and financially advantageous to the vendor. They also know that companies who's income depends on FPGA related products need to do things differently so they provide a way for them to handle project management in a way that makes sense for a customer. After all, that's where the money comes from. If you spend enough time doing FPGA development you re aware that the development flow preferred by the vendors isn't always how you should or even want to conduct you work efforts. You don't have to. 

Link to comment
Share on other sites

For anyone following this - it looks like I resolved it and got DVI2RGB working with inverse TMDS signals:

1. You can't change a differential signal till AFTER IBUFDS. I tried.. no go. Vivado makes it very clear that positive is positive and negative is negative all the way into the IBUFDS. It has nothing to do with naming conventions.. it is hardware. The IBUFDS sits at the port of the chip PRIOR to FPGA fabric.

2. To get DVI2RGB working I had to break out separate versions of TMDS_Decoder and corresponding InputSERDES to get access to IBUFDS. I inverted the signal in the appropriate place for the correct pin.

3. Ran into timing issue and forgot I needed to change the "path" names in the constraint file to accommodate my changes to InputSERDES naming convention. (not a timing issue but a naming issue :)

That was it. Unfortunately for me it took a weekend. :)

Link to comment
Share on other sites

@tschesnok,

Pin inversion has been implemented in rgb2dvi for some time now. On Xilinx architectures this has to be done in logic and requires careful consideration where exactly in the (de)serialization pipeline can this be done without causing timing issues. The best place to do this is right before serialization (rgb2dvi) or right after deserialization (dvi2rgb), which is in the parallel (slow) clock domain and does not impact other features like token detection, phase alignment and channel bonding.

I implemented the feature on branch https://github.com/Digilent/vivado-library/compare/master...Digilent:feature/dvi2rgb_invertpn. If you have a board with inverted polarity, give it a try.

Inversion of the clock pins is an interesting subject. I am of the opinion that our implementation is not affected by clock polarity. A clock inversion should be compensated by the phase alignment and channel bonding features automatically.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...