0
\$\begingroup\$

I would like to know if there are possibilities to change the following code:

static void ser_tx_ready_ISR(void)
{
    /* put data in tx buffer */
    if ((Rb_state(&serial_tx_buff) != RB_E_ERR_EMPTY) && (Rb_used_size(&serial_tx_buff)>=1))
    {
        Uart_put_char(Rb_retrieve_byte(&serial_tx_buff));
    }
}

where the important part is the Rb_retrieve_byte(struct ring_buff *_this) which returns a uint8_t.

Now, I would like to change that line by using a (rb_type)Rb_retrieve_byte(struct ring_buff *_this, uint8_t* char_ptr), but still doing it inline combined with (void)Uart_put_char(uint8_t c). So I can read back more optimally the status of the last RB access returned from the function, without using another rb API for that. The only way seems passing through reference.

Is it possible to pass the Uart_put_char(uint8_t c) as a parameter, with some reference wizard, to provide the pointer of char_ptr? I think I need to provide the reference pointer of the argument of c to the ring buffer retrieve, but I don't know how to do it without using a supporting variable, which may slow down the ISR. I just try to understand if it is really just not possible.

Or assembler with inline pointers is the only way? It is for an ATmega328p, but the code here is the application, not the HAL, so I was liking the idea of keeping it generic.

\$\endgroup\$
2
\$\begingroup\$

There is not much to improve on the following line.

Uart_put_char(Rb_retrieve_byte(&serial_tx_buff));

Since the return of a function is on stack or optimized into the registers. This is not an object you can take a pointer of.

Obviously you can remove the function calls, but that would be micro-optimization.
It counteracts the split into separate C modules.

If you change the producer to work with a returning pointer, then you must also change the consumer to work with pointers. Or have an intermediate.
But in the end, nothing changes.

Since Rb_retrieve_byte reads char to stack, which the compiler can optimize to return via a register.
And Uart_put_char reads an argument from stack, which the compiler can optimize to pass via a register.

So, enable the register passing calling convention optimization, and quit micro-optimizing.

Or if you are in need of super fast UART, make a custom driver without function calls.

\$\endgroup\$
  • \$\begingroup\$ Hey, thanks for the explanation. It was more for compatibility reasons that I need to use the pointer as argument. But if I change the consumer to work with pointers, is still possible to have the register optimization used? Because I am using references, so I guess the compiler will provide the address passed, instead of using internal registers (like one of the CPU located R0-R31 I guess). \$\endgroup\$ – thexeno Jul 22 at 7:17
  • \$\begingroup\$ @thexeno No, you will pass the pointer in a register. This will have to be dereferenced. The fact that you are using references means you hope the compiler will tear down the functions and inline it in some magic way. Check the resulting assembler to see what it does. \$\endgroup\$ – Jeroen3 Jul 22 at 7:24
  • \$\begingroup\$ @thexeno Why did you state in your question that you wanted to "optimize" the code if you really wanted to change it for arbitrary compatibility reasons? \$\endgroup\$ – Elliot Alderson Jul 22 at 11:36
  • \$\begingroup\$ Edited, so maybe is clearer for future readers. \$\endgroup\$ – thexeno Jul 22 at 18:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.