Compare commits

..

2 Commits

Author SHA1 Message Date
BCG 6b6b2bfc59 Adding extra files for testing multiline, globbing, etc. 2021-07-23 17:07:25 -04:00
BCG 778c79cc85 Started parsing of //go:embed pragmas 2021-07-19 20:12:44 -04:00
159 changed files with 987 additions and 4680 deletions
+21 -7
View File
@@ -286,8 +286,8 @@ commands:
- run:
name: "Install dependencies"
command: |
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.17.darwin-amd64.tar.gz
curl https://dl.google.com/go/go1.16.darwin-amd64.tar.gz -o go1.16.darwin-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.16.darwin-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
- install-xtensa-toolchain:
@@ -365,12 +365,24 @@ commands:
- /go/pkg/mod
jobs:
test-llvm10-go115:
test-llvm10-go113:
docker:
- image: circleci/golang:1.13-buster
steps:
- test-linux:
llvm: "10"
test-llvm10-go114:
docker:
- image: circleci/golang:1.14-buster
steps:
- test-linux:
llvm: "10"
test-llvm11-go115:
docker:
- image: circleci/golang:1.15-buster
steps:
- test-linux:
llvm: "10"
llvm: "11"
test-llvm11-go116:
docker:
- image: circleci/golang:1.16-buster
@@ -379,12 +391,12 @@ jobs:
llvm: "11"
assert-test-linux:
docker:
- image: circleci/golang:1.17-stretch
- image: circleci/golang:1.14-stretch
steps:
- assert-test-linux
build-linux:
docker:
- image: circleci/golang:1.17-stretch
- image: circleci/golang:1.14-stretch
steps:
- build-linux
build-macos:
@@ -398,7 +410,9 @@ jobs:
workflows:
test-all:
jobs:
- test-llvm10-go115
- test-llvm10-go113
- test-llvm10-go114
- test-llvm11-go115
- test-llvm11-go116
- build-linux
- build-macos
-3
View File
@@ -1,3 +0,0 @@
build/
llvm-*/
@@ -1,7 +1,7 @@
name: CI for tinygo-dev docker container
on:
push:
branches: [ dev, fix-docker-llvm-build ]
branches: [ dev ]
jobs:
push_to_registry:
+1 -1
View File
@@ -23,7 +23,7 @@ different guide:
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
build tools to be built. Go is of course necessary to build TinyGo itself.
* Go (1.15+)
* Go (1.13+)
* Standard build tools (gcc/clang)
* git
* CMake
-70
View File
@@ -1,73 +1,3 @@
0.20.0
---
* **command line**
- add support for Go 1.17
- improve Go version detection
- add support for the Black Magic Probe (BMP)
- add a flag for creating cpu profiles
* **compiler**
- `builder:` list libraries at the end of the linker command
- `builder:` strip debug information at link time instead of at compile time
- `builder:` add missing error check for `ioutil.TempFile()`
- `builder:` simplify running of jobs
- `compiler:` move LLVM math builtin support into the compiler
- `compiler:` move math aliases from the runtime to the compiler
- `compiler:` add aliases for many hashing packages
- `compiler:` add `*ssa.MakeSlice` bounds tests
- `compiler:` fix max possible slice
- `compiler:` add support for new language features of Go 1.17
- `compiler:` fix equally named structs in different scopes
- `compiler:` avoid zero-sized alloca in channel operations
- `interp:` don't ignore array indices for untyped objects
- `interp:` keep reverted package initializers in order
- `interp:` fix bug in compiler-time/run-time package initializers
- `loader:` fix panic in CGo files with syntax errors
- `transform:` improve GC stack slot pass to work around a bug
* **standard library**
- `crypto/rand`: switch to `arc4random_buf`
- `math:` fix `math.Max` and `math.Min`
- `math/big`: fix undefined symbols error
- `net:` add MAC address implementation
- `os:` implement `os.Executable`
- `os:` add `SEEK_SET`, `SEEK_CUR`, and `SEEK_END`
- `reflect:` add StructField.IsExported method
- `runtime:` reset heapptr to heapStart after preinit()
- `runtime:` add `subsections_via_symbols` to assembly files on darwin
- `testing:` add subset implementation of Benchmark
- `testing:` test testing package using `tinygo test`
- `testing:` add support for the `-test.v` flag
* **targets**
- `386:` bump minimum requirement to the Pentium 4
- `arm:` switch to Thumb instruction set on ARM
- `atsamd:` fix copy-paste error for atsamd21/51 calibTrim block
- `baremetal`,`wasm`: support command line params and environment variables
- `cortexm:` fix stack overflow because of unaligned stacks
- `esp32c3:` add support for the ESP32-C3 from Espressif
- `nrf52840:` fix ram size
- `nxpmk66f18:` fix a suspicious bitwise operation
- `rp2040:` add SPI support
- `rp2040:` add I2C support
- `rp2040:` add PWM implementation
- `rp2040:` add openocd configuration
- `stm32:` add support for PortMask* functions for WS2812 support
- `unix:` fix time base for time.Now()
- `unix:` check for mmap error and act accordingly
- `wasm:` override dlmalloc heap implementation from wasi-libc
- `wasm:` align heap to 16 bytes
- `wasm:` add support for the crypto/rand package
* **boards**
- add `DefaultUART` to adafruit boards
- `arduino-mkrwifi1010:` add board definition for Arduino MKR WiFi 1010
- `arduino-mkrwifi1010:` fix pin definition of `NINA_RESETN`
- `feather-nrf52:` fix pin definition of uart
- `feather-rp2040:` add pin name definition
- `gameboy-advance:` fix ROM header
- `mdbt50qrx-uf2:` add Raytac MDBT50Q-RX Dongle with TinyUF2
- `nano-rp2040:` define `NINA_SPI` and fix wifinina pins
- `teensy40:` enable hardware UART reconfiguration, fix receive watermark interrupt
0.19.0
---
+3 -3
View File
@@ -1,8 +1,8 @@
# TinyGo base stage installs the most recent Go 1.17.x, LLVM 11 and the TinyGo compiler itself.
FROM golang:1.17 AS tinygo-base
# TinyGo base stage installs the most recent Go 1.16.x, LLVM 11 and the TinyGo compiler itself.
FROM golang:1.16 AS tinygo-base
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-11 main" >> /etc/apt/sources.list && \
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main" >> /etc/apt/sources.list && \
apt-get update && \
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git
+1 -14
View File
@@ -100,7 +100,7 @@ endif
clean:
@rm -rf build
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/testing src/internal/reflectlite transform
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
fmt:
@gofmt -l -w $(FMT_PATHS)
fmt-check:
@@ -124,7 +124,6 @@ build/gen-device-svd: ./tools/gen-device-svd/*.go
gen-device-esp: build/gen-device-svd
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif -interrupts=software lib/cmsis-svd/data/Espressif/ src/device/esp/
GO111MODULE=off $(GO) fmt ./src/device/esp
gen-device-nrf: build/gen-device-svd
@@ -191,10 +190,6 @@ TEST_PACKAGES = \
container/list \
container/ring \
crypto/des \
crypto/md5 \
crypto/sha1 \
crypto/sha256 \
crypto/sha512 \
encoding \
encoding/ascii85 \
encoding/base32 \
@@ -204,7 +199,6 @@ TEST_PACKAGES = \
hash/crc64 \
math \
math/cmplx \
testing \
text/scanner \
unicode/utf8 \
@@ -364,8 +358,6 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-nano33 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-mkrwifi1010 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
@@ -433,8 +425,6 @@ ifneq ($(XTENSA), 0)
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
@$(MD5SUM) test.bin
endif
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=hifive1-qemu examples/serial
@@ -454,9 +444,6 @@ endif
@$(MD5SUM) test.nro
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
@$(MD5SUM) test.hex
ifneq ($(OS),Windows_NT)
$(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial
endif
wasmtest:
+1 -3
View File
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 70 microcontroller boards are currently supported:
The following 68 microcontroller boards are currently supported:
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
@@ -69,7 +69,6 @@ The following 70 microcontroller boards are currently supported:
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
* [Arduino MKR WiFi 1010](https://store.arduino.cc/usa/mkr-wifi-1010)
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble)
* [Arduino Nano 33 BLE Sense](https://store.arduino.cc/nano-33-ble-sense)
@@ -102,7 +101,6 @@ The following 70 microcontroller boards are currently supported:
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
steps:
- task: GoTool@0
inputs:
version: '1.17'
version: '1.16'
- checkout: self
fetchDepth: 1
- task: Cache@2
+22 -39
View File
@@ -100,7 +100,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
AutomaticStackSize: config.AutomaticStackSize(),
DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(),
Debug: true,
Debug: config.Debug(),
LLVMFeatures: config.LLVMFeatures(),
}
@@ -124,6 +124,11 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return err
}
// The slice of jobs that orchestrates most of the build.
// This is somewhat like an in-memory Makefile with each job being a
// Makefile target.
var jobs []*compileJob
// Create the *ssa.Program. This does not yet build the entire SSA of the
// program so it's pretty fast and doesn't need to be parallelized.
program := lprogram.LoadSSA()
@@ -306,6 +311,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return os.Rename(f.Name(), bitcodePath)
},
}
jobs = append(jobs, job)
packageJobs = append(packageJobs, job)
}
@@ -396,13 +402,14 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return nil
},
}
jobs = append(jobs, programJob)
// 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)
if outext == ".o" || outext == ".bc" || outext == ".ll" {
// Run jobs to produce the LLVM module.
err := runJobs(programJob)
err := runJobs(jobs)
if err != nil {
return err
}
@@ -443,6 +450,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
},
}
jobs = append(jobs, outputObjectFileJob)
// Prepare link command.
linkerDependencies := []*compileJob{outputObjectFileJob}
@@ -457,6 +465,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil {
return err
}
jobs = append(jobs, job.dependencies...)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
}
@@ -474,6 +484,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return err
},
}
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
}
@@ -492,6 +503,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return err
},
}
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
}
}
@@ -509,6 +521,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil {
return err
}
// The library needs to be compiled (cache miss).
jobs = append(jobs, job.dependencies...)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
case "wasi-libc":
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
@@ -516,6 +531,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
}
job := dummyCompileJob(path)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
case "":
// no library specified, so nothing to do
@@ -523,42 +539,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
}
// Strip debug information with -no-debug.
if !config.Debug() {
for _, tag := range config.BuildTags() {
if tag == "baremetal" {
// Don't use -no-debug on baremetal targets. It makes no sense:
// the debug information isn't flashed to the device anyway.
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
}
}
if config.Target.Linker == "wasm-ld" {
// Don't just strip debug information, also compress relocations
// while we're at it. Relocations can only be compressed when debug
// information is stripped.
ldflags = append(ldflags, "--strip-debug", "--compress-relocations")
} else {
switch config.GOOS() {
case "linux":
// Either real linux or an embedded system (like AVR) that
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
// case.
ldflags = append(ldflags, "-Wl,--strip-debug")
case "darwin":
// MacOS (darwin) doesn't have a linker flag to strip debug
// information. Apple expects you to use the strip command
// instead.
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
default:
// Other OSes may have different flags.
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
}
}
}
// Create a linker job, which links all object files together and does some
// extra stuff that can only be done after linking.
linkJob := &compileJob{
jobs = append(jobs, &compileJob{
description: "link",
dependencies: linkerDependencies,
run: func(job *compileJob) error {
@@ -631,12 +614,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return nil
},
}
})
// Run all jobs to compile and link the program.
// Do this now (instead of after elf-to-hex and similar conversions) as it
// is simpler and cannot be parallelized.
err = runJobs(linkJob)
err = runJobs(jobs)
if err != nil {
return err
}
@@ -661,7 +644,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil {
return err
}
case "esp32", "esp32c3", "esp8266":
case "esp32", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM
// bootloader).
tmppath = filepath.Join(dir, "main"+outext)
-4
View File
@@ -155,10 +155,6 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
// Write dependencies file.
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
if err != nil {
return "", err
}
buf, err = json.MarshalIndent(dependencySlice, "", "\t")
if err != nil {
panic(err) // shouldn't happen
+2 -2
View File
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
}
if major != 1 || minor < 15 || minor > 17 {
return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
if major != 1 || minor < 13 || minor > 16 {
return nil, fmt.Errorf("requires go version 1.13 through 1.16, got go%d.%d", major, minor)
}
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
+6 -19
View File
@@ -78,21 +78,11 @@ func makeESPFirmareImage(infile, outfile, format string) error {
// An added benefit is that we don't need to check for errors all the time.
outf := &bytes.Buffer{}
// Chip IDs. Source:
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
chip_id := map[string]uint16{
"esp32": 0x0000,
"esp32c3": 0x0005,
}[format]
// Image header.
switch format {
case "esp32", "esp32c3":
case "esp32":
// 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
// esptool.py while flashing and therefore the hash won't be valid
// anymore.
// https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
binary.Write(outf, binary.LittleEndian, struct {
magic uint8
segment_count uint8
@@ -101,18 +91,15 @@ func makeESPFirmareImage(infile, outfile, format string) error {
entry_addr uint32
wp_pin uint8
spi_pin_drv [3]uint8
chip_id uint16
min_chip_rev uint8
reserved [8]uint8
reserved [11]uint8
hash_appended bool
}{
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: 0, // irrelevant, replaced by esptool when flashing
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
entry_addr: uint32(inf.Entry),
wp_pin: 0xEE, // disable WP pin
chip_id: chip_id,
hash_appended: true, // add a SHA256 hash
})
case "esp8266":
@@ -155,7 +142,7 @@ func makeESPFirmareImage(infile, outfile, format string) error {
outf.Write(make([]byte, 15-outf.Len()%16))
outf.WriteByte(checksum)
if format != "esp8266" {
if format == "esp32" {
// SHA256 hash (to protect against image corruption, not for security).
hash := sha256.Sum256(outf.Bytes())
outf.Write(hash[:])
+6 -23
View File
@@ -65,29 +65,12 @@ func (job *compileJob) readyToRun() bool {
return true
}
// runJobs runs the indicated job and all its dependencies. For every job, all
// the dependencies are run first. It returns the error of the first job that
// fails.
// It runs all jobs in the order of the dependencies slice, depth-first.
// Therefore, if some jobs are preferred to run before others, they should be
// ordered as such in the job dependencies.
func runJobs(job *compileJob) error {
// Create a slice of jobs to run, where all dependencies are run in order.
jobs := []*compileJob{}
addedJobs := map[*compileJob]struct{}{}
var addJobs func(*compileJob)
addJobs = func(job *compileJob) {
if _, ok := addedJobs[job]; ok {
return
}
for _, dep := range job.dependencies {
addJobs(dep)
}
jobs = append(jobs, job)
addedJobs[job] = struct{}{}
}
addJobs(job)
// runJobs runs all the jobs indicated in the jobs slice and returns the error
// of the first job that fails to run.
// It runs all jobs in the order of the slice, as long as all dependencies have
// already run. Therefore, if some jobs are preferred to run before others, they
// should be ordered as such in this slice.
func runJobs(jobs []*compileJob) error {
// Create channels to communicate with the workers.
doneChan := make(chan *compileJob)
workerChan := make(chan *compileJob)
+4 -3
View File
@@ -46,14 +46,15 @@ func (l *Library) Load(target, tmpdir string) (path string, err error) {
if err != nil {
return "", err
}
err = runJobs(job)
jobs := append([]*compileJob{job}, job.dependencies...)
err = runJobs(jobs)
return job.result, err
}
// load returns a compile job to build this library file for the given target
// and CPU. It may return a dummy compileJob if the library build is already
// cached. The path is stored as job.result but is only valid after the job has
// been run.
// cached. The path is stored as job.result but is only valid if the job and
// job.dependencies have been run.
// The provided tmpdir will be used to store intermediary files and possibly the
// output archive file, it is expected to be removed after use.
func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) {
+9
View File
@@ -12,6 +12,7 @@ import (
"go/types"
"io/ioutil"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"
@@ -24,6 +25,14 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
// platforms and Go versions.
func normalizeResult(result string) string {
actual := strings.ReplaceAll(result, "\r\n", "\n")
// Make sure all functions are wrapped, even those that would otherwise be
// single-line functions. This is necessary because Go 1.14 changed the way
// such functions are wrapped and it's important to have consistent test
// results.
re := regexp.MustCompile(`func \((.+)\)( .*?) +{ (.+) }`)
actual = re.ReplaceAllString(actual, "func ($1)$2 {\n\t$3\n}")
return actual
}
+15 -5
View File
@@ -67,7 +67,9 @@ type C.union3_t = C.union_1
type C.union_nested_t = C.union_3
type C.unionarray_t = struct{ arr [10]C.uchar }
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
func (s *C.struct_4) bitfield_a() C.uchar {
return s.__bitfield_1 & 0x1f
}
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
}
@@ -103,9 +105,15 @@ type C.struct_type1 struct {
}
type C.struct_type2 struct{ _type C.int }
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
func (union *C.union_1) unionfield_i() *C.int {
return (*C.int)(unsafe.Pointer(&union.$union))
}
func (union *C.union_1) unionfield_d() *float64 {
return (*float64)(unsafe.Pointer(&union.$union))
}
func (union *C.union_1) unionfield_s() *C.short {
return (*C.short)(unsafe.Pointer(&union.$union))
}
type C.union_1 struct{ $union uint64 }
@@ -130,7 +138,9 @@ func (union *C.union_3) unionfield_thing() *C.union3_t {
type C.union_3 struct{ $union [2]uint64 }
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (union *C.union_union2d) unionfield_i() *C.int {
return (*C.int)(unsafe.Pointer(&union.$union))
}
func (union *C.union_union2d) unionfield_d() *[2]float64 {
return (*[2]float64)(unsafe.Pointer(&union.$union))
}
+6 -9
View File
@@ -55,7 +55,7 @@ func (c *Config) GOARCH() string {
// BuildTags returns the complete list of build tags used during this build.
func (c *Config) BuildTags() []string {
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
tags := append(c.Target.BuildTags, []string{"tinygo", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
for i := 1; i <= c.GoMinorVersion; i++ {
tags = append(tags, fmt.Sprintf("go1.%d", i))
}
@@ -209,8 +209,9 @@ func (c *Config) CFlags() []string {
cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include"))
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
}
// Always emit debug information. It is optionally stripped at link time.
cflags = append(cflags, "-g")
if c.Debug() {
cflags = append(cflags, "-g")
}
return cflags
}
@@ -249,9 +250,8 @@ func (c *Config) VerifyIR() bool {
return c.Options.VerifyIR
}
// Debug returns whether debug (DWARF) information should be retained by the
// linker. By default, debug information is retained but it can be removed with
// the -no-debug flag.
// Debug returns whether to add debug symbols to the IR, for debugging with GDB
// and similar.
func (c *Config) Debug() bool {
return c.Options.Debug
}
@@ -297,9 +297,6 @@ func (c *Config) Programmer() (method, openocdInterface string) {
case "openocd", "msd", "command":
// The -programmer flag only specifies the flash method.
return c.Options.Programmer, c.Target.OpenOCDInterface
case "bmp":
// The -programmer flag only specifies the flash method.
return c.Options.Programmer, ""
default:
// The -programmer flag specifies something else, assume it specifies
// the OpenOCD interface name.
-4
View File
@@ -172,7 +172,6 @@ func LoadTarget(target string) (*TargetSpec, error) {
"386": "i386",
"amd64": "x86_64",
"arm64": "aarch64",
"arm": "thumbv7",
}[goarch]
if llvmarch == "" {
llvmarch = goarch
@@ -254,9 +253,6 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
GDB: []string{"gdb"},
PortReset: "false",
}
if goarch == "386" {
spec.CPU = "pentium4"
}
if goos == "darwin" {
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
-99
View File
@@ -1,99 +0,0 @@
package compiler
// This file defines alias functions for functions that are normally defined in
// Go assembly.
//
// The Go toolchain defines many performance critical functions in assembly
// instead of plain Go. This is a problem for TinyGo as it currently (as of
// august 2021) is not able to compile these assembly files and even if it
// could, it would not be able to make use of them for many targets that are
// supported by TinyGo (baremetal RISC-V, AVR, etc). Therefore, many of these
// functions are aliased to their generic Go implementation.
// This results in slower than possible implementations, but at least they are
// usable.
import "tinygo.org/x/go-llvm"
var stdlibAliases = map[string]string{
// crypto packages
"crypto/md5.block": "crypto/md5.blockGeneric",
"crypto/sha1.block": "crypto/sha1.blockGeneric",
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
"crypto/sha256.block": "crypto/sha256.blockGeneric",
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
// math package
"math.Asin": "math.asin",
"math.Asinh": "math.asinh",
"math.Acos": "math.acos",
"math.Acosh": "math.acosh",
"math.Atan": "math.atan",
"math.Atanh": "math.atanh",
"math.Atan2": "math.atan2",
"math.Cbrt": "math.cbrt",
"math.Ceil": "math.ceil",
"math.archCeil": "math.ceil",
"math.Cos": "math.cos",
"math.Cosh": "math.cosh",
"math.Erf": "math.erf",
"math.Erfc": "math.erfc",
"math.Exp": "math.exp",
"math.archExp": "math.exp",
"math.Expm1": "math.expm1",
"math.Exp2": "math.exp2",
"math.archExp2": "math.exp2",
"math.Floor": "math.floor",
"math.archFloor": "math.floor",
"math.Frexp": "math.frexp",
"math.Hypot": "math.hypot",
"math.archHypot": "math.hypot",
"math.Ldexp": "math.ldexp",
"math.Log": "math.log",
"math.archLog": "math.log",
"math.Log1p": "math.log1p",
"math.Log10": "math.log10",
"math.Log2": "math.log2",
"math.Max": "math.max",
"math.archMax": "math.max",
"math.Min": "math.min",
"math.archMin": "math.min",
"math.Mod": "math.mod",
"math.Modf": "math.modf",
"math.archModf": "math.modf",
"math.Pow": "math.pow",
"math.Remainder": "math.remainder",
"math.Sin": "math.sin",
"math.Sinh": "math.sinh",
"math.Sqrt": "math.sqrt",
"math.archSqrt": "math.sqrt",
"math.Tan": "math.tan",
"math.Tanh": "math.tanh",
"math.Trunc": "math.trunc",
"math.archTrunc": "math.trunc",
}
// createAlias implements the function (in the builder) as a call to the alias
// function.
func (b *builder) createAlias(alias llvm.Value) {
if b.Debug {
if b.fn.Syntax() != nil {
// Create debug info file if present.
b.difunc = b.attachDebugInfo(b.fn)
}
pos := b.program.Fset.Position(b.fn.Pos())
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
}
entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry")
b.SetInsertPointAtEnd(entryBlock)
if b.llvmFn.Type() != alias.Type() {
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
b.CreateUnreachable()
return
}
result := b.CreateCall(alias, b.llvmFn.Params(), "")
if result.Type().TypeKind() == llvm.VoidTypeKind {
b.CreateRetVoid()
} else {
b.CreateRet(result)
}
}
-49
View File
@@ -101,55 +101,6 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic")
}
// createSliceToArrayPointerCheck adds a check for slice-to-array pointer
// conversions. This conversion was added in Go 1.17. For details, see:
// https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer
func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen int64) {
// From the spec:
// > If the length of the slice is less than the length of the array, a
// > run-time panic occurs.
arrayLenValue := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
isLess := b.CreateICmp(llvm.IntULT, sliceLen, arrayLenValue, "")
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
}
// createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
// function must panic if the ptr/len parameters are invalid.
func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
// From the documentation of unsafe.Slice:
// > At run time, if len is negative, or if ptr is nil and len is not
// > zero, a run-time panic occurs.
// However, in practice, it is also necessary to check that the length is
// not too big that a GEP wouldn't be possible without wrapping the pointer.
// These two checks (non-negative and not too big) can be merged into one
// using an unsiged greater than.
// Make sure the len value is at least as big as a uintptr.
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
if lenType.Info()&types.IsUnsigned != 0 {
len = b.CreateZExt(len, b.uintptrType, "")
} else {
len = b.CreateSExt(len, b.uintptrType, "")
}
}
// Determine the maximum slice size, and therefore the maximum value of the
// len parameter.
maxSize := b.maxSliceSize(ptr.Type().ElementType())
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
// Do the check. By using unsigned greater than for the length check, signed
// negative values are also checked (which are very large numbers when
// interpreted as signed values).
zero := llvm.ConstInt(len.Type(), 0, false)
lenOutOfBounds := b.CreateICmp(llvm.IntUGT, len, maxSizeValue, "")
ptrIsNil := b.CreateICmp(llvm.IntEQ, ptr, llvm.ConstNull(ptr.Type()), "")
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
assert = b.CreateOr(assert, lenOutOfBounds, "")
b.createRuntimeAssert(assert, "unsafe.Slice", "unsafeSlicePanic")
}
// createChanBoundsCheck creates a bounds check before creating a new channel to
// check that the value is not too big for runtime.chanMake.
func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {
+9 -28
View File
@@ -32,15 +32,8 @@ func (b *builder) createChanSend(instr *ssa.Send) {
// store value-to-send
valueType := b.getLLVMType(instr.X.Type())
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
if isZeroSize {
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
} else {
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
b.CreateStore(chanValue, valueAlloca)
}
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
b.CreateStore(chanValue, valueAlloca)
// Allocate blockedlist buffer.
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
@@ -53,9 +46,7 @@ func (b *builder) createChanSend(instr *ssa.Send) {
// This also works around a bug in CoroSplit, at least in LLVM 8:
// https://bugs.llvm.org/show_bug.cgi?id=41742
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
if !isZeroSize {
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
}
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
}
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
@@ -65,14 +56,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
ch := b.getValue(unop.X)
// Allocate memory to receive into.
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
if isZeroSize {
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
} else {
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
}
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
// Allocate blockedlist buffer.
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
@@ -80,14 +64,9 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
// Do the receive.
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
var received llvm.Value
if isZeroSize {
received = llvm.ConstNull(valueType)
} else {
received = b.CreateLoad(valueAlloca, "chan.received")
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
}
received := b.CreateLoad(valueAlloca, "chan.received")
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
if unop.CommaOk {
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
@@ -137,6 +116,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
// determine the receive buffer size and alignment.
recvbufSize := uint64(0)
recvbufAlign := 0
hasReceives := false
var selectStates []llvm.Value
chanSelectStateType := b.getLLVMRuntimeType("chanSelectState")
for _, state := range expr.States {
@@ -153,6 +133,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
if align := b.targetData.ABITypeAlignment(llvmType); align > recvbufAlign {
recvbufAlign = align
}
hasReceives = true
case types.SendOnly:
// Store this value in an alloca and put a pointer to this alloca
// in the send state.
@@ -169,7 +150,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
// Create a receive buffer, where the received value will be stored.
recvbuf := llvm.Undef(b.i8ptrType)
if recvbufSize != 0 {
if hasReceives {
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
recvbufAlloca.SetAlignment(recvbufAlign)
+45 -119
View File
@@ -23,7 +23,7 @@ import (
// Version of the compiler pacakge. Must be incremented each time the compiler
// package changes in a way that affects the generated LLVM module.
// This version is independent of the TinyGo version number.
const Version = 19 // last change: fix channel ops with zero values
const Version = 12 // last change: implement syscall.rawSyscallNoError
func init() {
llvm.InitializeAllTargets()
@@ -74,7 +74,6 @@ type compilerContext struct {
cu llvm.Metadata
difiles map[string]llvm.Metadata
ditypes map[types.Type]llvm.Metadata
llvmTypes map[types.Type]llvm.Type
machine llvm.TargetMachine
targetData llvm.TargetData
intType llvm.Type
@@ -95,7 +94,6 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
DumpSSA: dumpSSA,
difiles: make(map[string]llvm.Metadata),
ditypes: make(map[types.Type]llvm.Metadata),
llvmTypes: make(map[types.Type]llvm.Type),
machine: machine,
targetData: machine.CreateTargetData(),
astComments: map[string]*ast.CommentGroup{},
@@ -317,23 +315,10 @@ func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
return c.getLLVMType(typ)
}
// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
// created types. This is somewhat important for performance, but especially
// important for named struct types (which should only be created once).
// getLLVMType creates and returns a LLVM type for a Go type. In the case of
// named struct types (or Go types implemented as named LLVM structs such as
// strings) it also creates it first if necessary.
func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
// Try to load the LLVM type from the cache.
if t, ok := c.llvmTypes[goType]; ok {
return t
}
// Not already created, so adding this type to the cache.
llvmType := c.makeLLVMType(goType)
c.llvmTypes[goType] = llvmType
return llvmType
}
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
// getLLVMType instead.
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
switch typ := goType.(type) {
case *types.Array:
elemType := c.getLLVMType(typ.Elem())
@@ -382,10 +367,12 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
// LLVM. This is because it is otherwise impossible to create
// self-referencing types such as linked lists.
llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
llvmType := c.ctx.StructCreateNamed(llvmName)
c.llvmTypes[goType] = llvmType // avoid infinite recursion
underlying := c.getLLVMType(st)
llvmType.StructSetBody(underlying.StructElementTypes(), false)
llvmType := c.mod.GetTypeByName(llvmName)
if llvmType.IsNil() {
llvmType = c.ctx.StructCreateNamed(llvmName)
underlying := c.getLLVMType(st)
llvmType.StructSetBody(underlying.StructElementTypes(), false)
}
return llvmType
}
return c.getLLVMType(typ.Underlying())
@@ -706,6 +693,7 @@ func (c *compilerContext) getDIFile(filename string) llvm.Metadata {
// createPackage builds the LLVM IR for all types, methods, and global variables
// in the given package.
func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) {
println("called create package:", pkg.Pkg.Name())
// Sort by position, so that the order of the functions in the IR matches
// the order of functions in the source file. This is useful for testing,
// for example.
@@ -771,6 +759,35 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
case *ssa.Global:
// Global variable.
info := c.getGlobalInfo(member)
// process go:embed pragmas
// TODO: skip this if not Go 1.16 or higher
if strings.TrimSpace(info.embeds) != "" {
embeds, err := parseGoEmbed(info.embeds)
if err != nil {
c.addError(member.Pos(), `invalid go:embed pragma: `+err.Error())
goto getGlobal
}
importsEmbedPkg := false
for _, imprt := range pkg.Pkg.Imports() {
if imprt.Name() == "embed" {
importsEmbedPkg = true
break
}
}
if !importsEmbedPkg {
// FIXME: technically the "embed" package needs to be imported in every
// *source file* that has a //go:embed pragma... that will always be the
// case when referencing embed.FS but not necessarily when a go:embed
// pragma is added to a string or []byte. For now, as long as the embed
// package is imported in at least one source file, TinyGo will process
// any go:embed pragmas in the entire package.
c.addError(member.Pos(), `//go:embed only allowed in Go files that import "embed"`)
goto getGlobal
}
fmt.Printf("file embed found at %v (import: %v): %v\n", info.linkName, importsEmbedPkg, embeds)
}
getGlobal:
global := c.getGlobal(member)
if !info.extern {
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
@@ -779,29 +796,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
global.SetSection(info.section)
}
}
}
}
// Add forwarding functions for functions that would otherwise be
// implemented in assembly.
for _, name := range members {
member := pkg.Members[name]
switch member := member.(type) {
case *ssa.Function:
if member.Blocks != nil {
continue // external function
}
info := c.getFunctionInfo(member)
if aliasName, ok := stdlibAliases[info.linkName]; ok {
alias := c.mod.NamedFunction(aliasName)
if alias.IsNil() {
// Shouldn't happen, but perhaps best to just ignore.
// The error will be a link error, if there is an error.
continue
}
b := newBuilder(c, irbuilder, member)
b.createAlias(alias)
}
}
}
}
@@ -1306,38 +1301,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
case "ssa:wrapnilchk":
// TODO: do an actual nil check?
return argValues[0], nil
// Builtins from the unsafe package.
case "Add": // unsafe.Add
// This is basically just a GEP operation.
// Note: the pointer is always of type *i8.
ptr := argValues[0]
len := argValues[1]
return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
case "Slice": // unsafe.Slice
// This creates a slice from a pointer and a length.
// Note that the exception mentioned in the documentation (if the
// pointer and length are nil, the slice is also nil) is trivially
// already the case.
ptr := argValues[0]
len := argValues[1]
slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
ptr.Type(),
b.uintptrType,
b.uintptrType,
}, false))
b.createUnsafeSliceCheck(ptr, len, argTypes[1].Underlying().(*types.Basic))
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
// Too small, zero-extend len.
len = b.CreateZExt(len, b.uintptrType, "")
} else if len.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
// Too big, truncate len.
len = b.CreateTrunc(len, b.uintptrType, "")
}
slice = b.CreateInsertValue(slice, ptr, 0, "")
slice = b.CreateInsertValue(slice, len, 1, "")
slice = b.CreateInsertValue(slice, len, 2, "")
return slice, nil
default:
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
}
@@ -1366,11 +1329,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.createMemoryCopyCall(fn, instr.Args)
case name == "runtime.memzero":
return b.createMemoryZeroCall(instr.Args)
case name == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc":
result, ok := b.createMathOp(instr)
if ok {
return result, nil
}
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
return b.createInlineAsm(instr.Args)
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
@@ -1484,28 +1442,6 @@ func (b *builder) getValue(expr ssa.Value) llvm.Value {
}
}
// maxSliceSize determines the maximum size a slice of the given element type
// can be.
func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
// Calculate ^uintptr(0), which is the max value that fits in uintptr.
maxPointerValue := llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)).ZExtValue()
// Calculate (^uint(0))/2, which is the max value that fits in an int.
maxIntegerValue := llvm.ConstNot(llvm.ConstInt(c.intType, 0, false)).ZExtValue() / 2
// Determine the maximum allowed size for a slice. The biggest possible
// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
// divide by the element type to get the real maximum size.
maxSize := maxPointerValue / c.targetData.TypeAllocSize(elementType)
// len(slice) is an int. Make sure the length remains small enough to fit in
// an int.
if maxSize > maxIntegerValue {
maxSize = maxIntegerValue
}
return maxSize
}
// createExpr translates a Go SSA expression to LLVM IR. This can be zero, one,
// or multiple LLVM IR instructions and/or runtime calls.
func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
@@ -1719,8 +1655,10 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
elemSize := b.targetData.TypeAllocSize(llvmElemType)
elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
maxSize := b.maxSliceSize(llvmElemType)
if elemSize > maxSize {
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in
// uintptr if uintptr were signed.
maxSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false))
if elemSize > maxSize.ZExtValue() {
// This seems to be checked by the typechecker already, but let's
// check it again just to be sure.
return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize))
@@ -1729,8 +1667,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
// Bounds checking.
lenType := expr.Len.Type().Underlying().(*types.Basic)
capType := expr.Cap.Type().Underlying().(*types.Basic)
maxSizeValue := llvm.ConstInt(b.uintptrType, maxSize, false)
b.createSliceBoundsCheck(maxSizeValue, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
b.createSliceBoundsCheck(maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
// Allocate the backing array.
sliceCapCast, err := b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
@@ -1973,17 +1910,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
default:
return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String())
}
case *ssa.SliceToArrayPointer:
// Conversion from a slice to an array pointer, as the name clearly
// says. This requires a runtime check to make sure the slice is at
// least as big as the array.
slice := b.getValue(expr.X)
sliceLen := b.CreateExtractValue(slice, 1, "")
arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
ptr := b.CreateExtractValue(slice, 0, "")
ptr = b.CreateBitCast(ptr, b.getLLVMType(expr.Type()), "")
return ptr, nil
case *ssa.TypeAssert:
return b.createTypeAssert(expr), nil
case *ssa.UnOp:
+4 -18
View File
@@ -9,7 +9,6 @@ import (
"testing"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/loader"
"tinygo.org/x/go-llvm"
)
@@ -17,11 +16,6 @@ import (
// Pass -update to go test to update the output of the test files.
var flagUpdate = flag.Bool("update", false, "update tests based on test output")
type testCase struct {
file string
target string
}
// Basic tests for the compiler. Build some Go files and compare the output with
// the expected LLVM IR for regression testing.
func TestCompiler(t *testing.T) {
@@ -40,7 +34,10 @@ func TestCompiler(t *testing.T) {
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
}
tests := []testCase{
tests := []struct {
file string
target string
}{
{"basic.go", ""},
{"pointer.go", ""},
{"slice.go", ""},
@@ -51,17 +48,6 @@ func TestCompiler(t *testing.T) {
{"pragma.go", ""},
{"goroutine.go", "wasm"},
{"goroutine.go", "cortex-m-qemu"},
{"channel.go", ""},
{"intrinsics.go", "cortex-m-qemu"},
{"intrinsics.go", "wasm"},
}
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
if err != nil {
t.Fatal("could not read Go version:", err)
}
if minor >= 17 {
tests = append(tests, testCase{"go1.17.go", ""})
}
for _, tc := range tests {
+70
View File
@@ -0,0 +1,70 @@
package compiler
import (
"fmt"
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns.
// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings.
// Copied from https://github.com/golang/go/blob/219fe9d547d09d3de1b024c6c8b8314dd0bf12e4/src/cmd/compile/internal/noder/noder.go#L1717-L1776
func parseGoEmbed(args string) ([]string, error) {
var list []string
for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) {
var path string
Switch:
switch args[0] {
default:
i := len(args)
for j, c := range args {
if unicode.IsSpace(c) {
i = j
break
}
}
path = args[:i]
args = args[i:]
case '`':
i := strings.Index(args[1:], "`")
if i < 0 {
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
}
path = args[1 : 1+i]
args = args[1+i+1:]
case '"':
i := 1
for ; i < len(args); i++ {
if args[i] == '\\' {
i++
continue
}
if args[i] == '"' {
q, err := strconv.Unquote(args[:i+1])
if err != nil {
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1])
}
path = q
args = args[i+1:]
break Switch
}
}
if i >= len(args) {
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
}
}
if args != "" {
r, _ := utf8.DecodeRuneInString(args)
if !unicode.IsSpace(r) {
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
}
}
list = append(list, path)
}
return list, nil
}
-54
View File
@@ -48,57 +48,3 @@ func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
b.CreateCall(llvmFn, params, "")
return llvm.Value{}, nil
}
var mathToLLVMMapping = map[string]string{
"math.Sqrt": "llvm.sqrt.f64",
"math.Floor": "llvm.floor.f64",
"math.Ceil": "llvm.ceil.f64",
"math.Trunc": "llvm.trunc.f64",
}
// createMathOp tries to lower the given call as a LLVM math intrinsic, if
// possible. It returns the call result if possible, and a boolean whether it
// succeeded. If it doesn't succeed, the architecture doesn't support the given
// intrinsic.
func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) {
// Check whether this intrinsic is supported on the given GOARCH.
// If it is unsupported, this can have two reasons:
//
// 1. LLVM can expand the intrinsic inline (using float instructions), but
// the result doesn't pass the tests of the math package.
// 2. LLVM cannot expand the intrinsic inline, will therefore lower it as a
// libm function call, but the libm function call also fails the math
// package tests.
//
// Whatever the implementation, it must pass the tests in the math package
// so unfortunately only the below intrinsic+architecture combinations are
// supported.
name := call.StaticCallee().RelString(nil)
switch name {
case "math.Ceil", "math.Floor", "math.Trunc":
if b.GOARCH != "wasm" && b.GOARCH != "arm64" {
return llvm.Value{}, false
}
case "math.Sqrt":
if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" {
return llvm.Value{}, false
}
default:
return llvm.Value{}, false // only the above functions are supported.
}
llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name])
if llvmFn.IsNil() {
// The intrinsic doesn't exist yet, so declare it.
// At the moment, all supported intrinsics have the form "double
// foo(double %x)" so we can hardcode the signature here.
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType)
}
// Create a call to the intrinsic.
args := make([]llvm.Value, len(call.Args))
for i, arg := range call.Args {
args[i] = b.getValue(arg)
}
return b.CreateCall(llvmFn, args, ""), true
}
+4
View File
@@ -331,6 +331,7 @@ type globalInfo struct {
extern bool // go:extern
align int // go:align
section string // go:section
embeds string // go:embed
}
// loadASTComments loads comments on globals from the AST, for use later in the
@@ -448,6 +449,9 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
if len(parts) == 2 {
info.section = parts[1]
}
case "//go:embed":
raw := strings.TrimPrefix(comment.Text, "//go:embed")
info.embeds += " " + raw
}
}
}
-15
View File
@@ -55,18 +55,3 @@ func complexMul(x, y complex64) complex64 {
}
// TODO: complexDiv (requires runtime call)
// A type 'kv' also exists in function foo. Test that these two types don't
// conflict with each other.
type kv struct {
v float32
}
func foo(a *kv) {
// Define a new 'kv' type.
type kv struct {
v byte
}
// Use this type.
func(b *kv) {}(nil)
}
-14
View File
@@ -3,9 +3,6 @@ source_filename = "basic.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32--wasi"
%main.kv = type { float }
%main.kv.0 = type { i8 }
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
@@ -101,14 +98,3 @@ entry:
%7 = insertvalue { float, float } %6, float %5, 1
ret { float, float } %7
}
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
call void @"main.foo$1"(%main.kv.0* null, i8* undef, i8* undef)
ret void
}
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
-25
View File
@@ -1,25 +0,0 @@
package main
func chanIntSend(ch chan int) {
ch <- 3
}
func chanIntRecv(ch chan int) {
<-ch
}
func chanZeroSend(ch chan struct{}) {
ch <- struct{}{}
}
func chanZeroRecv(ch chan struct{}) {
<-ch
}
func selectZeroRecv(ch1 chan int, ch2 chan struct{}) {
select {
case ch1 <- 1:
case <-ch2:
default:
}
}
-114
View File
@@ -1,114 +0,0 @@
; ModuleID = 'channel.go'
source_filename = "channel.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32--wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
%"internal/task.state" = type { i8* }
%runtime.chanSelectState = type { %runtime.channel*, i8* }
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
%chan.value = alloca i32, align 4
%chan.value.bitcast = bitcast i32* %chan.value to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
store i32 3, i32* %chan.value, align 4
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
ret void
}
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #0
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
; Function Attrs: argmemonly nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #0
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
%chan.value = alloca i32, align 4
%chan.value.bitcast = bitcast i32* %chan.value to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
ret void
}
declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
ret void
}
define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
ret void
}
define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
%select.send.value = alloca i32, align 4
store i32 1, i32* %select.send.value, align 4
%select.states.alloca.bitcast = bitcast [2 x %runtime.chanSelectState]* %select.states.alloca to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
%.repack = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 0
store %runtime.channel* %ch1, %runtime.channel** %.repack, align 8
%.repack1 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 1
%0 = bitcast i8** %.repack1 to i32**
store i32* %select.send.value, i32** %0, align 4
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 0
store %runtime.channel* %ch2, %runtime.channel** %.repack3, align 8
%.repack4 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 1
store i8* null, i8** %.repack4, align 4
%select.states = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0
%select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef, i8* null)
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
%1 = extractvalue { i32, i1 } %select.result, 0
%2 = icmp eq i32 %1, 0
br i1 %2, label %select.done, label %select.next
select.done: ; preds = %select.body, %select.next, %entry
ret void
select.next: ; preds = %entry
%3 = icmp eq i32 %1, 1
br i1 %3, label %select.body, label %select.done
select.body: ; preds = %select.next
br label %select.done
}
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*, i8*)
attributes #0 = { argmemonly nounwind willreturn }
-41
View File
@@ -1,41 +0,0 @@
package main
// Test changes to the language introduced in Go 1.17.
// For details, see: https://tip.golang.org/doc/go1.17#language
// These tests should be merged into the regular slice tests once Go 1.17 is the
// minimun Go version for TinyGo.
import "unsafe"
func Add32(p unsafe.Pointer, len int) unsafe.Pointer {
return unsafe.Add(p, len)
}
func Add64(p unsafe.Pointer, len int64) unsafe.Pointer {
return unsafe.Add(p, len)
}
func SliceToArray(s []int) *[4]int {
return (*[4]int)(s)
}
func SliceToArrayConst() *[4]int {
s := make([]int, 6)
return (*[4]int)(s)
}
func SliceInt(ptr *int, len int) []int {
return unsafe.Slice(ptr, len)
}
func SliceUint16(ptr *byte, len uint16) []byte {
return unsafe.Slice(ptr, len)
}
func SliceUint64(ptr *int, len uint64) []int {
return unsafe.Slice(ptr, len)
}
func SliceInt64(ptr *int, len int64) []int {
return unsafe.Slice(ptr, len)
}
-136
View File
@@ -1,136 +0,0 @@
; ModuleID = 'go1.17.go'
source_filename = "go1.17.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32--wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = getelementptr i8, i8* %p, i32 %len
ret i8* %0
}
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = trunc i64 %len to i32
%1 = getelementptr i8, i8* %p, i32 %0
ret i8* %1
}
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = icmp ult i32 %s.len, 4
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
slicetoarray.throw: ; preds = %entry
call void @runtime.sliceToArrayPointerPanic(i8* undef, i8* null)
unreachable
slicetoarray.next: ; preds = %entry
%1 = bitcast i32* %s.data to [4 x i32]*
ret [4 x i32]* %1
}
declare void @runtime.sliceToArrayPointerPanic(i8*, i8*)
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%makeslice = call i8* @runtime.alloc(i32 24, i8* undef, i8* null)
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
slicetoarray.throw: ; preds = %entry
unreachable
slicetoarray.next: ; preds = %entry
%0 = bitcast i8* %makeslice to [4 x i32]*
ret [4 x i32]* %0
}
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = icmp ugt i32 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i32 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
unreachable
unsafe.Slice.next: ; preds = %entry
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
ret { i32*, i32, i32 } %7
}
declare void @runtime.unsafeSlicePanic(i8*, i8*)
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = icmp eq i8* %ptr, null
%1 = icmp ne i16 %len, 0
%2 = and i1 %0, %1
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
unreachable
unsafe.Slice.next: ; preds = %entry
%3 = zext i16 %len to i32
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
ret { i8*, i32, i32 } %6
}
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = icmp ugt i64 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i64 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
unreachable
unsafe.Slice.next: ; preds = %entry
%5 = trunc i64 %len to i32
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
ret { i32*, i32, i32 } %8
}
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = icmp ugt i64 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i64 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
unreachable
unsafe.Slice.next: ; preds = %entry
%5 = trunc i64 %len to i32
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
ret { i32*, i32, i32 } %8
}
-27
View File
@@ -1,27 +0,0 @@
; ModuleID = 'intrinsics.go'
source_filename = "intrinsics.go"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv7m-none-eabi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = call double @math.Sqrt(double %x, i8* undef, i8* undef)
ret double %0
}
declare double @math.Sqrt(double, i8*, i8*)
define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = call double @math.Trunc(double %x, i8* undef, i8* undef)
ret double %0
}
declare double @math.Trunc(double, i8*, i8*)
-31
View File
@@ -1,31 +0,0 @@
; ModuleID = 'intrinsics.go'
source_filename = "intrinsics.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32--wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = call double @llvm.sqrt.f64(double %x)
ret double %0
}
; Function Attrs: nounwind readnone speculatable willreturn
declare double @llvm.sqrt.f64(double) #0
define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = call double @llvm.trunc.f64(double %x)
ret double %0
}
; Function Attrs: nounwind readnone speculatable willreturn
declare double @llvm.trunc.f64(double) #0
attributes #0 = { nounwind readnone speculatable willreturn }
-14
View File
@@ -1,14 +0,0 @@
package main
// Test how intrinsics are lowered: either as regular calls to the math
// functions or as LLVM builtins (such as llvm.sqrt.f64).
import "math"
func mySqrt(x float64) float64 {
return math.Sqrt(x)
}
func myTrunc(x float64) float64 {
return math.Trunc(x)
}
-18
View File
@@ -23,21 +23,3 @@ func sliceAppendSlice(ints, added []int) []int {
func sliceCopy(dst, src []int) int {
return copy(dst, src)
}
// Test bounds checking in *ssa.MakeSlice instruction.
func makeByteSlice(len int) []byte {
return make([]byte, len)
}
func makeInt16Slice(len int) []int16 {
return make([]int16, len)
}
func makeArraySlice(len int) [][3]byte {
return make([][3]byte, len) // slice with element size of 3
}
func makeInt32Slice(len int) []int32 {
return make([]int32, len)
}
-76
View File
@@ -86,79 +86,3 @@ entry:
}
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%slice.maxcap = icmp slt i32 %len, 0
br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.throw: ; preds = %entry
call void @runtime.slicePanic(i8* undef, i8* null)
unreachable
slice.next: ; preds = %entry
%makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* undef, i8* null)
%0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0
%1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2
ret { i8*, i32, i32 } %2
}
declare void @runtime.slicePanic(i8*, i8*)
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%slice.maxcap = icmp slt i32 %len, 0
br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.throw: ; preds = %entry
call void @runtime.slicePanic(i8* undef, i8* null)
unreachable
slice.next: ; preds = %entry
%makeslice.cap = shl i32 %len, 1
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
%makeslice.array = bitcast i8* %makeslice.buf to i16*
%0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0
%1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { i16*, i32, i32 } %1, i32 %len, 2
ret { i16*, i32, i32 } %2
}
define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%slice.maxcap = icmp ugt i32 %len, 1431655765
br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.throw: ; preds = %entry
call void @runtime.slicePanic(i8* undef, i8* null)
unreachable
slice.next: ; preds = %entry
%makeslice.cap = mul i32 %len, 3
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
%makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]*
%0 = insertvalue { [3 x i8]*, i32, i32 } undef, [3 x i8]* %makeslice.array, 0
%1 = insertvalue { [3 x i8]*, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { [3 x i8]*, i32, i32 } %1, i32 %len, 2
ret { [3 x i8]*, i32, i32 } %2
}
define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%slice.maxcap = icmp ugt i32 %len, 1073741823
br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.throw: ; preds = %entry
call void @runtime.slicePanic(i8* undef, i8* null)
unreachable
slice.next: ; preds = %entry
%makeslice.cap = shl i32 %len, 2
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
%makeslice.array = bitcast i8* %makeslice.buf to i32*
%0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0
%1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { i32*, i32, i32 } %1, i32 %len, 2
ret { i32*, i32, i32 } %2
}
+3 -3
View File
@@ -1,6 +1,6 @@
module github.com/tinygo-org/tinygo
go 1.15
go 1.16
require (
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
@@ -10,7 +10,7 @@ require (
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
github.com/mattn/go-colorable v0.1.8
go.bug.st/serial v1.1.2
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
)
+6 -17
View File
@@ -30,8 +30,6 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q=
go.bug.st/serial v1.1.2 h1:6xDpbta8KJ+VLRTeM8ghhxXRMLE/Lr8h9iDKwydarAY=
@@ -40,32 +38,23 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 h1:nvvuMxmx1q0gfRki3T0hjG8EwAcVCs91oWAXvyt4zhI=
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
+5 -5
View File
@@ -12,7 +12,7 @@ import (
// Version of TinyGo.
// Update this value before release of new version of software.
const Version = "0.20.0"
const Version = "0.20.0-dev"
// GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
@@ -48,10 +48,7 @@ func GetGorootVersion(goroot string) (major, minor int, err error) {
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
// can have some variations (for beta releases, for example).
func GorootVersionString(goroot string) (string, error) {
if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
return string(data), nil
} else if data, err := ioutil.ReadFile(filepath.Join(
if data, err := ioutil.ReadFile(filepath.Join(
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
r := regexp.MustCompile("const TheVersion = `(.*)`")
@@ -62,6 +59,9 @@ func GorootVersionString(goroot string) (string, error) {
return string(matches[1]), nil
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
return string(data), nil
} else {
return "", err
}
+4
View File
@@ -590,14 +590,18 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// GetElementPtr does pointer arithmetic, changing the offset of the
// pointer into the underlying object.
var offset uint64
var gepOperands []uint64
for i := 2; i < len(operands); i += 2 {
index := operands[i].Uint()
elementSize := operands[i+1].Uint()
if int64(elementSize) < 0 {
// This is a struct field.
// The field number is encoded by flipping all the bits.
gepOperands = append(gepOperands, ^elementSize)
offset += index
} else {
// This is a normal GEP, probably an array index.
gepOperands = append(gepOperands, index)
offset += elementSize * index
}
}
+1 -1
View File
@@ -208,7 +208,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
// with the TinyGo version. This is the case on some targets.
func needsSyscallPackage(buildTags []string) bool {
for _, tag := range buildTags {
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "tinygo.wasm" {
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "wasi" {
return true
}
}
+1 -5
View File
@@ -386,11 +386,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
}
// Do CGo processing.
// This is done when there are any CgoFiles at all. In that case, len(files)
// should be non-zero. However, if len(GoFiles) == 0 and len(CgoFiles) == 1
// and there is a syntax error in a CGo file, len(files) may be 0. Don't try
// to call cgo.Process in that case as it will only cause issues.
if len(p.CgoFiles) != 0 && len(files) != 0 {
if len(p.CgoFiles) != 0 {
var initialCFlags []string
initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
initialCFlags = append(initialCFlags, "-I"+p.Dir)
+7 -86
View File
@@ -15,7 +15,6 @@ import (
"path/filepath"
"regexp"
"runtime"
"runtime/pprof"
"strconv"
"strings"
"sync/atomic"
@@ -159,7 +158,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
// Test runs the tests in the given package. Returns whether the test passed and
// possibly an error if the test failed to run.
func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVerbose bool, outpath string) (bool, error) {
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) (bool, error) {
options.TestConfig.CompileTestBinary = true
config, err := builder.NewConfig(options)
if err != nil {
@@ -185,7 +184,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVer
// Run the test.
start := time.Now()
var err error
passed, err = runPackageTest(config, result, testVerbose)
passed, err = runPackageTest(config, result)
if err != nil {
return err
}
@@ -211,14 +210,10 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVer
// runPackageTest runs a test binary that was previously built. The return
// values are whether the test passed and any errors encountered while trying to
// run the binary.
func runPackageTest(config *compileopts.Config, result builder.BuildResult, testVerbose bool) (bool, error) {
func runPackageTest(config *compileopts.Config, result builder.BuildResult) (bool, error) {
if len(config.Target.Emulator) == 0 {
// Run directly.
var flags []string
if testVerbose {
flags = append(flags, "-test.v")
}
cmd := executeCommand(config.Options, result.Binary, flags...)
cmd := executeCommand(config.Options, result.Binary)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = result.MainDir
@@ -234,7 +229,6 @@ func runPackageTest(config *compileopts.Config, result builder.BuildResult, test
return true, nil
} else {
// Run in an emulator.
// TODO: pass the -test.v flag if needed.
args := append(config.Target.Emulator[1:], result.Binary)
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
buf := &bytes.Buffer{}
@@ -294,8 +288,6 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
fileExt = filepath.Ext(config.Target.FlashFilename)
case "openocd":
fileExt = ".hex"
case "bmp":
fileExt = ".elf"
case "native":
return errors.New("unknown flash method \"native\" - did you miss a -target flag?")
default:
@@ -388,25 +380,6 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
return &commandError{"failed to flash", result.Binary, err}
}
return nil
case "bmp":
gdb, err := config.Target.LookupGDB()
if err != nil {
return err
}
var bmpGDBPort string
bmpGDBPort, _, err = getBMPPorts()
if err != nil {
return err
}
args := []string{"-ex", "target extended-remote " + bmpGDBPort, "-ex", "monitor swdp_scan", "-ex", "attach 1", "-ex", "load", filepath.ToSlash(result.Binary)}
cmd := executeCommand(config.Options, gdb, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return &commandError{"failed to flash", result.Binary, err}
}
return nil
default:
return fmt.Errorf("unknown flash method: %s", flashMethod)
}
@@ -461,13 +434,6 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
switch gdbInterface {
case "native":
// Run GDB directly.
case "bmp":
var bmpGDBPort string
bmpGDBPort, _, err = getBMPPorts()
if err != nil {
return err
}
gdbCommands = append(gdbCommands, "target extended-remote "+bmpGDBPort, "monitor swdp_scan", "compare-sections", "attach 1", "load")
case "openocd":
gdbCommands = append(gdbCommands, "target extended-remote :3333", "monitor halt", "load", "monitor reset halt")
@@ -875,35 +841,6 @@ func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err e
return "", errors.New("port you specified '" + strings.Join(portCandidates, ",") + "' does not exist, available ports are " + strings.Join(ports, ", "))
}
// getBMPPorts returns BlackMagicProbe's serial ports if any
func getBMPPorts() (gdbPort, uartPort string, err error) {
var portsList []*enumerator.PortDetails
portsList, err = enumerator.GetDetailedPortsList()
if err != nil {
return "", "", err
}
var ports []string
for _, p := range portsList {
if !p.IsUSB {
continue
}
if p.VID != "" && p.PID != "" {
vid, vidErr := strconv.ParseUint(p.VID, 16, 16)
pid, pidErr := strconv.ParseUint(p.PID, 16, 16)
if vidErr == nil && pidErr == nil && vid == 0x1d50 && pid == 0x6018 {
ports = append(ports, p.Name)
}
}
}
if len(ports) == 2 {
return ports[0], ports[1], nil
} else if len(ports) == 0 {
return "", "", errors.New("no BMP detected")
} else {
return "", "", fmt.Errorf("expected 2 BMP serial ports, found %d - did you perhaps connect more than one BMP?", len(ports))
}
}
func usage() {
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
fmt.Fprintln(os.Stderr, "version:", goenv.Version)
@@ -1082,7 +1019,7 @@ func main() {
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
printCommands := flag.Bool("x", false, "Print commands")
nodebug := flag.Bool("no-debug", false, "strip debug information")
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
@@ -1090,7 +1027,6 @@ func main() {
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
var flagJSON, flagDeps, flagTest *bool
if command == "help" || command == "list" {
@@ -1102,10 +1038,9 @@ func main() {
if command == "help" || command == "build" || command == "build-library" || command == "test" {
flag.StringVar(&outpath, "o", "", "output filename")
}
var testCompileOnlyFlag, testVerboseFlag *bool
var testCompileOnlyFlag *bool
if command == "help" || command == "test" {
testCompileOnlyFlag = flag.Bool("c", false, "compile the test binary but do not run it")
testVerboseFlag = flag.Bool("v", false, "verbose: print additional output")
}
// Early command processing, before commands are interpreted by the Go flag
@@ -1175,20 +1110,6 @@ func main() {
os.Exit(1)
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
fmt.Fprintln(os.Stderr, "could not create CPU profile: ", err)
os.Exit(1)
}
defer f.Close()
if err := pprof.StartCPUProfile(f); err != nil {
fmt.Fprintln(os.Stderr, "could not start CPU profile: ", err)
os.Exit(1)
}
defer pprof.StopCPUProfile()
}
switch command {
case "build":
if outpath == "" {
@@ -1280,7 +1201,7 @@ func main() {
allTestsPassed := true
for _, pkgName := range pkgNames {
// TODO: parallelize building the test binaries
passed, err := Test(pkgName, options, *testCompileOnlyFlag, *testVerboseFlag, outpath)
passed, err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
handleCompilerError(err)
if !passed {
allTestsPassed = false
+49 -73
View File
@@ -51,18 +51,9 @@ func TestCompiler(t *testing.T) {
"stdlib.go",
"string.go",
"structs.go",
"testing.go",
"zeroalloc.go",
}
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
if err != nil {
t.Fatal("could not read version from GOROOT:", err)
}
if minor >= 17 {
tests = append(tests, "go1.17.go")
}
if *testTarget != "" {
// This makes it possible to run one specific test (instead of all),
// which is especially useful to quickly check whether some changes
@@ -103,9 +94,21 @@ func TestCompiler(t *testing.T) {
t.Run("ARM64Linux", func(t *testing.T) {
runPlatTests("aarch64--linux-gnu", tests, t)
})
t.Run("WebAssembly", func(t *testing.T) {
runPlatTests("wasm", tests, t)
})
goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT"))
if err != nil {
t.Error("could not get Go version:", err)
return
}
minorVersion := strings.Split(goVersion, ".")[1]
if minorVersion != "13" {
// WebAssembly tests fail on Go 1.13, so skip them there. Versions
// below that are also not supported but still seem to pass, so
// include them in the tests for now.
t.Run("WebAssembly", func(t *testing.T) {
runPlatTests("wasm", tests, t)
})
}
t.Run("WASI", func(t *testing.T) {
runPlatTests("wasi", tests, t)
})
@@ -159,20 +162,14 @@ func runPlatTests(target string, tests []string, t *testing.T) {
runTest(name, target, t, nil, nil)
})
}
t.Run("env.go", func(t *testing.T) {
t.Parallel()
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
})
if target == "" || target == "wasi" {
if target == "wasi" || target == "" {
t.Run("filesystem.go", func(t *testing.T) {
t.Parallel()
runTest("filesystem.go", target, t, nil, nil)
})
}
if target == "" || target == "wasi" || target == "wasm" {
t.Run("rand.go", func(t *testing.T) {
t.Run("env.go", func(t *testing.T) {
t.Parallel()
runTest("rand.go", target, t, nil, nil)
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
})
}
}
@@ -219,43 +216,16 @@ func runTestWithConfig(name, target string, t *testing.T, options compileopts.Op
}
// Create a temporary directory for test output files.
tmpdir := t.TempDir()
// Determine whether we're on a system that supports environment variables
// and command line parameters (operating systems, WASI) or not (baremetal,
// WebAssembly in the browser). If we're on a system without an environment,
// we need to pass command line arguments and environment variables through
// global variables (built into the binary directly) instead of the
// conventional way.
spec, err := compileopts.LoadTarget(target)
tmpdir, err := ioutil.TempDir("", "tinygo-test")
if err != nil {
t.Fatal("failed to load target spec:", err)
t.Fatal("could not create temporary directory:", err)
}
needsEnvInVars := spec.GOOS == "js"
for _, tag := range spec.BuildTags {
if tag == "baremetal" {
needsEnvInVars = true
defer func() {
rerr := os.RemoveAll(tmpdir)
if rerr != nil {
t.Errorf("failed to remove temporary directory %q: %s", tmpdir, rerr.Error())
}
}
if needsEnvInVars {
runtimeGlobals := make(map[string]string)
if len(cmdArgs) != 0 {
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
}
if len(environmentVars) != 0 {
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
}
if len(runtimeGlobals) != 0 {
// This sets the global variables like they would be set with
// `-ldflags="-X=runtime.osArgs=first\x00second`.
// The runtime package has two variables (osArgs and osEnv) that are
// both strings, from which the parameters and environment variables
// are read.
options.GlobalValues = map[string]map[string]string{
"runtime": runtimeGlobals,
}
}
}
}()
// Build the test binary.
binary := filepath.Join(tmpdir, "test")
@@ -266,31 +236,37 @@ func runTestWithConfig(name, target string, t *testing.T, options compileopts.Op
return
}
// Create the test command, taking care of emulators etc.
// Run the test.
runComplete := make(chan struct{})
var cmd *exec.Cmd
if len(spec.Emulator) == 0 {
ranTooLong := false
if target == "" {
cmd = exec.Command(binary)
} else {
args := append(spec.Emulator[1:], binary)
cmd = exec.Command(spec.Emulator[0], args...)
}
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
// Allow reading from the current directory.
cmd.Args = append(cmd.Args, "--dir=.")
for _, v := range environmentVars {
cmd.Args = append(cmd.Args, "--env", v)
}
cmd.Env = append(cmd.Env, environmentVars...)
cmd.Args = append(cmd.Args, cmdArgs...)
} else {
if !needsEnvInVars {
cmd.Args = append(cmd.Args, cmdArgs...) // works on qemu-aarch64 etc
spec, err := compileopts.LoadTarget(target)
if err != nil {
t.Fatal("failed to load target spec:", err)
}
if len(spec.Emulator) == 0 {
cmd = exec.Command(binary)
} else {
args := append(spec.Emulator[1:], binary)
cmd = exec.Command(spec.Emulator[0], args...)
}
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
// Allow reading from the current directory.
cmd.Args = append(cmd.Args, "--dir=.")
for _, v := range environmentVars {
cmd.Args = append(cmd.Args, "--env", v)
}
cmd.Args = append(cmd.Args, cmdArgs...)
} else {
cmd.Env = append(cmd.Env, environmentVars...)
}
}
// Run the test.
runComplete := make(chan struct{})
ranTooLong := false
stdout := &bytes.Buffer{}
cmd.Stdout = stdout
cmd.Stderr = os.Stderr
-30
View File
@@ -1,30 +0,0 @@
// +build darwin freebsd tinygo.wasm
// This implementation of crypto/rand uses the arc4random_buf function
// (available on both MacOS and WASI) to generate random numbers.
//
// Note: arc4random_buf (unlike what the name suggets) does not use the insecure
// RC4 cipher. Instead, it uses a high-quality cipher, varying by the libc
// implementation.
package rand
import "unsafe"
func init() {
Reader = &reader{}
}
type reader struct {
}
func (r *reader) Read(b []byte) (n int, err error) {
if len(b) != 0 {
libc_arc4random_buf(unsafe.Pointer(&b[0]), uint(len(b)))
}
return len(b), nil
}
// void arc4random_buf(void *buf, size_t buflen);
//export arc4random_buf
func libc_arc4random_buf(buf unsafe.Pointer, buflen uint)
+38
View File
@@ -0,0 +1,38 @@
// +build darwin freebsd wasi
// This implementation of crypto/rand uses the getentropy system call (available
// on both MacOS and WASI) to generate random numbers.
package rand
import (
"errors"
"unsafe"
)
var errReadFailed = errors.New("rand: could not read random bytes")
func init() {
Reader = &reader{}
}
type reader struct {
}
func (r *reader) Read(b []byte) (n int, err error) {
if len(b) != 0 {
if len(b) > 256 {
b = b[:256]
}
result := libc_getentropy(unsafe.Pointer(&b[0]), len(b))
if result < 0 {
// Maybe we should return a syscall.Errno here?
return 0, errReadFailed
}
}
return len(b), nil
}
// int getentropy(void *buf, size_t buflen);
//export getentropy
func libc_getentropy(buf unsafe.Pointer, buflen int) int
-49
View File
@@ -1,49 +0,0 @@
// This is a very minimal bootloader for the ESP32-C3. 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.
//
// For reference, here is a nice introduction into RISC-V assembly:
// https://www.imperialviolet.org/2016/12/31/riscv.html
.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 DROM from flash.
// Somehow, this also sets up IROM from flash. Not sure why, but it avoids
// the need for another such call.
// C equivalent:
// Cache_Dbus_MMU_Set(MMU_ACCESS_FLASH, 0x3C00_0000, 0, 64, 128, 0)
li a0, 0 // ext_ram: MMU_ACCESS_FLASH
li a1, 0x3C000000 // vaddr: address in the data bus
li a2, 0 // paddr: physical address in the flash chip
li a3, 64 // psize: always 64 (kilobytes)
li a4, 128 // num: pages to be set (8192K / 64K = 128)
li a5, 0 // fixed
call Cache_Dbus_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.
// (It appears that the linker relaxes this jump and instead inserts the
// _start function right after here).
j _start
+14 -16
View File
@@ -2,7 +2,6 @@
// Type definitions, fields, and constants associated with the MPU peripheral
// of the NXP MIMXRT1062.
//go:build nxp && mimxrt1062
// +build nxp,mimxrt1062
package nxp
@@ -217,12 +216,6 @@ func enableIcache(enable bool) {
}
}
var (
dcacheCcsidr volatile.Register32
dcacheSets volatile.Register32
dcacheWays volatile.Register32
)
func enableDcache(enable bool) {
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
if enable {
@@ -251,6 +244,11 @@ func enableDcache(enable bool) {
isb 0xF
`, nil)
} else {
var (
ccsidr volatile.Register32
sets volatile.Register32
ways volatile.Register32
)
SystemControl.CSSELR.Set(0)
arm.AsmFull(`
dsb 0xF
@@ -259,17 +257,17 @@ func enableDcache(enable bool) {
arm.AsmFull(`
dsb 0xF
`, nil)
dcacheCcsidr.Set(SystemControl.CCSIDR.Get())
dcacheSets.Set((dcacheCcsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
for dcacheSets.Get() != 0 {
dcacheWays.Set((dcacheCcsidr.Get() & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos)
for dcacheWays.Get() != 0 {
ccsidr.Set(SystemControl.CCSIDR.Get())
sets.Set((ccsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
for sets.Get() != 0 {
ways.Set((ccsidr.Get() & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos)
for ways.Get() != 0 {
SystemControl.DCCISW.Set(
((dcacheSets.Get() << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |
((dcacheWays.Get() << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk))
dcacheWays.Set(dcacheWays.Get() - 1)
((sets.Get() << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |
((ways.Get() << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk))
ways.Set(ways.Get() - 1)
}
dcacheSets.Set(dcacheSets.Get() - 1)
sets.Set(sets.Get() - 1)
}
arm.AsmFull(`
dsb 0xF
+22
View File
@@ -0,0 +1,22 @@
package main
import (
"embed"
"log"
)
//go:embed file*.txt
//go:embed index.html styles.css
var files embed.FS
func main() {
println(".....")
println(msg)
contents, err := files.ReadDir(".")
if err != nil {
log.Fatal(err)
}
for _, c := range contents {
println("file:", c.Name())
}
}
+1
View File
@@ -0,0 +1 @@
file 1
+1
View File
@@ -0,0 +1 @@
file 2
+1
View File
@@ -0,0 +1 @@
file 3
View File
+1
View File
@@ -0,0 +1 @@
hello world!
View File
+6
View File
@@ -0,0 +1,6 @@
package main
import _ "embed"
//go:embed message.txt
var msg string
-11
View File
@@ -1,11 +0,0 @@
// +build pico
package main
import "machine"
var (
pwm = machine.PWM4 // Pin 25 (LED on pico) corresponds to PWM4.
pinA = machine.LED
pinB = machine.GPIO24
)
-3
View File
@@ -16,9 +16,6 @@ type calleeSavedRegs struct {
ebp uintptr
pc uintptr
// Pad this struct so that tasks start on a 16-byte aligned stack.
_ [3]uintptr
}
// archInit runs architecture-specific setup for the goroutine startup.
-5
View File
@@ -72,8 +72,3 @@ tinygo_swapTask:
// Return into the new task, as if tinygo_swapTask was a regular call.
ret
#ifdef __MACH__ // Darwin
// allow these symbols to stripped as dead code
.subsections_via_symbols
#endif
-154
View File
@@ -1,154 +0,0 @@
// +build arduino_mkrwifi1010
// This contains the pin mappings for the Arduino MKR WiFi 1010 board.
//
// For more information, see: https://store.arduino.cc/usa/mkr-wifi-1010
//
package machine
import (
"device/sam"
"runtime/interrupt"
)
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0x07738135
// GPIO Pins
const (
RX0 Pin = PB23 // UART1 RX
TX1 Pin = PB22 // UART1 TX
D0 Pin = PA22 // PWM available
D1 Pin = PA23 // PWM available
D2 Pin = PA10 // PWM available
D3 Pin = PA11 // PWM available
D4 Pin = PB10 // PWM available
D5 Pin = PB11 // PWM available
D6 Pin = PA20 // PWM available
D7 Pin = PA21 // PWM available
D8 Pin = PA16 // PWM available
D9 Pin = PA17
D10 Pin = PA19 // PWM available
D11 Pin = PA08 // SDA
D12 Pin = PA09 // PWM available, SCL
D13 Pin = PB23 // RX
D14 Pin = PB22 // TX
)
// Analog pins
const (
A0 Pin = PA02 // ADC0/AIN[0]
A1 Pin = PB02 // AIN[10]
A2 Pin = PB03 // AIN[11]
A3 Pin = PA04 // AIN[04]
A4 Pin = PA05 // AIN[05]
A5 Pin = PA06 // AIN[06]
A6 Pin = PA07 // AIN[07]
)
const (
LED = D6
)
// USBCDC pins
const (
USBCDC_DM_PIN Pin = PA24
USBCDC_DP_PIN Pin = PA25
)
// UART1 pins
const (
UART_TX_PIN Pin = PB22
UART_RX_PIN Pin = PB23
)
// I2C pins
const (
SDA_PIN Pin = D11 // SDA
SCL_PIN Pin = D12 // SCL
)
// SPI pins
const (
SPI0_SCK_PIN Pin = D9 // SCK: S1
SPI0_SDO_PIN Pin = D8 // SDO: S1
SPI0_SDI_PIN Pin = D10 // SDI: S1
)
// I2S pins
const (
I2S_SCK_PIN Pin = PA10
I2S_SD_PIN Pin = PA07
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino MKR WiFi 1010.
)
// NINA-W102 Pins
const (
NINA_SDO Pin = PA12
NINA_SDI Pin = PA13
NINA_CS Pin = PA14
NINA_SCK Pin = PA15
NINA_GPIO0 Pin = PA27
NINA_RESETN Pin = PB08
NINA_ACK Pin = PA28
NINA_TX Pin = PA22
NINA_RX Pin = PA23
)
// UART on the Arduino MKR WiFi 1010.
var (
UART1 = &_UART1
_UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM5_USART,
SERCOM: 5,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5, _UART1.handleInterrupt)
}
// I2C on the Arduino MKR WiFi 1010.
var (
I2C0 = &I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI on the Arduino MKR WiFi 1010.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPI,
SERCOM: 1,
}
SPI1 = SPI{
Bus: sam.SERCOM4_SPI,
SERCOM: 4,
}
NINA_SPI = SPI1
)
// I2S on the Arduino MKR WiFi 1010.
var (
I2S0 = I2S{Bus: sam.I2S}
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Arduino MKR WiFi 1010"
usb_STRING_MANUFACTURER = "Arduino"
)
var (
usb_VID uint16 = 0x2341
usb_PID uint16 = 0x8054
)
var (
DefaultUART = UART1
)
@@ -83,7 +83,3 @@ var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x8045
)
var (
DefaultUART = UART0
)
@@ -47,7 +47,3 @@ var (
var (
I2S0 = I2S{Bus: sam.I2S}
)
var (
DefaultUART = UART1
)
-4
View File
@@ -127,7 +127,3 @@ var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x8072
)
var (
DefaultUART = UART0
)
-4
View File
@@ -114,7 +114,3 @@ var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x801B
)
var (
DefaultUART = UART1
)
-4
View File
@@ -154,7 +154,3 @@ var (
Bus: sam.CAN1,
}
)
var (
DefaultUART = UART1
)
@@ -43,7 +43,3 @@ var (
SERCOM: 1,
}
)
var (
DefaultUART = UART1
)
+2 -6
View File
@@ -72,8 +72,8 @@ const (
// UART0 pins (logical UART1)
const (
UART_RX_PIN = D1
UART_TX_PIN = D0
UART_RX_PIN = D0
UART_TX_PIN = D1
)
// I2C pins
@@ -99,7 +99,3 @@ var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x8088
)
var (
DefaultUART = UART0
)
+2 -6
View File
@@ -72,8 +72,8 @@ const (
// UART0 pins (logical UART1)
const (
UART_RX_PIN = D1
UART_TX_PIN = D0
UART_RX_PIN = D0
UART_TX_PIN = D1
)
// I2C pins
@@ -99,7 +99,3 @@ var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x802A
)
var (
DefaultUART = UART0
)
-48
View File
@@ -8,51 +8,3 @@ const (
// Onboard crystal oscillator frequency, in MHz.
xoscFreq = 12 // MHz
)
// GPIO Pins
const (
D4 = GPIO6
D5 = GPIO7
D6 = GPIO8
D9 = GPIO9
D10 = GPIO10
D11 = GPIO11
D12 = GPIO12
D13 = GPIO13
D24 = GPIO24
D25 = GPIO25
)
// Analog pins
const (
A0 = GPIO26
A1 = GPIO27
A2 = GPIO28
A3 = GPIO29
)
// I2C Pins.
const (
I2C0_SDA_PIN = GPIO24
I2C0_SCL_PIN = GPIO25
I2C1_SDA_PIN = GPIO2
I2C1_SCL_PIN = GPIO3
)
// 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 = GPIO20 // 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
)
@@ -65,7 +65,3 @@ var (
SERCOM: 2,
}
)
var (
DefaultUART = UART1
)
-4
View File
@@ -135,7 +135,3 @@ var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x800F
)
var (
DefaultUART = UART1
)
@@ -43,7 +43,3 @@ var (
SERCOM: 1,
}
)
var (
DefaultUART = UART1
)
-4
View File
@@ -93,7 +93,3 @@ var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x8051
)
var (
DefaultUART = UART0
)
@@ -50,7 +50,3 @@ var (
SERCOM: 0,
}
)
var (
DefaultUART = UART1
)
-40
View File
@@ -1,40 +0,0 @@
// +build mdbt50qrx
package machine
const HasLowFrequencyCrystal = false
// GPIO Pins
const (
D0 = P1_13 // LED1
D1 = P1_11 // LED2 (not populated by default)
D2 = P0_15 // Button
)
const (
LED = D0
)
// MDBT50Q-RX dongle does not have pins broken out for the peripherals below,
// however the machine_nrf*.go implementations of I2C/SPI/etc expect the pin
// constants to be defined, so we are defining them all as 0
const (
UART_TX_PIN = 0
UART_RX_PIN = 0
SDA_PIN = 0
SCL_PIN = 0
SPI0_SCK_PIN = 0
SPI0_SDO_PIN = 0
SPI0_SDI_PIN = 0
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Raytac MDBT50Q - RX"
usb_STRING_MANUFACTURER = "Raytac Corporation"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x810B
)
@@ -52,7 +52,3 @@ var (
SERCOM: 1,
}
)
var (
DefaultUART = UART1
)
+4 -16
View File
@@ -49,27 +49,15 @@ const (
// I2C pins
const (
I2C0_SDA_PIN Pin = GPIO12
I2C0_SCL_PIN Pin = GPIO13
I2C1_SDA_PIN Pin = GPIO18
I2C1_SCL_PIN Pin = GPIO19
SDA_PIN Pin = GPIO12
SCL_PIN Pin = GPIO13
)
// SPI pins. SPI1 not available on Nano RP2040 Connect.
// SPI pins
const (
SPI0_SCK_PIN Pin = GPIO6
SPI0_SDO_PIN Pin = GPIO7
SPI0_SDI_PIN Pin = GPIO4
// GPIO22 does not have SPI functionality so we set it to avoid interfering with NINA.
SPI1_SCK_PIN Pin = GPIO22
SPI1_SDO_PIN Pin = GPIO22
SPI1_SDI_PIN Pin = GPIO22
)
var (
NINA_SPI = SPI1
)
// NINA-W102 Pins
@@ -80,7 +68,7 @@ const (
NINA_CS Pin = GPIO9
NINA_ACK Pin = GPIO10
NINA_GPIO0 Pin = GPIO2
NINA_GPIO0 Pin = GPIO0
NINA_RESETN Pin = GPIO3
NINA_TX Pin = GPIO9
-26
View File
@@ -37,29 +37,3 @@ const (
// 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
)
-4
View File
@@ -51,7 +51,3 @@ var (
SERCOM: 4,
}
)
var (
DefaultUART = UART1
)
-4
View File
@@ -36,7 +36,3 @@ var (
}
NINA_SPI = SPI0
)
var (
DefaultUART = UART1
)
-4
View File
@@ -119,7 +119,3 @@ var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x80CB
)
var (
DefaultUART = UART1
)
+10 -14
View File
@@ -88,20 +88,15 @@ const (
I2C_SCL_PIN = I2C1_SCL_PIN // D19/A5
)
// Default peripherals
var (
DefaultUART = UART1
)
func init() {
// register any interrupt handlers for this board's peripherals
_UART1.Interrupt = interrupt.New(nxp.IRQ_LPUART6, _UART1.handleInterrupt)
_UART2.Interrupt = interrupt.New(nxp.IRQ_LPUART4, _UART2.handleInterrupt)
_UART3.Interrupt = interrupt.New(nxp.IRQ_LPUART2, _UART3.handleInterrupt)
_UART4.Interrupt = interrupt.New(nxp.IRQ_LPUART3, _UART4.handleInterrupt)
_UART5.Interrupt = interrupt.New(nxp.IRQ_LPUART8, _UART5.handleInterrupt)
_UART6.Interrupt = interrupt.New(nxp.IRQ_LPUART1, _UART6.handleInterrupt)
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
UART1.Interrupt = interrupt.New(nxp.IRQ_LPUART6, _UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(nxp.IRQ_LPUART4, _UART2.handleInterrupt)
UART3.Interrupt = interrupt.New(nxp.IRQ_LPUART2, _UART3.handleInterrupt)
UART4.Interrupt = interrupt.New(nxp.IRQ_LPUART3, _UART4.handleInterrupt)
UART5.Interrupt = interrupt.New(nxp.IRQ_LPUART8, _UART5.handleInterrupt)
UART6.Interrupt = interrupt.New(nxp.IRQ_LPUART1, _UART6.handleInterrupt)
UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
}
// #=====================================================#
@@ -141,8 +136,9 @@ const (
)
var (
UART1 = &_UART1
_UART1 = UART{
DefaultUART = UART1
UART1 = &_UART1
_UART1 = UART{
Bus: nxp.LPUART6,
Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(),
-4
View File
@@ -105,7 +105,3 @@ var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x801E
)
var (
DefaultUART = UART1
)
+1 -1
View File
@@ -1,4 +1,4 @@
// +build atmega nrf sam stm32 fe310 k210 rp2040
// +build atmega nrf sam stm32 fe310 k210
package machine
+1 -1
View File
@@ -1958,7 +1958,7 @@ func handlePadCalibration() {
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
if calibTrim == 0x7 {
calibTrim = 3
calibTransN = 3
}
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
}
+1 -1
View File
@@ -2167,7 +2167,7 @@ func handlePadCalibration() {
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
if calibTrim == 0x7 {
calibTrim = 3
calibTransN = 3
}
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
}
-144
View File
@@ -1,144 +0,0 @@
// +build esp32c3
package machine
import (
"device/esp"
"runtime/volatile"
"unsafe"
)
// 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
)
// 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
// Make this pin an input pin (always).
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.
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:
// Clear the 'output enable' bit.
esp.GPIO.ENABLE_W1TC.Set(1 << 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.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.FUNC0_OUT_SEL_CFG)) + 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.Pointer((uintptr(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.Pointer((uintptr(unsafe.Pointer(&esp.IO_MUX.GPIO0)) + 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)
}
}
// 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.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.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
}
var DefaultUART = UART0
var (
UART0 = &_UART0
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
UART1 = &_UART1
_UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
)
type UART struct {
Bus *esp.UART_Type
Buffer *RingBuffer
}
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
}
+67 -41
View File
@@ -33,6 +33,7 @@ type UART struct {
// auxiliary state data used internally
configured bool
msbFirst bool
transmitting volatile.Register32
}
@@ -55,14 +56,13 @@ func (uart *UART) Configure(config UARTConfig) {
if config.BaudRate == 0 {
config.BaudRate = defaultUartFreq
}
uart.baud = config.BaudRate
// use default UART pins if not specified
if config.RX == 0 && config.TX == 0 {
config.RX = UART_RX_PIN
config.TX = UART_TX_PIN
}
uart.baud = config.BaudRate
uart.rx = config.RX
uart.tx = config.TX
@@ -77,38 +77,9 @@ func (uart *UART) Configure(config UARTConfig) {
// reset all internal logic and registers
uart.resetTransmitting()
// disable until we have finished configuring registers
uart.Bus.CTRL.Set(0)
// determine the baud rate and over-sample divisors
sbr, osr := uart.getBaudRateDivisor(uart.baud)
// set the baud rate, over-sample configuration, stop bits
baudBits := (((osr - 1) << nxp.LPUART_BAUD_OSR_Pos) & nxp.LPUART_BAUD_OSR_Msk) |
((sbr << nxp.LPUART_BAUD_SBR_Pos) & nxp.LPUART_BAUD_SBR_Msk)
if osr <= 8 {
// if OSR less than or equal to 8, we must enable sampling on both edges
baudBits |= nxp.LPUART_BAUD_BOTHEDGE
}
uart.Bus.BAUD.Set(baudBits)
uart.Bus.PINCFG.Set(0) // disable triggers
// configure watermarks, flush and enable TX/RX FIFOs
rxSize, txSize := uart.getFIFOSize()
rxWater := rxSize >> 1
if rxWater > uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk>>nxp.LPUART_FIFO_RXFIFOSIZE_Pos) {
rxWater = uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk >> nxp.LPUART_FIFO_RXFIFOSIZE_Pos)
}
txWater := txSize >> 1
if txWater > uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk>>nxp.LPUART_FIFO_TXFIFOSIZE_Pos) {
txWater = uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk >> nxp.LPUART_FIFO_TXFIFOSIZE_Pos)
}
uart.Bus.WATER.Set(
((rxWater << nxp.LPUART_WATER_RXWATER_Pos) & nxp.LPUART_WATER_RXWATER_Msk) |
((txWater << nxp.LPUART_WATER_TXWATER_Pos) & nxp.LPUART_WATER_TXWATER_Msk))
uart.Bus.FIFO.SetBits(nxp.LPUART_FIFO_RXFE | nxp.LPUART_FIFO_TXFE |
nxp.LPUART_FIFO_RXFLUSH | nxp.LPUART_FIFO_TXFLUSH)
// for now we assume some configuration. in particular:
// Data bits -> 8-bit
// Parity bit -> None (parity bit generation disabled)
@@ -119,16 +90,71 @@ func (uart *UART) Configure(config UARTConfig) {
// RX RTS enabled -> false
// TX CTS enabled -> false
// enable transmitter, receiver functions
uart.Bus.CTRL.Set(nxp.LPUART_CTRL_TE | nxp.LPUART_CTRL_RE |
// enable receiver, idle line interrupts
nxp.LPUART_CTRL_RIE | nxp.LPUART_CTRL_ILIE)
// set the baud rate, over-sample configuration, stop bits
baudBits := (((osr - 1) << nxp.LPUART_BAUD_OSR_Pos) & nxp.LPUART_BAUD_OSR_Msk) |
((sbr << nxp.LPUART_BAUD_SBR_Pos) & nxp.LPUART_BAUD_SBR_Msk) |
((nxp.LPUART_BAUD_SBNS_SBNS_0 << nxp.LPUART_BAUD_SBNS_Pos) & nxp.LPUART_BAUD_SBNS_Msk)
if osr <= 8 {
// if OSR less than or equal to 8, we must enable sampling on both edges
baudBits |= nxp.LPUART_BAUD_BOTHEDGE
}
uart.Bus.BAUD.Set(baudBits)
uart.Bus.PINCFG.Set(0) // disable triggers
// use 8 data bits, disable parity, use 1 idle char, and idle count starts
// after start bit
ctrlBits := uint32(((nxp.LPUART_CTRL_M_M_0 << nxp.LPUART_CTRL_M_Pos) & nxp.LPUART_CTRL_M_Msk) |
((nxp.LPUART_CTRL_PE_PE_0 << nxp.LPUART_CTRL_PE_Pos) & nxp.LPUART_CTRL_PE_Msk) |
((nxp.LPUART_CTRL_ILT_ILT_0 << nxp.LPUART_CTRL_ILT_Pos) & nxp.LPUART_CTRL_ILT_Msk) |
((nxp.LPUART_CTRL_IDLECFG_IDLECFG_0 << nxp.LPUART_CTRL_IDLECFG_Pos) & nxp.LPUART_CTRL_IDLECFG_Msk))
uart.Bus.CTRL.Set(ctrlBits)
rxSize, txSize := uart.getFIFOSize()
rxWater := rxSize >> 1
if rxWater > uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk>>nxp.LPUART_FIFO_RXFIFOSIZE_Pos) {
rxWater = uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk >> nxp.LPUART_FIFO_RXFIFOSIZE_Pos)
}
txWater := txSize >> 1
if txWater > uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk>>nxp.LPUART_FIFO_TXFIFOSIZE_Pos) {
txWater = uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk >> nxp.LPUART_FIFO_TXFIFOSIZE_Pos)
}
uart.Bus.WATER.Set(
((rxWater << nxp.LPUART_WATER_RXWATER_Pos) & nxp.LPUART_WATER_RXWATER_Msk) |
((txWater << nxp.LPUART_WATER_TXWATER_Pos) & nxp.LPUART_WATER_TXWATER_Msk))
// enable TX/RX FIFOs
uart.Bus.FIFO.SetBits(nxp.LPUART_FIFO_RXFE | nxp.LPUART_FIFO_TXFE)
// flush TX/RX FIFOs
uart.Bus.FIFO.SetBits(nxp.LPUART_FIFO_RXFLUSH | nxp.LPUART_FIFO_TXFLUSH)
uart.Bus.MODIR.SetBits( // set the CTS configuration/TX CTS source
((nxp.LPUART_MODIR_TXCTSC_TXCTSC_0 << nxp.LPUART_MODIR_TXCTSC_Pos) & nxp.LPUART_MODIR_TXCTSC_Msk) |
((nxp.LPUART_MODIR_TXCTSSRC_TXCTSSRC_0 << nxp.LPUART_MODIR_TXCTSSRC_Pos) & nxp.LPUART_MODIR_TXCTSSRC_Msk))
// clear all status flags
uart.Bus.STAT.Set(uart.Bus.STAT.Get())
stat := uint32(nxp.LPUART_STAT_RXEDGIF_Msk | nxp.LPUART_STAT_IDLE_Msk | nxp.LPUART_STAT_OR_Msk |
nxp.LPUART_STAT_NF_Msk | nxp.LPUART_STAT_FE_Msk | nxp.LPUART_STAT_PF_Msk |
nxp.LPUART_STAT_LBKDIF_Msk | nxp.LPUART_STAT_MA1F_Msk | nxp.LPUART_STAT_MA2F_Msk)
// enable RX interrupt
uart.Interrupt.SetPriority(0xC0)
// set data bits order
if uart.msbFirst {
stat |= nxp.LPUART_STAT_MSBF
} else {
stat &^= nxp.LPUART_STAT_MSBF
}
uart.Bus.STAT.SetBits(stat)
// enable RX/TX functions
uart.Bus.CTRL.SetBits(nxp.LPUART_CTRL_TE | nxp.LPUART_CTRL_RE)
// enable RX IRQ
uart.Interrupt.SetPriority(0xc0)
uart.Interrupt.Enable()
uart.configured = true
@@ -185,13 +211,13 @@ func (uart *UART) WriteByte(c byte) error {
// corresponding baud rate divisor (1..8191) that best partition a given baud
// rate into equal intervals.
//
// This is an integral (non-floating point) translation of the logic at the
// This is an integral (i.e. non-floating point) port of the logic at the
// beginning of:
// void HardwareSerial::begin(uint32_t baud, uint16_t format)
// (from Teensyduino: `cores/teensy4/HardwareSerial.cpp`)
//
// We don't want to use floating point here in case it gets called from an ISR
// or very early during system init.
// We don't want to risk using floating point here in the machine package in
// case it gets called before the FPU or interrupts are ready (e.g., init()).
func (uart *UART) getBaudRateDivisor(baudRate uint32) (sbr uint32, osr uint32) {
const clock = 24000000 // UART is muxed to 24 MHz OSC
err := uint32(0xFFFFFFFF)
+6 -43
View File
@@ -47,27 +47,14 @@ type pinFunc uint8
// GPIO function selectors
const (
fnJTAG pinFunc = 0
fnSPI pinFunc = 1 // Connect one of the internal PL022 SPI peripherals to GPIO
fnSPI pinFunc = 1
fnUART pinFunc = 2
fnI2C pinFunc = 3
// Connect a PWM slice to GPIO. There are eight PWM slices,
// each with two outputchannels (A/B). The B pin can also be used as an input,
// for frequency and duty cyclemeasurement
fnPWM pinFunc = 4
// Software control of GPIO, from the single-cycle IO (SIO) block.
// The SIO function (F5)must be selected for the processors to drive a GPIO,
// but the input is always connected,so software can check the state of GPIOs at any time.
fnSIO pinFunc = 5
// Connect one of the programmable IO blocks (PIO) to GPIO. PIO can implement a widevariety of interfaces,
// and has its own internal pin mapping hardware, allowing flexibleplacement of digital interfaces on bank 0 GPIOs.
// The PIO function (F6, F7) must beselected for PIO to drive a GPIO, but the input is always connected,
// so the PIOs canalways see the state of all pins.
fnPIO0, fnPIO1 pinFunc = 6, 7
// General purpose clock inputs/outputs. Can be routed to a number of internal clock domains onRP2040,
// e.g. Input: to provide a 1 Hz clock for the RTC, or can be connected to an internalfrequency counter.
// e.g. Output: optional integer divide
fnPWM pinFunc = 4
fnSIO pinFunc = 5
fnPIO0 pinFunc = 6
fnPIO1 pinFunc = 7
fnGPCK pinFunc = 8
// USB power control signals to/from the internal USB controller
fnUSB pinFunc = 9
fnNULL pinFunc = 0x1f
@@ -81,9 +68,6 @@ const (
PinInputPullup
PinAnalog
PinUART
PinPWM
PinI2C
PinSPI
)
// set drives the pin high
@@ -106,7 +90,7 @@ func (p Pin) xor() {
// get returns the pin value
func (p Pin) get() bool {
return rp.SIO.GPIO_IN.HasBits(1 << p)
return rp.SIO.GPIO_IN.HasBits(uint32(1) << p)
}
func (p Pin) ioCtrl() *volatile.Register32 {
@@ -132,17 +116,6 @@ func (p Pin) pulloff() {
p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE)
}
// setSlew sets pad slew rate control.
// true sets to fast. false sets to slow.
func (p Pin) setSlew(sr bool) {
p.padCtrl().ReplaceBits(boolToBit(sr)<<rp.PADS_BANK0_GPIO0_SLEWFAST_Pos, rp.PADS_BANK0_GPIO0_SLEWFAST_Msk, 0)
}
// setSchmitt enables or disables Schmitt trigger.
func (p Pin) setSchmitt(trigger bool) {
p.padCtrl().ReplaceBits(boolToBit(trigger)<<rp.PADS_BANK0_GPIO0_SCHMITT_Pos, rp.PADS_BANK0_GPIO0_SCHMITT_Msk, 0)
}
// setFunc will set pin function to fn.
func (p Pin) setFunc(fn pinFunc) {
// Set input enable, Clear output disable
@@ -182,16 +155,6 @@ func (p Pin) Configure(config PinConfig) {
p.pulloff()
case PinUART:
p.setFunc(fnUART)
case PinPWM:
p.setFunc(fnPWM)
case PinI2C:
// IO config according to 4.3.1.3 of rp2040 datasheet.
p.setFunc(fnI2C)
p.pullup()
p.setSchmitt(true)
p.setSlew(false)
case PinSPI:
p.setFunc(fnSPI)
}
}
-461
View File
@@ -1,461 +0,0 @@
// +build rp2040
package machine
import (
"device/rp"
"errors"
"strconv"
)
// I2C on the RP2040.
var (
I2C0 = &_I2C0
_I2C0 = I2C{
Bus: rp.I2C0,
}
I2C1 = &_I2C1
_I2C1 = I2C{
Bus: rp.I2C1,
}
)
// Features: Taken from datasheet.
// Default master mode, with slave mode available (not simulataneously).
// Default slave address of RP2040: 0x055
// Supports 10-bit addressing in Master mode
// 16-element transmit buffer
// 16-element receive buffer
// Can be driven from DMA
// Can generate interrupts
// Fast mode plus max transfer speed (1000kb/s)
// GPIO config
// Each controller must connect its clock SCL and data SDA to one pair of GPIOs.
// The I2C standard requires that drivers drivea signal low, or when not driven the signal will be pulled high.
// This applies to SCL and SDA. The GPIO pads should beconfigured for:
// Pull-up enabled
// Slew rate limited
// Schmitt trigger enabled
// Note: There should also be external pull-ups on the board as the internal pad pull-ups may not be strong enough to pull upexternal circuits.
// I2CConfig is used to store config info for I2C.
type I2CConfig struct {
Frequency uint32
// SDA/SCL Serial Data and clock pins. Refer to datasheet to see
// which pins match the desired bus.
SDA, SCL Pin
}
type I2C struct {
Bus *rp.I2C0_Type
restartOnNext bool
}
var (
ErrInvalidI2CBaudrate = errors.New("invalid i2c baudrate")
ErrInvalidTgtAddr = errors.New("invalid target i2c address not in 0..0x80 or is reserved")
ErrI2CGeneric = errors.New("i2c error")
ErrRP2040I2CDisable = errors.New("i2c rp2040 peripheral timeout in disable")
)
// Tx performs a write and then a read transfer placing the result in
// in r.
//
// Passing a nil value for w or r skips the transfer corresponding to write
// or read, respectively.
//
// i2c.Tx(addr, nil, r)
// Performs only a read transfer.
//
// i2c.Tx(addr, w, nil)
// Performs only a write transfer.
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
// timeout in microseconds.
const timeout = 40 * 1000 // 40ms is a reasonable time for a real-time system.
if len(w) > 0 {
if err := i2c.tx(uint8(addr), w, false, timeout); nil != err {
return err
}
}
if len(r) > 0 {
if err := i2c.rx(uint8(addr), r, false, timeout); nil != err {
return err
}
}
return nil
}
// Configure initializes i2c peripheral and configures I2C config's pins passed.
// Here's a list of valid SDA and SCL GPIO pins on bus I2C0 of the rp2040:
// SDA: 0, 4, 8, 12, 16, 20
// SCL: 1, 5, 9, 13, 17, 21
// Same as above for I2C1 bus:
// SDA: 2, 6, 10, 14, 18, 26
// SCL: 3, 7, 11, 15, 19, 27
func (i2c *I2C) Configure(config I2CConfig) error {
if config.SCL == 0 {
// If config pins are zero valued or clock pin is invalid then we set default values.
switch i2c.Bus {
case rp.I2C0:
config.SCL = I2C0_SCL_PIN
config.SDA = I2C0_SDA_PIN
case rp.I2C1:
config.SCL = I2C1_SCL_PIN
config.SDA = I2C1_SDA_PIN
}
}
config.SDA.Configure(PinConfig{PinI2C})
config.SCL.Configure(PinConfig{PinI2C})
return i2c.init(config)
}
// SetBaudrate sets the I2C frequency. It has the side effect of also
// enabling the I2C hardware if disabled beforehand.
//go:inline
func (i2c *I2C) SetBaudrate(br uint32) error {
const freqin uint32 = 125 * MHz
// Find smallest prescale value which puts o
// TODO there are some subtleties to I2C timing which we are completely ignoring here
period := (freqin + br/2) / br
lcnt := period * 3 / 5 // oof this one hurts
hcnt := period - lcnt
// Check for out-of-range divisors:
if hcnt > rp.I2C0_IC_FS_SCL_HCNT_IC_FS_SCL_HCNT_Msk || hcnt < 8 || lcnt > rp.I2C0_IC_FS_SCL_LCNT_IC_FS_SCL_LCNT_Msk || lcnt < 8 {
return ErrInvalidI2CBaudrate
}
// Per I2C-bus specification a device in standard or fast mode must
// internally provide a hold time of at least 300ns for the SDA signal to
// bridge the undefined region of the falling edge of SCL. A smaller hold
// time of 120ns is used for fast mode plus.
// sda_tx_hold_count = freq_in [cycles/s] * 300ns * (1s / 1e9ns)
// Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint.
// Add 1 to avoid division truncation.
sdaTxHoldCnt := ((freqin * 3) / 10000000) + 1
if br >= 1_000_000 {
// sda_tx_hold_count = freq_in [cycles/s] * 120ns * (1s / 1e9ns)
// Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint.
// Add 1 to avoid division truncation.
sdaTxHoldCnt = ((freqin * 3) / 25000000) + 1
}
if sdaTxHoldCnt > lcnt-2 {
return ErrInvalidI2CBaudrate
}
err := i2c.disable()
if err != nil {
return err
}
// Always use "fast" mode (<= 400 kHz, works fine for standard mode too)
i2c.Bus.IC_CON.ReplaceBits(rp.I2C0_IC_CON_SPEED_FAST<<rp.I2C0_IC_CON_SPEED_Pos, rp.I2C0_IC_CON_SPEED_Msk, 0)
i2c.Bus.IC_FS_SCL_HCNT.Set(hcnt)
i2c.Bus.IC_FS_SCL_LCNT.Set(lcnt)
i2c.Bus.IC_FS_SPKLEN.Set(u32max(1, lcnt/16))
i2c.Bus.IC_SDA_HOLD.ReplaceBits(sdaTxHoldCnt<<rp.I2C0_IC_SDA_HOLD_IC_SDA_TX_HOLD_Pos, rp.I2C0_IC_SDA_HOLD_IC_SDA_TX_HOLD_Msk, 0)
i2c.enable()
return nil
}
//go:inline
func (i2c *I2C) enable() {
i2c.Bus.IC_ENABLE.ReplaceBits(rp.I2C0_IC_ENABLE_ENABLE<<rp.I2C0_IC_ENABLE_ENABLE_Pos, rp.I2C0_IC_ENABLE_ENABLE_Msk, 0)
}
// Implemented as per 4.3.10.3. Disabling DW_apb_i2c section.
//go:inline
func (i2c *I2C) disable() error {
const MAX_T_POLL_COUNT = 64 // 64 us timeout corresponds to around 1000kb/s i2c transfer rate.
deadline := ticks() + MAX_T_POLL_COUNT
i2c.Bus.IC_ENABLE.Set(0)
for i2c.Bus.IC_ENABLE_STATUS.Get()&1 != 0 {
if ticks() > deadline {
return ErrRP2040I2CDisable
}
}
return nil
}
//go:inline
func (i2c *I2C) init(config I2CConfig) error {
i2c.reset()
if err := i2c.disable(); err != nil {
return err
}
i2c.restartOnNext = false
// Configure as a fast-mode master with RepStart support, 7-bit addresses
i2c.Bus.IC_CON.Set((rp.I2C0_IC_CON_SPEED_FAST << rp.I2C0_IC_CON_SPEED_Pos) |
rp.I2C0_IC_CON_MASTER_MODE | rp.I2C0_IC_CON_IC_SLAVE_DISABLE |
rp.I2C0_IC_CON_IC_RESTART_EN | rp.I2C0_IC_CON_TX_EMPTY_CTRL) // sets TX_EMPTY_CTRL to enable TX_EMPTY interrupt status
// Set FIFO watermarks to 1 to make things simpler. This is encoded by a register value of 0.
i2c.Bus.IC_TX_TL.Set(0)
i2c.Bus.IC_RX_TL.Set(0)
// Always enable the DREQ signalling -- harmless if DMA isn't listening
i2c.Bus.IC_DMA_CR.Set(rp.I2C0_IC_DMA_CR_TDMAE | rp.I2C0_IC_DMA_CR_RDMAE)
return i2c.SetBaudrate(config.Frequency)
}
// reset sets I2C register RESET bits in the reset peripheral and then clears them.
//go:inline
func (i2c *I2C) reset() {
resetVal := i2c.deinit()
rp.RESETS.RESET.ClearBits(resetVal)
// Wait until reset is done.
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
}
}
// deinit sets reset bit for I2C. Must call reset to reenable I2C after deinit.
//go:inline
func (i2c *I2C) deinit() (resetVal uint32) {
switch {
case i2c.Bus == rp.I2C0:
resetVal = rp.RESETS_RESET_I2C0
case i2c.Bus == rp.I2C1:
resetVal = rp.RESETS_RESET_I2C1
}
// Perform I2C reset.
rp.RESETS.RESET.SetBits(resetVal)
return resetVal
}
// tx is a primitive i2c blocking write to bus routine. timeout is time to wait
// in microseconds since calling this function for write to finish.
func (i2c *I2C) tx(addr uint8, tx []byte, nostop bool, timeout uint64) (err error) {
deadline := ticks() + timeout
if addr >= 0x80 || isReservedI2CAddr(addr) {
return ErrInvalidTgtAddr
}
tlen := len(tx)
// Quick return if possible.
if tlen == 0 {
return nil
}
err = i2c.disable()
if err != nil {
return err
}
i2c.Bus.IC_TAR.Set(uint32(addr))
i2c.enable()
// If no timeout was passed timeoutCheck is false.
abort := false
var abortReason uint32
byteCtr := 0
for ; byteCtr < tlen; byteCtr++ {
first := byteCtr == 0
last := byteCtr == tlen-1
i2c.Bus.IC_DATA_CMD.Set(
(boolToBit(first && i2c.restartOnNext) << rp.I2C0_IC_DATA_CMD_RESTART_Pos) |
(boolToBit(last && !nostop) << rp.I2C0_IC_DATA_CMD_STOP_Pos) |
uint32(tx[byteCtr]))
// Wait until the transmission of the address/data from the internal
// shift register has completed. For this to function correctly, the
// TX_EMPTY_CTRL flag in IC_CON must be set. The TX_EMPTY_CTRL flag
// was set in i2c_init.
// IC_RAW_INTR_STAT_TX_EMPTY: This bit is set to 1 when the transmit buffer is at or below
// the threshold value set in the IC_TX_TL register and the
// transmission of the address/data from the internal shift
// register for the most recently popped command is
// completed. It is automatically cleared by hardware when
// the buffer level goes above the threshold. When
// IC_ENABLE[0] is set to 0, the TX FIFO is flushed and held
// in reset. There the TX FIFO looks like it has no data within
// it, so this bit is set to 1, provided there is activity in the
// master or slave state machines. When there is no longer
// any activity, then with ic_en=0, this bit is set to 0.
for !i2c.interrupted(rp.I2C0_IC_RAW_INTR_STAT_TX_EMPTY) {
if ticks() > deadline {
i2c.restartOnNext = nostop
println(1)
return errI2CWriteTimeout // If there was a timeout, don't attempt to do anything else.
}
}
abortReason = i2c.getAbortReason()
if abortReason != 0 {
i2c.clearAbortReason()
abort = true
}
if abort || (last && !nostop) {
// If the transaction was aborted or if it completed
// successfully wait until the STOP condition has occured.
// TODO Could there be an abort while waiting for the STOP
// condition here? If so, additional code would be needed here
// to take care of the abort.
for !i2c.interrupted(rp.I2C0_IC_RAW_INTR_STAT_STOP_DET) {
if ticks() > deadline {
println(2)
i2c.restartOnNext = nostop
return errI2CWriteTimeout
}
}
i2c.Bus.IC_CLR_STOP_DET.Get()
}
}
// From Pico SDK: A lot of things could have just happened due to the ingenious and
// creative design of I2C. Try to figure things out.
if abort {
switch {
case abortReason == 0 || abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK != 0:
// No reported errors - seems to happen if there is nothing connected to the bus.
// Address byte not acknowledged
err = ErrI2CGeneric
case abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK != 0:
// Address acknowledged, some data not acknowledged
fallthrough
default:
// panic("unknown i2c abortReason:" + strconv.Itoa(abortReason)
err = makeI2CBuffError(byteCtr)
}
}
// nostop means we are now at the end of a *message* but not the end of a *transfer*
i2c.restartOnNext = nostop
return err
}
// rx is a primitive i2c blocking read routine. timeout is time to wait
// in microseconds since calling this function for read to finish.
func (i2c *I2C) rx(addr uint8, rx []byte, nostop bool, timeout uint64) (err error) {
deadline := ticks() + timeout
if addr >= 0x80 || isReservedI2CAddr(addr) {
return ErrInvalidTgtAddr
}
rlen := len(rx)
// Quick return if possible.
if rlen == 0 {
return nil
}
err = i2c.disable()
if err != nil {
return err
}
i2c.Bus.IC_TAR.Set(uint32(addr))
i2c.enable()
// If no timeout was passed timeoutCheck is false.
abort := false
var abortReason uint32
byteCtr := 0
for ; byteCtr < rlen; byteCtr++ {
first := byteCtr == 0
last := byteCtr == rlen-1
for i2c.writeAvailable() == 0 {
}
i2c.Bus.IC_DATA_CMD.Set(
boolToBit(first && i2c.restartOnNext)<<rp.I2C0_IC_DATA_CMD_RESTART_Pos |
boolToBit(last && !nostop)<<rp.I2C0_IC_DATA_CMD_STOP_Pos |
rp.I2C0_IC_DATA_CMD_CMD) // -> 1 for read
for !abort && i2c.readAvailable() == 0 {
abortReason = i2c.getAbortReason()
i2c.clearAbortReason()
if abortReason != 0 {
abort = true
}
if ticks() > deadline {
i2c.restartOnNext = nostop
return errI2CReadTimeout // If there was a timeout, don't attempt to do anything else.
}
}
if abort {
break
}
rx[byteCtr] = uint8(i2c.Bus.IC_DATA_CMD.Get())
}
if abort {
switch {
case abortReason == 0 || abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK != 0:
// No reported errors - seems to happen if there is nothing connected to the bus.
// Address byte not acknowledged
err = ErrI2CGeneric
default:
// undefined abort sequence
err = makeI2CBuffError(byteCtr)
}
}
i2c.restartOnNext = nostop
return err
}
// writeAvailable determines non-blocking write space available
//go:inline
func (i2c *I2C) writeAvailable() uint32 {
return rp.I2C0_IC_COMP_PARAM_1_TX_BUFFER_DEPTH_Pos - i2c.Bus.IC_TXFLR.Get()
}
// readAvailable determines number of bytes received
//go:inline
func (i2c *I2C) readAvailable() uint32 {
return i2c.Bus.IC_RXFLR.Get()
}
// Equivalent to IC_CLR_TX_ABRT.Get() (side effect clears ABORT_REASON)
//go:inline
func (i2c *I2C) clearAbortReason() {
// Note clearing the abort flag also clears the reason, and
// this instance of flag is clear-on-read! Note also the
// IC_CLR_TX_ABRT register always reads as 0.
i2c.Bus.IC_CLR_TX_ABRT.Get()
}
//go:inline
func (i2c *I2C) getAbortReason() uint32 {
return i2c.Bus.IC_TX_ABRT_SOURCE.Get()
}
// returns true if RAW_INTR_STAT bits in mask are all set. performs:
// RAW_INTR_STAT & mask == mask
//go:inline
func (i2c *I2C) interrupted(mask uint32) bool {
reg := i2c.Bus.IC_RAW_INTR_STAT.Get()
return reg&mask == mask
}
type i2cBuffError int
func (b i2cBuffError) Error() string {
return "i2c err after addr ack at data " + strconv.Itoa(int(b))
}
//go:inline
func makeI2CBuffError(idx int) error {
return i2cBuffError(idx)
}
//go:inline
func boolToBit(a bool) uint32 {
if a {
return 1
}
return 0
}
//go:inline
func u32max(a, b uint32) uint32 {
if a > b {
return a
}
return b
}
//go:inline
func isReservedI2CAddr(addr uint8) bool {
return (addr&0x78) == 0 || (addr&0x78) == 0x78
}
-384
View File
@@ -1,384 +0,0 @@
// +build rp2040
package machine
import (
"device/rp"
"errors"
"runtime/volatile"
"unsafe"
)
var (
ErrPeriodTooBig = errors.New("period outside valid range 1..4e9ns")
)
const (
maxPWMPins = 29
)
// pwmGroup is one PWM peripheral, which consists of a counter and two output
// channels. You can set the frequency using SetPeriod,
// but only for all the channels in this PWM peripheral at once.
//
// div: integer value to reduce counting rate by. Must be greater than or equal to 1.
//
// cc: counter compare level. Contains 2 channel levels. The 16 LSBs are Channel A's level (Duty Cycle)
// and the 16 MSBs are Channel B's level.
//
// top: Wrap. Highest number counter will reach before wrapping over. usually 0xffff.
//
// csr: Clock mode. PWM_CH0_CSR_DIVMODE_xxx registers have 4 possible modes, of which Free-running is used.
// csr contains output polarity bit at PWM_CH0_CSR_x_INV where x is the channel.
// csr contains phase correction bit at PWM_CH0_CSR_PH_CORRECT_Msk.
// csr contains PWM enable bit at PWM_CH0_CSR_EN. If not enabled PWM will not be active.
//
// ctr: PWM counter value.
type pwmGroup struct {
CSR volatile.Register32
DIV volatile.Register32
CTR volatile.Register32
CC volatile.Register32
TOP volatile.Register32
}
// Equivalent of
// var pwmSlice []pwmGroup = (*[8]pwmGroup)(unsafe.Pointer(rp.PWM))[:]
// return &pwmSlice[index]
// 0x14 is the size of a pwmGroup.
func getPWMGroup(index uintptr) *pwmGroup {
return (*pwmGroup)(unsafe.Pointer(uintptr(unsafe.Pointer(rp.PWM)) + 0x14*index))
}
// PWM peripherals available on RP2040. Each peripheral has 2 pins available for
// a total of 16 available PWM outputs. Some pins may not be available on some boards.
var (
PWM0 = getPWMGroup(0)
PWM1 = getPWMGroup(1)
PWM2 = getPWMGroup(2)
PWM3 = getPWMGroup(3)
PWM4 = getPWMGroup(4)
PWM5 = getPWMGroup(5)
PWM6 = getPWMGroup(6)
PWM7 = getPWMGroup(7)
)
// Configure enables and configures this PWM.
func (pwm *pwmGroup) Configure(config PWMConfig) error {
return pwm.init(config, true)
}
// Channel returns a PWM channel for the given pin. If pin does
// not belong to PWM peripheral ErrInvalidOutputPin error is returned.
// It also configures pin as PWM output.
func (pwm *pwmGroup) Channel(pin Pin) (channel uint8, err error) {
if pin > maxPWMPins || pwmGPIOToSlice(pin) != pwm.peripheral() {
return 3, ErrInvalidOutputPin
}
pin.Configure(PinConfig{PinPWM})
return pwmGPIOToChannel(pin), nil
}
// Peripheral returns the RP2040 PWM peripheral which ranges from 0 to 7. Each
// PWM peripheral has 2 channels, A and B which correspond to 0 and 1 in the program.
// This number corresponds to the package's PWM0 throughout PWM7 handles
func PWMPeripheral(pin Pin) (sliceNum uint8, err error) {
if pin > maxPWMPins {
return 0, ErrInvalidOutputPin
}
return pwmGPIOToSlice(pin), nil
}
// returns the number of the pwm peripheral (0-7)
func (pwm *pwmGroup) peripheral() uint8 {
return uint8((uintptr(unsafe.Pointer(pwm)) - uintptr(unsafe.Pointer(rp.PWM))) / 0x14)
}
// SetPeriod updates the period of this PWM peripheral.
// To set a particular frequency, use the following formula:
//
// period = 1e9 / frequency
//
// If you use a period of 0, a period that works well for LEDs will be picked.
//
// SetPeriod will not change the prescaler, but also won't change the current
// value in any of the channels. This means that you may need to update the
// value for the particular channel.
//
// Note that you cannot pick any arbitrary period after the PWM peripheral has
// been configured. If you want to switch between frequencies, pick the lowest
// frequency (longest period) once when calling Configure and adjust the
// frequency here as needed.
func (p *pwmGroup) SetPeriod(period uint64) error {
if period > 0xffff_ffff {
return ErrPeriodTooBig
}
if period == 0 {
period = 1e5
}
p.setPeriod(period)
return nil
}
// Top returns the current counter top, for use in duty cycle calculation.
//
// The value returned here is hardware dependent. In general, it's best to treat
// it as an opaque value that can be divided by some number and passed to Set
// (see Set documentation for more information).
func (p *pwmGroup) Top() uint32 {
return p.getWrap()
}
// Counter returns the current counter value of the timer in this PWM
// peripheral. It may be useful for debugging.
func (p *pwmGroup) Counter() uint32 {
return (p.CTR.Get() & rp.PWM_CH0_CTR_CH0_CTR_Msk) >> rp.PWM_CH0_CTR_CH0_CTR_Pos
}
// Period returns the used PWM period in nanoseconds. It might deviate slightly
// from the configured period due to rounding.
func (p *pwmGroup) Period() uint64 {
periodPerCycle := getPeriod()
top := p.getWrap()
phc := p.getPhaseCorrect()
Int, frac := p.getClockDiv()
return uint64((Int + frac/16) * (top + 1) * (phc + 1) * periodPerCycle) // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
}
// SetInverting sets whether to invert the output of this channel.
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
// the time and low for the rest. Inverting flips the output as if a NOT gate
// was placed at the output, meaning that the output would be 25% low and 75%
// high with a duty cycle of 25%.
func (p *pwmGroup) SetInverting(channel uint8, inverting bool) {
channel &= 1
p.setInverting(channel, inverting)
}
// Set updates the channel value. This is used to control the channel duty
// cycle, in other words the fraction of time the channel output is high (or low
// when inverted). For example, to set it to a 25% duty cycle, use:
//
// pwm.Set(channel, pwm.Top() / 4)
//
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
func (p *pwmGroup) Set(channel uint8, value uint32) {
val := uint16(value)
channel &= 1
p.setChanLevel(channel, val)
}
// Get current level (last set by Set). Default value on initialization is 0.
func (p *pwmGroup) Get(channel uint8) (value uint32) {
channel &= 1
return uint32(p.getChanLevel(channel))
}
// SetTop sets TOP control register. Max value is 16bit (0xffff).
func (p *pwmGroup) SetTop(top uint32) {
p.setWrap(uint16(top))
}
// Enable enables or disables PWM peripheral channels.
func (p *pwmGroup) Enable(enable bool) {
p.enable(enable)
}
// IsEnabled returns true if peripheral is enabled.
func (p *pwmGroup) IsEnabled() (enabled bool) {
return (p.CSR.Get()&rp.PWM_CH0_CSR_EN_Msk)>>rp.PWM_CH0_CSR_EN_Pos != 0
}
// Hardware Pulse Width Modulation (PWM) API
//
// The RP2040 PWM block has 8 identical slices. Each slice can drive two PWM output signals, or
// measure the frequency or duty cycle of an input signal. This gives a total of up to 16 controllable
// PWM outputs. All 30 GPIOs can be driven by the PWM block
//
// The PWM hardware functions by continuously comparing the input value to a free-running counter. This produces a
// toggling output where the amount of time spent at the high output level is proportional to the input value. The fraction of
// time spent at the high signal level is known as the duty cycle of the signal.
//
// The default behaviour of a PWM slice is to count upward until the wrap value (\ref pwm_config_set_wrap) is reached, and then
// immediately wrap to 0. PWM slices also offer a phase-correct mode, where the counter starts to count downward after
// reaching TOP, until it reaches 0 again.
type pwms struct {
slice pwmGroup
hw *rp.PWM_Type
}
// Handle to all pwm peripheral registers.
var _PWM = pwms{
hw: rp.PWM,
}
// Initialise a PWM with settings from a configuration object.
// If start is true then PWM starts on initialization.
func (pwm *pwmGroup) init(config PWMConfig, start bool) error {
// Not enable Phase correction
pwm.setPhaseCorrect(false)
// Clock mode set by default to Free running
pwm.setDivMode(rp.PWM_CH0_CSR_DIVMODE_DIV)
// Set Output polarity (false/false)
pwm.setInverting(0, false)
pwm.setInverting(1, false)
// Set wrap. The highest value the counter will reach before returning to zero, also known as TOP.
pwm.setWrap(0xffff)
// period is set after TOP (Wrap).
err := pwm.SetPeriod(config.Period)
if err != nil {
return err
}
// period already set beforea
// Reset counter and compare (pwm level set to zero)
pwm.CTR.ReplaceBits(0, rp.PWM_CH0_CTR_CH0_CTR_Msk, 0) // PWM_CH0_CTR_RESET
pwm.CC.Set(0) // PWM_CH0_CC_RESET
pwm.enable(start)
return nil
}
func (pwm *pwmGroup) setPhaseCorrect(correct bool) {
pwm.CSR.ReplaceBits(boolToBit(correct)<<rp.PWM_CH0_CSR_PH_CORRECT_Pos, rp.PWM_CH0_CSR_PH_CORRECT_Msk, 0)
}
// Takes any of the following:
// rp.PWM_CH0_CSR_DIVMODE_DIV, rp.PWM_CH0_CSR_DIVMODE_FALL,
// rp.PWM_CH0_CSR_DIVMODE_LEVEL, rp.PWM_CH0_CSR_DIVMODE_RISE
func (pwm *pwmGroup) setDivMode(mode uint32) {
pwm.CSR.ReplaceBits(mode<<rp.PWM_CH0_CSR_DIVMODE_Pos, rp.PWM_CH0_CSR_DIVMODE_Msk, 0)
}
// setPeriod sets the pwm peripheral period (frequency). Calculates DIV_INT and sets it from following equation:
// cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
// where cycles is amount of clock cycles per PWM period.
func (pwm *pwmGroup) setPeriod(period uint64) {
targetPeriod := uint32(period)
periodPerCycle := getPeriod()
top := pwm.getWrap()
phc := pwm.getPhaseCorrect()
_, frac := pwm.getClockDiv()
// clearing above expression:
// DIV_INT = cycles / ( (TOP+1) * (CSRPHCorrect+1) ) - DIV_FRAC/16
// where cycles must be converted to time:
// target_period = cycles * period_per_cycle ==> cycles = target_period/period_per_cycle
Int := targetPeriod/((1+phc)*periodPerCycle*(1+top)) - frac/16
if Int > 0xff {
Int = 0xff
}
pwm.setClockDiv(uint8(Int), 0)
}
// Int is integer value to reduce counting rate by. Must be greater than or equal to 1. DIV_INT is bits 4:11 (8 bits).
// frac's (DIV_FRAC) default value on reset is 0. Max value for frac is 15 (4 bits). This is known as a fixed-point
// fractional number.
//
// cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
func (pwm *pwmGroup) setClockDiv(Int, frac uint8) {
pwm.DIV.ReplaceBits((uint32(frac)<<rp.PWM_CH0_DIV_FRAC_Pos)|
u32max(uint32(Int), 1)<<rp.PWM_CH0_DIV_INT_Pos, rp.PWM_CH0_DIV_FRAC_Msk|rp.PWM_CH0_DIV_INT_Msk, 0)
}
// Set the highest value the counter will reach before returning to 0. Also
// known as TOP.
//
// The counter wrap value is double-buffered in hardware. This means that,
// when the PWM is running, a write to the counter wrap value does not take
// effect until after the next time the PWM slice wraps (or, in phase-correct
// mode, the next time the slice reaches 0). If the PWM is not running, the
// write is latched in immediately.
func (pwm *pwmGroup) setWrap(wrap uint16) {
pwm.TOP.ReplaceBits(uint32(wrap)<<rp.PWM_CH0_TOP_CH0_TOP_Pos, rp.PWM_CH0_TOP_CH0_TOP_Msk, 0)
}
// enables/disables the PWM peripheral with rp.PWM_CH0_CSR_EN bit.
func (pwm *pwmGroup) enable(enable bool) {
pwm.CSR.ReplaceBits(boolToBit(enable)<<rp.PWM_CH0_CSR_EN_Pos, rp.PWM_CH0_CSR_EN_Msk, 0)
}
func (pwm *pwmGroup) setInverting(channel uint8, invert bool) {
var pos uint8
var msk uint32
switch channel {
case 0:
pos = rp.PWM_CH0_CSR_A_INV_Pos
msk = rp.PWM_CH0_CSR_A_INV_Msk
case 1:
pos = rp.PWM_CH0_CSR_B_INV_Pos
msk = rp.PWM_CH0_CSR_B_INV_Msk
}
pwm.CSR.ReplaceBits(boolToBit(invert)<<pos, msk, 0)
}
// Set the current PWM counter compare value for one channel
//
// The counter compare register is double-buffered in hardware. This means
// that, when the PWM is running, a write to the counter compare values does
// not take effect until the next time the PWM slice wraps (or, in
// phase-correct mode, the next time the slice reaches 0). If the PWM is not
// running, the write is latched in immediately.
// Channel is 0 for A, 1 for B.
func (pwm *pwmGroup) setChanLevel(channel uint8, level uint16) {
var pos uint8
var mask uint32
switch channel {
case 0:
pos = rp.PWM_CH0_CC_A_Pos
mask = rp.PWM_CH0_CC_A_Msk
case 1:
pos = rp.PWM_CH0_CC_B_Pos
mask = rp.PWM_CH0_CC_B_Msk
}
pwm.CC.ReplaceBits(uint32(level)<<pos, mask, 0)
}
func (pwm *pwmGroup) getChanLevel(channel uint8) (level uint16) {
var pos uint8
var mask uint32
switch channel {
case 0:
pos = rp.PWM_CH0_CC_A_Pos
mask = rp.PWM_CH0_CC_A_Msk
case 1:
pos = rp.PWM_CH0_CC_B_Pos
mask = rp.PWM_CH0_CC_B_Msk
}
level = uint16((pwm.CC.Get() & mask) >> pos)
return level
}
func (pwm *pwmGroup) getWrap() (top uint32) {
return (pwm.TOP.Get() & rp.PWM_CH0_TOP_CH0_TOP_Msk) >> rp.PWM_CH0_TOP_CH0_TOP_Pos
}
func (pwm *pwmGroup) getPhaseCorrect() (phCorrect uint32) {
return (pwm.CSR.Get() & rp.PWM_CH0_CSR_PH_CORRECT_Msk) >> rp.PWM_CH0_CSR_PH_CORRECT_Pos
}
func (pwm *pwmGroup) getClockDiv() (Int, frac uint32) {
div := pwm.DIV.Get()
return (div & rp.PWM_CH0_DIV_INT_Msk) >> rp.PWM_CH0_DIV_INT_Pos, (div & rp.PWM_CH0_DIV_FRAC_Msk) >> rp.PWM_CH0_DIV_FRAC_Pos
}
// 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) {
return (uint8(gpio) >> 1) & 7
}
// Determine the PWM channel that is attached to the specified GPIO.
// Each slice 0 to 7 has two channels, A and B.
func pwmGPIOToChannel(gpio Pin) (channel uint8) {
return uint8(gpio) & 1
}
// Returns the period of a clock cycle for the raspberry pi pico in nanoseconds.
func getPeriod() uint32 {
const periodIn uint32 = 1e9 / (125 * MHz)
return periodIn
}
-361
View File
@@ -1,361 +0,0 @@
// +build rp2040
package machine
import (
"device/rp"
"errors"
)
// SPI on the RP2040
var (
SPI0 = &_SPI0
_SPI0 = SPI{
Bus: rp.SPI0,
}
SPI1 = &_SPI1
_SPI1 = SPI{
Bus: rp.SPI1,
}
)
// SPIConfig is used to store config info for SPI.
type SPIConfig struct {
Frequency uint32
// LSB not supported on rp2040.
LSBFirst bool
// Mode's two most LSB are CPOL and CPHA. i.e. Mode==2 (0b10) is CPOL=1, CPHA=0
Mode uint8
// Number of data bits per transfer. Valid values 4..16. Default and recommended is 8.
DataBits uint8
// Serial clock pin
SCK Pin
// TX or Serial Data Out (MOSI if rp2040 is master)
SDO Pin
// RX or Serial Data In (MISO if rp2040 is master)
SDI Pin
}
var (
ErrLSBNotSupported = errors.New("SPI LSB unsupported on PL022")
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
ErrSPITimeout = errors.New("SPI timeout")
ErrSPIBaud = errors.New("SPI baud too low or above 66.5Mhz")
)
type SPI struct {
Bus *rp.SPI0_Type
}
// time to wait on a transaction before dropping. Unit in Microseconds for compatibility with ticks().
const _SPITimeout = 10 * 1000 // 10 ms
// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
// interface, there must always be the same number of bytes written as bytes read.
// The Tx method knows about this, and offers a few different ways of calling it.
//
// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer.
// Note that the tx and rx buffers must be the same size:
//
// spi.Tx(tx, rx)
//
// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros
// until all the bytes in the command packet have been received:
//
// spi.Tx(tx, nil)
//
// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet":
//
// spi.Tx(nil, rx)
//
// Remark: This implementation (RP2040) allows reading into buffer with a custom repeated
// value on tx.
//
// spi.Tx([]byte{0xff}, rx) // may cause unwanted heap allocations.
//
// This form sends 0xff and puts the result into rx buffer. Useful for reading from SD cards
// which require 0xff input on SI.
func (spi SPI) Tx(w, r []byte) (err error) {
switch {
case w == nil:
// read only, so write zero and read a result.
err = spi.rx(r, 0)
case r == nil:
// write only
err = spi.tx(w)
case len(w) == 1 && len(r) > 1:
// Read with custom repeated value.
err = spi.rx(r, w[0])
default:
// write/read
err = spi.txrx(w, r)
}
return err
}
// Write a single byte and read a single byte from TX/RX FIFO.
func (spi SPI) Transfer(w byte) (byte, error) {
var deadline = ticks() + _SPITimeout
for !spi.isWritable() {
if ticks() > deadline {
return 0, ErrSPITimeout
}
}
spi.Bus.SSPDR.Set(uint32(w))
for !spi.isReadable() {
if ticks() > deadline {
return 0, ErrSPITimeout
}
}
return uint8(spi.Bus.SSPDR.Get()), nil
}
func (spi SPI) SetBaudRate(br uint32) error {
const freqin uint32 = 125 * MHz
const maxBaud uint32 = 66.5 * MHz // max output frequency is 66.5MHz on rp2040. see Note page 527.
// Find smallest prescale value which puts output frequency in range of
// post-divide. Prescale is an even number from 2 to 254 inclusive.
var prescale, postdiv uint32
for prescale = 2; prescale < 255; prescale += 2 {
if freqin < (prescale+2)*256*br {
break
}
}
if prescale > 254 || br > maxBaud {
return ErrSPIBaud
}
// Find largest post-divide which makes output <= baudrate. Post-divide is
// an integer in the range 1 to 256 inclusive.
for postdiv = 256; postdiv > 1; postdiv-- {
if freqin/(prescale*(postdiv-1)) > br {
break
}
}
spi.Bus.SSPCPSR.Set(prescale)
spi.Bus.SSPCR0.ReplaceBits((postdiv-1)<<rp.SPI0_SSPCR0_SCR_Pos, rp.SPI0_SSPCR0_SCR_Msk, 0)
return nil
}
func (spi SPI) GetBaudRate() uint32 {
const freqin uint32 = 125 * MHz
prescale := spi.Bus.SSPCPSR.Get()
postdiv := ((spi.Bus.SSPCR0.Get() & rp.SPI0_SSPCR0_SCR_Msk) >> rp.SPI0_SSPCR0_SCR_Pos) + 1
return freqin / (prescale * postdiv)
}
// Configure is intended to setup/initialize the SPI interface.
// Default baudrate of 115200 is used if Frequency == 0. Default
// word length (data bits) is 8.
// Below is a list of GPIO pins corresponding to SPI0 bus on the rp2040:
// SI : 0, 4, 17 a.k.a RX and MISO (if rp2040 is master)
// SO : 3, 7, 19 a.k.a TX and MOSI (if rp2040 is master)
// SCK: 2, 6, 18
// SPI1 bus GPIO pins:
// SI : 8, 12
// SO : 11, 15
// SCK: 10, 14
// No pin configuration is needed of SCK, SDO and SDI needed after calling Configure.
func (spi SPI) Configure(config SPIConfig) error {
const defaultBaud uint32 = 115200
if config.SCK == 0 {
// set default pins if config zero valued or invalid clock pin supplied.
switch spi.Bus {
case rp.SPI0:
config.SCK = SPI0_SCK_PIN
config.SDO = SPI0_SDO_PIN
config.SDI = SPI0_SDI_PIN
case rp.SPI1:
config.SCK = SPI1_SCK_PIN
config.SDO = SPI1_SDO_PIN
config.SDI = SPI1_SDI_PIN
}
}
if config.DataBits < 4 || config.DataBits > 16 {
config.DataBits = 8
}
if config.Frequency == 0 {
config.Frequency = defaultBaud
}
// SPI pin configuration
config.SCK.setFunc(fnSPI)
config.SDO.setFunc(fnSPI)
config.SDI.setFunc(fnSPI)
return spi.initSPI(config)
}
func (spi SPI) initSPI(config SPIConfig) (err error) {
spi.reset()
// LSB-first not supported on PL022:
if config.LSBFirst {
return ErrLSBNotSupported
}
err = spi.SetBaudRate(config.Frequency)
// Set SPI Format (CPHA and CPOL) and frame format (default is Motorola)
spi.setFormat(config.DataBits, config.Mode, rp.XIP_SSI_CTRLR0_SPI_FRF_STD)
// Always enable DREQ signals -- harmless if DMA is not listening
spi.Bus.SSPDMACR.SetBits(rp.SPI0_SSPDMACR_TXDMAE | rp.SPI0_SSPDMACR_RXDMAE)
// Finally enable the SPI
spi.Bus.SSPCR1.SetBits(rp.SPI0_SSPCR1_SSE)
return err
}
//go:inline
func (spi SPI) setFormat(databits, mode uint8, frameFormat uint32) {
cpha := uint32(mode) & 1
cpol := uint32(mode>>1) & 1
spi.Bus.SSPCR0.ReplaceBits(
(cpha<<rp.SPI0_SSPCR0_SPH_Pos)|
(cpol<<rp.SPI0_SSPCR0_SPO_Pos)|
(uint32(databits-1)<<rp.SPI0_SSPCR0_DSS_Pos)| // Set databits (SPI word length). Valid inputs are 4-16.
(frameFormat&0b11)<<rp.SPI0_SSPCR0_FRF_Pos, // Frame format bits 4:5
rp.SPI0_SSPCR0_SPH_Msk|rp.SPI0_SSPCR0_SPO_Msk|rp.SPI0_SSPCR0_DSS_Msk|rp.SPI0_SSPCR0_FRF_Msk, 0)
}
// reset resets SPI and waits until reset is done.
//go:inline
func (spi SPI) reset() {
resetVal := spi.deinit()
rp.RESETS.RESET.ClearBits(resetVal)
// Wait until reset is done.
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
}
}
//go:inline
func (spi SPI) deinit() (resetVal uint32) {
switch spi.Bus {
case rp.SPI0:
resetVal = rp.RESETS_RESET_SPI0
case rp.SPI1:
resetVal = rp.RESETS_RESET_SPI1
}
// Perform SPI reset.
rp.RESETS.RESET.SetBits(resetVal)
return resetVal
}
// isWritable returns false if no space is available to write. True if a write is possible
//go:inline
func (spi SPI) isWritable() bool {
return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_TNF)
}
// isReadable returns true if a read is possible i.e. data is present
//go:inline
func (spi SPI) isReadable() bool {
return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_RNE)
}
// PrintRegs prints SPI's peripheral common registries current values
func (spi SPI) PrintRegs() {
cr0 := spi.Bus.SSPCR0.Get()
cr1 := spi.Bus.SSPCR1.Get()
dmacr := spi.Bus.SSPDMACR.Get()
cpsr := spi.Bus.SSPCPSR.Get()
dr := spi.Bus.SSPDR.Get()
ris := spi.Bus.SSPRIS.Get()
println("CR0:", cr0)
println("CR1:", cr1)
println("DMACR:", dmacr)
println("CPSR:", cpsr)
println("DR:", dr)
println("RIS:", ris)
}
//go:inline
func (spi SPI) isBusy() bool {
return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_BSY)
}
// tx writes buffer to SPI ignoring Rx.
func (spi SPI) tx(tx []byte) error {
var deadline = ticks() + _SPITimeout
// Write to TX FIFO whilst ignoring RX, then clean up afterward. When RX
// is full, PL022 inhibits RX pushes, and sets a sticky flag on
// push-on-full, but continues shifting. Safe if SSPIMSC_RORIM is not set.
for i := range tx {
for !spi.isWritable() {
if ticks() > deadline {
return ErrSPITimeout
}
}
spi.Bus.SSPDR.Set(uint32(tx[i]))
}
// Drain RX FIFO, then wait for shifting to finish (which may be *after*
// TX FIFO drains), then drain RX FIFO again
for spi.isReadable() {
spi.Bus.SSPDR.Get()
}
for spi.isBusy() {
if ticks() > deadline {
return ErrSPITimeout
}
}
for spi.isReadable() {
spi.Bus.SSPDR.Get()
}
// Don't leave overrun flag set
spi.Bus.SSPICR.Set(rp.SPI0_SSPICR_RORIC)
return nil
}
// rx reads buffer to SPI ignoring x.
// txrepeat is output repeatedly on SO as data is read in from SI.
// Generally this can be 0, but some devices require a specific value here,
// e.g. SD cards expect 0xff
func (spi SPI) rx(rx []byte, txrepeat byte) error {
var deadline = ticks() + _SPITimeout
plen := len(rx)
const fifoDepth = 8 // see txrx
var rxleft, txleft = plen, plen
for txleft != 0 || rxleft != 0 {
if txleft != 0 && spi.isWritable() && rxleft < txleft+fifoDepth {
spi.Bus.SSPDR.Set(uint32(txrepeat))
txleft--
}
if rxleft != 0 && spi.isReadable() {
rx[plen-rxleft] = uint8(spi.Bus.SSPDR.Get())
rxleft--
continue // if reading succesfully in rx there is no need to check deadline.
}
if ticks() > deadline {
return ErrSPITimeout
}
}
return nil
}
// Write len bytes from src to SPI. Simultaneously read len bytes from SPI to dst.
// Note this function is guaranteed to exit in a known amount of time (bits sent * time per bit)
func (spi SPI) txrx(tx, rx []byte) error {
var deadline = ticks() + _SPITimeout
plen := len(tx)
if plen != len(rx) {
return ErrTxInvalidSliceSize
}
// Never have more transfers in flight than will fit into the RX FIFO,
// else FIFO will overflow if this code is heavily interrupted.
const fifoDepth = 8
var rxleft, txleft = plen, plen
for (txleft != 0 || rxleft != 0) && ticks() <= deadline {
if txleft != 0 && spi.isWritable() && rxleft < txleft+fifoDepth {
spi.Bus.SSPDR.Set(uint32(tx[plen-txleft]))
txleft--
}
if rxleft != 0 && spi.isReadable() {
rx[plen-rxleft] = uint8(spi.Bus.SSPDR.Get())
rxleft--
}
}
if txleft != 0 || rxleft != 0 {
// Transaction ended early due to timeout
return ErrSPITimeout
}
return nil
}
-16
View File
@@ -58,19 +58,3 @@ func (p Pin) Get() bool {
val := port.IDR.Get() & (1 << pin)
return (val > 0)
}
// PortMaskSet returns the register and mask to enable a given GPIO pin. This
// can be used to implement bit-banged drivers.
func (p Pin) PortMaskSet() (*uint32, uint32) {
port := p.getPort()
pin := uint8(p) % 16
return &port.BSRR.Reg, 1 << pin
}
// PortMaskClear returns the register and mask to disable a given port. This can
// be used to implement bit-banged drivers.
func (p Pin) PortMaskClear() (*uint32, uint32) {
port := p.getPort()
pin := uint8(p) % 16
return &port.BSRR.Reg, 1 << (pin + 16)
}
-9
View File
@@ -1,9 +0,0 @@
// +build !linux
package os
import "errors"
func Executable() (string, error) {
return "", errors.New("Executable not implemented")
}
-25
View File
@@ -1,25 +0,0 @@
// The following is copied from Go 1.17 official implementation.
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux
package os
func Executable() (string, error) {
path, err := Readlink("/proc/self/exe")
// When the executable has been deleted then Readlink returns a
// path appended with " (deleted)".
return stringsTrimSuffix(path, " (deleted)"), err
}
// stringsTrimSuffix is the same as strings.TrimSuffix.
func stringsTrimSuffix(s, suffix string) string {
if len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix {
return s[:len(s)-len(suffix)]
}
return s
}
-14
View File
@@ -10,15 +10,6 @@ import (
"syscall"
)
// Seek whence values.
//
// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
const (
SEEK_SET int = io.SeekStart
SEEK_CUR int = io.SeekCurrent
SEEK_END int = io.SeekEnd
)
// Mkdir creates a directory. If the operation fails, it will return an error of
// type *PathError.
func Mkdir(path string, perm FileMode) error {
@@ -33,11 +24,6 @@ func Mkdir(path string, perm FileMode) error {
return nil
}
// MkdirTemp is a stub, it will always return an error.
func MkdirTemp(dir, pattern string) (string, error) {
return "", &PathError{"mkdirtemp", dir, ErrNotImplemented}
}
// Remove removes a file or (empty) directory. If the operation fails, it will
// return an error of type *PathError.
func Remove(path string) error {
-5
View File
@@ -706,11 +706,6 @@ type StructField struct {
Offset uintptr
}
// IsExported reports whether the field is exported.
func (f StructField) IsExported() bool {
return f.PkgPath == ""
}
// rawStructField is the same as StructField but with the Type member replaced
// with rawType. For internal use only. Avoiding this conversion to the Type
// interface improves code size in many cases.

Some files were not shown because too many files have changed in this diff Show More