Compare commits

...

13 Commits

Author SHA1 Message Date
deadprogram e5caa99b9c feature: ability to generate nrf firmware manifest for Open DFU format using nrf_open_dfu build tag. Intended to fix #4089
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-02-22 11:49:05 +01:00
deadprogram 17bb1fec44 feature: add buildmode=wasi-legacy to support existing base of users who expected the
older behavior for wasi modules to not return an exit code as if they were reactors.

See #4726 for some details on what this is intended to address.

Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-02-21 13:06:14 +01:00
Ayke van Laethem f4fd79f7be runtime: manually initialize xorshift state
This ensures:

 1. The xorshift state is initialized during interp.
 2. The xorshift state gets initialized to a real random number on
    hardware that supports it at runtime.

This fixes a big binary size regression from the previous commit. It's
still not perfect: most programs increase binary size by a few bytes.
But it's not nearly as bad as before.
2025-02-21 11:36:17 +01:00
Ayke van Laethem 811b13a9d3 interp: correctly mark functions as modifying memory
Previously, if a function couldn't be interpreted in the interp pass, it
would just be run at runtime and the parameters would be marked as
potentially being modified. However, this is incorrect: the function
itself can also modify memory. So the function itself also needs to be
marked (recursively).

This fixes a number of regressions while upgrading to Go 1.24.

This results in a significant size regression that is mostly worked
around in the next commit.
2025-02-21 11:36:17 +01:00
Ron Evans 6e97079367 target: add Pimoroni Pico Plus2 (#4735)
* machine: separate definitions for ADC pins on rp2350/rp2350b

Signed-off-by: deadprogram <ron@hybridgroup.com>

* targets: add support for Pimoroni Pico Plus2

Signed-off-by: deadprogram <ron@hybridgroup.com>

---------

Signed-off-by: deadprogram <ron@hybridgroup.com>
Co-authored-by: Patricio Whittingslow <graded.sp@gmail.com>
2025-02-21 09:56:52 +01:00
soypat 9c7bbce029 rp2350: add pll generalized solution; fix ADC handles; pwm period fix 2025-02-21 09:56:52 +01:00
soypat 0ec1cb1e19 machine/rp2350: extending support to include the rp2350b 2025-02-21 09:56:52 +01:00
Scott Feldman e7118e5bda add comboat_fw tag for elecrow W5 boards with Combo-AT Wifi firmware 2025-02-21 07:22:17 +01:00
Ron Evans 150d9d1c6b fix: correctly handle id lookup for finalizeRef call
Modify the ID used for looking up the reference, based on suggestion made by @prochac

Also use console.error in the caase that the reference is not found, since it is now actually
known to be an error.
2025-02-20 22:17:38 +01:00
Laurent Demailly 8fe039156b fix: Avoid total failure on wasm finalizer call 2025-02-20 22:17:38 +01:00
leongross 2d17dec7bf os/file: add file.Chmod
Signed-off-by: leongross <leon.gross@9elements.com>
2025-02-19 22:12:51 +01:00
JP Hastings-Spital c1b267a208 Ensure build output directory is created
`tinygo build -o /path/to/out .` expected `/path/to/out` to already exist, which is different behaviour to `go build`. This change ensures tinygo creates any directories needed to be able to build to the specified output dir.
2025-02-19 19:55:48 +01:00
deadprogram f02c56c9e0 net: update to latest submodule with httptest subpackage and
ResolveIPAddress implementation.

Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-02-17 13:16:56 +01:00
37 changed files with 659 additions and 96 deletions
+4
View File
@@ -523,6 +523,8 @@ smoketest: testchdir
# regression test for #2563
cd tests/os/smoke && $(TINYGO) test -c -target=pybadge && rm smoke.test
# test all examples (except pwm)
$(TINYGO) build -size short -o test.hex -target=pga2350 examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc
@@ -745,6 +747,8 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=tiny2350 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pico-plus2 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-tiny examples/echo
@$(MD5SUM) test.hex
# test pwm
+15
View File
@@ -604,6 +604,11 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
},
}
// Create the output directory, if needed
if err := os.MkdirAll(filepath.Dir(outpath), 0777); err != nil {
return result, err
}
// Check whether we only need to create an object file.
// If so, we don't need to link anything and will be finished quickly.
outext := filepath.Ext(outpath)
@@ -667,6 +672,16 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
ldflags = append(ldflags, "--no-entry")
}
if config.Options.BuildMode == "wasi-legacy" {
if !strings.HasPrefix(config.Triple(), "wasm32-") {
return result, fmt.Errorf("buildmode wasi-legacy is only supported on wasm")
}
if config.Options.Scheduler != "none" {
return result, fmt.Errorf("buildmode wasi-legacy only supports scheduler=none")
}
}
// Add compiler-rt dependency if needed. Usually this is a simple load from
// a cache.
if config.Target.RTLib == "compiler-rt" {
+14 -2
View File
@@ -19,7 +19,7 @@ type jsonManifest struct {
DataFile string `json:"dat_file"`
InitPacketData nrfInitPacket `json:"init_packet_data"`
} `json:"application"`
DFUVersion float64 `json:"dfu_version"` // yes, this is a JSON number, not a string
DFUVersion float64 `json:"dfu_version,omitempty"` // yes, this is a JSON number, not a string
} `json:"manifest"`
}
@@ -93,7 +93,19 @@ func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string)
manifest.Manifest.Application.BinaryFile = "application.bin"
manifest.Manifest.Application.DataFile = "application.dat"
manifest.Manifest.Application.InitPacketData = initPacket
manifest.Manifest.DFUVersion = 0.5
// use build tag "nrf_open_dfu" to indicate open DFU format, otherwise defaults to secure DFU
openDFU := false
for _, tag := range options.Tags {
if tag == "nrf_open_dfu" {
openDFU = true
break
}
}
if !openDFU {
manifest.Manifest.DFUVersion = 0.5
}
// Write the JSON manifest to the file.
jsonw, err := w.Create("manifest.json")
+2 -2
View File
@@ -43,8 +43,8 @@ func TestBinarySize(t *testing.T) {
tests := []sizeTest{
// microcontrollers
{"hifive1b", "examples/echo", 4560, 280, 0, 2268},
{"microbit", "examples/serial", 2908, 388, 8, 2272},
{"wioterminal", "examples/pininterrupt", 7293, 1487, 116, 6912},
{"microbit", "examples/serial", 2916, 388, 8, 2272},
{"wioterminal", "examples/pininterrupt", 7315, 1489, 116, 6912},
// TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
var (
validBuildModeOptions = []string{"default", "c-shared"}
validBuildModeOptions = []string{"default", "c-shared", "wasi-legacy"}
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
+5
View File
@@ -283,6 +283,11 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
info.wasmName = "_start"
info.exported = true
}
if info.linkName == "runtime.wasmEntryLegacy" && c.BuildMode == "wasi-legacy" {
info.linkName = "_start"
info.wasmName = "_start"
info.exported = true
}
// Check for //go: pragmas, which may change the link name (among others).
c.parsePragmas(&info, f)
+3 -3
View File
@@ -970,9 +970,9 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
case llvm.Call:
llvmFn := operands[len(operands)-1]
args := operands[:len(operands)-1]
for _, arg := range args {
if arg.Type().TypeKind() == llvm.PointerTypeKind {
err := mem.markExternalStore(arg)
for _, op := range operands {
if op.Type().TypeKind() == llvm.PointerTypeKind {
err := mem.markExternalStore(op)
if err != nil {
return r.errorAt(inst, err)
}
+1 -1
View File
@@ -1501,7 +1501,7 @@ func main() {
var tags buildutil.TagsFlag
flag.Var(&tags, "tags", "a space-separated list of extra build tags")
target := flag.String("target", "", "chip/board name or JSON target specification file")
buildMode := flag.String("buildmode", "", "build mode to use (default, c-shared)")
buildMode := flag.String("buildmode", "", "build mode to use (default, c-shared, wasi-legacy)")
var stackSize uint64
flag.Func("stack-size", "goroutine stack size (if unknown at compile time)", func(s string) error {
size, err := bytesize.Parse(s)
+9
View File
@@ -594,6 +594,15 @@ func TestWasmExport(t *testing.T) {
noOutput: true, // wasm-unknown cannot produce output
command: true,
},
// Test buildmode=wasi-legacy with WASI.
{
name: "WASIp1-legacy",
target: "wasip1",
buildMode: "wasi-legacy",
scheduler: "none",
file: "wasmexport-noscheduler.go",
command: true,
},
}
for _, tc := range tests {
+98
View File
@@ -0,0 +1,98 @@
//go:build pga2350
package machine
// PGA2350 pin definitions.
const (
GP0 = GPIO0
GP1 = GPIO1
GP2 = GPIO2
GP3 = GPIO3
GP4 = GPIO4
GP5 = GPIO5
GP6 = GPIO6
GP7 = GPIO7
GP8 = GPIO8
GP9 = GPIO9
GP10 = GPIO10
GP11 = GPIO11
GP12 = GPIO12
GP13 = GPIO13
GP14 = GPIO14
GP15 = GPIO15
GP16 = GPIO16
GP17 = GPIO17
GP18 = GPIO18
GP19 = GPIO19
GP20 = GPIO20
GP21 = GPIO21
GP22 = GPIO22
GP26 = GPIO26
GP27 = GPIO27
GP28 = GPIO28
GP29 = GPIO29
GP30 = GPIO30 // peripherals: PWM7 channel A
GP31 = GPIO31 // peripherals: PWM7 channel B
GP32 = GPIO32 // peripherals: PWM8 channel A
GP33 = GPIO33 // peripherals: PWM8 channel B
GP34 = GPIO34 // peripherals: PWM9 channel A
GP35 = GPIO35 // peripherals: PWM9 channel B
GP36 = GPIO36 // peripherals: PWM10 channel A
GP37 = GPIO37 // peripherals: PWM10 channel B
GP38 = GPIO38 // peripherals: PWM11 channel A
GP39 = GPIO39 // peripherals: PWM11 channel B
GP40 = GPIO40 // peripherals: PWM8 channel A
GP41 = GPIO41 // peripherals: PWM8 channel B
GP42 = GPIO42 // peripherals: PWM9 channel A
GP43 = GPIO43 // peripherals: PWM9 channel B
GP44 = GPIO44 // peripherals: PWM10 channel A
GP45 = GPIO45 // peripherals: PWM10 channel B
GP46 = GPIO46 // peripherals: PWM11 channel A
GP47 = GPIO47 // peripherals: PWM11 channel B
)
var DefaultUART = UART0
// Peripheral defaults.
const (
xoscFreq = 12 // MHz
I2C0_SDA_PIN = GP4
I2C0_SCL_PIN = GP5
I2C1_SDA_PIN = GP2
I2C1_SCL_PIN = GP3
// Default Serial Clock Bus 0 for SPI communications
SPI0_SCK_PIN = GPIO18
// Default Serial Out Bus 0 for SPI communications
SPI0_SDO_PIN = GPIO19 // Tx
// Default Serial In Bus 0 for SPI communications
SPI0_SDI_PIN = GPIO16 // Rx
// Default Serial Clock Bus 1 for SPI communications
SPI1_SCK_PIN = GPIO10
// Default Serial Out Bus 1 for SPI communications
SPI1_SDO_PIN = GPIO11 // Tx
// Default Serial In Bus 1 for SPI communications
SPI1_SDI_PIN = GPIO12 // Rx
UART0_TX_PIN = GPIO0
UART0_RX_PIN = GPIO1
UART1_TX_PIN = GPIO8
UART1_RX_PIN = GPIO9
UART_TX_PIN = UART0_TX_PIN
UART_RX_PIN = UART0_RX_PIN
)
// USB identifiers
const (
usb_STRING_PRODUCT = "PGA2350"
usb_STRING_MANUFACTURER = "Pimoroni"
)
var (
usb_VID uint16 = 0x2E8A
usb_PID uint16 = 0x000A
)
+93
View File
@@ -0,0 +1,93 @@
//go:build pico_plus2
package machine
// GPIO pins
const (
GP0 Pin = GPIO0
GP1 Pin = GPIO1
GP2 Pin = GPIO2
GP3 Pin = GPIO3
GP4 Pin = GPIO4
GP5 Pin = GPIO5
GP6 Pin = GPIO6
GP7 Pin = GPIO7
GP8 Pin = GPIO8
GP9 Pin = GPIO9
GP10 Pin = GPIO10
GP11 Pin = GPIO11
GP12 Pin = GPIO12
GP13 Pin = GPIO13
GP14 Pin = GPIO14
GP15 Pin = GPIO15
GP16 Pin = GPIO16
GP17 Pin = GPIO17
GP18 Pin = GPIO18
GP19 Pin = GPIO19
GP20 Pin = GPIO20
GP21 Pin = GPIO21
GP22 Pin = GPIO22
GP26 Pin = GPIO26
GP27 Pin = GPIO27
GP28 Pin = GPIO28
GP32 Pin = GPIO32
GP33 Pin = GPIO33
GP34 Pin = GPIO34
GP35 Pin = GPIO35
GP36 Pin = GPIO36
// Onboard LED
LED Pin = GPIO25
// Onboard crystal oscillator frequency, in MHz.
xoscFreq = 12 // MHz
)
// I2C Default pins on Raspberry Pico.
const (
I2C0_SDA_PIN = GP4
I2C0_SCL_PIN = GP5
I2C1_SDA_PIN = GP2
I2C1_SCL_PIN = GP3
)
// SPI default pins
const (
// Default Serial Clock Bus 0 for SPI communications
SPI0_SCK_PIN = GPIO18
// Default Serial Out Bus 0 for SPI communications
SPI0_SDO_PIN = GPIO19 // Tx
// Default Serial In Bus 0 for SPI communications
SPI0_SDI_PIN = GPIO16 // Rx
// Default Serial Clock Bus 1 for SPI communications
SPI1_SCK_PIN = GPIO10
// Default Serial Out Bus 1 for SPI communications
SPI1_SDO_PIN = GPIO11 // Tx
// Default Serial In Bus 1 for SPI communications
SPI1_SDI_PIN = GPIO12 // Rx
)
// UART pins
const (
UART0_TX_PIN = GPIO0
UART0_RX_PIN = GPIO1
UART1_TX_PIN = GPIO8
UART1_RX_PIN = GPIO9
UART_TX_PIN = UART0_TX_PIN
UART_RX_PIN = UART0_RX_PIN
)
var DefaultUART = UART0
// USB identifiers
const (
usb_STRING_PRODUCT = "Pico Plus2"
usb_STRING_MANUFACTURER = "Pimoroni"
)
var (
usb_VID uint16 = 0x2E8A
usb_PID uint16 = 0x000F
)
+2
View File
@@ -16,6 +16,8 @@ const (
// Note: On RP2350, most spinlocks are unusable due to Errata 2
_NUMSPINLOCKS = 32
_PICO_SPINLOCK_ID_IRQ = 9
// is48Pin notes whether the chip is RP2040 with 32 pins or RP2350 with 48 pins.
is48Pin = _NUMBANK0_GPIOS == 48
)
// UART on the RP2040
+10
View File
@@ -45,6 +45,16 @@ const (
PinPIO1
)
// Analog pins on RP2040.
const (
ADC0 Pin = GPIO26
ADC1 Pin = GPIO27
ADC2 Pin = GPIO28
ADC3 Pin = GPIO29
thermADC = 30
)
const (
clkGPOUT0 clockIndex = iota // GPIO Muxing 0
clkGPOUT1 // GPIO Muxing 1
+8 -2
View File
@@ -126,11 +126,17 @@ func (p Pin) Configure(config PinConfig) {
return
}
p.init()
mask := uint32(1) << p
switch config.Mode {
case PinOutput:
p.setFunc(fnSIO)
rp.SIO.GPIO_OE_SET.Set(mask)
if is48Pin && p >= 32 {
mask := uint32(1) << (p % 32)
rp.SIO.GPIO_HI_OE_SET.Set(mask)
} else {
mask := uint32(1) << p
rp.SIO.GPIO_OE_SET.Set(mask)
}
case PinInput:
p.setFunc(fnSIO)
p.pulloff()
+14
View File
@@ -0,0 +1,14 @@
//go:build rp2350 && !rp2350b
package machine
// Analog pins on RP2350a.
const (
ADC0 Pin = GPIO26
ADC1 Pin = GPIO27
ADC2 Pin = GPIO28
ADC3 Pin = GPIO29
// fifth ADC channel.
thermADC = 30
)
+48
View File
@@ -0,0 +1,48 @@
//go:build rp2350b
package machine
// RP2350B has additional pins.
const (
GPIO30 Pin = 30 // peripherals: PWM7 channel A
GPIO31 Pin = 31 // peripherals: PWM7 channel B
GPIO32 Pin = 32 // peripherals: PWM8 channel A
GPIO33 Pin = 33 // peripherals: PWM8 channel B
GPIO34 Pin = 34 // peripherals: PWM9 channel A
GPIO35 Pin = 35 // peripherals: PWM9 channel B
GPIO36 Pin = 36 // peripherals: PWM10 channel A
GPIO37 Pin = 37 // peripherals: PWM10 channel B
GPIO38 Pin = 38 // peripherals: PWM11 channel A
GPIO39 Pin = 39 // peripherals: PWM11 channel B
GPIO40 Pin = 40 // peripherals: PWM8 channel A
GPIO41 Pin = 41 // peripherals: PWM8 channel B
GPIO42 Pin = 42 // peripherals: PWM9 channel A
GPIO43 Pin = 43 // peripherals: PWM9 channel B
GPIO44 Pin = 44 // peripherals: PWM10 channel A
GPIO45 Pin = 45 // peripherals: PWM10 channel B
GPIO46 Pin = 46 // peripherals: PWM11 channel A
GPIO47 Pin = 47 // peripherals: PWM11 channel B
)
// Analog pins on 2350b.
const (
ADC0 Pin = GPIO40
ADC1 Pin = GPIO41
ADC2 Pin = GPIO42
ADC3 Pin = GPIO43
ADC4 Pin = GPIO44
ADC5 Pin = GPIO45
ADC6 Pin = GPIO46
ADC7 Pin = GPIO47
// Ninth ADC channel.
thermADC = 48
)
// Additional PWMs on the RP2350B.
var (
PWM8 = getPWMGroup(8)
PWM9 = getPWMGroup(9)
PWM10 = getPWMGroup(10)
PWM11 = getPWMGroup(11)
)
+6 -38
View File
@@ -11,15 +11,6 @@ import (
// ADCChannel is the ADC peripheral mux channel. 0-4.
type ADCChannel uint8
// ADC channels. Only ADC_TEMP_SENSOR is public. The other channels are accessed via Machine.ADC objects
const (
adc0_CH ADCChannel = iota
adc1_CH
adc2_CH
adc3_CH // Note: GPIO29 not broken out on pico board
adcTempSensor // Internal temperature sensor channel
)
// Used to serialise ADC sampling
var adcLock sync.Mutex
@@ -58,20 +49,10 @@ func (a ADC) Get() uint16 {
// GetADCChannel returns the channel associated with the ADC pin.
func (a ADC) GetADCChannel() (c ADCChannel, err error) {
err = nil
switch a.Pin {
case ADC0:
c = adc0_CH
case ADC1:
c = adc1_CH
case ADC2:
c = adc2_CH
case ADC3:
c = adc3_CH
default:
err = errors.New("no ADC channel for pin value")
if a.Pin < ADC0 {
return 0, errors.New("no ADC channel for pin value")
}
return c, err
return ADCChannel(a.Pin - ADC0), nil
}
// Configure sets the channel's associated pin to analog input mode.
@@ -113,12 +94,12 @@ func ReadTemperature() (millicelsius int32) {
if rp.ADC.CS.Get()&rp.ADC_CS_EN == 0 {
InitADC()
}
thermChan, _ := ADC{Pin: thermADC}.GetADCChannel()
// Enable temperature sensor bias source
rp.ADC.CS.SetBits(rp.ADC_CS_TS_EN)
// T = 27 - (ADC_voltage - 0.706)/0.001721
return (27000<<16 - (int32(adcTempSensor.getVoltage())-706<<16)*581) >> 16
return (27000<<16 - (int32(thermChan.getVoltage())-706<<16)*581) >> 16
}
// waitForReady spins waiting for the ADC peripheral to become ready.
@@ -129,18 +110,5 @@ func waitForReady() {
// The Pin method returns the GPIO Pin associated with the ADC mux channel, if it has one.
func (c ADCChannel) Pin() (p Pin, err error) {
err = nil
switch c {
case adc0_CH:
p = ADC0
case adc1_CH:
p = ADC1
case adc2_CH:
p = ADC2
case adc3_CH:
p = ADC3
default:
err = errors.New("no associated pin for channel")
}
return p, err
return Pin(c) + ADC0, nil
}
+4 -8
View File
@@ -13,12 +13,6 @@ func CPUFrequency() uint32 {
return 125 * MHz
}
// Returns the period of a clock cycle for the raspberry pi pico in nanoseconds.
// Used in PWM API.
func cpuPeriod() uint32 {
return 1e9 / CPUFrequency()
}
// clockIndex identifies a hardware clock
type clockIndex uint8
@@ -143,6 +137,8 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) {
}
const pllsysFB, pllsysPD1, pllsysPD2 uint32 = 125, 6, 2 // RP2040 running 125MHz with 1500MHz VCO.
// init initializes the clock hardware.
//
// Must be called before any other clock function.
@@ -169,8 +165,8 @@ func (clks *clocksType) init() {
// REF FBDIV VCO POSTDIV
// pllSys: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
// pllUSB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz
pllSys.init(1, 1500*MHz, 6, 2)
pllUSB.init(1, 480*MHz, 5, 2)
pllSys.init(1, uint32(pllsysFB), uint32(pllsysPD1), uint32(pllsysPD2))
pllUSB.init(1, 40, 5, 2)
// Configure clocks
// clkRef = xosc (12MHz) / 1 = 12MHz
+31 -8
View File
@@ -63,8 +63,13 @@ func (p Pin) PortMaskSet() (*uint32, uint32) {
// set drives the pin high
func (p Pin) set() {
mask := uint32(1) << p
rp.SIO.GPIO_OUT_SET.Set(mask)
if is48Pin && p >= 32 {
mask := uint32(1) << (p % 32)
rp.SIO.GPIO_HI_OUT_SET.Set(mask)
} else {
mask := uint32(1) << p
rp.SIO.GPIO_OUT_SET.Set(mask)
}
}
func (p Pin) PortMaskClear() (*uint32, uint32) {
@@ -73,18 +78,31 @@ func (p Pin) PortMaskClear() (*uint32, uint32) {
// clr drives the pin low
func (p Pin) clr() {
mask := uint32(1) << p
rp.SIO.GPIO_OUT_CLR.Set(mask)
if is48Pin && p >= 32 {
mask := uint32(1) << (p % 32)
rp.SIO.GPIO_HI_OUT_CLR.Set(mask)
} else {
mask := uint32(1) << p
rp.SIO.GPIO_OUT_CLR.Set(mask)
}
}
// xor toggles the pin
func (p Pin) xor() {
mask := uint32(1) << p
rp.SIO.GPIO_OUT_XOR.Set(mask)
if is48Pin && p >= 32 {
mask := uint32(1) << (p % 32)
rp.SIO.GPIO_HI_OUT_XOR.Set(mask)
} else {
mask := uint32(1) << p
rp.SIO.GPIO_OUT_XOR.Set(mask)
}
}
// get returns the pin value
func (p Pin) get() bool {
if is48Pin && p >= 32 {
return rp.SIO.GPIO_HI_IN.HasBits(1 << (p % 32))
}
return rp.SIO.GPIO_IN.HasBits(1 << p)
}
@@ -134,8 +152,13 @@ func (p Pin) setFunc(fn pinFunc) {
// init initializes the gpio pin
func (p Pin) init() {
mask := uint32(1) << p
rp.SIO.GPIO_OE_CLR.Set(mask)
if is48Pin && p >= 32 {
mask := uint32(1) << (p % 32)
rp.SIO.GPIO_HI_OE_CLR.Set(mask)
} else {
mask := uint32(1) << p
rp.SIO.GPIO_OE_CLR.Set(mask)
}
p.clr()
}
+1 -7
View File
@@ -1,4 +1,4 @@
//go:build rp2040 || rp2350 || ae_rp2040 || badger2040 || challenger_rp2040 || feather_rp2040 || gopher_badge || kb2040 || macropad_rp2040 || nano_rp2040 || pico || qtpy_rp2040 || thingplus_rp2040 || thumby || tufty2040 || waveshare_rp2040_zero || xiao_rp2040
//go:build rp2040 || rp2350 || gopher_badge
package machine
@@ -34,10 +34,4 @@ const (
GPIO27 Pin = 27 // peripherals: PWM5 channel B
GPIO28 Pin = 28 // peripherals: PWM6 channel A
GPIO29 Pin = 29 // peripherals: PWM6 channel B
// Analog pins
ADC0 Pin = GPIO26
ADC1 Pin = GPIO27
ADC2 Pin = GPIO28
ADC3 Pin = GPIO29
)
+183 -4
View File
@@ -4,6 +4,9 @@ package machine
import (
"device/rp"
"errors"
"math"
"math/bits"
"runtime/volatile"
"unsafe"
)
@@ -29,12 +32,11 @@ var (
// Post Divider 1, postDiv1 with range 1-7 and be >= postDiv2.
//
// Post Divider 2, postDiv2 with range 1-7.
func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
func (pll *pll) init(refdiv, fbdiv, postDiv1, postDiv2 uint32) {
refFreq := xoscFreq / refdiv
// What are we multiplying the reference clock by to get the vco freq
// (The regs are called div, because you divide the vco output and compare it to the refclk)
fbdiv := vcoFreq / (refFreq * MHz)
// Check fbdiv range
if !(fbdiv >= 16 && fbdiv <= 320) {
@@ -54,13 +56,14 @@ func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
panic("postdiv1 should be greater than or equal to postdiv2")
}
// Check that reference frequency is no greater than vco / 16
// Check that reference frequency is no greater than vcoFreq / 16
vcoFreq := calcVCO(xoscFreq, fbdiv, refdiv)
if refFreq > vcoFreq/16 {
panic("reference frequency should not be greater than vco frequency divided by 16")
}
// div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
pdiv := postDiv1<<rp.PLL_SYS_PRIM_POSTDIV1_Pos | postDiv2<<rp.PLL_SYS_PRIM_POSTDIV2_Pos
pdiv := uint32(postDiv1)<<rp.PLL_SYS_PRIM_POSTDIV1_Pos | uint32(postDiv2)<<rp.PLL_SYS_PRIM_POSTDIV2_Pos
if pll.cs.HasBits(rp.PLL_SYS_CS_LOCK) &&
refdiv == pll.cs.Get()&rp.PLL_SYS_CS_REFDIV_Msk &&
@@ -98,3 +101,179 @@ func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
pll.pwr.ClearBits(rp.PLL_SYS_PWR_POSTDIVPD)
}
var errVCOOverflow = errors.New("VCO calculation overflow; use lower MHz")
// pllSearch enables searching for a good PLL configuration.
// Example for 12MHz crystal and RP2040:
//
// fbdiv, refdiv, pd1, pd2, _ := pllSearch{LockRefDiv:1}.CalcDivs(12*MHz, 125*MHz, MHz)
//
// Example for 12MHz crystal and RP2350:
//
// fbdiv, refdiv, pd1, pd2, _ := pllSearch{LockRefDiv:1}.CalcDivs(12*MHz, 133*MHz, MHz)
type pllSearch struct {
LowerVCO bool
LockRefDiv uint8
}
func (ps pllSearch) CalcDivs(xoscRef, targetFreq, MHz uint64) (fbdiv uint64, refdiv, pd1, pd2 uint8, err error) {
genTable()
var bestFreq, bestFbdiv uint64
var bestRefdiv, bestpd1, bestpd2 uint8
maxVCO, minVCO := 1600*MHz, 750*MHz
var bestMargin int64 = int64(maxVCO)
iters := 0
for refdiv = 1; refdiv < 64; refdiv++ {
if ps.LockRefDiv != 0 && refdiv != ps.LockRefDiv {
continue
}
firstFBDiv := minVCO * uint64(refdiv) / xoscRef
for fbdiv = firstFBDiv; fbdiv < 321; fbdiv++ {
overflow, vco := bits.Mul64(xoscRef, fbdiv)
vco /= uint64(refdiv)
if overflow != 0 {
return fbdiv, refdiv, pd1, pd2, errVCOOverflow
} else if vco > maxVCO {
break
}
calcPD12 := vco / targetFreq
if calcPD12 < 1 {
calcPD12 = 1
} else if calcPD12 > 49 {
calcPD12 = 49
}
iters++
pd1 = pdTable[calcPD12].hivco[0]
pd2 = pdTable[calcPD12].hivco[1]
fout, err := pllFreqOutPostdiv(xoscRef, fbdiv, MHz, refdiv, pd1, pd2)
found := false
margin := abs(int64(fout) - int64(targetFreq))
if err == nil && margin <= bestMargin {
found = true
bestFreq = fout
bestFbdiv = fbdiv
bestpd1 = pd1
bestpd2 = pd2
bestRefdiv = refdiv
bestMargin = margin
}
pd1 = pdTable[calcPD12].lovco[0]
pd2 = pdTable[calcPD12].lovco[1]
fout, err = pllFreqOutPostdiv(xoscRef, fbdiv, MHz, refdiv, pd1, pd2)
margin = abs(int64(fout) - int64(targetFreq))
if err == nil && margin <= bestMargin {
found = true
bestFreq = fout
bestFbdiv = fbdiv
bestpd1 = pd1
bestpd2 = pd2
bestRefdiv = refdiv
bestMargin = margin
}
if found && ps.LowerVCO {
break
}
}
}
if bestFreq == 0 {
return fbdiv, refdiv, pd1, pd2, errors.New("no best frequency found")
}
return bestFbdiv, bestRefdiv, bestpd1, bestpd2, nil
}
func abs(a int64) int64 {
if a == math.MinInt64 {
return math.MaxInt64
} else if a < 0 {
return -a
}
return a
}
func pllFreqOutPostdiv(xosc, fbdiv, MHz uint64, refdiv, postdiv1, postdiv2 uint8) (foutpostdiv uint64, err error) {
// testing grounds.
const (
mhz = 1
cfref = 12 * mhz // given by crystal oscillator selection.
crefd = 1
cfbdiv = 100
cvco = cfref * cfbdiv / crefd
cpd1 = 6
cpd2 = 2
foutpd = (cfref / crefd) * cfbdiv / (cpd1 * cpd2)
)
refFreq := xosc / uint64(refdiv)
overflow, vco := bits.Mul64(xosc, fbdiv)
vco /= uint64(refdiv)
foutpostdiv = vco / uint64(postdiv1*postdiv2)
switch {
case refdiv < 1 || refdiv > 63:
err = errors.New("reference divider out of range")
case fbdiv < 16 || fbdiv > 320:
err = errors.New("feedback divider out of range")
case postdiv1 < 1 || postdiv1 > 7:
err = errors.New("postdiv1 out of range")
case postdiv2 < 1 || postdiv2 > 7:
err = errors.New("postdiv2 out of range")
case postdiv1 < postdiv2:
err = errors.New("user error: use higher value for postdiv1 for lower power consumption")
case vco < 750*MHz || vco > 1600*MHz:
err = errors.New("VCO out of range")
case refFreq < 5*MHz:
err = errors.New("minimum reference frequency breach")
case refFreq > vco/16:
err = errors.New("maximum reference frequency breach")
case vco > 1200*MHz && vco < 1600*MHz && xosc < 75*MHz && refdiv != 1:
err = errors.New("refdiv should be 1 for given VCO and reference frequency")
case overflow != 0:
err = errVCOOverflow
}
if err != nil {
return 0, err
}
return foutpostdiv, nil
}
func calcVCO(xoscFreq, fbdiv, refdiv uint32) uint32 {
const maxXoscMHz = math.MaxUint32 / 320 / MHz // 13MHz maximum xosc apparently.
if fbdiv > 320 || xoscFreq > math.MaxUint32/320 {
panic("invalid VCO calculation args")
}
return xoscFreq * fbdiv / refdiv
}
var pdTable = [50]struct {
hivco [2]uint8
lovco [2]uint8
}{}
func genTable() {
if pdTable[1].hivco[1] != 0 {
return // Already generated.
}
for product := 1; product < len(pdTable); product++ {
bestProdhi := 255
bestProdlo := 255
for pd1 := 7; pd1 > 0; pd1-- {
for pd2 := pd1; pd2 > 0; pd2-- {
gotprod := pd1 * pd2
if abs(int64(gotprod-product)) < abs(int64(bestProdlo-product)) {
bestProdlo = gotprod
pdTable[product].lovco[0] = uint8(pd1)
pdTable[product].lovco[1] = uint8(pd2)
}
}
}
for pd1 := 1; pd1 < 8; pd1++ {
for pd2 := 1; pd2 <= pd1; pd2++ {
gotprod := pd1 * pd2
if abs(int64(gotprod-product)) < abs(int64(bestProdhi-product)) {
bestProdhi = gotprod
pdTable[product].hivco[0] = uint8(pd1)
pdTable[product].hivco[1] = uint8(pd2)
}
}
}
}
}
@@ -1,4 +1,4 @@
//go:build rp2040
//go:build rp2040 || rp2350
package machine
@@ -15,7 +15,7 @@ var (
)
const (
maxPWMPins = 29
maxPWMPins = _NUMBANK0_GPIOS - 1
)
// pwmGroup is one PWM peripheral, which consists of a counter and two output
@@ -146,12 +146,14 @@ func (p *pwmGroup) Counter() uint32 {
// Period returns the used PWM period in nanoseconds.
func (p *pwmGroup) Period() uint64 {
periodPerCycle := cpuPeriod()
// Lines below can overflow if operations done without care.
// maxInt=255, maxFrac=15, maxTop=65536, maxPHC=1 => maxProduct= (16*255+15) * (65536*2*1e9) = 5.3673e17 < MaxUint64=1.8e19 (close call.)
const compileTimeCheckPeriod uint64 = (255*16 + 15) * (65535 + 1) * 2 * 1e9
freq := uint64(CPUFrequency())
top := p.getWrap()
phc := p.getPhaseCorrect()
Int, frac := p.getClockDiv()
// Line below can overflow if operations done without care.
return (16*uint64(Int) + uint64(frac)) * uint64((top+1)*(phc+1)*periodPerCycle) / 16 // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
return (16*uint64(Int) + uint64(frac)) * uint64((top+1)*(phc+1)*1e9) / (16 * freq) // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
}
// SetInverting sets whether to invert the output of this channel.
@@ -266,6 +268,9 @@ func (pwm *pwmGroup) setPeriod(period uint64) error {
// Maximum Period is 268369920ns on rp2040, given by (16*255+15)*8*(1+0xffff)*(1+1)/16
// With no phase shift max period is half of this value.
maxPeriod = 268 * milliseconds
// This will be a compile time error if this method is at risk of overflowing. cpufreq=155MHz for typical RP2350.
maxCPUFreq = 4 * GHz // Can go up to 4GHz without overflowing :)
compileTimeCheckSetPeriod uint64 = 16 * maxPeriod * maxCPUFreq
)
if period > maxPeriod || period < 8 {
@@ -279,11 +284,13 @@ func (pwm *pwmGroup) setPeriod(period uint64) error {
// DIV_INT + DIV_FRAC/16 = cycles / ( (TOP+1) * (CSRPHCorrect+1) ) // DIV_FRAC/16 is always 0 in this equation
// where cycles must be converted to time:
// target_period = cycles * period_per_cycle ==> cycles = target_period/period_per_cycle
periodPerCycle := uint64(cpuPeriod())
phc := uint64(pwm.getPhaseCorrect())
rhs := 16 * period / ((1 + phc) * periodPerCycle * (1 + topStart)) // right-hand-side of equation, scaled so frac is not divided
whole := rhs / 16
frac := rhs % 16
var (
freq = uint64(CPUFrequency())
phc = uint64(pwm.getPhaseCorrect())
rhs = 16 * period * freq / ((1 + phc) * 1e9 * (1 + topStart)) // right-hand-side of equation, scaled so frac is not divided
whole = rhs / 16
frac = rhs % 16
)
switch {
case whole > 0xff:
whole = 0xff
@@ -296,7 +303,7 @@ func (pwm *pwmGroup) setPeriod(period uint64) error {
// Step 2 is acquiring a better top value. Clearing the equation:
// TOP = cycles / ( (DIVINT+DIVFRAC/16) * (CSRPHCorrect+1) ) - 1
top := 16*period/((16*whole+frac)*periodPerCycle*(1+phc)) - 1
top := 16*period*freq/((1+phc)*1e9*(16*whole+frac)) - 1
if top > maxTop {
top = maxTop
}
@@ -400,6 +407,9 @@ func (pwm *pwmGroup) getClockDiv() (Int, frac uint8) {
// pwmGPIOToSlice Determine the PWM channel that is attached to the specified GPIO.
// gpio must be less than 30. Returns the PWM slice number that controls the specified GPIO.
func pwmGPIOToSlice(gpio Pin) (slicenum uint8) {
if is48Pin && gpio >= 32 {
return uint8(8 + ((gpio-32)/2)%4)
}
return (uint8(gpio) >> 1) & 7
}
+1 -1
Submodule src/net updated: a237059610...ca7cd08f85
+11
View File
@@ -307,6 +307,17 @@ func (f *File) Sync() (err error) {
return
}
// Chmod changes the mode of the file to mode. If there is an error, it will be
// of type *PathError.
func (f *File) Chmod(mode FileMode) (err error) {
if f.handle == nil {
err = ErrClosed
} else {
err = f.chmod(mode)
}
return
}
// LinkError records an error during a link or symlink or rename system call and
// the paths that caused it.
type LinkError struct {
+4
View File
@@ -149,3 +149,7 @@ func (f *File) Truncate(size int64) (err error) {
return Truncate(f.name, size)
}
func (f *File) chmod(mode FileMode) error {
return ErrUnsupported
}
+15
View File
@@ -151,6 +151,21 @@ func (f *File) Truncate(size int64) (err error) {
return Truncate(f.name, size)
}
func (f *File) chmod(mode FileMode) error {
if f.handle == nil {
return ErrClosed
}
longName := fixLongPath(f.name)
e := ignoringEINTR(func() error {
return syscall.Chmod(longName, syscallMode(mode))
})
if e != nil {
return &PathError{Op: "chmod", Path: f.name, Err: e}
}
return nil
}
// ReadAt reads up to len(b) bytes from the File starting at the given absolute offset.
// It returns the number of bytes read and any error encountered, possibly io.EOF.
// At end of file, Pread returns 0, io.EOF.
+4
View File
@@ -142,3 +142,7 @@ func isWindowsNulName(name string) bool {
}
return true
}
func (f *File) chmod(mode FileMode) error {
return ErrNotImplemented
}
+3 -3
View File
@@ -23,13 +23,13 @@ func fastrand() uint32 {
return xorshift32State
}
func init() {
func initRand() {
r, _ := hardwareRand()
xorshift64State = uint64(r | 1) // protect against 0
xorshift32State = uint32(xorshift64State)
}
var xorshift32State uint32
var xorshift32State uint32 = 1
func xorshift32(x uint32) uint32 {
// Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs".
@@ -49,7 +49,7 @@ func fastrand64() uint64 {
return xorshift64State
}
var xorshift64State uint64
var xorshift64State uint64 = 1
// 64-bit xorshift multiply rng from http://vigna.di.unimi.it/ftp/papers/xorshift.pdf
func xorshiftMult64(x uint64) uint64 {
+10
View File
@@ -35,6 +35,7 @@ func wasmEntryReactor() {
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
initHeap()
initRand()
if hasScheduler {
// A package initializer might do funky stuff like start a goroutine and
@@ -51,6 +52,15 @@ func wasmEntryReactor() {
}
}
// This is the _start entry point, when using -buildmode=wasi-legacy.
func wasmEntryLegacy() {
// These need to be initialized early so that the heap can be initialized.
initializeCalled = true
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
run()
}
// Whether the runtime was initialized by a call to _initialize or _start.
var initializeCalled bool
+1
View File
@@ -247,6 +247,7 @@ func sleep(duration int64) {
// With a scheduler, init and the main function are invoked in a goroutine before starting the scheduler.
func run() {
initHeap()
initRand()
go func() {
initAll()
callMain()
+1
View File
@@ -13,6 +13,7 @@ const hasParallelism = false
// With the "none" scheduler, init and the main function are invoked directly.
func run() {
initHeap()
initRand()
initAll()
callMain()
mainExited = true
+1 -1
View File
@@ -1,6 +1,6 @@
{
"inherits": ["rp2040"],
"build-tags": ["elecrow_rp2040"],
"build-tags": ["elecrow_rp2040", "comboat_fw"],
"serial-port": ["2e8a:000a"],
"default-stack-size": 8192,
"ldflags": [
+1 -1
View File
@@ -1,6 +1,6 @@
{
"inherits": ["rp2350"],
"build-tags": ["elecrow_rp2350"],
"build-tags": ["elecrow_rp2350", "comboat_fw"],
"serial-port": ["2e8a:000f"],
"default-stack-size": 8192,
"ldflags": [
+4
View File
@@ -0,0 +1,4 @@
{
"inherits": ["rp2350b"],
"build-tags": ["pga2350"]
}
+11
View File
@@ -0,0 +1,11 @@
{
"inherits": [
"rp2350b"
],
"build-tags": ["pico_plus2"],
"ldflags": [
"--defsym=__flash_size=16M"
],
"serial-port": ["2e8a:000F"],
"default-stack-size": 8192
}
+5
View File
@@ -0,0 +1,5 @@
{
"inherits": ["rp2350"],
"build-tags": ["rp2350b"],
"serial-port": ["2e8a:000f"]
}
+14 -3
View File
@@ -303,9 +303,20 @@
// func finalizeRef(v ref)
"syscall/js.finalizeRef": (v_ref) => {
// Note: TinyGo does not support finalizers so this should never be
// called.
console.error('syscall/js.finalizeRef not implemented');
// Note: TinyGo does not support finalizers so this is only called
// for one specific case, by js.go:jsString. and can/might leak memory.
const id = v_ref & 0xffffffffn;
if (this._goRefCounts?.[id] !== undefined) {
this._goRefCounts[id]--;
if (this._goRefCounts[id] === 0) {
const v = this._values[id];
this._values[id] = null;
this._ids.delete(v);
this._idPool.push(id);
}
} else {
console.error("syscall/js.finalizeRef: unknown id", id);
}
},
// func stringVal(value string) ref