Dragos Makovei
Dragos Makovei

Reputation: 27

MPLAB XC8 compiler - too many instructions in disassembly view PIC10F200

I am trying to compile this code in order to generate a PWM signal 15us HIGH and 9us LOW.

#include <xc.h>
void g()
{
    for(char val=0; val<20; val++)
    {
        GPIO = 0xFF;
        NOP(); NOP(); NOP(); NOP(); NOP();
        NOP(); NOP(); NOP(); NOP(); NOP();
        NOP(); NOP(); NOP(); NOP();

        // Write all GPIO pins LOW
        GPIO = 0;
        NOP(); NOP(); NOP();
    }
}

void main(void) {

    // Set all pins at output
    TRIS = 0x00;
    GPIO = 0;

    while(1) {
        g();        
    }
}

the problem is that the returning from FOR loop takes too many instructions. I was expecting to take less. In this case, using only 3 NOP(); functions there are still 15 ticks.

CLRF GPIO  // GPIO = 0;
NOP
NOP
NOP
MOVLW 0x1
MOVWF wtemp1
MOVF wtemp1, W
ADDWF __pcstackBANK0, F
MOVLW 0x14
SUBWF __pcstackBANK0, W
BTFSC STATUS, 0x0
RETLW 0x0
GOTO 0x4  // Return to beginning of loop

Compiler XC8:

What should I do in order to squeeze the opcode? Is it something related to the mplab?

p.s I am not able to use -O3 flag as it is not included in my license.

enter image description here

Upvotes: 0

Views: 155

Answers (1)

Martin Brown
Martin Brown

Reputation: 2749

I don't have your toolchain so I can't cycle count but this code should give you the basis to do a fine tuned version that generates 15us high, 9 us low reliably. I have also included sample variable delays that I think should be PIC friendly machine code if you intended to do PWM at some stage.

The first and most fundamental trick as suggested by @KonstantinMurugov is to turn the inner for loop to countdown (which has hardware support on a PIC in the form of DECFSZ). I have made it into a while loop.

Then the next trick is to move the short delay into the middle of the loop where you have precise control of it and tweak the padding for the longer high delay until you get the desired times for both. My initial guess is based on some assumptions about the code generation and overheads so you may have to tweak it somewhat to get both the time constants exact. Also for testing purposes set val to 1 so that the systematic errors show up in the first couple of cycles. Then retest with 2 and 3 to make sure it generates a correct waveform as it goes around each of the nested loops. To get it exactly right you may have to decrease the NOP() count in the innermost loop and add them elsewhere.

#include <xc.h>

void g()
{
   char val = 20; 
   // for fine tuning set this to 1 so that the loop exits each time (then recheck with val = 2,3)
   do
   {
       NOP(); NOP(); NOP(); NOP(); 
       GPIO = 0;
       NOP(); NOP(); NOP(); NOP();
       GPIO = 0xff;
       NOP(); NOP(); NOP(); NOP(); 
   } while (val--);  
   // this form of countdown loop is friendly to PIC DECFSZ instruction  
}

// since `PWM` was mentioned in the original question here are two primitives.
// call overheads are disastrous so allow maximum inlining for speed 
// in some ways cycle counting is easier in assembler where you can have
// precise control of the instructions inside the various loops.

void delay(char x)
{   // delay by individual NOPs (a variant on Duff's device)
    // it should be PIC friendly but I don't have the toolchain to check.
    switch (x)
    {
     case 7: NOP();
     case 6: NOP();
     case 5: NOP();
     case 4: NOP();
     case 3: NOP();
     case 2: NOP();
     case 1: NOP();
     case 0:;
     default:;
    }
}

void delay2(char x)
{   // delay by countdown loop 2 cycles at a time
    while (x--)
       ;
}

void main(void) {

  // Set all pins at output
  TRIS = 0x00;
  GPIO = 0xff;
  NOP(); NOP(); NOP(); NOP(); // so that first cycle is correct length

  while (1) {
    g();
  }
  // NB you may have to tweak it again if while(1) becomes a conditional test
}

You will have to run it in the simulator and then adjust the exact number of NOPs to obtain your desired waveform. It should be not too far off though.

Upvotes: 1

Related Questions