EEVblog Electronics Community Forum

Products => Computers => Programming => Topic started by: LootMaster on November 16, 2020, 10:59:20 pm

Title: Help me understand this code
Post by: LootMaster on November 16, 2020, 10:59:20 pm
https://www.youtube.com/watch?v=Zqwq9nzTNF8 (https://www.youtube.com/watch?v=Zqwq9nzTNF8)

I dont seem to understand this guy's code @ 4:20

So I understand that according to Cortex-M standards, 0xE0000000 is the starting address of the "ITM stuff".

https://developer.arm.com/documentation/ddi0337/h/instrumentation-trace-macrocell-unit/itm-programmers-model (https://developer.arm.com/documentation/ddi0337/h/instrumentation-trace-macrocell-unit/itm-programmers-model)

#define ITM_Port32(n) (*((volatile unsigned long *)(0xE0000000+4*n)))

Can anybody explain to me moe specifically whats the parenthese mean and maybe give some broader insight? It hurts my eyes...

Thanks.
Title: Re: Help me understand this code
Post by: RenThraysk on November 16, 2020, 11:20:41 pm
There is a continuous section of memory forming an array of unsigned longs starting at 0xE0000000.

The sub expression, ((volatile unsigned long *)(0xE0000000+4*n)), is taking the index and computing the address of a particular unsigned long.

The complete expression, *((volatile unsigned long *)(0xE0000000+4*n)), is reading the unsigned long value stored at that address.
Title: Re: Help me understand this code
Post by: Ian.M on November 16, 2020, 11:38:05 pm
Most of the parentheses are just parentheses, used to group an expression so its evaluated before the outer operator gets it.  The outer pair are so that adjacent operators when/where the #defined name is used don't affect evaluation within it.  The (n) after the name makes the name a function-like macro, with parameter n.  The pair immediately round volatile unsigned long * are a cast operator, in this case casting an integer to that pointer type.

There's no particular 'magic', this is just 'vanilla' C, constructing a pointer to a port (using an architecture and processor dependent integer base address) and dereferencing it for access.

Arguably, the parameter n should have parentheses round where its used, as if an expression not wrapped in parentheses containing any operator with lower precedence than multiplication is used for n, the result will be incorrect and probably cause a misaligned pointer error.   Here's the fixed version:
Code: [Select]
#define ITM_Port32(n) (*((volatile unsigned long *)(0xE0000000+4*(n))))
Title: Re: Help me understand this code
Post by: ataradov on November 16, 2020, 11:50:15 pm
Or if you use stop that handmade approach and just use standard CMSIS headers, this will turn into:
Code: [Select]
ITM->PORT[n].u32
Title: Re: Help me understand this code
Post by: LootMaster on November 16, 2020, 11:56:16 pm
So lets say, I wanted to listen in on 2 different variables..

one would be sending to stimulus port 31 and the other stimulus port 0

ITM_Port32(31)=1

ITM_Port32(0)=9

How would that code look like? I want somthing like this, I think I would have to let my debugger know what to expect like this.
Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 12:13:15 am
Your code is correct assuming that both ports are enabled. I have no idea about that specific debugger and its configuration.

But usually you just use one port and define a protocol to differentiate your data.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 12:15:38 am
So why do the other 30 buffers or 31  :scared:

Exist at all?

Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 12:17:57 am
Because ITM is a very generic peripheral. And the sources don't have to be all from the software. It can be anything that system designer decided to configure inside the device.

And also, specific implementations do not have to support all of them. You are supposed to read the ID register to find out how many are implemented on your hardware.

So most people just use it as a fast debug UART with port 0 only. This is the most compatible use.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 12:21:55 am
So let me get this straight, if I go by CMSIS and #include "core_cm7.h" in my main.c file... This is a header that contains ITM stuff in the CMSIS section.

I can use your CMIS technique instead of his ugly ((( ))) thing.

As long as I understand that, using the following define, to get the same address I offset by 1?

#define ITM_Port32(n)  (*((volatile unsigned long *)(0xE0000000+4*n)))

ITM_Port32(31) is same as ITM->PORT[n].u32 ??
Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 12:25:57 am
I can use your CMIS technique instead of his ugly ((( ))) thing.
It is CMSIS and if you plan on doing embedded development you need to read those ugly things. This is a standard way to refer to memory mapped locations from C.

As long as I understand that, using the following define, to get the same address I offset by 1?

#define ITM_Port32(n)  (*((volatile unsigned long *)(0xE0000000+4*n)))

ITM_Port32(31) is same as ITM->PORT[n].u32 ??

I don't understand this. Your definition of ITM_Port32(n) is equivalent to ITM->PORT[n].u32. Where 'n' is the index of the port. u32 here is the size of the data to send. It can be u8, u16 or u32. In your macro you used "unsigned long", which is the same as u32.

So ITM_Port32(31) is ITM->PORT[31].u32.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 12:29:53 am
oh,

Yeah, something like that, I guess.

Monkey see , monkey do.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 01:55:12 am
Ok Help me out  :scared:, Heres me code ... It reacts strangely yet predictably, I think I outdone myself this time.  :clap:

/* Includes ------------------------------------------------------------------*/
#include "main.h"
.....



/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "core_cm7.h"
/* USER CODE END Includes */
....

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint16_t data31 =0;
uint16_t data0  =0;
/* USER CODE END 0 */

....

  while (1)
  {

                ITM->PORT[31].u32 =data31;
                data31++;
      HAL_Delay(500);
      ITM->PORT[0].u32 = data0;
      data0++;
      HAL_Delay(500);

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 01:58:21 am
What did you expect? It outputs raw binary numbers. If you want nice looking numbers you have to format them like that in the device into a string and print that string.

Also, you are supposed to wait for the ready bit. And use u8 instead of u32 if you are just printing strings.

The same core_cm7.h contains a function ITM_SendChar () that you can just use directly. It checks that things are enabled and port is ready.


Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 02:04:16 am
Cant this SWO thing send in 4 byte chunks?

I have a 16 bit int here, how to send as fast as possible?

Please change my code bro, I dont wanna use Send_char anymore I wanna use your method, this is way more slick.

edit: sorry, this thing is actually 2mhz pretty slow...it goes out as it comes.
Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 02:10:44 am
It can and it does, but this debugger interprets them as ASCII characters. Probably because that's how majority of people use it. There is nothing you can do here, you will have to convert your number internally into ASCII and print it byte by byte. Or get a different debugger that is more flexible in how it reads the data.

ITM_SendChar() is my method, but used correctly. It internally writes ITM->PORT[0].u8.

The quickest and hackiest way would be something like this:
Code: [Select]
//-----------------------------------------------------------------------------
void puthex(uint32_t v, int size)
{
  char hex[] = "0123456789abcdef";

  for (int i = 0; i < size; i++)
  {
    int offs = ((size - 1) - i) * 4;
    ITM_SendChar(hex[(v >> offs) & 0xf]);
  }
}

And in the loop:

puthex(data31++, 8);
ITM_SendChar('\r'); // Line ending so numbers go on a new line
ITM_SendChar('\n');
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 02:16:21 am
  Nope  :scared: :scared: :scared:

while (1)
  {

      ITM->PORT[31].u8 =data31;
      data31++;
      HAL_Delay(500);
      ITM->PORT[0].u8 = data0;
      data0++;
      HAL_Delay(500);

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

Does not work, nothing is shown in ITM console

Wow I am discovering symbols I never thought were possible...Cool
Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 02:19:27 am
Your code is incorrect. It does not wait for the readiness of the port to accept the data. And "500" may not be enough of a delay. Is it 500 ms or 500 us?. The expected end result would be the same with this code.

Or may be that debugger you are using can only receive words, which would be stupid.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 02:31:06 am
Those are in milliseconds. It should leave it enough time... when I replace 32 by 8 I get absolutely nothing.

But you are right, I should check the readyness of the port.

if (what flag?) ? .. How do you know by instinct the CMSIS command??

Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 02:33:50 am
΅Ά·ΈΉΊ΋Ό΍ΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώϏϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁ҂҃҄҅҆҇҈҉ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋ
Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 02:33:58 am
Stop resisting, open core_cm7.h, and look at the source of ITM_SendChar(). Why are you making your life harder for no reason?

The flag is the same register. If ITM->PORT[0].u32 reads as 0, the port is not ready.

But 500 ms should be enough. Something else is wrong.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 02:36:16 am
I should be able to change this on either

1: the debugger
2: the java interpretation

ST-LINK-V3...
Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 02:37:00 am
I should be able to change this on either

1: the debugger
2: the java interpretation

ST-LINK-V3...
Change what? I don't understand what you mean.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 02:39:16 am
obviously this is just an offsetted number of a wrong binary type.

Send Char has more instructions that this.
Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 02:42:48 am
It may be interpreting it as UTF-8-encoded string or something like this. I don't know.

But in any case there is no way your code would just print numbers. That's not how it works and that's the last time I will repeat that. If you don't want t o listen, it is fine with me.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 02:45:00 am
Ok I will stop resisting and check out that function.
you win you win
It doesnt work like I think it does not matter If want it to be that way... :rant:
Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 02:45:58 am
It is not about that function. It is about the puthex code that I proposed that would actually print the numbers you want (in hex).

You can use itoa() or sprintf() or whatever you like to turn your number into a string and then output that string on the port.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 02:48:08 am
Thats what I meant...  :phew:
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 04:42:56 am
I finally got this whole thing to work.

I think the host of the video, the ST employee, simply is a bit ....  :palm:

What is the point of ITM_Port32(31)=1; and ITM_Port32(31)=2; besides causing unnecessary confusion for beginners since send ITM_SendChar will send to port 0 anyways this making those lines useless number pushing



 \brief   ITM Send Character
  \details Transmits a character via the ITM channel 0, and
           \li Just returns when no debugger is connected that has booked the output.
           \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted.
  \param [in]     ch  Character to transmit.
  \returns            Character to transmit.
Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 04:45:36 am
Those two things are not related to each other. You should chill accusing other people if you just learned about the thing and don't really understand it.

You can write your own ITM_SendChar() that sends on whatever channel you like. The default one sends on 0.

He is showing the basic functionality that ITM_SendChar() is based on.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 04:53:31 am
I'm sorry, its just his voice...  :palm:
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 07:00:20 pm
Most of the parentheses are just parentheses, used to group an expression so its evaluated before the outer operator gets it.  The outer pair are so that adjacent operators when/where the #defined name is used don't affect evaluation within it.  The (n) after the name makes the name a function-like macro, with parameter n.  The pair immediately round volatile unsigned long * are a cast operator, in this case casting an integer to that pointer type.

There's no particular 'magic', this is just 'vanilla' C, constructing a pointer to a port (using an architecture and processor dependent integer base address) and dereferencing it for access.

Arguably, the parameter n should have parentheses round where its used, as if an expression not wrapped in parentheses containing any operator with lower precedence than multiplication is used for n, the result will be incorrect and probably cause a misaligned pointer error.   Here's the fixed version:
Code: [Select]
#define ITM_Port32(n) (*((volatile unsigned long *)(0xE0000000+4*(n))))

ITM_Port32(31)=1;

So essentially, he made an expression to to find a memory location that he can write too?

A "pointer" to an offsetted array by 0xE0000000 and distanced by 4 bytes according to n.

What bothers me is that there is no limit to that number that is set for n, isnt that dangerous to not declare a limit as you #define it? At least the compiler might warn you..

so when he puts ITM_Port32(31)=1;

the processor goes to a memory location pointer ITM_port... wich applies a formula go(0xE0000000+4*(31)) and write 1.

Am I correct? What is the correct terminology for this sort of mind tricks, honestly these boklets I read never contained such hardcore parentheses examples.

I was just confused as o why he would want to do that....ITM_Port32(31)=1

If all the action happens with ITM_Port32(0)=1 or w/e...Since this is a queued slow process anyways... Whats the benefit writing to those "ghost town" stimulus ports? If there was better data organization and faster with the eclipse interpretation I could understand.

Just poor clarity on the matter.. nd the guy just lost me with his goofy attitude...I  want SERIOUS videos made by pros.

Title: Re: Help me understand this code
Post by: ataradov on November 17, 2020, 07:12:22 pm
So essentially, he made an expression to to find a memory location that he can write too?
Yes, this is how memory mapped I/O works.

What bothers me is that there is no limit to that number that is set for n, isnt that dangerous to not declare a limit as you #define it? At least the compiler might warn you..
You are working with a low level language. Of course it is dangerous if you don't know what you are doing.

If there is ever a change that n can be taken in run-time from the user, then you need to verify it, of course. Any user inputs need to be verified.

Am I correct? What is the correct terminology for this sort of mind tricks, honestly these boklets I read never contained such hardcore parentheses examples.
Correct. This is called memory mapped I/O.

I was just confused as o why he would want to do that....ITM_Port32(31)=1
To show you that it is possible and multiple channels are implemented on this device.

If all the action happens with ITM_Port32(0)=1 or w/e...Since this is a queued slow process anyways.
The action happens where you make it happen.
Title: Re: Help me understand this code
Post by: LootMaster on November 17, 2020, 07:28:04 pm
"The action happens where you make it happen."

I can see a "very occasionnal use" for this if I had a situation where multiple data is changed back to back and I just want to dump each individual data into seperate channels, then for a "long long while", let the data flow to computer... But dont I already have tht sort f thing when I put 4 variables on the "swo comparator for channel 0 ???..It will utilize 100% of the BW of the SWO albeit with 4 variables only?, wich in my case with ST-Link V3 is 2MHz

A sort of "burst situation"... If I dont wanna deal with conditionnal constraints of port 0 and I am in a rush...I just dont see the need for this often.

But I would need a new ITM_Sendchar function,where now I change the 0.32 to [n].32 in the CMSIS thing, another argument is needed.

Title: Re: Help me understand this code
Post by: LootMaster on November 18, 2020, 07:07:30 am
So I discovered something... All that was needed was to observe this in the SWV Trace log...Tickbox port 31...When in this mode it interprets the raw binary correctly and I cna see the numbers on screen. 1 and 2

MX_GPIO_Init();

 
  MX_TIM2_Init();
  MX_TIM8_Init();
  /* USER CODE BEGIN 2 */
  ITM_Port(31)=1;
  HAL_Delay(100);
  ITM_Port(31)=2;

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

//printf("I'm in the ain loop =%ld\n", data0); not using

HAL_Delay(500); // boring code....

  }



Another question

Why does AXI-> not propose me any auto complete? There are a few AXI registers that have some writeable bit fields ( most are read only). Same for Flash->

I can have autocomplete work with JPEG-> and I dont have anything related to that I dont understand...
Title: Re: Help me understand this code
Post by: ataradov on November 18, 2020, 07:14:59 am
Why does AXI-> not propose me any auto complete?
That I don't know. I don't use IDEs and auto complete.

Most likely it takes autocomplete data from the header files, but the structure of the registers on the top comes from the SVD file. They may not match generally.

It is also possible that some files that have those definitions for AXI are not included in the project, so the IDE does not see them.
Title: Re: Help me understand this code
Post by: LootMaster on November 18, 2020, 07:19:35 am
Dam , it sucks that its not universally true that anything that I see in the SFR register view

X-> was always observeable and followed the datasheet by me in a monkey see monkey do fashion.

Guess there are exeptions. Could this be on purpose for limiting damage?
Title: Re: Help me understand this code
Post by: ataradov on November 18, 2020, 07:24:47 am
Guess there are exeptions. Could this be on purpose for limiting damage?
What damage? There is nothing permanently damaging you can do. And nobody is going to be babysitting you here, it is embedded development.

Just look in your header files and see where this structure is defined and how it is named. AXI may have conflicted with something else, so it was renamed to accommodate that conflict, who knows.

May be whoever generated those header files just forgot to include that structure, or they were generated based on some older SVD file that dud not have it.

Or may be Eclipse just fails to parse something in that file and silently fails to update the completion list.

The only way to tell is to look in the header file.