Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/examples/systick/systick.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,27 @@ import (
"machine"
)

var timerCh = make(chan struct{}, 1)

func main() {
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})

// timer fires 10 times per second
arm.SetupSystemTimer(machine.CPUFrequency() / 10)

for {
machine.LED.Low()
<-timerCh
machine.LED.High()
<-timerCh
}
}

var led_state bool

//export SysTick_Handler
func timer_isr() {
if led_state {
machine.LED.Low()
} else {
machine.LED.High()
select {
case timerCh <- struct{}{}:
default:
// The consumer is running behind.
}
led_state = !led_state
}
28 changes: 27 additions & 1 deletion src/internal/task/queue.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package task

import "runtime/interrupt"

const asserts = false

// Queue is a FIFO container of tasks.
Expand All @@ -10,7 +12,9 @@ type Queue struct {

// Push a task onto the queue.
func (q *Queue) Push(t *Task) {
i := interrupt.Disable()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be more efficient to put all these locks where the queue/stack are used.

if asserts && t.Next != nil {
interrupt.Restore(i)
panic("runtime: pushing a task to a queue with a non-nil Next pointer")
}
if q.tail != nil {
Expand All @@ -21,31 +25,45 @@ func (q *Queue) Push(t *Task) {
if q.head == nil {
q.head = t
}
interrupt.Restore(i)
}

// Pop a task off of the queue.
func (q *Queue) Pop() *Task {
i := interrupt.Disable()
t := q.head
if t == nil {
interrupt.Restore(i)
return nil
}
q.head = t.Next
if q.tail == t {
q.tail = nil
}
t.Next = nil
interrupt.Restore(i)
return t
}

// Append pops the contents of another queue and pushes them onto the end of this queue.
func (q *Queue) Append(other *Queue) {
i := interrupt.Disable()
if q.head == nil {
q.head = other.head
} else {
q.tail.Next = other.head
}
q.tail = other.tail
other.head, other.tail = nil, nil
interrupt.Restore(i)
}

// Empty checks if the queue is empty.
func (q *Queue) Empty() bool {
i := interrupt.Disable()
empty := q.head == nil
interrupt.Restore(i)
return empty
}

// Stack is a LIFO container of tasks.
Expand All @@ -57,19 +75,24 @@ type Stack struct {

// Push a task onto the stack.
func (s *Stack) Push(t *Task) {
i := interrupt.Disable()
if asserts && t.Next != nil {
interrupt.Restore(i)
panic("runtime: pushing a task to a stack with a non-nil Next pointer")
}
s.top, t.Next = t, s.top
interrupt.Restore(i)
}

// Pop a task off of the stack.
func (s *Stack) Pop() *Task {
i := interrupt.Disable()
t := s.top
if t != nil {
s.top = t.Next
t.Next = nil
}
interrupt.Restore(i)
return t
}

Expand All @@ -89,10 +112,13 @@ func (t *Task) tail() *Task {
// Queue moves the contents of the stack into a queue.
// Elements can be popped from the queue in the same order that they would be popped from the stack.
func (s *Stack) Queue() Queue {
i := interrupt.Disable()
head := s.top
s.top = nil
return Queue{
q := Queue{
head: head,
tail: head.tail(),
}
interrupt.Restore(i)
return q
}
4 changes: 4 additions & 0 deletions src/runtime/arch_cortexm.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,7 @@ func procPin() {
func procUnpin() {
arm.EnableInterrupts(procPinnedMask)
}

func waitForEvents() {
arm.Asm("wfe")
}
8 changes: 8 additions & 0 deletions src/runtime/arch_tinygoriscv.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,11 @@ func procPin() {
func procUnpin() {
riscv.EnableInterrupts(procPinnedMask)
}

func waitForEvents() {
mask := riscv.DisableInterrupts()
if !runqueue.Empty() {
riscv.Asm("wfi")
}
riscv.EnableInterrupts(mask)
}
2 changes: 2 additions & 0 deletions src/runtime/baremetal.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ func libc_malloc(size uintptr) unsafe.Pointer {
func libc_free(ptr unsafe.Pointer) {
free(ptr)
}

const baremetal = true
43 changes: 43 additions & 0 deletions src/runtime/chan.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ package runtime

import (
"internal/task"
"runtime/interrupt"
"unsafe"
)

Expand Down Expand Up @@ -308,13 +309,17 @@ func (ch *channel) trySend(value unsafe.Pointer) bool {
return false
}

i := interrupt.Disable()

switch ch.state {
case chanStateEmpty, chanStateBuf:
// try to dump the value directly into the buffer
if ch.push(value) {
ch.state = chanStateBuf
interrupt.Restore(i)
return true
}
interrupt.Restore(i)
return false
case chanStateRecv:
// unblock reciever
Expand All @@ -328,16 +333,21 @@ func (ch *channel) trySend(value unsafe.Pointer) bool {
ch.state = chanStateEmpty
}

interrupt.Restore(i)
return true
case chanStateSend:
// something else is already waiting to send
interrupt.Restore(i)
return false
case chanStateClosed:
interrupt.Restore(i)
runtimePanic("send on closed channel")
default:
interrupt.Restore(i)
runtimePanic("invalid channel state")
}

interrupt.Restore(i)
return false
}

Expand All @@ -351,6 +361,8 @@ func (ch *channel) tryRecv(value unsafe.Pointer) (bool, bool) {
return false, false
}

i := interrupt.Disable()

switch ch.state {
case chanStateBuf, chanStateSend:
// try to pop the value directly from the buffer
Expand All @@ -373,6 +385,7 @@ func (ch *channel) tryRecv(value unsafe.Pointer) (bool, bool) {
ch.state = chanStateEmpty
}

interrupt.Restore(i)
return true, true
} else if ch.blocked != nil {
// unblock next sender if applicable
Expand All @@ -386,19 +399,24 @@ func (ch *channel) tryRecv(value unsafe.Pointer) (bool, bool) {
ch.state = chanStateEmpty
}

interrupt.Restore(i)
return true, true
}
interrupt.Restore(i)
return false, false
case chanStateRecv, chanStateEmpty:
// something else is already waiting to recieve
interrupt.Restore(i)
return false, false
case chanStateClosed:
if ch.pop(value) {
interrupt.Restore(i)
return true, true
}

// channel closed - nothing to recieve
memzero(value, ch.elementSize)
interrupt.Restore(i)
return true, false
default:
runtimePanic("invalid channel state")
Expand Down Expand Up @@ -447,14 +465,18 @@ type chanSelectState struct {
// This operation will block unless a value is immediately available.
// May panic if the channel is closed.
func chanSend(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList) {
i := interrupt.Disable()

if ch.trySend(value) {
// value immediately sent
chanDebug(ch)
interrupt.Restore(i)
return
}

if ch == nil {
// A nil channel blocks forever. Do not schedule this goroutine again.
interrupt.Restore(i)
deadlock()
}

Expand All @@ -468,6 +490,7 @@ func chanSend(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList
}
ch.blocked = blockedlist
chanDebug(ch)
interrupt.Restore(i)
task.Pause()
sender.Ptr = nil
}
Expand All @@ -477,14 +500,18 @@ func chanSend(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList
// The recieved value is copied into the value pointer.
// Returns the comma-ok value.
func chanRecv(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList) bool {
i := interrupt.Disable()

if rx, ok := ch.tryRecv(value); rx {
// value immediately available
chanDebug(ch)
interrupt.Restore(i)
return ok
}

if ch == nil {
// A nil channel blocks forever. Do not schedule this goroutine again.
interrupt.Restore(i)
deadlock()
}

Expand All @@ -498,6 +525,7 @@ func chanRecv(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList
}
ch.blocked = blockedlist
chanDebug(ch)
interrupt.Restore(i)
task.Pause()
ok := receiver.Data == 1
receiver.Ptr, receiver.Data = nil, 0
Expand All @@ -511,15 +539,18 @@ func chanClose(ch *channel) {
// Not allowed by the language spec.
runtimePanic("close of nil channel")
}
i := interrupt.Disable()
switch ch.state {
case chanStateClosed:
// Not allowed by the language spec.
interrupt.Restore(i)
runtimePanic("close of closed channel")
case chanStateSend:
// This panic should ideally on the sending side, not in this goroutine.
// But when a goroutine tries to send while the channel is being closed,
// that is clearly invalid: the send should have been completed already
// before the close.
interrupt.Restore(i)
runtimePanic("close channel during send")
case chanStateRecv:
// unblock all receivers with the zero value
Expand All @@ -531,6 +562,7 @@ func chanClose(ch *channel) {
// Easy case. No available sender or receiver.
}
ch.state = chanStateClosed
interrupt.Restore(i)
chanDebug(ch)
}

Expand All @@ -541,8 +573,11 @@ func chanClose(ch *channel) {
// TODO: do this in a round-robin fashion (as specified in the Go spec) instead
// of picking the first one that can proceed.
func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelBlockedList) (uintptr, bool) {
istate := interrupt.Disable()

if selected, ok := tryChanSelect(recvbuf, states); selected != ^uintptr(0) {
// one channel was immediately ready
interrupt.Restore(istate)
return selected, ok
}

Expand Down Expand Up @@ -570,6 +605,7 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelB
case chanStateRecv:
// already in correct state
default:
interrupt.Restore(istate)
runtimePanic("invalid channel state")
}
} else {
Expand All @@ -582,6 +618,7 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelB
case chanStateBuf:
// already in correct state
default:
interrupt.Restore(istate)
runtimePanic("invalid channel state")
}
}
Expand All @@ -594,6 +631,7 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelB
t.Data = 1

// wait for one case to fire
interrupt.Restore(istate)
task.Pause()

// figure out which one fired and return the ok value
Expand All @@ -602,22 +640,27 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelB

// tryChanSelect is like chanSelect, but it does a non-blocking select operation.
func tryChanSelect(recvbuf unsafe.Pointer, states []chanSelectState) (uintptr, bool) {
istate := interrupt.Disable()

// See whether we can receive from one of the channels.
for i, state := range states {
if state.value == nil {
// A receive operation.
if rx, ok := state.ch.tryRecv(recvbuf); rx {
chanDebug(state.ch)
interrupt.Restore(istate)
return uintptr(i), ok
}
} else {
// A send operation: state.value is not nil.
if state.ch.trySend(state.value) {
chanDebug(state.ch)
interrupt.Restore(istate)
return uintptr(i), true
}
}
}

interrupt.Restore(istate)
return ^uintptr(0), false
}
Loading