ports as global variables in libraries

Technical questions regarding the XTC tools and programming with XMOS.
User avatar
Julien
Member++
Posts: 16
Joined: Mon Feb 22, 2010 4:43 pm
Location: France

ports as global variables in libraries

Post by Julien »

Hello,

I am currently working on a software project on a XMOS platform (Sparkfun XMOS XS1-L1-64). I choose to do my own Makefiles for easier project handling. In my project, I choose to build each software component as a static library for modularity.

One of my modules handles UART communication via 2 ports. Ports are defined as global variables as required by XMOS tools, in my file uart.xc. In my file main.xc the main function is declared, and no ports are defined (not needed and already defined in uart.xc).
When I compile my files together without using a library, all is fine. But if I make uart.xc as a static library (uart.a) I get a strange error message:

Code: Select all

xcc -o test.xe main.o uart.a XC-5.xn
xmap: Error: Symbol "txd" for resolution of resource expression for ".tmpLLNKi26" is undefined.
xmap: Error: Symbol "txr" for resolution of resource expression for ".tmpLLNKi29" is undefined.
Here are the files content for easier understanding.

uart.xc

Code: Select all

#include <platform.h>

#define BIT_LENGTH XS1_TIMER_HZ/9600	//13MHz / 9600 baud
#define STOP_BIT_LENGTH BIT_LENGTH		// One stop bit

out port txd = PORT_UART_TX; 	// X0D23, TX-O
in port txr = PORT_UART_RX; 		// X0D24, RX-I

void transmitByte(unsigned byte) 
{ 
	unsigned time; 

	// Start bit 
	txd <: 0 @ time;

	// Data bits 
	for (int j = 0; j < 8; j += 1) 
	{ 
		time += BIT_LENGTH; 
		txd @ time <: >> byte; 
	} 

	// Stop bit 
	time += BIT_LENGTH; 
	txd @ time <: 1; 
	time += STOP_BIT_LENGTH; 
	txd @ time <: 1; 
}

void uart(chanend c)
{
    unsigned data;
    while (1) {
        c :> data;
        transmitByte(data);
    }
}
main.xc

Code: Select all

#include <xs1.h>
#include <platform.h>

void uart(chanend c);

void testing(chanend c)
{
	unsigned data = 0x42;
	
    while (1) {
        c <: data;
	}
}

int main()
{
	chan ch;
	par {
        on stdcore[0] : testing(ch);
		on stdcore[0] : uart(ch);
	}

	return 0;
}
And the compile commands:

Code: Select all

xcc -O0 -g -Wall -c -o main.o main.xc XC-5.xn
xcc -O0 -g -Wall -c -o uart.o uart.xc XC-5.xn
xmosar crsv uart.a uart.o
xcc -o test.xe main.o uart.a XC-5.xn
The XC files as well as the Makefile are attached in a tarball attached. You might need to run Linux and use the SetEnv script to add XMOS tools to your PATH.

I have no idea why this way of doing is not working. When I declare the ports in main.xc and use extern port in uart.xc, it is also working fine (but not what I want, as I don't want my main.xc file to know about the ports).

Any ideas?

Cheers,
Julien
You do not have the required permissions to view the files attached to this post.


User avatar
Andy
Respected Member
Posts: 279
Joined: Fri Dec 11, 2009 1:34 pm

Post by Andy »

I was under the impression that the ports can only be declared in the scope of main - but could be wrong about this.

I too would be interested in knowing if there was a way around this. If this is a compiler limitation I suspect there's a way using assembly.
User avatar
f_petrini
Active Member
Posts: 43
Joined: Fri Dec 11, 2009 8:20 am

Post by f_petrini »

I do something similar in one of my projects where I have a bunch of files (with port specifications) compiled into a library using xmosar and then a main project using that library without problem.

The only real difference I can see is that I'm using "-l" while linking the final program.

Try this:
xcc -o test.xe main.o XC-5.xn -luart
User avatar
Julien
Member++
Posts: 16
Joined: Mon Feb 22, 2010 4:43 pm
Location: France

Post by Julien »

f_petrini wrote: The only real difference I can see is that I'm using "-l" while linking the final program.

Try this:
xcc -o test.xe main.o XC-5.xn -luart
Thanks for the suggestions. Just tried that, but I have the same error as previously:

Code: Select all

xcc -O0 -g -Wall -c -o main.o main.xc XC-5.xn
xcc -O0 -g -Wall -c -o uart.o uart.xc XC-5.xn
xmosar crsv libuart.a uart.o
a - uart.o
xcc -o test.xe main.o  XC-5.xn -L. -luart
xmap: Error: Symbol "txd" for resolution of resource expression for ".tmpLLNKi26" is undefined.
xmap: Error: Symbol "txr" for resolution of resource expression for ".tmpLLNKi29" is undefined
I am still reading the "Programming XC on XMOS devices" to find a hint about ports that need to be declared in the scope of main, but I can not find anything.

Any other suggestions ?

Cheers,
Julien
User avatar
trousers
Active Member
Posts: 44
Joined: Fri Dec 11, 2009 10:20 am

Post by trousers »

Unfortunately this is a bug in the linker/mapper: ports declared as being "on" a particular core in a static library are not correctly handled. More precisely, the correct per-core declaration is not generated by the mapper.

There are two possible workarounds. The first is to not declare the ports as being on a particular core. In this case the expansions of the XN macros PORT_UART_TX and PORT_UART_RX include "on"s. If you change them to the simple port initializers (XS1_PORT_1H and XS1_PORT_1I) then the example code compiles as expected. Clearly this workaround is applicable only to single core projects.

The other workaround is to keep the "on" style initialisers but to put the object file on the command line. All object files which contain "on" port declarations need this treatment; no other files do.


Regarding port declarations, they may be used only at the top level. That is to say they occur at the same scope level as main() but not inside the scope of main(). Aside from the bug described above they may occur in any compilation unit i.e. they need not be in the same file as your main() function.

However, it has sometimes been suggested that, as a matter of good style, ports should be declared in the same file as main() because:
  • (a) They represent a static part of the system and should all therefore be clearly visible.

    (b) If the library is intended for reuse then it makes sense not to tie it to particular ports. I.e. it may be easier to reuse a UART library if it's able to work on any two 1-bits ports rather than just the ones it was compiled for.

    (c) The act of declaring a port causes it to be initialized at runtime thus whether the library is pulled in (or not) may affect the behavior of the program. This may be a particularly distressing bug if the ports in question are muxed with other ports or with xmos links. In this case including a library may stop other, apparantly unrelated, parts of you appliction from working.
Best friends with the code fairy.
User avatar
Julien
Member++
Posts: 16
Joined: Mon Feb 22, 2010 4:43 pm
Location: France

Post by Julien »

trousers wrote:Unfortunately this is a bug in the linker/mapper: ports declared as being "on" a particular core in a static library are not correctly handled. More precisely, the correct per-core declaration is not generated by the mapper.

There are two possible workarounds. The first is to not declare the ports as being on a particular core. In this case the expansions of the XN macros PORT_UART_TX and PORT_UART_RX include "on"s. If you change them to the simple port initializers (XS1_PORT_1H and XS1_PORT_1I) then the example code compiles as expected. Clearly this workaround is applicable only to single core projects.

The other workaround is to keep the "on" style initialisers but to put the object file on the command line. All object files which contain "on" port declarations need this treatment; no other files do.
Thanks trousers for your reply. It is very accurate and complete! You saved my day :)

None of the workarounds you suggest are satisfying for my project. I plan to go for a 2-core XMOS in the future, so I should care about "on"s. I could just forget about static libraries and keep only object files of my modules and link it all together, but it is not very elegant.

Does anyone from XMOS know about this bug? Should I open a ticket? Any plans to fix it in a next tool release?

Anyway, the rest of your post is also enlightening.
trousers wrote: [...]
However, it has sometimes been suggested that, as a matter of good style, ports should be declared in the same file as main() because:
  • (a) They represent a static part of the system and should all therefore be clearly visible.

    (b) If the library is intended for reuse then it makes sense not to tie it to particular ports. I.e. it may be easier to reuse a UART library if it's able to work on any two 1-bits ports rather than just the ones it was compiled for.

    (c) The act of declaring a port causes it to be initialized at runtime thus whether the library is pulled in (or not) may affect the behavior of the program. This may be a particularly distressing bug if the ports in question are muxed with other ports or with xmos links. In this case including a library may stop other, apparantly unrelated, parts of you appliction from working.
I mostly agree with that part. However, if I wanted to go for a UART library for the Sparkfun board, then bullet point (b) does not make sense. It should actually be linked to the actual UART ports.

All in all, I will go for a static library which uses extern ports. Those must be defined in the application files, otherwise, I will have a linker error. Here is a code snippets for those who have the same issue.

uart.xc:

Code: Select all

#include <platform.h>

#define BIT_LENGTH XS1_TIMER_HZ/9600	//13MHz / 9600 baud
#define STOP_BIT_LENGTH BIT_LENGTH		// One stop bit

/* Must be defined in application source files */
extern out port txd;
extern in port txr;

void transmitByte(unsigned byte) 
{ 
	unsigned time; 

	// Start bit 
	txd <: 0 @ time;

	// Data bits 
	for (int j = 0; j < 8; j += 1) 
	{ 
		time += BIT_LENGTH; 
		txd @ time <: >> byte; 
	} 

	// Stop bit 
	time += BIT_LENGTH; 
	txd @ time <: 1; 
	time += STOP_BIT_LENGTH; 
	txd @ time <: 1; 
}

void uart(chanend c)
{
    unsigned data;
    while (1) {
        c :> data;
        transmitByte(data);
    }
}
main.xc:

Code: Select all

#include <xs1.h>
#include <platform.h>

out port txd = PORT_UART_TX;
in port txr = PORT_UART_RX;

void uart(chanend c);

void testing(chanend c)
{
	unsigned data = 0x42;
	
    while (1) {
        c <: data;
	}
}

int main()
{
	chan ch;
	par {
        on stdcore[0] : testing(ch);
        on stdcore[0] : uart(ch);
	}

	return 0;
}
Thanks again for your answer.

Cheers,
Julien