- Uses PLL1 to boost the system clock from the 4 MHz MSIS default to 160 MHz.
- Sets VOS to Range 1 (1.2V) and enables the EPOD booster for higher frequency support.
- Configures flash latency (4 wait states) and enables prefetch for 160 MHz operation.
- Updates CPU and APB timer frequencies in the machine package accordingly.
- Fixes LPUART baud rate divisor computation by using 64-bit arithmetic to prevent
overflow with the newly increased 160 MHz clock.
Signed-off-by: deadprogram <ron@hybridgroup.com>
Realized this while looking through the code: there is no way to use
this safely when the scheduler is involved. It can only be used safely
with a `wfe` or similar (part of scheduler code).
* runtime,syscall,internal/poll,os: wasip1 poll_oneoff scheduler integration + net.FileListener
On wasip1 today every syscall.Read/Write blocks the entire wasm module — the
cooperative scheduler invokes poll_oneoff only for sleep/timer wakeups, and
there's no path from the net package to a working TCP server. This change
fixes both: it threads poll_oneoff through the scheduler's idle path so a
goroutine doing FD I/O parks instead of blocking the module, and it provides
enough internal/poll / os / syscall surface that upstream Go's
net.FileListener / net.FileConn works on a host-pre-opened TCP socket.
* runtime: keep scheduler_cooperative idle-wait calls direct
TestBinarySize/hifive1b/examples/echo regressed by 32 bytes after the
previous commit routed the scheduler's idle wait through a
schedulerIdleWait helper. The extra call frame + branch landed on every
non-wasip1 cooperative target, where the original direct sleepTicks /
waitForEvents calls compile to a single inlined call.
* compiler, runtime, reflect: generate type-specific hash/equal for composite map keys
For map keys that are not trivially binary-comparable, the compiler now
generates type-specific hash and equal functions as LLVM IR instead of
going through the interface+reflection path. This covers comparable
types: strings, floats, complex numbers, interfaces, channels, and
composites containing any mix of these.
Previously, maps with composite keys containing strings or floats
converted the key to interface{}, hashed via reflection, and compared
through interface equality. Now the compiler walks struct fields and
array elements directly, dispatching to the right runtime helper for
each field type and storing keys at their actual type.
Struct keys are always handled field-by-field so padding bytes do not
affect equality or hashing. Blank fields are ignored, matching Go
equality. Generated hash/equal function names use canonical underlying
type structure so structurally identical key types can share generated
functions. Padding zeroing before map operations is no longer needed
because structs no longer use the binary key path.
Also fix reflect map iteration for interface-keyed maps: MapIter.Key
returns an interface Value for map[interface{}] keys instead of
unpacking to the concrete key kind.
* compiler: generate loops for array map key hash/equal
Previously, array key hash and equal functions were unrolled at compile
time, generating one block of IR per element. For large arrays like
[1000]int inside a struct with non-binary fields, this caused code
explosion.
Now, binary-element arrays dispatch directly to hash32/memequal for the
whole array. Non-binary-element arrays generate an LLVM IR loop. The
equal loop short-circuits on the first mismatch.
Small arrays are still unrolled instead of looping, keeping the simple
cases compact.
* reflect: fix at-runtime map issues from review, and more found locally
Maps created through reflect.MakeMap need hash/equal behavior that
matches compiler-created maps. Add hashmapMakeReflect for composite key
types, using runtime closures that reconstruct interface{} values from
raw key bytes and delegate to the interface hash and equality paths.
Interface-keyed maps are already stored as interface values, so use the
existing interface hash/equal helpers directly for those. This keeps
reflect insert, lookup, delete, and compiled lookup paths consistent.
Also fix addressable small values used as interface map keys or
interface map values. loadSmallValue puts small indirect values back in
the pointer-sized interface data field the same way valueInterfaceUnsafe
does.
* compiler, interp, reflect: fix pointer map literals; remove interface fallback
Package-level map literals with pointer keys (both *T and
unsafe.Pointer) crash the compiler: the interp pass panics when trying
to hash pointer data as raw bytes, because pointer values in the interp
memory model are symbolic identities that do not fit in a byte.
Fix this by setting a recoverable error flag instead of panicking. The
interp detects the error after each instruction and defers the map
insert to runtime init code, where real addresses are available for
hashing. This matches how the interp already handles other operations
it cannot evaluate at compile time.
With this fix, unsafe.Pointer can also be classified as a binary map
key, which was the last type requiring the interface-based fallback.
Since all comparable types now use either the binary or the
compiler-generated hash/equal path, remove the interface fallback from
the compiler and reflect packages.
* compiler, transform: always pass hash/equal function pointers to hashmapMakeGeneric
The compiler now always resolves the hash and equal functions at compile
time and passes them directly to hashmapMakeGeneric, instead of passing
an algorithm enum to hashmapMake and resolving at runtime. For string
keys, the runtime hashmapStringPtrHash/hashmapStringEqual functions are
referenced directly. For binary keys, hash32/memequal are referenced.
The old hashmapMake with alg enum is retained for reflect, which still
needs runtime resolution when creating maps dynamically.
The OptimizeMaps transform pass is updated to handle both hashmapMake
and hashmapMakeGeneric, and to recognize hashmapGenericSet in addition
to hashmapBinarySet and hashmapStringSet. The now-unused
hashmapCanGenerateHashEqual helper is removed.
* runtime: store large map keys and values indirectly
When a map key or value exceeds 128 bytes, the bucket now stores a
pointer to separately allocated memory instead of the data inline. This
matches Go's MapMaxKeyBytes/MapMaxElemBytes threshold and prevents
bucket sizes from exploding for large key/value types.
For example, map[[256]byte]int previously used 2128 bytes per bucket
(16 header + 256*8 keys + 8*8 values); now it uses 144 bytes per bucket
(16 header + 8*8 pointers + 8*8 values).
The indirection is fully encapsulated in the runtime via helper
functions. Store the computed key and value slot sizes on the hashmap so
all runtime and reflect paths use the same bucket layout, including
non-indirect keys and values.
Add big-key golden coverage and benchmarks. Make the benchmark vary
enough key bytes to exercise hashing.
When SPI is configured via the GPIO Matrix, SPI signal transitions set
GPIO.STATUS bits on the routed pins. With a level-triggered CPU interrupt
(line 8), the ISR re-enters continuously as long as any STATUS bit is
asserted — causing user GPIO callbacks to fire spuriously.
Switch cpuInterruptFromPin to CPU interrupt 10, which is edge-triggered
(level 1) on the Xtensa LX7. This ensures the ISR fires once per GPIO
event rather than looping while SPI is active.
Also move STATUS_W1TC clears to before callback dispatch so that new
GPIO events arriving during handler execution generate a fresh edge, and
add writeINTCLEAR(active) in handleInterrupt to properly acknowledge
edge-triggered CPU interrupt pending bits via the INTCLEAR register.
Fixes GPIO interrupts firing constantly when SPI and pin interrupts are
used together.
Signed-off-by: deadprogram <ron@hybridgroup.com>
* reflect: implement method-set based AssignableTo and Implements
Based on the design from #4376 by aykevl.
Fixes#4277, fixes#3580.
Co-authored-by: Ayke van Laethem <aykevanlaethem@gmail.com>
* builder: update expected binary sizes for reflect changes
* Make interface checks similar to invoke, allowing typeImplementsMethodSet and method info to be dropped when reflect is not present
* Add more tests that BigGo reflect tests
* Even more pruning
* Add go/token and net/url to passing tests
* Prune even further, I am less happy with this, though
* Update size test now that we are smaller
* Skip some tests
* elide method lists
* format, oops
* fix tests
* Add a panic, pull out constant to keep in sync
* Add debug info
* Remove code that was leftover from a previous refactor
---------
Co-authored-by: Ayke van Laethem <aykevanlaethem@gmail.com>
When no USB host is reading, flushAndWait() spins 50K iterations per
FIFO-full event. With putchar calling WriteByte per byte, the cumulative
delay starves I2C and other peripherals, freezing displays.
Add a txStalled flag: the first FIFO-full triggers one flushAndWait
attempt. If it fails (no host), txStalled is set and all subsequent
writes return immediately with no spin — just a register read and a
bool check. When a host reconnects, SERIAL_IN_EP_DATA_FREE goes back
to 1, bypassing the stall path and clearing the flag automatically.
Go 1.26 replaced the individual Darwin syscall entry points (syscall,
syscallX, syscallPtr, syscall6, syscall6X) with two variadic functions:
syscalln and rawsyscalln. The old wrappers now have Go bodies that call
these, then use errno/errnoX/errnoPtr to interpret the result.
This caused two bugs in our rawsyscalln implementation:
1. Return value truncation: We used call_syscall/call_syscall6 which
return int32, truncating 64-bit results (pointers from fdopendir,
offsets from lseek, addresses from mmap). For example, a DIR* pointer
returned by fdopendir would lose its upper 32 bits on arm64, causing
SIGSEGV when later accessed.
2. Lost 4th argument: The case 4/5/6 fallthrough had 'a3 = args[3]'
instead of 'a4 = args[3]', and a3 was immediately overwritten by
'a1, a2, a3 = args[0], args[1], args[2]'. This lost the 4th syscall
argument entirely, breaking pread (offset=0) and causing wrong data
to be read from files.
Fix by using call_syscallX/call_syscall6X (returning full uintptr),
always reading errno (letting the Go wrappers decide what to do with
it), and correcting the argument assignment.
Go 1.26 added syscall.runtimeClearenv (called by syscall.Clearenv) which
must be provided by the runtime via go:linkname. Without it, the os
package fails to link.
Go 1.26 changed all Windows syscall wrappers in zsyscall_windows.go
to use SyscallN instead of fixed-argument Syscall/Syscall6/etc. The
SyscallN function now has a body that calls an unexported syscalln
function (provided by runtime via //go:linkname).
TinyGo's existing createSyscall compiler builtin used call.Args[2:]
to extract syscall arguments, but for variadic SyscallN the SSA
representation passes args as a slice value (not individual args),
causing call.Args[2:] to be empty -- resulting in zero arguments
being passed to Windows API calls and 0xc0000005 access violations.
Fix this by:
1. Excluding syscall.SyscallN from builtin interception, letting
Go 1.26's function body compile normally (it calls syscalln)
2. Adding a new createSyscalln compiler builtin that intercepts
syscall.syscalln and correctly handles the variadic slice:
- Generates a switch on the arg count n (0-18 cases)
- Each case loads args from the slice via GEP/Load
- Wraps calls with SetLastError(0)/GetLastError() as before
- Handles i386 stdcall conventions
3. Adding runtime stubs for both Go versions:
- go1.26: syscall.syscalln stub (body intercepted by compiler)
- pre-go1.26: syscall.SyscallN stub (linker satisfaction)
Go 1.26 changed syscall.loadlibrary, syscall.loadsystemlibrary, and
syscall.getprocaddress from declarations to definitions in
syscall/dll_windows.go. TinyGo's runtime also defines these via
//go:linkname, causing "symbol multiply defined!" during LLVM module
linking.
Resolve this by detecting duplicate function definitions before calling
llvm.LinkModules and turning the incoming duplicate into a declaration,
so the runtime's version wins. TinyGo's implementations must take
precedence because Go 1.26's versions depend on //go:cgo_import_dynamic,
which TinyGo does not support.
Also fix the signature of syscall_loadsystemlibrary to match the
standard library (remove unused absoluteFilepath parameter).
Signed-off-by: deadprogram <ron@hybridgroup.com>
Rewrite kernel and double exception handlers to save EXCCAUSE/EPC1 to
RTC STORE registers before triggering a software reset, replacing the
LED-blink diagnostic with post-mortem debug info that survives reset.
Add user exception dispatch in the level-1 handler with a weak
espradio_user_exception symbol so programs without espradio still link.
Implement procPin/procUnpin for Xtensa using RSIL/WSR PS to properly
disable interrupts during atomic operations. Fix abort() to use a
waiti loop instead of bare spin.
Add --wrap ldflags for malloc/calloc/free/realloc/ppCheckTxConnTrafficIdle
to support espradio WiFi blob integration.
Signed-off-by: deadprogram <ron@hybridgroup.com>
Replace the busy-wait sleepTicks with an interrupt-driven version that
sets a TIMG0 timer alarm and waits for the interrupt to fire. The timer
alarm handler disables INT_ENA at the peripheral level to prevent
level-triggered re-assertion; sleepTicks re-enables it after each wake.
This avoids burning CPU cycles during time.Sleep and similar delays.
Replace the minimal inline ISR (which only disabled INTENABLE) with a
full level-1 interrupt handler that saves/restores the interrupted
context and dispatches to Go's handleInterrupt.
The handler uses callx4 (not callx0) to call into Go code because:
- callx0 does not set PS.CALLINC, so the Go function's entry
instruction uses stale CALLINC from the interrupted code, causing
wrong window rotation and a garbage stack pointer.
- callx4 explicitly sets CALLINC=1, and our frame pointer (a1) is
outside the callee's register window so it is preserved.
Also updates the USB Serial/JTAG ISR to disable INT_ENA (peripheral
level) instead of relying on INTENABLE, and adds signalInterrupt to
the dispatcher so sleepTicks can be woken by any interrupt.
* esp32s3: add interrupt support
This finally adds the long awaited support for interrupts on the
Xtensa arch. Initially just for the ESP32-S3 but then others.
Signed-off-by: deadprogram <ron@hybridgroup.com>
* esp32s3: get interrupts working correctly
There were a number of needed changes in order to get interrupts correctly working
on the esp32s3 processor:
- PS.UM=1 in interruptInit() - routed interrupts to user exception vector (0x340)
instead of kernel (0x300)
- Inline ISR in the vector slot - external handlers via j/call0 crashed (likely
clang Xtensa literal pool issue with large movi constants in separate sections)
- Disable INTENABLE (not just INT_CLR) - the USB RX interrupt is level-triggered;
clearing INT_CLR alone causes infinite re-entry since data is still in the FIFO
- Buffered() re-enables INTENABLE after draining the hardware FIFO
Signed-off-by: deadprogram <ron@hybridgroup.com>
---------
Signed-off-by: deadprogram <ron@hybridgroup.com>
Signed-off-by: deadprogram <ron@hybridgroup.com>
machine/stm32u585: fix PWR peripheral clock was never enabled
On STM32U5, PWR is on AHB3 and requires RCC.AHB3ENR.PWREN -
unlike some other STM32 families where PWR is always clocked.
Without this, all writes to PWR registers (including IO2SV for
VDDIO2) were silently dropped, so GPIOG pins could never drive.
Signed-off-by: deadprogram <ron@hybridgroup.com>
target: correct I2C pin mapping for Arduino UNO Q
Signed-off-by: deadprogram <ron@hybridgroup.com>
The complex 160MHz PLL initialization was hanging the MCU.
The fix: Replaced the entire PLL-based clock init with the MSI 4MHz default
Signed-off-by: deadprogram <ron@hybridgroup.com>
Add processor target (cortex-m33), linker script (2048K flash, 768K RAM),
and arduino-uno-q board target. Runtime initializes 160MHz clock via PLL1
from HSI16, with PWR Range 1 and EPOD booster.
Also add psctype abstraction for timer PSC register width, needed because
the U5 SVD defines PSC as a 16-bit register.
MCAUSE was never being cleared after handling an interrupt.
On RISC-V, mret does NOT zero MCAUSE — it retains the last
trap cause. Every other TinyGo RISC-V target (FE310, K210,
QEMU) explicitly does riscv.MCAUSE.Set(0) after handling.
The ESP32-C3 was missing this.
Signed-off-by: deadprogram <ron@hybridgroup.com>
Previously, there was no specific stm32f4r5.svd in lib/stm32-svd,
just stm32f4x5.svd was used; now, both files are present. This means
that the existing build-tag stm32f4r5 will include the
device/stm32/stm32f4r5.go file, and the additional build-tag stm32f4x5
would include the device/stm32/stm32f4x5.go file as well, resulting
in build conflicts. Renaming just the tag, which is used in src/machine,
and src/runtime, into stm32f4y5 solves this issue.
The compiler may generate calls to fminimum/fmaximum on some platforms.
Neither of the libm implementations we statically link against have these functions yet.
Implement them ourselves.
This changes the order for initialization of the random number
seed generation on wasm platforms until after the heap has been
initialized. Should fix#5198
Signed-off-by: deadprogram <ron@hybridgroup.com>
Make timeoffset atomic to be able to handle changing the system
time. Otherwise the scheduler can gets rather confused if you call
AdjustTimeOffset when there are multiple goroutines already running.
Signed-off-by: deadprogram <ron@hybridgroup.com>
* Add STM32G0B1 target support
Introduce support for STM32G0B1 microcontrollers, including target-specific JSON files, linker scripts, and runtime initialization. This update adds hardware support for GPIO, UART, SPI, I2C, timers, and additional board-specific configurations like Nucleo-G0B1RE.
* Update STM32G0 clock initialization to 64MHz and adjust related configurations
Reconfigure STM32G0 to use a 64MHz system clock via PLL with HSI16 as the source. Update flash latency, prescaler settings, and I2C timing values to reflect the new frequency.
* Cleanup
* Cleanup
* Add STM32G0-specific UART implementation
Introduce a new UART implementation for the STM32G0 series with chip-specific setup and configuration methods. Update the generic STM32 UART code to exclude STM32G0.
* Refactor STM32G0 runtime and machine code to utilize chip-specific register access functions
Simplify and standardize register operations with dedicated setter methods in the STM32G0 runtime and machine code and cleanup redundant syntax.
* Remove redundant commented-out APBENR1 register operations in STM32G0 machine code
* Introduce FDCAN support for STM32G0B1 series
Add FDCAN peripheral implementation targeting STM32G0B1, including support for standard, extended identifiers, and bit rate configuration. Update board files to include FDCAN pins, instances, and clock configuration for Nucleo-G0B1RE and Amken Trio boards.
The compiler now implements the copy builtin directly instead of calling sliceCopy.
The length is calculated with the llvm.umax.* intrinsics, and the move is performed by llvm.memmove.*.
Both of these operations are easily understood by LLVM's optimization passes.
The type's alignment is also provided to llvm.memmove.*, which is useful when rewriting the move.
Interp no longer needs to reimplement sliceCopy.
Some edge case handling was implemented by sliceCopy but not llvm.memmove.*/llvm.memcpy.*.
I copied this over, so copies of external slices should work now.
Volatile moves/copies are now run at runtime by interp.
There is a 4-byte size increase due to some confusing length logic in sendUSBPacket.
I will look at sendUSBPacket in a future PR.
The reason is that allocating without a heap is usually more visible
than using an uninitialized random generator which is subtle and may
lead to security vulnerabilities.
Based on suggestion from @eliasnaur
Signed-off-by: deadprogram <ron@hybridgroup.com>
Instead of looping over each block, we can use bit hacks to operate on an entire state byte.
I deinterleaved the state bits in order to enable these tricks.
Sweep used to count free/freed allocations/blocks.
I managed to move/remove all of these counters:
- The free space is now calculated in buildFreeRanges by adding the range lengths.
- ReadMemStats counts freed objects by subtracting live objects from allocated objects.
- gcFreedBlocks was never necessary because MemStats.HeapAlloc is the same as MemStats.HeapInUse.
The allocator originally just looped through the blocks until it found a sufficiently-long range.
This is simple, but it fragments very easily and can degrade to a full heap scan for long requests.
Instead, we now maintain a sorted nested list of free ranges by size.
The allocator will select the shortest sufficient-length range, generally reducing fragmentation.
This data structure can find a range in time directly proportional to the requested length.