Программирование ARM ESP32: система команд сопроцессора ULP Fri, December 06 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


ESP32: система команд сопроцессора ULP Печать
Добавил(а) microsin   

У сопроцессора ULPFSM имеется 4 регистра общего назначения, каждый разрядности 16-бит: R0, R1, R2, R3. У него также есть 8-битный регистр счетчика (stage_cnt), который может использоваться для реализации циклов. К регистру stage_cnt осуществляется доступ с помощью специальных инструкций.

Сопроцессор ULP может обращаться к 8 килобайтам области памяти RTC_SLOW_MEM. Память адресуется 32-битными словами. Сопроцессор также может обращаться к регистрам периферийных устройств RTC_CNTL, RTC_IO и SENS.

Все инструкции сопроцессора 32-разрядные. Инструкции перехода (Jump), арифметических операций (ALU), доступа к регистрам периферийных устройств и памяти выполняются за 1 такт. Инструкции, которые работают с периферийными устройствами (TSENS, ADC, I2C) выполняются переменное количество тактов, в зависимости от операции периферийного устройства.

Синтаксис инструкций не чувствителен к регистру символов. Большие и маленькие буквы могут использоваться произвольным образом, в том числе и вперемежку. Это верно как для имен регистров, так и для имен инструкций.

Адресация. Инструкции JUMP, ST, LD ожидают аргумент адреса, выраженный следующим способом, в зависимости от типа адреса аргумента:

• Когда аргумент адреса представлен как метка, то инструкция ожидает адрес, выраженный как 32-битное слово.

Рассмотрим следующий пример программы:

entry:
         NOP
         NOP
         NOP
         NOP
loop:
         MOVE R1, loop
         JUMP R1

Когда эта программа ассемблируется и линкуется, адрес метки цикла будет равен 16 (в байтах). Однако инструкция JUMP ожидает, что адрес, сохраненный в R1, выражен в 32-битном слове. Чтобы учесть такой общий случай, ассемблер преобразует байтовый адрес метки цикла в номер слова, затем генерирует инструкцию MOVE. Генерируемый код будет эквивалентен следующему:

0000     NOP
0004     NOP
0008     NOP
000c     NOP
0010     MOVE R1, 4
0014     JUMP R1

• Другой случай - когда аргумент инструкции MOVE не метка, а константа. В этом случае ассемблер будет использовать значение как есть, без преобразования:

.set     val, 0x10
MOVE     R1, val

В этом случае в регистр R1 будет загружено значение 0x10.

Однако когда непосредственное значение используется как смещение в инструкциях LD и ST, ассемблер считает аргумент адреса в байтах, и преобразует его в 32-битное слово перед выполнением инструкции:

ST R1, R2, 4        // смещение = 4 байта; Mem[R2 + 4 / 4] = R1

В этом случае значение R1 сохраняется в ячейку памяти, указанную как [R2 + смещение / 4].

Рассмотрим следующий код:

         .global array
array:   .long 0
         .long 0
         .long 0
         .long 0
 
         MOVE R1, array
         MOVE R2, 0x1234
         ST R2, R1, 0      // запись значения R2 в первый элемент массива,
                           // т. е. в array[0]
 
         ST R2, R1, 4      // запись значения R2 во второй элемент массива,
                           // со смещением 4 байта, т. е. в array[1]
         
         ADD R1, R1, 2     // эта команда инкрементирует адрес на 2 слова
                           // (8 байт)
         ST R2, R1, 0      // запись значения R2 во третий элемент массива,
                           // т. е. в array[2]

Время выполнения инструкции. Сопроцессор ULP тактируется с частотой RTC_FAST_CLK, которая обычно получается из внутреннего RC-генератора 8 МГц. Приложения, которым нужно знать точную частоту ULP, могут откалибровать её по частоте основных тактов XTAL:

// Калибровка тактов 8M/256 по частоте XTAL, получение 8M/256 периодов тактов
uint32_t rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 100);
uint32_t rtc_fast_freq_hz = 1000000ULL * (1 << RTC_CLK_CAL_FRACT)
                                       * 256 / rtc_8md256_period;

Для выборки каждой инструкции сопроцессору ULP требуется определенное количество тактов, плюс некоторое количество тактов для её выполнения, в зависимости от инструкции. Конкретное время выполнения можно узнать далее в описании каждой инструкции.

Время выборки инструкции равно:

2 периода тактов — для инструкций, которые идут за инструкцией ALU и инструкции ветвления.
4 периода тактов — в остальных случаях.

Обратите внимание, что когда осуществляется доступ к ячейкам памяти RTC и регистрам RTC, у сопроцессора ULP приоритет ниже, чем у ядер CPU. Это значит, что выполнение инструкций сопроцессора ULP может быть приостановлено, когда основной CPU обращается к тому же самому региону памяти, что и ULP.

[Справочник по системе команд ULP]

Синтаксис:

NOP

Операнды: отсутствуют.

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Пример:

         NOP

Синтаксис:

ADD Rdst, Rsrc1, Rsrc2
ADD Rdst, Rsrc1, imm

Операнды:

Rdst - регистр R[0..3]
Rsrc1 - регистр R[0..3]
Rsrc2 - регистр R[0..3]
Imm - 16-разрядное значение со знаком

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция добавляет регистр источника к другому регистру источника, или к 16-битному значению со знаком, и сохраняет результат в регистр назначения.

Пример1:

      ADD R1, R2, R3        // R1 = R2 + R3

Пример 2:

      Add R1, R2, 0x1234    // R1 = R2 + 0x1234

Пример 3:

      .set value1, 0x03     // константа value1=0x03
      Add R1, R2, value1    // R1 = R2 + value1

Пример 4:

      .global label         // декларация переменной label
      add R1, R2, label     // R1 = R2 + label
       ...
label:
      nop                   // определение переменной label

Синтаксис:

SUB Rdst, Rsrc1, Rsrc2
SUB Rdst, Rsrc1, imm

Операнды:

Rdst - регистр R[0..3]
Rsrc1 - регистр R[0..3]
Rsrc2 - регистр R[0..3]
Imm - 16-разрядное значение со знаком

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция вычитает регистр источника из другого регистра источника, или вычитает 16-битное значение со знаком из регистра источника, и сохраняет результат в регистр назначения.

Пример 1:

           SUB R1, R2, R3             // R1 = R2 - R3

Пример 2:

           sub R1, R2, 0x1234         // R1 = R2 - 0x1234

Пример 3:

      .set value1, 0x03     // константа value1=0x03
      SUB R1, R2, value1    // R1 = R2 - value1

Пример 4:

      .global label         // декларация переменной label
      SUB R1, R2, label     // R1 = R2 - label
      ...label:
      nop                   // определение переменной label

Синтаксис:

AND Rdst, Rsrc1, Rsrc2
AND Rdst, Rsrc1, imm

Операнды:

Rdst - регистр R[0..3]
Rsrc1 - регистр R[0..3]
Rsrc2 - регистр R[0..3]
Imm - 16-разрядное значение со знаком

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция выполнит логическое И исходного регистра и другого исходного регистра, или 16-битного значения со знаком, и результат сохраняет в регистр назначения.

Пример 1:

      AND R1, R2, R3        // R1 = R2 & R3

Пример 2:

      AND R1, R2, 0x1234    // R1 = R2 & 0x1234

Пример 3:

      .set value1, 0x03     // константа value1=0x03
      AND R1, R2, value1    //R1 = R2 & value1

Пример 4:

      .global label         // декларация переменной label
      AND R1, R2, label     // R1 = R2 & label
       ...
label:
      nop                   // определение переменной label

Синтаксис:

OR Rdst, Rsrc1, Rsrc2
OR Rdst, Rsrc1, imm

Операнды:

Rdst - регистр R[0..3]
Rsrc1 - регистр R[0..3]
Rsrc2 - регистр R[0..3]
Imm - 16-разрядное значение со знаком

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция выполняет логическое ИЛИ исходного регистра и другого исходного регистра, или 16-битного значения со знаком, и результат сохраняет в регистр назначения.

Пример 1:

      OR R1, R2, R3         // R1 = R2 | R3

Пример 2:

      OR R1, R2, 0x1234     // R1 = R2 | 0x1234

Пример 3:

      .set value1, 0x03     // константа value1=0x03
      OR R1, R2, value1     // R1 = R2 | value1

Пример 4:

      .global label         // декларация переменной label
      OR R1, R2, label      // R1 = R2 |label
       ...
label:
      nop                   // определение переменной label

Синтаксис:

LSH Rdst, Rsrc1, Rsrc2
LSH Rdst, Rsrc1, imm

Операнды:

Rdst - регистр R[0..3]
Rsrc1 - регистр R[0..3]
Rsrc2 - регистр R[0..3]
Imm - 16-разрядное значение со знаком

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция делает логический сдвиг влево исходного регистра на указанное количество бит в другом исходном регистре или 16-битном значении со знаком, и сохраняет результат в регистр назначения.

Пример 1:

       LSH R1, R2, R3            // R1 = R2 << R3

Пример 2:

       LSH R1, R2, 0x03          // R1 = R2 << 0x03

Пример 3:

      .set value1, 0x03     // константа value1=0x03
      LSH R1, R2, value1    // R1 = R2 << value1

Пример 4:

      .global label         // декларация переменной label
      LSH R1, R2, label     // R1 = R2 << label
       ...
label:
      nop                   // определение переменной label

Синтаксис:

RSH Rdst, Rsrc1, Rsrc2
RSH Rdst, Rsrc1, imm

Операнды:

Rdst - регистр R[0..3]
Rsrc1 - регистр R[0..3]
Rsrc2 - регистр R[0..3]
Imm - 16-разрядное значение со знаком

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция делает логический сдвиг вправо исходного регистра на указанное количество бит в другом исходном регистре или 16-битном значении со знаком, и сохраняет результат в регистр назначения.

Пример 1:

        RSH R1, R2, R3              // R1 = R2 >> R3

Пример 2:

        RSH R1, R2, 0x03            // R1 = R2 >> 0x03

Пример 3:

      .set value1, 0x03     // константа value1=0x03
      RSH R1, R2, value1    // R1 = R2 >> value1

Пример 4:

      .global label         // декларация переменной label
      RSH R1, R2, label     // R1 = R2 >> label
label:
      nop                   // определение переменной label

Синтаксис:

MOVE Rdst, Rsrc
MOVE Rdst, imm

Операнды:

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция перемещает значение из регистра источника или из 16-битного значения со знаком в регистр назначения.

Обратите внимание, что когда метка используется для непосредственного значения, адрес метки будет преобразован из байтов в слова. Это потому, что инструкции LD, ST и JUMP ожидают значение адреса регистра, выраженное в словах, а не в байтах. Чтобы избежать этого, используйте дополнительную инструкцию.

Пример 1:

        MOVE       R1, R2            // R1 = R2

Пример 2:

        MOVE       R1, 0x03          // R1 = 0x03

Пример 3:

      .set value1, 0x03     // константа value1=0x03
      MOVE R1, value1       // R1 = value1

Пример 4:

      .global label         // декларация label
       MOVE   R1, label     // R1 = адрес(label) / 4
       ...
label:
      nop                   // определение label

Синтаксис:

ST Rsrc, Rdst, offset

Операнды:

Rsrc – R[0..3], хранит 16-битное значение для сохранения
Rdst – R[0..3], адрес назначения, в 32-битных словах
Offset – 10-битное значение со знаком, смещение в байтах

Время выполнения: 4 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция сохранит 16-битное значение Rsrc в младшую половину слова в памяти по адресу Rdst+offset. Старшая половина слова записывается текущим значением программного счетчика (PC), выраженным в словах, сдвинутым влево на 5 бит:

Mem[Rdst + offset / 4]{31:0} = {PC[10:0], 5'b0, Rsrc[15:0]}

Приложение может использовать старшие 16 бит, чтобы определить, какая инструкция в программе ULP записала какое-либо определенное слово в память.

Пример 1:

        ST  R1, R2, 0x12        // MEM[R2+0x12] = R1

Пример 2:

      .data                 // определение секции данных
Addr1:
      .word  123            // определение метки Addr1 16 бит
      .set   offs, 0x00     // определение константы смещения
      .text                 // определение секции кода
      MOVE   R1, 1          // R1 = 1
      MOVE   R2, Addr1      // R2 = Addr1
      ST     R1, R2, offs   // MEM[R2 +  0] = R1
                            // MEM[Addr1 + 0] будет 32'h600001

Синтаксис:

LD Rdst, Rsrc, offset

Операнды:

Rdst – R[0..3], место назначения
Rsrc – R[0..3], хранит адрес, выраженный в 32-битных словах
Offset – 10-битное значение со знаком, смещение в байтах

Время выполнения: 4 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция загрузит младшую 16-битную половину слова из памяти по адресу Rsrc+offset в регистр назначения Rdst:

Rdst[15:0] = Mem[Rsrc + offset / 4][15:0]

Пример 1:

        LD  R1, R2, 0x12            // R1 = MEM[R2+0x12]

Пример 2:

      .data                 // определение секции данных
Addr1:
      .word 123             // определение метки Addr1 16 бит
      .set  offs, 0x00      // определение константы смещения
      .text                 // определение секции кода
      MOVE  R1, 1           // R1 = 1
      MOVE  R2, Addr1       // R2 = Addr1 / 4 (байтовый адрес метки
                            // преобразуется в адрес слова)
      LD    R1, R2, offs    // R1 = MEM[R2 +  0]
                            // R1 будет 123

Синтаксис:

JUMP Rdst
JUMP ImmAddr
JUMP Rdst, Condition
JUMP ImmAddr, Condition

Операнды:

Rdst – R[0..3], содержащий адрес для перехода (выраженный в 32-битных словах)
ImmAddr – 13-битный адрес (выраженный в байтах), выровненный на 4 байта
Condition:
   EQ – переход, если результат операции ALU был равен 0
   OV – переход, если последняя операция ALU установила флаг переполнения

Время выполнения: 2 такта на выполнение, 2 такта для выборки следующей инструкции.

Инструкция делает переход по указанному адресу. Это может быть либо безусловный переход, либо ветвление, основанное на флагах операции ALU.

Пример 1:

      JUMP  R1              // Переход по адресу в R1 (адрес в R1
                            // выражен в 32-битных словах)

Пример 2:

      JUMP  0x120, EQ       // Переход по адресу 0x120 (значение
                            // в байтах) если результат ALU ноль

Пример 3:

      JUMP   label          // Переход по метке
       ...
label:
      nop                   // Определение метки

Пример 4:

      .global label         // Декларация глобальной метки
      MOVE    R1, label     // R1 = label (значение, загруженное в R1 выраженное в словах)
      JUMP    R1            // Переход по метке label
       ...
label:
      nop                   // Определение метки

Синтаксис:

JUMPR Step, Threshold, Condition

Операнды:

Step – относительное смещение от текущей позиции, в байтах
Threshold – значение порога для условия ветвления
Condition:
   EQ (equal) – переход, если значение в R0 равно threshold
   LT (less than) – переход, если значение в R0 меньше threshold
   LE (less or equal) – переход, если значение в R0 меньше или равно threshold
   GT (greater than) – переход, если значение в R0 больше threshold
   GE (greater or equal) – переход, если значение в R0 больше или равно threshold

Время выполнения:

Условия LT, GE, LE и GT: 2 цикла на выполнение, 2 цикла на выборку следующей инструкции.
Условия LE и GT реализованы в ассемблере с использованием инструкций JUMPR:

// JUMPR target, threshold, GT реализовано как:
         JUMPR target, threshold+1, GE
 
// JUMPR target, threshold, LE реализовано как:
         JUMPR target, threshold + 1, LT

Условие EQ реализовано в ассемблере с использованием двух инструкций JUMPR:

// JUMPR target, threshold, EQ реализовано как:
         JUMPR next, threshold + 1, GE
         JUMPR target, threshold, GE
next:

Таким образом, время выполнения будет зависеть от того, какое происходит ветвление: либо 2 такта на выполнение + 2 такта на выборку инструкции, или 4 такта на выполнение + 4 такта на выборку.

Инструкция делает переход по относительному адресу, если условие true. Условие это результат сравнения R0 и значения threshold.

Пример 1:

pos:  JUMPR 16, 20, GE      // переход по адресу (позиция + 16 байт),
                            // если значение в R0 >= 20

Пример 2:

// Цикл с декрементируемым счетчиком на основе регистра R0
      MOVE   R0, 16         // загрузка 16 в R0
label:
      SUB    R0, R0, 1      // R0--
      NOP                   // какие-либо действия
      JUMPR  label, 1, GE   // переход по метке label, если R0 >= 1

Синтаксис:

JUMPS Step, Threshold, Condition

Операнды:

Step – относительное смещение от текущей позиции, в байтах
Threshold – значение порога для условия ветвления
Condition:
   EQ (equal) – переход, если stage_cnt равно threshold
   LT (less than) – переход, если stage_cnt меньше threshold
   LE (less or equal) - переход, если stage_cnt меньше или равно threshold
   GT (greater than) – переход, если stage_cnt больше threshold
   GE (greater or equal) — переход, если stage_cnt больше или равно

Время выполнения:

Условия LE, LT, GE: 2 цикла на выполнение, 2 цикла на выборку следующей инструкции.
Условия EQ, GT реализованы в ассемблере с использованием двух инструкций JUMPS:

// JUMPS target, threshold, EQ реализован как:
         JUMPS next, threshold, LT
         JUMPS target, threshold, LE
next:
 
// JUMPS target, threshold, GT реализован как:
         JUMPS next, threshold, LE
         JUMPS target, threshold, GE
next:

Таким образом, время выполнения будет зависеть от того, какое происходит ветвление: либо 2 такта на выполнение + 2 такта на выборку инструкции, или 4 такта на выполнение + 4 такта на выборку.

Инструкция делает переход по относительному адресу, если условие true. Условие это результат сравнения регистра счетчика и значения threshold.

Пример 1:

pos:  JUMPS 16, 20, EQ  // переход на (позиция + 16 байт),
                        // если stage_cnt == 20

Пример 2:

// Цикл, основанный на инкрементируемом счетчике в регистре stage_cnt
      STAGE_RST            // stage_cnt = 0
label:
      STAGE_INC  1         // stage_cnt++
      NOP                  // какие-либо действия
      JUMPS label, 16, LT  // переход по метке label, если stage_cnt < 16

Синтаксис:

STAGE_RST

Операнды: отсутствуют.

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция установит в 0 регистр stage count.

Пример:

       STAGE_RST      // Сброс регистра stage count

Синтаксис:

STAGE_INC Value

Операнды:

Value – 8-разрядное значение

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция инкрементирует регистр stage count на указанное значение.

Пример 1:

      STAGE_INC 10         // stage_cnt += 10

Пример 2:

// Пример цикла со счетчиком, считающим вверх:
      STAGE_RST            // установка stage_cnt в 0
label:
      STAGE_INC  1         // stage_cnt++
      NOP                  // какие-либо действия
      JUMPS label, 16, LT  // переход по метке label, если stage_cnt < 16

Синтаксис:

STAGE_DEC Value

Операнды:

Value – 8-разрядное значение

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция декрементирует регистр stage count на указанное значение.

Пример 1:

      STAGE_DEC 10         // stage_cnt -= 10

Пример 2:

// Пример цикла со счетчиком, считающим вниз:
      STAGE_RST            // установка stage_cnt в 0
      STAGE_INC  16        // инкремент stage_cnt на 16
label:
      STAGE_DECC 1         // stage_cnt--
      NOP                  // какие-либо действия
      JUMPS label, 0, LT   // переход по метке label, если stage_cnt > 0

Синтаксис:

HALT

Операнды: отсутствуют.

Время выполнения: 2 такта.

Инструкция вешает сопроцессор ULP и перезапускает таймер пробуждения ULP (wake up timer), если он разрешен.

Пример:

      HALT                 // остановка сопроцессора

Синтаксис:

WAKE

Операнды: отсутствуют.

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция посылает прерывание от ULP контроллеру RTC.

Если SoC находится в режиме глубокого сна (deep sleep mode), и разрешено ULP wakeup, то это  разбудит SoC.

Если SoC не в deep sleep mode, и бит прерывания ULP (RTC_CNTL_ULP_CP_INT_ENA) установлен в регистре RTC_CNTL_INT_ENA_REG, то произойдет прерывание RTC.

Обратите внимание, что перед использованием инструкции WAKE программе ULP может понадобиться подождать, пока контроллер RTC не будет готов тому, чтобы разбудить основной процессор. Это показывается битом RTC_CNTL_RDY_FOR_WAKEUP регистра RTC_CNTL_LOW_POWER_ST_REG. Если инструкция WAKE выполняется, когда RTC_CNTL_RDY_FOR_WAKEUP равен 0, то она не не дает эффекта (пробуждение не произойдет).

Пример:

is_rdy_for_wakeup:                // Чтение бита RTC_CNTL_RDY_FOR_WAKEUP
      READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
      AND r0, r0, 1
      JUMP is_rdy_for_wakeup, eq  // Повтор, пока этот бит не установится
      WAKE                        // Запуск пробуждения
      REG_WR 0x006, 24, 24, 0     // Остановка таймера ULP (очистка
                                  // RTC_CNTL_ULP_CP_SLP_TIMER_EN)
      HALT                        // Остановка программы ULP
      // После этой инструкции SoC выйдет из сна, и ULP не заработает опять,
      // пока не будет запущен основной программой.

Синтаксис:

SLEEP sleep_reg

Операнды:

sleep_reg – 0..4, выбор одного из регистров SENS_ULP_CP_SLEEP_CYCx_REG.

Время выполнения: 2 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция выберет, значение в каком из регистров SENS_ULP_CP_SLEEP_CYCx_REG (x = 0..4) будет использоваться для периода таймера пробуждения ULP. По умолчанию используется значение из SENS_ULP_CP_SLEEP_CYC0_REG.

Пример 1:

      SLEEP 1  // Используется период, установленный в SENS_ULP_CP_SLEEP_CYC1_REG

Пример 2:

      .set sleep_reg, 4 // Определение константы
      SLEEP  sleep_reg  // Используется период, установленный в SENS_ULP_CP_SLEEP_CYC4_REG

Синтаксис:

WAIT Cycles

Операнды:

Cycles – количество тактов для ожидания.

Время выполнения: 2 + Cycles тактов на выполнение, 4 такта для выборки следующей инструкции.

Инструкция задерживает выполнение кода на указанное количество тактов.

Пример 1:

      WAIT 10           // Ничего не делать в течение 10 тактов

Пример 2:

      .set  wait_cnt, 10   // Установка константы
      WAIT  wait_cnt       // Подождать 10 тактов

Синтаксис:

TSENS Rdst, Wait_Delay

Операнды:

Rdst – регистр назначения R[0..3], куда будет сохранен результат.
Wait_Delay – количество тактов для выполнения измерения.

Время выполнения: 2 + Wait_Delay + 3 * TSENS_CLK тактов на выполнение, 4 такта для выборки следующей инструкции.

Инструкция производит измерение температуры с использованием TSENS и сохранит результат в регистр общего назначения.

Пример:

      TSENS   R1, 1000     // Измерение температуры в течение 1000 тактов
                           // и сохранение результата в R1

Синтаксис:

ADC Rdst, Sar_sel, Mux
ADC Rdst, Sar_sel, Mux, 0 — устаревшая форма

Операнды:

Rdst – регистр назначения R[0..3], куда сохраняется измерение.
Sar_sel – выбор ADC: 0 = SARADC1, 1 = SARADC2.
Mux - выбранная ножка PAD, разрешено SARADC Pad[Mux+1].

Время выполнения: 23 + max(1, SAR_AMP_WAIT1) + max(1, SAR_AMP_WAIT2) + max(1, SAR_AMP_WAIT3) + SARx_SAMPLE_CYCLE + SARx_SAMPLE_BIT тактов на выполнение, 4 такта для выборки следующей инструкции.

Инструкция делает измерение уровня напряжения на входе ADC.

Пример:

      ADC     R1, 0, 1     // изменение значения с использованием ADC1 pad 2
                           // и сохранение результата в R1

Синтаксис:

I2C_RD Sub_addr, High, Low, Slave_sel

Операнды:

Sub_addr – адрес подчиненного (slave) устройства I2C.
High, Low — определяет диапазон считываемых бит. Биты, выходящие за диапазон [High, Low], маскируются.
Slave_sel - индекс адреса I2C slave.

Время выполнения: время выполнения инструкции зависит главным образом от времени обмена I2C. 4 такта уходит на выборку следующей инструкции.

Инструкция I2C_RD считывает 1 байт из устройства I2C slave с индексом Slave_sel. Адрес (в 7-битном формате) должен быть предварительно установлен в поле регистра SENS_I2C_SLAVE_ADDRx, где x == Slave_sel. 8 бит результата чтения сохраняются в регистр R0.

Пример:

      I2C_RD 0x10, 7, 0, 0 // Чтение байта из подчиненного устройства
                           // с адресом 0x10, с индексом адреса,
                           // установленным в SENS_I2C_SLAVE_ADDR0

Синтаксис:

I2C_WR Sub_addr, Value, High, Low, Slave_sel

Операнды:

Sub_addr – адрес подчиненного (slave) устройства I2C.
Value – 8-битное значение для записи.
High, Low — определяет диапазон записываемых бит. Биты, выходящие за диапазон [High, Low], будут маскированы.
Slave_sel - индекс адреса I2C slave.

Время выполнения: время выполнения инструкции зависит главным образом от времени обмена I2C. 4 такта уходит на выборку следующей инструкции.

Инструкция I2C_WR запишет 1 байт в I2C с индексом Slave_sel. Адрес (в 7-битном формате) должен быть предварительно установлен в поле регистра SENS_I2C_SLAVE_ADDRx, где x == Slave_sel.

Пример:

      I2C_WR 0x20, 0x33, 7, 0, 1 // Запись байта 0x33 в подчиненное устройство
                                 // с адресом 0x20, с индексом адреса,
                                 // установленным в SENS_I2C_SLAVE_ADDR1.

Синтаксис:

REG_RD Addr, High, Low

Операнды:

Addr – адрес регистра в 32-битных словах.
High – номер последнего бита регистра.
Low – номер первого бита регистра.

Время выполнения: 4 такта на выполнение, 4 такта для выборки следующей инструкции.

Инструкция читает до 16 бит из регистра периферийного устройства в регистр общего назначнения R0 = REG[Addr][High:Low].

Эта инструкция может обращаться к регистрам периферийных устройств RTC_CNTL, RTC_IO, SENS и RTC_I2C. Адрес регистра, как он видится ULP, может быть вычислен из адреса того же регистра на шине DPORT следующим образом:

addr_ulp = (addr_dport - DR_REG_RTCCNTL_BASE) / 4

Пример:

      REG_RD 0x120, 7, 4   // загрузка 4 бит: R0 = {12'b0, REG[0x120][7:4]}

Синтаксис:

REG_WR Addr, High, Low, Data

Операнды:

Addr – адрес регистра в 32-битных словах.
High – номер последнего бита регистра.
Low – номер первого бита регистра.
Data – значение для записи, 8 бит.

Время выполнения: 8 тактов на выполнение, 4 такта для выборки следующей инструкции.

Инструкция запишет до 8 из непосредственного значения данных в регистр периферийного устройства REG[Addr][High:Low] = данные.

Эта инструкция может обращаться к регистрам периферийных устройств RTC_CNTL, RTC_IO, SENS и RTC_I2C. Адрес регистра, как он видится ULP, может быть вычислен из адреса того же регистра на шине DPORT следующим образом:

addr_ulp = (addr_dport - DR_REG_RTCCNTL_BASE) / 4

Пример:

      REG_WR 0x120, 7, 0, 0x10   // установка 8 бит: REG[0x120][7:0] = 0x10

[Макросы для доступа к регистрам периферийного устройства]

Перед тем, как исходный код для ULP попадет ассемблеру, он сначала проходит через препроцессор C. Это позволяет использовать определенные макросы для упрощения доступа к регистрам периферийных устройств.

Некоторые готовые макросы определены в заголовке soc/soc_ulp.h. Эти макросы позволяют получать доступ к полям регистра периферийного устройства по их именам. Имена регистров периферийных устройств, которые могут использоваться с этими макросами, определены в soc/rtc_cntl_reg.h, soc/rtc_io_reg.h, soc/sens_reg.h и soc/rtc_i2c_reg.h.

Чтение до 16 бит из rtc_reg[low_bit + bit_width - 1 : low_bit] в регистр R0. Пример:

#include "soc/soc_ulp.h"
#include "soc/rtc_cntl_reg.h"
 
/* Чтение младших 16 бит RTC_CNTL_TIME0_REG в R0 */
READ_RTC_REG(RTC_CNTL_TIME0_REG, 0, 16)

Чтение до 16 бит из поля в регистре rtc_reg и размещение результата чтения в R0. Пример:

#include "soc/soc_ulp.h"
#include "soc/sens_reg.h"
 
/* Чтение 8-битного поля SENS_TSENS_OUT из SENS_SAR_SLAVE_ADDR3_REG в R0 */
READ_RTC_FIELD(SENS_SAR_SLAVE_ADDR3_REG, SENS_TSENS_OUT)

Запись непосредственного значения в rtc_reg[low_bit + bit_width - 1 : low_bit], bit_width < = 8. Пример:

#include "soc/soc_ulp.h"
#include "soc/rtc_io_reg.h"
 
/* Установит BIT(2) RTC_GPIO_OUT_DATA_W1TS field в регистре RTC_GPIO_OUT_W1TS_REG */
WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + 2, 1, 1)

Запись непосредственного значения в поле rtc_reg, до 8 бит. Пример:

#include "soc/soc_ulp.h"
#include "soc/rtc_cntl_reg.h"
 
/* Установит поле RTC_CNTL_ULP_CP_SLP_TIMER_EN регистра RTC_CNTL_STATE0_REG в 0 */
WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)

[Ссылки]

1. ESP32 ULP coprocessor instruction set site:docs.espressif.com.
2. ESP32: программирование сопроцессора ULP.

 

Добавить комментарий


Защитный код
Обновить

Top of Page