Instructions out of order?

Discussion to talk about software related topics only.
DBrunermer
Posts: 67
Joined: Thu Apr 21, 2011 7:06 am
Location: Pittsburgh, PA

Instructions out of order?

Post by DBrunermer »

Hi, <MOD5272 ; KIT 1.12>

I'm seeing something that I don't understand, and seemingly can't do anything about. I'm also a bit of a noob with this hardware. I have a device that's kind of like a latched FIFO, but it's actually 8 shift-registers (SR) in parallel, with a common clock (active low). The devices are SuperTex HV5523s. Anyway, it has a 'front-latch' and a 'back-latch', and all together the device is controlling 256 bits of data. To move data from the back to the front, you pulse the latch high and low, and then the back-latch can be reloaded without disturbing the bits in the front.

I have these code snippets:

Early stuff:
volatile char * const data1 = (char *)0x02800000; // Always write to the same address, mapped to nCS3
#define SET_LAT = 0x80; // Bit 7 on Port C controls the latch bit
char * g_pCurReadIdx; // Pointer to data store to read from, byte at a time, in SDRAM

This is the routine inside of a 1kHz interrupt.

// Latch in the line you already shifted.
sim.pcdat |= SET_LAT;
__asm__ ("nop");
sim.pcdat &= ~SET_LAT;
__asm__ ("nop");

// Send in the next line to the latch
for( int i=0; i<32; i++ )
{
*data1 = *g_pCurReadIdx;
g_pCurReadIdx++;
}

I added the nop instructions to try and correct what I'm seeing, but it didn't work. When I put my oscope on the lines in question, that is, Chip Select 3 and the Latch, I get a strange result. Instead of seeing the latch go high followed by 32 writes to the shift register, The Latch goes high seemingly anywhere within the 32 writes, instead of before them. I've checked the scope and my setup, and the triggering is all correct, so I know it's not that. Is this some kind of weird caching issue? Or pipeline issue? Or interrupt issue? Any advice?

Thank you,
Dan B.
rnixon
Posts: 833
Joined: Thu Apr 24, 2008 3:59 pm

Re: Instructions out of order?

Post by rnixon »

Are you using the data bus? If so, which signals?
User avatar
tod
Posts: 587
Joined: Sat Apr 26, 2008 8:27 am
Location: Southern California
Contact:

Re: Instructions out of order?

Post by tod »

I don't know about pipeline, caching but it seems unlikely. I have seen optimization bugs in the past so you could try turning off the compiler optimization (or at least taking it down a level) to see if that changes anything but given your description it doesn't sound like an optimization issue.

In your interrupt, once you enter the interrupt are you disabling the interrupt and keeping it disabled until you finish the 32 writes? Your description makes it sound like one interrupt is being processed and another one gets triggered, which causes the latch to go again. Also are you masking out all other interrupts when this one is handled? You don't have to of course but that might explain why the timing is non-repeatable. I guess as a test I would slow down that 1KHz interrupt to something much slower and see if the behavior changes.
seulater
Posts: 445
Joined: Fri Apr 25, 2008 5:26 am

Re: Instructions out of order?

Post by seulater »

I think you should get the 32 writes out of the IRQ.
what i have done in the past for things like this is to create a pending task to do the functionality, in your case the 32 writes.

THen in the IRQ, you could do something like this

Code: Select all

 //*********************************************************************
//******************  IRQ 7 Used For Button Board  ********************
//*********************************************************************
INTERRUPT(irq7_isr, 0x2700)
{

	// Disable IRQ7, This will be re-enabled by the Button Board task when its complete.
	sim.eport.epier &=~ 0x80;

	// Release the Buuton Board Task to run, to see what was pressed
    OSSemPost(&BUTTON_BOARD_Sem);
}


then your waiting task would be something like this

Code: Select all

OS_SEM BUTTON_BOARD_Sem;
//*********************************************************************
//***********************  Button Board TASK  *************************
//*********************************************************************
void PCA9555_IRQ7_Task( void *pd )
{
	WORD 	temp_read;

	printf("Button Board Task Started\r\n");

	while(1)
	{
                //Wait here until IRQ flags us to run
		OSSemPend(&BUTTON_BOARD_Sem,0);


                // Do your 32 writes here. 



		// Clear IRQ7 Bit
		sim.eport.epfr |=0x80;

		// Enable IRQ7
		sim.eport.epier |= 0x80;

	}
}
rnixon
Posts: 833
Joined: Thu Apr 24, 2008 3:59 pm

Re: Instructions out of order?

Post by rnixon »

Remember IRQ7 is the non-maskable one. While you can disable it, you can't set the standard irq mask to prevent more interrupts from happening. This is where you typically see a system crash because the task stack overflows when you can't service IRQ 7 quick enough.
seulater
Posts: 445
Joined: Fri Apr 25, 2008 5:26 am

Re: Instructions out of order?

Post by seulater »

Ya, should have put a disclaimer in there, as it was for the code i was using. I just expected him to do the homework ;)
DBrunermer
Posts: 67
Joined: Thu Apr 21, 2011 7:06 am
Location: Pittsburgh, PA

Re: Instructions out of order?

Post by DBrunermer »

Wow guys, thanks for all the advice. I should have just put the whole function in here. seulater, I'm not sure if this would change your advice, but I'm generating the interrupt through a timer (sim.timer2 or TMR1, depending on how you want to define it), instead of through external hardware. And I also kind of need it to be completely deterministic. The sequence of events that has to unfold must be as close to perfect as I can possibly make it, and I worry that if I use an OS construct like a semaphore and a blocking function that I could see a high amount of variability in the actual timing of events. The shift has to happen when it has to happen, and if other functions with higher priority happened to unblock at the wrong time, it could really cause problems. There are probably ways to work around that, but I'm really not sophisticated enough to know what they are.

And Tod, I'll try the optimization settings first thing in the morning. I agree that it seems unlikely to be that. I'd considered reentry, but nothing about that makes sense to me either. When I watch the actual output pin that gets set inside the interrupt, it rings at 1kHz on the dot, and I always see only 32 shifts. And I never see the output trigger anywhere near the shifts. As for the masking of the interrupts, I hope I'm doing that right. I'm kind of afraid to mask them off, because I don't know what effect that has on the OS (as far as things like Ethernet are concerned). What I can say is, I don't clear the timer's interrupt signal until I'm ready to exit.

And rnixon, yes I'm using the buffered data bus for the actual transfers, bits 31->24. nCS3 is connected directly to the CLK pins of the supertex devices. Well, not directly, they're actually hooked up to RS422 driver/receiver pairs (2631's and 2632's). But that's a different story. I'm actually watching the signals all the way at the very endpoint, so I'm pretty sure the basic infrastructure is behaving the way I expect it to. OK, so maybe there's a bit more ring than I would like, but I don't think that's the issue.

INTERRUPT( FirePass, 0x2400)
{
// First thing: Set the pulseform output high as fast as possible.
i2 = g_PulseWidth; // Make local copy
sim.pwcr2 = 0x20; // Enable the fire pulse
for( int i=0; i<i2; i++ )
__asm__ ("nop"); // HACK - Find a better way
// Turn it off
sim.pwcr2 = 0x00;
// Insert space before toggling the latch in case the pulse is still above VCC
for( int i=0; i<10; i++ )
__asm__ ("nop"); // HACK - Find a better way

// Latch in the line you already shifted.
sim.pcdat |= SET_LAT;
__asm__ ("nop");
sim.pcdat &= ~SET_LAT;
__asm__ ("nop");
// Shift the next batch of lines
for( int i=0; i<32; i++ )
{
*data1 = *g_pCurReadIdx;
g_pCurReadIdx++;
}

g_LinesRendered++;
if( g_LinesRendered >= g_LinesToRender )
vector_base.table[70] = (long)&FirePassNLL;
sim.timer2.ter = 0x02; // clear Timer2 event condition

}

So, (boy I hate it when I run out of space on this board), one thing to note is how I actually have the output confgured. I had a string of questions over on the hardware board, and eventually settled on controlling the TMR1/PWM1 output by connecting the PWM to the pin, disabling the PWM, and changing the output by changing the 'output state when disabled' bit in its configuration register. That's what the assignments to .pwcr2 do. I know the rest of the code is ugly, but there it is. It switches a high voltage on for a period of time, switches it off, allows excess charge to dissipate for a bit (it's a capacitive load), pushes the latch from back to front, shifts, and if it has to, it switches the interrupt landing vector for handling the last two cases.

Thanks again, Dan B.
User avatar
tod
Posts: 587
Joined: Sat Apr 26, 2008 8:27 am
Location: Southern California
Contact:

Re: Instructions out of order?

Post by tod »

Dan,
I'm a bit bleary-eyed and may not be thinking straight but I thought you had to explicitly disable the timer2 interrupt and stop the timer as soon as you enter the interrupt if you don't want it to fire again. In case it helps here's some code from a recent project:

Code: Select all

    extern "C"
    {
        INTERRUPT(Timer2ISR, 0x2600)
        {
            Interrupt::DisableTimer2Interrupt();
            sim.timer2.ter &= 0x02; // clear Timer2 event condition
            Interrupt::StopTimer2();
            sim.timer2.tcn = 0; //reset the count
            //Here I typically POST -  and I'm done
...elided code
          }
}

where
    void Interrupt::DisableTimer2Interrupt()
    {
        sim.icr1 = ((sim.icr1 & ~TIMER2_MASK) | DISABLE_TIMER2_IRQ);
    }
and the constants are
            static const long DISABLE_TIMER2_IRQ = 0x00000800L; //  see 5272 UM section 7.2.2.1 (ICR - interrupt control reg)
            static const long ENABLE_TIMER2_IRQ = 0x00000E00L;
            static const unsigned long TIMER2_MASK = 0x00000F00L;
Then where I'm pending and doing the work, when I'm done I do this:

Code: Select all

    
void Interrupt::RestartTimer2(void)
    {
        EnableTimer2Interrupt();
        StartTimer2();
    }
    void Interrupt::EnableTimer2Interrupt()
    {
        sim.icr1 = ((sim.icr1 & ~TIMER2_MASK) | ENABLE_TIMER2_IRQ);
    }
    inline void Interrupt::StartTimer2()
    {
        //start timer with undivided system clock - ie every 15ns
        sim.timer2.tmr = (sim.timer2.tmr & 0xFFF9) | 0x0002; //clear bits 1 and 2 and then set bit 1 (counting from 0)
    }
rnixon
Posts: 833
Joined: Thu Apr 24, 2008 3:59 pm

Re: Instructions out of order?

Post by rnixon »

Ok, I was just asking about the databus to see if you were using the upper 8 signals and not the lower ones.
User avatar
tod
Posts: 587
Joined: Sat Apr 26, 2008 8:27 am
Location: Southern California
Contact:

Re: Instructions out of order?

Post by tod »

Dan - some debugging suggestions:
1. Slow Timer2 WAY down (say 100ms or 200 ms), see if the problem goes away. If it does speed up until it happens.
2. If it doesn't, at this slower rate (for debug purposes only) move to POST/PEND so that you do very little in the interrupt itself
3. Now in your PEND method that does the work you can use console I/O to write out some messages (which is why I suggested hundreds of ms delay). I typically output some single chars like . * + - etc so that I can verify the order of things happening.

I know you said you instrumented correctly but this can give you a sanity check.

Other suggestions:
1. Turn off the Timer2 interrupt and monitor the LE lines on your scope and make sure nothing else in your code is messing with them
2. Turn off all other interrupts, and see if the Timer2 loop behaves.
Post Reply