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

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