]> granicus.if.org Git - esp-idf/commitdiff
ulp: Expand ULP macro functionality
authorboarchuz <46267286+boarchuz@users.noreply.github.com>
Mon, 3 Jun 2019 09:32:38 +0000 (19:32 +1000)
committerIvan Grokhotkov <ivan@espressif.com>
Thu, 15 Aug 2019 15:34:11 +0000 (17:34 +0200)
Merges https://github.com/espressif/esp-idf/pull/3580

components/ulp/include/esp32/ulp.h
components/ulp/ulp_macro.c

index c9ca51101797bc202b6efb749f45ed8a2aafb329..d479913f96e14b79f0546f286bc878c88dd59048 100644 (file)
@@ -46,20 +46,22 @@ extern "C" {
  * @{
  */
 
-#define OPCODE_WR_REG 1         /*!< Instruction: write peripheral register (RTC_CNTL/RTC_IO/SARADC) (not implemented yet) */
+#define OPCODE_WR_REG 1         /*!< Instruction: write peripheral register (RTC_CNTL/RTC_IO/SARADC) */
 
-#define OPCODE_RD_REG 2         /*!< Instruction: read peripheral register (RTC_CNTL/RTC_IO/SARADC) (not implemented yet) */
+#define OPCODE_RD_REG 2         /*!< Instruction: read peripheral register (RTC_CNTL/RTC_IO/SARADC) */
 
 #define RD_REG_PERIPH_RTC_CNTL 0    /*!< Identifier of RTC_CNTL peripheral for RD_REG and WR_REG instructions */
 #define RD_REG_PERIPH_RTC_IO   1    /*!< Identifier of RTC_IO peripheral for RD_REG and WR_REG instructions */
 #define RD_REG_PERIPH_SENS     2    /*!< Identifier of SARADC peripheral for RD_REG and WR_REG instructions */
 #define RD_REG_PERIPH_RTC_I2C  3    /*!< Identifier of RTC_I2C peripheral for RD_REG and WR_REG instructions */
 
-#define OPCODE_I2C 3            /*!< Instruction: read/write I2C (not implemented yet) */
+#define OPCODE_I2C 3            /*!< Instruction: read/write I2C */
+#define SUB_OPCODE_I2C_RD 0     /*!< I2C read */
+#define SUB_OPCODE_I2C_WR 1     /*!< I2C write */
 
 #define OPCODE_DELAY 4          /*!< Instruction: delay (nop) for a given number of cycles */
 
-#define OPCODE_ADC 5            /*!< Instruction: SAR ADC measurement (not implemented yet) */
+#define OPCODE_ADC 5            /*!< Instruction: SAR ADC measurement */
 
 #define OPCODE_ST 6             /*!< Instruction: store indirect to RTC memory */
 #define SUB_OPCODE_ST 4         /*!< Store 32 bits, 16 MSBs contain PC, 16 LSBs contain value from source register */
@@ -67,7 +69,7 @@ extern "C" {
 #define OPCODE_ALU 7            /*!< Arithmetic instructions */
 #define SUB_OPCODE_ALU_REG 0    /*!< Arithmetic instruction, both source values are in register */
 #define SUB_OPCODE_ALU_IMM 1    /*!< Arithmetic instruction, one source value is an immediate */
-#define SUB_OPCODE_ALU_CNT 2    /*!< Arithmetic instruction between counter register and an immediate (not implemented yet)*/
+#define SUB_OPCODE_ALU_CNT 2    /*!< Arithmetic instruction, stage counter and an immediate */
 #define ALU_SEL_ADD 0           /*!< Addition */
 #define ALU_SEL_SUB 1           /*!< Subtraction */
 #define ALU_SEL_AND 2           /*!< Logical AND */
@@ -75,21 +77,29 @@ extern "C" {
 #define ALU_SEL_MOV 4           /*!< Copy value (immediate to destination register or source register to destination register */
 #define ALU_SEL_LSH 5           /*!< Shift left by given number of bits */
 #define ALU_SEL_RSH 6           /*!< Shift right by given number of bits */
+#define ALU_SEL_SINC  0         /*!< Increment the stage counter */
+#define ALU_SEL_SDEC  1         /*!< Decrement the stage counter */
+#define ALU_SEL_SRST  2         /*!< Reset the stage counter */
 
 #define OPCODE_BRANCH 8         /*!< Branch instructions */
 #define SUB_OPCODE_BX  0        /*!< Branch to absolute PC (immediate or in register) */
+#define SUB_OPCODE_BR  1        /*!< Branch to relative PC, conditional on R0 */
+#define SUB_OPCODE_BS  2        /*!< Branch to relative PC, conditional on the stage counter */
 #define BX_JUMP_TYPE_DIRECT 0   /*!< Unconditional jump */
 #define BX_JUMP_TYPE_ZERO 1     /*!< Branch if last ALU result is zero */
 #define BX_JUMP_TYPE_OVF 2      /*!< Branch if last ALU operation caused and overflow */
 #define SUB_OPCODE_B  1         /*!< Branch to a relative offset */
 #define B_CMP_L 0               /*!< Branch if R0 is less than an immediate */
 #define B_CMP_GE 1              /*!< Branch if R0 is greater than or equal to an immediate */
+#define JUMPS_LT 0              /*!< Branch if the stage counter < */
+#define JUMPS_GE 1              /*!< Branch if the stage counter >= */
+#define JUMPS_LE 2              /*!< Branch if the stage counter <= */
 
 #define OPCODE_END 9            /*!< Stop executing the program */
 #define SUB_OPCODE_END 0        /*!< Stop executing the program and optionally wake up the chip */
 #define SUB_OPCODE_SLEEP 1      /*!< Stop executing the program and run it again after selected interval */
 
-#define OPCODE_TSENS 10         /*!< Instruction: temperature sensor measurement (not implemented yet) */
+#define OPCODE_TSENS 10         /*!< Instruction: temperature sensor measurement */
 
 #define OPCODE_HALT 11          /*!< Halt the coprocessor */
 
@@ -98,6 +108,7 @@ extern "C" {
 #define OPCODE_MACRO 15         /*!< Not a real opcode. Used to identify labels and branches in the program */
 #define SUB_OPCODE_MACRO_LABEL 0    /*!< Label macro */
 #define SUB_OPCODE_MACRO_BRANCH 1   /*!< Branch macro */
+#define SUB_OPCODE_MACRO_LABELPC 2  /*!< Label pointer macro */
 /**@}*/
 
 /**@{*/
@@ -173,7 +184,17 @@ typedef union {
         uint32_t sign : 1;          /*!< Sign of target PC offset: 0: positive, 1: negative */
         uint32_t sub_opcode : 3;    /*!< Sub opcode (SUB_OPCODE_B) */
         uint32_t opcode : 4;        /*!< Opcode (OPCODE_BRANCH) */
-    } b;                            /*!< Format of BRANCH instruction (relative address) */
+    } b;                            /*!< Format of BRANCH instruction (relative address, conditional on R0) */
+
+    struct {
+        uint32_t imm : 8;           /*!< Immediate value to compare against */
+        uint32_t unused : 7;        /*!< Unused */
+        uint32_t cmp : 2;           /*!< Comparison to perform: JUMPS_LT, JUMPS_GE or JUMPS_LE */
+        uint32_t offset : 7;        /*!< Absolute value of target PC offset w.r.t. current PC, expressed in words */
+        uint32_t sign : 1;          /*!< Sign of target PC offset: 0: positive, 1: negative */
+        uint32_t sub_opcode : 3;    /*!< Sub opcode (SUB_OPCODE_BS) */
+        uint32_t opcode : 4;        /*!< Opcode (OPCODE_BRANCH) */
+    } bs;                           /*!< Format of BRANCH instruction (relative address, conditional on the stage counter) */
 
     struct {
         uint32_t dreg : 2;          /*!< Destination register */
@@ -185,6 +206,15 @@ typedef union {
         uint32_t opcode : 4;        /*!< Opcode (OPCODE_ALU) */
     } alu_reg;                      /*!< Format of ALU instruction (both sources are registers) */
 
+    struct {
+        uint32_t unused1 : 4;       /*!< Unused */
+        uint32_t imm : 8;           /*!< Immediate value of operand */
+        uint32_t unused2 : 9;       /*!< Unused */
+        uint32_t sel : 4;           /*!< Operation to perform, one of ALU_SEL_Sxxx */
+        uint32_t sub_opcode : 3;    /*!< Sub opcode (SUB_OPCODE_ALU_CNT) */
+        uint32_t opcode : 4;        /*!< Opcode (OPCODE_ALU) */
+    } alu_reg_s;                    /*!< Format of ALU instruction (stage counter and an immediate) */
+
     struct {
         uint32_t dreg : 2;          /*!< Destination register */
         uint32_t sreg : 2;          /*!< Register with operand A */
@@ -232,10 +262,10 @@ typedef union {
 
     struct {
         uint32_t i2c_addr : 8;      /*!< I2C slave address */
-        uint32_t data : 8;          /*!< Data to read or write */
-        uint32_t low_bits : 3;      /*!< TBD */
-        uint32_t high_bits : 3;     /*!< TBD */
-        uint32_t i2c_sel : 4;       /*!< TBD, select reg_i2c_slave_address[7:0] */
+        uint32_t data : 8;          /*!< 8 bits of data for write operation */
+        uint32_t low_bits : 3;      /*!< number of low bits to mask for write operation */
+        uint32_t high_bits : 3;     /*!< number of high bits to mask for write operation */
+        uint32_t i2c_sel : 4;       /*!< index of slave address register [7:0] */
         uint32_t unused : 1;        /*!< Unused */
         uint32_t rw : 1;            /*!< Write (1) or read (0) */
         uint32_t opcode : 4;        /*!< Opcode (OPCODE_I2C) */
@@ -256,11 +286,12 @@ typedef union {
     } sleep;                        /*!< Format of END instruction with sleep */
 
     struct {
+        uint32_t dreg : 2;          /*!< Destination register (for SUB_OPCODE_MACRO_LABELPC) > */
         uint32_t label : 16;        /*!< Label number */
-        uint32_t unused : 8;        /*!< Unused */
-        uint32_t sub_opcode : 4;    /*!< SUB_OPCODE_MACRO_LABEL or SUB_OPCODE_MACRO_BRANCH */
+        uint32_t unused : 6;        /*!< Unused */
+        uint32_t sub_opcode : 4;    /*!< SUB_OPCODE_MACRO_LABEL or SUB_OPCODE_MACRO_BRANCH or SUB_OPCODE_MACRO_LABELPC */
         uint32_t opcode: 4;         /*!< Opcode (OPCODE_MACRO) */
-    } macro;                        /*!< Format of tokens used by LABEL and BRANCH macros */
+    } macro;                        /*!< Format of tokens used by MACROs */
 
 } ulp_insn_t;
 
@@ -763,6 +794,7 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) {
  * below.
  */
 #define M_LABEL(label_num) { .macro = { \
+    .dreg = 0, \
     .label = label_num, \
     .unused = 0, \
     .sub_opcode = SUB_OPCODE_MACRO_LABEL, \
@@ -772,11 +804,35 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) {
  * Token macro used by M_B and M_BX macros. Not to be used directly.
  */
 #define M_BRANCH(label_num) { .macro = { \
+    .dreg = 0, \
     .label = label_num, \
     .unused = 0, \
     .sub_opcode = SUB_OPCODE_MACRO_BRANCH, \
     .opcode = OPCODE_MACRO } }
 
+/**
+ * Token macro used by M_MOVL macro. Not to be used directly.
+ */
+#define M_LABELPC(label_num) { .macro = { \
+    .dreg = 0, \
+    .label = label_num, \
+    .unused = 0, \
+    .sub_opcode = SUB_OPCODE_MACRO_LABELPC, \
+    .opcode = OPCODE_MACRO } }
+
+/**
+ * Macro: Move the program counter at the given label into the register.
+ * This address can then be used with I_BXR, I_BXZR, I_BXFR, etc.
+ *
+ * This macro generates two ulp_insn_t values separated by a comma, and should
+ * be used when defining contents of ulp_insn_t arrays. First value is not a
+ * real instruction; it is a token which is removed by ulp_process_macros_and_load
+ * function.
+ */
+#define M_MOVL(reg_dest, label_num) \
+    M_LABELPC(label_num), \
+    I_MOVI(reg_dest, 0)
+
 /**
  * Macro: branch to label label_num if R0 is less than immediate value.
  *
@@ -837,7 +893,154 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) {
     M_BRANCH(label_num), \
     I_BXFI(0)
 
+/**
+ * Increment the stage counter by immediate value
+ */
+#define I_STAGE_INC(imm_) { .alu_reg_s = { \
+    .unused1 = 0, \
+    .imm = imm_, \
+    .unused2 = 0, \
+    .sel = ALU_SEL_SINC, \
+    .sub_opcode = SUB_OPCODE_ALU_CNT, \
+    .opcode = OPCODE_ALU } }
+
+/**
+ * Decrement the stage counter by immediate value
+ */
+#define I_STAGE_DEC(imm_) { .alu_reg_s = { \
+    .unused1 = 0, \
+    .imm = imm_, \
+    .unused2 = 0, \
+    .sel = ALU_SEL_SDEC, \
+    .sub_opcode = SUB_OPCODE_ALU_CNT, \
+    .opcode = OPCODE_ALU } }
+
+/**
+ * Reset the stage counter
+ */
+#define I_STAGE_RST() { .alu_reg_s = { \
+    .unused1 = 0, \
+    .imm = 0, \
+    .unused2 = 0, \
+    .sel = ALU_SEL_SRST, \
+    .sub_opcode = SUB_OPCODE_ALU_CNT, \
+    .opcode = OPCODE_ALU } }
+
+/**
+ * Macro: branch to label if the stage counter is less than immediate value
+ *
+ * This macro generates two ulp_insn_t values separated by a comma, and should
+ * be used when defining contents of ulp_insn_t arrays. First value is not a
+ * real instruction; it is a token which is removed by ulp_process_macros_and_load
+ * function.
+ */
+#define M_BSLT(label_num, imm_value) \
+    M_BRANCH(label_num), \
+    I_JUMPS(0, imm_value, JUMPS_LT)
+
+/**
+ * Macro: branch to label if the stage counter is greater than or equal to immediate value
+ *
+ * This macro generates two ulp_insn_t values separated by a comma, and should
+ * be used when defining contents of ulp_insn_t arrays. First value is not a
+ * real instruction; it is a token which is removed by ulp_process_macros_and_load
+ * function.
+ */
+#define M_BSGE(label_num, imm_value) \
+    M_BRANCH(label_num), \
+    I_JUMPS(0, imm_value, JUMPS_GE)
 
+/**
+ * Macro: branch to label if the stage counter is less than or equal to immediate value
+ *
+ * This macro generates two ulp_insn_t values separated by a comma, and should
+ * be used when defining contents of ulp_insn_t arrays. First value is not a
+ * real instruction; it is a token which is removed by ulp_process_macros_and_load
+ * function.
+ */
+#define M_BSLE(label_num, imm_value) \
+    M_BRANCH(label_num), \
+    I_JUMPS(0, imm_value, JUMPS_LE)
+
+/**
+ * Macro: branch to label if the stage counter is equal to immediate value.
+ *  Implemented using two JUMPS instructions:
+ *      JUMPS next, imm_value, LT
+ *      JUMPS label_num, imm_value, LE
+ *
+ * This macro generates three ulp_insn_t values separated by commas, and should
+ * be used when defining contents of ulp_insn_t arrays. Second value is not a
+ * real instruction; it is a token which is removed by ulp_process_macros_and_load
+ * function.
+ */
+#define M_BSEQ(label_num, imm_value) \
+    I_JUMPS(2, imm_value, JUMPS_LT), \
+    M_BRANCH(label_num), \
+    I_JUMPS(0, imm_value, JUMPS_LE)
+
+/**
+ * Macro: branch to label if the stage counter is greater than immediate value.
+ *  Implemented using two instructions:
+ *      JUMPS next, imm_value, LE
+ *      JUMPS label_num, imm_value, GE
+ *
+ * This macro generates three ulp_insn_t values separated by commas, and should
+ * be used when defining contents of ulp_insn_t arrays. Second value is not a
+ * real instruction; it is a token which is removed by ulp_process_macros_and_load
+ * function.
+ */
+#define M_BSGT(label_num, imm_value) \
+    I_JUMPS(2, imm_value, JUMPS_LE), \
+    M_BRANCH(label_num), \
+    I_JUMPS(0, imm_value, JUMPS_GE)
+
+/**
+ *  Branch relative if (stage counter [comp_type] [imm_value]) evaluates to true.
+ *
+ *  pc_offset is expressed in words, and can be from -127 to 127
+ *  imm_value is an 8-bit value to compare the stage counter against
+ *  comp_type is the type of comparison to perform: JUMPS_LT (<), JUMPS_GE (>=) or JUMPS_LE (<=)
+ */
+#define I_JUMPS(pc_offset, imm_value, comp_type) { .bs = { \
+    .imm = imm_value, \
+    .unused = 0, \
+    .cmp = comp_type, \
+    .offset = abs(pc_offset), \
+    .sign = (pc_offset >= 0) ? 0 : 1, \
+    .sub_opcode = SUB_OPCODE_BS, \
+    .opcode = OPCODE_BRANCH } }
+
+/**
+ *  Perform an I2C transaction with a slave device.
+ *  I_I2C_READ and I_I2C_WRITE are provided for convenience, instead of using this directly.
+ * 
+ *  Slave address (in 7-bit format) has to be set in advance into SENS_I2C_SLAVE_ADDRx register field, where x == slave_sel. 
+ *  For read operations, 8 bits of read result is stored into R0 register.
+ *  For write operations, bits outside range [high_bit:low_bit] of val are masked.
+ */
+#define I_I2C_RW(sub_addr, val, low_bit, high_bit, slave_sel, rw_bit) { .i2c = {\
+        .i2c_addr = sub_addr, \
+        .data = val, \
+        .low_bits = low_bit, \
+        .high_bits = high_bit, \
+        .i2c_sel = slave_sel, \
+        .unused = 0, \
+        .rw = rw_bit, \
+        .opcode = OPCODE_I2C } }
+
+/**
+ * Read a byte from the sub address of an I2C slave, and store the result in R0.
+ * 
+ * Slave address (in 7-bit format) has to be set in advance into SENS_I2C_SLAVE_ADDRx register field, where x == slave_sel. 
+ */
+#define I_I2C_READ(slave_sel, sub_addr) I_I2C_RW(sub_addr, 0, 0, 0, slave_sel, SUB_OPCODE_I2C_RD)
+
+/**
+ * Write a byte to the sub address of an I2C slave.
+ * 
+ * Slave address (in 7-bit format) has to be set in advance into SENS_I2C_SLAVE_ADDRx register field, where x == slave_sel.
+ */
+#define I_I2C_WRITE(slave_sel, sub_addr, val) I_I2C_RW(sub_addr, val, 0, 7, slave_sel, SUB_OPCODE_I2C_WR)
 
 #define RTC_SLOW_MEM ((uint32_t*) 0x50000000)       /*!< RTC slow memory, 8k size */
 
@@ -917,4 +1120,4 @@ esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us);
 
 #ifdef __cplusplus
 }
-#endif
+#endif
\ No newline at end of file
index c9b6e6dcbdfbfd66149a523fe6db7bb24185ace7..619094ae531282329249896113f7d36ef736c758 100644 (file)
@@ -38,6 +38,7 @@ typedef struct {
 
 #define RELOC_TYPE_LABEL   0
 #define RELOC_TYPE_BRANCH  1
+#define RELOC_TYPE_LABELPC 2
 
 /* This record means: there is a label at address
  * insn_addr, with number label_num.
@@ -58,6 +59,16 @@ typedef struct {
     .unused = 0, \
     .type = RELOC_TYPE_BRANCH }
 
+/* This record means: there is a move instruction at insn_addr,
+ * imm needs to be changed to the program counter of the instruction
+ * at label label_num.
+ */
+#define RELOC_INFO_LABELPC(label_num, insn_addr) (reloc_info_t) { \
+    .label = label_num, \
+    .addr = insn_addr, \
+    .unused = 0, \
+    .type = RELOC_TYPE_LABELPC }
+
 /* Comparison function used to sort the relocations array */
 static int reloc_sort_func(const void* p_lhs, const void* p_rhs)
 {
@@ -110,45 +121,61 @@ static int reloc_sort_func(const void* p_lhs, const void* p_rhs)
  *    For each label number, label entry comes first
  *    because the array was sorted at the previous step.
  *    Label address is recorded, and all subsequent
- *    "branch" entries which point to the same label number
- *    are processed. For each branch entry, correct offset
- *    or absolute address is calculated, depending on branch
- *    type, and written into the appropriate field of
- *    the instruction.
+ *    entries which point to the same label number
+ *    are processed. For each entry, correct offset
+ *    or absolute address is calculated, depending on 
+ *    type and subtype, and written into the appropriate 
+ *    field of the instruction.
  *
  */
 
 static esp_err_t do_single_reloc(ulp_insn_t* program, uint32_t load_addr,
-        reloc_info_t label_info, reloc_info_t branch_info)
+        reloc_info_t label_info, reloc_info_t the_reloc)
 {
-    size_t insn_offset = branch_info.addr - load_addr;
+    size_t insn_offset = the_reloc.addr - load_addr;
     ulp_insn_t* insn = &program[insn_offset];
-    // B and BX have the same layout of opcode/sub_opcode fields,
-    // and share the same opcode
-    assert(insn->b.opcode == OPCODE_BRANCH
-            && "branch macro was applied to a non-branch instruction");
-    switch (insn->b.sub_opcode) {
-        case SUB_OPCODE_B: {
-            int32_t offset = ((int32_t) label_info.addr) - ((int32_t) branch_info.addr);
-            uint32_t abs_offset = abs(offset);
-            uint32_t sign = (offset >= 0) ? 0 : 1;
-            if (abs_offset > 127) {
-                ESP_LOGW(TAG, "target out of range: branch from %x to %x",
-                        branch_info.addr, label_info.addr);
-                return ESP_ERR_ULP_BRANCH_OUT_OF_RANGE;
+
+    switch (the_reloc.type) {
+        case RELOC_TYPE_BRANCH: {
+            // B, BS and BX have the same layout of opcode/sub_opcode fields,
+            // and share the same opcode. B and BS also have the same layout of
+            // offset and sign fields.
+            assert(insn->b.opcode == OPCODE_BRANCH
+                    && "branch macro was applied to a non-branch instruction");
+            switch (insn->b.sub_opcode) {
+                case SUB_OPCODE_B:
+                case SUB_OPCODE_BS:{
+                    int32_t offset = ((int32_t) label_info.addr) - ((int32_t) the_reloc.addr);
+                    uint32_t abs_offset = abs(offset);
+                    uint32_t sign = (offset >= 0) ? 0 : 1;
+                    if (abs_offset > 127) {
+                        ESP_LOGW(TAG, "target out of range: branch from %x to %x",
+                                the_reloc.addr, label_info.addr);
+                        return ESP_ERR_ULP_BRANCH_OUT_OF_RANGE;
+                    }
+                    insn->b.offset = abs_offset; //== insn->bs.offset = abs_offset;
+                    insn->b.sign = sign;         //== insn->bs.sign = sign;
+                    break;
+                }
+                case SUB_OPCODE_BX:{
+                    assert(insn->bx.reg == 0 &&
+                            "relocation applied to a jump with offset in register");
+                    insn->bx.addr = label_info.addr;
+                    break;
+                }
+                default:
+                    assert(false && "unexpected branch sub-opcode");
             }
-            insn->b.offset = abs_offset;
-            insn->b.sign = sign;
             break;
         }
-        case SUB_OPCODE_BX: {
-            assert(insn->bx.reg == 0 &&
-                    "relocation applied to a jump with offset in register");
-            insn->bx.addr = label_info.addr;
+        case RELOC_TYPE_LABELPC: {
+            assert((insn->alu_imm.opcode == OPCODE_ALU && insn->alu_imm.sub_opcode == SUB_OPCODE_ALU_IMM && insn->alu_imm.sel == ALU_SEL_MOV)
+                        && "pc macro was applied to an incompatible instruction");
+            insn->alu_imm.imm = label_info.addr;
             break;
         }
         default:
-            assert(false && "unexpected sub-opcode");
+            assert(false && "unknown reloc type");
     }
     return ESP_OK;
 }
@@ -199,7 +226,7 @@ esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* prog
     while (read_ptr < end) {
         ulp_insn_t r_insn = *read_ptr;
         if (r_insn.macro.opcode == OPCODE_MACRO) {
-            switch(r_insn.macro.sub_opcode) {
+            switch (r_insn.macro.sub_opcode) {
                 case SUB_OPCODE_MACRO_LABEL:
                     *cur_reloc = RELOC_INFO_LABEL(r_insn.macro.label,
                             cur_insn_addr);
@@ -208,6 +235,10 @@ esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* prog
                     *cur_reloc = RELOC_INFO_BRANCH(r_insn.macro.label,
                             cur_insn_addr);
                     break;
+                case SUB_OPCODE_MACRO_LABELPC:
+                    *cur_reloc = RELOC_INFO_LABELPC(r_insn.macro.label,
+                            cur_insn_addr);
+                    break;
                 default:
                     assert(0 && "invalid sub_opcode for macro insn");
             }