Learning to program the PIC16LF1823

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
I really need help here ... any input or experienced opinion would be greatly appreciated.

I added quite a bit of code at the bottom of my current project, and when I tried to build it I got the following message from MPASM Assembler:

Error - section '.org_2' can not fit the absolute section. Section '.org_2' start=0x00000004, length=0x00001002

If my interpretation is correct, the problem lies with the fact that somehow the new code intersected the 0x1002 length boundary, which amounts to about 4kB ... and that seems to be a big no-no ... The chip I'm working with (PIC16LF1825), however, has 8kB of program memory available. So what I did was I added the ORG 0x10FF directive (and not simply 0x1007, so as to be on the safe side) to the beginning of my latest routine and the problem went away. The project was built perfectly.

What gives? Is this going to be a problem? Will I somehow have to watch out for crashes in logic? Is what I did the right thing to do in these circumstances? Is there a better way to allocate program memory space to each routine instead of allowing MAPSM to do that automatically?
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
I think I'm beginning to understand... what's happening has to do with this table:

1657893173441.png

I browsed through the "Program Memory" section of MPLAB after building the project, and identified some of the instructions and routines within it. It seems that I ran out of space in Page 0, and my last routine was crossing the boundary between Page 0 and Page 1. So what I did was I included an ORG 0x0800 directive in the routine's header and the problem was solved. What I'm not sure of, though, is if I'm going to have to perform a sort of "page switching" whenever I call the routine, or if the assembler does that automatically for me ...
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
A question for those in the know ... what happens if I need to call a routine that lies in a different code page? Would the "pagesel" directive need to be used? Or would such thing happen automatically? How would such pagesel directive be used in such case?
 

Papabravo

Joined Feb 24, 2006
22,083
A question for those in the know ... what happens if I need to call a routine that lies in a different code page? Would the "pagesel" directive need to be used? Or would such thing happen automatically? How would such pagesel directive be used in such case?
I think the assembler can handle routines that begin and end within a page. What it cannot handle is a routine that begins on page n and continues over the boundary to page n+1. In some processors this is not a problem, but in the PIC architecture you have to look at how the Program Counter (PC) register, which is used for instruction fetches, is implemented. The least significant bits may not propagate a carry to the most significant bits. In that case the PC goes from the last location on a page to the first location on the same page. That is why moving your routine past the page boundary fixed the immediate problem.

I'll look in the datasheet to see if I can find the relevant explanation.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
I think the assembler can handle routines that begin and end within a page. What it cannot handle is a routine that begins on page n and continues over the boundary to page n+1. In some processors this is not a problem, but in the PIC architecture you have to look at how the Program Counter (PC) register, which is used for instruction fetches, is implemented. The least significant bits may not propagate a carry to the most significant bits. In that case the PC goes from the last location on a page to the first location on the same page. That is why moving your routine past the page boundary fixed the immediate problem.

I'll look in the datasheet to see if I can find the relevant explanation.
Many thanks, PPB ... your explanation fits exactly what I've been suspecting. OTH, I also found that it seems to be necessary to use the pagesel directive when calling routines outside the current program memory page, and then using the same directive upon returning to restore the page. A bit annoying, but as long as things are nice and clear I think it can easily be handled using a macro:

1657900089436.png
The trick will be knowing when code crosses page boundaries as it becomes larger so that the appropriate ORG directives can be used at the right places. I guess I'm going to have to keep monitoring the "Program Memory" tab in MPLAB to catch it when it happens.

A couple of relevant references:
http://www.piclist.com/techref/microchip/pages.htm
https://www.microchip.com/forums/m909980.aspx
 

Papabravo

Joined Feb 24, 2006
22,083
I looked in the datasheet for the PIC18LF1825 and found Section 3.3 which described how the Program counter is modified in 5 different conditions. Curiously they ignored execution across a page boundary. AFAIK it may or may not be a problem and the documentation I have seen is not clear on this issue. What is clear is that there are 11 bits which cover 0x000 to 0x7FF and that PCLATH needs to be modified to cross over the boundary. This can be done with one of five specific methods according to Figure 3-3 in DS41440A-page 44
  1. An instruction with PCL as a destination
  2. A GOTO or a CALL instruction
  3. A CALLW instruction
  4. A BRW instruction
  5. A BRA instruction
I infer from this that execution across a page (2048 bytes) is not possible. NB - this is not a conclusion, but merely an inferential hypothesis.
It is still a PITA, but better than it used to be.
 

Papabravo

Joined Feb 24, 2006
22,083
I looked in the datasheet for the PIC18LF1825 and found Section 3.3 which described how the Program counter is modified in 5 different conditions. Curiously they ignored execution across a page boundary. AFAIK it may or may not be a problem and the documentation I have seen is not clear on this issue. What is clear is that there are 11 bits which cover 0x000 to 0x7FF and that PCLATH needs to be modified to cross over the boundary. This can be done with one of five specific methods according to Figure 3-3 in DS41440A-page 44
  1. An instruction with PCL as a destination
  2. A GOTO or a CALL instruction
  3. A CALLW instruction
  4. A BRW instruction
  5. A BRA instruction
I infer from this that execution across a page (2048 bytes) is not possible. NB - this is not a conclusion, but merely an inferential hypothesis.
It is still a PITA, but better than it used to be.
The original problem was that the first instruction of a routine was in a particular page, but the last instruction was not.
I had another idea that is kind wild but might be useful. It is as follows:
  1. Put the entry point of your routine after the RETURN instruction
  2. At the entry point make a relative branch with a negative offset to the first instruction of the routine
This will work as long as both the first and last instruction are within the page. You can easily verify this if both addresses are in the linkage editor map.
 

Papabravo

Joined Feb 24, 2006
22,083
Your noobie friend here would very much appreciate it if you could explain to him where said map can be found ...
My friend, you do not need to sell yourself short on my account.
In your post #466 you included a screen shot of a manual that you possess, but I do not:
1657905666637.png
It refers to a linker which will accept the 'pagesel' instruction. That is the linker which should have an option to produce a map of where all the global symbols are located. I don't know if the one you are using will do that automatically or not. You may have to turn on an option for it to be produced. I don't even know if you use that linker. In most cases where there is an integrated debugger this is a largely unused feature. In times past it was essential in decoding what were called "core memory dumps". This was basically a post-fault printout in octal(hex) of the contents of memory. These could occupy a full box of 14 & 7/8 by 11" Green bar, which were a real pain to go through.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
I'll have a look, but not right away. Other irons in the fire.
Thanks PPB, take all the time you want, of course ... I was never expecting drive-through service from you ... I consider it a privilege your helping me in this annoying little quest, and I'm quite grateful for it. Have a great weekend.
 

Papabravo

Joined Feb 24, 2006
22,083
Here's the manual I took that screenshot from. The highlighted text is in page 144
Thank you for that manual.
In §9.6 on p. 225 Figure 9-1
1657916779608.png
shows a reference to a file called "prog.map". The file "prog.map" should contain in summary form the beginning and end of each linked section for both program and data. It should also contain the address of each global symbol, again both program and data.

At the bottom of p. 225 is the mention of a ".lst" file which contains a complete listing of source code and object code. It names a utility called "MP2COD" which implies a post processing step to assembly and linking.

Finally the linker calls the MP2HEX utility to generate the executable output. The
MPLINK linker also generates symbolic information for debugging your application with
the IDE (.cof and .map files). A list file (.lst) can also be generated by calling the
MP2COD utility.

§9.9.7 Map File, on page 229 describes the .map file in detail.

The following table is an example of the section table in a map file:
Section Info
Section Type Address Location Size(Bytes)
--------- --------- --------- --------- ---------
Reset code 0x000000 program 0x000002
.cinit romdata 0x000021 program 0x000004
.code code 0x000023 program 0x000026
.udata udata 0x000020 data 0x000005
The second table (Program Memory Usage) lists program memory addresses that
were used and provides a total usage statistic. For example:
Program Memory Usage
Start End
--------- ---------
0x000000 0x000005
0x00002a 0x00002b
0x0000bc 0x001174
0x001176 0x002895
10209 out of 32786 program addresses used, program memory utilization is 31%
The third table in the map file (Symbols - Sorted by Name) provides information about
the symbols in the output module. The table is sorted by the symbol name and includes
the address of the symbol, whether the symbol resides in program or data memory,
whether the symbol has external or static linkage, and the name of the file where
defined. The following table is an example of the symbol table sorted by symbol name
in a map file:
Symbols - Sorted by Name
Name Address Location Storage File
------- -------- -------- -------- ---------
call_m 0x000026 program static C:\PROGRA~1\MPLAB\ASMFOO\sampobj.asm
loop 0x00002e program static C:\MPASM assemblerV2\MUL8X8.ASM
main 0x000024 program static C:\PROGRA~1\MPLAB\ASMFOO\sampobj.asm
mpy 0x000028 program extern C:\MPASM assemblerV2\MUL8X8.ASM
start 0x000023 program static C:\PROGRA~1\MPLAB\ASMFOO\sampobj.asm
H_byte 0x000022 data extern C:\MPASM assemblerV2\MUL8X8.ASM
L_byte 0x000023 data extern C:\MPASM assemblerV2\MUL8X8.ASM
count 0x000024 data static C:\MPASM assemblerV2\MUL8X8.ASM
mulcnd 0x000020 data extern C:\MPASM assemblerV2\MUL8X8.ASM
mulplr 0x000021 data extern C:\MPASM assemblerV2\MUL8X8.ASM

That is a boatload of information you may have been missing. I understand not being anxious to read a long document in excruciating detail, and I'm sorry you appear to have missed this.

I'm glad if I was helpful to you.
Buena suerte
 

Papabravo

Joined Feb 24, 2006
22,083
Buena suerte, Jim ... is the catchphrase of one of my favorite TV shows back in the 1960's ... and it's pretty appropriate to the task that I'm facing at the moment ... many thanks again for your always helpful insight.
You're welcome. Happy to oblige.
(Feliz de complacer)
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
So here I thought: ... "gee, all I have to do is write a simple little macro that will do the page selection automatically for me, right?" ...

Code:
;Select appropriate program memory page before and after calling a routine
Fcall macro Routine
         pagesel Routine            ;select Routine's program memory page before calling
         call Routine               ;execute the routine
         pagesel $                  ;restore the current program memory page
       endm
Nope ... it wasn't as simple as I thought ... I'm so naïve:

1657985440732.png
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
Fixed it ... it turns out that I was using Fcall within the Fcall macro ... so the assembly process got caught in an endless recursive loop ...

But now the assembled code has grown perceptibly .... I hope that is not an issue at the end of the day.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,768
Well, I've tamed the Beast ... or at least I've temporarily appeased it.

After substituting every "call" instruction with the "Fcall" macro shown above, things worked nice and well for a while. Until I started noticing some weird discrepancies reported by my routines when, for instance, a routine in page 0 called a routine in page 1, and then the latter called a routine in page 0 which in turn called another routine within the same page 0. That is, whenever a pagesel directive was performed in a nested routine that did not need to change program memory pages things got kind of weird ...

So what I did was turn back every "Fcall" macro in the program to a simple "call" instruction, and then used "Fcall" only when it became strictly necessary. That resulted in a more compact code size, but also a little difficult to read and interpret, since I had to place an ORG directive in a couple of places to make things work in the best possible way.

So what I did is write an auxiliary file with a memory map describing where every routine is located in the chip's memory pages in plain english. And I intend to keep and maintain said file as a reference. I mean, right now things are crystal clear in my head ... but in a few moths or years whenever I have to maintain the code it will be best to have a good, clear document describing how and why things are being done in the program
 
Top