Jallib Device Files Users Guide

by Rob Hamerling

Introduction

When I started programming in JAL it struck me that there were so few JALV2 include files, in particular not for some of my favourite PICmicros (such as the 16F690 and 12F683). The Inc2Jal utility to generate device definition files was designed for the original Jal compiler, was not updated for JalV2 and the device files it generated for my preferred PICs gave compile errors. Possible ways to resolve this issue might have been to update the generated device files manually or make Inc2Jal up-to-date for JalV2. I didn't like either of these (Inc2Jal is written in Pascal, which I hadn't used for many years).
Since I already had a Rexx script to scan MPlab files for my Xwisp2 program I decided to take that route for a replacement of Inc2Jal. I named the new script Dev2Jal because it uses the .dev files of MPlab as base in stead of the .inc files like Inc2Jal.

The advantages of automated generation of device files are pretty obvious, such as:

The advantages of a consistent naming convention are also obvious:

This document gives some design information and some instructions for the use of these JalV2 device files. The sources of information are the xxxx.dev files and xxxx.lkr of MPLAB, supplemented and corrected with information from the datasheets.


The Overall Picture

With the design of the device files I had in mind a structure as shown below.

                      +----------+   +------------------+
                      | device   |   |     general      |
                      | specific |---|     include      |
                      | include  |   |chipdef_jallib.jal|
                      +----------+   +------------------+
                           |
           +---------------+---------------+-------------
           |               |               |
     +----------+    +----------+    +----------+    +-------
     | function |    | function |    | function |    |
     | include  |    | include  |    | include  |    |   etc
     |'delay_..'|    | 'adc_...'|    |'lcd_....'|    |
     +----------+    +----------+    +----------+    +-------

These device files are now part of the central JalV2 library repository JalLib at code.google.com, which uses the same structure.

Device Files

The device files are the base for other include files and contain:

Including a device file doesn't change anything to the PIC. For example pins which are input after power-on or reset remain input, etc. Required changes are the responsibility of the application program or function libraries. For convenience reasons every device file contains a procedure to disable all analog modules of the PIC and to change all pins which are analog by default to digital I/O: enable_digital_io().

The defaults for the configuration bits may be slightly different than their specifications in the datasheet. You can find the default configuration bits settings in the top of the device file.

Common Include File 'chipdef_jallib.jal'

The file 'chipdef_jallib.jal' which comes with these device files replaces the file 'chipdef.jal' which comes with the compiler distribution. It is included by each of the device files and contains:

With the statement 'pragma target chip = .....' in every device file the compiler assigns a value to the variable 'target_chip'. The program may reference this variable with a symbolic name. This symbolic name consists of 'pic_' followed by the type of the PIC, which makes it possible to use the same source file to generate a hex file for different types of PICs, as the following example shows:

    include 16f88

    if (target_chip == PIC_16F88) then          -- (not for 16f87)

      ....                                      -- 16F88 unique code

    end if

By changing the include statement to 16f87 (or any other) the statements between 'if' and 'end if' will be skipped by the compiler.

The list of targets in chipdef_jallib.jal makes sure that every possible target name and the corresponding value of target_chip is known by the compiler.

Note: The original chipdef.jal file of the compiler package specifies a different value for 'target_chip' and not for all PIC type. Therefore it has to be replaced when using this set of device files.

Function Include Files

Function specific include files offer facilities to ease the use of PIC peripherals (such as USART, ADC), external devices (such as LCDs, sensors), or extensions to the Jal language such as for data formatting, mathematical functions, etc.

Function specific include files should be included explicitly as required by the application program, this is not done by the device files.

In most cases the function include files require some statements to couple function specific registers and pins with the device. Read the comments in the library sources and the library documentation for instructions. Most libraries contain comments with user instructions in the header of include files and just ahead of the procedures and functions in these files.


User Information

Sample program

The device files define static device (PICmicro) specific matter. This allows writing elementary programs, such as for a blinking LED, which are almost device independent. Differences are mostly in the fuse settings.

The device files are also the base for extensions, such as libraries for more complicated functions like displaying text on an LCD display or handling analog devices.

Below a simple blink-an-LED program (LED on pin 1 of Port A) for a PIC16F886 using a 20 MHz resonator. In addition to the device-specific information obtained from the include file '16f886.jal' some run-time information is needed, like the speed and type of the oscillator and some other 'environmental' variables. No extra function libraries are required.

-- ------ Blink-an-LED on pin A1 of a PIC16F886 --------

  include 16f886                        -- target is a PIC16F886
                                        -- Notes: - The extension .jal is
                                        --          added by the compiler!
                                        --        - No other includes needed.

  pragma target clock 20_000_000        -- oscillator frequency (in Hz)
                                        -- required for delays

  pragma target OSC         HS          -- high speed external oscillator
  pragma target WDT         Disabled    -- watchdog off
  pragma target MCLR        External    -- external chip reset
  pragma target LVP         Disabled    -- no low voltage programming

  enable_digital_io()                   -- disable analog module(s)

  var volatile bit led           is pin_A1     -- define alias for pin_A1
  var volatile bit led_direction is pin_A1_direction

  led_direction = output                -- make LED-pin output
  forever loop                          -- endless loop
    led = on                            -- there is light!
    _usec_delay(250000)                 -- spin 1/4 seconds
    led = off                           -- flip (on->off,off->on)
    _usec_delay(250000)                 -- spin 1/4 seconds
  end loop

When loaded in a 16F886 with 20 MHz resonator or crystal an LED connected (with series resistor!) to pin 3 (RA1) should blink twice a second.

Naming conventions for Ports and Pins

Unfortunately MPLAB of Microchip is not particularly consistent in its choice of names! The datasheets and the various informational files in MPLAB not infrequently use different names for the same entity! As a rule the device files use the names as used by the datasheets. However the device files have been generated from the MPLAB information files, not the datasheets! Therefore it is possible that some names may not be conform the datasheet. When you find such a deviation, please report to the Jallib team!

For all registers of the chip a name is defined and where appropriate also the individual bits or groups of bits are declared. Also some aliases are declared for easy the migration or conversion of existing JalV2 libraries and programs to the Jallib environment.

There are also exceptions to the rules above. For example the interrupt bits of Timer 0 are declared as TMR0IE and TMR0IF for all devices, even though some datasheets use the names T0IE and T0IF. This is just an example, see below for more.
This 'normalization' is done to be able to use all libraries for all types of PICmicros. As side-effect also programs can be migrated to other types of PICs more easily.

PORTx and TRISx

For all ports and port pins a device independent alias is defined and a similar direction definition, as the following examples show:

  var  volatile  byte  PORTA  at  <addr>
  var            byte  PORTA_low                 -- low order nibble
  var            byte  PORTA_high                -- high order nibble
  var  volatile  bit   pin_A0  at  PORTA : 0
  var  volatile  byte  TRISA  at  <addr>
  var  volatile  byte  PORTA_direction at TRISA
  var  volatile  bit   pin_A0_direction at TRISA : 0

etc. (for all other existing pins and ports)

GPIO and TRISIO (with the smaller chips)

Although the smaller PICs have no 'official' PORTA and TRISA registers, the device files contain aliases for these. So even with the smaller PICs you can use the names PORTA, pin_A0, etc.

  var  volatile  byte  GPIO   at  <addr>
  var  volatile  byte  PORTA  at  GPIO
  var            byte  PORTA_low                -- low order nibble
  var            byte  PORTA_high               -- high order nibble
  var  volatile  bit   pin_A0  at  GPIO : 0
  var  volatile  byte  TRISIO  at <addr>
  var  volatile  byte  TRISA  at  TRISIO
  var  volatile  byte  PORTA_direction at TRISIO
  var  volatile  bit   pin_A0_direction at TRISIO : 0

etc. (for all other existing pins)

Non-memory-mapped registers

Some PICs, especially in the baseline series, are missing some addressable ('memory mapped') registers. For example the 12-bit core PICs (10Fs, 12F5x, etc) have no memory mapped TRISx registers, in stead these PICs have TRISx instructions to set the direction of ports or pins. This would make it impossible for function libraries and application programs to use statements like:

  PORTA_direction = all_output

The device files contain pseudo variables which mimic the existence of memory mapped registers, and now you can use statements like the one above.

For example: even though a 16F59 has no addressable TRISC register, you can still specify:

  pin_C5_direction = output

Nibbles

Since frequently the upper and lower 4 bits ('nibble') of a port are used as a unit, these are defined as pseudo variables.

  PORTx_low             - bits 0..3
  PORTx_high            - bits 4..7
  PORTx_low_direction
  PORTx_high_direction

This allows nibbles to be used as a regular variables, and also to set pin directions by 4 at a time:

   PORTA_high = "7"                  -- lower nibble remains unchanged
   PORTA_low_direction = all_output  -- direction upper nibble unchanged

Several function libraries in the Jallib collection use this facility.

Names of MSSP modules

Names of registers of MSSP modules have been normalized as follows:

Miscellaneous remarks about names

When you have used Jal before with other device files or libraries you may notice some differences in the naming convention:

About Port Shadowing

Port shadowing is a technique to prevent the Read-Modify-Write ('RMW') problem with I/O ports of PICmicro's. This is a problem related to its hardware design. Search the Internet for "PIC and read-modify-read" and you'll get many hits to more or less interesting articles! None of the explanations are repeated here. And you don't absolutely need to understand the problem, since by using the Jallib device files you won't face the problem when you follow some simple rules and avoid a few pitfalls.

With port shadowing for the baseline and midrange PICs (10F, 12F, 16F) a RAM location is used as replacement for the port for output. The 18F series have a special register for this purpose (LATx). Although the techniques are slightly different, the general rules are: reading is done from the port directly, while writing is done to the shadow register of which the contents is subsequently passed to the real port.

With the Jallib device files shadowing is automatic, as long as you use the following names:

  PORTx          - all bits of port x
  PORTx_low      - low order nibble of port x (bits 3..0)
  PORTx_high     - high order nibble of port x (bits 7..4)
  pin_xy         - single bit 'y' of port 'x'
(in which 'x' is a port-letter and 'y' a bit number).

Note: The value for both Portx_low and Portx_high is passed with reading from (and must be passed with writing to) in the lower nibble (bits 3..0) of a constant or variable. Portx_low is read from or written to bits 3..0 of Portx, Portx_high is read from or written to bits 7..4 of Portx.

If you want to use other names for ports, nibbles or individual pins you must specify an alias. For example when you have a red LED connected to pin 0 of PortA, you could specify:

  var  bit  led_red  is  pin_A0
and use 'led_red = on' or 'led_red = off' in your program.

You should avoid direct pin and I/O port manipulation, because it will be overruled by the automatic shadowing mechanism. For example do not specify:

  var bit led_red at portA : 0
With this specification a 'led-red = on' will have the desired result, but it will not update the shadow register. Any next operation which uses the shadowing mechanism will override the previous direct control operation.

Shadowing is also bypassed when you initialise the alias with the declaration. So declaring and initialising an alias as follows:

  var  bit led_red is pin_A0 = off
is bad practice! Initialize an alias separatedly after the declaration.

Naming convention for configuration bits (fuses)

The configuration bits or groups of bits is such a large variety that it is almost impossible to obtain a naming convention which covers it all.

Only for the oscillator specification the MPLAB information files contain more than 140 different descriptions! Because of synonyms this number could be normalized to a much smaller number! The first part is the oscillator type, the [optional] second part indicates a related subfunction. For example it may indicate if the OSC2 pin is CLKOUT or I/O, or if PLL is active for the 18F series. Descriptions in MPLAB which do not fit in the normalization scheme are copied almost literally.

Fuse_Def OSC (oscillator)

      LP              - Low Power crystal on OSC1,OSC2
      XT              - Crystal or Resonator on OSC1,OSC2
      HS              - High Speed Crystal or Resonator on OSC1,OSC2
      HS_PLL          - as HS, PLL active
      EC_CLKOUT       - External Clock (TTL) signal on OSC1, OSC2 is ClockOut
      EC_NOCLKOUT     - External Clock (TTL) signal on OSC1, OSC2 is I/O
      EC_PLL          - as EC, PLL active
      RC_CLKOUT       - RC oscillator on OSC1, OSC2 is ClockOut
      RC_NOCLKOUT     - RC oscillator on OSC1, OSC2 is I/O
      EXTOSC_CLKOUT   - External oscillator on OSC1, ClockOut on OSC2
      EXTOSC_NOCLKOUT - External oscillator on OSC1, OSC2 is I/O
      INTOSC_CLKOUT   - Internal oscillator, OSC1 is I/O, ClockOut on OSC2
      INTOSC_NOCLKOUT - Internal oscillator, OSC1 and OSC2 are I/O
      (other keywords may be used as well)

Fuse_Def WDT (watchdog)

      ENABLED          - Watchdog enabled
      DISABLED         - Watchdog disabled

Fuse_Def WDTPS (Watchdog postscaler)

      P32768           -  1 : 32,768
      P16384           -  1 : 16,384
      P...             -  1 : ...
      P..              -  1 : ..
      P2               -  1 : 2
      P1               -  1 : 1

Fuse_Def MCLR (reset)

      EXTERNAL         - /MCLR pin enabled
      INTERNAL         - /MCLR pin is digital I/O

Fuse_Def PWRTE (Power-up Timer Enable)

      ENABLED          - Power up timer enabled
      DISABLED         - Power Up timer disabled

Fuse_Def BROWNOUT (Brown Out detect)

      ENABLED          - BOD enabled, SBOREN disabled
      RUNONLY          - BOD enabled in run, disabled in sleep
      CONTROL          - SBOREN controls BOR function
      DISABLED         - BOD and SBOREN disabled

Fuse_Def VOLTAGE (Brown Out voltage)

      V20              - 2.0 Volt
      V27              - 2.7 Volt
      V42              - 4.0 Volt
      V45              - 4.5 Volt
      ...  etc (whatever voltages are applicable)

Fuse_Def LVP (Low Voltage Programming)

      ENABLED          - LVP on, enabled
      DISABLED         - LVP off, disabled

Fuse_Def CP (Code Protection)

      ENABLED          - Code memory read protection on
      DISABLED         - Code mewmory read protection off

Fuse_Def CPD (Data Code Protection)

      ENABLED          - Data (EEPROM) memory read protection on
      DISABLED         - Data (EEPROM) memory read protection off

Fuse_Def WRT (Program Memory Self-Write Protection)

      NO_PROTECTION    - All program memory writable
      ALL_PROTECTED    - Writing of program memory prohibited
      Rxxxx_yyyy       - Protected memory range
                         (only specific ranges can be write protected)

Fuse_Def IOSCFS (Internal Oscillator Frequency Select)

      F4MHZ            - 4 MHz
      F8MHZ            - 8 MHz

Notes:

  1. In addition to these 'standard' fuse_defs above there may be others, depending on the features of the specific PICmicro. Please read the device file to see which fuse-defs are available for your target PICmicro.
  2. The terms 'Enabled' and 'Disabled' may need to be specified where usually 'On' and 'Off' are used.

When a fuse_def statement causes compile-time error messages you may simply delete it and specify the fuse-word(s) or -byte(s) explicitly with bit patterns in stead of using fuse option pragma statements. For example for the PIC16F690 the following group of statements:

   pragma target OSC       HS
   pragma target WDT       Disabled
   pragma target PWRTE     Enabled
   pragma target MCLR      External
   pragma target CP        Disabled
   pragma target CPD       Disabled
   pragma target BROWNOUT  Enabled
   pragma target IESO      Disabled
   pragma target FCMEN     Disabled
is equivalent with:
   pragma target fuses   0b11_0011_1110_0010

PICs with 16-bits core (the 18F series) have such a large set and variety of configuration bits that explicit specification is probably the best way to make sure all configuration bits are set correctly for your program. As an example see the following list for a simple blink-a-LED program with an 18F242.

  pragma  target fuses 0  0b0000_0000       -- (n/a)
  pragma  target fuses 1  0b0010_0010       -- not switchable, HS osc, no PLL
  pragma  target fuses 2  0b0000_0001       -- BOR disabled, PWTR disabled
  pragma  target fuses 3  0b0000_0000       -- watchdog disabled
  pragma  target fuses 4  0b0000_0000       -- (n/a)
  pragma  target fuses 5  0b0000_0001       -- CCP2 on RC1
  pragma  target fuses 6  0b1000_0001       -- no bg debug, no LVP, STVREN
  pragma  target fuses 7  0b0000_0000       -- (n/a)
  pragma  target fuses 8  0b0000_1111       -- no code protection
  pragma  target fuses 9  0b1100_0000       -- no data protection
  pragma  target fuses 10 0b0000_1111       -- no code write protection
  pragma  target fuses 11 0b1110_0000       -- no other write protection
  pragma  target fuses 12 0b0000_1111       -- no table read protection
  pragma  target fuses 13 0b0100_0000       -- no boot block write protect

Notes:

  1. All pragma statements must be specified after the include statement of the device file.
  2. When a PIC has multiple configuration words or bytes the index value of the word or byte must be specified before the value.
  3. The device file contains a fuses specification: how many words/bytes and their corresponding memory addresses. Also a default fuse setting is specified, which may or may not be suitable for your application!

The meaning of configuration bits can in most cases be found in the DataSheet of the specific chip, in the section 'Special Features of the CPU'. This info can certainly be found in the Programming Specifications of the chip. For your convenience the MicroChip document numbers are mentioned in the heading of the device files.


Compiler requirements

The compiler - at the moment of this writing version 2.4j - has a number of requirements for device specifications. The most important from a user perspective are the following:

Memory allocation

The device files specify the amounts of available shared and unshared memory (RAM, gpr) in bytes.

For user program memory (variables, constants) the compiler allocates memory first in unshared RAM then in shared RAM. Some specific compiler 'internally' used bytes can be and should be allocated in shared RAM for optimum performance.
For the compiler 'shared' means: accessible in all banks. Memory which is accessible in more than one bank but not in all is declared as unshared RAM.

Most PICS have both shared and unshared RAM and then there is no issue, but some PICs have only shared memory while some others have no shared memory at all. This complication is solved in the device files as follows:

The compiler supports a maximum of 4 memory banks for baseline and midrange PICs. When a PIC has more memory banks the device file declares only 4 of these, memory in the other banks is unusable. Example: 16F59.

Analog modules

(to be done)

Miscellaneous

These device files are part of the central JalV2 repository 'Jallib' (http://jallib.googlecode.com). Other libraries of Jallib have been or are being converted to use the names in these device files. You are strongly recommended to use only this combination of include files. Using these device files in combination with other libraries may cause problems, especially with libraries for the old (pre JalV2) compiler.