A13 Real Time Linux

Started by uMinded, February 11, 2013, 06:29:56 PM

Previous topic - Next topic

uMinded

What is the status of the real time elements in the A1x linux kernel?

The A13-OLINUXINO-MICRO really suits my bill for price, preformance, size and IO but I will need to toggle 10 of the unused GPIO (Pins 4-9 & 29-37) I need however the ability to output a very accurate 31kHz pulse train and the host software needs the ability to modify this train no slower than 15us. This train is also not a simple Hi/Lo every n'th cycle so real time functionality is a make or break I think.

Anybody dealt with this in the A1x kernels? Or even where I would look in the kernel sources for the possibility of adding it in?

Thanks!

crubille

Hi

Look here:

https://www.olimex.com/forum/index.php?topic=812.0

but we just start it. Nothing to use at this time and even nothing to test.


uMinded

Oh nice lol, I had the idea to ask a few weeks ago but just got around to it. Glad to know I'm not the only one looking for real time support.

I will take a look at sources this weekend and see if I can lend a hand.

What are your plans for a RT-A13 system if you don't mind me asking?

ehj666

Personally I am trying to see if I can port Linuxcnc http://www.linuxcnc.org/ to the Olinuxino. There has been some success getting it on the Beaglebone and RPi, but the Olinuxino suits my needs better.

You might be interested in looking at the HAL layer of lcnc http://www.linuxcnc.org/docs/HAL_User_Manual.pdf

It has native components such as freqgen, stepgen and pwmgen for generating various types of wave forms. However even on faster multi-core processors (SMP) where the realtime can be dedicated to a single core, the fastest thread typically runs in the 25usec range. It will be interesting to see what kind of latency numbers one can get out of an Olinuxino.

I am not really expecting to be able to generate soft stepper signals, so likely will have to go through something like an FPGA.

uMinded

Ha, we are both working on nearly the same project. I was looking at seeing what I could get out of the HAL layer before I worked on re-inventing the wheel.

If I could not get >30kHz with 95% reliably on a kernel level RT then I was considering a high speed SPI bus to an STM32F1 to offload the pulse generation.

HERE is a good write up on comparing real time instances on a similar platform.
HERE is a good walkthrough on getting Xenomai compiled from start to finish.

ehj666

Per the terminology of the first link, lcnc requires 100% hard realtime, which is why it must run on an RTOS such as RTAI or Xenomai. There is a simulator version that will run without an RTOS, but you cannot control real hardware with it.

As for the second, been there, done that. The problem is the Olinuxino kernel is significantly modified from a vanilla kernel. I just found that out the hard way, thinking I was just going back to an older version of the kernel and not realizing it was a vanilla kernel.

I can patch a vanilla kernel with no problem, it is the Olinuxino modifed kernel that presents more of a challenge.


uMinded

Just took a look at your blog, it sounds like you have 4 of these A13-MINI after getting the faulty SD reader sussed out. Any way you would sell a defective one? I can contribute on weekends but won't be able to purchase a board myself until Mar 1st.

ehj666

Quote from: uMinded on February 11, 2013, 11:24:11 PM
Just took a look at your blog, it sounds like you have 4 of these A13-MINI after getting the faulty SD reader sussed out. Any way you would sell a defective one? I can contribute on weekends but won't be able to purchase a board myself until Mar 1st.

Two actually, the other two go back to Olimex. I have found I need two because if I screw up a kernel or SD the board won't boot anything for a while, so I swap boards and work on the other one until I do the same thing to it. I have speculated that there is some residual potential persisting for a short time, and that maybe shorting the cap behind the PWR connector might do the trick. But I have not tried yet.

crubille

So if i get a running system with a patched kernel with ipipe and xenomai opions on,
there is still things to do and xenomai test dont work.

If you think i dont share infos:
- i cant do anything up to Saturday, then writing the forst code and take a lot of time to compile on the A13.
- the first kernel dont run monday morning and the A13 hang,
the second one, this morning allow linux to start and behave as usual but i dont have time tu compile and test xenomai.
- this evening, i check xenomai test and everything using timers dont run.

I use a 3.4.24 sunxi kernel. The patch from xenomai fails only in very few place and the correction are obvious:

I use as i suggest in a post here, TIMER2 for the clock and TIMER4 for the xenomai timer.

Only three files need to be modified:
- arch/arm/Kconfig:

diff  -p arch/arm/Kconfig.ori arch/arm/Kconfig
*** arch/arm/Kconfig.ori        2013-02-19 23:58:25.000000000 +0100
--- arch/arm/Kconfig    2013-02-17 23:15:27.000000000 +0100
*************** config ARCH_SUN5I
*** 744,749 ****
--- 744,750 ----
          select ARCH_HAS_CPUFREQ
          select ARM_L1_CACHE_SHIFT_6
        select NEED_MACH_IO_H
+       select IPIPE_ARM_KUSER_TSC if IPIPE
          help
            This enables support for Allwinner Technology Co., Ltd A12/A13 SoC based systems


- arch/arm/plat-sunxi/include/plat/platform.h (in order to add adress for the timer2 and timers4 registers)

*** arch/arm/plat-sunxi/include/plat/platform.h.ori     2013-02-20 00:03:36.000000000 +0100
--- arch/arm/plat-sunxi/include/plat/platform.h 2013-02-17 13:28:31.000000000 +0100
***************
*** 196,205 ****
--- 196,216 ----
 
  #define SW_TIMER_INT_CTL_REG              (SW_VA_TIMERC_IO_BASE + 0x00)
  #define SW_TIMER_INT_STA_REG              (SW_VA_TIMERC_IO_BASE + 0x04)
+
  #define SW_TIMER0_CTL_REG                 (SW_VA_TIMERC_IO_BASE + 0x10)
  #define SW_TIMER0_INTVAL_REG              (SW_VA_TIMERC_IO_BASE + 0x14)
  #define SW_TIMER0_CNTVAL_REG              (SW_VA_TIMERC_IO_BASE + 0x18)
 
+ #ifdef CONFIG_IPIPE
+ #define SW_TIMER2_CTL_REG                 (SW_VA_TIMERC_IO_BASE + 0x30)
+ #define SW_TIMER2_INTVAL_REG              (SW_VA_TIMERC_IO_BASE + 0x34)
+ #define SW_TIMER2_CNTVAL_REG              (SW_VA_TIMERC_IO_BASE + 0x38)
+
+ #define SW_TIMER4_CTL_REG                 (SW_VA_TIMERC_IO_BASE + 0x50)
+ #define SW_TIMER4_INTVAL_REG              (SW_VA_TIMERC_IO_BASE + 0x54)
+ #define SW_TIMER4_CNTVAL_REG              (SW_VA_TIMERC_IO_BASE + 0x58)
+ #endif /* CONFIG_IPIPE */
+
 
  /**
   * Interrupt controller registers




- the main part in arch/arm/mach-sun5i/core.c
*** arch/arm/mach-sun5i/core.c.ori 2013-02-20 00:07:57.000000000 +0100
--- arch/arm/mach-sun5i/core.c 2013-02-19 01:35:44.000000000 +0100
***************
*** 64,69 ****
--- 64,74 ----
  #include <plat/core.h>
  #include <plat/sys_config.h>
 
+ #include <linux/ipipe.h>
+ #include <linux/ipipe_tickdev.h>
+
+
+
  /**
   * Machine Implementations
   *
*************** static int timer_set_next_event(unsigned
*** 317,322 ****
--- 322,472 ----
  return 0;
  }
 
+
+ #ifdef CONFIG_IPIPE
+
+ /* ***********************************************
+    The IPIPE TIMER
+    *********************************************** */
+ /*
+  * Reprogram the timer
+  */
+ static int sun5i_set_timer4(unsigned long evt, void *timer)
+ {
+   volatile u32  val = 0;
+
+   val = readl(SW_TIMER4_CTL_REG);
+   if( (val & 0x1 ) == 1 )
+     {
+       /* running */
+       val &= ~(0x1);  /* stop the timer */
+       writel(val, SW_TIMER4_CTL_REG);
+       // sleep for 2 T_cycle that is 2 ticks
+       __delay(50); // wait for hardware synchronisation
+       if( evt > 1 ) evt --;
+     }
+   /* set the timer */
+   writel( evt, SW_TIMER4_CNTVAL_REG);
+   /* start the timer */
+   val |=  0x2; // reload!
+   writel(val, SW_TIMER4_CTL_REG);
+   val |=  0x1 ; // start
+   writel(val, SW_TIMER4_CTL_REG);
+   return 0;
+ }
+
+ /*
+  * IRQ handler for the timer.
+  */
+ static void sun5i_ack_timer4(void)
+ {
+ writel(0x1<<4, SW_TIMER_INT_STA_REG);
+ }
+
+ static void sun5i_request_timer4(struct ipipe_timer *timer, int steal)
+ {
+ /* Set timer on  - Enable interrupt. */
+ volatile u32  val = 0;
+
+ val = readl(SW_TIMER4_CTL_REG);
+ val &= ~(0x07<<4);
+ val &= ~(0x03<<2);
+ val |=  (1<<2) ; // 24 MHz
+ val &= ~(1<<1);
+ val |= 1<<7; // single mode -- stop continuous mode
+ writel(val, SW_TIMER4_CTL_REG);
+ __delay(50);
+ sun5i_ack_timer4();
+ /* Enable timer4 interrupt */
+ val = readl(SW_TIMER_INT_CTL_REG);
+ val |= (1<<4);
+ writel(val, SW_TIMER_INT_CTL_REG);
+ }
+
+ static void sun5i_release_timer4(struct ipipe_timer *timer)
+ {
+ volatile u32  val = 0;
+ /* Disable interrupt. */
+ val = readl(SW_TIMER_INT_CTL_REG);
+ val &= ~(1<<4);
+ writel(val, SW_TIMER_INT_CTL_REG);
+
+ }
+
+ static struct ipipe_timer sun5i_itimer = {
+   .irq = SW_INT_IRQNO_TIMER4 ,
+   .request = sun5i_request_timer4,
+   .set = sun5i_set_timer4,
+   .ack = sun5i_ack_timer4,
+   .release = sun5i_release_timer4,
+   .name = "sun5i_timer4",
+   .rating =  240 ,
+   .freq = 24000000 ,
+   .min_delay_ticks = 20 ,
+   //  .cpumask = ,
+ };
+
+ /* ***********************************************
+    The IPIPE CLOCK
+    *********************************************** */
+
+ static struct __ipipe_tscinfo tsc_info = {
+   .type = IPIPE_TSC_TYPE_FREERUNNING_COUNTDOWN ,
+   .freq = 24000000 , // ATTENTION should go to PPL6/6
+   .counter_vaddr = SW_TIMER2_CNTVAL_REG ,
+   .u = {
+     {
+       .counter_paddr = SW_TIMER2_CNTVAL_REG - SW_VA_TIMERC_IO_BASE + SW_PA_TIMERC_IO_BASE,
+       .mask = 0xffffffff,
+     },
+   },
+ };
+
+ void  sun5i_ipipe_tsc_timer2_init( void )
+ {
+ /* Set timer on  */
+ volatile u32  val = 0;
+
+ /* set clock source to HOSC (24Mhz) LOOK for PLL6/6 for future */
+ val = readl(SW_TIMER2_CTL_REG);
+ val &= ~(0x1); // stop timer
+ val &= ~(0x07<<4); // clear divisor
+ val &= ~(0x03<<2); // clear source
+
+ val &= ~(0x1<<7); // continous mode
+ val |=  (1<<2) ;  // 24 Mhz
+ writel(val, SW_TIMER2_CTL_REG);
+
+ /* set value */
+ writel( tsc_info.u.mask, SW_TIMER2_INTVAL_REG);
+ /* start and auto reload */
+ __delay(50); // wait for hardware synchronisation
+ val = readl(SW_TIMER2_CTL_REG);
+ val |= 1 | (1<<1);
+ writel(val, SW_TIMER2_CTL_REG);
+
+ }
+
+ /* ***********************************************
+    The IPIPE IRQ
+    *********************************************** */
+
+ /* ***********************************************
+    Register IPIPE
+    *********************************************** */
+
+
+
+ void sun5i_ipipe_init( void )
+ {
+   ipipe_timer_register(&sun5i_itimer); // register timer
+
+   //  tsc_info.freq = clock_tick_rate;// from the example dont care
+   sun5i_ipipe_tsc_timer2_init();
+   __ipipe_tsc_register(&tsc_info); // register clock
+ }
+ #endif /* CONFIG_IPIPE */
+
  static struct clock_event_device timer0_clockevent = {
  .name = "timer0",
  .shift = 32,
*************** static irqreturn_t sw_timer_interrupt(in
*** 331,336 ****
--- 481,490 ----
  {
  struct clock_event_device *evt = (struct clock_event_device *)dev_id;
 
+ #ifdef CONFIG_IPIPE
+  __ipipe_tsc_update();
+ #endif /* CONFIG_IPIPE */
+
  writel(0x1, SW_TIMER_INT_STA_REG);
 
  /*
*************** static void __init sw_timer_init(void)
*** 383,388 ****
--- 537,546 ----
  timer0_clockevent.cpumask = cpumask_of(0);
  timer0_clockevent.irq = sw_timer_irq.irq;
  clockevents_register_device(&timer0_clockevent);
+
+ #ifdef CONFIG_IPIPE
+ sun5i_ipipe_init();
+ #endif
  }
 
  struct sys_timer sw_sys_timer = {