Sign in to follow this  
Followers 0
wander_who

ST routine logic causing a program fault

5 posts in this topic

Hi all, Apologies for the length of this post.. but I'd really appreciate some advice... I have some ST code generating a program fault (major fault), and it is driving me (CompactLogix controller, rev 15.3) Normally, none of the code is causing a fault - it verifies correctly, and does what I expect it to do... regardless if I enter it as an online edit, or download the program to the controller... However, when the PLC is switched from program to run, or the PLC powers up, I get a major fault (type 4 code 83 - out of range / data tested not in required limits). I have my controller fault routine clearing the fault and generating an alarm message, but I have disabled it, in order to find out where in the program the fault is happening. I first found the problem in one of my ST routines, made some changes that seemed to fix it, and tried to apply those changes to a second ST routine - but got inconsistent results! I also narrowed down what was happening, when trying to change the second ST routine (see notes below) - I managed to get it to fault based on the value of one my index tags... The first ST routine, original code: - The purpose of this ST routine is to clear, then check and set, some temperature deviation alarms, based on the deviation flags from a PIDE instruction. - This routine is called by a JSR from the parent routine. - Tags "Dev_Alarms_Low" & "Dev_Alarms_High" are of type DINT[2] - Tags "PID_Dev_Low_Alarms" & "PID_Dev_High_Alarms" are of type DINT[2]. One bit of each is set from a one of 32 PIDE instructions. - The tags "Dev_Alarms_Low[0]", "Dev_Alarms_High[0]", "Dev_Alarms_Low[1]", "Dev_Alarms_High[1]" are returned to the parent routine via the JSR, as DINT tags "Alarms[4]", "Alarms[6]", "Alarms[5]", "Alarms[7]". - The tag "Alarms" (not used inside the ST routine), is of type DINT[18] [color="#3366ff"](* Receive parameters from parent routine *) SBR(Number_Zones, Current_Temp, Current_SP, Dev_Alarms_Low[0], Dev_Alarms_Low[1], Dev_Alarms_High[0], Dev_Alarms_High[1] ) ;[/color] [color="#3333ff"][color="#3366ff"](* Clear alarm flags *) For Index_1 := 0 To 31 By 1 Do[/color] [color="#ff0000"][faults here][/color] [/color][color="#3366ff"] Dev_Alarms_Low[0].[Index_1] := 0 ; Dev_Alarms_Low[1].[Index_1] := 0 ; Dev_Alarms_High[0].[Index_1] := 0 ; Dev_Alarms_High[1].[Index_1] := 0 ; End_For ;[/color] [color="#3333ff"][color="#3366ff"]For Index_2 := 0 To (Number_Zones - 1) By 1 Do (* Set devs if average temp greater than 200degC *) If (Current_Temp[Index_2] > 2000) Then (* Check devs for PIDEs 1 to 32 *) If (Index_2 <= 31) Then Dev_Alarms_Low[0].[Index_2] := PID_Dev_Low_Alarms[0].[Index_2] ; Dev_Alarms_High[0].[Index_2] := PID_Dev_High_Alarms[0].[Index_2] ; (* Check devs for PIDEs 33+ *) ElsIf (Index_2 >= 32) Then[/color] [color="#ff0000"][faults here][/color] [/color][color="#3366ff"] Dev_Alarms_Low[1].[Index_2 - 32] := PID_Dev_Low_Alarms[1].[Index_2 - 32] ; Dev_Alarms_High[1].[Index_2 - 32] := PID_Dev_High_Alarms[1].[Index_2 - 32] ; End_If ; End_If ; End_For ;[/color] [color="#3366ff"](* Return parameters to parent routine *) RET(Dev_Alarms_Low[0], Dev_Alarms_Low[1], Dev_Alarms_High[0], Dev_Alarms_High[1]) ;[/color] - The controller faults at the first 'For...Do' loop (clearing alarms), and at the second 'For...Do' loop (setting alarms). - So I changed the code to this: The first ST routine, changed code: [color="#3366ff"](* Receive parameters from parent routine *) SBR(Number_Zones, Current_Temp, Current_SP, Dev_Alarms_Low[0], Dev_Alarms_Low[1], Dev_Alarms_High[0], Dev_Alarms_High[1] ) ;[/color] [color="#3366ff"](* Clear alarm flags *) Dev_Alarms_Zero_DINT := 0 ; COP(Dev_Alarms_Zero_DINT,Dev_Alarms_Low[0],31) ; COP(Dev_Alarms_Zero_DINT,Dev_Alarms_Low[1],31) ; COP(Dev_Alarms_Zero_DINT,Dev_Alarms_High[0],31) ; COP(Dev_Alarms_Zero_DINT,Dev_Alarms_High[1],31) ;[/color] [color="#3366ff"]For Index_2 := 0 To (Number_Zones - 1) By 1 Do (* Set devs if average temp greater than 200degC *) If (Current_Temp[Index_2] > 2000) Then (* Check devs for PIDEs 1 to 32 *) If (Index_2 <= 31) Then Dev_Alarms_Low[0].[Index_2] := PID_Dev_Low_Alarms[0].[Index_2] ; Dev_Alarms_High[0].[Index_2] := PID_Dev_High_Alarms[0].[Index_2] ; (* Check devs for PIDEs 33+ *) ElsIf (Index_2 >= 32) Then Index_3 := Index_2 - 32 ; Dev_Alarms_Low[1].[Index_3] := PID_Dev_Low_Alarms[1].[Index_3] ; Dev_Alarms_High[1].[Index_3] := PID_Dev_High_Alarms[1].[Index_3] ; End_If ; End_If ; End_For ;[/color] [color="#3366ff"](* Return parameters to parent routine *) RET(Dev_Alarms_Low[0], Dev_Alarms_Low[1], Dev_Alarms_High[0], Dev_Alarms_High[1]) ;[/color] - This fixed the fault for the first 'For..Do loop' (used COP instead), and seemed to fix the second 'For...Do" loop as well. All good, or so I thought. - So I was going to apply these changes to a another ST routine that works in a similar way (that was giving me the same fault with the original code).... Second ST routine, original code - The structure of the tags & the logic for this routine is similar to the first. [color="#3333ff"][color="#3366ff"]If Offset_Number_Zones < Offset_Number_TCs Then For Index_13 := Offset_Number_Zones To (Offset_Number_TCs - 1) By 1 Do If Index_13 <= 31 Then If (Not(Alarm[2].[Index_13])) And (Not(Alarm[12].[Index_13])) Then Offset_Current_Temp[Index_13] := Offset_TC_Temp[Index_13] ; Else Offset_Current_Temp[Index_13] := Offset_Average_Temp ; End_If ; ElsIf Index_13 >= 32 Then[/color] [/color][color="#ff0000"][faults here][/color] [color="#3333ff"][color="#3366ff"] If (Not(Alarm[3].[Index_13 - 32])) And (Not(Alarm[13].[Index_13 - 32])) Then Offset_Current_Temp[Index_13] := Offset_TC_Temp[Index_13] ; Else Offset_Current_Temp[Index_13] := Offset_Average_Temp ; End_If ; End_If ; End_For ; End_If ;[/color] [/color] - So I made the same changes as I did for the first ST routine: Second ST routine, changed code: [color="#3366ff"]If Offset_Number_Zones < Offset_Number_TCs Then For Index_13 := Offset_Number_Zones To (Offset_Number_TCs - 1) By 1 Do If Index_13 <= 31 Then [color="#3333ff"] [/color][color="#ff0000"][faults here!!][/color] If (Index_13 < 31) And (Not(Alarm[2].[Index_13])) And (Not(Alarm[12].[Index_13])) Then Offset_Current_Temp[Index_13] := Offset_TC_Temp[Index_13] ; Else Offset_Current_Temp[Index_13] := Offset_Average_Temp ; End_If ; ElsIf Index_13 >= 32 Then Index_26 := (Index_13 - 32) ; If (Index_26 < 31) And (Not(Alarm[3].[Index_26])) And (Not(Alarm[13].[Index_26])) Then Offset_Current_Temp[Index_13] := Offset_TC_Temp[Index_13] ; Else Offset_Current_Temp[Index_13] := Offset_Average_Temp ; End_If ; End_If ; End_For ; End_If ; [/color] - I was expecting this fix the fault... but instead, I was getting a fault in the first "If... Then' loop, rather than the second !! - I tried setting the tag "Index_13" prior to the 'For..Do' loop, but that did not work. - I noticed that the value of the tag "Index_13" was always 33 after the routine was executed. If I then changed the PLC from Run to Program and back to Run, the controller would fault. However, if I first manually set tag "Index_13" to zero (type in a value of zero in the tag monitoring / editing window), then changed the controller to Run mode, it would not fault!! So... I can't find what is wrong. Is there something fundamental that I am doing wrong? The easy fix is not to set any alarms in my program for this fault type & code.. but I don't like that, I'd like to fix it properly... Can anyone help me ?! Cheers, Andrew W

Share this post


Link to post
Share on other sites
Andrew, The key piece of information here is that the fault happens when you transition from program to run and no other time. That indicates that your problem is with the "pre-scan". Basically, what is happening is that the processor is running a check on the code before it executes it. One of the things it checks are indices. If you have a tag like Index_13 with a value outside the range of 0-31 and you use it in an expression as the bit reference of a DINT tag, you are going to get a fault. Note that this happens even if the code would never allow the expression to be executed with an illegal value. If you don't need all 32 bits, you could cut the For-Next down to 0 to 30, that should work (it just wastes bit 31). I can't guaruntee that this will work, but give it a try. Simply set Index_13 to zero immediately after the For-Next loop. You do have a very small chance of catching the processor with a transition to program mode just before it executes the line "set index_13 to zero". If that happens, you still fault. My real problem with this approach though is it adds executable code that doesn't really do anything. It looks like dead code and someone may delete it and not associate that action with the problem (could be a while before you have a program to run transition). A more elegant solution would probably involve reorganizing your data to avoid indexing a bit in a DINT. A BOOL array will do much the same job, and the size of its index is not limited. Good luck,

Share this post


Link to post
Share on other sites
Mellis is dead on. I ran into a similiar situation with indirect addressing of I/O bits. Pointer 1 went from 0 to 32 and code never allowed for a bit greater than 16 but it faulted until I cahnged the logic. Pre-Scan can be a killer in indirect schemes.

Share this post


Link to post
Share on other sites
Hi guys, Thanks for the replies. Makes sense - and explains why the I was getting the faults only when the index tag had a value of greater than 31... I am looking at changing the DINT's to an array of BOOLs... is messy but I could do it... Pre-scan.. this got me thinking, could I just modify my fault handling routine to not set an alarm for this type of fault, during pre-scan only? Is considered the lazy way out? Also, reading up on pre-scan, I noticed that the manaul states: "During prescan, the controller automatically clears any faults due to an array subscript that is beyond the range of the array (out of range)." for revision 13.0 controllers or later.. So since I am using revision 15.3, and getting fault type 4 code 83, does this mean that I shouldn't be getting a fault during pre-scan ?? (evidently not!) Cheers, Andrew W

Share this post


Link to post
Share on other sites
Andrew, Part way through writing my reply I thought "Didn't they fix this in rev 13?". But you're not using an array currently, you're using a bit reference and that's the difference.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0