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.
- Cybiko uC/OS-II port - Cybiko h8 enhancements to the base ucos distribution.
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.
Context Switch
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
Timer Interrupt Service
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