This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS 6.1, MSP430 TI assembler v 4.4.4: Cannot equate an external symbol to an external symbol

Hello again...

I'm using CCS 6.1 on openSUSE 13.1. I always program microcontrollers in assembly, so my question is for the assembler of TI v4.4.4.

Suppose I have a file containing all the code for driving a real time clock chip through SPI. I created this file in a way to be able to define if the SPI bus is used only for this device or if the bus is shared with other SPI devices. So, I needed one definition to be global to the whole project, defining at which port lies the Chip Select pin. This is a must in order to create an interrupt dispatcher according to which SPI device is enabled. So much for the introduction of the problem.


When i create the definition:

RTCCTLOUT          .equ          P1OUT

and then

      .global RTCCTLOUT

i get en error message "ERROR!   at line 8: [E0300] Cannot equate an external symbol to an external symbol"

The problem can be reproduced using the following code (which is the default main assembly program the CCS created in an assembly-only project), together with the definitions that produce the problem:

;-------------------------------------------------------------------------------
; MSP430 Assembler Code Template for use with TI Code Composer Studio
;
;
;-------------------------------------------------------------------------------
            .cdecls C,LIST,"msp430.h"       ; Include device header file

RTCCTLOUT	.equ	P1OUT		    ;Control port of external Real Time Clock
		.global	RTCCTLOUT           ;Must be global to othe files

;-------------------------------------------------------------------------------
            .text                           ; Assemble into program memory
            .retain                         ; Override ELF conditional linking
                                            ; and retain current section
            .retainrefs                     ; Additionally retain any sections
                                            ; that have references to current
                                            ; section
            .global	RESET		    ; Entry point must be global
;-------------------------------------------------------------------------------
RESET       mov.w   #__STACK_END,SP         ; Initialize stackpointer
StopWDT     mov.w   #WDTPW|WDTHOLD,&WDTCTL  ; Stop watchdog timer
	    jmp	    $			    ; Nothing else to do

;-------------------------------------------------------------------------------
                                            ; Main loop here
;-------------------------------------------------------------------------------


;-------------------------------------------------------------------------------
;           Stack Pointer definition
;-------------------------------------------------------------------------------
            .global __STACK_END
            .sect 	.stack

;-------------------------------------------------------------------------------
;           Interrupt Vectors
;-------------------------------------------------------------------------------
            .sect   ".reset"                ; MSP430 RESET Vector
            .short  RESET

What am I doing wrong?

Thank you for your help

Ilias

  • The problem is you are trying to create a symbolic constant out of a substitution symbol.  The assembler does not support that.

    Please read the sections titled Symbolic Constants and Substitution Symbols in the MSP430 assembly tools manual.  It is important to understand the differences in those terms.  

    If you build your test case with the option -al, it creates a listing file with a file extension of .lst.  Inspect this file to see this line ...

    A  1408                       .define "(PAOUT_L)",P1OUT

    Look up the .define directive in the MSP430 assembly tools manual.  You will see it creates a substitution symbol, and not a symbolic constant.  This line in your code ...

    Ilias Chrisocheris said:
    RTCCTLOUT .equ P1OUT

    attempts to create a symbolic constant based on a substitution symbol.  That doesn't work.  You can only create another substitution symbol.  You can do that with either the .asg directive or the .define directive.  Substitution symbols cannot be global the way symbolic constants can.  They have to be defined in a header file that is included in all the assembly files.

    Another solution to consider ... Just use P1OUT everywhere you presently use RTCCTLOUT.

    This analogy may help ... C #define symbols are like substitution symbols, and C global variable names are like symbolic constants.

    Thanks and regards,

    -George

  • The truth is that I could not understand the difference between .define and .equ/.set so I tried different things. Your answer clarifies their difference.

    George Mock said:

    Another solution to consider ... Just use P1OUT everywhere you presently use RTCCTLOUT.

    Well, this is not a solution cause RTCCTLOUT value is the result of preprocessor, according to settings to other definitions, so it cannot be substituted... Another solution I figured out is to make a file called definitions.asm that runs all the preprocessor commands and it could be included into every file that uses the library for the RTC. So, I wouldn't have to .global any definitions


    What I've done after reading your answer is that by doing:

    .define       P1OUT,RTCCTLOUT

    and then

    .global RTCCTLOUT

    the problem is solved (well, the program passes the building of this file, but I don't know if the final project works). So, until I finish this project and make it work I consider your answer as the solution to my problem. If I find any problem according to this question, of course I will post again :)

    Thank you very much for your time and effort to help me in this project

    Regards

    Ilias

  • Well, unfortunately the final code does not pass linking... By doing

    .global RTCCTLOUT

    I found out that it expects RTCCTLOUT to be defined in another file, while I wanted it to be exported to other files... By doing

    .def RTCCTLOUT

    It says that P1OUT is not defined in this file, which, of course, is true. So, how can I define a symbol that can be referenced by other file and have a value of P1OUT?

    To clarify my question, I add the preprocessor code that lies in my assembly file and defines some variables I need. Only RTCCTLOUT is the one I need to be referenced by another file:

    ;============================================================================================
    ; DEFINITIONS - This section contains all necessary definition visible only to this file
    ;--------------------------------------------------------------------------------------------
    RTCSPI		.equ		"UCA0"				;The UCSI that communicates to RTC
    RTCCtlPort	.equ		1				;The port the control pins of RTC are connected
    ;RTCIntPort	.equ		1				;Uncomment if RTC interrupt is on a different port
    								; than RTCCtlPort
    RTC_IRQ		.equ		BIT4				;Interrupt pin
    RTC_CS		.equ		BIT3				;Chip select pin
    
    ;...
    ;...
    ;...
    ;============================================================================================
    ; AUTO DEFINITIONS - This section contains definitions calculated by preprocessor, mainly
    ; according to the previously specified ones
    ;--------------------------------------------------------------------------------------------
    		.if (RTCCtlPort == 1)
    RTCCTLIN	.equ		P1IN
    		.define		P1OUT,RTCCTLOUT
    RTCCTLDIR	.equ		P1DIR
    RTCCTLSEL	.equ		P1SEL
    RTCCTLREN	.equ		P1REN
    		.elseif (RTCCtlPort == 2)
    RTCCTLIN	.equ		P2IN
    		.define		P2OUT,RTCCTLOUT
    RTCCTLDIR	.equ		P2DIR
    RTCCTLSEL	.equ		P2SEL
    RTCCTLREN	.equ		P2REN
    		.elseif (RTCCtlPort == 3)
    RTCCTLIN	.equ		P3IN
    		.define		P3OUT,RTCCTLOUT
    RTCCTLDIR	.equ		P3DIR
    RTCCTLSEL	.equ		P3SEL
    RTCCTLREN	.equ		P3REN
    		.elseif (RTCCtlPort == 4)
    RTCCTLIN	.equ		P4IN
    		.define		P4OUT,RTCCTLOUT
    RTCCTLDIR	.equ		P4DIR
    RTCCTLSEL	.equ		P4SEL
    RTCCTLREN	.equ		P4REN
    		.elseif (RTCCtlPort == 5)
    RTCCTLIN	.equ		P5IN
    		.define		P5OUT,RTCCTLOUT
    RTCCTLDIR	.equ		P5DIR
    RTCCTLSEL	.equ		P5SEL
    RTCCTLREN	.equ		P5REN
    		.elseif (RTCCtlPort == 6)
    RTCCTLIN	.equ		P6IN
    		.define		P6OUT,RTCCTLOUT
    RTCCTLDIR	.equ		P6DIR
    RTCCTLSEL	.equ		P6SEL
    RTCCTLREN	.equ		P6REN
    		.elseif (RTCCtlPort == 7)
    RTCCTLIN	.equ		P7IN
    		.define		P7OUT,RTCCTLOUT
    RTCCTLDIR	.equ		P7DIR
    RTCCTLSEL	.equ		P7SEL
    RTCCTLREN	.equ		P7REN
    		.elseif (RTCCtlPort == 8)
    RTCCTLIN	.equ		P8IN
    		.define		P8OUT,RTCCTLOUT
    RTCCTLDIR	.equ		P8DIR
    RTCCTLSEL	.equ		P8SEL
    RTCCTLREN	.equ		P8REN
    		.else
    			emsg	"RTCCtlPort was not defined correctly!"
    		.endif
    
                    .def    RTCCTLOUT
                    .def    RTC_CS

    The first part of the code defines hardware side connections. The rest (AUTO DEFINITIONS) define the rest of the symbols needed by the code and depend on the hardware design.

    I know that a way to do it is to add another file called something like RTC_Definitions.asm that would contain all this preprocessor code (without .def lines) which should be included to every assembly code file uses the RTC library. But it would be much more comfortable if everything was in a single file, exporting only the necessary symbols...

    Again, thanks everybody for your time

    Ilias

  • Ilias Chrisocheris said:
    I know that a way to do it is to add another file called something like RTC_Definitions.asm that would contain all this preprocessor code (without .def lines) which should be included to every assembly code file uses the RTC library.

    That's the only solution I can suggest.

    Thanks and regards,

    -George

  • Well, there is a drawback in that approach. You cannot easily take advantage of command line definitions. The real code I use is:

    ;============================================================================================
    ; DEFINITIONS - This section contains all necessary definition visible only to this file
    ;--------------------------------------------------------------------------------------------
    		.if $isdefed("RTCSPI") == 0		;RTCSPI could be defined in command line
    RTCSPI		.equ	"UCA0"				;The UCSI that communicates to RTC
    		.endif
    		.if $isdefed("RTCCtlPort") == 0		;RTCCtlPort could be defined in command line
    RTCCtlPort	.equ	1				;The port the control pins of RTC are connected
    		.endif
    ;RTCIntPort	.equ	1				;Uncomment if RTC interrupt is on a different port
    											; than RTCCtlPort. Could also be defined in CLI
    		.if $isdefed("RTC_IRQ") == 0		;RTC_IRQ could be defined in command line
    RTC_IRQ		.equ	BIT4				;Interrupt pin
    		.endif
    		.if $isdefed("RTC_CS") == 0		;RTC_CS could be defined in command line
    RTC_CS		.equ	BIT3				;Chip select pin
    		.endif
    
    		.if $isdefed("SHAREDBUS") == 0		;SHAREDBUS could be defined in command line
    SHAREDBUS	.equ	1				;Giving a value of 0 means that the bus is used
    		.endif					; only by this peripheral. A value of 1 means
    							; that SPI bus is shared with other peripherals
    		.if $isdefed("CPUSMCLK") == 0
    CPUSMCLK	.equ	16000000			;The CPU SMCLK clock frequency in Hz
    		.endif
    
    		.if $isdefed("RTCRXBUFLEN") == 0	;RTCRXBUFLEN could be defined in command line
    RTCRXBUFLEN	.equ	18				;Size of incoming stream buffer
    		.endif
    		.if $isdefed("RTCTXBUFLEN") == 0	;RTCTXBUFLEN could be defined in command line
    RTCTXBUFLEN	.equ	18				;Size of outgoing stream buffer
    		.endif
    

    I use this approach to be able to define some or all of those values through command line. If someone wants to have truly robust code then this is not the case because it can lead to inconsistencies during project building. Using the split file approach makes the usage of same definitions to all assembly commands in command line. Fail to do this can lead to have different definitions of the same thing in two different files of the project.


    To make this more clear... Suppose you have a file called RTC.asm that contains all the functions needed to control an RTC chip. The file RTC_Defs.asm contains all the definitions and preprocessor functionality as the one I described in this thread. By including RTC_Defs.asm in RTC.asm everything is fine up to now. The problem starts when you want to change the port from UCA0 to UCA1. By assembling the file using -ad=RTCSPI="UCA1" everything is fine.

    The same preprocessing RTC_Defs.asm should be included in main.asm in order for the interrupt dispatcher functions to redirect the interrupt to the correct ISR according to the current chip select active pin. If you forget to add -ad=RTCSPI="UCA1" also in main.asm then the interrupt service routine of RTC will be bind to the dispatcher of another interrupt vector, making the code unusable, the debugging almost impossible and the programmer start using to drugs :). As far as I found out is that even when defining something in the command line, for some, unknown to me, reason, it parses also the block inside an .if $isdefed... .endif firing an error... (checked by including -ad=RTC_CS=BIT7, well, to be honest I added the line RTC_CS=BIT7 in the properties of the file in assembler definitions)

    To my opinion .equ or .set should be able to create a symbol using the value assigned to another symbol, such as RTCCTLOUT .equ P1OUT. All the assemblers I've used before (programming MSP Assembly since 2002) do it and I believe this is a real feature...


    Anyway, I really thank you for your help and for your valuable time (I know it is)

    Best regards

    Ilias