Electronic – Printing variable characters to UART does not work, constants work fine


I have a rather strange issue with XC8 on a PIC18F27K40 microcontroller. On a PIC16F1778 it works. I have defined:

void uart_putch(unsigned char byte) {
    while (!PIR3bits.TX1IF);
    TX1REG = byte;

When, in my main loop, I call uart_putch('a');, this works fine. However, when I define const char c = 'a'; and call uart_putch(c);, it does not work. It prints something, though not an a – I think they are 0x00 characters, which I get from hexdump -x /dev/ttyUSB0. This is not an issue with the serial port on my computer; I looked with a scope and the signal is different (left works, right does not):

enter image description here

The code is simple:

void main(void) {
    init(); // Sets up ports and UART control registers
    while (1) {
        uart_putch('a'); // or c

What does not work either is using any of the string functions (puts, printf, etc.), which I think is related – so in this question I made a minimal working example with characters.

The generated assembly when I use a variable c has:

    db  low(061h)
    global __end_of_c

    ; ...
    movlw   low((_c))
    movwf   tblptrl
    if  1   ;There is more than 1 active tblptr byte
    movlw   high((_c))
    movwf   tblptrh
    if  1   ;There are 3 active tblptr bytes
    movlw   low highword((_c))
    movwf   tblptru
    tblrd   *
    movf    tablat,w
    call    _putch

And with a constant it has in the _main block:

    movlw   (061h)&0ffh 
    call    _putch

I'm using MPLAB XC8 C Compiler V1.41 (Jan 24 2017), with part support version 1.41.

The relevant parts of my Makefile:

CFLAGS:=-I. --chip=18F27K40 -Q -Wall

SRC:=main.c uart.c
PRS:=$(subst .c,.p1,$(SRC))

all: $(OBJ)

$(OBJ): $(PRS)
    $(CC) $(CFLAGS) $^

$(PRS): %.p1: %.c $(DEP)
    $(CC) $(CFLAGS) -o$@ --pass1 $<

Any help to get this working would be very much appreciated.

Best Answer

Your program is fine, It's a bug on the PIC18F27K40.

See http://ww1.microchip.com/downloads/en/DeviceDoc/80000713A.pdf

Use XC8 compilier V1.41 and mplabx IDE, select XC8 Global options / XC8 linker and select "Additional options", then add +nvmreg in the Errata box and all will be fine.

Excerpt from the linked doc, keywords marked bold:

TBLRD requires NVMREG value to point to appropriate memory

The affected silicon revisions of the PIC18FXXK40 devices improperly require the NVMREG<1:0> bits in the NVMCON register to be set for TBLRD access of the various memory regions. The issue is most apparent in compiled C programs when the user defines a const type and the compiler uses TBLRD instructions to retrieve the data from program Flash memory (PFM). The issue is also apparent when the user defines an array in RAM for which the complier creates start-up code, executed before main(), that uses TBLRD instructions to initialize RAM from PFM.