From 7edbb3eb0f98471773798e4471758435f44b9c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Henrique=20Guard=C3=A3o=20Gandarez?= Date: Mon, 16 Mar 2026 22:18:39 -0300 Subject: [PATCH] feat: add basic ESP32-C6 processor and runtime support Add target definition, linker script, assembly startup, interrupt handling, runtime initialization, GPIO, and UART for the ESP32-C6 (RISC-V based) microcontroller. Co-Authored-By: Claude Opus 4.6 (1M context) --- GNUmakefile | 2 + builder/build.go | 2 +- builder/builder_test.go | 1 + builder/esp.go | 29 +- main.go | 3 +- src/crypto/rand/rand_baremetal.go | 2 +- src/device/esp/esp32c6.S | 66 ++ src/machine/board_esp32c6.go | 54 ++ src/machine/machine_esp32c6.go | 552 +++++++++++++ src/machine/machine_esp32xx_usb.go | 2 +- src/runtime/interrupt/interrupt_esp32c6.go | 238 ++++++ src/runtime/rand_hwrng.go | 2 +- src/runtime/rand_norng.go | 2 +- src/runtime/runtime_esp32_io.go | 21 + src/runtime/runtime_esp32c6.go | 141 ++++ src/runtime/runtime_esp32xx.go | 19 +- targets/esp32c6.json | 19 + targets/esp32c6.ld | 853 +++++++++++++++++++++ 18 files changed, 1973 insertions(+), 35 deletions(-) create mode 100644 src/device/esp/esp32c6.S create mode 100644 src/machine/board_esp32c6.go create mode 100644 src/machine/machine_esp32c6.go create mode 100644 src/runtime/interrupt/interrupt_esp32c6.go create mode 100644 src/runtime/runtime_esp32_io.go create mode 100644 src/runtime/runtime_esp32c6.go create mode 100644 targets/esp32c6.json create mode 100644 targets/esp32c6.ld diff --git a/GNUmakefile b/GNUmakefile index 2c640e403b..a895d26204 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -977,6 +977,8 @@ endif @$(MD5SUM) test.bin $(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/blinky1 @$(MD5SUM) test.bin + $(TINYGO) build -size short -o test.bin -target=esp32c6 examples/machinetest + @$(MD5SUM) test.bin $(TINYGO) build -size short -o test.bin -target=makerfabs-esp32c3spi35 examples/machinetest @$(MD5SUM) test.bin diff --git a/builder/build.go b/builder/build.go index 44f41eb233..44775b6246 100644 --- a/builder/build.go +++ b/builder/build.go @@ -1047,7 +1047,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe if err != nil { return result, err } - case "esp32", "esp32-img", "esp32c3", "esp32s3", "esp8266": + case "esp32", "esp32-img", "esp32c3", "esp32c6", "esp32s3", "esp8266": // Special format for the ESP family of chips (parsed by the ROM // bootloader). result.Binary = filepath.Join(tmpdir, "main"+outext) diff --git a/builder/builder_test.go b/builder/builder_test.go index 8e62d9183c..0e0a156d4a 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -28,6 +28,7 @@ func TestClangAttributes(t *testing.T) { "cortex-m4", "cortex-m7", "esp32c3", + "esp32c6", "esp32s3", "fe310", "gameboy-advance", diff --git a/builder/esp.go b/builder/esp.go index c866a5c316..c4fd4a9730 100644 --- a/builder/esp.go +++ b/builder/esp.go @@ -9,7 +9,6 @@ package builder import ( "bytes" - "crypto/sha256" "debug/elf" "encoding/binary" "fmt" @@ -100,12 +99,24 @@ func makeESPFirmwareImage(infile, outfile, format string) error { chip_id := map[string]uint16{ "esp32": 0x0000, "esp32c3": 0x0005, + "esp32c6": 0x000D, "esp32s3": 0x0009, }[chip] + // SPI flash speed/size byte (byte 3 of header): + // Upper nibble = flash size, lower nibble = flash frequency. + // The espflasher auto-detects and patches the flash size (upper nibble), + // but the frequency (lower nibble) must be correct per chip. + spiSpeedSize := map[string]uint8{ + "esp32": 0x1f, // 80MHz=0x0F, 2MB=0x10 + "esp32c3": 0x1f, // 80MHz=0x0F, 2MB=0x10 + "esp32c6": 0x10, // 80MHz=0x00, 2MB=0x10 (C6 uses different freq encoding) + "esp32s3": 0x1f, // 80MHz=0x0F, 2MB=0x10 + }[chip] + // Image header. switch chip { - case "esp32", "esp32c3", "esp32s3": + case "esp32", "esp32c3", "esp32c6", "esp32s3": // Header format: // https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71 // Note: not adding a SHA256 hash as the binary is modified by @@ -126,12 +137,12 @@ func makeESPFirmwareImage(infile, outfile, format string) error { }{ magic: 0xE9, segment_count: byte(len(segments)), - spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO - spi_speed_size: 0x1f, // ESP_IMAGE_SPI_SPEED_80M, ESP_IMAGE_FLASH_SIZE_2MB + spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO + spi_speed_size: spiSpeedSize, entry_addr: uint32(inf.Entry), wp_pin: 0xEE, // disable WP pin chip_id: chip_id, - hash_appended: true, // add a SHA256 hash + hash_appended: false, // disabled: espflasher patches header, invalidating the hash }) case "esp8266": // Header format: @@ -173,11 +184,9 @@ func makeESPFirmwareImage(infile, outfile, format string) error { outf.Write(make([]byte, 15-outf.Len()%16)) outf.WriteByte(checksum) - if chip != "esp8266" { - // SHA256 hash (to protect against image corruption, not for security). - hash := sha256.Sum256(outf.Bytes()) - outf.Write(hash[:]) - } + // Note: SHA256 hash intentionally omitted. espflasher patches the header + // (SPI mode/speed/size), which invalidates the hash. The ROM would report + // "SHA-256 comparison failed" and boot anyway, so it's just noise. // QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the // image to be a certain size. diff --git a/main.go b/main.go index 30985022e1..fd1deefde2 100644 --- a/main.go +++ b/main.go @@ -1046,10 +1046,9 @@ const ( ) func flashBinUsingEsp32(port, resetMode, tmppath string, options *compileopts.Options) error { - var opts *espflasher.FlasherOptions + opts := espflasher.DefaultOptions() // On Windows, we have to explicitly specify the reset mode to use USB JTAG. if runtime.GOOS == "windows" && resetMode == jtagReset { - opts = espflasher.DefaultOptions() opts.ResetMode = espflasher.ResetUSBJTAG } diff --git a/src/crypto/rand/rand_baremetal.go b/src/crypto/rand/rand_baremetal.go index b39eee17e7..d3e3dde106 100644 --- a/src/crypto/rand/rand_baremetal.go +++ b/src/crypto/rand/rand_baremetal.go @@ -1,4 +1,4 @@ -//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt) +//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32c6 || esp32s3 || tkey || (tinygo.riscv32 && virt) // If you update the above build constraint, you'll probably also need to update // src/runtime/rand_hwrng.go. diff --git a/src/device/esp/esp32c6.S b/src/device/esp/esp32c6.S new file mode 100644 index 0000000000..7afd39f681 --- /dev/null +++ b/src/device/esp/esp32c6.S @@ -0,0 +1,66 @@ +// This is a very minimal bootloader for the ESP32-C6. It only initializes the +// flash and then continues with the generic RISC-V initialization code, which +// in turn will call runtime.main. +// It is written in assembly (and not in a higher level language) to make sure +// it is entirely loaded into IRAM and doesn't accidentally call functions +// stored in IROM. +// +// The ESP32-C6 has a unified IRAM/DRAM address space at 0x40800000, and +// separate DROM (0x42800000) / IROM (0x42000000) flash-mapped regions. + +.section .init +.global call_start_cpu0 +.type call_start_cpu0,@function +call_start_cpu0: + // At this point: + // - The ROM bootloader is finished and has jumped to here. + // - We're running from IRAM: both IRAM and DRAM segments have been loaded + // by the ROM bootloader. + // - We have a usable stack (but not the one we would like to use). + // - No flash mappings (MMU) are set up yet. + + // Reset MMU, see bootloader_reset_mmu in the ESP-IDF. + call Cache_Suspend_ICache + mv s0, a0 // autoload value + call Cache_Invalidate_ICache_All + call Cache_MMU_Init + + // Set up flash mapping (both IROM and DROM). + // On ESP32-C6, Cache_Dbus_MMU_Set is replaced by Cache_MSPI_MMU_Set + // which has an extra "sensitive" parameter. + // C equivalent: + // Cache_MSPI_MMU_Set(0, 0, 0x42000000, 0, 64, 256, 0) + // Maps 16MB starting at 0x42000000, covering both IROM and DROM. + li a0, 0 // sensitive: no flash encryption + li a1, 0 // ext_ram: MMU_ACCESS_FLASH + li a2, 0x42000000 // vaddr: start of flash-mapped region + li a3, 0 // paddr: physical address in the flash chip + li a4, 64 // psize: always 64 (kilobytes) + li a5, 256 // num: pages (16MB / 64K = 256, covers IROM+DROM) + li a6, 0 // fixed + call Cache_MSPI_MMU_Set + + // Enable the flash cache. + mv a0, s0 // restore autoload value from Cache_Suspend_ICache call + call Cache_Resume_ICache + + // Jump to generic RISC-V initialization, which initializes the stack + // pointer and globals register. It should not return. + j _start + +.section .text.exception_vectors +.global _vector_table +.type _vector_table,@function + +_vector_table: + + .option push + .option norvc + + .rept 32 + j handleInterruptASM /* interrupt handler */ + .endr + + .option pop + +.size _vector_table, .-_vector_table diff --git a/src/machine/board_esp32c6.go b/src/machine/board_esp32c6.go new file mode 100644 index 0000000000..1a6692e572 --- /dev/null +++ b/src/machine/board_esp32c6.go @@ -0,0 +1,54 @@ +//go:build esp32c6 + +// This file contains the default pin mappings for the ESP32-C6-DevKitC target. + +package machine + +// Digital Pins +const ( + IO0 = GPIO0 + IO1 = GPIO1 + IO2 = GPIO2 + IO3 = GPIO3 + IO4 = GPIO4 + IO5 = GPIO5 + IO6 = GPIO6 + IO7 = GPIO7 + IO8 = GPIO8 + IO9 = GPIO9 + IO10 = GPIO10 + IO11 = GPIO11 + IO12 = GPIO12 + IO13 = GPIO13 + IO14 = GPIO14 + IO15 = GPIO15 + IO16 = GPIO16 + IO17 = GPIO17 + IO18 = GPIO18 + IO19 = GPIO19 + IO20 = GPIO20 + IO21 = GPIO21 + IO22 = GPIO22 + IO23 = GPIO23 + IO24 = GPIO24 + IO25 = GPIO25 + IO26 = GPIO26 + IO27 = GPIO27 + IO28 = GPIO28 + IO29 = GPIO29 + IO30 = GPIO30 +) + +// Built-in WS2812 (NeoPixel) addressable RGB LED on the ESP32-C6-DevKitC. +// Use tinygo.org/x/drivers/ws2812 to control it. +const ( + LED = WS2812 + WS2812 = GPIO8 + NEOPIXEL = GPIO8 +) + +// UART pins +const ( + UART_RX_PIN = GPIO17 + UART_TX_PIN = GPIO16 +) diff --git a/src/machine/machine_esp32c6.go b/src/machine/machine_esp32c6.go new file mode 100644 index 0000000000..36c2890d0a --- /dev/null +++ b/src/machine/machine_esp32c6.go @@ -0,0 +1,552 @@ +//go:build esp32c6 + +package machine + +import ( + "device/esp" + "device/riscv" + "errors" + "runtime/interrupt" + "runtime/volatile" + "sync" + "unsafe" +) + +const deviceName = esp.Device +const maxPin = 31 +const cpuInterruptFromPin = 6 + +// CPUFrequency returns the current CPU frequency of the chip. +// Currently it is a fixed frequency but it may allow changing in the future. +func CPUFrequency() uint32 { + return 160e6 // 160MHz +} + +const ( + PinOutput PinMode = iota + PinInput + PinInputPullup + PinInputPulldown + PinAnalog +) + +const ( + GPIO0 Pin = 0 + GPIO1 Pin = 1 + GPIO2 Pin = 2 + GPIO3 Pin = 3 + GPIO4 Pin = 4 + GPIO5 Pin = 5 + GPIO6 Pin = 6 + GPIO7 Pin = 7 + GPIO8 Pin = 8 + GPIO9 Pin = 9 + GPIO10 Pin = 10 + GPIO11 Pin = 11 + GPIO12 Pin = 12 + GPIO13 Pin = 13 + GPIO14 Pin = 14 + GPIO15 Pin = 15 + GPIO16 Pin = 16 + GPIO17 Pin = 17 + GPIO18 Pin = 18 + GPIO19 Pin = 19 + GPIO20 Pin = 20 + GPIO21 Pin = 21 + GPIO22 Pin = 22 + GPIO23 Pin = 23 + GPIO24 Pin = 24 + GPIO25 Pin = 25 + GPIO26 Pin = 26 + GPIO27 Pin = 27 + GPIO28 Pin = 28 + GPIO29 Pin = 29 + GPIO30 Pin = 30 +) + +type PinChange uint8 + +// Pin change interrupt constants for SetInterrupt. +const ( + PinRising PinChange = iota + 1 + PinFalling + PinToggle +) + +// Configure this pin with the given configuration. +func (p Pin) Configure(config PinConfig) { + if p == NoPin { + // This simplifies pin configuration in peripherals such as SPI. + return + } + + var muxConfig uint32 + + // Configure this pin as a GPIO pin. + const function = 1 // function 1 is GPIO for every pin + muxConfig |= function << esp.IO_MUX_GPIO_MCU_SEL_Pos + + // FUN_IE: disable for PinAnalog (high-Z for ADC) + if config.Mode != PinAnalog { + muxConfig |= esp.IO_MUX_GPIO_FUN_IE + } + + // Set drive strength: 0 is lowest, 3 is highest. + muxConfig |= 2 << esp.IO_MUX_GPIO_FUN_DRV_Pos + + // Select pull mode (no pulls for PinAnalog). + if config.Mode == PinInputPullup { + muxConfig |= esp.IO_MUX_GPIO_FUN_WPU + } else if config.Mode == PinInputPulldown { + muxConfig |= esp.IO_MUX_GPIO_FUN_WPD + } + + // Configure the pad with the given IO mux configuration. + p.mux().Set(muxConfig) + + // Set the output signal to the simple GPIO output. + p.outFunc().Set(0x80) + + switch config.Mode { + case PinOutput: + // Set the 'output enable' bit. + esp.GPIO.ENABLE_W1TS.Set(1 << p) + case PinInput, PinInputPullup, PinInputPulldown, PinAnalog: + // Clear the 'output enable' bit. + esp.GPIO.ENABLE_W1TC.Set(1 << p) + } +} + +// configure is the same as Configure, but allows setting a specific GPIO matrix signal. +func (p Pin) configure(config PinConfig, signal uint32) { + p.Configure(config) + if signal == 256 { + return + } + if config.Mode == PinOutput { + p.outFunc().Set(signal) + } else { + inFunc(signal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(p)) + } +} + +// outFunc returns the FUNCx_OUT_SEL_CFG register used for configuring the +// output function selection. +func (p Pin) outFunc() *volatile.Register32 { + return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_OUT_SEL_CFG), uintptr(p)*4)) +} + +func (p Pin) pinReg() *volatile.Register32 { + return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.PIN0)) + uintptr(p)*4))) +} + +// inFunc returns the FUNCy_IN_SEL_CFG register used for configuring the input +// function selection. +func inFunc(signal uint32) *volatile.Register32 { + return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_IN_SEL_CFG), uintptr(signal)*4)) +} + +// mux returns the I/O mux configuration register corresponding to the given +// GPIO pin. +func (p Pin) mux() *volatile.Register32 { + return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.IO_MUX.GPIO0), uintptr(p)*4)) +} + +// pin returns the PIN register corresponding to the given GPIO pin. +func (p Pin) pin() *volatile.Register32 { + return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.PIN0), uintptr(p)*4)) +} + +// Set the pin to high or low. +// Warning: only use this on an output pin! +func (p Pin) Set(value bool) { + if value { + reg, mask := p.portMaskSet() + reg.Set(mask) + } else { + reg, mask := p.portMaskClear() + reg.Set(mask) + } +} + +// Get returns the current value of a GPIO pin when configured as an input or as +// an output. +func (p Pin) Get() bool { + reg := &esp.GPIO.IN + return (reg.Get()>>p)&1 > 0 +} + +// Return the register and mask to enable a given GPIO pin. This can be used to +// implement bit-banged drivers. +// +// Warning: only use this on an output pin! +func (p Pin) PortMaskSet() (*uint32, uint32) { + reg, mask := p.portMaskSet() + return ®.Reg, mask +} + +// Return the register and mask to disable a given GPIO pin. This can be used to +// implement bit-banged drivers. +// +// Warning: only use this on an output pin! +func (p Pin) PortMaskClear() (*uint32, uint32) { + reg, mask := p.portMaskClear() + return ®.Reg, mask +} + +func (p Pin) portMaskSet() (*volatile.Register32, uint32) { + return &esp.GPIO.OUT_W1TS, 1 << p +} + +func (p Pin) portMaskClear() (*volatile.Register32, uint32) { + return &esp.GPIO.OUT_W1TC, 1 << p +} + +// SetInterrupt sets an interrupt to be executed when a particular pin changes +// state. The pin should already be configured as an input, including a pull up +// or down if no external pull is provided. +// +// You can pass a nil func to unset the pin change interrupt. If you do so, +// the change parameter is ignored and can be set to any value (such as 0). +// If the pin is already configured with a callback, you must first unset +// this pins interrupt before you can set a new callback. +func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) (err error) { + if p >= maxPin { + return ErrInvalidInputPin + } + + if callback == nil { + // Disable this pin interrupt + p.pin().ClearBits(esp.GPIO_PIN_INT_TYPE_Msk | esp.GPIO_PIN_INT_ENA_Msk) + + if pinCallbacks[p] != nil { + pinCallbacks[p] = nil + } + return nil + } + + if pinCallbacks[p] != nil { + // The pin was already configured. + // To properly re-configure a pin, unset it first and set a new + // configuration. + return ErrNoPinChangeChannel + } + pinCallbacks[p] = callback + + onceSetupPinInterrupt.Do(func() { + err = setupPinInterrupt() + }) + if err != nil { + return err + } + + p.pin().Set( + (p.pin().Get() & ^uint32(esp.GPIO_PIN_INT_TYPE_Msk|esp.GPIO_PIN_INT_ENA_Msk)) | + uint32(change)< 1 { + return errWrongStopBitSize + } + // - data length + uart.Bus.SetCONF0_BIT_NUM(uint32(dataBits - 5)) + // - stop bit + uart.Bus.SetCONF0_STOP_BIT_NUM(uint32(stopBits)) + // - parity check + switch parity { + case ParityNone: + uart.Bus.SetCONF0_PARITY_EN(0) + case ParityEven: + uart.Bus.SetCONF0_PARITY_EN(1) + uart.Bus.SetCONF0_PARITY(0) + case ParityOdd: + uart.Bus.SetCONF0_PARITY_EN(1) + uart.Bus.SetCONF0_PARITY(1) + } + return nil +} + +func initUARTClock(bus *esp.UART_Type, regs registerSet) { + // On ESP32-C6, use the PCR peripheral for clock enable/reset. + switch { + case bus == esp.UART0: + // Enable UART0 clock via PCR + esp.PCR.UART0_CONF.SetBits(1 << 0) // UART0_CLK_EN + // Reset sequence + esp.PCR.UART0_CONF.SetBits(1 << 1) // UART0_RST_EN + esp.PCR.UART0_CONF.ClearBits(1 << 1) // clear reset + // Enable UART0 function clock + esp.PCR.UART0_SCLK_CONF.SetBits(1 << 22) // UART0_SCLK_EN + // Select 80MHz PLL clock source (UART0_SCLK_SEL = 1) + esp.PCR.UART0_SCLK_CONF.Set( + (esp.PCR.UART0_SCLK_CONF.Get() & ^uint32(3<<20)) | (1 << 20)) + case bus == esp.UART1: + // Enable UART1 clock via PCR + esp.PCR.UART1_CONF.SetBits(1 << 0) // UART1_CLK_EN + // Reset sequence + esp.PCR.UART1_CONF.SetBits(1 << 1) // UART1_RST_EN + esp.PCR.UART1_CONF.ClearBits(1 << 1) // clear reset + // Enable UART1 function clock + esp.PCR.UART1_SCLK_CONF.SetBits(1 << 22) // UART1_SCLK_EN + // Select 80MHz PLL clock source (UART1_SCLK_SEL = 1) + esp.PCR.UART1_SCLK_CONF.Set( + (esp.PCR.UART1_SCLK_CONF.Get() & ^uint32(3<<20)) | (1 << 20)) + } + + bus.SetCLK_CONF_RST_CORE(1) + bus.SetCLK_CONF_RST_CORE(0) + // synchronize core register + bus.SetREG_UPDATE(0) + // wait for Core Clock to ready for configuration + for bus.GetREG_UPDATE() > 0 { + riscv.Asm("nop") + } +} + +func (uart *UART) SetBaudRate(baudRate uint32) { + // based on esp-idf + max_div := uint32((1 << 12) - 1) + sclk_div := (pplClockFreq + (max_div * baudRate) - 1) / (max_div * baudRate) + clk_div := (pplClockFreq << 4) / (baudRate * sclk_div) + uart.Bus.SetCLKDIV(clk_div >> 4) + uart.Bus.SetCLKDIV_FRAG(clk_div & 0xf) + uart.Bus.SetCLK_CONF_SCLK_DIV_NUM(sclk_div - 1) +} + +func (uart *UART) setupPins(config UARTConfig, regs registerSet) { + config.RX.Configure(PinConfig{Mode: PinInputPullup}) + config.TX.Configure(PinConfig{Mode: PinInputPullup}) + + // link TX with GPIO signal X (technical reference manual) (this is not interrupt signal!) + config.TX.outFunc().Set(regs.gpioMatrixSignal) + // link RX with GPIO signal X and route signals via GPIO matrix (GPIO_SIGn_IN_SEL 0x40) + inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX)) +} + +func (uart *UART) configureInterrupt(intrMapReg *volatile.Register32) { + // Disable all UART interrupts + uart.Bus.INT_ENA.ClearBits(0x0ffff) + + intrMapReg.Set(7) + onceUart.Do(func() { + _ = interrupt.New(7, func(i interrupt.Interrupt) { + UART0.handleInterrupt(0) + UART1.handleInterrupt(1) + }).Enable() + }) +} + +func (uart *UART) handleInterrupt(num int) { + // get interrupt status + interrutFlag := uart.Bus.INT_ST.Get() + if (interrutFlag & uartInterrupts) == 0 { + return + } + + // block UART interrupts while processing + uart.Bus.INT_ENA.ClearBits(uartInterrupts) + + if interrutFlag&esp.UART_INT_ENA_RXFIFO_FULL_INT_ENA > 0 { + for uart.Bus.GetSTATUS_RXFIFO_CNT() > 0 { + b := uart.Bus.GetFIFO_RXFIFO_RD_BYTE() + if !uart.Buffer.Put(byte(b & 0xff)) { + uart.DataOverflowDetected = true + } + } + } + if interrutFlag&esp.UART_INT_ENA_PARITY_ERR_INT_ENA > 0 { + uart.ParityErrorDetected = true + } + if 0 != interrutFlag&esp.UART_INT_ENA_FRM_ERR_INT_ENA { + uart.DataErrorDetected = true + } + if 0 != interrutFlag&esp.UART_INT_ENA_RXFIFO_OVF_INT_ENA { + uart.DataOverflowDetected = true + } + if 0 != interrutFlag&esp.UART_INT_ENA_GLITCH_DET_INT_ENA { + uart.DataErrorDetected = true + } + + // Clear the UART interrupt status + uart.Bus.INT_CLR.SetBits(interrutFlag) + uart.Bus.INT_CLR.ClearBits(interrutFlag) + // Enable interrupts + uart.Bus.INT_ENA.Set(uartInterrupts) +} + +const uart_empty_thresh_default = 10 + +func (uart *UART) enableTransmitter() { + uart.Bus.SetCONF0_TXFIFO_RST(1) + uart.Bus.SetCONF0_TXFIFO_RST(0) + // TXINFO empty threshold is when txfifo_empty_int interrupt produced after the amount of data in Tx-FIFO is less than this register value. + uart.Bus.SetCONF1_TXFIFO_EMPTY_THRHD(uart_empty_thresh_default) +} + +func (uart *UART) enableReceiver() { + uart.Bus.SetCONF0_RXFIFO_RST(1) + uart.Bus.SetCONF0_RXFIFO_RST(0) + // using value 1 so that we can start populate ring buffer with data as we get it + uart.Bus.SetCONF1_RXFIFO_FULL_THRHD(1) + // enable interrupts for: + uart.Bus.SetINT_ENA_RXFIFO_FULL_INT_ENA(1) + uart.Bus.SetINT_ENA_FRM_ERR_INT_ENA(1) + uart.Bus.SetINT_ENA_PARITY_ERR_INT_ENA(1) + uart.Bus.SetINT_ENA_GLITCH_DET_INT_ENA(1) + uart.Bus.SetINT_ENA_RXFIFO_OVF_INT_ENA(1) +} + +func (uart *UART) writeByte(b byte) error { + for (uart.Bus.STATUS.Get()&esp.UART_STATUS_TXFIFO_CNT_Msk)>>esp.UART_STATUS_TXFIFO_CNT_Pos >= 128 { + // Read UART_TXFIFO_CNT from the status register, which indicates how + // many bytes there are in the transmit buffer. Wait until there are + // less than 128 bytes in this buffer (the default buffer size). + } + uart.Bus.FIFO.Set(uint32(b)) + return nil +} + +func (uart *UART) flush() {} + +// GetRNG returns 32-bit random numbers using the ESP32-C6 true random number generator. +// Random numbers are generated based on the thermal noise in the system and the +// asynchronous clock mismatch. +func GetRNG() (ret uint32, err error) { + return esp.RNG.DATA.Get(), nil +} diff --git a/src/machine/machine_esp32xx_usb.go b/src/machine/machine_esp32xx_usb.go index a2e79dc0c1..983d48b731 100644 --- a/src/machine/machine_esp32xx_usb.go +++ b/src/machine/machine_esp32xx_usb.go @@ -1,4 +1,4 @@ -//go:build esp32s3 +//go:build esp32s3 || esp32c3 || esp32c6 package machine diff --git a/src/runtime/interrupt/interrupt_esp32c6.go b/src/runtime/interrupt/interrupt_esp32c6.go new file mode 100644 index 0000000000..c0c1505a07 --- /dev/null +++ b/src/runtime/interrupt/interrupt_esp32c6.go @@ -0,0 +1,238 @@ +//go:build esp32c6 + +package interrupt + +import ( + "device/esp" + "device/riscv" + "errors" + "runtime/volatile" + "unsafe" +) + +// Enable register CPU interrupt with interrupt.Interrupt. +// The ESP32-C6 has 31 CPU independent interrupts. +// The Interrupt.New(x, f) (x = [1..31]) attaches CPU interrupt to function f. +// Caller must map the selected interrupt using following sequence (for example using id 5): +// +// // map interrupt 5 to my XXXX module +// esp.INTERRUPT_CORE0.XXXX_INTERRUPT_PRO_MAP.Set( 5 ) +// _ = Interrupt.New(5, func(interrupt.Interrupt) { +// ... +// }).Enable() +func (i Interrupt) Enable() error { + if i.num < 1 && i.num > 31 { + return errors.New("interrupt for ESP32-C6 must be in range of 1 through 31") + } + mask := riscv.DisableInterrupts() + defer riscv.EnableInterrupts(mask) + + // enable CPU interrupt number i.num + esp.INTPRI.CPU_INT_ENABLE.SetBits(1 << i.num) + + // Set pulse interrupt type (rising edge detection) + esp.INTPRI.CPU_INT_TYPE.SetBits(1 << i.num) + + // Set default threshold to defaultThreshold + reg := (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.INTPRI.CPU_INT_PRI_0), i.num*4)) + reg.Set(defaultThreshold) + + // Reset interrupt before reenabling + esp.INTPRI.CPU_INT_CLEAR.SetBits(1 << i.num) + esp.INTPRI.CPU_INT_CLEAR.ClearBits(1 << i.num) + + // we must wait for any pending write operations to complete + riscv.Asm("fence") + return nil +} + +// Adding pseudo function calls that is replaced by the compiler with the actual +// functions registered through interrupt.New. +// +//go:linkname callHandlers runtime/interrupt.callHandlers +func callHandlers(num int) + +const ( + IRQNUM_1 = 1 + iota + IRQNUM_2 + IRQNUM_3 + IRQNUM_4 + IRQNUM_5 + IRQNUM_6 + IRQNUM_7 + IRQNUM_8 + IRQNUM_9 + IRQNUM_10 + IRQNUM_11 + IRQNUM_12 + IRQNUM_13 + IRQNUM_14 + IRQNUM_15 + IRQNUM_16 + IRQNUM_17 + IRQNUM_18 + IRQNUM_19 + IRQNUM_20 + IRQNUM_21 + IRQNUM_22 + IRQNUM_23 + IRQNUM_24 + IRQNUM_25 + IRQNUM_26 + IRQNUM_27 + IRQNUM_28 + IRQNUM_29 + IRQNUM_30 + IRQNUM_31 +) + +const ( + defaultThreshold = 5 + disableThreshold = 10 +) + +//go:inline +func callHandler(n int) { + switch n { + case IRQNUM_1: + callHandlers(IRQNUM_1) + case IRQNUM_2: + callHandlers(IRQNUM_2) + case IRQNUM_3: + callHandlers(IRQNUM_3) + case IRQNUM_4: + callHandlers(IRQNUM_4) + case IRQNUM_5: + callHandlers(IRQNUM_5) + case IRQNUM_6: + callHandlers(IRQNUM_6) + case IRQNUM_7: + callHandlers(IRQNUM_7) + case IRQNUM_8: + callHandlers(IRQNUM_8) + case IRQNUM_9: + callHandlers(IRQNUM_9) + case IRQNUM_10: + callHandlers(IRQNUM_10) + case IRQNUM_11: + callHandlers(IRQNUM_11) + case IRQNUM_12: + callHandlers(IRQNUM_12) + case IRQNUM_13: + callHandlers(IRQNUM_13) + case IRQNUM_14: + callHandlers(IRQNUM_14) + case IRQNUM_15: + callHandlers(IRQNUM_15) + case IRQNUM_16: + callHandlers(IRQNUM_16) + case IRQNUM_17: + callHandlers(IRQNUM_17) + case IRQNUM_18: + callHandlers(IRQNUM_18) + case IRQNUM_19: + callHandlers(IRQNUM_19) + case IRQNUM_20: + callHandlers(IRQNUM_20) + case IRQNUM_21: + callHandlers(IRQNUM_21) + case IRQNUM_22: + callHandlers(IRQNUM_22) + case IRQNUM_23: + callHandlers(IRQNUM_23) + case IRQNUM_24: + callHandlers(IRQNUM_24) + case IRQNUM_25: + callHandlers(IRQNUM_25) + case IRQNUM_26: + callHandlers(IRQNUM_26) + case IRQNUM_27: + callHandlers(IRQNUM_27) + case IRQNUM_28: + callHandlers(IRQNUM_28) + case IRQNUM_29: + callHandlers(IRQNUM_29) + case IRQNUM_30: + callHandlers(IRQNUM_30) + case IRQNUM_31: + callHandlers(IRQNUM_31) + } +} + +//export handleInterrupt +func handleInterrupt() { + mcause := riscv.MCAUSE.Get() + exception := mcause&(1<<31) == 0 + interruptNumber := uint32(mcause & 0x1f) + + if !exception && interruptNumber > 0 { + // save MSTATUS & MEPC, which could be overwritten by another CPU interrupt + mstatus := riscv.MSTATUS.Get() + mepc := riscv.MEPC.Get() + // Using threshold to temporary disable this interrupts. + // FYI: using CPU interrupt enable bit make runtime to loose interrupts. + reg := (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.INTPRI.CPU_INT_PRI_0), interruptNumber*4)) + thresholdSave := reg.Get() + reg.Set(disableThreshold) + riscv.Asm("fence") + + interruptBit := uint32(1 << interruptNumber) + + // reset pending status interrupt + if esp.INTPRI.CPU_INT_TYPE.Get()&interruptBit != 0 { + // this is edge type interrupt + esp.INTPRI.CPU_INT_CLEAR.SetBits(interruptBit) + esp.INTPRI.CPU_INT_CLEAR.ClearBits(interruptBit) + } else { + // this is level type interrupt + esp.INTPRI.CPU_INT_CLEAR.ClearBits(interruptBit) + } + + // enable CPU interrupts + riscv.MSTATUS.SetBits(riscv.MSTATUS_MIE) + + // Call registered interrupt handler(s) + callHandler(int(interruptNumber)) + + // disable CPU interrupts + riscv.MSTATUS.ClearBits(riscv.MSTATUS_MIE) + + // restore interrupt threshold to enable interrupt again + reg.Set(thresholdSave) + riscv.Asm("fence") + + // Zero MCAUSE so that interrupt.In() returns false once we + // return to normal (non-interrupt) code. Other RISC-V targets + // (FE310, K210) do the same. + riscv.MCAUSE.Set(0) + + // restore MSTATUS & MEPC + riscv.MSTATUS.Set(mstatus) + riscv.MEPC.Set(mepc) + + // do not enable CPU interrupts now + // the 'MRET' in src/device/riscv/handleinterrupt.S will copies the state of MPIE back into MIE, and subsequently clears MPIE. + } else { + // Topmost bit is clear, so it is an exception of some sort. + handleException(mcause) + } +} + +func handleException(mcause uintptr) { + println("*** Exception: pc:", riscv.MEPC.Get()) + println("*** Exception: code:", uint32(mcause&0x1f)) + println("*** Exception: mcause:", mcause) + switch uint32(mcause & 0x1f) { + case riscv.InstructionAccessFault: + println("*** virtual address:", riscv.MTVAL.Get()) + case riscv.IllegalInstruction: + println("*** opcode:", riscv.MTVAL.Get()) + case riscv.LoadAccessFault: + println("*** read address:", riscv.MTVAL.Get()) + case riscv.StoreOrAMOAccessFault: + println("*** write address:", riscv.MTVAL.Get()) + } + for { + riscv.Asm("wfi") + } +} diff --git a/src/runtime/rand_hwrng.go b/src/runtime/rand_hwrng.go index 5f95caf386..93bcfd0243 100644 --- a/src/runtime/rand_hwrng.go +++ b/src/runtime/rand_hwrng.go @@ -1,4 +1,4 @@ -//go:build baremetal && (nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32g0)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt) || rp2040 || rp2350) +//go:build baremetal && (nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32g0)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32c6 || esp32s3 || tkey || (tinygo.riscv32 && virt) || rp2040 || rp2350) // If you update the above build constraint, you'll probably also need to update // src/crypto/rand/rand_baremetal.go. diff --git a/src/runtime/rand_norng.go b/src/runtime/rand_norng.go index b9ab475c76..b073a8d28d 100644 --- a/src/runtime/rand_norng.go +++ b/src/runtime/rand_norng.go @@ -1,4 +1,4 @@ -//go:build baremetal && !(nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32g0)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt) || rp2040 || rp2350) +//go:build baremetal && !(nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32g0)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32c6 || esp32s3 || tkey || (tinygo.riscv32 && virt) || rp2040 || rp2350) package runtime diff --git a/src/runtime/runtime_esp32_io.go b/src/runtime/runtime_esp32_io.go new file mode 100644 index 0000000000..d7cb2e1c6d --- /dev/null +++ b/src/runtime/runtime_esp32_io.go @@ -0,0 +1,21 @@ +//go:build esp32 || esp32c3 + +package runtime + +import "machine" + +func putchar(c byte) { + machine.Serial.WriteByte(c) +} + +func getchar() byte { + for machine.Serial.Buffered() == 0 { + Gosched() + } + v, _ := machine.Serial.ReadByte() + return v +} + +func buffered() int { + return machine.Serial.Buffered() +} diff --git a/src/runtime/runtime_esp32c6.go b/src/runtime/runtime_esp32c6.go new file mode 100644 index 0000000000..a2cbd5dc3b --- /dev/null +++ b/src/runtime/runtime_esp32c6.go @@ -0,0 +1,141 @@ +//go:build esp32c6 + +package runtime + +import ( + "device/esp" + "device/riscv" + "machine" + "runtime/volatile" + "unsafe" +) + +// This is the function called on startup after the flash (IROM/DROM) is +// initialized and the stack pointer has been set. +// +//export main +func main() { + // This initialization configures the following things: + // * It disables all watchdog timers. They might be useful at some point in + // the future, but will need integration into the scheduler. For now, + // they're all disabled. + // * It sets the CPU frequency to 160MHz, which is the maximum speed allowed + // for this CPU. Lower frequencies might be possible in the future, but + // running fast and sleeping quickly is often also a good strategy to save + // power. + + // Disable Timer Group 0 watchdog (unlock first). + esp.TIMG0.WDTWPROTECT.Set(0x50D83AA1) + esp.TIMG0.WDTCONFIG0.Set(0) + + // Disable Timer Group 1 watchdog (unlock first). + esp.TIMG1.WDTWPROTECT.Set(0x50D83AA1) + esp.TIMG1.WDTCONFIG0.Set(0) + + // Disable LP watchdog (write-protect key first). + esp.LP_WDT.WDTWPROTECT.Set(0x50D83AA1) + esp.LP_WDT.WDTCONFIG0.Set(0) + + // Disable super watchdog. + esp.LP_WDT.SWD_WPROTECT.Set(0x50D83AA1) + esp.LP_WDT.SWD_CONF.SetBits(1 << 30) // SWD_DISABLE bit + + // Change CPU frequency to 160MHz from SPLL (480MHz). + // + // Clock tree: SPLL (480MHz) → HP root → CPU / AHB / APB + // + // Set dividers BEFORE switching the clock source so the first PLL + // cycle already arrives divided: + // HP root = SPLL / (HS_DIV_NUM+1) = 480 / 3 = 160 MHz + // CPU = HP root / (CPU_HS_DIV_NUM+1) = 160 / 1 = 160 MHz + // AHB = HP root / (AHB_HS_DIV_NUM+1) = 160 / 4 = 40 MHz + // APB = AHB / (APB_HS_DIV_NUM+1) = 40 / 1 = 40 MHz + esp.PCR.CPU_FREQ_CONF.Set(0 << 8) // CPU_HS_DIV_NUM = 0 (div1) + esp.PCR.AHB_FREQ_CONF.Set(3 << 8) // AHB_HS_DIV_NUM = 3 (div4) + esp.PCR.APB_FREQ_CONF.Set(0 << 8) // APB_HS_DIV_NUM = 0 (div1) + + // Switch to PLL: SOC_CLK_SEL = 1 (SPLL), HS_DIV_NUM = 2 (div3). + esp.PCR.SYSCLK_CONF.Set(1<<16 | 2<<8) + + clearbss() + + // Configure interrupt handler + interruptInit() + + // Initialize main system timer used for time.Now. + initTimer() + + // Initialize the heap, call main.main, etc. + run() + + // Fallback: if main ever returns, hang the CPU. + exit(0) +} + +func init() { + machine.InitSerial() +} + +func abort() { + // lock up forever + for { + riscv.Asm("wfi") + } +} + +// interruptInit initialize the interrupt controller and called from runtime once. +func interruptInit() { + mie := riscv.DisableInterrupts() + + // Reset all interrupt source priorities to zero. + priReg := &esp.INTPRI.CPU_INT_PRI_1 + for i := 0; i < 31; i++ { + priReg.Set(0) + priReg = (*volatile.Register32)(unsafe.Add(unsafe.Pointer(priReg), 4)) + } + + // default threshold for interrupts is 5 + esp.INTPRI.CPU_INT_THRESH.Set(5) + + // Set the interrupt address. + // Set MODE field to 1 - a vector base address (only supported by ESP32-C6) + // Note that this address must be aligned to 256 bytes. + riscv.MTVEC.Set((uintptr(unsafe.Pointer(&_vector_table))) | 1) + + riscv.EnableInterrupts(mie) +} + +//go:extern _vector_table +var _vector_table [0]uintptr + +// Serial I/O via USB-Serial-JTAG controller. +// The ESP32-C6-DevKitC connects its USB port to the internal USB-Serial-JTAG +// peripheral, not to UART0. The ROM bootloader also uses this path, so output +// appears on the same port as boot messages. + +func putchar(c byte) { + // Wait for the USB-Serial-JTAG TX FIFO to have space. + // Use a timeout to avoid hanging if no host is reading. + for i := 0; i < 10000; i++ { + if esp.USB_DEVICE.EP1_CONF.Get()&0x2 != 0 { // SERIAL_IN_EP_DATA_FREE + break + } + } + // Write byte directly to EP1 FIFO. We must NOT use the generated + // SetEP1_RDWR_BYTE accessor because it does a read-modify-write, + // and reading EP1 pops a byte from the RX FIFO. + esp.USB_DEVICE.EP1.Set(uint32(c)) + // Signal that data has been written to the FIFO. + esp.USB_DEVICE.EP1_CONF.Set(1) // WR_DONE +} + +func getchar() byte { + // Not implemented for USB-Serial-JTAG yet. + for { + riscv.Asm("wfi") + } +} + +func buffered() int { + return 0 +} diff --git a/src/runtime/runtime_esp32xx.go b/src/runtime/runtime_esp32xx.go index f1c62243f1..9a6e19cb11 100644 --- a/src/runtime/runtime_esp32xx.go +++ b/src/runtime/runtime_esp32xx.go @@ -1,10 +1,9 @@ -//go:build esp32 || esp32c3 +//go:build esp32 || esp32c3 || esp32c6 package runtime import ( "device/esp" - "machine" "unsafe" ) @@ -66,19 +65,3 @@ func sleepTicks(d timeUnit) { func exit(code int) { abort() } - -func putchar(c byte) { - machine.Serial.WriteByte(c) -} - -func getchar() byte { - for machine.Serial.Buffered() == 0 { - Gosched() - } - v, _ := machine.Serial.ReadByte() - return v -} - -func buffered() int { - return machine.Serial.Buffered() -} diff --git a/targets/esp32c6.json b/targets/esp32c6.json new file mode 100644 index 0000000000..44af0304ea --- /dev/null +++ b/targets/esp32c6.json @@ -0,0 +1,19 @@ +{ + "inherits": ["riscv32"], + "features": "+32bit,+a,+c,+m,+zaamo,+zalrsc,+zmmul,-b,-d,-e,-experimental-sdext,-experimental-sdtrig,-experimental-smctr,-experimental-ssctr,-experimental-svukte,-experimental-xqcia,-experimental-xqciac,-experimental-xqcicli,-experimental-xqcicm,-experimental-xqcics,-experimental-xqcicsr,-experimental-xqciint,-experimental-xqcilo,-experimental-xqcilsm,-experimental-xqcisls,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-experimental-zvbc32e,-experimental-zvkgs,-f,-h,-relax,-sha,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smdbltrp,-smepmp,-smmpm,-smnpm,-smrnmi,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssdbltrp,-ssnpm,-sspm,-ssqosid,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-supm,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-svvptc,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xesppie,-xmipscmove,-xmipslsp,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zabha,-zacas,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b", + "build-tags": ["esp32c6", "esp"], + "serial": "usb", + "rtlib": "compiler-rt", + "libc": "picolibc", + "linkerscript": "targets/esp32c6.ld", + "extra-files": [ + "src/device/esp/esp32c6.S" + ], + "binary-format": "esp32c6", + "flash-method": "esp32jtag", + "serial-port": ["303a:1001"], + "openocd-interface": "esp_usb_jtag", + "openocd-target": "esp32c6", + "openocd-commands": ["gdb_memory_map disable"], + "gdb": ["riscv32-esp-elf-gdb"] +} diff --git a/targets/esp32c6.ld b/targets/esp32c6.ld new file mode 100644 index 0000000000..63ad6eff86 --- /dev/null +++ b/targets/esp32c6.ld @@ -0,0 +1,853 @@ +/* Linker script for the ESP32-C6 + * + * The ESP32-C6 has a simpler memory layout than the ESP32-C3: + * - It has 512kB of HP-SRAM. Unlike the C3, IRAM and DRAM share the same + * address space at 0x40800000, so there is no need for separate DRAM/IRAM + * regions. + * - It has 16kB of LP-SRAM at 0x50000000 for low-power / RTC use. + * - DROM and IROM are in separate, non-overlapping 8MB address ranges: + * IROM at 0x42000000 and DROM at 0x42800000. + * - The MMU works in pages of 64kB, which means the bottom 16 bits of the + * address in flash and the address in DROM/IROM need to match. + * - Memory in SRAM is loaded at reset by the ROM bootloader. + * + * The firmware image segments are sorted by virtual address (by esp.go): + * 1. .data (SRAM 0x408...) - loaded by ROM bootloader + * 2. .iram (SRAM 0x408...) - loaded by ROM bootloader + * 3. .text (IROM 0x420...) - flash-mapped via MMU + * 4. .rodata (DROM 0x428...) - flash-mapped via MMU + * + * IMPORTANT: SRAM sections (.data, .iram) are defined BEFORE the dummy + * sections so that SIZEOF() references are backward (not forward). lld may + * not correctly resolve forward SIZEOF() references, which would produce + * wrong dummy sizes and corrupt the firmware image. + */ + +MEMORY +{ + /* HP-SRAM: unified IRAM/DRAM address space (512K). + * Unlike the C3, IRAM and DRAM share the same address space, so we use + * a single memory region to avoid linker overlap errors. + */ + SRAM (rwx) : ORIGIN = 0x40800000, LENGTH = 512K + + /* DROM and IROM are in separate, non-overlapping address ranges. */ + DROM (r) : ORIGIN = 0x42800000, LENGTH = 8M /* Data bus (read-only, flash-mapped) */ + IROM (rx) : ORIGIN = 0x42000000, LENGTH = 8M /* Instruction bus (flash-mapped) */ +} + +/* The entry point. It is set in the image flashed to the chip, so must be + * defined. + */ +ENTRY(call_start_cpu0) + +SECTIONS +{ + /* === SRAM sections (loaded by ROM bootloader) === */ + + /* Put the stack at the bottom of SRAM, so that the application will + * crash on stack overflow instead of silently corrupting memory. + * See: http://blog.japaric.io/stack-overflow-protection/ + */ + .stack (NOLOAD) : + { + . = ALIGN(16); + . += _stack_size; + _stack_top = .; + } >SRAM + + /* Global variables that are mutable and zero-initialized. + * These must be zeroed at startup (unlike data, which is loaded by the + * bootloader). + */ + .bss (NOLOAD) : ALIGN(4) + { + . = ALIGN (4); + _sbss = ABSOLUTE(.); + *(.sbss) + *(.bss .bss.*) + . = ALIGN (4); + _ebss = ABSOLUTE(.); + } >SRAM + + /* Mutable global variables. This data (in the SRAM segment) is initialized + * by the ROM bootloader. + */ + .data : ALIGN(4) + { + . = ALIGN (4); + _sdata = ABSOLUTE(.); + *(.sdata) + *(.data .data.*) + *(.dram*) + . = ALIGN (4); + _edata = ABSOLUTE(.); + } >SRAM + + /* Code that must run from IRAM (not flash-mapped), for example interrupt + * vectors and code that runs before the flash has been mapped. + * Since IRAM and DRAM share the same address space on ESP32-C6, this is + * placed sequentially after .data in the same SRAM region. + */ + .iram : ALIGN(4) + { + *(.init) /* call_start_cpu0 (before flash is mapped) */ + *(.iram*) /* code that must run from IRAM */ + *(.text.handleInterruptASM) /* must be in SRAM, within JAL range of _vector_table */ + . = ALIGN(256); + *(.text.exception_vectors) + . = ALIGN(4); + } > SRAM + + /* Heap starts after IRAM. */ + . = ALIGN(4); + _heap_start = ABSOLUTE(.); + _heap_end = ORIGIN(SRAM) + LENGTH(SRAM); + + /* === IROM sections (flash-mapped code) === */ + + /* Dummy section to align .text virtual address with its flash offset. + * In the firmware image, .data and .iram (SRAM) come before .text (IROM). + * SIZEOF(.data) and SIZEOF(.iram) are backward references (defined above). + * .data may be empty (esp.go skips empty sections), so use a ternary. + */ + .irom_dummy (NOLOAD) : ALIGN(0x10000) + { + . += 0x18; /* image header */ + . += (SIZEOF(.data) > 0) ? (SIZEOF(.data) + 0x8) : 0; /* data segment + header (if non-empty) */ + . += SIZEOF(.iram) + 0x8; /* iram segment + header */ + . += 0x8; /* text segment header */ + } > IROM + + /* Code stored in IROM (flash-mapped). + * This is the main program code. + */ + .text : ALIGN(4) + { + *(.text .text.*) + } > IROM + + /* === DROM sections (flash-mapped read-only data) === */ + + /* Dummy section to align .rodata virtual address with its flash offset. + * Segments in the image are sorted by address: SRAM (.data, .iram), + * IROM (.text), then DROM (.rodata). So .rodata comes last, and we must + * account for all preceding segments. + * All SIZEOF() references are backward (sections defined above). + */ + .rodata_dummy (NOLOAD): ALIGN(4) + { + . += 0x18; /* image header (24 bytes) */ + . += (SIZEOF(.data) > 0) ? (SIZEOF(.data) + 0x8) : 0; /* data segment + header (if non-empty) */ + . += SIZEOF(.iram) + 0x8; /* iram segment + header */ + . += SIZEOF(.text) + 0x8; /* text segment + header */ + . += 0x8; /* rodata segment header */ + } > DROM + + /* Constant global variables, stored in DROM. */ + .rodata : ALIGN(4) + { + *(.rodata*) + . = ALIGN (4); + } >DROM + + /DISCARD/ : + { + *(.eh_frame) /* we don't do exception handling in C */ + } + + /* For the garbage collector. */ + .tinygo_stacksizes (INFO) : + { + *(.tinygo_stacksizes) + } + .tinygo_arm_entries (INFO) : + { + *(.tinygo_arm_entries) + } +} + +/* For the GC and scheduler. */ +_globals_start = _sbss; +_globals_end = _edata; + +/* Default stack size. */ +_stack_size = 4K; + +/* ROM function addresses for ESP32-C6. + * Source: ESP-IDF components/esp_rom/esp32c6/ld/esp32c6.rom.ld + */ +PROVIDE( ets_delay_us = 0x40000040 ); + +/* Cache ROM functions */ +Cache_Get_ICache_Line_Size = 0x40000628; +Cache_Get_Mode = 0x4000062c; +Cache_Address_Through_Cache = 0x40000630; +ROM_Boot_Cache_Init = 0x40000634; +MMU_Set_Page_Mode = 0x40000638; +MMU_Get_Page_Mode = 0x4000063c; +Cache_Invalidate_ICache_Items = 0x40000640; +Cache_Op_Addr = 0x40000644; +Cache_Invalidate_Addr = 0x40000648; +Cache_Invalidate_ICache_All = 0x4000064c; +Cache_Mask_All = 0x40000650; +Cache_UnMask_Dram0 = 0x40000654; +Cache_Suspend_ICache_Autoload = 0x40000658; +Cache_Resume_ICache_Autoload = 0x4000065c; +Cache_Start_ICache_Preload = 0x40000660; +Cache_ICache_Preload_Done = 0x40000664; +Cache_End_ICache_Preload = 0x40000668; +Cache_Config_ICache_Autoload = 0x4000066c; +Cache_Enable_ICache_Autoload = 0x40000670; +Cache_Disable_ICache_Autoload = 0x40000674; +Cache_Enable_ICache_PreLock = 0x40000678; +Cache_Disable_ICache_PreLock = 0x4000067c; +Cache_Lock_ICache_Items = 0x40000680; +Cache_Unlock_ICache_Items = 0x40000684; +Cache_Lock_Addr = 0x40000688; +Cache_Unlock_Addr = 0x4000068c; +Cache_Disable_ICache = 0x40000690; +Cache_Enable_ICache = 0x40000694; +Cache_Suspend_ICache = 0x40000698; +Cache_Resume_ICache = 0x4000069c; +Cache_Freeze_ICache_Enable = 0x400006a0; +Cache_Freeze_ICache_Disable = 0x400006a4; +Cache_Set_IDROM_MMU_Size = 0x400006a8; +Cache_Get_IROM_MMU_End = 0x400006ac; +Cache_Get_DROM_MMU_End = 0x400006b0; +Cache_MMU_Init = 0x400006b4; +Cache_MSPI_MMU_Set = 0x400006b8; +Cache_Travel_Tag_Memory = 0x400006bc; +Cache_Get_Virtual_Addr = 0x400006c0; + +/* Coexist ROM functions + * Source: esp32c6.rom.coexist.ld + */ +esp_coex_rom_version_get = 0x40000afc; +coex_bt_release = 0x40000b00; +coex_bt_request = 0x40000b04; +coex_core_ble_conn_dyn_prio_get = 0x40000b08; +coex_core_pti_get = 0x40000b10; +coex_core_release = 0x40000b14; +coex_core_request = 0x40000b18; +coex_core_status_get = 0x40000b1c; +coex_event_duration_get = 0x40000b24; +coex_hw_timer_disable = 0x40000b28; +coex_hw_timer_enable = 0x40000b2c; +coex_hw_timer_set = 0x40000b30; +coex_schm_interval_set = 0x40000b34; +coex_schm_lock = 0x40000b38; +coex_schm_unlock = 0x40000b3c; +coex_wifi_release = 0x40000b44; +esp_coex_ble_conn_dynamic_prio_get = 0x40000b48; +/* Coexist ROM data */ +coex_env_ptr = 0x4087ffc4; +coex_pti_tab_ptr = 0x4087ffc0; +coex_schm_env_ptr = 0x4087ffbc; +coexist_funcs = 0x4087ffb8; +g_coa_funcs_p = 0x4087ffb4; +g_coex_param_ptr = 0x4087ffb0; + +/* net80211 ROM functions + * Source: esp32c6.rom.net80211.ld + */ +esp_net80211_rom_version_get = 0x40000b4c; +ampdu_dispatch = 0x40000b50; +ampdu_dispatch_all = 0x40000b54; +ampdu_dispatch_as_many_as_possible = 0x40000b58; +ampdu_dispatch_movement = 0x40000b5c; +ampdu_dispatch_upto = 0x40000b60; +chm_is_at_home_channel = 0x40000b64; +cnx_node_is_existing = 0x40000b68; +cnx_node_search = 0x40000b6c; +ic_ebuf_recycle_rx = 0x40000b70; +ic_ebuf_recycle_tx = 0x40000b74; +ic_reset_rx_ba = 0x40000b78; +ieee80211_align_eb = 0x40000b7c; +ieee80211_ampdu_start_age_timer = 0x40000b84; +ieee80211_is_tx_allowed = 0x40000b8c; +ieee80211_output_pending_eb = 0x40000b90; +wifi_get_macaddr = 0x40000ba0; +wifi_rf_phy_disable = 0x40000ba4; +wifi_rf_phy_enable = 0x40000ba8; +ic_ebuf_alloc = 0x40000bac; +ieee80211_copy_eb_header = 0x40000bb4; +ieee80211_recycle_cache_eb = 0x40000bb8; +ieee80211_search_node = 0x40000bbc; +ieee80211_crypto_encap = 0x40000bc0; +ieee80211_decap = 0x40000bc8; +wifi_is_started = 0x40000bcc; +ieee80211_gettid = 0x40000bd0; +/* net80211 ROM data */ +net80211_funcs = 0x4087ffac; +g_scan = 0x4087ffa8; +g_chm = 0x4087ffa4; +g_ic_ptr = 0x4087ffa0; +g_hmac_cnt_ptr = 0x4087ff9c; +g_tx_cacheq_ptr = 0x4087ff98; +s_netstack_free = 0x4087ff94; +mesh_rxcb = 0x4087ff90; +sta_rxcb = 0x4087ff8c; +g_itwt_fid = 0x4087ff88; + +/* pp ROM functions + * Source: esp32c6.rom.pp.ld + */ +esp_pp_rom_version_get = 0x40000bd8; +ppCalTxopRTSThreshold = 0x40000bdc; +RC_GetBlockAckTime = 0x40000be0; +ebuf_list_remove = 0x40000be4; +GetAccess = 0x40000bf4; +hal_mac_is_low_rate_enabled = 0x40000bf8; +hal_mac_tx_get_blockack = 0x40000bfc; +ic_get_trc = 0x40000c04; +ic_interface_enabled = 0x40000c10; +is_lmac_idle = 0x40000c14; +lmacDiscardAgedMSDU = 0x40000c1c; +lmacIsIdle = 0x40000c28; +lmacIsLongFrame = 0x40000c2c; +lmacPostTxComplete = 0x40000c34; +lmacProcessAllTxTimeout = 0x40000c38; +lmacProcessCollisions = 0x40000c3c; +lmacReachLongLimit = 0x40000c44; +lmacReachShortLimit = 0x40000c48; +lmacRecycleMPDU = 0x40000c4c; +lmacRxDone = 0x40000c50; +mac_tx_set_duration = 0x40000c60; +mac_tx_set_plcp2 = 0x40000c6c; +pm_disable_sleep_delay_timer = 0x40000c78; +pm_mac_wakeup = 0x40000c80; +pm_mac_sleep = 0x40000c84; +pm_enable_sleep_delay_timer = 0x40000c8c; +pm_local_tsf_process = 0x40000c90; +pm_is_waked = 0x40000c9c; +pm_on_data_rx = 0x40000ca8; +pm_sleep_for = 0x40000cc4; +ppAMPDU2Normal = 0x40000ccc; +ppCalFrameTimes = 0x40000cd4; +ppCalSubFrameLength = 0x40000cd8; +ppCheckTxAMPDUlength = 0x40000ce0; +ppDequeueRxq_Locked = 0x40000ce4; +ppDequeueTxQ = 0x40000ce8; +ppEmptyDelimiterLength = 0x40000cec; +ppEnqueueRxq = 0x40000cf0; +ppEnqueueTxDone = 0x40000cf4; +ppGetTxframe = 0x40000cf8; +ppProcessRxPktHdr = 0x40000d04; +ppRecordBarRRC = 0x40000d0c; +ppRecycleAmpdu = 0x40000d10; +ppRecycleRxPkt = 0x40000d14; +ppResumeTxAMPDU = 0x40000d1c; +ppSearchTxQueue = 0x40000d2c; +ppSearchTxframe = 0x40000d30; +ppSelectNextQueue = 0x40000d34; +ppSubFromAMPDU = 0x40000d38; +ppTxProtoProc = 0x40000d44; +ppTxqUpdateBitmap = 0x40000d48; +pp_hdrsize = 0x40000d50; +pp_post = 0x40000d54; +pp_process_hmac_waiting_txq = 0x40000d58; +rcGetAmpduSched = 0x40000d5c; +rcUpdateRxDone = 0x40000d60; +rc_get_trc = 0x40000d64; +rc_get_trc_by_index = 0x40000d68; +rcAmpduLowerRate = 0x40000d6c; +rcampduuprate = 0x40000d70; +rcClearCurAMPDUSched = 0x40000d74; +rcClearCurSched = 0x40000d78; +rcClearCurStat = 0x40000d7c; +rcLowerSched = 0x40000d84; +rcSetTxAmpduLimit = 0x40000d88; +rcTxUpdatePer = 0x40000d8c; +rcUpdateAckSnr = 0x40000d90; +rcUpSched = 0x40000da0; +rssi_margin = 0x40000da4; +rx11NRate2AMPDULimit = 0x40000da8; +TRC_AMPDU_PER_DOWN_THRESHOLD = 0x40000dac; +TRC_AMPDU_PER_UP_THRESHOLD = 0x40000db0; +trc_calc_duration = 0x40000db4; +trc_isTxAmpduOperational = 0x40000db8; +trc_onAmpduOp = 0x40000dbc; +TRC_PER_IS_GOOD = 0x40000dc0; +trc_SetTxAmpduState = 0x40000dc4; +trc_tid_isTxAmpduOperational = 0x40000dc8; +trcAmpduSetState = 0x40000dcc; +wDev_DiscardFrame = 0x40000dd8; +wDev_GetNoiseFloor = 0x40000ddc; +wDev_IndicateAmpdu = 0x40000de0; +wdev_mac_reg_load = 0x40000de8; +wdev_mac_reg_store = 0x40000dec; +wdev_mac_special_reg_load = 0x40000df0; +wdev_mac_special_reg_store = 0x40000df4; +wdev_mac_wakeup = 0x40000df8; +wdev_mac_sleep = 0x40000dfc; +hal_mac_is_dma_enable = 0x40000e00; +wdev_csi_len_align = 0x40000e10; +ppDequeueTxDone_Locked = 0x40000e14; +config_is_cache_tx_buf_enabled = 0x40000e20; +ppProcessWaitingQueue = 0x40000e28; +ppDisableQueue = 0x40000e2c; +pm_allow_tx = 0x40000e30; +ppProcTxCallback = 0x40000e38; +ppCalPreFecPaddingFactor = 0x40000e40; +hal_get_tsf_timer = 0x40000e4c; +ppTxPktForceWaked = 0x40000e50; +lmacProcessLongFrameSuccess = 0x40000e54; +lmacProcessShortFrameSuccess = 0x40000e58; +lmacProcessTBSuccess = 0x40000e60; +lmacProcessAckTimeout = 0x40000e68; +get_estimated_batime = 0x40000e74; +is_use_muedca = 0x40000e78; +hal_mac_clr_txq_state = 0x40000e84; +hal_mac_get_txq_complete = 0x40000e88; +ht_get_min_subframe_len = 0x40000e8c; +rx11ACRate2AMPDULimit = 0x40000e90; +pwr_hal_clear_intr_status = 0x40000e94; +pwr_hal_clear_mac_modem_beacon_miss_intr_filter = 0x40000e98; +pwr_hal_clear_mac_modem_rx_beacon_info = 0x40000e9c; +pwr_hal_clear_mac_modem_rx_beacon_miss_counter = 0x40000ea0; +pwr_hal_clear_mac_modem_rx_beacon_sleep_counter = 0x40000ea4; +pwr_hal_clear_mac_modem_state_wakeup_protect_signal = 0x40000ea8; +pwr_hal_get_intr_raw_signal = 0x40000eac; +pwr_hal_get_intr_status = 0x40000eb0; +pwr_hal_get_mac_modem_beacon_miss_limit_exceeded_status = 0x40000eb4; +pwr_hal_get_mac_modem_rx_beacon_location_state = 0x40000eb8; +pwr_hal_get_mac_modem_rx_beacon_valid_state = 0x40000ebc; +pwr_hal_get_mac_modem_state_sleep_limit_exceeded_status = 0x40000ec0; +pwr_hal_set_beacon_filter_abort_disable = 0x40000ec4; +pwr_hal_set_beacon_filter_abort_enable = 0x40000ec8; +pwr_hal_set_beacon_filter_abort_length = 0x40000ecc; +pwr_hal_set_beacon_filter_disable = 0x40000ed8; +pwr_hal_set_beacon_filter_enable = 0x40000edc; +pwr_hal_set_beacon_filter_force_dump_disable = 0x40000ee0; +pwr_hal_set_beacon_filter_force_dump_enable = 0x40000ee4; +pwr_hal_set_beacon_filter_force_dump_limit = 0x40000ee8; +pwr_hal_set_beacon_filter_force_sync_disable = 0x40000eec; +pwr_hal_set_beacon_filter_force_sync_enable = 0x40000ef0; +pwr_hal_set_beacon_filter_force_sync_limit = 0x40000ef4; +pwr_hal_set_beacon_filter_frame_crc_state = 0x40000ef8; +pwr_hal_set_beacon_filter_soc_wakeup_and_intr_disable = 0x40000efc; +pwr_hal_set_beacon_filter_soc_wakeup_and_intr_enable = 0x40000f00; +pwr_hal_set_beacon_filter_unicast_wakeup_disable = 0x40000f04; +pwr_hal_set_beacon_filter_unicast_wakeup_enable = 0x40000f08; +pwr_hal_set_lpclk_cycle_time = 0x40000f0c; +pwr_hal_set_lpclk_sync_disable = 0x40000f10; +pwr_hal_set_lpclk_sync_enable = 0x40000f14; +pwr_hal_set_mac_modem_beacon_miss_intr_disable = 0x40000f18; +pwr_hal_set_mac_modem_beacon_miss_intr_enable = 0x40000f1c; +pwr_hal_set_mac_modem_beacon_miss_limit = 0x40000f20; +pwr_hal_set_mac_modem_beacon_miss_limit_exceeded_wakeup_disable = 0x40000f24; +pwr_hal_set_mac_modem_beacon_miss_limit_exceeded_wakeup_enable = 0x40000f28; +pwr_hal_set_mac_modem_beacon_miss_timeout = 0x40000f2c; +pwr_hal_set_mac_modem_state_sleep_limit = 0x40000f30; +pwr_hal_set_mac_modem_state_sleep_limit_exceeded_wakeup_disable = 0x40000f34; +pwr_hal_set_mac_modem_state_sleep_limit_exceeded_wakeup_enable = 0x40000f38; +pwr_hal_set_mac_modem_state_wakeup_protect_disable = 0x40000f3c; +pwr_hal_set_mac_modem_state_wakeup_protect_early_time = 0x40000f40; +pwr_hal_set_mac_modem_state_wakeup_protect_enable = 0x40000f44; +pwr_hal_set_mac_modem_tbtt_auto_period_disable = 0x40000f48; +pwr_hal_set_mac_modem_tbtt_auto_period_enable = 0x40000f4c; +pwr_hal_set_mac_modem_tbtt_auto_period_interval = 0x40000f50; +pwr_hal_set_modem_state_interface = 0x40000f54; +hal_tsf_clear_soc_wakeup_request = 0x40000f58; +tsf_hal_clear_mac_modem_rf_power_state = 0x40000f5c; +tsf_hal_clear_soc_wakeup_request = 0x40000f60; +tsf_hal_get_counter_value = 0x40000f64; +tsf_hal_get_mac_modem_rf_power_state = 0x40000f68; +tsf_hal_get_tbtt_interval = 0x40000f6c; +tsf_hal_get_time = 0x40000f70; +tsf_hal_get_timer_target = 0x40000f74; +tsf_hal_is_tsf_enabled = 0x40000f78; +tsf_hal_map_tbtt_target_to_rx_frame = 0x40000f7c; +tsf_hal_map_tsf_to_bssid = 0x40000f80; +tsf_hal_set_counter_value = 0x40000f84; +tsf_hal_set_modem_wakeup_early_time = 0x40000f88; +tsf_hal_set_rx_beacon_abort_tsf_time_deviation_sync_disable = 0x40000f8c; +tsf_hal_set_rx_beacon_abort_tsf_time_deviation_sync_enable = 0x40000f90; +tsf_hal_set_rx_beacon_fail_tsf_time_deviation_sync_disable = 0x40000f94; +tsf_hal_set_rx_beacon_fail_tsf_time_deviation_sync_enable = 0x40000f98; +tsf_hal_set_rx_beacon_success_tsf_time_deviation_sync_disable = 0x40000f9c; +tsf_hal_set_rx_beacon_success_tsf_time_deviation_sync_enable = 0x40000fa0; +tsf_hal_set_tbtt_disable = 0x40000fa4; +tsf_hal_set_tbtt_early_time = 0x40000fa8; +tsf_hal_set_tbtt_enable = 0x40000fac; +tsf_hal_set_tbtt_interval = 0x40000fb0; +tsf_hal_set_tbtt_intr_disable = 0x40000fb4; +tsf_hal_set_tbtt_intr_enable = 0x40000fb8; +tsf_hal_set_tbtt_modem_wakeup_disable = 0x40000fbc; +tsf_hal_set_tbtt_modem_wakeup_enable = 0x40000fc0; +tsf_hal_set_tbtt_rf_ctrl_disable = 0x40000fc4; +tsf_hal_set_tbtt_rf_ctrl_enable = 0x40000fc8; +tsf_hal_set_tbtt_rf_ctrl_wait_cycles = 0x40000fcc; +tsf_hal_set_tbtt_soc_wakeup_disable = 0x40000fd0; +tsf_hal_set_tbtt_soc_wakeup_enable = 0x40000fd4; +tsf_hal_set_time = 0x40000fdc; +tsf_hal_set_timer_disable = 0x40000fe0; +tsf_hal_set_timer_enable = 0x40000fe4; +tsf_hal_set_timer_intr_disable = 0x40000fe8; +tsf_hal_set_timer_intr_enable = 0x40000fec; +tsf_hal_set_timer_modem_wakeup_disable = 0x40000ff0; +tsf_hal_set_timer_modem_wakeup_enable = 0x40000ff4; +tsf_hal_set_timer_rf_ctrl_disable = 0x40000ff8; +tsf_hal_set_timer_rf_ctrl_enable = 0x40000ffc; +tsf_hal_set_timer_rf_ctrl_wait_cycles = 0x40001000; +tsf_hal_set_timer_soc_wakeup_disable = 0x40001004; +tsf_hal_set_timer_soc_wakeup_enable = 0x40001008; +tsf_hal_set_timer_target = 0x4000100c; +tsf_hal_set_tsf_disable = 0x40001010; +tsf_hal_set_tsf_enable = 0x40001014; +tsf_hal_set_tsf_time_deviation = 0x40001018; +tsf_hal_set_tsf_time_deviation_sync_disable = 0x4000101c; +tsf_hal_set_tsf_time_deviation_sync_enable = 0x40001020; +tsf_hal_unmap_tbtt_target_to_rx_frame = 0x40001024; +rcGetRate = 0x4000103c; +rcGetDCMMaxRate = 0x40001040; +ppDirectRecycleAmpdu = 0x40001048; +ppAdd2AMPDUTail = 0x4000105c; +esp_test_disable_tx_statistics = 0x40001060; +esp_test_enable_tx_statistics = 0x40001064; +esp_test_clr_tx_statistics = 0x40001068; +esp_test_get_tx_statistics = 0x4000106c; +esp_test_clr_tx_tb_statistics = 0x40001070; +esp_test_get_tx_tb_statistics = 0x40001074; +test_tx_fail_statistics = 0x40001078; +esp_test_tx_enab_statistics = 0x40001088; +esp_test_tx_tb_complete = 0x4000108c; +esp_test_tx_count_retry = 0x40001090; +esp_test_tx_count_collision = 0x40001094; +esp_test_tx_count_timeout = 0x40001098; +hal_enable_tx_statistics = 0x4000109c; +test_rx_process_complete_noeb = 0x400010a0; +test_rx_process_complete_retry = 0x400010a4; +esp_test_rx_process_complete = 0x400010a8; +esp_test_clr_rx_statistics = 0x400010ac; +esp_test_get_rx_statistics = 0x400010b0; +test_free_rx_statistics = 0x400010b4; +esp_test_set_rx_error_occurs = 0x400010b8; +esp_test_get_rx_error_occurs = 0x400010bc; +esp_test_clr_rx_error_occurs = 0x400010c0; +esp_test_disable_rx_statistics = 0x400010c4; +esp_test_enable_rx_statistics = 0x400010c8; +hal_enable_rx_statistics = 0x400010cc; +get_user_num = 0x400010d0; +mumimo_spatial_cfg_get_nsts = 0x400010d4; +mumimo_spatial_cfg_get_nsts_tot = 0x400010d8; +test_mumimo_get_heltf_num = 0x400010dc; +test_mimo_update_user_info = 0x400010e0; +test_parse_rx_mu_mimo = 0x400010e4; +test_nonmimo_update_user_info = 0x400010e8; +test_parse_rx_mu_nonmimo = 0x400010ec; +esp_test_rx_parse_mu = 0x400010f0; +esp_test_get_rx_mu_statistics = 0x400010f4; +esp_test_clr_rx_mu_statistics = 0x400010f8; +esp_test_enable_rx_mu_statistics = 0x400010fc; +esp_test_disable_rx_mu_statistics = 0x40001100; +/* pp ROM data */ +our_instances_ptr = 0x4004ffe0; +pTxRx = 0x4087ff80; +lmacConfMib_ptr = 0x4087ff7c; +our_wait_eb = 0x4087ff78; +our_tx_eb = 0x4087ff74; +pp_wdev_funcs = 0x4087ff70; +g_osi_funcs_p = 0x4087ff6c; +wDevCtrl_ptr = 0x4087ff68; +g_wdev_last_desc_reset_ptr = 0x4004ffdc; +wDevMacSleep_ptr = 0x4087ff64; +g_lmac_cnt_ptr = 0x4087ff60; +our_controls_ptr = 0x4004ffd8; +pp_sig_cnt_ptr = 0x4087ff5c; +g_eb_list_desc_ptr = 0x4087ff58; +s_fragment_ptr = 0x4087ff54; +if_ctrl_ptr = 0x4087ff50; +g_intr_lock_mux = 0x4087ff4c; +g_wifi_global_lock = 0x4087ff48; +s_wifi_queue = 0x4087ff44; +pp_task_hdl = 0x4087ff40; +s_pp_task_create_sem = 0x4087ff3c; +s_pp_task_del_sem = 0x4087ff38; +g_wifi_menuconfig_ptr = 0x4087ff34; +xphyQueue = 0x4087ff30; +ap_no_lr_ptr = 0x4087ff2c; +rc11BSchedTbl_ptr = 0x4087ff28; +rc11NSchedTbl_ptr = 0x4087ff24; +rcLoRaSchedTbl_ptr = 0x4087ff20; +BasicOFDMSched_ptr = 0x4087ff1c; +trc_ctl_ptr = 0x4087ff18; +g_pm_cnt_ptr = 0x4087ff14; +g_pm_ptr = 0x4087ff10; +g_pm_cfg_ptr = 0x4087ff0c; +g_esp_mesh_quick_funcs_ptr = 0x4087ff08; +g_txop_queue_status_ptr = 0x4087ff04; +g_mac_sleep_en_ptr = 0x4087ff00; +g_mesh_is_root_ptr = 0x4087fefc; +g_mesh_topology_ptr = 0x4087fef8; +g_mesh_init_ps_type_ptr = 0x4087fef4; +g_mesh_is_started_ptr = 0x4087fef0; +g_config_func = 0x4087feec; +g_net80211_tx_func = 0x4087fee8; +g_timer_func = 0x4087fee4; +s_michael_mic_failure_cb = 0x4087fee0; +wifi_sta_rx_probe_req = 0x4087fedc; +g_tx_done_cb_func = 0x4087fed8; +g_per_conn_trc = 0x4087fe8c; +s_encap_amsdu_func = 0x4087fe88; +s_ht_ampdu_density_us = 0x4087fd02; +s_ht_ampdu_density = 0x4087fd01; +s_running_phy_type = 0x4087fd00; +complete_ena_tb_seqno = 0x4087fe4c; +complete_ena_tb_final = 0x4087fe48; +complete_ena_tb_count = 0x4087fe44; +g_dbg_interp_tsf = 0x4087fe3c; +g_dbg_interp_tsf_end = 0x4087fe38; +s_he_min_len_bytes = 0x4087fdf0; +s_he_dcm_min_len_bytes = 0x4087fdd0; +esp_wifi_cert_tx_mcs = 0x4087fcfc; +esp_wifi_cert_tx_bcc = 0x4087fcf8; +esp_wifi_cert_tx_nss = 0x4087fcec; +esp_test_tx_statistics_aci_bitmap = 0x4087fda4; +esp_test_tx_statistics = 0x4087fd94; +esp_test_tx_tb_statistics = 0x4087fd84; +esp_test_tx_fail_statistics = 0x4087fd24; +esp_test_rx_statistics = 0x4087fd1c; +esp_test_rx_mu_statistics = 0x4087fd18; +esp_test_mu_print_ru_allocation = 0x4087fd14; +sigb_ru_allocation_user_num = 0x4004ffc8; +sigb_common_ru_allocation = 0x4004ff38; +mu_mimo_special_cfg_user_num_2 = 0x4004fee8; +mu_mimo_special_cfg_user_num_3 = 0x4004fe80; +mu_mimo_special_cfg_user_num_4 = 0x4004fe28; +mu_mimo_special_cfg_user_num_5 = 0x4004fdf0; +mu_mimo_special_cfg_user_num_6 = 0x4004fdd0; +mu_mimo_special_cfg_user_num_7 = 0x4004fdc0; +mu_mimo_special_cfg_user_num_8 = 0x4004fdb8; +esp_test_rx_error_occurs = 0x4087fd10; +he_max_apep_length = 0x4004fd40; + +/* PHY ROM functions + * Source: esp32c6.rom.phy.ld + */ +phy_param_addr = 0x40001104; +phy_get_romfuncs = 0x40001108; +chip761_phyrom_version = 0x4000110c; +chip761_phyrom_version_num = 0x40001110; +get_rc_dout = 0x40001114; +rc_cal = 0x40001118; +rom_enter_critical_phy = 0x4000111c; +rom_exit_critical_phy = 0x40001120; +rom_set_chan_cal_interp = 0x40001124; +rom_loopback_mode_en = 0x40001128; +rom_bb_bss_cbw40 = 0x4000112c; +abs_temp = 0x40001130; +get_data_sat = 0x40001134; +phy_byte_to_word = 0x40001138; +set_chan_reg = 0x4000113c; +i2c_master_reset = 0x40001140; +rom_set_chan_freq_sw_start = 0x40001144; +freq_module_resetn = 0x40001148; +freq_chan_en_sw = 0x4000114c; +write_chan_freq = 0x40001150; +get_freq_mem_param = 0x40001154; +get_freq_mem_addr = 0x40001158; +bt_txpwr_freq = 0x4000115c; +wr_rf_freq_mem = 0x40001160; +read_rf_freq_mem = 0x40001164; +freq_i2c_mem_write = 0x40001168; +freq_num_get_data = 0x4000116c; +freq_i2c_num_addr = 0x40001170; +freq_i2c_write_set = 0x40001174; +pll_dac_mem_update = 0x40001178; +pll_cap_mem_update = 0x4000117c; +get_rf_freq_cap = 0x40001180; +get_rf_freq_init = 0x40001184; +phy_en_hw_set_freq = 0x40001188; +phy_dis_hw_set_freq = 0x4000118c; +rom_pwdet_sar2_init = 0x40001190; +rom_en_pwdet = 0x40001194; +rom_get_sar_sig_ref = 0x40001198; +rom_pwdet_tone_start = 0x4000119c; +rom_pwdet_wait_idle = 0x400011a0; +rom_read_sar_dout = 0x400011a4; +get_tone_sar_dout = 0x400011a8; +get_fm_sar_dout = 0x400011ac; +txtone_linear_pwr = 0x400011b0; +linear_to_db = 0x400011b4; +get_power_db = 0x400011b8; +meas_tone_pwr_db = 0x400011bc; +pkdet_vol_start = 0x400011c0; +read_sar2_code = 0x400011c4; +get_sar2_vol = 0x400011c8; +get_pll_vol = 0x400011cc; +tx_pwctrl_bg_init = 0x400011d0; +phy_pwdet_always_en = 0x400011d4; +phy_pwdet_onetime_en = 0x400011d8; +esp_tx_state_out_rom = 0x400011dc; +ant_dft_cfg_rom = 0x400011e0; +ant_wifitx_cfg_rom = 0x400011e4; +ant_wifirx_cfg_rom = 0x400011e8; +ant_bttx_cfg_rom = 0x400011ec; +ant_btrx_cfg_rom = 0x400011f0; +phy_chan_dump_cfg_rom = 0x400011f4; +phy_enable_low_rate = 0x400011f8; +phy_disable_low_rate = 0x400011fc; +phy_is_low_rate_enabled = 0x40001200; +phy_dig_reg_backup_rom = 0x40001204; +phy_chan_filt_set_rom = 0x40001208; +phy_rx11blr_cfg = 0x4000120c; +set_cca_rom = 0x40001210; +set_rx_sense_rom = 0x40001214; +rx_gain_force_rom = 0x40001218; +rom_rfpll_set_freq = 0x4000121c; +mhz2ieee = 0x40001220; +chan_to_freq = 0x40001224; +restart_cal = 0x40001228; +write_rfpll_sdm = 0x4000122c; +wait_rfpll_cal_end = 0x40001230; +set_rf_freq_offset = 0x40001234; +set_rfpll_freq = 0x40001238; +set_channel_rfpll_freq = 0x4000123c; +rfpll_cap_correct = 0x40001240; +rfpll_cap_init_cal = 0x40001244; +write_pll_cap = 0x40001248; +read_pll_cap = 0x4000124c; +chip_v7_set_chan_ana = 0x40001250; +freq_set_reg = 0x40001254; +gen_rx_gain_table = 0x40001258; +bt_txdc_cal = 0x4000125c; +bt_txiq_cal = 0x40001260; +txiq_cal_init = 0x40001264; +txdc_cal_init = 0x40001268; +txdc_cal = 0x4000126c; +txiq_get_mis_pwr = 0x40001270; +txiq_cover = 0x40001274; +rfcal_txiq = 0x40001278; +get_power_atten = 0x4000127c; +pwdet_ref_code = 0x40001280; +pwdet_code_cal = 0x40001284; +rfcal_txcap = 0x40001288; +tx_cap_init = 0x4000128c; +rfcal_pwrctrl = 0x40001290; +tx_pwctrl_init_cal = 0x40001294; +tx_pwctrl_init = 0x40001298; +bt_tx_pwctrl_init = 0x4000129c; +rom_i2c_enter_critical = 0x400012a0; +rom_i2c_exit_critical = 0x400012a4; +rom_get_i2c_read_mask = 0x400012a8; +rom_get_i2c_mst0_mask = 0x400012ac; +rom_get_i2c_hostid = 0x400012b0; +rom_chip_i2c_readReg_org = 0x400012b4; +rom_chip_i2c_readReg = 0x400012b8; +rom_chip_i2c_writeReg = 0x400012c0; +rom_set_txcap_reg = 0x400012d0; +i2c_paral_set_mst0 = 0x400012d4; +i2c_paral_set_read = 0x400012d8; +i2c_paral_read = 0x400012dc; +i2c_paral_write = 0x400012e0; +i2c_paral_write_num = 0x400012e4; +i2c_paral_write_mask = 0x400012e8; +i2c_sar2_init_code = 0x400012ec; +rom_pbus_force_mode = 0x400012f0; +rom_pbus_rd_addr = 0x400012f4; +rom_pbus_rd_shift = 0x400012f8; +rom_pbus_force_test = 0x400012fc; +rom_pbus_rd = 0x40001300; +rom_pbus_set_rxgain = 0x40001304; +rom_pbus_xpd_rx_off = 0x40001308; +rom_pbus_xpd_rx_on = 0x4000130c; +rom_pbus_xpd_tx_off = 0x40001310; +rom_pbus_xpd_tx_on = 0x40001314; +rom_set_loopback_gain = 0x40001318; +rom_txcal_debuge_mode = 0x4000131c; +pbus_debugmode = 0x40001320; +pbus_workmode = 0x40001324; +pbus_set_dco = 0x40001328; +txcal_work_mode = 0x4000132c; +rom_start_tx_tone_step = 0x40001330; +rom_stop_tx_tone = 0x40001334; +disable_agc = 0x40001338; +enable_agc = 0x4000133c; +phy_disable_cca = 0x40001340; +phy_enable_cca = 0x40001344; +write_gain_mem = 0x40001348; +bb_bss_cbw40_dig = 0x4000134c; +cbw2040_cfg = 0x40001350; +mac_tx_chan_offset = 0x40001354; +tx_paon_set = 0x40001358; +pwdet_reg_init = 0x4000135c; +i2cmst_reg_init = 0x40001360; +bt_gain_offset = 0x40001364; +fe_reg_init = 0x40001368; +mac_enable_bb = 0x4000136c; +bb_wdg_cfg = 0x40001370; +fe_txrx_reset = 0x40001374; +set_rx_comp = 0x40001378; +agc_reg_init = 0x4000137c; +bb_reg_init = 0x40001380; +open_i2c_xpd = 0x40001384; +txiq_set_reg = 0x40001388; +rxiq_set_reg = 0x4000138c; +set_txclk_en = 0x40001390; +set_rxclk_en = 0x40001394; +bb_wdg_test_en = 0x40001398; +noise_floor_auto_set = 0x4000139c; +read_hw_noisefloor = 0x400013a0; +iq_corr_enable = 0x400013a4; +wifi_agc_sat_gain = 0x400013a8; +phy_bbpll_cal = 0x400013ac; +phy_ant_init = 0x400013b0; +phy_set_bbfreq_init = 0x400013b4; +wifi_fbw_sel = 0x400013b8; +bt_filter_reg = 0x400013bc; +phy_rx_sense_set = 0x400013c0; +tx_state_set = 0x400013c4; +phy_close_pa = 0x400013c8; +phy_freq_correct = 0x400013cc; +set_pbus_reg = 0x400013d0; +wifi_rifs_mode_en = 0x400013d4; +nrx_freq_set = 0x400013d8; +fe_adc_on = 0x400013dc; +phy_force_pwr_index = 0x400013e0; +rom_iq_est_enable = 0x400013e4; +rom_iq_est_disable = 0x400013e8; +rom_bb_gain_index = 0x400013ec; +rom_rfrx_gain_index = 0x400013f0; +dc_iq_est = 0x400013f4; +set_cal_rxdc = 0x400013f8; +rxiq_get_mis = 0x400013fc; +rxiq_cover_mg_mp = 0x40001400; +rfcal_rxiq = 0x40001404; +get_rfcal_rxiq_data = 0x40001408; +get_dco_comp = 0x4000140c; +pbus_rx_dco_cal = 0x40001410; +rxdc_est_min = 0x40001414; +pbus_rx_dco_cal_1step = 0x40001418; +set_lb_txiq = 0x4000141c; +set_rx_gain_cal_iq = 0x40001420; +set_rx_gain_cal_dc = 0x40001424; +spur_reg_write_one_tone = 0x40001428; +spur_cal = 0x4000142c; +spur_coef_cfg = 0x40001430; +tsens_power_up = 0x40001434; +tsens_read_init = 0x40001438; +code_to_temp = 0x4000143c; +tsens_index_to_dac = 0x40001440; +tsens_index_to_offset = 0x40001444; +tsens_dac_cal = 0x40001448; +tsens_code_read = 0x4000144c; +tsens_temp_read = 0x40001450; +temp_to_power = 0x40001454; +get_temp_init = 0x40001458; +txbbgain_to_index = 0x4000145c; +index_to_txbbgain = 0x40001460; +bt_index_to_bb = 0x40001464; +bt_bb_to_index = 0x40001468; +bt_get_tx_gain = 0x4000146c; +dig_gain_check = 0x40001470; +wifi_get_tx_gain = 0x40001474; +wifi_11g_rate_chg = 0x40001478; +bt_chan_pwr_interp = 0x4000147c; +get_rate_fcc_index = 0x40001480; +get_chan_target_power = 0x40001484; +get_tx_gain_value = 0x40001488; +wifi_get_target_power = 0x4000148c; +/* PHY ROM data */ +phy_param_rom = 0x4087fce8;