esp32-c6 blink led baremetal riscv not run

watchdog40
Posts: 8
Joined: Fri Jun 26, 2026 2:20 pm

esp32-c6 blink led baremetal riscv not run

Postby watchdog40 » Fri Jun 26, 2026 2:33 pm

Hello.

I wrote a RISC-V assembly program for the ESP32-C6 to blink an external LED connected to pin 5.
I compiled this program using the IDF SDK with the command:
idf.py -p com14 flash monitor
and
idf.py set-target esp32-c6


The LED blinks 5 times as expected. So far, so good.

CMakeLists.txt :

Code: Select all

idf_component_register(SRCS "debledsdk.s"
                       INCLUDE_DIRS ".")
Program debledsdk.s:

Code: Select all

/*******************************************/
/* CONSTANTES                              */
/*******************************************/
.equ  LED_PIN, 5      

.equ GPIO_MATRIX,       0x60091000       
.equ GPIO_OUT_REG,      0x4
.equ GPIO_OUT_W1TS_REG, 0x8
.equ GPIO_OUT_W1TC_REG, 0xC
.equ GPIO_ENABLE_REG,   0x20
.equ GPIO_ENABLE_W1TS_REG,0x24
.equ GPIO_ENABLE_W1TC_REG,0x28
.equ GPIO_PIN_REG,     0x74  + (4*LED_PIN)
.equ GPIO_FUNCPIN_OUT_SEL_CFG_REG,  0x554 + (4*LED_PIN)
.equ GPIO_STATUS_REG,     0x44
.equ GPIO_CLOCK_GATE_REG ,0x062C

.equ IO_MUX,            0x60090000
.equ IO_MUX_GPIOPIN_REG, 0x0004 + (4 * LED_PIN)

.equ PCR,                    0x60096000       # clock, reset 
.equ PCR_SYSCLK_CONF_REG,    0x0110
.equ PCR_IOMUX_CLK_CONF_REG, 0x00EC
.equ PCR_IOMUX_CONF_REG,     0x00E8
/*******************************************/
/* DONNEES INITIALISEES                    */
/*******************************************/ 
.data    

/***********************************************/
/* Données non initialisées                    */
/***********************************************/
.bss

/*******************************************/
/*  CODE PROGRAMME                         */
/*******************************************/  
.text
.global app_main
/************************************/
/*      Main             */
/***********************************/
app_main:                       # INFO: main

    li a0,LED_PIN
    call initGpioLed
    li s0,GPIO_MATRIX
    li t0,1
    slli s1,t0,LED_PIN
    li s2,5
1:
    sw s1,GPIO_OUT_W1TS_REG(s0)        # allumage led pin 20
    li a0,100
    call attendre
    sw s1,GPIO_OUT_W1TC_REG(s0)        # extinction led pin 20
    li a0,100
    call attendre
    sw s1,GPIO_OUT_W1TS_REG(s0)        # allumage led pin 20
    li a0,100
    call attendre
    addi s2,s2,-1
    bgtz s2,1b


2:  j 2b                      # end loop

    
/************************************/
/*       init gpio               */ 
/***********************************/
/* a0 pin led */
initGpioLed:                   # INFO: initGpioLed

    addi    sp, sp, -8
    sw      ra, 0(sp)
    li    t4,GPIO_MATRIX
    li t0,1
    sll    t2,t0,a0                  # bit pin 8
    sw t2,GPIO_ENABLE_W1TC_REG(t4)
    li t0,0x280                       # 
    sw t0,GPIO_FUNCPIN_OUT_SEL_CFG_REG(t4)
    #li t0,0x1                         #   not effect
    #sw t0,GPIO_CLOCK_GATE_REG(t4)
    
    li t4,IO_MUX
    li t0,0x1902
    sw t0,IO_MUX_GPIOPIN_REG(t4)
    
    li    t4,GPIO_MATRIX
    sw t2,GPIO_ENABLE_W1TS_REG(t4)
    
100:
    lw      ra, 0(sp)
    addi    sp, sp, 8
    ret
 
/************************************/
/*       boucle attente            */
/***********************************/
/* a0 valeur en milliseconde   */
attendre:                     # INFO: attendre
    addi    sp, sp, -8        # save des registres
    sw      t0, 0(sp)
    sw      t1, 4(sp)

    li t0,19                  # approximatif 
    sll t1,a0,t0
    li t0,16
    sll a0,a0,t0
    add a0,a0,t1
1:                            # loop 
    addi a0,a0, -1            # decrement indice
    bne a0,x0,1b
    lw      t0, 0(sp)
    lw      t1, 4(sp)
    addi    sp, sp, 8         # restaur registres
    ret 
Now, I add a few instructions at the beginning of the program (magic word, initializing the stack and some PCR registers) and compile it without the SDK, using the `riscv32-esp-elf-as.exe` compiler found in the IDF tools. I then flash it to the chip using this command:
C:\Espressif\tools\python\v6.0.1\venv\Scripts\esptool --chip esp32c6 -p com14 -b 460800 write-flash --flash-mode dio --flash-freq 80m --flash-size 2MB 0x0 deblederr3.bin

Upon execution, the LED turns on but either fails to blink or blinks continuously, depending on the specific instructions I add while trying to find a solution.
I must be missing an initialization step, but I haven't found it in the documentation yet: reset? vector table? GPIO register init? clock?

Does anyone have a solution?

makefile :

Code: Select all

ARMGNU ?= C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/bin
ARMGNU1 ?=  C:\PrincipalA\Outils\tools\arm-gnu-toolchain-13.3.rel1-mingw-w64-i686-arm-none-eabi\arm-none-eabi\bin
#AOPS =   -mabi=ilp32 -march=rv32i_m_zbs_zicsr_f_zba
AOPS =  -march=rv32imac_zicsr_zifencei_zaamo_zalrsc
all : deblederr3.flash

deblederr3.o : deblederr3.s 
	$(ARMGNU)/riscv32-esp-elf-as.exe  $(AOPS) deblederr3.s -o deblederr3.o

deblederr3.bin :   deblederr3.o
	$(ARMGNU)/riscv32-esp-elf-ld.exe -T memmap.ld   deblederr3.o -o deblederr3.elf  -M >deblederr3_map.txt

	$(ARMGNU)/riscv32-esp-elf-objcopy -O binary deblederr3.elf deblederr3.bin
deblederr3.elf :   deblederr3.bin
	$(ARMGNU)/riscv32-esp-elf-objdump.exe -D deblederr3.elf > deblederr3_list.txt
deblederr3.flash : deblederr3.elf
	C:\Espressif\tools\python\v6.0.1\venv\Scripts\esptool --chip esp32c6 -p com14 -b 460800  write-flash --flash-mode dio  --flash-freq 80m --flash-size 2MB 0x0  deblederr3.bin
program deblederr3:

Code: Select all

/*******************************************/
/* CONSTANTES                              */
/*******************************************/
.equ  LED_PIN, 5      


.equ GPIO_MATRIX,       0x60091000       
.equ GPIO_OUT_REG,      0x4
.equ GPIO_OUT_W1TS_REG, 0x8
.equ GPIO_OUT_W1TC_REG, 0xC
.equ GPIO_ENABLE_REG,   0x20
.equ GPIO_ENABLE_W1TS_REG,0x24
.equ GPIO_ENABLE_W1TC_REG,0x28
.equ GPIO_PIN_REG,     0x74  + (4*LED_PIN)
.equ GPIO_FUNCPIN_OUT_SEL_CFG_REG,  0x554 + (4*LED_PIN)
.equ GPIO_STATUS_REG,     0x44
.equ GPIO_CLOCK_GATE_REG ,0x062C

.equ IO_MUX,            0x60090000
.equ IO_MUX_GPIOPIN_REG, 0x0004 + (4 * LED_PIN)

.equ PCR,                    0x60096000       # clock, reset 
.equ PCR_SYSCLK_CONF_REG,    0x0110
.equ PCR_IOMUX_CLK_CONF_REG, 0x00EC
.equ PCR_IOMUX_CONF_REG,     0x00E8

.equ RTC_WDT,      0x600B1C00   # RTC Watch Dog Timer
.equ RTC_WDT_CONFIG0_REG,       0x0
.equ RTC_WDT_CONFIG1_REG,       0x4
.equ RTC_WDT_FEED_REG, 0x0014
.equ RTC_WDT_WPROTECT_REG, 0x0018
.equ RTC_WDT_SWD_WPROTECT_REG, 0x0020
.equ RTC_WDT_SWD_CONFIG_REG, 0x001C
/*******************************************/
/* DONNEES INITIALISEES                    */
/*******************************************/ 
.data    

/***********************************************/
/* Données non initialisées                    */
/***********************************************/
.bss

/*******************************************/
/*  CODE PROGRAMME                         */
/*******************************************/  
.text
.global main
/************************************/
/*      Main             */
/***********************************/
main:                       # INFO: main
.int 0xAEDB041D, 0xAEDB041D
   li t1,1
   li t1,0
   li sp,0x4080cc00      # stack address
   li t1,PCR 
  
    li t0,0x28010200
    sw t0,PCR_SYSCLK_CONF_REG(t1)
   
    li t0,0x00700000
    sw t0,PCR_IOMUX_CLK_CONF_REG(t1)
    
    li t0,1
    sw t0,PCR_IOMUX_CONF_REG(t1)
    
    
    /*
    li t1,RTC_WDT
    li t0,0x50D83AA1
    sw t0,RTC_WDT_WPROTECT_REG(t1)
    li t0,0x50D83AA1
    sw t0,RTC_WDT_SWD_WPROTECT_REG(t1)
    li t0,0x40000000
    sw t0,RTC_WDT_SWD_CONFIG_REG(t1)
    sw x0,RTC_WDT_CONFIG0_REG(t1)
    sw x0,RTC_WDT_CONFIG1_REG(t1)
    */

    li a0,LED_PIN    
    call initGpioLed
   # j 2f                              # TODO: a supprimer
    li s0,GPIO_MATRIX
    li t0,1
    slli s1,t0,LED_PIN
    li s2,5
1:
    sw s1,GPIO_OUT_W1TC_REG(s0)        # allumage led pin 20
    li a0,100
    call attendre
    sw s1,GPIO_OUT_W1TS_REG(s0)        # extinction led pin 20
    li a0,100
    call attendre
    sw s1,GPIO_OUT_W1TC_REG(s0)        # allumage led pin 20
    li a0,100
    call attendre
    addi s2,s2,-1
    bgtz s2,1b


2:  j 2b                      # end loop

    
/************************************/
/*       init gpio               */ 
/***********************************/
/* a0 pin led */
initGpioLed:                   # INFO: initGpioLed  

    addi    sp, sp, -8
    sw      ra, 0(sp)
    li    t4,GPIO_MATRIX
    li t0,1
    sll    t2,t0,a0                  # bit pin 8
    sw t2,GPIO_ENABLE_W1TC_REG(t4)
    li t0,0x280                       # 
    sw t0,GPIO_FUNCPIN_OUT_SEL_CFG_REG(t4)
    #li t0,0x1                         #   not effect
    #sw t0,GPIO_CLOCK_GATE_REG(t4)
    
    li t4,IO_MUX
    li t0,0x1902
    sw t0,IO_MUX_GPIOPIN_REG(t4)
    
    li    t4,GPIO_MATRIX
    sw t2,GPIO_ENABLE_W1TS_REG(t4)
    
100:
    lw      ra, 0(sp)
    addi    sp, sp, 8
    ret
 
/************************************/
/*       boucle attente            */
/***********************************/
/* a0 valeur en milliseconde   */
attendre:                     # INFO: attendre
    addi    sp, sp, -8        # save des registres
    sw      t0, 0(sp)
    sw      t1, 4(sp)

    li t0,19                  # approximatif 
    sll t1,a0,t0
    li t0,16
    sll a0,a0,t0
    add a0,a0,t1
1:                            # loop 
    addi a0,a0, -1            # decrement indice
    bne a0,x0,1b
    lw      t0, 0(sp)
    lw      t1, 4(sp)
    addi    sp, sp, 8         # restaur registres
    ret 
memmap.ld :

Code: Select all

MEMORY
{
  flash     (rx)  : ORIGIN = 0x42000000, LENGTH = 2048k
  ram      (rwx)  : ORIGIN = 0x40800000, LENGTH = 0x80000
}

STACK_SIZE = 0x4000;

RAM_SIZE =  0x80000;

/* Section Definitions */
SECTIONS
{
    .flash_header : {
        KEEP(*(flash_headerH))
    }> FLASH
    .text  ORIGIN(flash) + 8 :
    {
       /*  KEEP(*(.vectors .vectors.*)) */
        *(.text*)
        *(.rodata*)
    } > flash

    . = ALIGN(4);
    _debutFlashData = . ;
    .data :
    {
      . = ALIGN(2);

      _debutRamData = . ;
      . = ALIGN(2);

        *(.data*);
    } > ram AT > flash
     /* .bss section which is used for uninitialized data */
    . = ALIGN(4); /* aligne bss pour initialisation dans pgm */
     _debutRamBss = . ;
    .bss (NOLOAD) :
    {
        *(.bss*)
        *(COMMON)
    } > ram
    _finRamBss = . ;

    .SC1 (NOLOAD):
    {
        . = ALIGN(8);
     _debutTas = . ;
    . = _debutRamData - . + RAM_SIZE - (STACK_SIZE) ;
    } > ram
    _finTas = . ;

   /* stack section */
    .stack (NOLOAD):
    {
        . = ALIGN(8);
        . = . + STACK_SIZE;
        _stack = .;
        . = ALIGN(8);
    } > ram

    _end = . ;
}

lichurbagan
Posts: 62
Joined: Thu Nov 13, 2025 3:20 pm

Re: esp32-c6 blink led baremetal riscv not run

Postby lichurbagan » Mon Jun 29, 2026 11:46 pm

I think the biggest difference is that when you build with ESP-IDF, the second-stage bootloader and startup code have already initialized a lot of the hardware before app_main() is called. When you flash your own binary at 0x0, you're effectively replacing that entire boot flow, so you become responsible for everything the ROM normally expects .... flash mapping, stack setup etc. there's a long list.

Since your GPIO code already works under ESP-IDF, I'd focus less on the LED registers and more on the startup sequence. One thing I'd also verify is whether the flash is actually mapped to 0x42000000 before your code begins executing, since the ROM and bootloader normally take care of that. If the mapping or cache configuration isn't correct, random behavior like this wouldn't be too surprising.

watchdog40
Posts: 8
Joined: Fri Jun 26, 2026 2:20 pm

Re: esp32-c6 blink led baremetal riscv not run

Postby watchdog40 » Tue Jun 30, 2026 8:23 am

Hello.
Thank you for your reply. I am well aware that I need to replace all the initializations performed by the stage 2 bootloader, and I will do so gradually as I encounter issues.

But I found the problem. I had previously tried stopping the watchdog. I added a step to stop the watchdog timer, and now everything works.
Here is the correct program:
<code>
/*******************************************/
/* CONSTANTES */
/*******************************************/
.equ LED_PIN, 5


.equ GPIO_MATRIX, 0x60091000
.equ GPIO_OUT_REG, 0x4
.equ GPIO_OUT_W1TS_REG, 0x8
.equ GPIO_OUT_W1TC_REG, 0xC
.equ GPIO_ENABLE_REG, 0x20
.equ GPIO_ENABLE_W1TS_REG,0x24
.equ GPIO_ENABLE_W1TC_REG,0x28
.equ GPIO_PIN_REG, 0x74 + (4*LED_PIN)
.equ GPIO_FUNCPIN_OUT_SEL_CFG_REG, 0x554 + (4*LED_PIN)
.equ GPIO_STATUS_REG, 0x44
.equ GPIO_CLOCK_GATE_REG ,0x062C

.equ IO_MUX, 0x60090000
.equ IO_MUX_GPIOPIN_REG, 0x0004 + (4 * LED_PIN)

.equ PCR, 0x60096000 # clock, reset
.equ PCR_SYSCLK_CONF_REG, 0x0110
.equ PCR_IOMUX_CLK_CONF_REG, 0x00EC
.equ PCR_IOMUX_CONF_REG, 0x00E8

.equ TIMG0, 0x60008000
.equ TIMG1, 0x60009000
.equ TIMG_WDTCONFIG0_REG, 0x0048
.equ TIMG_WDTWPROTECT_REG, 0x0064 # paragraphe 14.5
.equ VALUEPROCT, 0x50d83aa1

.equ RTC_WDT, 0x600B1C00 # RTC Watch Dog Timer
.equ RTC_WDT_CONFIG0_REG, 0x0
.equ RTC_WDT_CONFIG1_REG, 0x4
.equ RTC_WDT_FEED_REG, 0x0014
.equ RTC_WDT_WPROTECT_REG, 0x0018
.equ RTC_WDT_SWD_WPROTECT_REG, 0x0020
.equ RTC_WDT_SWD_CONFIG_REG, 0x001C
/*******************************************/
/* DONNEES INITIALISEES */
/*******************************************/
.data

/***********************************************/
/* Données non initialisées */
/***********************************************/
.bss

/*******************************************/
/* CODE PROGRAMME */
/*******************************************/
.text
.global main
/************************************/
/* Main */
/***********************************/
main: # INFO: main
.int 0xAEDB041D, 0xAEDB041D
#li t1,1
#li t1,0
li sp,0x4080cc00 # stack address
li t1,PCR

li t0,0x28010200
sw t0,PCR_SYSCLK_CONF_REG(t1)

li t0,0x00700000
sw t0,PCR_IOMUX_CLK_CONF_REG(t1)

li t0,1
sw t0,PCR_IOMUX_CONF_REG(t1)


li t0,TIMG0 # disable watchdog timer 0
li t1,VALUEPROCT
sw t1,TIMG_WDTWPROTECT_REG(t0)
li t1,0
sw t1,TIMG_WDTCONFIG0_REG(t0)
li t1,1
sw t1,TIMG_WDTWPROTECT_REG(t0)



li t1,RTC_WDT # disable watchdog
li t0,0x50D83AA1
sw t0,RTC_WDT_WPROTECT_REG(t1)
li t0,0x50D83AA1
sw t0,RTC_WDT_SWD_WPROTECT_REG(t1)
li t0,0x40000000
sw t0,RTC_WDT_SWD_CONFIG_REG(t1)
sw x0,RTC_WDT_CONFIG0_REG(t1)
# sw x0,RTC_WDT_CONFIG1_REG(t1)
li t0,1
sw t0,RTC_WDT_SWD_WPROTECT_REG(t1)
li a0,LED_PIN
call initGpioLed # init led gpio

li s0,GPIO_MATRIX
li t0,1
slli s1,t0,LED_PIN
li s2,5
1:
sw s1,GPIO_OUT_W1TC_REG(s0) # allumage led pin 20
li a0,100
call attendre
sw s1,GPIO_OUT_W1TS_REG(s0) # extinction led pin 20
li a0,100
call attendre
# sw s1,GPIO_OUT_W1TC_REG(s0) # allumage led pin 20
# li a0,100
# call attendre
addi s2,s2,-1
bgtz s2,1b


2: j 2b # end loop


/************************************/
/* init gpio */
/***********************************/
/* a0 pin led */
initGpioLed: # INFO: initGpioLed

addi sp, sp, -8
sw ra, 0(sp)
li t4,GPIO_MATRIX
li t0,1
sll t2,t0,a0 # bit pin 8
sw t2,GPIO_ENABLE_W1TC_REG(t4)
li t0,0x280 #
sw t0,GPIO_FUNCPIN_OUT_SEL_CFG_REG(t4)

li t4,IO_MUX
li t0,0x1902
sw t0,IO_MUX_GPIOPIN_REG(t4)

li t4,GPIO_MATRIX
sw t2,GPIO_ENABLE_W1TS_REG(t4)

100:
lw ra, 0(sp)
addi sp, sp, 8
ret

/************************************/
/* boucle attente */
/***********************************/
/* a0 valeur en milliseconde */
attendre: # INFO: attendre
addi sp, sp, -8 # save des registres
sw t0, 0(sp)
sw t1, 4(sp)

li t0,19 # approximatif
sll t1,a0,t0
li t0,16
sll a0,a0,t0
add a0,a0,t1
1: # loop
addi a0,a0, -1 # decrement indice
bne a0,x0,1b
lw t0, 0(sp)
lw t1, 4(sp)
addi sp, sp, 8 # restaur registres
ret
</code>

Who is online

Users browsing this forum: ChatGPT-User, PetalBot and 1 guest