hints for using '#const _wdt_type', option WDT, and system

General discussion relating to the library modules supplied with the compiler

Moderators: David Barker, Jerry Messina

Post Reply
Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

hints for using '#const _wdt_type', option WDT, and system

Post by Jerry Messina » Mon Jul 06, 2015 2:48 pm

The new SystemConvert utility adds a '#const _wdt_type' to the device files that can be used to determine the proper watchdog config type for the device.

note: the K40 devices add a new wdt config type, so now it's 'config WDT/WDTE/WDTEN'

Here's the settings and their meanings:

Code: Select all

'#const _wdt_type' can be used to identify the watchdog config type
	0   WDT(WDT) = [OFF, ON]
	1   WDTEN(WDTEN) = [OFF, ON]
	2   WDTEN(WDTEN) = [OFF, NOSLP, SWON, ON]
	3   WDTEN(WDTEN) = [OFF, NOSLP, ON, SWDTDIS]
	4   WDTE(WDTE) = [OFF, SWDTEN, NSLEEP, ON]

for _wdt_type = 0  (18F4520 SF default device):
    config WDT: Watchdog Timer Enable bit
    1 = WDT enabled
    0 = WDT disabled (control is placed on the SWDTEN bit)

for _wdt_type = 1  (18F14K50):
    config WDTEN: Watchdog Timer Enable bit
    1 = WDT is always enabled. SWDTEN bit has no effect
    0 = WDT is controlled by SWDTEN bit of the WDTCON register

for _wdt_type = 2 (18F2xK22):
    config WDTEN<1:0>: Watchdog Timer Enable bits
    11 (ON)    = WDT enabled in hardware (SWDTEN ignored)
    10 (SWON)  = WDT controlled by firmware (SWDTEN enabled)
    01 (NOSLP) = WDT enabled in hardware, disabled in Sleep mode (SWDTEN ignored)
    00 (OFF)   = WDT disabled in hardware (SWDTEN ignored)

for _wdt_type = 3 (18F2xK80):
    WDTEN<1:0>: Watchdog Timer Enable bits
    11 (SWDTDIS)= WDT is enabled in hardware; SWDTEN bit is disabled
    10 (ON)     = WDT is controlled by the SWDTEN bit setting
    01 (NOSLP)  = WDT is enabled only while the device is active and is disabled in Sleep mode; SWDTEN bit is disabled
    00 (OFF)    = WDT is disabled in hardware; SWDTEN bit is disabled

for _wdt_type = 4 (18FxxK40):
    WDTE<1:0>: WDT Operating Mode bits
    00 (OFF)    = WDT is disabled, SWDTEN is ignored
    01 (SWDTEN) = WDT is enabled/disabled by the SWDTEN bit in WDTCON0
    10 (NSLEEP) = WDT is enabled while sleep=0, suspended when sleep=1; SWDTEN is ignored
    11 (ON)     = WDT is enabled regardless of sleep; SWDTEN is ignored

most devices have a register bit SWDTEN somewhere that allows for software control of the wdt... 
usually found in WDTCON.bits(0) except for the J94/J99 family where it's RCON2.bits(5)

_wdt_types 0 and 1 are similar, except that the config bit changes names from WDT to WDTEN.
likewise, _wdt_types 2 and 3 are similar except the settings change meanings slightly, esp ON
What would you do with that? Several existing modules were written before WDTEN existed, and have to be modified to account for the changing config bit name in more recent devices.

For example, here's the code from the existing System.bas module:

Code: Select all

#option WDT = false
#if IsOption(WDT) And Not (WDT in (true, false))
   #error WDT, "Invalid option. WDT must be true or false."
#endif
#if WDT
   #if _device in (18F66J16)
   Config WDTEN = on
   #else
   Config WDT = on
   #endif
#endif
Public Inline Sub ClrWDT()
   #if WDT
   Asm-
      ClrWDT
   End Asm
   #endif   
End Sub
As you can see, there's a device-specific check to determine using 'Config WDTEN' vs 'Config WDT', and it only works for the 18F66J16.

Note: don't confuse '#option WDT' with 'Config WDT'... they're two different things. The '#option WDT' is a boolean true/false value. The compiler checks this value to determine if it will add a 'clrwdt' instruction to routines like DelayMS/DelayUS, and also how the ClrWDT() sub functions. If the value is true then the compiler inserts asm 'clrwdt' instructions, otherwise it doesn't.

That code above could be made a bit more generic by checking the value of '_wdt_type' instead of the device name...

Code: Select all

// account for WDT/WDTEN
#option WDT = false
#if IsOption(WDT) and not (WDT in (true, false))
   #error WDT, "Invalid option. WDT must be true or false."
#endif
#if (WDT)       // check option setting...
  // get _wdt_type from device file so we use the correct config
  #if (_wdt_type = 0)       // the config bit is named WDT
    config WDT = ON
  #elseif (_wdt_type = 4)     // the config bit is named WDTE
    config WDTE = ON
  #else                     // the config bit is named WDTEN
    config WDTEN = ON
  #endif
#endif
public inline sub ClrWDT()
   // check the #option setting. only add clrwdt if option is true (config = ON)
   #if (WDT)
   asm
      CLRWDT
   end asm
   #endif   
end sub
That fixes selecting 'Config WDT/WDTEN' for you, but the only problem with it is you're stuck using the value ON, and depending on the device you may want to use one of the other WDTEN settings.

EDIT: you probably don't want to use the following mods. See the later post as to why these aren't a good idea
Since the '#option WDT' needs to remain a boolean for the compiler, you can add a new '#option _WDT' that lets you set the desired WDT/WDTEN config setting and let it take care of '#option WDT' for you...

Code: Select all

// account for WDT/WDTEN and setting
// add new '#option _WDT' for the setting
#option _WDT = OFF
// make sure no one's using '#option WDT'... we need it later
#if (IsOption(WDT))
  #error "#option WDT no longer used...replace with #option _WDT"
#endif
// set old WDT boolean option based on _WDT setting
// this option controls internal libray usage of 'clrwdt'
#if (_WDT = OFF)
  #option WDT = false
#else
  #option WDT = true
#endif  
// get _wdt_type from device file so we use the correct config
#if (_wdt_type = 0)       // the config bit is named WDT
  config WDT = _WDT
#else                     // the config bit is named WDTEN
  config WDTEN = _WDT
#endif
public inline sub ClrWDT()
   // check the #option setting... only add clrwdt if not OFF
   #if (_WDT <> OFF)
   asm
      CLRWDT
   end asm
   #endif   
end sub
This lets you use the new option to set the desired config setting...

Code: Select all

device = 18f25k22  ' _wdt_type 2

// set '#option _WDT' to the desired config setting for your device (ie ON, OFF, NOSLP, SWON, etc)
// as long as it's not OFF then system.bas will enable the code to use the wdt and clrwdt
// (it defaults to OFF if not specified)
#option _WDT = SWON
include "system.bas"

// check to see if ClrWDT() produces a 'clrwdt' instruction (look at the asm code)
ClrWDT()

// check system SBDLYUS16 for clrwdt
delayus(100)
Last edited by Jerry Messina on Sun Oct 16, 2016 2:14 pm, edited 1 time in total.
Reason: modified to include new wdt_type 4 (k40)

bitfogav
Registered User
Registered User
Posts: 169
Joined: Sat Oct 09, 2010 1:39 pm
Location: United Kingdom

Re: hints for using '#const _wdt_type', option WDT, and syst

Post by bitfogav » Mon Jul 06, 2015 8:07 pm

Thanks for sharing this Jerry :)

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: hints for using '#const _wdt_type', option WDT, and syst

Post by Jerry Messina » Mon Jul 06, 2015 9:38 pm

Thanks, but rethinking my previous post, it may be better to forget creating a new _WDT option and just use the first modification above which checks for _wdt_type...

Code: Select all

#option WDT = false
#if IsOption(WDT) And Not (WDT in (true, false))
   #error WDT, "Invalid option. WDT must be true or false."
#endif
#if (WDT)
   // get _wdt_type from device file so we use the correct config
   #if (_wdt_type = 0)       // the config bit is named WDT
      config WDT = ON
   #else                     // the config bit is named WDTEN
      config WDTEN = ON
   #endif
#endif

public inline sub ClrWDT()
   #if (WDT)
   asm
      CLRWDT
   end asm
   #endif   
end sub
The _WDT option didn't account very well for _wdt_type's 0 and 1 (the ones with OFF/ON only). For those, OFF means "under control of the SWDTEN reg bit", and if you want to use SWDTEN you need the option to be true even though you're setting the config off ('#option WDT=true' and 'config WDT=OFF').

For the other types (2 and 3), config WDTEN=OFF really means OFF.. to use firmware control you have to use a different setting.

If you wish to change the config setting you can always do it AFTER including system.bas since the last 'config' statement overrides any previous ones...

Code: Select all

device = 18F4520    	// _wdt_type 0

#option WDT = true      // ensure blocking module calls tickle WDT
include "system.bas"
config WDT = OFF        // allows software control using SWDTEN
or

Code: Select all

device = 18F25K22  		// _wdt_type 2

#option WDT = true      // ensure blocking module calls tickle WDT
include "system.bas"
config WDTEN = SWON     // allows software control using SWDTEN
In the end that probably works out better.

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: hints for using '#const _wdt_type', option WDT, and syst

Post by Jerry Messina » Tue Jul 07, 2015 3:05 pm

Here's another example of using the _wdt_type settings.
This is an updated version of David's SoftwareWDT module posted on the wiki. It should work well with the modification to system.bas shown above.

Watchdog Module (wdt.bas)

Code: Select all

{
*****************************************************************************
*  Name    : Watchdog.bas                                                   *
*  Author  : David John Barker                                              *
*  Notice  : Copyright (c) 2007 Mecanique                                   *
*          : All Rights Reserved                                            *
*  Date    : 07/06/2015                                                     *
*  Version : 1.1                                                            *
*  Notes   : ORIGINAL NOTES v1.0:                                           *
*          : Enable or disable the WDT (and postscaler) under software      *
*          : control. As per the datasheet, enabling or disabling the WDT   *
*          : under software control will only work when the configuration   *
*          : bit has disabled the WDT. That is, if you set the WDT          *
*          : configuration fuse to ON, you CANNOT disable the WDT under     *
*          : software control                                               *
*          : Setting the #option WDT = true ensures that any modules with   *
*          : blocking calls tickle the watchdog timer. Note however that    *
*          : low level routines such as DelayMS WILL NOT tickle the WDT, as *
*          : the fuse setting is disabled - use the delay() routine in this *
*          : module for WDT safe delays                                     *
*  ver 1.1 : modified to use _wdt_type                                      *
*****************************************************************************
}
module Watchdog

//
// note: this module was originally written before devices used WDTEN, and 
// setting 'config WDT = OFF' enabled the use of SWDTEN in the WDTCON reg
// now, if the device uses WDTEN then you may have to use one of the other 
// WDTEN modes to enable firmware control. 
// the module now uses '_wdt_type' to determine an appropriate setting
//

// check to see if WDT option has already been set false
// (ie system.bas was included before wdt.bas)
#if (IsOption(WDT) and (WDT = false))
  #error "#option WDT must be true for Watchdog module"
#endif
#option WDT = true    // ensure blocking module calls tickle WDT
include "system.bas"  // import system module

// get _wdt_type from device file so we use the correct config
#if (_wdt_type = 0)         // the config bit is named WDT
  config WDT = OFF              // WDT setting MUST BE OFF to allow SWDTEN control
#elseif (_wdt_type = 1)     // the config bit is named WDTEN
  config WDTEN = OFF            // WDTEN setting MUST BE OFF to allow SWDTEN control
#elseif (_wdt_type = 2)
  config WDTEN = SWON           // SWON allows SWDTEN bit control
#elseif (_wdt_type = 3)
  config WDTEN = ON             // ON allows SWDTEN bit control
#else
  #error "unknown '_wdt_type' setting"
#endif

// public postscaler value - some devices support up to 32768, some don't
// you need to READ THE DATASHEET to determine: 
//   (a) what PS values are supported and 
//   (b) the nominal WDT timeout, in ms...
// ** THIS IS ESPECIALLY TRUE FOR THE 18FxxJ, AND 18FxxK DEVICES **
// ** THESE VALUES WILL OVERWRITE THE EXISTING WDTEN BIT SETTINGS **
public const
   ps32768 = %11110,
   ps16384 = %11100,
   ps8192 = %11010,
   ps4096 = %11000,
   ps2048 = %10110,
   ps1024 = %10100,
   ps512 = %10010,
   ps256 = %10000,
   ps128 = %01110,
   ps64 = %01100,
   ps32 = %01010,
   ps16 = %01000,
   ps8 = %00110,
   ps4 = %00100,
   ps2 = %00010,
   ps1 = %00000

// enable or disable the WDT
// normally, the SWDTEN bit is found in WDTCON.bits(0), but for some 
// devices like the J94/J99 the SWDTEN bit is in RCON2.bits(5)
#if _device in ( _
    18F65J94, 18F66J94, 18F67J94, 18F85J94, 18F86J94, 18F87J94,  _
    18F95J94, 18F96J94, 18F97J94, 18F66J99, 18F86J99, 18F96J99)
  public dim Enabled as RCON2.booleans(5)
#else
  public dim Enabled as WDTCON.booleans(0)
#endif

// reset the WDT  (note: changed to reflect new SF SysModule name)
public dim Reset as SysModule.ClrWDT

// alias to make original routine in system.bas visible
public dim ClrWDT as SysModule.ClrWDT

{
****************************************************************************
* Name    : Postscale                                                      *
* Purpose : Set the WDT postscale value. You should ALWAYS disable any     *
*         : interrupts before making this call and re-enable them when the *
*         : call returns                                                   *
* v1.1    : read CONFIG2 first, and skip writing if it's already set to    *
*         : the desired value. Add disable/restore interrupts              *
****************************************************************************
*  IT IS VERY IMPORTANT TO CHECK THE DATASHEET FOR THE DEVICE TO DETERMINE *
*  THE PROPER POSTSCALER VALUES AND CONFIG2H REGISTER SETTINGS             *
*  CONFIG2H IS DIFFERENT IN THE 18F, 18FxxJ, AND 18FxxK DEVICES            *
****************************************************************************
}
public sub Postscale(pValue as byte)    
   const GIE = 7                // INTCON.GIE bit
   const WR = 1                 // EECON1.WR bit
   dim t_intcon as byte         // copy of INTCON

   // save current state of GIE intr bit and disable interrupts
   t_intcon = INTCON
   INTCON.bits(GIE) = 0

   // point to CONFIG2H reg at 300003h
   TBLPTRU = $30
   TBLPTRH = $00
   TBLPTRL = $03

   // read current CONFIG2H setting (get watchdog postscaler setting)
   EECON1 = $C0
   asm
      TBLRD*
   end asm

   // if the postscaler setting is different, then write the new config word
   if (TABLAT <> pValue) then
      // enable write to configuration area, then write new value to latch...   
      EECON1 = $C4      
      TABLAT = pValue
      asm
         TBLWT*
      end asm   

      // mandatory write sequence...
      EECON2 = $55
      EECON2 = $AA
      EECON1.bits(WR) = 1  // initiate a WR cycle
      Nop                  // delay... we will stall here while write
   end if

   EECON1 = $80        // back to FLASH read...
   TBLPTRU = $00       // restore tableptr upper byte

   // restore GIE intr bit setting
   if (t_intcon.bits(GIE) = 1) then
      INTCON.bits(GIE) = 1
   end if
end sub

{
****************************************************************************
* Name    : Delay                                                          *
* Purpose : DelayMS WILL NOT tickle the WDT when the WDT has been enabled  *
*         : through software. Use this routinee for WDT safe delays        *
*         : v1.1 - the previous note may no longer be true as DelayMS and  *
*         : DelayUs tickle the WDT based on the '#option WDT' setting, and *
*         : not the config fuse setting. leave routine for legacy code     * 
****************************************************************************
}
public inline sub Delay(pDelay as word)
    DelayMS(pDelay)
end sub

end module


Here's some sample code:

Code: Select all

device = 18F4520

// import WDT module...
include "wdt.bas"

// check to see if ClrWDT() produces a 'clrwdt' instruction (look at the asm code)
ClrWDT()

// check system SBDLYUS16 code for 'clrwdt'
delayus(100)

// change wdog timeout value (!!!be sure to read the notes in wdt.bas!!!)
watchdog.Postscale(ps64)

// enable the WDT...
watchdog.Enabled = true

// no longer required... can use normal delayms calls and they should work too
watchdog.delay(5000)

// with the WDT disabled, this loop would normally execute forever - however, we 
// have enable the WDT with PS value of 64, so PIC will reset in approx 1 second and 
// toggle the LED again - assumes nominal WDT value of 4ms - some PICs have different 
// nominal values - read the datasheet for your device...
while true
wend   
When you use the wdt.bas module it's important that it gets included BEFORE any module that might set '#option WDT = false', including system.bas.
If wdt.bas detects it set false then it should issue an error message.

If you want to use Postscale() to change the timeout value, be sure to verify that the const values and the CONFIG register matches your device. It's likely that it needs to be changed to suit your use.

Post Reply