diff --git a/builder/build.go b/builder/build.go index 714a331a39..923af2dc40 100644 --- a/builder/build.go +++ b/builder/build.go @@ -189,7 +189,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe defer unlock() libcDependencies = append(libcDependencies, libcJob) libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...) - case "": + case "", "none": // no library specified, so nothing to do default: return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc) diff --git a/compileopts/config.go b/compileopts/config.go index 1920d2b9cb..05ed0aff42 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -437,7 +437,7 @@ func (c *Config) LibcCFlags() []string { ) } return cflags - case "": + case "", "none": // No libc specified, nothing to add. return nil default: diff --git a/goenv/goenv.go b/goenv/goenv.go index fe4c8bf63e..cecb5a85dd 100644 --- a/goenv/goenv.go +++ b/goenv/goenv.go @@ -151,6 +151,8 @@ func Get(name string) string { panic("could not find cache dir: " + err.Error()) } return filepath.Join(dir, "tinygo") + case "CGO_CFLAGS": + return os.Getenv("CGO_CFLAGS") case "CGO_ENABLED": // Always enable CGo. It is required by a number of targets, including // macOS and the rp2040. diff --git a/loader/loader.go b/loader/loader.go index 1ca1b6679d..c323fb5193 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -485,6 +485,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) { var initialCFlags []string initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...) initialCFlags = append(initialCFlags, "-I"+p.Dir) + initialCFlags = append(initialCFlags, goenv.Get("CGO_CFLAGS")) generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags, p.program.config.GOOS()) p.CFlags = append(initialCFlags, cflags...) p.CGoHeaders = headerCode diff --git a/src/crypto/rand/rand_espidf.go b/src/crypto/rand/rand_espidf.go new file mode 100644 index 0000000000..bf18b4aa16 --- /dev/null +++ b/src/crypto/rand/rand_espidf.go @@ -0,0 +1,22 @@ +//go:build espidf + +package rand + +import "unsafe" + +func init() { + Reader = &reader{} +} + +type reader struct{} + +//export esp_fill_random +func esp_fill_random(buf unsafe.Pointer, len uintptr) + +func (r *reader) Read(b []byte) (n int, err error) { + if len(b) == 0 { + return + } + esp_fill_random(unsafe.Pointer(&b[0]), uintptr(len(b))) + return len(b), nil +} diff --git a/src/device/esp/esp32s3-stack.S b/src/device/esp/esp32s3-stack.S new file mode 100644 index 0000000000..a4f54a5f9d --- /dev/null +++ b/src/device/esp/esp32s3-stack.S @@ -0,0 +1,52 @@ +// ----------------------------------------------------------------------- +// tinygo_scanCurrentStack — Spill all Xtensa register windows to the +// stack, then call tinygo_scanstack(sp) so the conservative GC can +// discover live heap pointers that are currently in physical registers. +// +// On RISC-V / ARM the equivalent function pushes callee-saved registers +// before the call. On Xtensa windowed ABI the same effect is achieved +// by forcing hardware window-overflow for every occupied pane: each +// overflow saves the four registers in that pane to the stack frame +// pointed to by the pane's a1 (sp). After all panes are flushed, a +// scan from the current sp to stackTop covers every live value. +// ----------------------------------------------------------------------- +.section .text.tinygo_scanstack + +.global tinygo_scanCurrentStack +tinygo_scanCurrentStack: + entry a1, 48 + + // Disable interrupts while flushing register windows. + rsr a4, PS + s32i a4, a1, 0 // save PS for later restore + rsil a4, 3 // XCHAL_EXCM_LEVEL + + // Flush all register windows using recursive call4. + // For NAREG=64 (16 panes), 15 recursive levels cover all panes + // except the current one (which is kept active). + movi a6, 15 + call4 .Lscan_spill + + // Restore interrupts. + l32i a4, a1, 0 + wsr.ps a4 + rsync + + // Pass current sp to tinygo_scanstack. + // call4 maps caller's a5→callee's a1 (stack ptr for callee's entry) + // and caller's a6→callee's a2 (first argument = sp). + mov a5, a1 // callee's a1 = valid stack pointer + mov a6, a1 // callee's a2 = sp argument + call4 tinygo_scanstack + + retw + + .balign 4 +.Lscan_spill: + entry a1, 16 + beqz a2, .Lscan_spill_done + addi a2, a2, -1 + mov a6, a2 + call4 .Lscan_spill +.Lscan_spill_done: + retw diff --git a/src/device/esp/esp32s3.S b/src/device/esp/esp32s3.S index 6566e3f342..5572a3d484 100644 --- a/src/device/esp/esp32s3.S +++ b/src/device/esp/esp32s3.S @@ -342,56 +342,3 @@ call_start_cpu0: // If main returns, loop forever. 1: j 1b - -// ----------------------------------------------------------------------- -// tinygo_scanCurrentStack — Spill all Xtensa register windows to the -// stack, then call tinygo_scanstack(sp) so the conservative GC can -// discover live heap pointers that are currently in physical registers. -// -// On RISC-V / ARM the equivalent function pushes callee-saved registers -// before the call. On Xtensa windowed ABI the same effect is achieved -// by forcing hardware window-overflow for every occupied pane: each -// overflow saves the four registers in that pane to the stack frame -// pointed to by the pane's a1 (sp). After all panes are flushed, a -// scan from the current sp to stackTop covers every live value. -// ----------------------------------------------------------------------- -.section .text.tinygo_scanCurrentStack - -.global tinygo_scanCurrentStack -tinygo_scanCurrentStack: - entry a1, 48 - - // Disable interrupts while flushing register windows. - rsr a4, PS - s32i a4, a1, 0 // save PS for later restore - rsil a4, 3 // XCHAL_EXCM_LEVEL - - // Flush all register windows using recursive call4. - // For NAREG=64 (16 panes), 15 recursive levels cover all panes - // except the current one (which is kept active). - movi a6, 15 - call4 .Lscan_spill - - // Restore interrupts. - l32i a4, a1, 0 - wsr.ps a4 - rsync - - // Pass current sp to tinygo_scanstack. - // call4 maps caller's a5→callee's a1 (stack ptr for callee's entry) - // and caller's a6→callee's a2 (first argument = sp). - mov a5, a1 // callee's a1 = valid stack pointer - mov a6, a1 // callee's a2 = sp argument - call4 tinygo_scanstack - - retw - - .balign 4 -.Lscan_spill: - entry a1, 16 - beqz a2, .Lscan_spill_done - addi a2, a2, -1 - mov a6, a2 - call4 .Lscan_spill -.Lscan_spill_done: - retw diff --git a/src/runtime/atomics_critical.go b/src/runtime/atomics_critical.go index 74ce321f10..33173c7901 100644 --- a/src/runtime/atomics_critical.go +++ b/src/runtime/atomics_critical.go @@ -1,4 +1,4 @@ -//go:build baremetal && !tinygo.wasm +//go:build baremetal && !tinygo.wasm && !espidf // Automatically generated file. DO NOT EDIT. // This file implements standins for non-native atomics using critical sections. diff --git a/src/runtime/baremetal.go b/src/runtime/baremetal.go index 9915f191b2..04b931e664 100644 --- a/src/runtime/baremetal.go +++ b/src/runtime/baremetal.go @@ -1,4 +1,4 @@ -//go:build baremetal +//go:build baremetal && !espidf package runtime diff --git a/src/runtime/interrupt/interrupt_none.go b/src/runtime/interrupt/interrupt_none.go index ea8bdb68c6..b8e009e142 100644 --- a/src/runtime/interrupt/interrupt_none.go +++ b/src/runtime/interrupt/interrupt_none.go @@ -1,4 +1,4 @@ -//go:build !baremetal || tkey +//go:build !baremetal || tkey || espidf package interrupt diff --git a/src/runtime/interrupt/interrupt_tinygoriscv.go b/src/runtime/interrupt/interrupt_tinygoriscv.go index 558e67150c..4e897eaa73 100644 --- a/src/runtime/interrupt/interrupt_tinygoriscv.go +++ b/src/runtime/interrupt/interrupt_tinygoriscv.go @@ -1,4 +1,4 @@ -//go:build tinygo.riscv && !tkey +//go:build tinygo.riscv && !tkey && !espidf package interrupt diff --git a/src/runtime/interrupt/interrupt_xtensa.go b/src/runtime/interrupt/interrupt_xtensa.go index bbe70afb6f..67206d139d 100644 --- a/src/runtime/interrupt/interrupt_xtensa.go +++ b/src/runtime/interrupt/interrupt_xtensa.go @@ -1,4 +1,4 @@ -//go:build xtensa && !esp32s3 +//go:build xtensa && !esp32s3 && !espidf package interrupt diff --git a/src/runtime/rand_norng.go b/src/runtime/rand_norng.go index b9ab475c76..53de54f328 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 || esp32s3 || espidf || tkey || (tinygo.riscv32 && virt) || rp2040 || rp2350) package runtime diff --git a/src/runtime/runtime_espidf.go b/src/runtime/runtime_espidf.go new file mode 100644 index 0000000000..63f188d032 --- /dev/null +++ b/src/runtime/runtime_espidf.go @@ -0,0 +1,128 @@ +//go:build espidf + +package runtime + +import "unsafe" + +var ( + heapStart uintptr + heapEnd uintptr + globalsStart uintptr + globalsEnd uintptr + stackTop uintptr +) + +// Allows C consumers of the library to set the GC variables. +// +//export tinygo_init +func tinygo_init(heap, heapSize, glob, globEnd, stack uintptr) { + heapStart, heapEnd = heap, heap+heapSize + globalsStart, globalsEnd = glob, globEnd + stackTop = stack + initRand() + initHeap() + initAll() +} + +func growHeap() bool { + return false +} + +//export abort +func abort() + +//export exit +func exit(code int) + +//export putchar +func libc_putchar(c byte) + +func putchar(c byte) { + libc_putchar(c) +} + +//export getchar +func libc_getchar() byte + +func getchar() byte { + return libc_getchar() +} + +func buffered() int { + return 0 +} + +const ( + clock_REALTIME = 1 + clock_MONOTONIC = 4 +) + +type timespec struct { + tv_sec int64 + tv_nsec int32 +} + +//export clock_gettime +func clock_gettime(clock int32, ts *timespec) + +func getTime(clock int32) uint64 { + var ts timespec + clock_gettime(clock, &ts) + return uint64(ts.tv_sec)*1e9 + uint64(ts.tv_nsec) +} + +func monotime() uint64 { + return getTime(clock_MONOTONIC) +} + +func ticks() timeUnit { + return timeUnit(monotime()) +} + +func ticksToNanoseconds(ticks timeUnit) int64 { + return int64(ticks) +} + +func nanosecondsToTicks(ns int64) timeUnit { + return timeUnit(ns) +} + +//export usleep +func usleep(usec uint) int + +func sleepTicks(d timeUnit) { + usleep(uint(d / 1e3)) +} + +const baremetal = true + +//go:linkname now time.now +func now() (sec int64, nsec int32, mono int64) { + var ts timespec + clock_gettime(clock_REALTIME, &ts) + sec = ts.tv_sec + nsec = ts.tv_nsec + mono = nanotime() + return +} + +// Picolibc is not configured to define its own errno value, instead it calls +// __errno_location. +// TODO: a global works well enough for now (same as errno on Linux with +// -scheduler=tasks), but this should ideally be a thread-local variable stored +// in task.Task. +// Especially when we add multicore support for microcontrollers. +var errno int32 + +//export __errno_location +func libc_errno_location() *int32 { + return &errno +} + +//export esp_fill_random +func esp_fill_random(buf unsafe.Pointer, len uintptr) + +func hardwareRand() (n uint64, ok bool) { + esp_fill_random(unsafe.Pointer(&n), 8) + return n, true +} diff --git a/targets/esp32c3-idf.json b/targets/esp32c3-idf.json new file mode 100644 index 0000000000..540e9842e3 --- /dev/null +++ b/targets/esp32c3-idf.json @@ -0,0 +1,15 @@ +{ + "inherits": ["riscv32"], + "features": "+32bit,+c,+m,+zmmul,-a,-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,-zaamo,-zabha,-zacas,-zalrsc,-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": ["espidf", "esp"], + "scheduler": "none", + "rtlib": "none", + "libc": "none", + "cflags": [ + "-march=rv32imc" + ], + "ldflags": [ + "-r", + "--no-gc-sections" + ] +} diff --git a/targets/esp32s3-idf.json b/targets/esp32s3-idf.json new file mode 100644 index 0000000000..3bf9dac8c1 --- /dev/null +++ b/targets/esp32s3-idf.json @@ -0,0 +1,14 @@ +{ + "inherits": ["xtensa"], + "cpu": "esp32s3", + "features": "+atomctl,+bool,+clamps,+coprocessor,+debug,+density,+div32,+esp32s3,+exception,+fp,+highpriinterrupts,+interrupt,+loop,+mac16,+memctl,+minmax,+miscsr,+mul32,+mul32high,+nsa,+prid,+regprotect,+rvector,+s32c1i,+sext,+threadptr,+timerint,+windowed", + "build-tags": ["espidf", "esp"], + "linker": "ld.lld", + "ldflags": [ + "-r", + "--no-gc-sections" + ], + "extra-files": [ + "src/device/esp/esp32s3-stack.S" + ] +} diff --git a/targets/esp32s3.json b/targets/esp32s3.json index ea6527d6d5..a1c3c28475 100644 --- a/targets/esp32s3.json +++ b/targets/esp32s3.json @@ -20,6 +20,7 @@ "linkerscript": "targets/esp32s3.ld", "extra-files": [ "src/device/esp/esp32s3.S", + "src/device/esp/esp32s3-stack.S", "targets/esp32s3-interrupts.S", "src/internal/task/task_stack_esp32.S" ], diff --git a/tools/gen-critical-atomics/gen-critical-atomics.go b/tools/gen-critical-atomics/gen-critical-atomics.go index 98ceebb020..a8ce6f3819 100644 --- a/tools/gen-critical-atomics/gen-critical-atomics.go +++ b/tools/gen-critical-atomics/gen-critical-atomics.go @@ -17,7 +17,7 @@ var tmpl = template.Must(template.New("go").Funcs(template.FuncMap{ return v }, "title": strings.Title, -}).Parse(`//go:build baremetal && !tinygo.wasm +}).Parse(`//go:build baremetal && !tinygo.wasm && !espidf // Automatically generated file. DO NOT EDIT. // This file implements standins for non-native atomics using critical sections.