Skip to content

Ip 408 interrupt support#4

Open
grasswang wants to merge 5 commits into
epics-modules:masterfrom
grasswang:IP-408-interrupt-support
Open

Ip 408 interrupt support#4
grasswang wants to merge 5 commits into
epics-modules:masterfrom
grasswang:IP-408-interrupt-support

Conversation

@grasswang

Copy link
Copy Markdown

No description provided.

@MarkRivers MarkRivers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intFunc probably needs some changes for the IP408. The logic to clear the interrupt looks like it should be OK, except you don't want to be accessing the High registers that are NULL. The logic near the end with invertMask does not apply to the IP408, because it can be told to interrupt on Change of State. The IPUnidig cannot, so the logic is more complicate to change the polarity register depending on the current state of the input.

Comment thread ipUnidigApp/src/drvIpUnidig.cpp Outdated
}

*regs_.intPolarityRegisterLow = (epicsUInt16)polarityMask_;
*regs_.intPolarityRegisterHigh = (epicsUInt16)(polarityMask_ >> 16);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line will generate an access violation because you have defined regs_.intPolarityRegisterHigh =NULL.
You either need to change this line to test for !NULL or you need to put it inside a model_ dependent code.

Comment thread ipUnidigApp/src/drvIpUnidig.cpp Outdated
regs_.outputRegisterLow = base + 0x2;
regs_.outputRegisterHigh = base + 0x3;
regs_.intEnableRegisterLow = base + 0x4;
regs_.intEnableRegisterHigh = NULL;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to check that the register addresses that you set to NULL are never dereferenced.

@MarkRivers MarkRivers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure that you do not have any Tab characters in the file, only use the space character. It looks like perhaps there are tabs, but I can't tell for sure on Github.

Is the new version you just committed working with interrupts?

@grasswang grasswang left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this commit, I was able to get the interrupt from the first channel.
This IP408 is getting a 10us TTL pulse at Ch0. There are no input to other channels.
The setting in st.cmd is
initIpUnidig("Unidig1", 0, 0, 100, 0xd0, 1,0)"
This setup rising edge interrupts for Ch0.
Setting with both edge also works
initIpUnidig("Unidig1", 0, 0, 100, 0xd0, 1,1)"
However, if I try to turn on more channels, or using falling edge, the Vxworks freeze or rebooting
initIpUnidig("Unidig1", 0, 0, 100, 0xd0, 0,1)" //falling edge
initIpUnidig("Unidig1", 0, 0, 100, 0xd0, 3,0)" // rising edge for Ch0 and Ch1

@grasswang

Copy link
Copy Markdown
Author

Please make sure that you do not have any Tab characters in the file, only use the space character. It looks like perhaps there are tabs, but I can't tell for sure on Github.

Is the new version you just committed working with interrupts?

Thanks Mark! I committed a new version and untabify the file.
The interrupt works if without specific setup in st.cmd, the VxWorks freeze or rebooting.
I am not sure if that is what is suppose to be, or there is something I can fix.

@MarkRivers

Copy link
Copy Markdown
Member

The interrupt works if without specific setup in st.cmd, the VxWorks freeze or rebooting.

I am not sure what you mean by that?

Your code looks OK. I don't understand why it crashes if you only enable the falling edge, or if you enable Ch1.

What happens if you only enable rising on Ch1, and move the pulse source to that channel?

@grasswang

Copy link
Copy Markdown
Author

The interrupt works if without specific setup in st.cmd, the VxWorks freeze or rebooting.

I am not sure what you mean by that?

Your code looks OK. I don't understand why it crashes if you only enable the falling edge, or if you enable Ch1.

What happens if you only enable rising on Ch1, and move the pulse source to that channel?

I suspect that falling edge problem is because the pulse is only 10us, so there is only 10 us TTL high before the falling edge happens. Due to Covid restriction, I actually don't have access to the hardware now. When my colleague is on-site next time, he could help me to plug it to another channel, or flip the pulse. Thanks Mark!

@grasswang

grasswang commented Apr 12, 2021

Copy link
Copy Markdown
Author

I did some tests; it looks like this IOC crash because the pulse is short. If I flipped the pulse then the IOC crashs if I setup the rising edge for interrupt. Turn on the neighboring channels crashes the IOC when there is a short positive pulse, but seem to be ok when there is a short negative pulse (normally 5V, the 10 us pulse to 0V)

### input 10us positive pulse, in ch1

====when ch1 is not setup for interrupt============
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x5,0x5) -- didn't crash; neighboring channels are COS, but ch1 is not set for interrupt
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x0,0x1) ----didn't crash; one of neighboring channel is set for falling edge
====crash ====================================
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x0,0x2) -- crash --(ch1 failing)
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x7,0x2) -- crash -- neighboring channels of ch1 are also setup for interrupt (ch1 COS)
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x3,0x2) -- crash -- one neighboring channel of ch1 is setup for interrupt (ch1 COS)
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x3,0x0) -- crash -- one neighboring channel of ch1 is setup for interrupt (ch1 rising)
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x7,0x0) -- crash -- neighboring channels of ch1 are setup for interrupt (ch1 rising)
====works!!====================================
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x2,0x2) --Good --getting interrupt at channel1 for once each pulse, (ch1 COS)
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x2,0) --Good --getting interrupt at channel1 for once each pulse, (ch1 rising)

input 10us negative pulse (normally 5V, goes to 0V for 10 us), in ch1

initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x5,0x5) -- didn't crash; neighboring channels are COS, but ch1 is not set for > interrupt
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x0,0x1) ----didn't crash; one of neighboring channel is set for falling edge
====crash ====================================
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x2,0) --crash-- (ch1 rising)
====works!!====================================
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x2,0x2) --Good -- getting interrupt at channel1 for once each pulse, (ch1 COS)
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x0,0x2) --Good -- getting interrupt at channel1 for once each pulse, (ch1 falling)
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x2,0x7) -- Good-- neighboring channels of ch1 are also setup for interrupt (ch1 COS)
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x2,0x3) -- Good-- one neighboring channel of ch1 is setup for interrupt (ch1 COS)
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x0,0x3) -- Good-- one neighboring channel of ch1 is setup for interrupt (ch1 falling)
initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x7,0x0) -- Good-- neighboring channels of ch1 are setup for interrupt (ch1 falling)

@anjohnson

Copy link
Copy Markdown
Member

I wonder if the IOC is crashing on a too-short input pulse because the board's interrupt is being cleared at the beginning of the interrupt routine instead of at the end. I usually put code that clears an interrupt state as the last thing the ISR does, since I don't want subsequent hardware events to trigger another interrupt until the routine has finished handling the first event, so that aspect of the code here looks a bit strange to me. I recommend moving the code that writes to the intClearRegisters to after the lines that queue the message and adjust the interrupt polarity.

The thing you have to be careful about with modern CPUs when exiting an ISR is to make sure that the CPU has actually flushed all the register write operations out to the hardware before it returns from the interrupt. To do that the code must read back a value from the last register that it wrote to; for an example see the end of an ISR in my IPAC module although that may be a bit more complicated than should be needed here.

@MarkRivers

MarkRivers commented Apr 12, 2021

Copy link
Copy Markdown
Member

This seems strange.

These tests seem to show that the problem is not electrical cross-talk causing interrupts on other channels.

initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x5,0x5) -- didn't crash; neighboring channels are COB, but ch1 is not set for interrupt

This crash does not make sense to me. Even if what Andrew says is valid, there should not be a second interrupt because you are only triggering on the trailing (falling) edge of a short positive pulse , and the next pulse comes much later.

initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x0,0x2) -- crash --(ch1 faliing)

Have you looked at the pulse on a scope right near the IP module? Could there be ringing that is causing the TTL threshold to be crossed a second time?

What happens if you stretch the pulse width, to 100 us or 1 ms? Is the crash pattern the same? If not that could mean that it is indeed a second interrupt on the other edge happening before the first is complete.

@grasswang

Copy link
Copy Markdown
Author

I modified the IntFunc() as below according to Andrew's comment.
I still have the same crash. This time I was lucky enough to catch the error message.

sorry I miss spelled Change Of Status (COS) before.
I am wondering if the problem is second interrupt, the COS should have doubled the interrupts compare with the single edge. I don't understand why this code works fine with setting to "COS" and "the leading edge" but fail to the tailing edge.
I don't have access to the hardware yet; I will ask my co-worker to make the pulse longer next week and see what happens. Thanks!

=====Error after crash ====

workQPanic: Kernel work queue overflow.

Exception at interrupt level:
Regs at 0x0

Exception at interrupt level:

program
Exception current instruction address: 0x00000000
Machine Status Register: 0x00001230
Condition Register: 0x44002484
Exception Syndrome Register: 0x08000000
Regs at 0x3121b8

============Updated intFunc()======

 void IpUnidig::intFunc()
{
  ipUnidigRegisters r = regs_;
  epicsUInt32 inputs=0, pendingLow, pendingHigh, pendingMask, invertMask;
  ipUnidigMessage msg;
  volatile epicsUInt16 *flushLow = NULL;
  volatile epicsUInt16 *flushHigh = NULL;

  /* Clear the interrupts by copying from the interrupt pending register to the interrupt clear register */
  switch (model_) {
      case ACROMAG_IP408_32:
          pendingLow = *r.intPendingRegisterLow  & IP408_8CH_MASK;
          pendingMask = pendingLow;
          break;
      default:
          pendingLow = *r.intPendingRegisterLow;
          pendingHigh = *r.intPendingRegisterHigh;
          pendingMask = pendingLow | (pendingHigh << 16);
      break;
  }
  /* Read the current input.  Don't use read() because that can print debugging. */
  if (r.inputRegisterLow)  inputs = (epicsUInt32) *r.inputRegisterLow;
  if (r.inputRegisterHigh) inputs |= (epicsUInt32) (*r.inputRegisterHigh << 16);
  msg.bits = inputs;
  msg.interruptMask = pendingMask;
  if (epicsMessageQueueTrySend(msgQId_, &msg, sizeof(msg)) == 0)
    messagesSent_++;
  else
    messagesFailed_++;

  switch (model_) {
      case ACROMAG_IP408_32:
      /*if the rising edge and falling edge can both generate interrupt, in the IP408 it should change controlRegister, not the PolarityRegister*/
           *r.intClearRegisterLow = pendingLow;
           flushLow = r.intClearRegisterLow;
           break;
      default:
           /* Are there any bits which should generate interrupts on both the rising  and falling edge, and which just generated this interrupt? */
           invertMask = pendingMask & risingMask_ & fallingMask_;
           if (invertMask != 0) {
               /* We want to invert all bits in the polarityMask that are set in * invertMask. This is done with xor.*/
                 polarityMask_ = polarityMask_ ^ invertMask;
                 *r.intPolarityRegisterLow  = (epicsUInt16) polarityMask_;
                 *r.intPolarityRegisterHigh = (epicsUInt16) (polarityMask_ >> 16);
           }
           *r.intClearRegisterLow = pendingLow;
           flushLow = r.intClearRegisterLow;
           *r.intClearRegisterHigh = pendingHigh;
           flushHigh = r.intClearRegisterHigh;
           break;
     
  }
  if (flushLow){
    *r.intClearRegisterLow = *flushLow;
  }
  if (flushHigh){
    *r.intClearRegisterHigh= *flushHigh;
  }

@MarkRivers

Copy link
Copy Markdown
Member

workQPanic: Kernel work queue overflow.
Exception at interrupt level:
Regs at 0x0

That error almost certainly does mean that it got a second interrupt before finishing the first, or it is getting interrupts too fast.

I could understand that error happening with a very short pulse and interrupts on both leading and trailing edge. But if you only enable one edge or the other I don't understand that error. That is why I suggested looking for ringing on the signal, because that could cause multiple closely spaced interrupts.

@grasswang

Copy link
Copy Markdown
Author

These tests seem to show that the problem is not electrical cross-talk causing interrupts on other channels.

initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x5,0x5) -- didn't crash; neighboring channels are COB, but ch1 is not set for interrupt

I realized that I forgot an important test while reading Mark's comment again.
What if we turn off interrupt on ch1 but turn on the interrupt of trailing edge on neighboring channels?
We have a negative pulse now, the trailing edge of 10us pulse is the rising edge.

initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x5,0) -- crash

That indicates we might have a cross-talk issue.

@MarkRivers

Copy link
Copy Markdown
Member

What kind of connectors do you have on these signals? Are you using one of the BCDA Lemo breakout panels?

I suggest putting shorting plugs or 50 ohm terminators on the unused channels. If you do that does it still crash with this test?

initIpUnidig("Unidig1", 0, 0, 1, 0xd0,0x5,0) -- crash

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

Successfully merging this pull request may close these issues.

3 participants