diff --git a/alloc.ccp b/alloc.ccp index dc3e6f8..bed497c 100644 --- a/alloc.ccp +++ b/alloc.ccp @@ -179,7 +179,7 @@ Thread *Memory::alloc_thread (): ret->pc = 0 ret->sp = 0 Thread_arch_init (ret) - ret->sleep = 0 + ret->sleep_count = ~0 ret->flags = 0 ret->schedule_prev = NULL ret->schedule_next = NULL diff --git a/boot-programs/lcd.ccp b/boot-programs/lcd.ccp index 214135a..6cf2255 100644 --- a/boot-programs/lcd.ccp +++ b/boot-programs/lcd.ccp @@ -37,97 +37,53 @@ static void set_backlight (bool state): PWM_CTR (0) = 0x3f GPIO_GPDR (2) &= ~PWM_ENABLE -// Write to a register. Value must be in range [0, 0xff]. -static void write_reg (unsigned reg, unsigned value): - unsigned data = (reg << 0xa) | 0x200 | value - GPIO_GPDR (2) |= SPEN - GPIO_GPDR (2) = (GPIO_GPDR (2) & ~SPDA) | SPCK - GPIO_GPDR (2) &= ~SPEN - udelay(25) - for unsigned i = 0; i < 16; ++i: - GPIO_GPDR (2) &= ~SPCK - if data & 0x8000: - GPIO_GPDR (2) |= SPDA - else: - GPIO_GPDR (2) &= ~SPDA - udelay (25) - GPIO_GPDR (2) |= SPCK - udelay (25) - data <<= 1 - GPIO_GPDR (2) |= SPEN - udelay(200) - -static void lcd_enable (): - udelay (50) - GPIO_GPDR (2) &= ~LCD_RET - udelay(150000) - GPIO_GPDR (2) |= LCD_RET - udelay(10000) - // These values have been copied from the linux source. - // I have no idea what they do. - write_reg (0x00, 0x03) - write_reg (0x01, 0x40) - write_reg (0x02, 0x11) - write_reg (0x03, 0xcd) - write_reg (0x04, 0x32) - write_reg (0x05, 0x0e) - write_reg (0x07, 0x03) - write_reg (0x08, 0x08) - write_reg (0x09, 0x32) - write_reg (0x0A, 0x88) - write_reg (0x0B, 0xc6) - write_reg (0x0C, 0x20) - write_reg (0x0D, 0x20) - set_backlight (true) - -static void lcd_disable (): - write_reg (0x00, 0x03) - set_backlight (false) - static void reset (): - gpio_as_pwm () - gpio_as_lcd_master () - - GPIO_GPDR (2) &= ~PWM_ENABLE - PWM_CTR (0) = 0x3f PWM_PER (0) = 300 - pwm_set_duty (0, 300) - pwm_set_full_duty (0) + set_backlight (false) // initialize things. GPIO_GPIER (2) &= ~(PWM_ENABLE | LCD_RET | SPEN | SPCK | SPDA) GPIO_GPDIR (2) |= PWM_ENABLE | LCD_RET | SPEN | SPCK | SPDA udelay (50) GPIO_GPDR (2) &= ~LCD_RET - udelay (150000) + ddelay (2) GPIO_GPDR (2) |= LCD_RET - udelay (10000) - lcd_enable () + ddelay (1) // For now, support only 16 bpp. // Screen is 800x480 tft. + unsigned h = 800, v = 480, hs = 80, vs = 20, fps = 60, Bpp = 2 LCD_CTRL = LCD_CTRL_BPP_16 | LCD_CTRL_BST_16 - LCD_VSYNC = 20 - LCD_HSYNC = 80 - LCD_DAV = (20 << 16) | 500 - LCD_DAH = (80 << 16) | 880 - LCD_VAT = (880 << 16) | 500 - //LCD_CFG = MODE_TFT_GEN | PCLK_N | VSYNC_N + LCD_VSYNC = vs + LCD_HSYNC = hs + LCD_DAV = (vs << 16) | (vs + v) + LCD_DAH = (hs << 16) | (hs + h) + LCD_VAT = ((hs + h) << 16) | (vs + v) + #define MODE_TFT_GEN 0 + #define PCLK_N (1 << 10) + #define VSYNC_N (1 << 8) + LCD_CFG = MODE_TFT_GEN | PCLK_N | VSYNC_N - // Stop lcd. - CPM_MSCR |= 1 << 7 + cpm_stop_lcd () - unsigned pclk = 60 * (800 * 3 + 80) * 500 unsigned pllout = cpm_get_pllout () - CPM_CFCR2 = pllout / pclk - 1 - unsigned v = pllout / (pclk * 4) - 1 - while v < 0xf && pllout / (v + 1) > 150000000: - ++v - CPM_CFCR = (CPM_CFCR & ~CPM_CFCR_LFR_MASK) | (v << CPM_CFCR_LFR_BIT) | CPM_CFCR_UPE + unsigned pixclock = fps * (hs + h) * (vs + v) + CPM_CFCR2 = pllout / pixclock - 1 + + unsigned val = pllout / (pixclock * 4) - 1 + while val < 0xf && pllout / (val + 1) > 150000000: + ++val + cpm_set_lcdclk_div (val) + CPM_CFCR |= CPM_CFCR_UPE + + cpm_start_lcd () - // Start lcd. - CPM_MSCR &= ~(1 << 7) udelay (1000) + //LCD_DA0 = framebuffer + unsigned frame_size = v * h * Bpp + LCD_CMD0 = LCD_CMD_SOFINT | LCD_CMD_EOFINT | (frame_size << LCD_CMD_LEN_BIT) + lcd_set_ena () + lcd_enable_eof_intr () int main (): map_gpio () @@ -136,19 +92,20 @@ int main (): map_cpm () reset () - - while true: - set_backlight (false) - kdebug (0) - set_backlight (true) - kdebug (0) - schedule () + register_interrupt (IRQ_LCD) + set_backlight (true) while true: Message msg if !wait (&msg): continue switch msg.protected_data: + case IRQ_LCD: + kdebug (0) + LCD_STATE &= ~LCD_STATE_EOF + register_interrupt (IRQ_LCD) + // TODO: allow callback + break case LCD_BACKLIGHT: set_backlight (msg.data[0]) break diff --git a/invoke.ccp b/invoke.ccp index f220f30..4a5ca17 100644 --- a/invoke.ccp +++ b/invoke.ccp @@ -18,6 +18,7 @@ #include "kernel.hh" +// From user-provided, thus untrusted, data, find a capability. Capability *Memory::find_capability (unsigned code, bool *copy): if !code: return NULL @@ -39,6 +40,7 @@ Capability *Memory::find_capability (unsigned code, bool *copy): return c return NULL +// Try to deliver a message. bool Receiver::try_deliver (): if !messages || !owner || !owner->is_waiting (): return false @@ -61,6 +63,7 @@ bool Receiver::try_deliver (): owner->unwait () return true +// Send a message to a receiver; try to deliver it immediately. bool Receiver::send_message (unsigned protected_data, Capability::Context *c): bool tried_direct = false if owner && owner->is_waiting () && (protected_data == reply_protected_data || !protected_only): @@ -328,14 +331,19 @@ static void thread_invoke (unsigned target, unsigned protected_data, Capability: thread->unrun () break case CAP_THREAD_INFO_SLEEP: - value = &thread->sleep + value = &thread->sleep_count + if thread->flags & THREAD_FLAG_WAITING: + unsigned newval = (*value & ~c->data[3]) | (c->data[2] & c->data[3]) + if *value == ~0 && newval != ~0: + thread->sleep () + else if *value != ~0 && newval == ~0: + thread->unsleep () break default: value = Thread_arch_info (thread, c->data[1]) break if value: - *value &= ~c->data[3] - *value |= c->data[2] & c->data[3] + *value = (*value & ~c->data[3]) | (c->data[2] & c->data[3]) reply_num (*value) else: reply_num (0) @@ -655,19 +663,19 @@ static bool kernel_invoke (unsigned target, unsigned protected_data, Capability: reply_receiver = (Receiver *)protected_data reply_receiver->protected_only = !(target & (1 << CAP_RECEIVER_CALL_ASYNC)) Capability *c0 = c->cap[0] - if ((unsigned)c0->target & ~KERNEL_MASK) != 0: - Capability r - fill_cap (&r, protected_data, reply_receiver->reply_protected_data) - c->cap[0] = &r - c->copy[0] = true - c0->target->send_message (c0->protected_data, c) - r.invalidate () - return true - else: - // Kernel call: don't create actual capablities. - c->cap[0] = NULL - kernel_invoke ((unsigned)c0->target, c0->protected_data, c, c0) - return true + if c0: + if ((unsigned)c0->target & ~KERNEL_MASK) != 0: + Capability r + fill_cap (&r, protected_data, reply_receiver->reply_protected_data) + c->cap[0] = &r + c->copy[0] = true + c0->target->send_message (c0->protected_data, c) + r.invalidate () + else: + // Kernel call: don't create actual capablities. + c->cap[0] = NULL + kernel_invoke ((unsigned)c0->target, c0->protected_data, c, c0) + return true if (target & (CAPTYPE_MASK | (1 << CAP_RECEIVER_REPLY))) == (CAPTYPE_RECEIVER | (1 << CAP_RECEIVER_REPLY)): // This is a reply capability. Receiver *r = (Receiver *)protected_data @@ -717,4 +725,5 @@ bool Capability::invoke (Capability::Context *c): // This is not a kernel capability: send a message to the receiver. return target->send_message (protected_data, c) // This is a kernel capability. Use a function to allow optimized call capabilities. + reply_receiver = NULL return kernel_invoke ((unsigned)target, protected_data, c, this) diff --git a/iris.h b/iris.h index af73b8e..e2a7769 100644 --- a/iris.h +++ b/iris.h @@ -18,6 +18,13 @@ #ifndef __IRIS_H #define __IRIS_H +// Without the standard library, we don't have this definition. +// I preferred ((void*)0), but C++ has too strict type-checking to +// make that work. +#ifndef NULL +#define NULL 0 +#endif + #ifdef __cplusplus extern "C" { #endif @@ -501,9 +508,17 @@ static unsigned thread_sleep (Capability thread, unsigned value) return thread_info (thread, CAP_THREAD_INFO_SLEEP, value, ~0); } -static void my_sleep (unsigned value) +static int my_sleep (unsigned value, Message *ret) { - call_n04 (__my_thread, CAP_THREAD_INFO, CAP_THREAD_INFO_SLEEP, value, ~0); + ret->data[0] = CAP_THREAD_INFO; + ret->data[1] = CAP_THREAD_INFO_SLEEP; + ret->data[2] = value; + ret->data[3] = ~0; + ret->cap[0] = 0; + ret->cap[1] = 0; + ret->cap[2] = 0; + ret->cap[3] = 0; + return call (__my_thread, ret); } static unsigned thread_get_sleep (Capability thread) diff --git a/kernel.hhp b/kernel.hhp index 9a7e579..061b678 100644 --- a/kernel.hhp +++ b/kernel.hhp @@ -33,11 +33,6 @@ #define EXTERN extern #endif -// Without the standard library, we don't have this definition. -// I preferred ((void*)0), but C++ has too strict type-checking to -// make that work. -#define NULL 0 - struct Object_base struct Page struct Thread @@ -73,8 +68,10 @@ struct Thread : public Object : unsigned pc, sp Thread_arch arch unsigned flags - unsigned sleep + unsigned sleep_count Thread *schedule_prev, *schedule_next + void sleep () + void unsleep () void run () void unrun () void wait () diff --git a/mips/entry.S b/mips/entry.S index 1d55ed5..f9c883d 100644 --- a/mips/entry.S +++ b/mips/entry.S @@ -111,9 +111,6 @@ start_idle: // 280 // Wait for the next interrupt, then the first thread will be scheduled. // It is impractical to try to call schedule, because for that the // idle task would need to own capabilities. - move $v0, $zero - syscall - nop 1: wait b 1b nop @@ -221,7 +218,8 @@ save_regs: #ifndef NDEBUG // Allow interrupts to set EPC and friends. mfc0 $k0, $CP0_STATUS - andi $k0, $k0, 0xff00 + li $k1, 0x1000ff00 + and $k0, $k0, $k1 mtc0 $k0, $CP0_STATUS #endif diff --git a/mips/init.ccp b/mips/init.ccp index 3eae0dd..7872427 100644 --- a/mips/init.ccp +++ b/mips/init.ccp @@ -257,12 +257,14 @@ void init (): GPIO_GPDIR (GPIO_USB_CLK_EN_PORT) |= 1 << GPIO_USB_CLK_EN GPIO_GPDR (GPIO_USB_CLK_EN_PORT) |= 1 << GPIO_USB_CLK_EN - // Start the operating system timer. - unsigned latch = (JZ_EXTAL + (HZ>>1)) / HZ + // Start the operating system timer, and set it to give an interrupt immediately. + // This is better, because the kernel start with jumping into the idle task and + // waiting for the first interrupt. + unsigned latch = (JZ_EXTAL + (HZ >> 1)) / HZ ost_disable_all () ost_set_mode (0, OST_TCSR_UIE | OST_TCSR_CKS_EXTAL) ost_set_reload (0, latch) - ost_set_count (0, latch) + ost_set_count (0, 0) ost_set_mode (0, OST_TCSR_UIE | OST_TCSR_CKS_EXTAL) ost_enable_channel (0) intc_unmask_irq (IRQ_OST0) diff --git a/mips/interrupts.ccp b/mips/interrupts.ccp index 17b3b5b..b01d174 100644 --- a/mips/interrupts.ccp +++ b/mips/interrupts.ccp @@ -19,11 +19,40 @@ #define ARCH #include "../kernel.hh" +static void handle_exit (Thread *old_current): + if !current: + schedule () + if !current: + current = &idle + if old_current == current: + return + if current != &idle: + if (Memory *)asids[current->address_space->arch.asid] != current->address_space: + if asids[0]: + current->address_space->arch.asid = asids[0] + asids[0] = asids[asids[0]] + else: + static unsigned random = 1 + current->address_space->arch.asid = random + // Overwrite used asid, so flush those values from tlb. + flush_tlb (random) + ++random + if random >= 64: + random = 1 + asids[current->address_space->arch.asid] = (unsigned)current->address_space + cp0_set (CP0_ENTRY_HI, current->address_space->arch.asid) + directory = current->address_space->arch.directory + if current->flags & THREAD_FLAG_PRIV: + cp0_set (CP0_STATUS, 0x1000ff13) + else: + cp0_set (CP0_STATUS, 0x0000ff13) + /// A TLB miss has occurred. This is the slow version. It is only used /// when k0 or k1 is not 0, or when an error occurs. /// Otherwise, the ultra-fast code in entry.S is used. Thread *tlb_refill (): //panic (0x88776655, "TLB refill") + Thread *old_current = current if !directory: panic (0x44449999, "No directory") unsigned EntryHi @@ -41,11 +70,13 @@ Thread *tlb_refill (): cp0_set (CP0_ENTRY_LO0, t[idx]) cp0_set (CP0_ENTRY_LO1, t[idx + 1]) __asm__ volatile ("tlbwr") + handle_exit (old_current) return current /// An interrupt which is not an exception has occurred. Thread *interrupt (): //panic (0x88877722, "Interrupt") + Thread *old_current = current //dbg_send (INTC_IPR) unsigned ipr = INTC_IPR for unsigned i = 0; i < 32; ++i: @@ -68,6 +99,7 @@ Thread *interrupt (): ost_clear_uf (0) intc_ack_irq (IRQ_OST0) timer_interrupt () + handle_exit (old_current) return current void flush_tlb (unsigned asid): @@ -87,10 +119,9 @@ static void arch_invoke (): Thread *caller = current target = caller->address_space->find_capability (caller->arch.v0, &wait) if !target: - // TODO: there must be no action here. This is just because the rest doesn't work yet. - dbg_send (3, 2) + // There must be no action here. + //dbg_send (3, 2) //dbg_send (caller->arch.v0) - schedule () // Calling an invalid capability always fails. caller->arch.v0 = 0 else: @@ -108,35 +139,10 @@ static void arch_invoke (): caller->arch.v0 = target->invoke (&c) ? 1 : 0 if caller != current && caller != &idle && (caller->flags & (THREAD_FLAG_RUNNING | THREAD_FLAG_WAITING)) == THREAD_FLAG_RUNNING: current = caller - else if !current: - schedule () - if !current: - current = &idle - if caller != current: - if (Memory *)asids[current->address_space->arch.asid] != current->address_space: - if asids[0]: - current->address_space->arch.asid = asids[0] - asids[0] = asids[asids[0]] - else: - static unsigned random = 1 - current->address_space->arch.asid = random - // Overwrite used asid, so flush those values from tlb. - flush_tlb (random) - ++random - if random >= 64: - random = 1 - asids[current->address_space->arch.asid] = (unsigned)current->address_space - cp0_set (CP0_ENTRY_HI, current->address_space->arch.asid) - directory = current->address_space->arch.directory - unsigned status - cp0_get (CP0_STATUS, status) - status &= 0x0fffffff - if current->flags & THREAD_FLAG_PRIV: - status |= 0x10000000 - cp0_set (CP0_STATUS, status | 0x13) /// A general exception has occurred. Thread *exception (): + Thread *old_current = current unsigned cause cp0_get (CP0_CAUSE, cause) switch (cause >> 2) & 0x1f: @@ -220,9 +226,12 @@ Thread *exception (): panic (0xf5223344, "Reserved exception code") default: panic (0xf6223344, "Impossible exception code") + handle_exit (old_current) return current /// There's a cache error. Big trouble. Probably not worth trying to recover. Thread *cache_error (): panic (0x33333333, "cache error") + Thread *old_current = current + handle_exit (old_current) return current diff --git a/mips/jz4730.hhp b/mips/jz4730.hhp index c8fb69c..7722668 100644 --- a/mips/jz4730.hhp +++ b/mips/jz4730.hhp @@ -23,6 +23,7 @@ // Main clock, for cpu, serial port, and with divisors for most other hardware #define JZ_EXTAL 3686400 +//#define JZ_EXTAL 3072000 // RTC clock #define RTC_CLOCK 32768 @@ -2311,8 +2312,9 @@ static __inline__ void udelay (unsigned us): GPIO_GPDR (0) = GPIO_GPDR (0) #ifndef __KERNEL -static __inline__ void mdelay (unsigned ms): - my_sleep ((ms + 99) / 100) +static __inline__ void ddelay (unsigned ds): + Message m + my_sleep (ds, &m) #endif /*************************************************************************** diff --git a/mips/test.ccp b/mips/test.ccp index f081b94..e47ceb8 100644 --- a/mips/test.ccp +++ b/mips/test.ccp @@ -109,6 +109,6 @@ void dbg_send (unsigned code, unsigned bits): dbg_led (false, false, false) dbg_sleep (200) dbg_led (true, true, false) - dbg_sleep (500) + dbg_sleep (50) dbg_led (false, false, false) - dbg_sleep (500) + dbg_sleep (50) diff --git a/schedule.ccp b/schedule.ccp index b052c70..fe3d667 100644 --- a/schedule.ccp +++ b/schedule.ccp @@ -33,18 +33,29 @@ static void unrun_thread (Thread *thread): first_scheduled = thread->schedule_next if thread->schedule_next: thread->schedule_next->schedule_prev = thread->schedule_prev - if thread->sleep: - thread->schedule_next = first_sleeper - thread->schedule_prev = NULL - if thread->schedule_next: - thread->schedule_next->schedule_prev = thread - first_sleeper = thread + +void Thread::sleep (): + schedule_next = first_sleeper + if schedule_next: + schedule_next->schedule_prev = this + schedule_prev = NULL + first_sleeper = this + +void Thread::unsleep (): + if schedule_next: + schedule_next->schedule_prev = schedule_prev + if schedule_prev: + schedule_prev->schedule_next = schedule_next + else: + first_sleeper = schedule_next void Thread::run (): if flags & THREAD_FLAG_RUNNING: return flags |= THREAD_FLAG_RUNNING if flags & THREAD_FLAG_WAITING: + if sleep_count != ~0: + sleep () return run_thread (this) @@ -53,41 +64,47 @@ void Thread::unrun (): return flags &= ~THREAD_FLAG_RUNNING if flags & THREAD_FLAG_WAITING: + if sleep_count != ~0: + unsleep () return unrun_thread (this) +void Thread::unwait (): + if !(flags & THREAD_FLAG_WAITING): + return + flags &= ~THREAD_FLAG_WAITING + if sleep_count != ~0: + unsleep () + if flags & THREAD_FLAG_RUNNING: + run_thread (this) + +static void sleep_tick (Thread *thread): + if !thread->sleep_count: + thread->unwait () + // Time-out: let the thread know with a special message. + Capability::Context c + for unsigned i = 0; i < 4; ++i: + c.data[i] = 0 + c.cap[i] = NULL + c.copy[i] = false + Thread_arch_receive (thread, ~0, &c) + // Fall through to let sleep be set to ~0, so the next wait will be infinitely long again. + --thread->sleep_count + void Thread::wait (): if flags & THREAD_FLAG_WAITING: return if flags & THREAD_FLAG_RUNNING: unrun_thread (this) flags |= THREAD_FLAG_WAITING + if sleep_count != ~0: + sleep () // Try to receive a message from a Receiver immediately. for Receiver *r = receivers; r; r = r->next_owned: if r->try_deliver (): - break - -void Thread::unwait (): - if !(flags & THREAD_FLAG_WAITING): - return - flags &= ~THREAD_FLAG_WAITING - if flags & THREAD_FLAG_RUNNING: - run_thread (this) - -void timer_interrupt (): - //panic (0x88877744, "Timer interrupt") - Thread *thread, *next - for thread = first_sleeper; thread; thread = next: - next = thread->next - if !--thread->sleep: - if thread->flags & THREAD_FLAG_WAITING: - thread->unwait () - else: - run_thread (thread) - //#ifndef NDEBUG - //static bool ledstate = false - //dbg_led (false, false, ledstate = !ledstate) - //#endif + return + if sleep_count != ~0: + sleep_tick (this) void schedule (): Thread *old = current @@ -95,3 +112,17 @@ void schedule (): current = current->schedule_next if !current: current = first_scheduled + if !current: + current = &idle + +void timer_interrupt (): + //panic (0x88877744, "Timer interrupt") + Thread *thread, *next + for thread = first_sleeper; thread; thread = next: + next = thread->next + sleep_tick (thread) + schedule () + //#ifndef NDEBUG + //static bool ledstate = false + //dbg_led (false, false, ledstate = !ledstate) + //#endif