Jump to content

    

В чем причина различного написания для Cortex M4 и М0?

Библиотека DSP STM32F4xx_DSP_StdPeriph_Lib_V1.4.0

Файл arm_add_q31.c

 

 

Прошу разъяснить, какие отличия архитектуры Cortex-M3, M4 заставляют группировать по 4 операции в цикле, в отличии от одной операции в цикле для M0. Это для правильной работы конвеера?

 

/**    
* @brief Q31 vector addition.    
* @param[in]       *pSrcA points to the first input vector    
* @param[in]       *pSrcB points to the second input vector    
* @param[out]      *pDst points to the output vector    
* @param[in]       blockSize number of samples in each vector    
* @return none.    
*    
* <b>Scaling and Overflow Behavior:</b>    
* \par    
* The function uses saturating arithmetic.    
* Results outside of the allowable Q31 range[0x80000000 0x7FFFFFFF] will be saturated.    
*/

void arm_add_q31(
  q31_t * pSrcA,
  q31_t * pSrcB,
  q31_t * pDst,
  uint32_t blockSize)
{
  uint32_t blkCnt;                               /* loop counter */

#ifndef ARM_MATH_CM0_FAMILY

/* Run the below code for Cortex-M4 and Cortex-M3 */
  q31_t inA1, inA2, inA3, inA4;
  q31_t inB1, inB2, inB3, inB4;

  /*loop Unrolling */
  blkCnt = blockSize >> 2u;

  /* First part of the processing with loop unrolling.  Compute 4 outputs at a time.    
   ** a second loop below computes the remaining 1 to 3 samples. */
  while(blkCnt > 0u)
  {
    /* C = A + B */
    /* Add and then store the results in the destination buffer. */
    inA1 = *pSrcA++;
    inA2 = *pSrcA++;
    inB1 = *pSrcB++;
    inB2 = *pSrcB++;

    inA3 = *pSrcA++;
    inA4 = *pSrcA++;
    inB3 = *pSrcB++;
    inB4 = *pSrcB++;

    *pDst++ = __QADD(inA1, inB1);
    *pDst++ = __QADD(inA2, inB2);
    *pDst++ = __QADD(inA3, inB3);
    *pDst++ = __QADD(inA4, inB4);

    /* Decrement the loop counter */
    blkCnt--;
  }

  /* If the blockSize is not a multiple of 4, compute any remaining output samples here.    
   ** No loop unrolling is used. */
  blkCnt = blockSize % 0x4u;

  while(blkCnt > 0u)
  {
    /* C = A + B */
    /* Add and then store the results in the destination buffer. */
    *pDst++ = __QADD(*pSrcA++, *pSrcB++);

    /* Decrement the loop counter */
    blkCnt--;
  }

#else

  /* Run the below code for Cortex-M0 */



  /* Initialize blkCnt with number of samples */
  blkCnt = blockSize;

  while(blkCnt > 0u)
  {
    /* C = A + B */
    /* Add and then store the results in the destination buffer. */
    *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrcA++ + *pSrcB++);

    /* Decrement the loop counter */
    blkCnt--;
  }

#endif /* #ifndef ARM_MATH_CM0_FAMILY */

}

Share this post


Link to post
Share on other sites

пардон за оффтоп, но это уже кошмар какой-то

while(blkCnt > 0u)
  {
// тело цикла
    blkCnt--;
  }

 

Share this post


Link to post
Share on other sites

Почему такая конструкция считается кошмаром?

Share this post


Link to post
Share on other sites
Почему такая конструкция считается кошмаром?

Потому что это for(;;)

 

Share this post


Link to post
Share on other sites
Почему такая конструкция считается кошмаром?

В основном потому, что для С-стиля совершенно естественно написать

while( blkCnt-- )

это одно из так называемых "идеоматических выражений" в языке, устойчивая, часто встречающаяся конструкция, которую все знают с первых дней изучения языка и с пониманием которой не возникает никаких проблем как раз ввиду частого использования.

Т.е. появление в коде такой конструкции с головой выдаёт недостаток опыта работы на С/С++ и то, что человек мало читал хорошего чужого кода. Это как стилистические несуразности в прозе - сразу видно что автор ни Толстого с Пушкиным, ни даже "Трёх мушкетёров" почти не читал, букварём ограничился.

Впрочем там и аналогов грамматических ошибок хватает, я в кубе уже не меньше десятка нашёл.

 

А уж уточнение что ноль беззнаковый (0u)- это просто шедевр.

Первый операнд в операции сравнения, blkCnt, объявлен как uint32_t и потому второй операнд всё равно будет приведён к unsigned типу и сравнение будет беззнаковым. Не говоря уж о том, что разница между знаковым и беззнаковым сравнением будет только когда этот несчастный blkCnt перевалит границу в 2 Гига, что на STM32 означает никогда, у него столько памяти просто нет.

Share this post


Link to post
Share on other sites
появление в коде такой конструкции с головой выдаёт недостаток опыта работы на С/С++
а может и наоборот - обнаружили что компилер лучше оптимизирует именно этот вариант.

Share this post


Link to post
Share on other sites
while( blkCnt-- )

Это будет другая конструкция, с преддекрементом индексной переменной относительно тела цикла

 

а может и наоборот - обнаружили что компилер лучше оптимизирует именно этот вариант.

Это было бы странно.

Share this post


Link to post
Share on other sites
Это будет другая конструкция, с преддекрементом индексной переменной относительно тела цикла

Эта переменная ни разу не используется в теле цикла, кроме, собственно, сравнения в while() и постдекремента.

Edited by one_eight_seven

Share this post


Link to post
Share on other sites
Это будет другая конструкция, с преддекрементом индексной переменной относительно тела цикла

Разве это не постдекремент? Сначала проверили blkCnt, приняли решение идти в цикл, а потом декрементировали.

Share this post


Link to post
Share on other sites
Разве это не постдекремент? Сначала проверили blkCnt, приняли решение идти в цикл, а потом декрементировали.

относительно тела цикла

 

но в данном случае без разницы, как уже выяснили.

а я бы все равно for() написал.

Share this post


Link to post
Share on other sites
А уж уточнение что ноль беззнаковый (0u)- это просто шедевр.

Так делают, чтобы компилер не сыпал варнинги, типа "сравнение беззнакового и знакового".

Share this post


Link to post
Share on other sites
но в данном случае без разницы, как уже выяснили.

а я бы все равно for() написал.

Как же, for()? Здесь не бесконечный цикл. Все работает, пока до нуля не усохнет.

Share this post


Link to post
Share on other sites
Прошу разъяснить, какие отличия архитектуры Cortex-M3, M4 заставляют группировать по 4 операции в цикле, в отличии от одной операции в цикле для M0. Это для правильной работы конвеера?

 

в вашем коде идет считывание и сохранение. Это инструкции LDM и STM

для M3 аргументами могуть быть все регистры, но из-за особенностей реализации компиляторов C, используются только четыре

вот к примеру как идет копирование в СМ3 - сразу по 16 байт ( четыре регистра )

LDM R1!, {R3-R5, R12}

SUBS R2, R2, #16

STM R0!, {R3-R5, R12}

BCS.N 0x295a

 

но в CM0 для инструкций LDM и SDM есть ограничение reglist and Rn are limited to R0-R7.

 

 

P.S. словоблуды до While докопались

Share this post


Link to post
Share on other sites
для M3 аргументами могуть быть все регистры, но из-за особенностей реализации компиляторов C, используются только четыре

Но тут почему-то 8 регистров за раз! Это не считая счётчика цикла и указателей src/dst.

Какой компилятор такое умеет, интересно?

 

 

 

P.S. словоблуды до While докопались

Вот да. А оригинальный-то вопрос интересный!

Следующий холивар "только последние дилетанты используют пробелы для отступов в коде!!!" :-(

Share this post


Link to post
Share on other sites

Пробовал компилировать для разных архитектур, ничего принципиально выигрышного от такой записи не увидел (но я программист - никакой). Может это наследие от типов q7 и q15 для использования simd инструкций? Но у М3 нет simd. Так ничего и не понял.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this