Saturday, January 30, 2016

Updated MQX 4.1.0 with rpmsg

Open-Amp and MQX 4.1.0

The latest release from NXP is a port of the FreeRTOS operating system to the imx6sx SOC and imx7 SOC.  This release has rpmsg/open-amp along with drives and examples.  Also, here is a good overview of open-amp located here.  MQX uses MCC; until now, spend a few days last week porting rpmsg delivered by NXP to MQX 4.1.0.  FreeRTOS example is portable to MQX.    The main difference in porting open-amp to MQX, is the ISR only posts the channel message to a queue instead of calling open-amp stack and processing the message in the ISR. There are still some open issues; but it is working.

At this time I don't know if it is possible to release the code due to all of the licensing issues; still need to see if it is possible;





Saturday, January 23, 2016

How to reload the same firmware on the Neo M4 without a reboot

Reloading M4 without rebooting on the UDOO Neo


Most of the time I have to modify and reload firmware on the M4 many times during a debug session, i.e adding debug code or features etc, and each time when reloading firmware the Neo must reboot each time.  This gets old; plus all of the time wasted.

So, what is causes the firmware not to be reloaded? Well that is simple, it is the memory protection setup on the RDC, one simple way to to reload firmware to turn off the RDC protection, another way, just add a compile option and not enable RDC memory protection.

For MQX, it is in the init_hardware.c file, function void rdc_init_memory(void) so, I added a simple #ifdef and allows the code to be reloaded.

void RDC_memory_init(void)
{
    uint32_t start, end;
#if defined(__CC_ARM)
    extern uint32_t Image$$VECTOR_ROM$$Base[];
    extern uint32_t Image$$ER_m_text$$Limit[];
    extern uint32_t Image$$RW_m_data$$Base[];
    extern uint32_t Image$$RW_m_data$$Limit[];

    start = (uint32_t)Image$$VECTOR_ROM$$Base & 0xFFFFF000;
    end = (uint32_t)(Image$$ER_m_text$$Limit + (Image$$RW_m_data$$Limit - Image$$RW_m_data$$Base));
    end = (end + 0xFFF) & 0xFFFFF000;
#else
    extern uint32_t __FLASH_START[];
    extern uint32_t __FLASH_END[];

    start = (uint32_t)__FLASH_START & 0xFFFFF000;
    end   = ((uint32_t)__FLASH_END + 0xFFF) & 0xFFFFF000;
#endif
#if _DEBUG_
#pragma message "Turned off memory protection"
#else
    RDC_SetMrAccess(RDC, rdcMrMmdc, start, end, (3 << (BOARD_DOMAIN_ID * 2)), true, false);    
#endif
}


When compiling just define _DEBUG_ and  load the app.


Sunday, January 17, 2016

Udoo Neo M4 memory layout and performance

The M4 has several non-contiguous memory blocks for code and data

  • TCM (tightly coupled memory)
    • TCMU
      • This is 32K SRAM for Code
    • TCML
      • This is 32K SRAM for Data
  • OCRAM
    • (Need to look into how much can be access TBD)
  • DDR
    • Linux sets aside 8MB 
      • A9 and M4 must share this RAM 
    • The last 1MB is used for MCC/RPMSG
  • SPI Flash

The application baseflight port will be more then 32K of text (code)  So, the code must be in one memory or split.  

Using a split method requires some up front work with the linker file and the loader.  It is possible to create a linker file putting some code in TCMU and DDR.  The idea being RTOS, interrupt; code which has a high bandwidth rate.  Less bandwidth code would go into DDR, or even OCRAM or SPI flash.

Here is an example of a loader file:


/* Specify the memory areas */
MEMORY
{
  m_interrupts          (RX)  : ORIGIN = 0x9ff00000, LENGTH = 0x00008000
  m_text                (RX)  : ORIGIN = 0x84000000, LENGTH = 0x00040000
  m_data                (RW)  : ORIGIN = 0x84040000, LENGTH = 0x00028000
}

__FLASH_START = ORIGIN(m_interrupts);
__FLASH_END   = ORIGIN(m_text) + LENGTH(m_text);

/* Define output sections */
SECTIONS
{
  /* The startup code goes first into Flash */
  .interrupts :
  {
    __VECTOR_TABLE = .;
    . = ALIGN(4);
    KEEP(*(.isr_vector))     /* Startup code */
    . = ALIGN(4);
    *croutine.c.obj (.text .text*)
    *event_groups.c.obj (.text .text*)
    *list.c.obj (.text .text*)
    *queue.c.obj (.text .text*)
    *tasks.c.obj (.text .text*)
    *timers.c.obj (.text .text*)
    *port.c.obj (.text .text*)
    *startup_MCIMX6X_M4.S.obj (.text .text*)
    *system_MCIMX6X_M4.c.obj (.text .text*)
    *uart_imx.c.obj (.text .text*)
    *mu_imx.c.obj (.text .text*)
    *heap_2.c.obj (.text .text*)
    *rpmsg_rtos.c.obj (.text .text*)
    *platform.c.obj (.text .text*)
    *hil.c.obj (.text .text*)
    *sh_mem.c.obj (.text .text*)
    *remote_device.c.obj (.text .text*)
    *rpmsg.c.obj (.text .text*)
    *rpmsg_ext.c.obj (.text .text*)
    *rpmsg_core.c.obj (.text .text*)
    *rpmsg_porting.c.obj (.text .text*)
    
    *baseflight.c.obj (.text .text*)
    
  } > m_interrupts

  /* The program code and other data goes into Flash */
  .text :
  {
    . = ALIGN(4);
    *(.text)                 /* .text sections (code) */
    *(.text*)                /* .text* sections (code) */
    *(.rodata)               /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)              /* .rodata* sections (constants, strings, etc.) */
    *(.glue_7)               /* glue arm to thumb code */
    *(.glue_7t)              /* glue thumb to arm code */
    *(.eh_frame)
    KEEP (*(.init))
    KEEP (*(.fini))
    . = ALIGN(4);
  } > m_text


The m_interrupt section, need to call out the file name with the text, this will move the code into the section.  So, for slow code put in DDR. 

When creating a binary file, the code is it two separate sections:

  m_interrupts          (RX)  : ORIGIN = 0x9ff00000, LENGTH = 0x00008000
  m_text                (RX)  : ORIGIN = 0x84000000, LENGTH = 0x00040000

so, the binary will span from 0x84000000 to 0x9FF08000.  So, to fix this, must use a Intel Hex file. The Intel hex file will only create records for the sections that need to be fixed with data.

So, a M4 loader now must able to load intel hex files into memory.

Also, the next pass will move .data sections into SRAM along with the stack. 





Sunday, January 3, 2016

UDOO Neo and openocd working

With the help of IIRC openocd able to create a configuration file for openocd to connect to the M4 DAP and able to halt and reset just the M4 processor.

I'm using an old Amontec JTAGkey (using FTDI 232 chip) here is a photos of the wiring.


Using the imx6sx.cfg file and the following command to connect to M4.

sudo  ./src/openocd -f interface/ftdi/jtagkey.cfg -f ./tcl/target/imx6sx.cfg

Output:

Open On-Chip Debugger 0.10.0-dev-00023-g8590315 (2016-01-02-19:34)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
adapter speed: 1000 kHz
Warn : imx6sx.sdma: nonstandard IR value
Warn : imx6sx.sjc: nonstandard IR value
imx6sx.dapM4
Info : clock speed 1000 kHz
Info : JTAG tap: imx6sx.dapM4 tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4)
Info : JTAG tap: imx6sx.dapA9 tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4)
Info : TAP imx6sx.sdma does not have IDCODE
Info : JTAG tap: imx6sx.sjc tap/device found: 0x0891c01d (mfg: 0x00e, part: 0x891c, ver: 0x0)
Info : imx6sx.dapM4: hardware has 6 breakpoints, 4 watchpoints


Then start a gdb session:
./arm-none-eabi-gdb

(gdb) tar ext :3333   <---- connects to openocd
Remote debugging using :3333
(gdb)  monitor  halt    <------------- halts only M4
(gdb)  monitor  reset   <------------ reset M4 (resets the M4 and restarts from 0x0)

Next will rebuild and see if M4 will single step...



Wednesday, December 23, 2015

Ethernet driver for mcc/rpmsg status

mccethernet is loading on Ubuntu 15.10.:

ifconfig
eth0      Link encap:Ethernet  HWaddr 00:21:70:5a:c0:7d  
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:722 errors:0 dropped:0 overruns:0 frame:0
          TX packets:722 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:88472 (88.4 KB)  TX bytes:88472 (88.4 KB)

mcc0      Link encap:Ethernet  HWaddr 00:00:00:00:00:01  
          UP BROADCAST RUNNING MULTICAST  MTU:1024  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

wlan0     Link encap:Ethernet  HWaddr 00:c0:a8:c3:56:15  
          inet addr:192.168.9.6  Bcast:192.168.9.255  Mask:255.255.255.0
          inet6 addr: fe80::2c0:a8ff:fec3:5615/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8630 errors:0 dropped:0 overruns:0 frame:0
          TX packets:7939 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:4416731 (4.4 MB)  TX bytes:1681525 (1.6 MB)

Background on driver development for mccethernet, mccmulti, and mcctty:

I start by creating a basic driver that can be loaded by my desktop PC, which is running 15.10 Ubuntu.  This way I can load/debug/ etc faster then recompiling the kernel or loading the Udoo Neo.  Also, the Ubuntu is using 4.x and neo is using a 3.4.x and there are some API differences that have to be accounted for in the mccethernet.c 

For example: in 3.4.x alloc_netdev only needs 3 arguments where alloc_netdev in 4.x needs 4 arguments. 
Linux kernel has some macros to help you out:
    
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)        
mccethernet_dev = alloc_netdev(sizeof(struct mcc_ethernet_device), "mcc0", mcc_setup);
#else
mccethernet_dev = alloc_netdev(sizeof(struct mcc_ethernet_device), "mcc0", NET_NAME_UNKNOWN, mcc_setup);

#endif 

mccmulti driver was a little harder to since it is using MCC api calls; the first pass was to stub MCC calls.


For the mccethernet it is using basic file operations on m4mcc1 on the mccmulti user interface.  Code can be found here.


Monday, December 21, 2015

Progress on TCP/IP via MCC

Have chosen LwIP as the TCP/IP stack for the M4.  LwIP runs on a number of different processors and it has a rich application stack; HTTP, Auto IP, IPv6, etc.

Created a MQX interface following the porting guide and several examples.  Created a loop back interface and able to create sockets.  Working on a modification to the lwIP select function to allow passing a semaphore and a condition variable.    This allows one thread to wait on TCP/UDP sockets and messages from a queue using the passed in semaphore; this helps with multithreading issues with LwIP.

Tasks pending:
- MCC driver for lwIP; it's a simple thread pending on read MCC function; write will be passed via the TCP/IP thread.  MTU size is 1024; based on the MCC buffer size.  Here is the code:
- Ethernet/MCC driver for Linux: pass packets from MCC to Linux TCP/IP stack
- TTY MCC interface.


The reason for doing this:

  1. Applications on the M4 side need a configuration file or script for startup:
    1. Can use TFTP
      1. Setup a TFTP server on the Linux side, M4 can get or write files
    2. Syslog
      1. Create a syslog forwarder on M4, now message so up in Linux log
    3. One MCC port can transport several different messages to several different apps without any forwarder or mux.  TCP/IP uses port numbers for each app. 

Here is a link to a basic drawing of MCC TCP/IP:


Saturday, December 12, 2015

Udoo.org Neo progress Imx6sx Solo

Completed a Linux kernel driver mccmulti; this driver provides 3 dev interfaces:

  • /dev/m4mcc0
    • This has two mcc ports (1) A9 and (1) M4 The goal is to create another driver a ttyMCC0 to allow terminal apps, minicom, screen etc access to the MCC interface.  i.e command line interface etc.
  • /dev/m4mcc1
    • This is the same as above, but on different mcc ports. 
  • /dev/m4mcc2
    • This is the same as above, but on different mcc ports. 
    • The goal is to create another driver, a ethernet HW driver to use the MCC interface for IP.  The M4 needs a IP stack, might use lwIP, Current NXP does not provide the IP stack for the IMX6sx.
Also, created a user space app using epoll to test the mccmulti kernel driver.  This way, it is possible to create an app, with non-blocking IO and use async-io type of programming.

Once the IP stack is working, then will start porting Baseflight.  Just got my NTSC camera, so need to test that also.