github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_k210.go (about)

     1  //go:build k210
     2  
     3  // This file implements target-specific things for the K210 chip as used in the
     4  // MAix Bit with Mic.
     5  
     6  package runtime
     7  
     8  import (
     9  	"device/kendryte"
    10  	"device/riscv"
    11  	"machine"
    12  	"runtime/volatile"
    13  	"unsafe"
    14  )
    15  
    16  type timeUnit int64
    17  
    18  //export main
    19  func main() {
    20  
    21  	// Both harts should disable all interrupts on startup.
    22  	initPLIC()
    23  
    24  	// Only use one hart for the moment.
    25  	if riscv.MHARTID.Get() != 0 {
    26  		abort()
    27  	}
    28  
    29  	// Reset all interrupt source priorities to zero.
    30  	for i := 0; i < kendryte.IRQ_max; i++ {
    31  		kendryte.PLIC.PRIORITY[i].Set(0)
    32  	}
    33  
    34  	// Zero MCAUSE, which is set to the reset reason on reset. It must be zeroed
    35  	// to make interrupt.In() work.
    36  	// This would also be a good time to save the reset reason, but that hasn't
    37  	// been implemented yet.
    38  	riscv.MCAUSE.Set(0)
    39  
    40  	// Set the interrupt address.
    41  	// Note that this address must be aligned specially, otherwise the MODE bits
    42  	// of MTVEC won't be zero.
    43  	riscv.MTVEC.Set(uintptr(unsafe.Pointer(&handleInterruptASM)))
    44  
    45  	// Reset the MIE register and enable external interrupts.
    46  	// It must be reset here because it not zeroed at startup.
    47  	riscv.MIE.Set(1 << 11) // bit 11 is for machine external interrupts
    48  
    49  	// Enable global interrupts now that they've been set up.
    50  	riscv.MSTATUS.SetBits(1 << 3) // MIE
    51  
    52  	preinit()
    53  	initPeripherals()
    54  	run()
    55  	exit(0)
    56  }
    57  
    58  func initPLIC() {
    59  	hartId := riscv.MHARTID.Get()
    60  
    61  	// Zero the PLIC enable bits at startup.
    62  	for i := 0; i < ((kendryte.IRQ_max + 32) / 32); i++ {
    63  		kendryte.PLIC.TARGET_ENABLES[hartId].ENABLE[i].Set(0)
    64  	}
    65  
    66  	// Zero the PLIC threshold bits to allow all interrupts.
    67  	kendryte.PLIC.TARGETS[hartId].THRESHOLD.Set(0)
    68  }
    69  
    70  //go:extern handleInterruptASM
    71  var handleInterruptASM [0]uintptr
    72  
    73  //export handleInterrupt
    74  func handleInterrupt() {
    75  	cause := riscv.MCAUSE.Get()
    76  	code := uint64(cause &^ (1 << 63))
    77  	if cause&(1<<63) != 0 {
    78  		// Topmost bit is set, which means that it is an interrupt.
    79  		switch code {
    80  		case 7: // Machine timer interrupt
    81  			// Signal timeout.
    82  			timerWakeup.Set(1)
    83  			// Disable the timer, to avoid triggering the interrupt right after
    84  			// this interrupt returns.
    85  			riscv.MIE.ClearBits(1 << 7) // MTIE bit
    86  		case 11: // Machine external interrupt
    87  			hartId := riscv.MHARTID.Get()
    88  
    89  			// Claim this interrupt.
    90  			id := kendryte.PLIC.TARGETS[hartId].CLAIM.Get()
    91  			// Call the interrupt handler, if any is registered for this ID.
    92  			kendryte.HandleInterrupt(int(id))
    93  			// Complete this interrupt.
    94  			kendryte.PLIC.TARGETS[hartId].CLAIM.Set(id)
    95  		}
    96  	} else {
    97  		// Topmost bit is clear, so it is an exception of some sort.
    98  		// We could implement support for unsupported instructions here (such as
    99  		// misaligned loads). However, for now we'll just print a fatal error.
   100  		handleException(code)
   101  	}
   102  
   103  	// Zero MCAUSE so that it can later be used to see whether we're in an
   104  	// interrupt or not.
   105  	riscv.MCAUSE.Set(0)
   106  }
   107  
   108  // initPeripherals configures periperhals the way the runtime expects them.
   109  func initPeripherals() {
   110  	// Enable APB0 clock.
   111  	kendryte.SYSCTL.CLK_EN_CENT.SetBits(kendryte.SYSCTL_CLK_EN_CENT_APB0_CLK_EN)
   112  
   113  	// Enable FPIOA peripheral.
   114  	kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_FPIOA_CLK_EN)
   115  
   116  	machine.InitSerial()
   117  }
   118  
   119  func putchar(c byte) {
   120  	machine.Serial.WriteByte(c)
   121  }
   122  
   123  func getchar() byte {
   124  	for machine.Serial.Buffered() == 0 {
   125  		Gosched()
   126  	}
   127  	v, _ := machine.Serial.ReadByte()
   128  	return v
   129  }
   130  
   131  func buffered() int {
   132  	return machine.Serial.Buffered()
   133  }
   134  
   135  var timerWakeup volatile.Register8
   136  
   137  func ticks() timeUnit {
   138  	highBits := uint32(kendryte.CLINT.MTIME.Get() >> 32)
   139  	for {
   140  		lowBits := uint32(kendryte.CLINT.MTIME.Get() & 0xffffffff)
   141  		newHighBits := uint32(kendryte.CLINT.MTIME.Get() >> 32)
   142  		if newHighBits == highBits {
   143  			return timeUnit(lowBits) | (timeUnit(highBits) << 32)
   144  		}
   145  		highBits = newHighBits
   146  	}
   147  }
   148  
   149  func sleepTicks(d timeUnit) {
   150  	target := uint64(ticks() + d)
   151  	kendryte.CLINT.MTIMECMP[0].Set(target)
   152  	riscv.MIE.SetBits(1 << 7) // MTIE
   153  	for {
   154  		if timerWakeup.Get() != 0 {
   155  			timerWakeup.Set(0)
   156  			// Disable timer.
   157  			break
   158  		}
   159  		riscv.Asm("wfi")
   160  	}
   161  }
   162  
   163  // handleException is called from the interrupt handler for any exception.
   164  // Exceptions can be things like illegal instructions, invalid memory
   165  // read/write, and similar issues.
   166  func handleException(code uint64) {
   167  	// For a list of exception codes, see:
   168  	// https://content.riscv.org/wp-content/uploads/2019/08/riscv-privileged-20190608-1.pdf#page=49
   169  	print("fatal error: exception with mcause=")
   170  	print(code)
   171  	print(" pc=")
   172  	print(riscv.MEPC.Get())
   173  	println()
   174  	abort()
   175  }