From ffa38b183b32331dd247e337a985c4eb5a7d9350 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 30 Mar 2019 12:54:36 +0100 Subject: [PATCH] all: add HiFive1 rev B board with RISC-V architecture This page has been a big help in adding support for this new chip: https://wiki.osdev.org/HiFive-1_Bare_Bones --- .circleci/config.yml | 20 +++-- .gitignore | 6 +- .gitmodules | 2 +- Makefile | 11 ++- compiler/compiler.go | 10 +-- compiler/gc.go | 2 +- compiler/inlineasm.go | 13 ++- lib/cmsis-svd | 2 +- src/device/riscv/riscv.go | 10 +++ src/device/riscv/start.S | 13 +++ src/machine/board_fe310.go | 38 ++++++++ src/machine/board_hifive1b.go | 19 ++++ src/machine/i2c.go | 2 +- src/machine/machine_fe310.go | 54 ++++++++++++ src/machine/machine_generic.go | 2 +- src/machine/spi.go | 2 +- src/machine/uart.go | 2 +- src/os/file_other.go | 2 +- src/os/file_unix.go | 2 +- src/runtime/arch_arm.go | 2 +- src/runtime/arch_avr.go | 17 +--- src/runtime/arch_cortexm.go | 25 ------ src/runtime/arch_tinygoriscv.go | 19 ++++ src/runtime/baremetal.go | 30 +++++++ src/runtime/gc_globals_conservative.go | 2 +- src/runtime/gc_globals_precise.go | 2 +- src/runtime/gc_stack_portable.go | 2 +- src/runtime/gc_stack_raw.go | 2 +- src/runtime/runtime_fe310.go | 115 +++++++++++++++++++++++++ src/runtime/runtime_tinygoriscv.go | 19 ++++ src/runtime/runtime_unix.go | 2 +- targets/fe310.json | 5 ++ targets/hifive1b.json | 7 ++ targets/hifive1b.ld | 10 +++ targets/riscv.json | 25 ++++++ targets/riscv.ld | 56 ++++++++++++ tools/gen-device-svd.py | 7 +- 37 files changed, 485 insertions(+), 74 deletions(-) create mode 100644 src/device/riscv/riscv.go create mode 100644 src/device/riscv/start.S create mode 100644 src/machine/board_fe310.go create mode 100644 src/machine/board_hifive1b.go create mode 100644 src/machine/machine_fe310.go create mode 100644 src/runtime/arch_tinygoriscv.go create mode 100644 src/runtime/baremetal.go create mode 100644 src/runtime/runtime_fe310.go create mode 100644 src/runtime/runtime_tinygoriscv.go create mode 100644 targets/fe310.json create mode 100644 targets/hifive1b.json create mode 100644 targets/hifive1b.ld create mode 100644 targets/riscv.json create mode 100644 targets/riscv.ld diff --git a/.circleci/config.yml b/.circleci/config.yml index 4b0a054e..772fe51a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -67,7 +67,7 @@ commands: - run: go install . - run: go test -v - run: make gen-device -j4 - - run: make smoketest + - run: make smoketest RISCV=0 - save_cache: key: go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_BUILD_NUM }} paths: @@ -100,7 +100,7 @@ commands: - llvm-source-linux - restore_cache: keys: - - llvm-build-8-linux-v6 + - llvm-build-8-linux-v7 - run: name: "Build LLVM" command: | @@ -118,7 +118,7 @@ commands: make llvm-build fi - save_cache: - key: llvm-build-8-linux-v6 + key: llvm-build-8-linux-v7 paths: llvm-build - run: @@ -149,6 +149,11 @@ commands: tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo tinygo version + - run: + name: "Download SiFive GNU toolchain" + command: | + curl -O https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-linux-ubuntu14.tar.gz + sudo tar -C /usr/local --strip-components=1 -xf riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-linux-ubuntu14.tar.gz - run: make smoketest build-macos: steps: @@ -173,7 +178,7 @@ commands: - llvm-project - restore_cache: keys: - - llvm-build-8-macos-v5 + - llvm-build-8-macos-v6 - run: name: "Build LLVM" command: | @@ -185,7 +190,7 @@ commands: make llvm-build fi - save_cache: - key: llvm-build-8-macos-v5 + key: llvm-build-8-macos-v6 paths: llvm-build - run: @@ -209,6 +214,11 @@ commands: tar -C /usr/local/opt -xf /tmp/tinygo.darwin-amd64.tar.gz ln -s /usr/local/opt/tinygo/bin/tinygo /usr/local/bin/tinygo tinygo version + - run: + name: "Download SiFive GNU toolchain" + command: | + curl -O https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-apple-darwin.tar.gz + sudo tar -C /usr/local --strip-components=1 -xf riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-apple-darwin.tar.gz - run: make smoketest AVR=0 diff --git a/.gitignore b/.gitignore index 4f2fc845..2e425ebd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,12 @@ src/device/avr/*.ld src/device/avr/*.s src/device/nrf/*.go src/device/nrf/*.s -src/device/stm32/*.go -src/device/stm32/*.s src/device/sam/*.go src/device/sam/*.s +src/device/sifive/*.go +src/device/sifive/*.s +src/device/stm32/*.go +src/device/stm32/*.s vendor llvm llvm-build diff --git a/.gitmodules b/.gitmodules index 899df8f8..36288c43 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,7 +9,7 @@ url = https://github.com/avr-rust/avr-mcu.git [submodule "lib/cmsis-svd"] path = lib/cmsis-svd - url = https://github.com/posborne/cmsis-svd + url = https://github.com/tinygo-org/cmsis-svd [submodule "lib/compiler-rt"] path = lib/compiler-rt url = https://github.com/llvm-mirror/compiler-rt.git diff --git a/Makefile b/Makefile index d83b8120..239a2dfa 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ fmt-check: @unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1 -gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-stm32 +gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-avr: ./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/ @@ -56,6 +56,10 @@ gen-device-sam: ./tools/gen-device-svd.py lib/cmsis-svd/data/Atmel/ src/device/sam/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel go fmt ./src/device/sam +gen-device-sifive: + ./tools/gen-device-svd.py lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/ --source=https://github.com/AdaCore/svd2ada/tree/master/CMSIS-SVD/SiFive-Community + go fmt ./src/device/sifive + gen-device-stm32: ./tools/gen-device-svd.py lib/cmsis-svd/data/STMicro/ src/device/stm32/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro go fmt ./src/device/stm32 @@ -69,7 +73,7 @@ llvm-source: llvm-project/README.md # Configure LLVM. TINYGO_SOURCE_DIR=$(shell pwd) $(LLVM_BUILDDIR)/build.ninja: llvm-source - mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/llvm-project/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=OFF -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF + mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/llvm-project/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;RISCV" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=OFF -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF # Build LLVM. $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja @@ -123,6 +127,9 @@ smoketest: ifneq ($(AVR), 0) tinygo build -size short -o test.elf -target=arduino examples/blinky1 tinygo build -size short -o test.elf -target=digispark examples/blinky1 +endif +ifneq ($(RISCV), 0) + tinygo build -size short -o test.elf -target=hifive1b examples/blinky1 endif tinygo build -o wasm.wasm -target=wasm examples/wasm/export tinygo build -o wasm.wasm -target=wasm examples/wasm/main diff --git a/compiler/compiler.go b/compiler/compiler.go index 4693aef0..82aaee36 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -226,7 +226,7 @@ func (c *Compiler) Compile(mainPath string) []error { return path } else if path == "syscall" { for _, tag := range c.BuildTags { - if tag == "avr" || tag == "cortexm" || tag == "darwin" { + if tag == "avr" || tag == "cortexm" || tag == "darwin" || tag == "riscv" { return path } } @@ -1304,11 +1304,11 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e if fn := instr.StaticCallee(); fn != nil { name := fn.RelString(nil) switch { - case name == "device/arm.ReadRegister": - return c.emitReadRegister(instr.Args) - case name == "device/arm.Asm" || name == "device/avr.Asm": + case name == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister": + return c.emitReadRegister(name, instr.Args) + case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm": return c.emitAsm(instr.Args) - case name == "device/arm.AsmFull" || name == "device/avr.AsmFull": + case name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull": return c.emitAsmFull(frame, instr) case strings.HasPrefix(name, "device/arm.SVCall"): return c.emitSVCall(frame, instr.Args) diff --git a/compiler/gc.go b/compiler/gc.go index 6b523299..bb6633df 100644 --- a/compiler/gc.go +++ b/compiler/gc.go @@ -18,7 +18,7 @@ func (c *Compiler) needsStackObjects() bool { return false } for _, tag := range c.BuildTags { - if tag == "cortexm" { + if tag == "cortexm" || tag == "tinygo.riscv" { return false } } diff --git a/compiler/inlineasm.go b/compiler/inlineasm.go index b832e43a..f5dbc9f8 100644 --- a/compiler/inlineasm.go +++ b/compiler/inlineasm.go @@ -18,10 +18,19 @@ import ( // func ReadRegister(name string) uintptr // // The register name must be a constant, for example "sp". -func (c *Compiler) emitReadRegister(args []ssa.Value) (llvm.Value, error) { +func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, error) { fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{}, false) regname := constant.StringVal(args[0].(*ssa.Const).Value) - target := llvm.InlineAsm(fnType, "mov $0, "+regname, "=r", false, false, 0) + var asm string + switch name { + case "device/arm.ReadRegister": + asm = "mov $0, " + regname + case "device/riscv.ReadRegister": + asm = "mv $0, " + regname + default: + panic("unknown architecture") + } + target := llvm.InlineAsm(fnType, asm, "=r", false, false, 0) return c.builder.CreateCall(target, nil, ""), nil } diff --git a/lib/cmsis-svd b/lib/cmsis-svd index b6f0a65a..5910f3d1 160000 --- a/lib/cmsis-svd +++ b/lib/cmsis-svd @@ -1 +1 @@ -Subproject commit b6f0a65ac37760f52d6ade23dd3205424d6c91fa +Subproject commit 5910f3d115a53ead55c8158da24a8fa8bda1eb50 diff --git a/src/device/riscv/riscv.go b/src/device/riscv/riscv.go new file mode 100644 index 00000000..947b3f18 --- /dev/null +++ b/src/device/riscv/riscv.go @@ -0,0 +1,10 @@ +package riscv + +// Run the given assembly code. The code will be marked as having side effects, +// as it doesn't produce output and thus would normally be eliminated by the +// optimizer. +func Asm(asm string) + +// ReadRegister returns the contents of the specified register. The register +// must be a processor register, reachable with the "mov" instruction. +func ReadRegister(name string) uintptr diff --git a/src/device/riscv/start.S b/src/device/riscv/start.S new file mode 100644 index 00000000..0c094200 --- /dev/null +++ b/src/device/riscv/start.S @@ -0,0 +1,13 @@ +.section .init +.global _start +.type _start,@function + +_start: + // Workaround for missing support of the la pseudo-instruction in Clang 8: + // https://reviews.llvm.org/D55325 + lui sp, %hi(_stack_top) + addi sp, sp, %lo(_stack_top) + // see https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register + lui gp, %hi(__global_pointer$) + addi gp, gp, %lo(__global_pointer$) + call main diff --git a/src/machine/board_fe310.go b/src/machine/board_fe310.go new file mode 100644 index 00000000..b6e88a3c --- /dev/null +++ b/src/machine/board_fe310.go @@ -0,0 +1,38 @@ +// +build hifive1b + +package machine + +const ( + P00 Pin = 0 + P01 Pin = 1 + P02 Pin = 2 + P03 Pin = 3 + P04 Pin = 4 + P05 Pin = 5 + P06 Pin = 6 + P07 Pin = 7 + P08 Pin = 8 + P09 Pin = 9 + P10 Pin = 10 + P11 Pin = 11 + P12 Pin = 12 + P13 Pin = 13 + P14 Pin = 14 + P15 Pin = 15 + P16 Pin = 16 + P17 Pin = 17 + P18 Pin = 18 + P19 Pin = 19 + P20 Pin = 20 + P21 Pin = 21 + P22 Pin = 22 + P23 Pin = 23 + P24 Pin = 24 + P25 Pin = 25 + P26 Pin = 26 + P27 Pin = 27 + P28 Pin = 28 + P29 Pin = 29 + P30 Pin = 30 + P31 Pin = 31 +) diff --git a/src/machine/board_hifive1b.go b/src/machine/board_hifive1b.go new file mode 100644 index 00000000..bb82849c --- /dev/null +++ b/src/machine/board_hifive1b.go @@ -0,0 +1,19 @@ +// +build hifive1b + +package machine + +const ( + LED = LED1 + LED1 = LED_RED + LED2 = LED_GREEN + LED3 = LED_BLUE + LED_RED = P22 + LED_GREEN = P19 + LED_BLUE = P21 +) + +const ( + // TODO: figure out the pin numbers for these. + UART_TX_PIN = NoPin + UART_RX_PIN = NoPin +) diff --git a/src/machine/i2c.go b/src/machine/i2c.go index bd6c1dc2..4fe7b816 100644 --- a/src/machine/i2c.go +++ b/src/machine/i2c.go @@ -1,4 +1,4 @@ -// +build !stm32f4disco +// +build !stm32f4disco,!hifive1b package machine diff --git a/src/machine/machine_fe310.go b/src/machine/machine_fe310.go new file mode 100644 index 00000000..58240a1f --- /dev/null +++ b/src/machine/machine_fe310.go @@ -0,0 +1,54 @@ +// +build fe310 + +package machine + +import ( + "device/sifive" +) + +type PinMode uint8 + +const ( + PinInput PinMode = iota + PinOutput +) + +// Configure this pin with the given configuration. +func (p Pin) Configure(config PinConfig) { + sifive.GPIO0.INPUT_EN.SetBits(1 << uint8(p)) + if config.Mode == PinOutput { + sifive.GPIO0.OUTPUT_EN.SetBits(1 << uint8(p)) + } +} + +// Set the pin to high or low. +func (p Pin) Set(high bool) { + if high { + sifive.GPIO0.PORT.SetBits(1 << uint8(p)) + } else { + sifive.GPIO0.PORT.ClearBits(1 << uint8(p)) + } +} + +type UART struct { + Bus *sifive.UART_Type + Buffer *RingBuffer +} + +var ( + UART0 = UART{Bus: sifive.UART0, Buffer: NewRingBuffer()} +) + +func (uart UART) Configure(config UARTConfig) { + // Assuming a 16Mhz Crystal (which is Y1 on the HiFive1), the divisor for a + // 115200 baud rate is 138. + sifive.UART0.DIV.Set(138) + sifive.UART0.TXCTRL.Set(sifive.UART_TXCTRL_ENABLE) +} + +func (uart UART) WriteByte(c byte) { + for sifive.UART0.TXDATA.Get()&sifive.UART_TXDATA_FULL != 0 { + } + + sifive.UART0.TXDATA.Set(uint32(c)) +} diff --git a/src/machine/machine_generic.go b/src/machine/machine_generic.go index ccc3e2b7..7bfc5d2e 100644 --- a/src/machine/machine_generic.go +++ b/src/machine/machine_generic.go @@ -1,4 +1,4 @@ -// +build !avr,!nrf,!sam,!stm32 +// +build !avr,!nrf,!sam,!sifive,!stm32 package machine diff --git a/src/machine/spi.go b/src/machine/spi.go index a3e6235f..f79c8a24 100644 --- a/src/machine/spi.go +++ b/src/machine/spi.go @@ -1,4 +1,4 @@ -// +build !stm32f407,!avr +// +build !stm32f407,!avr,!hifive1b package machine diff --git a/src/machine/uart.go b/src/machine/uart.go index 06a9f9c3..bcaefa9d 100644 --- a/src/machine/uart.go +++ b/src/machine/uart.go @@ -1,4 +1,4 @@ -// +build avr nrf sam stm32 +// +build avr nrf sam sifive stm32 package machine diff --git a/src/os/file_other.go b/src/os/file_other.go index ba828304..abe90868 100644 --- a/src/os/file_other.go +++ b/src/os/file_other.go @@ -1,4 +1,4 @@ -// +build avr cortexm wasm +// +build avr cortexm tinygo.riscv wasm package os diff --git a/src/os/file_unix.go b/src/os/file_unix.go index a6471f67..d881f62f 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -1,4 +1,4 @@ -// +build darwin linux,!avr,!cortexm +// +build darwin linux,!avr,!cortexm,!tinygo.riscv package os diff --git a/src/runtime/arch_arm.go b/src/runtime/arch_arm.go index ad9bab88..6f75323b 100644 --- a/src/runtime/arch_arm.go +++ b/src/runtime/arch_arm.go @@ -1,4 +1,4 @@ -// +build arm,!avr,!cortexm +// +build arm,!avr,!cortexm,!tinygo.riscv package runtime diff --git a/src/runtime/arch_avr.go b/src/runtime/arch_avr.go index 38e75732..4512e85c 100644 --- a/src/runtime/arch_avr.go +++ b/src/runtime/arch_avr.go @@ -2,26 +2,11 @@ package runtime -import ( - "unsafe" -) - -const GOARCH = "avr" +const GOARCH = "arm" // avr pretends to be arm // The bitness of the CPU (e.g. 8, 32, 64). const TargetBits = 8 -//go:extern _heap_start -var heapStartSymbol unsafe.Pointer - -//go:extern _heap_end -var heapEndSymbol unsafe.Pointer - -var ( - heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) - heapEnd = uintptr(unsafe.Pointer(&heapEndSymbol)) -) - // Align on a word boundary. func align(ptr uintptr) uintptr { // No alignment necessary on the AVR. diff --git a/src/runtime/arch_cortexm.go b/src/runtime/arch_cortexm.go index af974a0a..7a0aeffd 100644 --- a/src/runtime/arch_cortexm.go +++ b/src/runtime/arch_cortexm.go @@ -3,8 +3,6 @@ package runtime import ( - "unsafe" - "device/arm" ) @@ -13,29 +11,6 @@ const GOARCH = "arm" // The bitness of the CPU (e.g. 8, 32, 64). const TargetBits = 32 -//go:extern _heap_start -var heapStartSymbol unsafe.Pointer - -//go:extern _heap_end -var heapEndSymbol unsafe.Pointer - -//go:extern _globals_start -var globalsStartSymbol unsafe.Pointer - -//go:extern _globals_end -var globalsEndSymbol unsafe.Pointer - -//go:extern _stack_top -var stackTopSymbol unsafe.Pointer - -var ( - heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) - heapEnd = uintptr(unsafe.Pointer(&heapEndSymbol)) - globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol)) - globalsEnd = uintptr(unsafe.Pointer(&globalsEndSymbol)) - stackTop = uintptr(unsafe.Pointer(&stackTopSymbol)) -) - // Align on word boundary. func align(ptr uintptr) uintptr { return (ptr + 3) &^ 3 diff --git a/src/runtime/arch_tinygoriscv.go b/src/runtime/arch_tinygoriscv.go new file mode 100644 index 00000000..1c5d8b78 --- /dev/null +++ b/src/runtime/arch_tinygoriscv.go @@ -0,0 +1,19 @@ +// +build tinygo.riscv + +package runtime + +import "device/riscv" + +const GOARCH = "arm" // riscv pretends to be arm + +// The bitness of the CPU (e.g. 8, 32, 64). +const TargetBits = 32 + +// Align on word boundary. +func align(ptr uintptr) uintptr { + return (ptr + 3) &^ 3 +} + +func getCurrentStackPointer() uintptr { + return riscv.ReadRegister("sp") +} diff --git a/src/runtime/baremetal.go b/src/runtime/baremetal.go new file mode 100644 index 00000000..90c15628 --- /dev/null +++ b/src/runtime/baremetal.go @@ -0,0 +1,30 @@ +// +build avr cortexm tinygo.riscv + +package runtime + +import ( + "unsafe" +) + +//go:extern _heap_start +var heapStartSymbol unsafe.Pointer + +//go:extern _heap_end +var heapEndSymbol unsafe.Pointer + +//go:extern _globals_start +var globalsStartSymbol unsafe.Pointer + +//go:extern _globals_end +var globalsEndSymbol unsafe.Pointer + +//go:extern _stack_top +var stackTopSymbol unsafe.Pointer + +var ( + heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) + heapEnd = uintptr(unsafe.Pointer(&heapEndSymbol)) + globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol)) + globalsEnd = uintptr(unsafe.Pointer(&globalsEndSymbol)) + stackTop = uintptr(unsafe.Pointer(&stackTopSymbol)) +) diff --git a/src/runtime/gc_globals_conservative.go b/src/runtime/gc_globals_conservative.go index 6efbd3f9..a09d01ae 100644 --- a/src/runtime/gc_globals_conservative.go +++ b/src/runtime/gc_globals_conservative.go @@ -1,5 +1,5 @@ // +build gc.conservative -// +build cortexm +// +build cortexm tinygo.riscv package runtime diff --git a/src/runtime/gc_globals_precise.go b/src/runtime/gc_globals_precise.go index 9ebcfa82..464c8e03 100644 --- a/src/runtime/gc_globals_precise.go +++ b/src/runtime/gc_globals_precise.go @@ -1,5 +1,5 @@ // +build gc.conservative -// +build !cortexm +// +build !cortexm,!tinygo.riscv package runtime diff --git a/src/runtime/gc_stack_portable.go b/src/runtime/gc_stack_portable.go index 97c3e93a..1f1847c3 100644 --- a/src/runtime/gc_stack_portable.go +++ b/src/runtime/gc_stack_portable.go @@ -1,5 +1,5 @@ // +build gc.conservative -// +build !cortexm +// +build !cortexm,!tinygo.riscv package runtime diff --git a/src/runtime/gc_stack_raw.go b/src/runtime/gc_stack_raw.go index e899eb7c..0c37af60 100644 --- a/src/runtime/gc_stack_raw.go +++ b/src/runtime/gc_stack_raw.go @@ -1,5 +1,5 @@ // +build gc.conservative -// +build cortexm +// +build cortexm tinygo.riscv package runtime diff --git a/src/runtime/runtime_fe310.go b/src/runtime/runtime_fe310.go new file mode 100644 index 00000000..ded5edde --- /dev/null +++ b/src/runtime/runtime_fe310.go @@ -0,0 +1,115 @@ +// +build fe310 + +// This file implements target-specific things for the FE310 chip as used in the +// HiFive1. + +package runtime + +import ( + "machine" + "unsafe" + + "device/riscv" + "device/sifive" +) + +type timeUnit int64 + +const tickMicros = 32768 // RTC runs at 32.768kHz + +//go:extern _sbss +var _sbss unsafe.Pointer + +//go:extern _ebss +var _ebss unsafe.Pointer + +//go:extern _sdata +var _sdata unsafe.Pointer + +//go:extern _sidata +var _sidata unsafe.Pointer + +//go:extern _edata +var _edata unsafe.Pointer + +//go:export main +func main() { + preinit() + initAll() + callMain() + abort() +} + +func init() { + pric_init() + machine.UART0.Configure(machine.UARTConfig{}) +} + +func pric_init() { + // Make sure the HFROSC is on + sifive.PRIC.HFROSCCFG.SetBits(sifive.PRIC_HFROSCCFG_ENABLE) + + // Run off 16 MHz Crystal for accuracy. + sifive.PRIC.PLLCFG.SetBits(sifive.PRIC_PLLCFG_REFSEL | sifive.PRIC_PLLCFG_BYPASS) + sifive.PRIC.PLLCFG.SetBits(sifive.PRIC_PLLCFG_SEL) + + // Turn off HFROSC to save power + sifive.PRIC.HFROSCCFG.ClearBits(sifive.PRIC_HFROSCCFG_ENABLE) + + // Enable the RTC. + sifive.RTC.CONFIG.Set(sifive.RTC_CONFIG_ENALWAYS) +} + +func preinit() { + // Initialize .bss: zero-initialized global variables. + ptr := uintptr(unsafe.Pointer(&_sbss)) + for ptr != uintptr(unsafe.Pointer(&_ebss)) { + *(*uint32)(unsafe.Pointer(ptr)) = 0 + ptr += 4 + } + + // Initialize .data: global variables initialized from flash. + src := uintptr(unsafe.Pointer(&_sidata)) + dst := uintptr(unsafe.Pointer(&_sdata)) + for dst != uintptr(unsafe.Pointer(&_edata)) { + *(*uint32)(unsafe.Pointer(dst)) = *(*uint32)(unsafe.Pointer(src)) + dst += 4 + src += 4 + } +} + +func putchar(c byte) { + machine.UART0.WriteByte(c) +} + +func ticks() timeUnit { + // Combining the low bits and the high bits yields a time span of over 270 + // years without counter rollover. + highBits := sifive.RTC.HI.Get() + for { + lowBits := sifive.RTC.LO.Get() + newHighBits := sifive.RTC.HI.Get() + if newHighBits == highBits { + // High bits stayed the same. + return timeUnit(lowBits) | (timeUnit(highBits) << 32) + } + // Retry, because there was a rollover in the low bits (happening every + // 1.5 days). + highBits = newHighBits + } +} + +const asyncScheduler = false + +func sleepTicks(d timeUnit) { + target := ticks() + d + for ticks() < target { + } +} + +func abort() { + // lock up forever + for { + riscv.Asm("wfi") + } +} diff --git a/src/runtime/runtime_tinygoriscv.go b/src/runtime/runtime_tinygoriscv.go new file mode 100644 index 00000000..691680b4 --- /dev/null +++ b/src/runtime/runtime_tinygoriscv.go @@ -0,0 +1,19 @@ +// +build tinygo.riscv + +package runtime + +import "unsafe" + +// Implement memset for LLVM. +//go:export memset +func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) { + for i := uintptr(0); i < size; i++ { + *(*byte)(unsafe.Pointer(uintptr(ptr) + i)) = c + } +} + +// Implement memmove for LLVM. +//go:export memmove +func libc_memmove(dst, src unsafe.Pointer, size uintptr) { + memmove(dst, src, size) +} diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index 1eb8d0a1..61572ba7 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -1,4 +1,4 @@ -// +build darwin linux,!avr,!cortexm +// +build darwin linux,!avr,!cortexm,!tinygo.riscv package runtime diff --git a/targets/fe310.json b/targets/fe310.json new file mode 100644 index 00000000..36bb7452 --- /dev/null +++ b/targets/fe310.json @@ -0,0 +1,5 @@ +{ + "inherits": ["riscv"], + "features": ["+a", "+c", "+m"], + "build-tags": ["fe310", "sifive"] +} diff --git a/targets/hifive1b.json b/targets/hifive1b.json new file mode 100644 index 00000000..9161c77b --- /dev/null +++ b/targets/hifive1b.json @@ -0,0 +1,7 @@ +{ + "inherits": ["fe310"], + "build-tags": ["hifive1b"], + "ldflags": [ + "-T", "targets/hifive1b.ld" + ] +} diff --git a/targets/hifive1b.ld b/targets/hifive1b.ld new file mode 100644 index 00000000..29ec78ad --- /dev/null +++ b/targets/hifive1b.ld @@ -0,0 +1,10 @@ + +MEMORY +{ + FLASH_TEXT (rw) : ORIGIN = 0x20010000, LENGTH = 0x6a120 + RAM (xrw) : ORIGIN = 0x80000000, LENGTH = 0x4000 +} + +_stack_size = 2K; + +INCLUDE "targets/riscv.ld" diff --git a/targets/riscv.json b/targets/riscv.json new file mode 100644 index 00000000..581c5e8c --- /dev/null +++ b/targets/riscv.json @@ -0,0 +1,25 @@ +{ + "llvm-target": "riscv32--none", + "goos": "linux", + "goarch": "arm", + "build-tags": ["tinygo.riscv", "linux", "arm"], + "gc": "conservative", + "compiler": "riscv64-unknown-elf-gcc", + "linker": "riscv64-unknown-elf-ld", + "cflags": [ + "-march=rv32imac", + "-mabi=ilp32", + "-Os", + "-Werror", + "-nostdinc", + "-fno-exceptions", "-fno-unwind-tables", + "-ffunction-sections", "-fdata-sections" + ], + "ldflags": [ + "-melf32lriscv", + "--gc-sections" + ], + "extra-files": [ + "src/device/riscv/start.S" + ] +} diff --git a/targets/riscv.ld b/targets/riscv.ld new file mode 100644 index 00000000..857d2ee4 --- /dev/null +++ b/targets/riscv.ld @@ -0,0 +1,56 @@ +SECTIONS +{ + .text : + { + KEEP(*(.init)) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + . = ALIGN(4); + } >FLASH_TEXT + + /* Put the stack at the bottom of RAM, so that the application will + * crash on stack overflow instead of silently corrupting memory. + * See: http://blog.japaric.io/stack-overflow-protection/ */ + .stack : + { + . = ALIGN(4); + . += _stack_size; + _stack_top = .; + } >RAM + + /* Start address (in flash) of .data, used by startup code. */ + _sidata = LOADADDR(.data); + + /* Globals with initial value */ + .data : + { + . = ALIGN(4); + /* see https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register */ + PROVIDE( __global_pointer$ = . + (4K / 2) ); + _sdata = .; /* used by startup code */ + *(.data) + *(.data*) + . = ALIGN(4); + _edata = .; /* used by startup code */ + } >RAM AT>FLASH_TEXT + + /* Zero-initialized globals */ + .bss : + { + . = ALIGN(4); + _sbss = .; /* used by startup code */ + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; /* used by startup code */ + } >RAM +} + +/* For the memory allocator. */ +_heap_start = _ebss; +_heap_end = ORIGIN(RAM) + LENGTH(RAM); +_globals_start = _sdata; +_globals_end = _ebss; diff --git a/tools/gen-device-svd.py b/tools/gen-device-svd.py index 0f6085fa..fc9e3f16 100755 --- a/tools/gen-device-svd.py +++ b/tools/gen-device-svd.py @@ -151,7 +151,10 @@ def readSVD(path, sourceURL): dimIncrement = None else: dim = int(getText(cluster.find('dim'))) - dimIncrement = int(getText(cluster.find('dimIncrement')), 0) + if dim == 1: + dimIncrement = None + else: + dimIncrement = int(getText(cluster.find('dimIncrement')), 0) clusterRegisters = [] for regEl in cluster.findall('register'): clusterRegisters.extend(parseRegister(groupName or name, regEl, baseAddress + clusterOffset, clusterPrefix)) @@ -241,7 +244,7 @@ def parseBitfields(groupName, regName, fieldsEls, bitfieldPrefix=''): }) for enumEl in fieldEl.findall('enumeratedValues/enumeratedValue'): enumName = getText(enumEl.find('name')) - enumDescription = getText(enumEl.find('description')) + enumDescription = getText(enumEl.find('description')).replace('\n', ' ') enumValue = int(getText(enumEl.find('value')), 0) fields.append({ 'name': '{}_{}{}_{}_{}'.format(groupName, bitfieldPrefix, regName, fieldName, enumName),