Skip to content

Conversation

@firelizzard18
Copy link
Contributor

@firelizzard18 firelizzard18 commented Jan 22, 2020

I want to use TinyGo for the Teensy 3.6, which is powered by the MK66FX1M0VMD18, a NXP Kinetis K66 processor. TinyGo does not currently support any NXP device.

@firelizzard18
Copy link
Contributor Author

I would love any help I can get on this. I am out of my depth when it comes to the low level stuff, such as the runtime files and linker scripts. @PaulStoffregen, would you be willing to advise and/or help?

This was referenced Jan 22, 2020
@aykevl
Copy link
Member

aykevl commented Jan 22, 2020

What I usually do is to try and find the most simple bare-metal C code that works, without any SDK. Just turning a LED on/off in an endless loop for example, without any delay even. You can often even ignore clock initialization. And then try and port it to Go, writing it directly in the Reset_Handler.

@aykevl
Copy link
Member

aykevl commented Jan 22, 2020

For the linker script, you could start with this (copied from the microbit):

MEMORY
{
    FLASH_TEXT (rw) : ORIGIN = 0x00000000, LENGTH = 256K /* .text */
    RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 16K
}

_stack_size = 2K;

INCLUDE "targets/arm.ld"

Flash/RAM is much bigger than that, but it should work.

This should also be a useful resource: https://www.seanet.com/~karllunt/bareteensy31.html

@firelizzard18
Copy link
Contributor Author

firelizzard18 commented Jan 23, 2020

I made a stripped down blink program using the Teensy 3 Arduino core. Besides main.c, the binary only includes mk20dx128.c and pins_teensy.c from the core. I have been removing bits from those files to see how small I can get.

@firelizzard18
Copy link
Contributor Author

I am trying to get blinky.go to compile, but I am running into odd errors. I'm building on macOS. Maybe XCode's clang is weird?

package main

import (
	"device/nxp"
	"runtime/volatile"
	"time"
	"unsafe"
)

func main() {
	mode := (*volatile.Register8)(unsafe.Pointer(uintptr(0x43FE1294)))
	config := nxp.PORTC.PCR5

	mode.Set(1)
	config.SetBits(nxp.PORT_PCR5_SRE | nxp.PORT_PCR5_DSE | 1<<nxp.PORT_PCR0_MUX_Pos)
	config.ClearBits(nxp.PORT_PCR5_ODE)

	for {
		nxp.GPIOC.PSOR.SetBits(1 << 5)
		time.Sleep(time.Millisecond * 1000)

		nxp.GPIOC.PCOR.SetBits(1 << 5)
		time.Sleep(time.Millisecond * 1000)
	}
}
warning: optimization flag '-fsingle-precision-constant' is not supported
warning: argument unused during compilation: '-mthumb'
warning: argument unused during compilation: '-mfloat-abi=hard'
warning: argument unused during compilation: '-mfpu=fpv4-sp-d16'
TINYGO/src/device/arm/cortexm.s:1:1: error: unknown directive
.syntax unified
^
TINYGO/src/device/arm/cortexm.s:3:33: error: unexpected token in '.section' directive
.section .text.HardFault_Handler
                                ^
TINYGO/src/device/arm/cortexm.s:5:1: error: unknown directive
.type HardFault_Handler, %function
^
TINYGO/src/device/arm/cortexm.s:10:5: error: unknown use of instruction mnemonic without a size suffix
    mov r0, sp
    ^
TINYGO/src/device/arm/cortexm.s:16:14: error: unknown token in expression
    movs r3, #0
             ^
TINYGO/src/device/arm/cortexm.s:17:13: error: brackets expression not supported on this target
    ldr r3, [r3]
            ^
TINYGO/src/device/arm/cortexm.s:18:5: error: unknown use of instruction mnemonic without a size suffix
    mov sp, r3
    ^
TINYGO/src/device/arm/cortexm.s:21:5: error: invalid instruction mnemonic 'bl'
    bl handleHardFault
    ^~
TINYGO/src/device/arm/cortexm.s:25:31: error: unexpected token in '.section' directive
.section .text.SemihostingCall
                              ^
TINYGO/src/device/arm/cortexm.s:27:1: error: unknown directive
.type SemihostingCall, %function
^
TINYGO/src/device/arm/cortexm.s:29:5: error: invalid instruction mnemonic 'bkpt'
    bkpt 0xab
    ^~~~
TINYGO/src/device/arm/cortexm.s:30:5: error: invalid instruction mnemonic 'bx'
    bx lr
    ^~
error: failed to build src/device/arm/cortexm.s: failed to compile using built-in clang

I got the address for mode by expanding *portModeRegister(13) = 1: https://godbolt.org/z/tAR6U-.

@aykevl
Copy link
Member

aykevl commented Jan 23, 2020

Those Clang errors indicate that Clang does not get the correct --target flag. Take a look at this file for example: https://github.com/tinygo-org/tinygo/blob/master/targets/atsamd51g19a.json

My recommendation would be to just copy/modify that file, you can ignore float at least during the initial porting:

{
	"inherits": ["cortex-m"],
	"llvm-target": "armv7em-none-eabi",
	"build-tags": ["teensy36", "teensy", "mk66f18", "nxp"],
	"cflags": [
		"--target=armv7em-none-eabi",
		"-Qunused-arguments"
	],
	"linkerscript": "targets/nxpmk66f18.ld",
	"extra-files": [
		"src/device/nxp/mk66f18.s"
	],
	"flash-command": "teensy_loader_cli -mmcu=mk66fx1m0 -v -w {hex}"
}

@aykevl
Copy link
Member

aykevl commented Jan 23, 2020

I got the address for mode by expanding *portModeRegister(13) = 1: https://godbolt.org/z/tAR6U-.

That's an odd address. Maybe it is using bitbanding? Register addresses usually form a certain pattern.

@firelizzard18
Copy link
Contributor Author

That's an odd address. Maybe it is using bitbanding? Register addresses usually form a certain pattern.

Ah, yes! There are bitband macros, but I had no idea what they were about. A partial expansion is *((volatile uint8_t *)(GPIO_BITBAND_PTR(GPIOC_PDOR, CORE_PIN13_BIT) + 160)) = 1; and the macros are:

#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))

I updated teensy36.json so it builds now. I left in the float flags because its not complaining about those, but I did remove a number of other flags. Now I just have to get it to work!

@firelizzard18
Copy link
Contributor Author

firelizzard18 commented Jan 23, 2020

I added bitband support (behind build tags) to runtime/volatile, so my test program is much cleaner:

package main

import (
	"device/nxp"
	"time"
)

func main() {
	nxp.GPIOC.PDDR.Bit(5).Set(1)
	nxp.PORTC.PCR5.SetBits(nxp.PORT_PCR5_SRE | nxp.PORT_PCR5_DSE | 1<<nxp.PORT_PCR0_MUX_Pos)
	nxp.PORTC.PCR5.ClearBits(nxp.PORT_PCR5_ODE)

	for {
		nxp.GPIOC.PSOR.SetBits(1 << 5)
		time.Sleep(time.Millisecond * 1000)

		nxp.GPIOC.PCOR.SetBits(1 << 5)
		time.Sleep(time.Millisecond * 1000)
	}
}

@aykevl
Copy link
Member

aykevl commented Jan 24, 2020

You should be able to simplify things by not using bitbanding. These regions are really just aliases for bits in registers. Instead, you can use SetBits and ClearBits which were designed for this purpose.

To be honest, I think it would be best to leave it out initially. As far as I know, bitbanding is mainly an optimization.

@aykevl
Copy link
Member

aykevl commented Jan 24, 2020

Have you gotten the LED to blink?

You could perhaps try without bitbanding. These lines should be equivalent:

nxp.GPIOC.PDDR.Bit(5).Set(1)
nxp.GPIOC.PDDR.SetBits(1 << 5)

Also, you can try removing the time.Sleep: if the LED works, it should light up. Also: LEDs are often connected to VCC which means that setting the pin to low will enable it (somewhat counterintuitively).

@aykevl
Copy link
Member

aykevl commented Jan 24, 2020

Also, something that looks odd:

nxp.GPIOC.PSOR.SetBits(1 << 5)
// ...
nxp.GPIOC.PCOR.SetBits(1 << 5)

I think this should be:

nxp.GPIOC.PSOR.Set(1 << 5)
// ...
nxp.GPIOC.PCOR.Set(1 << 5)

If you haven't seen already, this is described in the 2000+ page Reference Manual for this chip, somewhere near the end in the GPIO peripheral.

@firelizzard18
Copy link
Contributor Author

I have not tried to load anything onto the board (excluding C/C++ programs). I am currently working on translating the initialization code from the Teensy core libraries. Once I have that translated, I'll start trying to get it to work.

I am using bit banding there because I blindly copied the contents of pinMode from the Teensy core libraries and then blindly translated that to go.

I'm not sure why I decided to use SetBit. After reading the spec, you are clearly right.

In the current version blinky, I am using runtime.sleepTicks, which (at the moment) is just a port of delay from the Teensy core libraries and does not sleep but spins until the appropriate duration has elapsed, according to runtime.ticks. runtime.ticks is a port of micros from the Teensy core libraries.

@firelizzard18
Copy link
Contributor Author

I've been putting my test program in a gist: https://gist.github.com/firelizzard18/eaaa532f7ee18a7619b3b682490960ee

@firelizzard18
Copy link
Contributor Author

I tried to create a PR to merge posborne/cmsis-svd into tinygo-org/cmsis-svd, but it seems that I can't do that

@firelizzard18
Copy link
Contributor Author

I have translated all the initialization code, but blink does not work. Time to start debugging...

@firelizzard18
Copy link
Contributor Author

firelizzard18 commented Jan 25, 2020

The MK66FX1M0VMD18 requires a 16 byte flash config section at 0x400. To add this, I added teensy36.s, copied arm.ld into nxpmk66f18.ld, and added .flashconfig to .text. After doing this, I see that .Lmain.go.main$gowrapper is located at 0x400 instead of 0x1d0, but I do not see .flashconfig. What am I missing? I corrected teensy36.s and this now works.

@PaulStoffregen
Copy link

Hi, Paul here, the guy behind Teensy. I can't get involved in Tiny Go, but I would like to quickly bring a couple issues about the 16 byte flash config to your attention.

The FSEC byte controls powerful features, like security which prevents external access to memory, and an option to disable mass erase. If you activate both of these features, it will permanently disable reprogramming the flash. Normal programming over USB with Teensy Loader tries to prevent this from ever happening. It programs one of those bits which needs to be a 1 for that complete lock out to a 0. If you experiment with arbitrary values for FSEC, you'll see the byte that ends up in flash isn't always the same as what you defined. That is the lock-out safety feature at work, trying to prevent you from permanently bricking your Teensy!

Teensy Loader does allow you to turn on the security feature. While this is recoverable, it does have some practical consequences. Normally when you press the button on Teensy, it enters programming mode, but nothing in flash actually changes until Teensy Loader sends new code. If you power cycle without programming, your code is still there and runs again. But security changes all that. Pressing the button causes complete flash erase. So be aware that a touch of that button becomes a code "self destruct" for flash memory. Not a big deal if you were going to upload new code, but something to keep in mind if the button is accidentally pressed.

If you don't use the flash controller to do your own erasing and programming of the flash, using only Teensy Loader to write hex files, you should be safe while experimenting. But if you write your own code which erases those 16 bytes and writes them with new values, please remember you're running without a safety net. You can permanently brick your Teensy if the wrong bit pattern ends up in those flash config bytes!

@aykevl
Copy link
Member

aykevl commented Jan 26, 2020

@PaulStoffregen that's very useful information, thank you for sharing!

@firelizzard18 I wanted to say "use GDB with a debug probe to see what's wrong" and while that may be a good idea, I then stumbled on this post which shows that it's unfortunately not easy to set up on the Teensy 3.6. Still, a real debugger (available for under $20, and around $2 if you want to play with AliExpress-quality products) would probably make it much easier to see where the problem lies.

I quickly looked at the linker script and it looks good to me.

@firelizzard18
Copy link
Contributor Author

I have a SWD debugger and a soldering iron, so I'll see if I can hook it up without destroying something :P

@firelizzard18
Copy link
Contributor Author

AFAICT, I fried my teensy, or at least the MKL02. I'll try debugging again once I have the equipment to completely remove the MKL02.

@firelizzard18
Copy link
Contributor Author

Debugging the teensy is a no-go. My soldering skills are not up to that level of work. I might buy a FRDM-K66, but for now I'm going to have fun comparing disassembly listings.

@firelizzard18
Copy link
Contributor Author

firelizzard18 commented Feb 17, 2020

I have a bare minimum functioning (blinking) system, compiled with -gc=none, with the program below:

package main

import (
	"device/arm"
	"device/nxp"
	_ "machine"
)

func main() {
	nxp.GPIOC.PDDR.SetBits(1 << 5)
	nxp.PORTC.PCR5.SetBits(nxp.PORT_PCR5_SRE | nxp.PORT_PCR5_DSE | 1<<nxp.PORT_PCR0_MUX_Pos)
	nxp.PORTC.PCR5.ClearBits(nxp.PORT_PCR5_ODE)

	for {
		nxp.GPIOC.PSOR.Set(1 << 5)
		for i := 0; i < 1<<23; i++ {
			arm.Asm("nop")
		}

		nxp.GPIOC.PCOR.Set(1 << 5)
		for i := 0; i < 1<<25; i++ {
			arm.Asm("nop")
		}
	}
}

@aykevl
Copy link
Member

aykevl commented Feb 18, 2020

Awesome!
I think you should be able to go from there to a fully working target, but feel free to ask if you have any questions as to how to do that.

@firelizzard18
Copy link
Contributor Author

firelizzard18 commented Feb 19, 2020

I can use -gc=none or -gc=leaking, but if TinyGo inserts scheduling into the process (which happens if I omit -gc and either call Gosched() anywhere or uncomment initAll()), it breaks. Since I don't want to debug that via blinking the LED, my plan is implement UART0, then USB serial, then scheduling.

@aykevl
Copy link
Member

aykevl commented Feb 20, 2020

(which happens if I omit -gc and either call Gosched() anywhere or uncomment initAll())

initAll should really be fixed, without it the program is in an inconsistent state. What is the issue you're getting?

@deadprogram
Copy link
Member

Actually, looking more closely, I think you should avoid any changes all all to the existing USB code at this time. There are changes made in this PR that would be much better made as part of some other PR specific to refactoring USB code, and also I am pretty sure that you could be using some of the shared USB code instead of code that you have added.

So that means just removing the last commit, it would appear. As @aykevl said we can take up getting the USB CDC working in a subsequent PR.

I will wire up my setup for testing my Teensy 3.6 now.

@firelizzard18 firelizzard18 force-pushed the teensy-3.6 branch 2 times, most recently from b37f0e2 to 3eee51d Compare July 5, 2020 20:33
@firelizzard18
Copy link
Contributor Author

@deadprogram I pushed some very minor modifications. Mostly I added arm.Asm("dsb 0xF") after looking through Freescale's SDK. I pushed the USB changes to teensy-3.6-usb

some other PR specific to refactoring USB code

I didn't refactor the USB code so much as rearrange it so I could selectively exclude sendDescriptor and sendConfiguration.

you could be using some of the shared USB code instead of code that you have added

I could and planned to use the descriptor structs and constructors more. But for the initial implementation, I wanted to keep everything as close to Paul's implementation as I could. I planned to modify getUSBDescriptor to use those constructors instead of a byte array, once CDC worked.

sendConfiguration and therefore sendDescriptor does not allow any customization of the USB configuration. The USB support has to behave in a certain way in order to fully support the Teensy programmer. I don't know enough about it to know if full programmer support requires a custom USB config descriptor. Regardless, the Teensy can support many different USB functions, such as mouse, keyboard, joystick, MIDI, audio, etc. Supporting anything beyond CDC would require customization of the config descriptor.

I have spent a fair amount of time by now looking at Wireshark USB dumps

Would you like me to email you some Windows/USBPcap dumps? Wireshark understands *.pcap files. Since I don't know how to isolate the Teensy, this would include all of my USB traffic (because basically everything in my system is on a single root hub), so I am unwilling to post them publicly.

@firelizzard18
Copy link
Contributor Author

FYI, all hell broke loose when I attempted to use UART0 directly from within the USB ISR. Hence why I write debug messages to a global variable.

@deadprogram
Copy link
Member

@firelizzard18 looks like you need to run go fmt on src/machine/board_teensy36.go please.

@deadprogram
Copy link
Member

I just tried this branch with the latest dev branch, and got the following:

$ tinygo flash -target teensy36 examples/blinky1
# runtime
/home/ron/.gvm/pkgsets/go1.14.2/global/src/github.com/tinygo-org/tinygo/src/runtime/runtime.go:64:9: undeclared name: ticksToNanoseconds
/home/ron/.gvm/pkgsets/go1.14.2/global/src/github.com/tinygo-org/tinygo/src/runtime/scheduler_any.go:10:31: undeclared name: nanosecondsToTicks

@firelizzard18
Copy link
Contributor Author

@deadprogram The build failure is fixed now. You might not be able to use tinygo flash. The Teensy CLI loader didn't work for me. I am using the GUI tool.

@deadprogram
Copy link
Member

Using this branch as of the last commit, I have blinky1 success!

$ tinygo flash -target teensy36 examples/blinky1
Teensy Loader, Command Line, Version 2.1
Read "/tmp/tinygo278174858/main.hex": 7188 bytes, 0.7% usage
Waiting for Teensy device...
 (hint: press the reset button)
Found HalfKay Bootloader
Read "/tmp/tinygo278174858/main.hex": 7188 bytes, 0.7% usage
Programming........
Booting

I did have to press the reset button when flashing, but then it worked.

@deadprogram
Copy link
Member

deadprogram commented Jul 7, 2020

In further testing, I am able to run this branch against the TinyHCI and all of the current hardware passes.

This PR certainly seems functional enough to merge to keep working on additional features.

@aykevl or anyone else have any further comments here?

@deadprogram
Copy link
Member

@jaddr2line can you also please lookover this PR? Also anyone else who is interested. Thank you!

@deadprogram deadprogram added this to the v0.14 milestone Jul 7, 2020
@deadprogram
Copy link
Member

Sorry @firelizzard18 but just one last merge conflict (hopefully!) to resolve, if you please.

@firelizzard18 firelizzard18 force-pushed the teensy-3.6 branch 2 times, most recently from 6a9e183 to 0e1caeb Compare July 7, 2020 23:47
@firelizzard18
Copy link
Contributor Author

@deadprogram Merge conflicts resolved

- Fix UART & putChar
- Timer-based sleep
- Enable systick in abort
- Buffered, interrupt-based UART TX
- Use the new interrupt API and fix sleepTicks
- Make pins behave more like other boards
- Use the MCU's UART numbering
- Allow interrupts to wake the scheduler (tinygo-org#1214)
@deadprogram
Copy link
Member

At long last, time to merge this pull request. I know there are still things to be done for this board, but it will be a lot easier to work on those in smaller, more specific PRs.

Thank you very much @firelizzard18 for all of your work on this contribution, and thanks everyone else who helped get it done.

Now rebasing/merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants