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.

Linux/AM3358: PRU General Purpose Register access

Part Number: AM3358


Tool/software: Linux

Hi,

General purpose registers __R30 and __R31 are defined in pru header files and can be used for writing and reading I/O pins. If I want to write to GP R1. Can I use the following expression:

 (*(volatile unsigned int *) 0x00024404) = 300;

-Based on global memory map on page 207 of TRM the PRU1 Debug register is located at: 0x0002_4400

-On page 283 register description provides 0x4 offset to get to GPREG1. That is how I came up with address 0x00024404.

My problem is that I don't get correct behavior. In my C code I use function start() defined in assembly file.

void main(void)
{

//    (*(volatile unsigned int *) 0x00024404) = 300;

    start();
}

//Assembly file:

        .cdecls "perfTestContainer.c"

        .clink
        .global start
start:
	;nop
	LDI	r1, 40	 ; load the DELAY value into r1

MAINLOOP:
	CLR	r30.t5		; 
	CLR	r30.t5		; 
	MOV	r0, r1		; l

DELAYOFF:
	SUB	r0, r0, 1	; 
	QBNE	DELAYOFF, r0, 0	; 
	SET	r30.t5		; 
	MOV	r0, r1		; 

DELAYON:
	SUB	r0, r0, 1	; 
	QBNE	DELAYON, r0, 0	; 
	QBA	MAINLOOP	; 
END:
	HALT			; halt the pru 

If I write manually to r1 (    LDI    r1, 40     ; load the DELAY value into r1) I get expected output. However, using (*(volatile unsigned int *) 0x00024404) doesn't work.

I didn't find that those registers are protected or require some special access. Did I miss anything?

Thanks in advance.

  • Hello Lukasz,

    What are you trying to accomplish by accessing the debug registers? Note that PRU1 must be halted if you want to access its debug register regions. There might be a better solution to accomplish what you want.

    Note that a PRU would not be able to to access PRU0/PRU1 Debug registers from its local data memory map. You will see that the debug addresses in the PRU-ICSS Global Instruction Memory Map are reserved in the PRU-ICSS Local Data Memory Map. If you do decide that debug register accesses make sense for your design, then you want to take a closer look at the examples in the PRU-ICSS Global Memory Map section (e.g. Example 1).

    Regards,
    Nick
  • Hi Nick,

    I am just exploring capabilities of PRU, I am trying to use some of the assembly written routines inside of my C code. Basically I wanted to change the value that is used in assembly code. After start label (line 15 in my post) I load a given value to r1. What I would like is to have this value already written inside of the r1 register.

    -For instance, page 285 in TRM describes GPREG0. It is written that writing or reading to these registers will have the same effect as read or write to these registers from an internal instruction in the PRU. From what I understood writing to address 0x00024404 would allow me to provide a desired value for my assembly routine.

    -After you comment I have noticed that in a local memory map 0x00024400 address is actually reserved (page 206). In this case r1 inside of the assembly code doesn't refer to one of the General Purpose Registers that are described in the section 4.5.2 of the TRM? How could I load a value to r1 before the assembly part?

    I hope it makes more sense.

    Thank you a lot.
  • Hello Lukasz,

    Ok, if I understand your question properly: You have a function written in assembly, and you want to pass a variable in from your C code to your assembly code.

    Take a look at the PRU Optimizing C/C++ Compiler User's Guide , section "Interfacing C and C++ With Assembly Language".

    Regards,

    Nick

  • Hi Nick,

    I checked the Compiler User Guide. I tried to call function defined in assembly inside the C code (passing input variable).  My code looks like this:

    #include <stdint.h>
    #include <pru_cfg.h>
    #include "resource_table_empty.h"
    
    extern void start(int); 
    int gvar= 1000;
    
    volatile register uint32_t __R30;
    void main(void)
    {
    
        int p=100;
        start(p);
    
    }
            .cdecls "perfTestContainer.c"
            .clink
            .global start
    		.global gvar
        ;//    #define A 498
    start:
    	;nop
    	LDI	r1, gvar ; load the DELAY value into r1
    
    MAINLOOP:
    	CLR	r30.t5		
    	CLR	r30.t5		
    	MOV	r0, r1		
    
    DELAYOFF:
    	SUB	r0, r0, 1	
    	QBNE	DELAYOFF, r0, 0	
    	SET	r30.t5		
    	MOV	r0, r1		
    
    DELAYON:
    	SUB	r0, r0, 1	
    	QBNE	DELAYON, r0, 0	
    
    	QBA	MAINLOOP	
    END:
    	HALT			

    I was referring to section 6.6.2 of the user guide:

    Compilation is ok, however, input argument to start() doesn't change the behavior. Do you see any mistake?

    Thank you again.

  • "Functions defined in assembly that will be called from C++ must be prototyped as extern "C" in C++" - take another look at example 6-1
  • Hello Lukasz,

    I am going to close this thread on my end. Please reply if we need to continue the discussion.

    Regards,
    Nick
  • Hi Nick,

    I am sorry for the delay but I didn't manage to look back on the case during last couple days. I am going to check your last comments shortly.

    Thank you.
  • Hello Lukasz,

    No problem, no rush. Talk with you soon.

    Regards,
    Nick
  • Hi Nick,

    Doesn't the 6.1 example refer to C++ code? Extern "C" is a feature for function names mangling. I write in C.

    Thank you.
  • Hi Nick,

    To summarize:

    - with function declared as below inside the C file the assembly defined function (start()) runs fine. I don't need to use extern "C".  .global start in assembly file and extern void start(void) in C file seem to do the job when function has void argument.

    Declaration in C:

    extern void start(void);
    

    Assembly function:

            .cdecls "perfTestContainer.c"
            .clink
            .global start
            ;.global gvar
    
    start:
    	;nop
    	;LDI	r1, gvar
    	LDI	r1, 3000 
    MAINLOOP:
    	CLR	r30.t5		
    	CLR	r30.t5		
    	MOV	r0, r1		
    DELAYOFF:
    	SUB	r0, r0, 1	
    	QBNE	DELAYOFF, r0, 0	
    	SET	r30.t5		
    	MOV	r0, r1		
    DELAYON:
    	SUB	r0, r0, 1	
    	QBNE	DELAYON, r0, 0	
    
    	QBA	MAINLOOP	
    END:
    	HALT			

    -The problem that I cannot figure out is that I cannot successfully modify this function to accept input. As in my response from Apr 1, 2019 12:43 AM.

    Thank you Nick in advance.

  • Hi Nick,

    An update:
    - Section 6.4 of SPRUHV7B (Function Structure and Calling Conventions) describes rules for assembly function calls (or more describes the C/C++ compiler rules of calling functions). Pages 109-110 describe that R14-R29 are used by the function caller to place the arguments of the calee function.

    Thank you.
  • Hello Lukasz,

    1) You are correct that extern C is only used when programming in C++, my apologies.

    2) Section 6.3 "Register Conventions" may also be useful. Note that R14 may be used to return a value back to the code that called the assembly function.

    Regards,

    Nick

  • Hi,

    Thank you for the discussion. In twas good step into exploring PRU.