uC/OS-II

Port of the uC/OS-II Realtime kernel to the cybiko. The trial and tribulations of porting this famous RTOS from http://www.micrium.com/ to the cybiko.

The book, which comes with a CD containing the base uC/OS-II source, can be bought from micrium. I cannot re-distribute the uC/OS-II RTOS source, for reference I was using version kernel v2.76. My H8 cybiko port should be upwardly compatible thou.

You may find uC/OS-II on a P2P network if you look.

One of the issues that needs to be resolved is how to generate a software context switch. This would normally be done using the following H8 instruction.

  TRAPA

Dissassembling the ROM for the locations of the where TRAPA vectors would be loaded reveal everything is set to 0.

;Trap instruction Vector 8, 9, 10, 11
;TRAPA  #xx:2   Instruction has 2 bit vector
 
;Vector 8:
      20: 00 00  ; trapa #0 
;Vector 9:
      24: 00 00  ; trapa #1
;Vector 10:
      28: 00 00  ; trapa #2
;Vector 11:
      2C: 00 00  ; trapa #3   

As this instruction uses a vector that is loaded into ROM that cannot be changed an alternative way needs to be found. Thanks to some tips by the developer of the CyBorn Project there is a workaround.

The IRQ6 port of the H8 processor is not connect to anything. It is possible to toggle this port in software using a BNOT instruction and trigger an interrupt (IRQ6) that can be intercepted and re-vectored via memory. The trigger will need to be on both the raising and falling edges of the bit, that is; 0→1 and 1→0

void OSInitHookBegin (void)
{
  asm("orc #0xc0,ccr");       // Mask interrupts
  asm("bset #4, @0x2c:8");    // ISCRH - Trigger on both edges.
  asm("bset #5, @0x2c:8");
                              // IER - Enable IRQ6
  asm("bset #6, @0x2e:8");
                              // ISR - Clear interrupt status
  asm("mov.b @0x2f:8, r0l");  // Read
  asm("and.b #0xbf, r0l");    // Clear IRQ6 bit
  asm("mov.b r0l, @0x2f:8");  // Write
 
  asm("mov.b #0x1f,r0l");      // Enable PGDDR pin 0
  asm("mov.b r0l,@0xfebf:16");
  // IRQ6 can now be software generated via - BNOT #0,@0X6F:8
  // This will be used for the OS_TASK_SW() context switch.
}

When IRQ6 is fired it will load the jump vector contained at this location in the ROM.

;IRQ6 vector 22 					- 00 00 11 c8
      58:	00 00       	00 00             nop
      5a:	11 c8       	11 c8             shar	#0x2,r0l

This will result in the following ROM instructions being executed. As you can see the memory address 0xec2c is a RAM location that contains a vector that IRQ6 will be re-directed to. At this location we will need to load our ContextSwitch vector.

IRQ6:
    11c8:	01 30 6d f0 	01 30 6d f0       stm.l	er0-er3,@-sp
    11cc:	01 20 6d f4 	01 20 6d f4       stm.l	er4-er6,@-sp
    11d0:	01 00 6b 02 	01 00 6b 02 ec 2c mov.l	@0xec2c:16,er2
    11d4:	ec 2c
    11d6:	5d 20       	5d 20             jsr	@er2
    11d8:	01 20 6d 76 	01 20 6d 76       ldm.l	@sp+,er4-er6
    11dc:	01 30 6d 73 	01 30 6d 73       ldm.l	@sp+,er0-er3
    11e0:	56 70       	56 70             rte

So assuming we have loaded the vector of our OSCtxSw routine into 0xec2c the next issue we will need to deal with is how the stack is left when our routine is entered.

On entry OSCtxSw    sp--> -  4         IRQ6 ROM vector (jsr pushed vector from 11d6:)
                          -  2         IRQ6 ROM vector
Move SP to here --->      +  0         er6   (H)
                          +  2         er6   (L)
                          +  4         er5   (H)
                          +  6         er5   (L)
                          +  8         er4   (H)
                          + 10         er4   (L)
                          + 12         er3   (H)
                          + 14         er3   (L)
                          + 16         er2   (H)
                          + 18         er2   (L)
                          + 20         er1   (H)
                          + 22         er1   (L)
                          + 24         er0   (H)
                          + 26         er0   (L)
                          + 28         task  (CCR & Upper 8-bits of PC)
                          + 30         task  (Lower 16-bits of PC)

Adjusting the Stack pointer to remove the JSR return address should be first thing that the context switch will need to do, after this you need to clear IRQ6. Then you are free to perform whatever Task Context Block movement are necessary to setup for the next task.

_OSCtxSw:
; Adjust for ROM JSR call
 	  ADD.L    #4,sp
; Clear IRQ6  - read/mask/write
	  mov.b @0x2f:8, r0l
	  and.b #0xbf, r0l
	  mov.b r0l, @0x2f:8
 
; Custom TCB code here..
 
; Replace stack and re-vector to new TCB via RTE
	  ldm.l @sp+,er4-er6
	  ldm.l @sp+,er0-er3
          RTE

The timer is the heartbeat that allows for pre-emptive multitasking.
We can use the TNCT0 timer as Vector 66 is redirected to a ROM routine that uses RAM based vector.

;ROM disassembly
;OVI0 Overflow vector 66 - 00 00 13 d0
     108:	00 00       	00 00             nop
     10a:	13 d0       	13 d0             rotr	#0x2,r0

Timer 0 overflow interrupt service routine - calls the vector in RAM location 0xFFEC7C
Disassembly of ROM revectoring code.

    13d0:	01 30 6d f0 	01 30 6d f0       stm.l	er0-er3,@-sp
    13d4:	01 20 6d f4 	01 20 6d f4       stm.l	er4-er6,@-sp
    13d8:	01 00 6b 02 	01 00 6b 02 ec 7c mov.l	@0xec7c:16,er2
    13dc:	ec 7c
    13de:	5d 20       	5d 20             jsr	@er2
    13e0:	01 20 6d 76 	01 20 6d 76       ldm.l	@sp+,er4-er6
    13e4:	01 30 6d 73 	01 30 6d 73       ldm.l	@sp+,er0-er3
    13e8:	56 70       	56 70             rte

So how to setup the ISR?

#define MSTPCR		( *( volatile INT8U *)( 0xFFFFFF3C ) )    /* Module Stop Control */
#define TCR0		( *( volatile INT8U *)( 0xFFFFFFB0 ) )	  //  Timer 0 control
#define TCNT0		( *( volatile INT16U *)( 0xFFFFFFB8 ) )	  //  Timer 0 counter
 
void AppTickInit(INT16U tick_rate)
{
  MSTPCR &= 0xef; // enable the 8 bit timer by UnStopping it
  TCR0 = 0x23;    // set TCR0 to OVIE interrupts on and clock of internal / 8192
  // Note: TCNT0 must be reset to the same number in the OSTickISR()
  TCNT0 = 0xe4;   // so interrupts must happen 11059200 / 8192 / (255-228) = 50 times a second  
  asm("andc #0x3f,ccr");      // Enable interrupts
}

So assuming we have located the _OSTickISR address into 0xec7c the following code fragment would be representative of an ISR

;; Vector 66
;; The cybiko ROM vector will push er0-er3,er4-er6
;; and a JSR return address (which we want to ignore)
_OSTickISR:
 	ADD.L    #4,sp
	JSR @_OSIntEnter               ; Call required routine.
 
;; Reload TNCT0
	mov.b	#0xe4,r2l            ; Reloaded with same value as use in AppTickInit()
	mov.b	r2l,@0xb8:8	
 
;; clear overflow interrupt bit
	BCLR #0x5, @0xB2:8
 
	JSR @_OSTimeTick               ; Call required routine.
	JSR @_OSIntExit                ; Call required routine.
 
	ldm.l @sp+,er4-er6
	ldm.l @sp+,er0-er3
	RTE