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.

CC1310: Sensor Controller Studio / Code Generation

Part Number: CC1310
Other Parts Discussed in Thread: SYSCONFIG,

In my project i use i2c for accessing an acceleration sensor. A quick look to the generated code (lst-file) shows horrible code: Many redundant load and stores.

I have a marked all redundant load/store in yellow, and other options for better code in orange.

SCS-Source-Code:

macro setMpuReg( reg, val) {
    i2cStart();
    i2cTx( MPU_ADDR | I2C_OP_WRITE);
    if (state.i2cStatus == 0x0000) {
        i2cTx( reg );
    }
    if (state.i2cStatus == 0x0000) {
        i2cTx( val );
    }
    i2cStop();
}
// LowPower-Mode with ACC only
setMpuReg( MPU_REG_PM1, MPU_CFG_PM1);           // cycle+dis-temp+best-osc
setMpuReg( MPU_REG_PM2, MPU_CFG_PM2);           // WupFreq=40Hz, Acc:on, Gyro:off
... and more settings of registers

Generated Code :

               ;?     // LowPower-Mode with ACC only
               ;?     setMpuReg( MPU_REG_PM1, MPU_CFG_PM1);           // cycle+dis-temp+best-osc
               ;?     >   i2cStart();
014e ---- 6000                             ld          R6, #0x0000
014f ---- 16a6                             jsr         I2cStart
0150 ---- 6c9a                             st          R6, [#accsampling/state/i2cStatus]           // no further useof state.i2cStatus
               ;?     >   i2cTx( MPU_ADDR | I2C_OP_WRITE);
0151 ---- 70d0                             ld          R7, #208
0152 ---- 689a                             ld          R6, [#accsampling/state/i2cStatus]          // value already in R6 
0153 ---- 16bd                             jsr         I2cTxByte
0154 ---- 6c9a                             st          R6, [#accsampling/state/i2cStatus]
               ;?     >   if (state.i2cStatus == 0x0000) {
0155 ---- 089a                             ld          R0, [#accsampling/state/i2cStatus]         // use value in R6 for compare
0156 ---- 8a00                             cmp         R0, #0
0157 ---- be04                             bneq        /id0257
               ;?     >       i2cTx( reg );
0158 ---- 706b                                 ld          R7, #107
0159 ---- 689a                                 ld          R6, [#accsampling/state/i2cStatus]     // value already in R6
015a ---- 16bd                                 jsr         I2cTxByte
015b ---- 6c9a                                 st          R6, [#accsampling/state/i2cStatus]
               ;?     >   }
               /id0257:
               ;?     >   if (state.i2cStatus == 0x0000) {
015c ---- 089a                             ld          R0, [#accsampling/state/i2cStatus]        // use value in R6 for compare
015d ---- 8a00                             cmp         R0, #0
015e ---- be04                             bneq        /id0263
               ;?     >       i2cTx( val );
015f ---- 7029                                 ld          R7, #41
0160 ---- 689a                                 ld          R6, [#accsampling/state/i2cStatus]    // value already in R6
0161 ---- 16bd                                 jsr         I2cTxByte
0162 ---- 6c9a                                 st          R6, [#accsampling/state/i2cStatus]   // no further use of state.i2cStatus
               ;?     >   }
               /id0263:
               ;?     >   i2cStop();
0163 ---- 689a                             ld          R6, [#accsampling/state/i2cStatus]       // value already in R6
0164 ---- 16db                             jsr         I2cStop
0165 ---- 6c9a                             st          R6, [#accsampling/state/i2cStatus]
               ;?     setMpuReg( MPU_REG_PM2, MPU_CFG_PM2);           // WupFreq=40Hz, Acc:on, Gyro:off
... same as above

With some soft optimization rom 24 instructions 6 could simply removed and 2 instructions could removed with changing the compares -> Code-size reduced by 1/3
All this could be done with simple peep-hole optimisation.

Are there any plans to improve the SCS codegenerator with a simple peep-hole optimizer?

To generate better code i can create special procedures in asm, better i can't find any documentation about the code-generation templates.

My wishes:

  • Better codegenerator with optimizer
  • Better documentation of CG (.prd-files and .asm-files)
  • Integration of SCS into CCS

  • The compiler currently optimizes:
    - Register copying
    - Dead code

    There has been more focus on generating correct code than on generating highly optimized code. Also, since many applications use self-timed code, we want to keep execution time for existing code as constant as possible.

    Note that your code can be optimized as follows (since i2cTx() does nothing if one or more status bits are set):
    macro setMpuReg( reg, val) {
    i2cStart();
    i2cTx( MPU_ADDR | I2C_OP_WRITE);
    i2cTx( reg );
    i2cTx( val );
    i2cStop();
    }

    There are plans to create a help document on resource and procedure definitions (that is, how to add your own or modify existing). The current documentation consist of DTD files, and the existing definition files as examples. So far we have not "encouraged" users to write their own definitions, as there is a significant support effort from our side.

    The are many reasons why Sensor Controller Studio is not integrated into CCS, most importantly that it also needs to work with other IDEs such as IAR. There may be better integration with CCS in the future through SysConfig, but there are limitations in the current SysConfig framework that prevent this.
  • Thank you for your fast reply.
    That i can remove the checks for i2cStatus is a usefull hint.

    After removing the checks for i2cStatus the assembler code for my (simple) task is excessive large.

    In Initialization i setup some registers of the Acc-Sensor.
    In Execution i read 3 or 7 16-bit values from device.
    In Termination i write 2 regsiters in device to activate standby/sleep
    In summary the Task-Code is 447 bytes. Almost half of the SC-memory in CC1310

    For codesize reduction 2 additional i2c-functions would be usefull:
    - i2cSetRegister( i2cAddr, regAddr, regValue)
    - i2cRxWord( value )

    Can you give some advise how to implement this functions via .prd-/.asm-file ?

    I think at least the function i2cSetRegister() would be usefull for many people, because this is wide spread standard pattern for I2C peripherals.
  • Is the main objective here to be sure that your code fits in the available code space?
  • Yes, i have problems to fit the code+data in the available space. With the big code there is not enough room for a second task and the data.

  • Would running the two tasks from a separate RAM image solve the problem?
  • A better solution would be to reduce the code size.
    In my application using functions for the most common cases (set register in i2c-device and reading a 16-bit word) is the best solution. That is why i asked for support in creating this functions.
  • There are limitations in hardware that makes this slightly more difficult than it sounds. The Sensor Controller CPU core has a program counter stack in hardware that allows for three levels of subroutine calls. For the I2C case, the framework uses one level to get to the task code, the i2cXxxxx() procedure calls use another level, and then the I2C procedures use the final level internally. This means that you cannot add another level of subroutine calls using the "jsr" instruction.

    It is possible to use the indirect jump "jmp R0" instruction to get around this limitation, with some added complexity (= overhead) for each procedure call.

    It sounds like a good idea to provide more complex operations such as i2cWriteReg and i2cReadReg, with support for 8- and 16-bit values, so we will look into it.

    For now, the best way to optimize your code, I think, is to put your address/value pairs in data structure array members, and do the register accesses in loop(s). If you do sequences of many writes or many reads this method should give the smallest code size. It also enables you to setup the register accesses from the System CPU application.
  • Ok, i understand the problems with the limited stack space.

    As a quick solution to reduce code size i have created a prd-file that put all the code inline. With this i have reduced the code for simple 8-bit register set from 18 instructions to 10 instructions. The i2c address of the device has always to be immediate, but register address and value can be immediate or register.

    Together with your suggestion to use a loop for initialisation it will reduce the code size enough.

    Here is my .prd-file:

    i2c_write_register_byte.prd.txt
    <?xml version="1.0" encoding="ISO-8859-15"?>
    <!DOCTYPE proc_def SYSTEM "proc_def.dtd"[]>
    <proc_def name="i2cWriteReg8" version="1.0.0">
    	<desc>Writes a 8-bit value to a register in i2c device</desc>
    	<task_resource_ref>I2C Master</task_resource_ref>
    	<impl chip_family="0,1">
    		<param type="imm" name="i2cAddr">The i2c-address of the device</param>
    		<param type="imm" name="regAddr">The address if register in i2c device</param>
            <param type="imm" name="regValue">The value to be writen to register</param>
    
            <internal name="txShiftReg" reg="R7"/>
            <internal name="status" reg="R6"/>
            <internal name="temp0" reg="R5"/>
    
            <code>
                <![CDATA[
                    ld          RS{status}, #0x0000
                    ref         RS{temp0}
                    jsr         I2cStart
                    ref         RG{temp0}
    
    				ld			RS{txShiftReg}, #I{i2cAddr}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
    				ld			RS{txShiftReg}, #I{regAddr}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
    				ld			RS{txShiftReg}, #I{regValue}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
                    ref         RS{temp0}
                    jsr         I2cStop
                    ref         RG{temp0}
                    st          RG{status}, [#A{taskName}/state/i2cStatus]
                ]]>
            </code>
    		
            <asm_file_dep>i2c_start.asm</asm_file_dep>
            <asm_file_dep>i2c_tx_byte.asm</asm_file_dep>
            <asm_file_dep>i2c_stop.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_delay.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_scl_stretch.asm</asm_file_dep>	
    	</impl>
    	<impl chip_family="0,1">
    		<param type="imm" name="i2cAddr">The i2c-address of the device</param>
    		<param type="reg" name="regAddr">The address if register in i2c device</param>
            <param type="reg" name="regValue">The value to be writen to register</param>
    
            <internal name="txShiftReg" reg="R7"/>
            <internal name="status" reg="R6"/>
            <internal name="temp0" reg="R5"/>
    
            <code>
                <![CDATA[
                    ld          RS{status}, #0x0000
                    ref         RS{temp0}
                    jsr         I2cStart
                    ref         RG{temp0}
    
    				ld			RS{txShiftReg}, #I{i2cAddr}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
    				ld			RS{txShiftReg}, RG{regAddr}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
    				ld			RS{txShiftReg}, RG{regValue}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
                    ref         RS{temp0}
                    jsr         I2cStop
                    ref         RG{temp0}
                    st          RG{status}, [#A{taskName}/state/i2cStatus]
                ]]>
            </code>
    		
            <asm_file_dep>i2c_start.asm</asm_file_dep>
            <asm_file_dep>i2c_tx_byte.asm</asm_file_dep>
            <asm_file_dep>i2c_stop.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_delay.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_scl_stretch.asm</asm_file_dep>	
    	</impl>
    </proc_def>
    

    Next step is to create a function i2_rx_word(). If it works i will post my code for use by other.

  • Thanks. I will mark the thread as resolved for now but feel free to post code if you get it up and running.
  • With some custom .prd-files, a custom asm procedure and some looping i have reduced my memory usage.

    Task Description: MPU-6050 init, get values triggered by RTC-ticks, deinit

    Original Task: 482 words (445 Code+37 Data) + 107 words procedure library

    Task with custom prd+asm : 305 words (268 Code + 37 Data) + 128 words procedure library

    Task with custom prd+asm and loops : 200 words (138 Code + 62 Data) + 128 words procedure library

    For information (and possible help for other users) i attache my custom files.

    7343.i2c_write_register_byte.prd.txt
    <?xml version="1.0" encoding="ISO-8859-15"?>
    <!DOCTYPE proc_def SYSTEM "proc_def.dtd"[]>
    <proc_def name="i2cWriteReg8" version="1.0.0">
    	<desc>Writes a 8-bit value to a register in i2c device</desc>
    	<task_resource_ref>I2C Master</task_resource_ref>
    	<impl chip_family="0,1">
    		<param type="imm" name="i2cAddr">The i2c-address of the device</param>
    		<param type="imm" name="regAddr">The address if register in i2c device</param>
            <param type="imm" name="regValue">The value to be writen to register</param>
    
            <internal name="txShiftReg" reg="R7"/>
            <internal name="status" reg="R6"/>
            <internal name="temp0" reg="R5"/>
    
            <code>
                <![CDATA[
                    ld          RS{status}, #0x0000
                    ref         RS{temp0}
                    jsr         I2cStart
                    ref         RG{temp0}
    
    				ld			RS{txShiftReg}, #I{i2cAddr}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
    				ld			RS{txShiftReg}, #I{regAddr}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
    				ld			RS{txShiftReg}, #I{regValue}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
                    ref         RS{temp0}
                    jsr         I2cStop
                    ref         RG{temp0}
                    st          RG{status}, [#A{taskName}/state/i2cStatus]
                ]]>
            </code>
    		
            <asm_file_dep>i2c_start.asm</asm_file_dep>
            <asm_file_dep>i2c_tx_byte.asm</asm_file_dep>
            <asm_file_dep>i2c_stop.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_delay.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_scl_stretch.asm</asm_file_dep>	
    	</impl>
    	<impl chip_family="0,1">
    		<param type="imm" name="i2cAddr">The i2c-address of the device</param>
    		<param type="reg" name="regAddr">The address if register in i2c device</param>
            <param type="reg" name="regValue">The value to be writen to register</param>
    
            <internal name="txShiftReg" reg="R7"/>
            <internal name="status" reg="R6"/>
            <internal name="temp0" reg="R5"/>
    
            <code>
                <![CDATA[
                    ld          RS{status}, #0x0000
                    ref         RS{temp0}
                    jsr         I2cStart
                    ref         RG{temp0}
    
    				ld			RS{txShiftReg}, #I{i2cAddr}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
    				ld			RS{txShiftReg}, RG{regAddr}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
    				ld			RS{txShiftReg}, RG{regValue}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
                    ref         RS{temp0}
                    jsr         I2cStop
                    ref         RG{temp0}
                    st          RG{status}, [#A{taskName}/state/i2cStatus]
                ]]>
            </code>
    		
            <asm_file_dep>i2c_start.asm</asm_file_dep>
            <asm_file_dep>i2c_tx_byte.asm</asm_file_dep>
            <asm_file_dep>i2c_stop.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_delay.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_scl_stretch.asm</asm_file_dep>	
    	</impl>
    </proc_def>
    

    i2c_rx_word_nack.prd.txt
    <?xml version="1.0" encoding="ISO-8859-15"?>
    <!DOCTYPE proc_def SYSTEM "proc_def.dtd"[]>
    <proc_def name="i2cRxWordNack" version="1.0.0">
        <desc>Receives without acknowledgment a single word (16-bit) over I2C.</desc>
        <task_resource_ref>I2C Master</task_resource_ref>
        <impl chip_family="0,1">
            <return name="rxValue" reg="R7">The value received</return>
            <internal name="status" reg="R6"/>
            <internal name="temp0" reg="R5"/>
    		<internal name="ackValue" reg="R4"/>
            <code>
                <![CDATA[
    				ref			RS{rxValue}
                    ld          RS{ackValue}, #0x0001
                    ld          RS{status}, [#A{taskName}/state/i2cStatus]
                    ref         RS{temp0}
                    jsr         I2cRxWord
                    ref         RG{temp0}
    				ref			RG{ackValue}
                    st          RG{status}, [#A{taskName}/state/i2cStatus]
                    ref         RG{rxValue}
                ]]>
            </code>
            <asm_file_dep>i2c_rx_word.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_delay.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_scl_stretch.asm</asm_file_dep>
        </impl>
    </proc_def>
    

    i2c_rx_word_ack.prd.txt
    <?xml version="1.0" encoding="ISO-8859-15"?>
    <!DOCTYPE proc_def SYSTEM "proc_def.dtd"[]>
    <proc_def name="i2cRxWordAck" version="1.0.0">
        <desc>Receives with acknowledgment a single word (16-bit) over I2C.</desc>
        <task_resource_ref>I2C Master</task_resource_ref>
        <impl chip_family="0,1">
            <return name="rxValue" reg="R7">The value received</return>
            <internal name="status" reg="R6"/>
            <internal name="temp0" reg="R5"/>
    		<internal name="ackValue" reg="R4"/>
            <code>
                <![CDATA[
    				ref			RS{rxValue}
                    ld          RS{ackValue}, #0x0000
                    ld          RS{status}, [#A{taskName}/state/i2cStatus]
                    ref         RS{temp0}
                    jsr         I2cRxWord
                    ref         RG{temp0}
    				ref			RG{ackValue}
                    st          RG{status}, [#A{taskName}/state/i2cStatus]
                    ref         RG{rxValue}
                ]]>
            </code>
            <asm_file_dep>i2c_rx_word.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_delay.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_scl_stretch.asm</asm_file_dep>
        </impl>
    </proc_def>
    

    i2c_rx_word.asm

    i2c_read_register_start.prd.txt
    <?xml version="1.0" encoding="ISO-8859-15"?>
    <!DOCTYPE proc_def SYSTEM "proc_def.dtd"[]>
    <proc_def name="i2cReadRegStart" version="1.0.0">
    	<desc>
            <![CDATA[
                <p>Send Register address to device and switch to read mode. Has to be followed by i2cRx*() and i2cStop()</p>
    			<p>Typical use is for reading some data from an I2C-Device like MPU-6050 IMU:</p>
    			<p>
    			i2cReadRegStart( MPU_I2C_ADDR &lt;&lt;1, MPU_REG_ACC);<br>
    			i2cRxWordAck( accX );<br>
    			i2cRxWordAck( accY );<br>
    			i2cRxWordNack( accZ );<br>
    			i2cStop();</p>
            ]]>	  </desc>
    	<task_resource_ref>I2C Master</task_resource_ref>
    	<impl chip_family="0,1">
    		<param type="imm" name="i2cAddr">The i2c-address of the device</param>
    		<param type="imm" name="regAddr">The address if register in i2c device</param>
    
            <internal name="txShiftReg" reg="R7"/>
            <internal name="status" reg="R6"/>
            <internal name="temp0" reg="R5"/>
    
            <code>
                <![CDATA[
                    ld          RS{status}, #0x0000
                    ref         RS{temp0}
                    jsr         I2cStart
                    ref         RG{temp0}
    
    				ld			RS{txShiftReg}, #I{i2cAddr} | 0x00
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
    				ld			RS{txShiftReg}, #I{regAddr}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
                    ref         RS{temp0}
                    jsr         I2cRepeatedStart
                    ref         RG{temp0}
    				
    				ld			RS{txShiftReg}, #I{i2cAddr} | 0x01
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
                    st          RG{status}, [#A{taskName}/state/i2cStatus]
                ]]>
            </code>
    		
            <asm_file_dep>i2c_start.asm</asm_file_dep>
            <asm_file_dep>i2c_tx_byte.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_delay.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_scl_stretch.asm</asm_file_dep>	
    	</impl>
    	<impl chip_family="0,1">
    		<param type="imm" name="i2cAddr">The i2c-address of the device</param>
    		<param type="reg" name="regAddr">The address if register in i2c device</param>
    
            <internal name="txShiftReg" reg="R7"/>
            <internal name="status" reg="R6"/>
            <internal name="temp0" reg="R5"/>
    
            <code>
                <![CDATA[
                    ld          RS{status}, #0x0000
                    ref         RS{temp0}
                    jsr         I2cStart
                    ref         RG{temp0}
    
    				ld			RS{txShiftReg}, #I{i2cAddr} | 0x00
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
    				ld			RS{txShiftReg}, RG{regAddr}
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
                    ref         RS{temp0}
                    jsr         I2cRepeatedStart
                    ref         RG{temp0}
    				
    				ld			RS{txShiftReg}, #I{i2cAddr} | 0x01
                    ref         RS{temp0}
                    jsr         I2cTxByte
                    ref         RG{temp0}
                    ref         RG{txShiftReg}
    
                    st          RG{status}, [#A{taskName}/state/i2cStatus]
                ]]>
            </code>
    		
            <asm_file_dep>i2c_start.asm</asm_file_dep>
            <asm_file_dep>i2c_tx_byte.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_delay.asm</asm_file_dep>
            <asm_file_dep>i2c_wait_scl_stretch.asm</asm_file_dep>	
    	</impl>
    </proc_def>