Traumflug / Teacup_Firmware

Firmware for RepRap and other 3D printers
http://forums.reprap.org/read.php?147
GNU General Public License v2.0
312 stars 199 forks source link

ARM port to Arduino Due #182

Open Wurstnase opened 9 years ago

Wurstnase commented 9 years ago

Hi @Traumflug

first commit works and upload also. Just tried to upload it as a new branch, but I don't have write access.

Traumflug commented 9 years ago

D'oh. Must have gotten lost. Will add you in a second.

Traumflug commented 9 years ago

Done. Welcome to Teacup Development!

Wurstnase commented 9 years ago

Thanks. I will upload it maybe this evening. But I need some rework. Something went wrong. My first binary size is only ~460byte. Yours was >900.

Traumflug commented 9 years ago

My first binary size is only ~460byte.

That's fine, certainly not a problem.

I mean, the obvious reason for a too small binary is, there are some missing functions. Or unneeded functions in the bigger one. Neither is a problem, missing functions will ineviteably show up sooner or later.

Can't wait for your push :-)

Wurstnase commented 9 years ago

Took the lunchtime for some research. I think I've taken the wrong startup-file. For the sam it's a c-file. I took the s-template.

%appdata%\Roaming\Arduino15\packages\arduino\hardware\sam\1.6.4\system\CMSIS\Device\ATMEL\sam3xa\source\gcc_arm\startup_sam3xa.c

Also for reference: http://forum.arduino.cc/index.php?topic=135403.msg1033950#msg1033950 under number 7 are some interesting folders.

Traumflug commented 9 years ago

For this extreme low level stuff (placing reset and interrupt vectors, copying the initial RAM contents from Flash, etc.), assembly is usually the better choice.

If you'd take the minute to do a push, I could see what you're doing. Topic branches are by no means permanent, you can change them later as much as you want.

Wurstnase commented 9 years ago

Just pushed.

I put a copy of the c-file also in that branch.

Wurstnase commented 9 years ago

Well, I don't know what I'm doing but:

replace in startup.s:

    /* External interrupts */
    .long    Default_Handler

with

    /* External interrupts */
    .long    SUPC_Handler,    /*  0  Supply Controller */
    .long    RSTC_Handler,    /*  1  Reset Controller */
    .long    RTC_Handler,     /*  2  Real Time Clock */
    .long    RTT_Handler,     /*  3  Real Time Timer */
    .long    WDT_Handler,     /*  4  Watchdog Timer */
.
.
.

and setting up each unique handler in the end with def_irq_default_handler?

Traumflug commented 9 years ago

Just pushed.

Thanks. I took a brief look and what you did looks fine. The .s file looks also fine. The only thing I'm missing is a SystemInit() function, but if the compiler doesn't complain, that's fine as well.

Which problem do you try to solve?

Wurstnase commented 9 years ago

I stumble a bit with it. I tried to combile some steps further but the size doesn't increase. So I thought it's something in the first files I missed.

Could the SystemInit() be in the .a-file? I found a libsam_sam3x8e_gcc_rel.a.

Traumflug commented 9 years ago

If the size doesn't increase, the additional stuff isn't used and removed at the link stage. You have to actually use the code you add.

Wurstnase commented 9 years ago

Slowly, but I think I'm learning it. The main-difference of your first compile was only the Reset_Handler.

The UART-stuff takes me a lot of time. Maybe next week I can try to send some stuff.

Wurstnase commented 9 years ago

Not that complicated I thought first.

On the sam we don't need to calculate so much stuff (DLL, DLM, FDR). It's simple port->UART_BRGR = (SystemCoreClock / BAUDRATE) >> 4;

Traumflug commented 9 years ago

On the sam we don't need to calculate so much stuff (DLL, DLM, FDR).

All the better, then you can simply wrap this part with #ifdef LPC1114.

BTW., this simple strategy would work on the LPC1114, too, but the User Manual says using a divider and multiplier makes baud rate more precise. As the code was already there I kept it and made kind of an easter egg from it.

Wurstnase commented 9 years ago

Yes, right. Just read something about this. It could be also possible with sam on the PLLA (maybe I'm wrong?). But this is maybe for later.

Thanks for your feedback.

Also I found a great tutorial about the UART on the Due. http://codetron.net/uart-interface-sam3x8e-arduino-due/

triffid commented 9 years ago

Not sure how similar the serial blocks are, but https://github.com/mbedmicro/mbed/pull/116 may give you some ideas for UART PLL setup :)

Definitely applies to the lpc1117 target!

Wurstnase commented 9 years ago

@triffid Thx for that link. Traumflug already use it :) https://github.com/Traumflug/Teacup_Firmware/blob/experimental/serial-arm.c#L108

Wurstnase commented 9 years ago

Should I see any echo on a serial connection?

Traumflug commented 9 years ago

Right after releasing the reset button you should see

start
ok

Characters sent aren't echoed normally, but you can put arbitrary code into main.c, before the endless loop for testing. Just echoing incoming characters would be something like this:

for (;;) {
  if (serial_rxchars() != 0) {
    serial_writechar(serial_popchar());
  }
}
Wurstnase commented 9 years ago

If the size doesn't increase, the additional stuff isn't used and removed at the link stage. You have to actually use the code you add.

I think this is my actual problem. But at this moment I have no clue to solve or to see where my new code is not linked in.

triffid commented 9 years ago

The linker builds a set of "used references" starting from the entry point (usually _start which eventually calls main) and other root references such as interrupts.

If your functions are never called from main, or from something that main calls, etc, then the linker will discard them as unreachable code.

Wurstnase commented 9 years ago

Ok, but my code should be called. In mendel.c the serial_init() is called. This goes from serial.c to serial-arm.c.

I think there is something missing to initialize the due or whatelse :/

On any other Makefile for the Due I see that they use the libchip_sam3x8_gcc_rel.a. Do I need this also?

Traumflug commented 9 years ago

serial_init() isn't much, it just sets a few registers. Perhaps 100 bytes binary, much less than what the LPC1114 had. LPC1114 intitially had extensive baud rate calculations there, which the SAM apparently doesn't need.

I'd try to ignore binary size and concentrate on what works and what not. If getting serial working at all requires additional code or interrupts, go for it. Once you can see what's happening it's much easier to refine it. This looks like a good code sample: http://codetron.net/uart-interface-sam3x8e-arduino-due/

Wurstnase commented 9 years ago

Well, it wont grow up. With or without serial_init() it's 124 bytes.

  CC        build/mendel.o
  CC        build/cpu.o
  CC        build/serial.o
  CC        build/sermsg.o
  CC        build/sersendf.o
  CC        build/cmsis-system_sam3xa.o
  LINK      build/teacup.elf
  OBJCOPY   teacup.hex
  OBJDUMP   build/teacup.lst
  SYM       build/teacup.sym
    SIZES          ARM...     sam3x8e
    FLASH  :   124 bytes           1%
    RAM    :    28 bytes           1%
    EEPROM :     0 bytes           0%
Traumflug commented 9 years ago

arm-startup_due.s isn't compiled. This is where the reset handler is called, which in turn calls SystemInit() and _start(). _start() inititalises a few things, then jumps to main(). My take is, your binary doesn't even call main(), so pretty much everything is optimised out.

To see which functions end up in the binary, you can use something like this:

nm -Cg path/to/teacup.elf
Wurstnase commented 9 years ago

Small progress. Got the compiler stuff I think. I use now a modified LPC.ld. Size increase now with more functions inside.

But I can't read or write anything over UART (programming port).

Today I give up.

The nm helps alot!

Wurstnase commented 9 years ago

It's still something with the start part.

After some hours I've got this to run. I've made also a simple main.c with the serial stuff. The good news, this works pretty good.

The bad news, it's heavy bloated, has it's own Makefile. It uses the libsam_sam3x8e_gcc_rel.a with all arduino libraries.

Actually I have no idea how the startup.c or .s will work.

Wurstnase commented 9 years ago

I want to implement the 'low level' from atwillys. But I'm not very good in makefiles. Where I should put that part in the makefile? Especially the libsam-part?

My intend of this is to take that part of code and reduce it step by step.

LIBSAM_ARCHIVE=$(ROOT)/lib/libsam_sam3x8e_gcc_rel.a

#  -> .elf
$(OBJ_DIR)/$(OUTPUT_NAME).elf: $(OBJ_DIR)/$(OUTPUT_NAME).a
    "$(LKELF)" -Os -Wl,--gc-sections -mcpu=cortex-m3 \
      "-T$(LNK_SCRIPT)" "-Wl,-Map,$(OBJ_DIR)/$(OUTPUT_NAME).map" \
      -o $@ \
      "-L$(OBJ_DIR)" \
      -lm -lgcc -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections \
      -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common \
      -Wl,--warn-section-align -Wl,--warn-unresolved-symbols \
      -Wl,--start-group \
      $^ $(LIBSAM_ARCHIVE) \
      -Wl,--end-group
Traumflug commented 9 years ago

Put it wherever you want, Makefiles don't rely on a specific order.

Wurstnase commented 9 years ago

Ok, do I need to copy the complete part for the libsam? Or do I need to just copy

-Wl,--start-group \
      $^ $(LIBSAM_ARCHIVE) \
      -Wl,--end-group

anywhere?

Traumflug commented 9 years ago

A few Makefile basics:

Wurstnase commented 9 years ago

Thanks again :+1:

I'm getting closer. There are some predefined linker scripts. Which one do I need? gcc or gcc-arm?

Traumflug commented 9 years ago

Which ones work? There are several flavours of gcc for ARM. The one coming with Ubuntu is arm-none-eabi-gcc, so neither "gcc" nor "gcc-arm".

Traumflug commented 9 years ago

From cmsis-core_cm0.h:

#if   defined ( __CC_ARM )
  #define __ASM            __asm                                      /*!< asm keyword for ARM Compiler          */
  #define __INLINE         __inline                                   /*!< inline keyword for ARM Compiler       */
  #define __STATIC_INLINE  static __inline

#elif defined ( __ICCARM__ )
  #define __ASM            __asm                                      /*!< asm keyword for IAR Compiler          */
  #define __INLINE         inline                                     /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */
  #define __STATIC_INLINE  static inline

#elif defined ( __GNUC__ )
  #define __ASM            __asm                                      /*!< asm keyword for GNU Compiler          */
  #define __INLINE         inline                                     /*!< inline keyword for GNU Compiler       */
  #define __STATIC_INLINE  static inline

#elif defined ( __TASKING__ )
  #define __ASM            __asm                                      /*!< asm keyword for TASKING Compiler      */
  #define __INLINE         inline                                     /*!< inline keyword for TASKING Compiler   */
  #define __STATIC_INLINE  static inline

#endif

As one can see: the differences are small, but existing. Perhaps these two linker scripts are almost identical anyways. I'd compare and try both. When different and both working, a die (Würfel) enters the game :-)

Wurstnase commented 9 years ago

Ok. What i found out, that some weak handlers are defined in one and in the other someone removed them and put them in the mainfile, because with the arduino (whatever that exactly means) needs this.

I think that this could be my issue at the moment. Actually when I compile my test-code the linking goes up to the main. But not init() is calling.

Traumflug commented 9 years ago

IIRC, weak references are similar to "if this function happens to exist, put it here". If the function doesn't exist, it isn't linked in. Accordingly, weak references don't really matter, unless you want to use them.

What makes you think that init() should be called?

Wurstnase commented 9 years ago

What makes you think that init() should be called?

Because the serial_init() works on a pure Arduino sketch. https://github.com/arduino/Arduino/commit/65f00a69c74ca0def8d7f1d7597327945a04a33f

Wurstnase commented 9 years ago

Hi,

today I cut anything out I don't need from the libsam archive (atwillys low level version).

Start:

adc12_sam3u.o
adc.o
can.o
dacc.o
efc.o
emac.o
gpbr.o
interrupt_sam_nvic.o
pio.o
pmc.o
pwmc.o
rstc.o
rtc.o
rtt.o
spi.o
ssc.o
tc.o
timetick.o
trng.o
twi.o
udp.o
udphs.o
uotghs.o
uotghs_device.o
uotghs_host.o
usart.o
wdt.o
system_sam3xa.o
startup_sam3xa.o

ends with:

timetick.o
wdt.o
system_sam3xa.o
startup_sam3xa.o
Wurstnase commented 9 years ago

@Traumflug where do you initialize the systick_handler? When I make the systick_handler weak on my version at home, the sam doesn't start.

(I don't know exactly what is going wrong, but the UART-thing won't work)

Actually I need the timetick, and I want to delete this also. https://github.com/arduino/Arduino/blob/master/hardware/arduino/sam/system/libsam/source/timetick.c

Traumflug commented 9 years ago

where do you initialize the systick_handler?

In timer_init() in timer-arm.c: https://github.com/Traumflug/Teacup_Firmware/blob/master/timer-arm.c#L31

Wurstnase commented 9 years ago

Ah, ok. That will come some steps later. Thanks.

Wurstnase commented 9 years ago

I'm hanging around this step: https://github.com/Traumflug/Teacup_Firmware/tree/32480bdfb62f95677db20f38c55db8cf6b4c30bc

Here it looks like you don't use any systick_handler. Could this be elementary for sam3x?

Traumflug commented 9 years ago

System clock != SysTickTimer. System clock is the clock which makes the CPU running. Often a quartz oscillator. On top of the system clock there's a PLL which makes a 48 MHz clock from a 12 MHz one, depending on some register settings.

SysTickTimer is a timer, a peripheral, something outside the CPU, still on chip. Very similar to the other timers.

Wurstnase commented 9 years ago

I've found that missing key. :)

and finally:

system_sam3xa.o
startup_sam3xa.o
Wurstnase commented 9 years ago

How can I print an uint8_t with serial_write_char()?

Wurstnase commented 9 years ago

I answer myself: SOURCES += sermsg.c and serwrite_hex32().

Traumflug commented 9 years ago

Uhm, serial_writechar() is one of the functions you have to implement for a port. Examples are in serial-avr.c and serial-arm.c.

Wurstnase commented 9 years ago

serial_writechar() was not my issue. Sending an uint_8 or something like that won't help me, because it was translated to a char. serwrite_hex does that job perfect for me to read some registers.

triffid commented 9 years ago

hex32 is for uint32s and pointers, not uint8s

Traumflug commented 9 years ago

Sending an uint_8 or something like that won't help me, because it was translated to a char.

Now I got it, you want a hex string of this character. In this case it's no problem add sersendf.c, too, it's platform independent and should compile flawlessly. sersendf() works similar to printf(), with formatting string and such stuff. uint8_t would be "%su", then. Formatting is described in the source code file.