├── .gitignore ├── source ├── arch │ ├── arm-cortex-m │ │ ├── armv8-m.zig │ │ ├── armv8.1-m.zig │ │ ├── armv7m.s │ │ ├── armv7m_hf.s │ │ ├── armv6m.s │ │ ├── arch.zig │ │ ├── armv6-m.zig │ │ └── armv7-m.zig │ ├── arch_interface.zig │ └── test │ │ └── test_arch.zig ├── synchronization │ ├── semaphore.zig │ ├── mutex.zig │ ├── msg_queue.zig │ ├── sync_control.zig │ ├── event_group.zig │ └── timer.zig ├── os_core.zig └── task.zig ├── os.zig ├── README.md ├── LICENSE └── test.zig /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # zig 55 | .zig-cache 56 | -------------------------------------------------------------------------------- /source/arch/arm-cortex-m/armv8-m.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | pub const minStackSize = 17; 18 | pub const LOWEST_PRIO_MSK: u8 = 0xFF; 19 | -------------------------------------------------------------------------------- /source/arch/arm-cortex-m/armv8.1-m.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const OsTask = @import("../../task.zig"); 18 | const Os = @import("../../../os.zig"); 19 | 20 | pub inline fn contextSwitch() void { 21 | //context switch here 22 | OsTask.TaskControl.current_task.?._state = OsTask.State.running; 23 | } 24 | -------------------------------------------------------------------------------- /source/arch/arch_interface.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const Task = @import("../task.zig").Task; 18 | const builtin = @import("builtin"); 19 | const std = @import("std"); 20 | const cpu = std.Target.arm.cpu; 21 | 22 | const TestArch = @import("test/test_arch.zig"); 23 | const ArmCortexM = @import("arm-cortex-m/arch.zig"); 24 | //Import future architecture implementions here 25 | 26 | pub const Arch = getArch: { 27 | const cpu_model = builtin.cpu.model.*; 28 | 29 | if (builtin.is_test == true) { 30 | break :getArch TestArch; 31 | } else if (std.meta.eql(cpu_model, cpu.cortex_m0) or // 32 | std.meta.eql(cpu_model, cpu.cortex_m0plus) or // 33 | std.meta.eql(cpu_model, cpu.cortex_m3) or // 34 | std.meta.eql(cpu_model, cpu.cortex_m4) or // 35 | std.meta.eql(cpu_model, cpu.cortex_m7)) 36 | { 37 | break :getArch ArmCortexM; 38 | } else { 39 | @compileError("Unsupported architecture selected."); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /source/arch/arm-cortex-m/armv7m.s: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Edward Pizzella 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | .extern current_task 15 | .extern next_task 16 | .extern g_stack_offset 17 | .global PendSV_Handler 18 | .thumb_func 19 | 20 | PendSV_Handler: 21 | CPSID I 22 | LDR R2, =g_stack_offset 23 | LDR R2, [R2] 24 | LDR R0, =current_task 25 | LDR R1, [R0] 26 | CMP R1, #0 /* If current_task is null skip context save */ 27 | BEQ ContextRestore 28 | MRS R12, PSP /* Move process stack pointer into R12 */ 29 | 30 | STMDB R12!, {R4-R11, LR} /* Push registers R4-R11 & LR on the stack */ 31 | STR R12, [R1, R2] /* Save the current stack pointer in current_task */ 32 | ContextRestore: 33 | LDR R3, =next_task 34 | LDR R3, [R3] 35 | LDR R12, [R3, R2] /* Set stack pointer to next_task stack pointer */ 36 | LDMIA R12!, {R4-R11, LR} /* Pop registers R4-R11 & LR */ 37 | 38 | MSR PSP, R12 /* Load PSP with new stack pointer */ 39 | STR R3, [R0, #0x00] /* Set current_task to next_task */ 40 | CPSIE I 41 | BX LR 42 | .size PendSV_Handler, .-PendSV_Handler -------------------------------------------------------------------------------- /os.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const std = @import("std"); 18 | const builtin = @import("builtin"); 19 | 20 | const OsCore = @import("source/os_core.zig"); 21 | const OsTask = @import("source/task.zig"); 22 | const ArchInterface = @import("source/arch/arch_interface.zig"); 23 | 24 | const Arch = ArchInterface.Arch; 25 | 26 | pub const Task = OsTask.Task; 27 | pub const EventGroup = @import("source/synchronization/event_group.zig").EventGroup; 28 | pub const Mutex = @import("source/synchronization/mutex.zig").Mutex; 29 | pub const Semaphore = @import("source/synchronization/semaphore.zig").Semaphore; 30 | pub const Timer = @import("source/synchronization/timer.zig").Timer; 31 | pub const createMsgQueueType = @import("source/synchronization/msg_queue.zig").createMsgQueueType; 32 | pub const Time = OsCore.Time; 33 | pub const Error = OsCore.Error; 34 | pub const OsConfig = OsCore.OsConfig; 35 | 36 | /// The operating system will begin multitasking. This function should only be 37 | /// called once. Subsequent calls have no effect. The frist time this function 38 | /// is called it will not return as multitasking has started. 39 | pub fn startOS(comptime config: OsConfig) void { 40 | OsCore.startOS(config); 41 | } 42 | 43 | //Disable all interrupts 44 | pub inline fn criticalStart() void { 45 | Arch.criticalStart(); 46 | } 47 | 48 | //Enable all interrupts 49 | pub inline fn criticalEnd() void { 50 | Arch.criticalEnd(); 51 | } 52 | -------------------------------------------------------------------------------- /source/arch/arm-cortex-m/armv7m_hf.s: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Edward Pizzella 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | .extern current_task 15 | .extern next_task 16 | .extern g_stack_offset 17 | .global PendSV_Handler 18 | .thumb_func 19 | 20 | PendSV_Handler: 21 | CPSID I 22 | LDR R2, =g_stack_offset 23 | LDR R2, [R2] 24 | LDR R0, =current_task 25 | LDR R1, [R0] 26 | CMP R1, #0 /* If current_task is null skip context save */ 27 | BEQ ContextRestore 28 | MRS R12, PSP /* Move process stack pointer into R12 */ 29 | 30 | TST LR, #0x10 /* If task was using FPU push FPU registers */ 31 | IT EQ 32 | VSTMDBEQ R12!,{S16-S31} 33 | 34 | STMDB R12!, {R4-R11, LR} /* Push registers R4-R11 & LR on the stack */ 35 | STR R12, [R1, R2] /* Save the current stack pointer in current_task */ 36 | ContextRestore: 37 | LDR R3, =next_task 38 | LDR R3, [R3] 39 | LDR R12, [R3, R2] /* Set stack pointer to next_task stack pointer */ 40 | LDMIA R12!, {R4-R11, LR} /* Pop registers R4-R11 & LR */ 41 | 42 | TST LR, #0x10 /* If task was using FPU pop FPU registers */ 43 | IT EQ 44 | VLDMIAEQ R12!,{S16-S31} 45 | 46 | MSR PSP, R12 /* Load PSP with new stack pointer */ 47 | STR R3, [R0, #0x00] /* Set current_task to next_task */ 48 | CPSIE I 49 | BX LR 50 | .size PendSV_Handler, .-PendSV_Handler -------------------------------------------------------------------------------- /source/arch/test/test_arch.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const Task = @import("../../task.zig").Task; 18 | const OsCore = @import("../../os_core.zig"); 19 | const Self = @This(); 20 | 21 | pub var scheduler: bool = false; 22 | pub var contex_switch: bool = false; 23 | pub var int_active: bool = false; 24 | pub var debug_atached: bool = false; 25 | pub var criticalSection: bool = false; 26 | 27 | //Test function 28 | pub fn schedulerRan() bool { 29 | defer scheduler = false; 30 | return scheduler; 31 | } 32 | 33 | pub fn contextSwitchRan() bool { 34 | defer contex_switch = false; 35 | return contex_switch; 36 | } 37 | 38 | pub fn getCriticalSection() bool { 39 | return criticalSection; 40 | } 41 | 42 | pub fn setDebug(attached: bool) void { 43 | debug_atached = attached; 44 | } 45 | 46 | pub fn setInterruptActive(active: bool) void { 47 | int_active = active; 48 | } 49 | 50 | //Interface 51 | pub const minStackSize = 10; 52 | 53 | pub fn coreInit(clock_config: *const OsCore.ClockConfig) void { 54 | _ = clock_config; 55 | } 56 | 57 | pub fn initStack(task: *Task) void { 58 | _ = task; 59 | } 60 | 61 | pub fn interruptActive() bool { 62 | return int_active; 63 | } 64 | 65 | //Enable Interrupts 66 | pub inline fn criticalEnd() void { 67 | criticalSection = false; 68 | } 69 | 70 | //Disable Interrupts 71 | pub inline fn criticalStart() void { 72 | criticalSection = true; 73 | } 74 | 75 | pub inline fn runScheduler() void { 76 | scheduler = true; 77 | } 78 | 79 | pub inline fn runContextSwitch() void { 80 | contex_switch = true; 81 | } 82 | 83 | pub inline fn startOs() void {} 84 | 85 | pub inline fn isDebugAttached() bool { 86 | return debug_atached; 87 | } 88 | -------------------------------------------------------------------------------- /source/arch/arm-cortex-m/armv6m.s: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Edward Pizzella 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | .extern current_task 15 | .extern next_task 16 | .extern g_stack_offset 17 | .global PendSV_Handler 18 | .thumb_func 19 | 20 | PendSV_Handler: 21 | CPSID I 22 | LDR R3, =g_stack_offset 23 | LDR R3, [R3] 24 | LDR R0, =current_task 25 | LDR R1, [R0] 26 | CMP R1, #0 /* If current_task is null skip context save */ 27 | BEQ ContextRestore 28 | 29 | MRS R2, PSP /* Move process stack pointer into R2 */ 30 | SUBS R2, R2, #0x20 /* Adjust stack point for R8-R11 */ 31 | 32 | STR R2, [R1, R3] /* Save the current stack pointer in current_task */ 33 | 34 | STMIA R2!, {R4-R7} /* Push registers R4-R7 on the stack */ 35 | 36 | MOV R4, R8 /* Push registers R8-R11 on the stack */ 37 | MOV R5, R9 38 | MOV R6, R10 39 | MOV R7, R11 40 | STMIA R2!, {R4-R7} 41 | 42 | ContextRestore: 43 | LDR R1, =next_task 44 | LDR R1, [R1] 45 | LDR R2, [R1, R3] /* Set stack pointer to next_task stack pointer */ 46 | 47 | ADDS R2, R2, #0x10 /* Adjust stack pointer */ 48 | LDMIA R2!, {R4-R7} /* Pop registers R8-R11 */ 49 | MOV R8, R4 50 | MOV R9, R5 51 | MOV R10, R6 52 | MOV R11, R7 53 | 54 | MSR PSP, R2 /* Load PSP with final stack pointer */ 55 | 56 | SUBS R2, R2, #0x20 /* Adjust stack pointer */ 57 | LDMIA R2!, {R4-R7} /* Pop register R4-R7 */ 58 | 59 | STR R1, [R0, #0x00] /* Set current_task to next_task */ 60 | 61 | MOVS R3, 0x02 /* Load EXC_RETURN with 0xFFFFFFFD*/ 62 | MVNS R3, R3 63 | MOV LR, R3 64 | 65 | CPSIE I 66 | BX LR 67 | .size PendSV_Handler, .-PendSV_Handler 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Echo OS 2 | 3 | Echo OS is a Real Time Operating System written in Zig. As is tradition in the realm of RTOSes, Echo OS is really more of a real time kernel than an actual OS. 4 | 5 | ## Features 6 | 7 | Echo OS has the following features: 8 | 9 | - Premptive priority based scheduler 10 | - 32 priority levels for user tasks plus 1 resevered priority level (lowest prioirty) for the idle task 11 | - Unlimited tasks per priority level 12 | - Task Syncronization 13 | - Event Groups 14 | - Mutexes 15 | - Semaphores 16 | - Message Queues 17 | - Software Timers 18 | - Tasks return anyerror!void. Users have the option to provide an error handler callback. 19 | 20 | ## Supported Architectures 21 | 22 | - [X] ARMv6-M 23 | - [X] ARMv7-M 24 | - [ ] ARMv8-M 25 | - [ ] ARMv8.1-M 26 | - [ ] RISC-V 27 | 28 | ## Getting Started 29 | 30 | To add the master branch of Echo Os to your project: 31 | 32 | ``` 33 | zig fetch --save git+https://github.com/epizzella/Echo-OS 34 | ``` 35 | 36 | To add a tagged version of Echo Os to your project: 37 | 38 | ``` 39 | # Replace with the version of Echo OS that you want to use 40 | zig fetch --save git+https://github.com/epizzella/Echo-OS# 41 | 42 | ``` 43 | 44 | Then add the following to your ```build.zig```: 45 | 46 | ``` 47 | const rtos = b.dependency("EchoOS", .{ .target = target, .optimize = optimize }); 48 | elf.root_module.addImport("EchoOS", rtos.module("EchoOS")); 49 | ``` 50 | 51 | Echo OS uses the ```cpu_model``` field in the ```target``` argument at comptime to pull in the architecture specific files. Example: 52 | 53 | ``` 54 | const target = b.resolveTargetQuery(.{ 55 | .cpu_arch = .thumb, 56 | .cpu_model = std.zig.CrossTarget.CpuModel{ .explicit = &std.Target.arm.cpu.cortex_m3 }, 57 | .abi = .eabi, 58 | .os_tag = .freestanding, 59 | }); 60 | ``` 61 | 62 | ## API 63 | 64 | The entire API is accessabile through a single file: os.zig. You can use os.zig via @import: ```const Os = @import("EchoOS");``` 65 | 66 | ### Basic Example 67 | 68 | The following is a basic example of creating a task and starting multitasking. A single task is created and multitasking is started. 69 | 70 | ``` 71 | const Os = @import("EchoOS"); //Import Echo OS 72 | const Task = Os.Task 73 | 74 | //task 1 subroutine 75 | fn task1() !void { 76 | while(true) {} 77 | } 78 | 79 | //task stack 80 | const stackSize = 25; 81 | var stack1: [stackSize]u32 = [_]u32{0xDEADC0DE} ** stackSize; 82 | 83 | //Create a task 84 | var tcb = Task.create_task(.{ 85 | .name = "task", 86 | .priority = 1, 87 | .stack = &stack1, 88 | .subroutine = &task1, 89 | }); 90 | 91 | export fn main() void() { 92 | //Initialize drivers before starting 93 | 94 | // initalize the task & make the OS aware of it 95 | tcb.init(); 96 | 97 | //Start multitasking 98 | Os.startOS( 99 | .clock_config = .{ 100 | .cpu_clock_freq_hz = 64_000_000, 101 | .os_sys_clock_freq_hz = 1_000, 102 | }, 103 | ) 104 | 105 | unreachable; 106 | } 107 | ``` 108 | 109 | For a more detailed example please refer to the [Echo OS example projects.](https://github.com/epizzella/Echo-OS-Examples) 110 | -------------------------------------------------------------------------------- /source/synchronization/semaphore.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const OsTask = @import("../task.zig"); 18 | const OsCore = @import("../os_core.zig"); 19 | const TaskQueue = OsTask.TaskQueue; 20 | const SyncControl = @import("sync_control.zig"); 21 | const ArchInterface = @import("../arch/arch_interface.zig"); 22 | 23 | const Arch = ArchInterface.Arch; 24 | 25 | const task_control = &OsTask.task_control; 26 | const os_config = &OsCore.getOsConfig; 27 | const Task = OsTask.Task; 28 | const Error = OsCore.Error; 29 | 30 | pub const Control = SyncControl.SyncControl; 31 | const SyncContex = SyncControl.SyncContext; 32 | 33 | pub const Semaphore = struct { 34 | const Self = @This(); 35 | _name: []const u8, 36 | _count: usize, 37 | _syncContext: SyncContex, 38 | 39 | pub const CreateOptions = struct { 40 | /// Name of sempahore 41 | name: []const u8, 42 | /// Start value of the semaphore counter 43 | inital_value: usize, 44 | }; 45 | 46 | /// Create a semaphore object 47 | pub fn create_semaphore(comptime options: CreateOptions) Self { 48 | return Self{ 49 | ._name = options.name, 50 | ._count = options.inital_value, 51 | ._syncContext = .{}, 52 | }; 53 | } 54 | 55 | /// Add the semaphore to the OS 56 | pub fn init(self: *Self) Error!void { 57 | try Control.add(&self._syncContext); 58 | } 59 | 60 | /// Remove the semaphore from the OS 61 | pub fn deinit(self: *Self) Error!void { 62 | try Control.remove(&self._syncContext); 63 | } 64 | 65 | pub const WaitOptions = struct { 66 | /// an optional timeout in milliseconds. When set to a non-zero value the 67 | /// task will block for the amount of time specified. If the timeout expires 68 | /// before the count is non-zero acquire() will return OsError.TimedOut. When 69 | /// set to zero the task will block until the count is non-zero. 70 | timeout_ms: u32 = 0, 71 | }; 72 | 73 | /// Decrements the counter. If the counter is at zero the running task will be 74 | /// blocked until the counter's value becomes non zero. Cannot be called from an 75 | /// interrupt. 76 | pub fn wait(self: *Self, options: WaitOptions) Error!void { 77 | _ = try SyncControl.validateCallMajor(); 78 | if (!self._syncContext._init) return Error.Uninitialized; 79 | Arch.criticalStart(); 80 | defer Arch.criticalEnd(); 81 | 82 | if (self._count == 0) { 83 | //locked 84 | try Control.blockTask(&self._syncContext, options.timeout_ms); 85 | } else { 86 | //unlocked 87 | self._count -= 1; 88 | } 89 | } 90 | 91 | pub const PostOptions = struct { 92 | runScheduler: bool = true, 93 | }; 94 | 95 | /// Increments the counter. If the pending task is higher priority 96 | /// than the running task the scheduler is called. 97 | pub fn post(self: *Self, options: PostOptions) Error!void { 98 | Arch.criticalStart(); 99 | defer Arch.criticalEnd(); 100 | const running_task = try SyncControl.validateCallMinor(); 101 | 102 | if (self._syncContext._pending.head) |head| { 103 | task_control.readyTask(head); 104 | if (head._priority < running_task._priority and options.runScheduler) { 105 | Arch.criticalEnd(); 106 | Arch.runScheduler(); 107 | } 108 | } else { 109 | self._count += 1; 110 | } 111 | } 112 | 113 | /// Readys the task if it is waiting on the semaphore. When the task next 114 | /// runs acquire() will return OsError.Aborted 115 | pub fn abortAcquire(self: *Self, task: *Task) Error!void { 116 | try Control.abort(&self._syncContext, task); 117 | } 118 | }; 119 | -------------------------------------------------------------------------------- /source/synchronization/mutex.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const OsTask = @import("../task.zig"); 18 | const OsCore = @import("../os_core.zig"); 19 | const TaskQueue = OsTask.TaskQueue; 20 | const SyncControl = @import("sync_control.zig"); 21 | const ArchInterface = @import("../arch/arch_interface.zig"); 22 | 23 | const Arch = ArchInterface.Arch; 24 | 25 | const task_control = &OsTask.task_control; 26 | const Task = OsTask.Task; 27 | const os_config = &OsCore.getOsConfig; 28 | const Error = OsCore.Error; 29 | pub const Control = SyncControl.SyncControl; 30 | const SyncContex = SyncControl.SyncContext; 31 | 32 | pub const Mutex = struct { 33 | const Self = @This(); 34 | const CreateOptions = struct { name: []const u8, enable_priority_inheritance: bool = false }; 35 | 36 | _name: []const u8, 37 | _owner: ?*Task = null, 38 | _prioInherit: bool, 39 | _syncContext: SyncContex = .{}, 40 | 41 | /// Create a mutex object 42 | pub fn create_mutex(options: CreateOptions) Self { 43 | return Self{ 44 | ._name = options.name, 45 | ._prioInherit = options.enable_priority_inheritance, 46 | }; 47 | } 48 | 49 | /// Add the mutex to the OS 50 | pub fn init(self: *Self) Error!void { 51 | if (!self._syncContext._init) { 52 | try Control.add(&self._syncContext); 53 | } 54 | } 55 | 56 | /// Remove the mutex from the OS 57 | pub fn deinit(self: *Self) Error!void { 58 | try Control.remove(&self._syncContext); 59 | } 60 | 61 | pub const AquireOptions = struct { 62 | /// An optional timeout in milliseconds. When set to a non-zero value the task 63 | /// will block for the amount of time specified. If the timeout expires before 64 | /// the mutex is unlocked acquire() will return OsError.TimedOut. When set to 65 | /// zero the task will block until the mutex unlocks. 66 | timeout_ms: u32 = 0, 67 | }; 68 | 69 | /// Locks the mutex and gives the calling task ownership. If the mutex 70 | /// is lock when acquire() is called the running task will be blocked until 71 | /// the mutex is unlocked. Cannot be called from an interrupt. 72 | pub fn acquire(self: *Self, options: AquireOptions) Error!void { 73 | const running_task = try SyncControl.validateCallMajor(); 74 | if (running_task == self._owner) return Error.MutexOwnerAquire; 75 | Arch.criticalStart(); 76 | defer Arch.criticalEnd(); 77 | 78 | if (self._owner) |owner| { 79 | //locked 80 | 81 | //Priority Inheritance 82 | if (self._prioInherit and running_task._priority > owner._priority) { 83 | owner._priority = running_task._priority; 84 | task_control.readyTask(owner); 85 | } 86 | 87 | try Control.blockTask(&self._syncContext, options.timeout_ms); 88 | } else { 89 | //unlocked 90 | self._owner = running_task; 91 | } 92 | } 93 | 94 | /// Unlocks the mutex and removes ownership from the running task. If the 95 | /// running task is not the owner OsError.TaskNotOwner is returned. Cannot be 96 | /// called from an interrupt. 97 | pub fn release(self: *Self) Error!void { 98 | Arch.criticalStart(); 99 | defer Arch.criticalEnd(); 100 | const active_task = try SyncControl.validateCallMajor(); 101 | 102 | if (active_task == self._owner) { 103 | self._owner = self._syncContext._pending.head; 104 | 105 | //Priority Inheritance 106 | if (self._prioInherit and active_task._priority != active_task._basePriority) { 107 | if (task_control.popRunningTask()) |r_task| { 108 | r_task._priority = r_task._basePriority; 109 | task_control.readyTask(r_task); 110 | Arch.criticalEnd(); 111 | Arch.runScheduler(); 112 | } 113 | } else if (self._owner) |head| { 114 | task_control.readyTask(head); 115 | if (head._priority < task_control.running_priority) { 116 | Arch.criticalEnd(); 117 | Arch.runScheduler(); 118 | } 119 | } 120 | } else { 121 | return Error.InvalidMutexOwner; 122 | } 123 | } 124 | 125 | /// Readys the task if it is waiting on the mutex. When the task next 126 | /// runs acquire() will return OsError.Aborted. 127 | /// * task - The task to abort & ready 128 | pub fn abortAcquire(self: *Self, task: *Task) Error!void { 129 | try Control.abort(&self._syncContext, task); 130 | } 131 | }; 132 | -------------------------------------------------------------------------------- /source/synchronization/msg_queue.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const OsTask = @import("../task.zig"); 18 | const OsCore = @import("../os_core.zig"); 19 | const SyncControl = @import("sync_control.zig"); 20 | const ArchInterface = @import("../arch/arch_interface.zig"); 21 | 22 | const Task = OsTask.Task; 23 | const task_control = &OsTask.task_control; 24 | const Arch = ArchInterface.Arch; 25 | 26 | pub const Control = SyncControl.SyncControl; 27 | const SyncContex = SyncControl.SyncContext; 28 | const QError = error{ QueueFull, QueueEmpty, QueueUninitialized }; 29 | const OsError = OsCore.Error; 30 | const Error = QError || OsError; 31 | 32 | /// Options for creating a type of message queue 33 | pub const CreateOptions = struct { 34 | /// The type that the message queue will accept 35 | MsgType: type, 36 | /// The maximum number of messages that this type of queue can contain 37 | buffer_size: usize, 38 | }; 39 | 40 | /// Create a message queue for a specific type 41 | pub fn createMsgQueueType(comptime opt: CreateOptions) type { 42 | return struct { 43 | const Self = @This(); 44 | 45 | _name: []const u8, 46 | _buffer: [opt.buffer_size]opt.MsgType, 47 | _head: usize, 48 | _tail: usize, 49 | _empty: bool = true, 50 | _syncContex: SyncContex = SyncContex{}, 51 | 52 | pub const InitOptions = struct { 53 | /// Messages queue name 54 | name: []const u8, 55 | /// Inital value for all buffer elements 56 | inital_val: opt.MsgType, 57 | }; 58 | 59 | /// Create a message queue 60 | pub fn createQueue(optQ: InitOptions) Self { 61 | return Self{ 62 | ._name = optQ.name, 63 | ._buffer = [_]opt.MsgType{optQ.inital_val} ** opt.buffer_size, 64 | ._head = 0, 65 | ._tail = 0, 66 | }; 67 | } 68 | 69 | /// Add message queue to OS 70 | pub fn init(self: *Self) Error!void { 71 | if (!self._syncContex._init) { 72 | try Control.add(&self._syncContex); 73 | } 74 | } 75 | 76 | pub fn deinit(self: *Self) Error!void { 77 | try Control.remove(&self._syncContext); 78 | } 79 | 80 | pub fn pushMsg(self: *Self, msg: opt.MsgType) Error!void { 81 | _ = try SyncControl.validateCallMinor(); 82 | Arch.criticalStart(); 83 | defer Arch.criticalEnd(); 84 | 85 | if (self._isfull()) return Error.QueueFull; 86 | 87 | //do a mem copy instead of using = ? 88 | self._buffer[self._head] = msg; 89 | if (self._head != opt.buffer_size - 1) { 90 | self._head += 1; 91 | } else { 92 | self._head = 0; 93 | } 94 | 95 | self._empty = false; 96 | 97 | if (self._syncContex._pending.head) |head| { 98 | task_control.readyTask(head); 99 | if (head._priority < task_control.running_priority) { 100 | Arch.criticalEnd(); 101 | Arch.runScheduler(); 102 | } 103 | } 104 | } 105 | 106 | fn popMsg(self: *Self) Error!opt.MsgType { 107 | if (self._empty) return Error.QueueEmpty; 108 | 109 | //do a mem copy instead of using = ? 110 | const msg = self._buffer[self._tail]; 111 | if (self._tail != opt.buffer_size - 1) { 112 | self._tail += 1; 113 | } else { 114 | self._tail = 0; 115 | } 116 | 117 | if (self._head == self._tail) self._empty = true; 118 | 119 | return msg; 120 | } 121 | 122 | const AwaitOptions = struct { 123 | timeout_ms: u32 = 0, 124 | }; 125 | 126 | pub fn awaitMsg(self: *Self, options: AwaitOptions) Error!opt.MsgType { 127 | _ = try SyncControl.validateCallMajor(); 128 | Arch.criticalStart(); 129 | defer Arch.criticalEnd(); 130 | 131 | if (self._empty) { 132 | try Control.blockTask(&self._syncContex, options.timeout_ms); 133 | Arch.criticalStart(); 134 | } 135 | 136 | return self.popMsg(); 137 | } 138 | 139 | pub fn abortAwaitMsg(self: *Self, task: Task) Error!void { 140 | try Control.abort(&self._syncContext, task); 141 | } 142 | 143 | pub fn flush(self: *Self) void { 144 | Arch.criticalStart(); 145 | defer Arch.criticalEnd(); 146 | 147 | self._head = 0; 148 | self._tail = 0; 149 | self._empty = true; 150 | } 151 | 152 | pub fn isEmpty(self: *Self) bool { 153 | Arch.criticalStart(); 154 | defer Arch.criticalEnd(); 155 | 156 | return self._empty; 157 | } 158 | 159 | inline fn _isfull(self: *Self) bool { 160 | return !self._empty and (self._head == self._tail); 161 | } 162 | 163 | pub fn isFull(self: *Self) bool { 164 | Arch.criticalStart(); 165 | defer Arch.criticalEnd(); 166 | 167 | return self._isfull(); 168 | } 169 | }; 170 | } 171 | -------------------------------------------------------------------------------- /source/synchronization/sync_control.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const OsTask = @import("../task.zig"); 18 | const OsCore = @import("../os_core.zig"); 19 | const OsTimer = @import("timer.zig"); 20 | const ArchInterface = @import("../arch/arch_interface.zig"); 21 | 22 | const OsBuildConfig = @import("echoConfig"); 23 | 24 | const Error = OsCore.Error; 25 | const task_control = &OsTask.task_control; 26 | const Task = OsTask.Task; 27 | const TaskQueue = OsTask.TaskQueue; 28 | const Timer = OsTimer.Timer; 29 | 30 | const Arch = ArchInterface.Arch; 31 | 32 | pub const SyncContext = struct { 33 | _next: ?*SyncContext = null, 34 | _prev: ?*SyncContext = null, 35 | _pending: TaskQueue = .{}, 36 | _init: bool = false, 37 | }; 38 | 39 | pub const SyncControl = struct { 40 | const Self = @This(); 41 | var objList: SyncControlList = .{}; 42 | 43 | pub fn add(new: *SyncContext) Error!void { 44 | try objList.add(new); 45 | } 46 | 47 | pub fn remove(detach: *SyncContext) Error!void { 48 | if (detach._pending.head != null) return Error.TaskPendingOnSync; 49 | try objList.remove(detach); 50 | } 51 | 52 | /// Update the timeout of all the task pending on the synchronization object 53 | pub fn updateTimeOut() void { 54 | var sync_objs = objList.list; 55 | while (sync_objs) |sync_obj| { 56 | var opt_task = sync_obj._pending.head; 57 | while (opt_task) |task| { 58 | if (task._timeout > 0) { //tasks wait indefinetly when _timeout is set to 0 59 | task._timeout -= 1; 60 | if (task._timeout == 0) task._SyncContext.timed_out = true; 61 | task_control.readyTask(task); 62 | } 63 | 64 | opt_task = task._to_tail; 65 | } 66 | 67 | sync_objs = sync_obj._next; 68 | } 69 | } 70 | 71 | pub fn blockTask(blocker: *SyncContext, timeout_ms: u32) !void { 72 | if (task_control.popRunningTask()) |task| { 73 | blocker._pending.insertSorted(task); 74 | task._timeout = (timeout_ms * OsCore.getOsConfig().clock_config.os_sys_clock_freq_hz) / 1000; 75 | task._state = OsTask.State.blocked; 76 | Arch.criticalEnd(); 77 | Arch.runScheduler(); 78 | 79 | if (task._SyncContext.timed_out) { 80 | task._SyncContext.timed_out = false; 81 | return Error.TimedOut; 82 | } 83 | if (task._SyncContext.aborted) { 84 | task._SyncContext.aborted = false; 85 | return Error.Aborted; 86 | } 87 | } else { 88 | return Error.RunningTaskNull; 89 | } 90 | } 91 | 92 | pub fn abort(blocker: *SyncContext, task: *Task) Error!void { 93 | const running_task = try validateCallMinor(); 94 | if (!blocker._init) return Error.Uninitialized; 95 | const q = task._queue orelse return Error.TaskNotBlockedBySync; 96 | if (q != &blocker._pending) return Error.TaskNotBlockedBySync; 97 | 98 | Arch.criticalStart(); 99 | defer Arch.criticalEnd(); 100 | task._SyncContext.aborted = true; 101 | task_control.readyTask(task); 102 | if (task._priority < running_task._priority) { 103 | Arch.criticalEnd(); 104 | Arch.runScheduler(); 105 | } 106 | } 107 | }; 108 | 109 | const SyncControlList = struct { 110 | const Self = @This(); 111 | list: ?*SyncContext = null, 112 | 113 | pub fn add(self: *Self, new: *SyncContext) Error!void { 114 | if (new._init) return Error.Reinitialized; 115 | Arch.criticalStart(); 116 | new._next = self.list; 117 | if (self.list) |l| { 118 | l._prev = new; 119 | } 120 | self.list = new; 121 | new._init = true; 122 | Arch.criticalEnd(); 123 | } 124 | 125 | pub fn remove(self: *Self, detach: *SyncContext) Error!void { 126 | if (!detach._init) return Error.Uninitialized; 127 | 128 | Arch.criticalStart(); 129 | defer Arch.criticalEnd(); 130 | 131 | if (self.list == detach) { 132 | self.list = detach._next; 133 | } 134 | 135 | if (detach._next) |next| { 136 | next._prev = detach._prev; 137 | } 138 | 139 | if (detach._prev) |prev| { 140 | prev._next = detach._next; 141 | } 142 | 143 | detach._next = null; 144 | detach._prev = null; 145 | detach._init = false; 146 | } 147 | }; 148 | 149 | pub fn validateCallMajor() Error!*Task { 150 | const running_task = try validateCallMinor(); 151 | 152 | if (OsBuildConfig.enable_software_timers and // 153 | running_task == &OsCore.timer_task and // 154 | OsTimer.getCallbackExecution()) 155 | { 156 | return Error.IllegalTimerTask; 157 | } 158 | 159 | if (running_task._priority == OsTask.IDLE_PRIORITY_LEVEL) return Error.IllegalIdleTask; 160 | if (Arch.interruptActive()) return Error.IllegalInterruptAccess; 161 | return running_task; 162 | } 163 | 164 | pub fn validateCallMinor() Error!*Task { 165 | if (!OsCore.isOsStarted()) return Error.OsOffline; 166 | const running_task = task_control.table[task_control.running_priority].ready_tasks.head orelse return Error.RunningTaskNull; 167 | return running_task; 168 | } 169 | -------------------------------------------------------------------------------- /source/synchronization/event_group.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | const OsTask = @import("../task.zig"); 17 | const OsCore = @import("../os_core.zig"); 18 | const TaskQueue = OsTask.TaskQueue; 19 | const Task = OsTask.Task; 20 | const SyncControl = @import("sync_control.zig"); 21 | const ArchInterface = @import("../arch/arch_interface.zig"); 22 | 23 | const Arch = ArchInterface.Arch; 24 | const Error = OsCore.Error; 25 | const task_control = &OsTask.task_control; 26 | pub const Control = SyncControl.SyncControl; 27 | const SyncContex = SyncControl.SyncContext; 28 | 29 | pub const EventGroup = struct { 30 | const Self = @This(); 31 | const EventContext = OsCore.SyncContext; 32 | 33 | _name: []const u8, 34 | _event: usize, 35 | _syncContex: SyncContex, 36 | 37 | pub const EventTrigger = OsCore.SyncContext.EventTrigger; 38 | 39 | const EventGroupConfig = struct { 40 | //Name of the event group 41 | name: []const u8, 42 | }; 43 | 44 | /// Create an event group object 45 | pub fn createEventGroup(config: EventGroupConfig) Self { 46 | return Self{ 47 | ._name = config.name, 48 | ._event = 0, 49 | ._syncContex = .{}, 50 | }; 51 | } 52 | 53 | /// Add the event group to the OS 54 | pub fn init(self: *Self) Error!void { 55 | if (!self._syncContex._init) { 56 | try Control.add(&self._syncContex); 57 | } 58 | } 59 | 60 | pub fn deinit(self: *Self) Error!void { 61 | try Control.remove(&self._syncContext); 62 | } 63 | 64 | const writeOptions = struct { 65 | /// The event flag 66 | event: usize, 67 | }; 68 | 69 | /// Set the event flag of an event group 70 | pub fn writeEvent(self: *Self, options: writeOptions) Error!void { 71 | const running_task = try SyncControl.validateCallMinor(); 72 | if (!self._syncContex._init) return Error.Uninitialized; 73 | 74 | Arch.criticalStart(); 75 | defer Arch.criticalEnd(); 76 | self._event = options.event; 77 | var pending_task = self._syncContex._pending.head; 78 | var highest_pending_prio: usize = OsTask.IDLE_PRIORITY_LEVEL; 79 | while (true) { 80 | if (pending_task) |task| { 81 | const event_triggered = checkEventTriggered(task._SyncContext, self._event); 82 | if (event_triggered) { 83 | task_control.readyTask(task); 84 | task._SyncContext.triggering_event = self._event; 85 | if (task._priority < highest_pending_prio) { 86 | highest_pending_prio = task._priority; 87 | } 88 | } 89 | pending_task = task._to_tail; 90 | } else { 91 | break; 92 | } 93 | } 94 | 95 | if (highest_pending_prio < running_task._priority) { 96 | Arch.criticalEnd(); 97 | Arch.runScheduler(); 98 | } 99 | } 100 | 101 | /// Read the event flag from the event group 102 | pub fn readEvent(self: *Self) Error!usize { 103 | if (!self._syncContex._init) return Error.Uninitialized; 104 | return self._event; 105 | } 106 | 107 | pub const AwaitEventOptions = struct { 108 | /// The event bits to pend on 109 | event_mask: usize, 110 | /// The state change of the event bits to pend on 111 | PendOn: EventTrigger, 112 | /// The timeout in milliseconds. Set to 0 to pend indefinitely. 113 | timeout_ms: u32 = 0, 114 | }; 115 | 116 | /// Block the running task until the pending event is set. If the pending event 117 | /// is set when awaitEvent is called the running task will not be blocked. 118 | pub fn awaitEvent(self: *Self, options: AwaitEventOptions) Error!usize { 119 | const running_task = try SyncControl.validateCallMajor(); 120 | if (!self._syncContex._init) return Error.Uninitialized; 121 | 122 | running_task._SyncContext.pending_event = options.event_mask; 123 | running_task._SyncContext.trigger_type = options.PendOn; 124 | 125 | Arch.criticalStart(); 126 | defer Arch.criticalEnd(); 127 | 128 | const event_triggered = checkEventTriggered(running_task._SyncContext, self._event); 129 | if (event_triggered) { 130 | running_task._SyncContext.triggering_event = self._event; 131 | } else { 132 | try Control.blockTask(&self._syncContex, options.timeout_ms); 133 | } 134 | 135 | return running_task._SyncContext.triggering_event; 136 | } 137 | 138 | fn checkEventTriggered(eventContext: EventContext, current_event: usize) bool { 139 | return switch (eventContext.trigger_type) { 140 | EventTrigger.all_set => (current_event & eventContext.pending_event) == current_event, 141 | EventTrigger.all_clear => (~current_event & eventContext.pending_event) == current_event, 142 | EventTrigger.any_set => current_event & eventContext.pending_event > 0, 143 | EventTrigger.any_clear => ~current_event & eventContext.pending_event > 0, 144 | }; 145 | } 146 | 147 | pub const AbortOptions = struct { 148 | /// The task to abort pend and ready 149 | task: *Task, 150 | }; 151 | 152 | /// Readys the task if it is waiting on the event group. When the task next 153 | /// runs awaitEvent() will return OsError.Aborted 154 | pub fn abortAwait(self: *Self, options: AbortOptions) Error!void { 155 | try Control.abort(&self._syncContext, options.task); 156 | } 157 | }; 158 | -------------------------------------------------------------------------------- /source/arch/arm-cortex-m/arch.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const std = @import("std"); 18 | const cpu = std.Target.arm.cpu; 19 | const builtin = @import("builtin"); 20 | 21 | const OsTask = @import("../../task.zig"); 22 | const OsCore = @import("../../os_core.zig"); 23 | const Os = @import("../../../os.zig"); 24 | 25 | const V6 = @import("armv6-m.zig"); 26 | const V7 = @import("armv7-m.zig"); 27 | const V8 = @import("armv8-m.zig"); 28 | const V8P1 = @import("armv8.1-m.zig"); 29 | 30 | const core = getCore: { 31 | const cpu_model = builtin.cpu.model.*; 32 | 33 | if (std.meta.eql(cpu_model, cpu.cortex_m0) or // 34 | std.meta.eql(cpu_model, cpu.cortex_m0plus)) 35 | { 36 | break :getCore V6; 37 | } else if (std.meta.eql(cpu_model, cpu.cortex_m3) or // 38 | std.meta.eql(cpu_model, cpu.cortex_m4) or // 39 | std.meta.eql(cpu_model, cpu.cortex_m7)) 40 | { 41 | break :getCore V7; 42 | } else { 43 | @compileError("Unsupported architecture selected."); 44 | } 45 | }; 46 | 47 | const task_ctrl = &OsTask.task_control; 48 | const Task = OsTask.Task; 49 | 50 | pub const Self = @This(); 51 | 52 | ///////////////////////////////////////////////////////// 53 | // Architecture specific Function Implemntations // 54 | /////////////////////////////////////////////////////// 55 | pub const minStackSize = core.minStackSize; 56 | 57 | const Error = error{ 58 | SysTickAddressInvalid, 59 | SvcAddressInvalid, 60 | PendSvAddressInvalid, 61 | }; 62 | 63 | //NVIC table offsets 64 | const systick_offset = 0x3c; 65 | const svc_offset = 0x2c; 66 | const pendsv_offset = 0x38; 67 | 68 | pub fn coreInit(clock_config: *const OsCore.ClockConfig) void { 69 | const systick_address: u32 = @intFromPtr(&SysTick_Handler); 70 | const svc_address: u32 = @intFromPtr(&SVC_Handler); 71 | const pendsv_address: u32 = @intFromPtr(&PendSV_Handler); 72 | 73 | const vtor_reg: *u32 = @ptrFromInt(core.VTOR_ADDRESS); 74 | const vector_table_address = vtor_reg.*; 75 | 76 | //Addresses of the NVIC table that store exception handler pointers. 77 | const nvic_systick: *u32 = @ptrFromInt(vector_table_address + systick_offset); 78 | const nvic_svc: *u32 = @ptrFromInt(vector_table_address + svc_offset); 79 | const nvic_pendsv: *u32 = @ptrFromInt(vector_table_address + pendsv_offset); 80 | 81 | //Exception Handler addresses stored in the NVIC table. 82 | const nvic_systick_address = nvic_systick.*; 83 | const nvic_svc_address = nvic_svc.*; 84 | const nvic_pendsv_address = nvic_pendsv.*; 85 | 86 | //Panic if exceptions are not setup correctly 87 | if (systick_address != nvic_systick_address) { 88 | @panic("SysTick Handler address in NVIC table does not match SysTick_Handler() address.\n"); 89 | } 90 | 91 | if (svc_address != nvic_svc_address) { 92 | @panic("SVC Handler address in NVIC table does not match SVC_Handler() address.\n"); 93 | } 94 | 95 | if (pendsv_address != nvic_pendsv_address) { 96 | @panic("PendSV Handler address in NVIC table does not match PendSV_Handler() address.\n"); 97 | } 98 | 99 | SHPR3.PRI_PENDSV = core.LOWEST_PRIO_MSK; //Set the pendsv to the lowest priority to tail chain ISRs 100 | SHPR3.PRI_SYSTICK = ~core.LOWEST_PRIO_MSK; //Set sysTick to the highest priority. 101 | 102 | //Set SysTick reload value 103 | const ticks: u32 = (clock_config.cpu_clock_freq_hz / clock_config.os_sys_clock_freq_hz) - 1; 104 | SYST_RVR.RELOAD = @intCast(ticks); 105 | 106 | //Enable SysTick counter & interrupt 107 | SYST_CSR.CLKSOURCE = 1; //TODO: Make this configurable some how 108 | SYST_CSR.ENABLE = true; 109 | SYST_CSR.TICKINT = true; 110 | } 111 | 112 | pub fn initStack(task: *Task) void { 113 | task._stack_ptr = @intFromPtr(&task._stack.ptr[task._stack.len - minStackSize]); 114 | task._stack.ptr[task._stack.len - 1] = 0x1 << 24; // xPSR 115 | task._stack.ptr[task._stack.len - 2] = @intFromPtr(&OsTask.taskTopRoutine); // PC 116 | task._stack.ptr[task._stack.len - 3] = 0x14141414; // LR (R14) 117 | task._stack.ptr[task._stack.len - 4] = 0x12121212; // R12 118 | task._stack.ptr[task._stack.len - 5] = 0x03030303; // R3 119 | task._stack.ptr[task._stack.len - 6] = 0x02020202; // R2 120 | task._stack.ptr[task._stack.len - 7] = 0x01010101; // R1 121 | task._stack.ptr[task._stack.len - 8] = 0x00000000; // R0 122 | task._stack.ptr[task._stack.len - 9] = 0xFFFFFFFD; // EXEC_RETURN (LR) 123 | 124 | //These registers are push/poped via context switch code 125 | task._stack.ptr[task._stack.len - 10] = 0x11111111; // R11 126 | task._stack.ptr[task._stack.len - 11] = 0x10101010; // R10 127 | task._stack.ptr[task._stack.len - 12] = 0x09090909; // R9 128 | task._stack.ptr[task._stack.len - 13] = 0x08080808; // R8 129 | task._stack.ptr[task._stack.len - 14] = 0x07070707; // R7 130 | task._stack.ptr[task._stack.len - 14] = 0x06060606; // R6 131 | task._stack.ptr[task._stack.len - 14] = 0x05050505; // R5 132 | task._stack.ptr[task._stack.len - 14] = 0x04040404; // R4 133 | } 134 | 135 | pub fn interruptActive() bool { 136 | return ICSR.VECTACTIVE > 0; 137 | } 138 | 139 | ///Enable Interrupts 140 | pub inline fn criticalEnd() void { 141 | asm volatile ("CPSIE I"); 142 | } 143 | 144 | ///Disable Interrupts 145 | pub inline fn criticalStart() void { 146 | asm volatile ("CPSID I"); 147 | } 148 | 149 | pub inline fn runScheduler() void { 150 | asm volatile ("SVC #0"); 151 | } 152 | 153 | pub inline fn runContextSwitch() void { 154 | OsTask.TaskControl.next_task._state = .running; 155 | ICSR.PENDSVSET = true; 156 | } 157 | 158 | pub inline fn startOs() void { 159 | // firstContextSwitch(); 160 | } 161 | 162 | pub inline fn isDebugAttached() bool { 163 | return DHCSR.C_DEBUGEN; 164 | } 165 | 166 | ///////////////////////////////////////////// 167 | // Exception Handlers // 168 | /////////////////////////////////////////// 169 | 170 | export fn SysTick_Handler() void { 171 | criticalStart(); 172 | OsCore.OsTick(); 173 | criticalEnd(); 174 | } 175 | 176 | export fn SVC_Handler() void { 177 | criticalStart(); 178 | OsCore.schedule(); 179 | criticalEnd(); 180 | } 181 | 182 | extern fn PendSV_Handler() void; 183 | 184 | ///////////////////////////////////////////// 185 | // System Control Registers // 186 | /////////////////////////////////////////// 187 | const ICSR: *volatile core.ICSR_REG = @ptrFromInt(core.ICSR_ADDRESS); 188 | const SHPR2: *volatile core.SHPR2_REG = @ptrFromInt(core.SHPR2_ADDRESS); 189 | const SHPR3: *volatile core.SHPR3_REG = @ptrFromInt(core.SHPR3_ADDRESS); 190 | const DHCSR: *volatile core.DHCSR_REG = @ptrFromInt(core.DHCSR_ADDRESS); 191 | const SYST_CSR: *volatile core.SYST_CSR_REG = @ptrFromInt(core.SYST_CSR_ADDRESS); 192 | const SYST_RVR: *volatile core.SYST_RVR_REG = @ptrFromInt(core.SYST_RVR_ADDRESS); 193 | -------------------------------------------------------------------------------- /source/arch/arm-cortex-m/armv6-m.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | pub const minStackSize = 16; 18 | pub const LOWEST_PRIO_MSK: u2 = 0x3; 19 | 20 | ///////////////////////////////////////////// 21 | // System Control Register Addresses // 22 | /////////////////////////////////////////// 23 | pub const ACTLR_ADDRESS: u32 = 0xE000E008; // Auxiliary Control Register 24 | 25 | //SysTic Timer is optional on armv6-m 26 | pub const SYST_CSR_ADDRESS: u32 = 0xE000E010; // SysTick Control and Status Register 27 | pub const SYST_RVR_ADDRESS: u32 = 0xE000E014; // SysTick Reload Value Register 28 | pub const SYST_CVR_ADDRESS: u32 = 0xE000E018; // SysTick Current Value Register 29 | pub const SYST_CALIB_ADDRESS: u32 = 0xE000E01C; // SysTick Calibration Value Register (Implementation specific) 30 | 31 | pub const CPUID_ADDRESS: u32 = 0xE000ED00; // CPUID Base Register 32 | pub const ICSR_ADDRESS: u32 = 0xE000ED04; // Interrupt Control and State Register (RW or RO) 33 | pub const VTOR_ADDRESS: u32 = 0xE000ED08; // Vector Table Offset Register 34 | pub const AIRCR_ADDRESS: u32 = 0xE000ED0C; // Application Interrupt and Reset Control Register 35 | pub const SCR_ADDRESS: u32 = 0xE000ED10; // System Control Register 36 | pub const CCR_ADDRESS: u32 = 0xE000ED14; // Configuration and Control Register 37 | pub const SHPR2_ADDRESS: u32 = 0xE000ED1C; // System Handler Priority Register 2 38 | pub const SHPR3_ADDRESS: u32 = 0xE000ED20; // System Handler Priority Register 3 39 | pub const SHCSR_ADDRESS: u32 = 0xE000ED24; // System Handler Control and State Register 40 | pub const DFSR_ADDRESS: u32 = 0xE000ED30; // Debug Fault Status Register 41 | 42 | //System Debug Registers 43 | pub const DHCSR_ADDRESS: u32 = 0xE000EDF0; // Halting Control and Status Register 44 | pub const DCRSR_ADDRESS: u32 = 0xE000EDF4; // Core Register Selector Register 45 | pub const DCRDR_ADDRESS: u32 = 0xE000EDF8; // Core Register Data Register 46 | pub const DEMCR_ADDRESS: u32 = 0xE000EDFC; // Exception and Monitor Control Register 47 | 48 | ///////////////////////////////////////////// 49 | // System Control Register Structs // 50 | /////////////////////////////////////////// 51 | 52 | ///SysTick Control and Status Register 53 | pub const SYST_CSR_REG = packed struct { 54 | /// RW - Indicates if the SysTick counter is enabled 55 | ENABLE: bool, 56 | /// RW - Indicates if counting to 0 causes the SysTick exception to pend. 57 | /// False: Count to 0 does not affect the SysTick exception status 58 | /// True: Count to 0 changes the SysTick exception status to pending. 59 | TICKINT: bool, 60 | /// RW - Indicates the SysTick clock source: 61 | /// 0 = SysTick uses the IMPLEMENTATION DEFINED external reference clock. 62 | /// 1 = SysTick uses the processor clock. 63 | /// If no external clock is provided, this bit reads as 1 and ignores writes. 64 | CLKSOURCE: u1, 65 | /// Reserved 66 | RESERVED_3_15: u13, 67 | /// Indicates whether the counter has counted to 0 since the last read of this register 68 | /// False = Timer has not counted to 0. 69 | /// True = Timer has counted to 0. 70 | COUNTFLAG: bool, 71 | RESERVED_17_31: u15, 72 | comptime { 73 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 74 | } 75 | }; 76 | 77 | ///SysTick Reload Value Register 78 | const SYST_RVR_REG = packed struct { 79 | /// RW - The value to load into the SYST_CVR when the counter reaches 0. 80 | RELOAD: u24, 81 | ///Reserved 82 | RESERVED_24_31: u8, 83 | 84 | comptime { 85 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 86 | } 87 | }; 88 | 89 | /// Vector Table Offset Register 90 | pub const VTOR_REG = packed struct { 91 | /// Reserved 92 | RESERVED_0_6: u7, 93 | /// RW - Bits 31-7 of the vector table address 94 | TBLOFF: u25, 95 | 96 | comptime { 97 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 98 | } 99 | }; 100 | 101 | ///Interrupt Control State Register 102 | pub const ICSR_REG = packed struct { 103 | /// RO - Current executing exception's number. A value of 0 = Thread mode 104 | VECTACTIVE: u9, 105 | RESERVED_9_11: u3, 106 | /// RO - Highest priority pending enabled execption's number 107 | VECTPENDING: u9, 108 | RESERVED_21: u1, 109 | /// RO - Extneral interrrupt pending 110 | ISRPENDING: bool, 111 | /// RO - Service pending execption on on exit from debug halt 112 | ISRPREEMPT: bool, 113 | RESERVED_24: u1, 114 | /// WO - 0 = No effect; 1 = Clear SysTick pending status 115 | PENDSTCLR: bool, 116 | /// WR - Write: sets SysTick exception pending. Reads: indicates state of SysTick exception 117 | PENDSTSET: bool, 118 | /// WO - 0 = No effect; 1 = Clear PendSV pending status 119 | PENDSVCLR: bool, 120 | /// WR - Write: sets PendSV exception pending. Reads: indicates state of PendSV exception 121 | PENDSVSET: bool, 122 | RESERVED_29_30: u2, 123 | /// RW - Write: sets NMI exception pending. Reads: indicates state of NMI exception 124 | NMIPENDSET: bool, 125 | 126 | comptime { 127 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 128 | } 129 | }; 130 | 131 | ///System Handler Priority Register 2 132 | pub const SHPR2_REG = packed struct { 133 | RESERVED_0_7: u8, 134 | RESERVED_8_15: u8, 135 | RESERVED_16_23: u8, 136 | /// RW - Systme Handler 11 Priority: Debug Monitor 137 | PRI_SVC: u8, 138 | 139 | comptime { 140 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 141 | } 142 | }; 143 | 144 | ///System Handler Priority Register 3 145 | pub const SHPR3_REG = packed struct { 146 | RESERVED_0_21: u22, 147 | /// RW - System Handler 14 Priority: Pend_SV 148 | PRI_PENDSV: u2, 149 | RESERVED_24_29: u6, 150 | /// RW - System Handler 15 Priority: SystTick 151 | PRI_SYSTICK: u2, 152 | 153 | comptime { 154 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 155 | } 156 | }; 157 | 158 | ///Debug Halting Control and Status Register 159 | pub const DHCSR_REG = packed struct { 160 | /// RO - 0 = No debugger attached; 1 = Debugger attached 161 | C_DEBUGEN: bool, 162 | /// RW - 0 = Request processor run; 1 = Request processor halt 163 | C_HALT: bool, 164 | /// RW - 0 = Single step disable; 1 = Single step enabled 165 | C_STEP: bool, 166 | /// RW - 0 = Do not Mask; 1 = Mask PendSV, SysTick, & external interrupts 167 | C_MASKINTS: bool, 168 | RESERVED_4_15: u12, 169 | ///Debug key. Write 0xA05F to this field to enable write accesses to bits[15:0] 170 | DBGKEY: u16, 171 | 172 | comptime { 173 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 174 | } 175 | }; 176 | -------------------------------------------------------------------------------- /source/synchronization/timer.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const OsTask = @import("../task.zig"); 18 | const OsCore = @import("../os_core.zig"); 19 | const SyncControl = @import("sync_control.zig"); 20 | const OsSemaphore = @import("semaphore.zig"); 21 | const ArchInterface = @import("../arch/arch_interface.zig"); 22 | const Semaphore = OsSemaphore.Semaphore; 23 | const Arch = ArchInterface.Arch; 24 | 25 | const Task = OsTask.Task; 26 | 27 | pub const State = enum { running, expired, idle }; 28 | 29 | pub const CreateOptions = struct { 30 | name: []const u8, 31 | callback: *const fn () void, 32 | }; 33 | 34 | pub const SetOptions = struct { 35 | timeout_ms: u32, 36 | autoreload: bool = false, 37 | callback: ?*const fn () void = null, 38 | }; 39 | 40 | const CallbackArgs = struct {}; 41 | 42 | pub const Timer = struct { 43 | const Self = @This(); 44 | 45 | _name: []const u8, 46 | _timeout_ms: u32 = 0, 47 | _running_time_ms: u32 = 0, 48 | _callback: *const fn () void, 49 | _state: State = State.idle, 50 | _autoreload: bool = false, 51 | _next: ?*Timer = null, 52 | _prev: ?*Timer = null, 53 | _init: bool = false, 54 | 55 | pub fn create(options: CreateOptions) Self { 56 | return Self{ 57 | ._name = options.name, 58 | ._callback = options.callback, 59 | ._state = State.idle, 60 | }; 61 | } 62 | 63 | pub fn set(self: *Self, options: SetOptions) Error!void { 64 | if (self._state != State.idle) return Error.TimerRunning; 65 | 66 | self._timeout_ms = options.timeout_ms; 67 | self._running_time_ms = options.timeout_ms; 68 | self._autoreload = options.autoreload; 69 | self._callback = options.callback orelse return; 70 | } 71 | 72 | pub fn start(self: *Self) Error!void { 73 | if (self._timeout_ms == 0) return Error.TimeoutCannotBeZero; 74 | if (self._state != State.idle) return Error.TimerRunning; 75 | 76 | try TimerControl.start(self); 77 | } 78 | 79 | pub fn restart(self: *Self) Error!void { 80 | if (self._timeout_ms == 0) return Error.TimeoutCannotBeZero; 81 | self._running_time_ms = self._timeout_ms; 82 | } 83 | 84 | pub fn cancel(self: *Self) Error!void { 85 | if (self._state != State.running) return Error.TimerNotRunning; 86 | try TimerControl.stop(self); 87 | } 88 | 89 | pub fn getRemainingTime(self: *Self) u32 { 90 | return self._running_time_ms; 91 | } 92 | 93 | pub fn getTimerState(self: *Self) State { 94 | return self._state; 95 | } 96 | }; 97 | 98 | pub var timer_sem = Semaphore.create_semaphore(.{ .name = "Timer Semaphore", .inital_value = 0 }); 99 | 100 | var callback_execution = false; 101 | pub fn timerSubroutine() !void { 102 | while (true) { 103 | callback_execution = false; 104 | try timer_sem.wait(.{}); 105 | callback_execution = true; 106 | 107 | var timer = TimerControl.getExpiredList() orelse continue; 108 | timer._callback(); 109 | if (timer._autoreload) { 110 | timer._running_time_ms = timer._timeout_ms; 111 | try TimerControl.restart(timer); 112 | } else { 113 | try TimerControl.stop(timer); 114 | } 115 | } 116 | } 117 | 118 | pub fn getCallbackExecution() bool { 119 | return callback_execution; 120 | } 121 | 122 | const Error = TmrError || OsError; 123 | 124 | const TmrError = error{ 125 | TimeoutCannotBeZero, 126 | TimerRunning, 127 | TimerNotRunning, 128 | }; 129 | 130 | const OsError = OsCore.Error; 131 | 132 | pub const TimerControl = struct { 133 | const Self = @This(); 134 | var _runningList: TimerControlList = .{}; 135 | var _expiredList: TimerControlList = .{}; 136 | 137 | pub fn start(timer: *Timer) Error!void { 138 | try _runningList.add(timer); 139 | timer._state = State.running; 140 | } 141 | 142 | pub fn stop(timer: *Timer) Error!void { 143 | try _runningList.remove(timer); 144 | timer._state = State.idle; 145 | } 146 | 147 | pub fn restart(timer: *Timer) Error!void { 148 | try _expiredList.remove(timer); 149 | try _runningList.add(timer); 150 | timer._state = State.running; 151 | } 152 | 153 | pub fn expired(timer: *Timer) Error!void { 154 | try _runningList.remove(timer); 155 | try _expiredList.add(timer); 156 | timer._state = State.expired; 157 | } 158 | 159 | pub fn getExpiredList() ?*Timer { 160 | return _expiredList.list; 161 | } 162 | 163 | /// Update the timeout of all running timers 164 | pub fn updateTimeOut() void { 165 | var running_timer = _runningList.list; 166 | while (running_timer) |timer| { 167 | timer._running_time_ms -= 1; 168 | if (timer._running_time_ms == 0) { 169 | expired(timer) catch { 170 | @panic("Unable to mark timer as expired"); 171 | }; 172 | 173 | timer_sem.post(.{ .runScheduler = false }) catch { 174 | @panic("Unable to post timer semaphore."); 175 | }; 176 | } 177 | 178 | running_timer = timer._next; 179 | } 180 | } 181 | }; 182 | 183 | const TimerControlList = struct { 184 | const Self = @This(); 185 | list: ?*Timer = null, 186 | 187 | //Sorted insert for timers based on timeout 188 | pub fn add(self: *Self, new: *Timer) Error!void { 189 | if (new._init) return Error.Reinitialized; 190 | Arch.criticalStart(); 191 | defer Arch.criticalEnd(); 192 | 193 | if (self.list == null) { 194 | self.list = new; 195 | } else { 196 | var timer = self.list; 197 | while (timer) |tmr| { 198 | if (new._running_time_ms <= tmr._running_time_ms) { 199 | //insert 200 | new._next = tmr; 201 | new._prev = tmr._prev; 202 | if (tmr._prev) |prev| { 203 | prev._next = new; 204 | } 205 | 206 | tmr._prev = new; 207 | if (timer == self.list) { 208 | self.list = new; 209 | } 210 | break; 211 | } else if (tmr._next == null) { 212 | //insert at end 213 | tmr._next = new; 214 | new._prev = tmr; 215 | break; 216 | } else { 217 | timer = tmr._next; 218 | } 219 | } 220 | } 221 | new._init = true; 222 | } 223 | 224 | pub fn remove(self: *Self, detach: *Timer) Error!void { 225 | if (!detach._init) return Error.Uninitialized; 226 | 227 | Arch.criticalStart(); 228 | defer Arch.criticalEnd(); 229 | 230 | if (self.list == detach) { 231 | self.list = detach._next; 232 | } 233 | 234 | if (detach._next) |next| { 235 | next._prev = detach._prev; 236 | } 237 | 238 | if (detach._prev) |prev| { 239 | prev._next = detach._next; 240 | } 241 | 242 | detach._next = null; 243 | detach._prev = null; 244 | detach._init = false; 245 | } 246 | }; 247 | -------------------------------------------------------------------------------- /source/arch/arm-cortex-m/armv7-m.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | pub const minStackSize = 17; 18 | pub const LOWEST_PRIO_MSK: u8 = 0xFF; 19 | 20 | ///////////////////////////////////////////// 21 | // System Control Register Addresses // 22 | /////////////////////////////////////////// 23 | pub const ACTLR_ADDRESS: u32 = 0xE000E008; // Auxiliary Control Register 24 | pub const SYST_CSR_ADDRESS: u32 = 0xE000E010; // SysTick Control and Status Register 25 | pub const SYST_RVR_ADDRESS: u32 = 0xE000E014; // SysTick Reload Value Register 26 | pub const SYST_CVR_ADDRESS: u32 = 0xE000E018; // SysTick Current Value Register 27 | pub const SYST_CALIB_ADDRESS: u32 = 0xE000E01C; // SysTick Calibration Value Register (Implementation specific) 28 | 29 | pub const CPUID_ADDRESS: u32 = 0xE000ED00; // CPUID Base Register 30 | pub const ICSR_ADDRESS: u32 = 0xE000ED04; // Interrupt Control and State Register (RW or RO) 31 | pub const VTOR_ADDRESS: u32 = 0xE000ED08; // Vector Table Offset Register 32 | pub const AIRCR_ADDRESS: u32 = 0xE000ED0C; // Application Interrupt and Reset Control Register 33 | pub const SCR_ADDRESS: u32 = 0xE000ED10; // System Control Register 34 | pub const CCR_ADDRESS: u32 = 0xE000ED14; // Configuration and Control Register 35 | pub const SHPR1_ADDRESS: u32 = 0xE000ED18; // System Handler Priority Register 1 36 | pub const SHPR2_ADDRESS: u32 = 0xE000ED1C; // System Handler Priority Register 2 37 | pub const SHPR3_ADDRESS: u32 = 0xE000ED20; // System Handler Priority Register 3 38 | pub const SHCSR_ADDRESS: u32 = 0xE000ED24; // System Handler Control and State Register 39 | pub const CFSR_ADDRESS: u32 = 0xE000ED28; // Configurable Fault Status Registers 40 | pub const HFSR_ADDRESS: u32 = 0xE000ED2C; // HardFault Status Register 41 | pub const DFSR_ADDRESS: u32 = 0xE000ED30; // Debug Fault Status Register 42 | pub const MMFAR_ADDRESS: u32 = 0xE000ED34; // MemManage Address Register 43 | pub const BFAR_ADDRESS: u32 = 0xE000ED38; // BusFault Address Register 44 | pub const AFSR_ADDRESS: u32 = 0xE000ED3C; // Auxiliary Fault Status Register 45 | pub const ID_PFR0_ADDRESS: u32 = 0xE000ED40; // Processor Feature Register 0 46 | pub const ID_PFR1_ADDRESS: u32 = 0xE000ED44; // Processor Feature Register 1 47 | pub const ID_DFR0_ADDRESS: u32 = 0xE000ED48; // Debug Features Register 0 48 | pub const ID_AFR0_ADDRESS: u32 = 0xE000ED4C; // Auxiliary Features Register 0 49 | pub const ID_MMFR0_ADDRESS: u32 = 0xE000ED50; // Memory Model Feature Register 0 50 | pub const ID_MMFR1_ADDRESS: u32 = 0xE000ED54; // Memory Model Feature Register 1 51 | pub const ID_MMFR2_ADDRESS: u32 = 0xE000ED58; // Memory Model Feature Register 2 52 | pub const ID_MMFR3_ADDRESS: u32 = 0xE000ED5C; // Memory Model Feature Register 3 53 | pub const ID_ISAR0_ADDRESS: u32 = 0xE000ED60; // Instruction Set Attributes Register 0 54 | pub const ID_ISAR1_ADDRESS: u32 = 0xE000ED64; // Instruction Set Attributes Register 1 55 | pub const ID_ISAR2_ADDRESS: u32 = 0xE000ED68; // Instruction Set Attributes Register 2 56 | pub const ID_ISAR3_ADDRESS: u32 = 0xE000ED6C; // Instruction Set Attributes Register 3 57 | pub const ID_ISAR4_ADDRESS: u32 = 0xE000ED70; // Instruction Set Attributes Register 4 58 | pub const CPACR_ADDRESS: u32 = 0xE000ED88; // Coprocessor Access Control Register 59 | pub const STIR_ADDRESS: u32 = 0xE000EF00; // Software Triggered Interrupt Register 60 | 61 | //System Debug Registers 62 | pub const DHCSR_ADDRESS: u32 = 0xE000EDF0; // Halting Control and Status Register 63 | pub const DCRSR_ADDRESS: u32 = 0xE000EDF4; // Core Register Selector Register 64 | pub const DCRDR_ADDRESS: u32 = 0xE000EDF8; // Core Register Data Register 65 | pub const DEMCR_ADDRESS: u32 = 0xE000EDFC; // Exception and Monitor Control Register 66 | 67 | ///////////////////////////////////////////// 68 | // System Control Register Structs // 69 | /////////////////////////////////////////// 70 | 71 | ///SysTick Control and Status Register 72 | pub const SYST_CSR_REG = packed struct { 73 | /// RW - Indicates if the SysTick counter is enabled 74 | ENABLE: bool, 75 | /// RW - Indicates if counting to 0 causes the SysTick exception to pend. 76 | /// False: Count to 0 does not affect the SysTick exception status 77 | /// True: Count to 0 changes the SysTick exception status to pending. 78 | TICKINT: bool, 79 | /// RW - Indicates the SysTick clock source: 80 | /// 0 = SysTick uses the IMPLEMENTATION DEFINED external reference clock. 81 | /// 1 = SysTick uses the processor clock. 82 | /// If no external clock is provided, this bit reads as 1 and ignores writes. 83 | CLKSOURCE: u1, 84 | /// Reserved 85 | RESERVED_3_15: u13, 86 | /// Indicates whether the counter has counted to 0 since the last read of this register 87 | /// False = Timer has not counted to 0. 88 | /// True = Timer has counted to 0. 89 | COUNTFLAG: bool, 90 | RESERVED_17_31: u15, 91 | 92 | comptime { 93 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 94 | } 95 | }; 96 | 97 | ///SysTick Reload Value Register 98 | pub const SYST_RVR_REG = packed struct { 99 | /// RW - The value to load into the SYST_CVR when the counter reaches 0. 100 | RELOAD: u24, 101 | ///Reserved 102 | RESERVED_24_31: u8, 103 | 104 | comptime { 105 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 106 | } 107 | }; 108 | 109 | /// Vector Table Offset Register 110 | pub const VTOR_REG = packed struct { 111 | /// Reserved 112 | RESERVED_0_6: u7, 113 | /// RW - Bits 31-7 of the vector table address 114 | TBLOFF: u25, 115 | 116 | comptime { 117 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 118 | } 119 | }; 120 | 121 | ///Interrupt Control State Register 122 | pub const ICSR_REG = packed struct { 123 | /// RO - Current executing exception's number. A value of 0 = Thread mode 124 | VECTACTIVE: u9, 125 | RESERVED_9_10: u2, 126 | /// RO - Handler mode: Indicates if there is active exception other than the one indicated by IPSR; Thread mode: N/A 127 | RETTOBASE: bool, 128 | /// RO - Highest priority pending enabled execption's number 129 | VECTPENDING: u9, 130 | RESERVED_21: u1, 131 | /// RO - Extneral interrrupt pending 132 | ISRPENDING: bool, 133 | /// RO - Service pending execption on on exit from debug halt 134 | ISRPREEMPT: bool, 135 | RESERVED_24: u1, 136 | /// WO - 0 = No effect; 1 = Clear SysTick pending status 137 | PENDSTCLR: bool, 138 | /// WR - Write: sets SysTick exception pending. Reads: indicates state of SysTick exception 139 | PENDSTSET: bool, 140 | /// WO - 0 = No effect; 1 = Clear PendSV pending status 141 | PENDSVCLR: bool, 142 | /// WR - Write: sets PendSV exception pending. Reads: indicates state of PendSV exception 143 | PENDSVSET: bool, 144 | RESERVED_29_30: u2, 145 | /// RW - Write: sets NMI exception pending. Reads: indicates state of NMI exception 146 | NMIPENDSET: bool, 147 | 148 | comptime { 149 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 150 | } 151 | }; 152 | 153 | ///System Handler Priority Register 2 154 | pub const SHPR2_REG = packed struct { 155 | RESERVED_0_7: u8, 156 | RESERVED_8_15: u8, 157 | RESERVED_16_23: u8, 158 | /// RW - Systme Handler 11 Priority: Debug Monitor 159 | PRI_SVC: u8, 160 | 161 | comptime { 162 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 163 | } 164 | }; 165 | 166 | ///System Handler Priority Register 3 167 | pub const SHPR3_REG = packed struct { 168 | /// RW - Systme Handler 12 Priority: Debug Monitor 169 | PRI_DEBUG_MON: u8, 170 | RESERVED: u8, 171 | /// RW - Systme Handler 14 Priority: Pend_SV 172 | PRI_PENDSV: u8, 173 | /// RW - Systme Handler 12 Priority: SysTick 174 | PRI_SYSTICK: u8, 175 | 176 | comptime { 177 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 178 | } 179 | }; 180 | 181 | /// 182 | pub const DEMCR_REG = packed struct { 183 | /// RW - Enable Reset Vector Catch. This will cause a local reset to halt the system. 184 | VC_CORERESET: bool, 185 | RESERVED_1_3: u3, 186 | /// RW - Enable halting debug trap: MemManage Execption 187 | VC_MMERR: bool, 188 | /// RW - Enable halting debug trap: UsageFault caused by coprocessor 189 | VC_NOCERR: bool, 190 | /// RW - Enable halting debug trap: UsageFault caused by checking error 191 | VC_CHKERR: bool, 192 | /// RW - Enable halting debug trap: UsageFault caused by state information error 193 | VC_STATERR: bool, 194 | /// RW - Enable halting debug trap: BusFault Execption 195 | VC_BUSERR: bool, 196 | /// RW - Enable halting debug trap: Exception entry or return 197 | VC_INTER: bool, 198 | /// RW - Enable halting debug trap: HardFault Execption 199 | VC_HARDERR: bool, 200 | RESERVED_11_15: u5, 201 | /// RW - Enable DebugMonitor execption 202 | MON_EN: bool, 203 | /// RW - Write: 0 = Clear; 1 Set; Pending state of DebugMonitor execption 204 | MON_PEND: bool, 205 | /// RW - When MON_EN == 1; Write: 0 = Do not step; 1 = Step the processor 206 | MON_STEP: bool, 207 | ///DebugMonitor semaphore bit. The processor does not use this bit. The monitor software defines the meaning and use of this bit. 208 | MON_REQ: bool, 209 | RESERVED_20_23: u4, 210 | /// RW - Write: 0 = DWT and ITM units disabled; 1 = DWT and ITM units enabled 211 | TRCENA: bool, 212 | RESERVED_25_31: u7, 213 | 214 | comptime { 215 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 216 | } 217 | }; 218 | 219 | ///Debug Halting Control and Status Register 220 | pub const DHCSR_REG = packed struct { 221 | /// RO - 0 = No debugger attached; 1 = Debugger attached 222 | C_DEBUGEN: bool, 223 | /// RW - 224 | C_HALT: bool, 225 | /// 226 | C_STEP: bool, 227 | /// 228 | C_MASKINTS: bool, 229 | RESERVED_4: bool, 230 | /// 231 | C_SNAPSTALL: bool, 232 | RESERVED_6_15: u10, 233 | ///Debug key. Write 0xA05F to this field to enable write accesses to bits[15:0] 234 | DBGKEY: u16, 235 | 236 | comptime { 237 | if (@bitSizeOf(@This()) != @bitSizeOf(u32)) @compileError("Register struct must be must be 32 bits"); 238 | } 239 | }; 240 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /source/os_core.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const OsTask = @import("task.zig"); 18 | const Mutex = @import("synchronization/mutex.zig"); 19 | const Semaphore = @import("synchronization/semaphore.zig"); 20 | const EventGroup = @import("synchronization/event_group.zig"); 21 | const ArchInterface = @import("arch/arch_interface.zig"); 22 | const OsSyncControl = @import("synchronization/sync_control.zig"); 23 | const OsTimer = @import("synchronization/timer.zig"); 24 | const builtin = @import("builtin"); 25 | 26 | const OsBuildConfig = @import("echoConfig"); 27 | 28 | pub const Task = OsTask.Task; 29 | 30 | const Arch = ArchInterface.Arch; 31 | const task_ctrl = &OsTask.task_control; 32 | const SyncControl = OsSyncControl.SyncControl; 33 | const TimerControl = OsTimer.TimerControl; 34 | 35 | pub const DEFAULT_IDLE_TASK_SIZE = Arch.minStackSize; 36 | 37 | var os_config: OsConfig = undefined; 38 | 39 | pub fn getOsConfig() OsConfig { 40 | return os_config; 41 | } 42 | 43 | pub fn setOsConfig(comptime config: OsConfig) void { 44 | if (!os_started) { 45 | os_config = config; 46 | } 47 | } 48 | 49 | fn idle_subroutine() !void { 50 | while (true) {} 51 | } 52 | 53 | pub const OsConfig = struct { 54 | /// OS & CPU clock Configuration 55 | clock_config: ClockConfig, 56 | /// Idle Task Configuration 57 | idle_task_config: IdleTaskConfig = .{}, 58 | /// Function to execute at the beginning of the sysTick interrupt; 59 | os_tick_callback: ?*const fn () void = null, 60 | /// Software Timer Configuration 61 | timer_config: ?TimerConfig = null, 62 | }; 63 | 64 | pub const IdleTaskConfig = struct { 65 | /// Subroutine executed by the idle task. Replaces the default idle task. This subroutine cannot be suspended or blocked; 66 | idle_task_subroutine: *const fn () anyerror!void = &idle_subroutine, 67 | /// Number of words in the idle task stack. Note: if idle_task_subroutine is provided idle_stack_size must be 68 | /// larger than DEFAULT_IDLE_TASK_SIZE; 69 | idle_stack_size: usize = DEFAULT_IDLE_TASK_SIZE, 70 | }; 71 | 72 | pub const ClockConfig = struct { 73 | /// The frequency of the OS system clock in hz. 74 | os_sys_clock_freq_hz: u32, 75 | ///The frequency of the CPU clock in hz 76 | cpu_clock_freq_hz: u32, 77 | }; 78 | 79 | pub const TimerConfig = struct { 80 | timer_task_priority: u5, 81 | timer_stack_size: usize, 82 | }; 83 | 84 | var os_started: bool = false; 85 | pub fn setOsStarted() void { 86 | os_started = true; 87 | } 88 | 89 | /// Returns true when the OS is running 90 | pub fn isOsStarted() bool { 91 | return os_started; 92 | } 93 | 94 | pub export var g_stack_offset: usize = 0x08; 95 | 96 | pub var timer_task: Task = undefined; 97 | 98 | /// Start Multitasking 99 | pub inline fn startOS(comptime config: OsConfig) void { 100 | const iss = config.idle_task_config.idle_stack_size; 101 | if (isOsStarted() == false) { 102 | comptime { 103 | if (iss < DEFAULT_IDLE_TASK_SIZE) { 104 | @compileError("Idle stack size cannont be less than the default size."); 105 | } 106 | } 107 | 108 | setOsConfig(config); 109 | 110 | Arch.coreInit(&config.clock_config); 111 | 112 | var idle_stack: [iss]u32 = [_]u32{0xDEADC0DE} ** iss; 113 | var idle_task = Task.create_task(.{ 114 | .name = "idle task", 115 | .priority = 0, //Idle task priority is ignored 116 | .stack = &idle_stack, 117 | .subroutine = config.idle_task_config.idle_task_subroutine, 118 | }); 119 | 120 | task_ctrl.addIdleTask(&idle_task); 121 | 122 | var timer_stack = comptime blk: { 123 | if (OsBuildConfig.enable_software_timers) { 124 | if (config.timer_config) |tmr_config| { 125 | if (tmr_config.timer_stack_size < DEFAULT_IDLE_TASK_SIZE) { 126 | @compileError("Timer stack size cannont be less than the default size."); 127 | } 128 | const stack: [tmr_config.timer_stack_size]u32 = [_]u32{0xDEADC0DE} ** tmr_config.timer_stack_size; 129 | break :blk stack; 130 | } else { 131 | @compileError("Software timers enabled but TimerConfig passed to startOS()"); 132 | } 133 | } else { 134 | if (config.timer_config != null) { 135 | @compileError("TimerConfig passed to startOS() but software timers are disabled "); 136 | } 137 | } 138 | break :blk {}; 139 | }; 140 | 141 | if (OsBuildConfig.enable_software_timers) { 142 | if (config.timer_config) |tmr_config| { 143 | timer_task = Task.create_task(.{ 144 | .name = "timer task", 145 | .priority = tmr_config.timer_task_priority, 146 | .stack = &timer_stack, 147 | .subroutine = OsTimer.timerSubroutine, 148 | }); 149 | } 150 | timer_task.init(); 151 | OsTimer.timer_sem.init() catch unreachable; 152 | } 153 | 154 | //Find offset to stack ptr as zig does not guarantee struct field order 155 | g_stack_offset = @abs(@intFromPtr(&idle_task._stack_ptr) -% @intFromPtr(&idle_task)); 156 | 157 | setOsStarted(); 158 | Arch.runScheduler(); //begin os 159 | 160 | if (Arch.isDebugAttached()) { 161 | // Os failed to start. Likely something CPU specific is configured incorrectly. 162 | @breakpoint(); 163 | } 164 | 165 | if (!builtin.is_test) unreachable; 166 | } 167 | } 168 | 169 | /// Schedule the next task to run 170 | pub fn schedule() void { 171 | task_ctrl.setNextRunningTask(); 172 | if (task_ctrl.validSwitch()) { 173 | Arch.runContextSwitch(); 174 | } 175 | } 176 | 177 | pub const SyncContext = struct { 178 | //Event context 179 | pending_event: usize = 0, 180 | triggering_event: usize = 0, 181 | trigger_type: EventTrigger = EventTrigger.all_set, 182 | //Common Sync Context 183 | aborted: bool = false, 184 | timed_out: bool = false, 185 | 186 | pub const EventTrigger = enum { 187 | all_set, 188 | all_clear, 189 | any_set, 190 | any_clear, 191 | }; 192 | }; 193 | 194 | /// System tick counter 195 | var ticks: u64 = 0; 196 | 197 | pub const Time = struct { 198 | const math = @import("std").math; 199 | 200 | /// Get the current number of elapsed ticks 201 | pub fn getTicks() u64 { 202 | return ticks; 203 | } 204 | 205 | /// Get the current number of elapsed ticks as milliseconds (rounded down) 206 | pub fn getTicksMs() u64 { 207 | return (ticks * 1000) / os_config.clock_config.os_sys_clock_freq_hz; 208 | } 209 | 210 | /// Put the active task to sleep. It will become ready to run again after `time_ms` milliseconds. 211 | /// * `time_ms` when converted to system ticks cannot exceed 2^32 system ticks. 212 | pub fn delay(time_ms: u32) Error!void { 213 | var running_task = try validateCall(); 214 | if (time_ms != 0) { 215 | var timeout: u32 = math.mul(u32, time_ms, os_config.clock_config.os_sys_clock_freq_hz) catch return Error.SleepDurationOutOfRange; 216 | timeout /= 1000; 217 | Arch.criticalStart(); 218 | task_ctrl.yeildTask(running_task); 219 | running_task._timeout = timeout; 220 | Arch.criticalEnd(); 221 | Arch.runScheduler(); 222 | } 223 | } 224 | 225 | pub const SleepTime = struct { 226 | ms: usize = 0, 227 | sec: usize = 0, 228 | min: usize = 0, 229 | hr: usize = 0, 230 | days: usize = 0, 231 | }; 232 | 233 | fn sleepTimeToMs(time: *SleepTime) !u32 { 234 | var total_ms = time.ms; 235 | var temp_ms = try math.mul(u32, time.sec, 1000); 236 | total_ms = try math.add(u32, total_ms, temp_ms); 237 | temp_ms = try math.mul(u32, time.min, 60_000); 238 | total_ms = try math.add(u32, total_ms, temp_ms); 239 | temp_ms = try math.mul(u32, time.hr, 3_600_000); 240 | total_ms = try math.add(u32, total_ms, temp_ms); 241 | temp_ms = try math.mul(u32, time.hr, 86_400_000); 242 | total_ms = try math.add(u32, total_ms, temp_ms); 243 | return total_ms; 244 | } 245 | 246 | /// Put the active task to sleep. The value of `time` must be less than 2^32 milliseconds (~49.7 days) and less than 2^32 system ticks. 247 | pub fn sleep(time: SleepTime) Error!void { 248 | const timeout = sleepTimeToMs(&time) catch return Error.SleepDurationOutOfRange; 249 | try delay(timeout); 250 | } 251 | 252 | fn validateCall() Error!*Task { 253 | if (!os_started) return Error.OsOffline; 254 | const running_task = task_ctrl.table[task_ctrl.running_priority].ready_tasks.head orelse return Error.RunningTaskNull; 255 | 256 | if (OsBuildConfig.enable_software_timers and // 257 | running_task == &timer_task and // 258 | OsTimer.getCallbackExecution()) 259 | { 260 | return Error.IllegalTimerTask; 261 | } 262 | 263 | if (running_task._priority == OsTask.IDLE_PRIORITY_LEVEL) return Error.IllegalIdleTask; 264 | if (Arch.interruptActive()) return Error.IllegalInterruptAccess; 265 | return running_task; 266 | } 267 | }; 268 | 269 | ///System tick functionality. Should be called from the System Clock interrupt. e.g. SysTick_Handler 270 | pub inline fn OsTick() void { 271 | if (os_config.os_tick_callback) |callback| { 272 | callback(); 273 | } 274 | 275 | if (os_started) { 276 | ticks +%= 1; 277 | if (OsBuildConfig.enable_software_timers) { 278 | TimerControl.updateTimeOut(); 279 | } 280 | 281 | SyncControl.updateTimeOut(); 282 | task_ctrl.updateDelayedTasks(); 283 | task_ctrl.cycleActive(); 284 | schedule(); 285 | } 286 | } 287 | 288 | pub const Error = error{ 289 | /// The running task is null. This is an illegal state once multi tasking as started. 290 | RunningTaskNull, 291 | /// The operating system has not started multi tasking. 292 | OsOffline, 293 | /// Illegal call from idle task 294 | IllegalIdleTask, 295 | /// Illegal call from timer task 296 | IllegalTimerTask, 297 | /// Illegal call from interrupt 298 | IllegalInterruptAccess, 299 | /// A task that does not own this mutex attempted release 300 | InvalidMutexOwner, 301 | ///The task that owns this mutex attempted to aquire it a second time 302 | MutexOwnerAquire, 303 | /// Time out limit reached. 304 | TimedOut, 305 | /// Function manually aborted 306 | Aborted, 307 | /// Os Object not initalized 308 | Uninitialized, 309 | /// Os Object already initalized 310 | Reinitialized, 311 | /// The task is not blocked by the synchonization object 312 | TaskNotBlockedBySync, 313 | /// The synchonization object cannot be deleted because there is atleast 1 task pending on it. 314 | TaskPendingOnSync, 315 | /// The amount of time specified for the task to sleep exceeds the max value of 2^32 ms 316 | SleepDurationOutOfRange, 317 | /// Task cannot be resumed as it is not suspended 318 | IllegalTaskResume, 319 | }; 320 | -------------------------------------------------------------------------------- /source/task.zig: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2024 Edward Pizzella 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const std = @import("std"); 18 | const OsCore = @import("os_core.zig"); 19 | const ArchInterface = @import("arch/arch_interface.zig"); 20 | 21 | const Arch = ArchInterface.Arch; 22 | const os_config = &OsCore.getOsConfig; 23 | const SyncContext = OsCore.SyncContext; 24 | const Error = OsCore.Error; 25 | const SyncControl = @import("synchronization/sync_control.zig"); 26 | 27 | pub const Task = struct { 28 | _stack: []usize, 29 | _stack_ptr: usize = 0, //updated when os is started 30 | _state: State = State.uninitialized, 31 | _queue: ?*TaskQueue = null, 32 | _subroutine: *const fn () anyerror!void, 33 | _subroutineExitHandler: ?*const fn (task: *Task, err: anyerror!void) void = null, 34 | _timeout: u32 = 0, 35 | _timed_out: bool = false, 36 | _priority: u5, 37 | _basePriority: u5, 38 | _to_tail: ?*Task = null, 39 | _to_head: ?*Task = null, 40 | _SyncContext: SyncContext = .{}, 41 | _init: bool = false, 42 | _name: []const u8, 43 | 44 | const Self = @This(); 45 | 46 | pub const TaskConfig = struct { 47 | /// Task name 48 | name: []const u8, 49 | /// Task stack 50 | stack: []usize, 51 | /// Function executed by task 52 | subroutine: *const fn () anyerror!void, 53 | /// If `subroutine` returns an erorr that error will be passed to `subroutineErrHandler`. 54 | /// The task is suspsended after `subroutineErrHandler` completes, or if `subroutine` returns void. 55 | subroutineExitHandler: ?*const fn (task: *Task, err: anyerror!void) void = null, 56 | ///Priority level of the task. Lower number = higher priority. 57 | priority: u5, 58 | }; 59 | 60 | /// Create a task 61 | pub fn create_task(config: TaskConfig) Task { 62 | if (config.stack.len < Arch.minStackSize) { 63 | @panic("Task stack size smaller than minimum required."); 64 | } 65 | 66 | return Task{ 67 | ._name = config.name, 68 | ._stack = config.stack, 69 | ._priority = config.priority, 70 | ._basePriority = config.priority, 71 | ._subroutine = config.subroutine, 72 | ._subroutineExitHandler = config.subroutineExitHandler, 73 | }; 74 | } 75 | 76 | /// Add task to the OS 77 | pub fn init(self: *Self) void { 78 | if (!self._init) { 79 | Arch.initStack(self); 80 | Arch.criticalStart(); 81 | self._init = true; 82 | task_control.readyTask(self); 83 | Arch.criticalEnd(); 84 | } 85 | } 86 | 87 | /// Remove task from the OS 88 | /// 89 | /// This function will not return if `self` is the running task 90 | pub fn deinit(self: *Self) Error!void { 91 | const running_task = try SyncControl.validateCallMajor(); 92 | Arch.criticalStart(); 93 | //TODO: Check if task owns a mutex & release it. 94 | self._init = false; 95 | self._state = State.uninitialized; 96 | task_control.removeTask(self); 97 | if (running_task == self) { 98 | Arch.criticalEnd(); 99 | Arch.runScheduler(); 100 | } 101 | Arch.criticalEnd(); 102 | } 103 | 104 | /// Suspend the task 105 | pub fn suspendMe(self: *Self) Error!void { 106 | if (!self._init) return OsCore.Error.Uninitialized; 107 | if (self._priority == IDLE_PRIORITY_LEVEL) return OsCore.Error.IllegalIdleTask; 108 | Arch.criticalStart(); 109 | task_control.suspendTask(self); 110 | Arch.criticalEnd(); 111 | Arch.runScheduler(); 112 | } 113 | 114 | /// Resume the task 115 | pub fn resumeMe(self: *Self) Error!void { 116 | if (!self._init) return OsCore.Error.Uninitialized; 117 | if (self._priority == IDLE_PRIORITY_LEVEL) return OsCore.Error.IllegalIdleTask; 118 | if (!task_control.table[self._priority].suspended_tasks.contains(self)) return OsCore.Error.IllegalTaskResume; 119 | Arch.criticalStart(); 120 | task_control.readyTask(self); 121 | Arch.criticalEnd(); 122 | Arch.runScheduler(); 123 | } 124 | }; 125 | 126 | pub const State = enum { running, ready, suspended, yeilded, blocked, uninitialized }; 127 | 128 | pub var task_control: TaskControl = .{}; 129 | 130 | // 32 user accessable priority levels + idle task at lowest priority level 131 | const MAX_PRIO_LEVEL = 33; 132 | //idle task is the lowest priority. 133 | pub const IDLE_PRIORITY_LEVEL: u32 = 32; 134 | const PRIO_ADJUST: u5 = 31; 135 | const ONE: u32 = 0x1; 136 | 137 | pub const TaskControl = struct { 138 | table: [MAX_PRIO_LEVEL]TaskStateQ = [_]TaskStateQ{.{}} ** MAX_PRIO_LEVEL, 139 | ready_mask: u32 = 0, // mask of ready tasks 140 | running_priority: u6 = 0x00, // priority level of the current running task 141 | 142 | pub export var current_task: ?*volatile Task = null; 143 | pub export var next_task: *volatile Task = undefined; 144 | 145 | inline fn clearReadyBit(self: *TaskControl, priority: u6) void { 146 | self.ready_mask &= ~(ONE << (priorityAdjust[priority])); 147 | } 148 | 149 | inline fn setReadyBit(self: *TaskControl, priority: u6) void { 150 | self.ready_mask |= ONE << (priorityAdjust[priority]); 151 | } 152 | 153 | ///Set task ready to run 154 | pub fn readyTask(self: *TaskControl, task: *Task) void { 155 | if (task._queue) |q| _ = q.remove(task); 156 | self.addReady(task); 157 | } 158 | 159 | ///Set task as yeilded 160 | pub fn yeildTask(self: *TaskControl, task: *Task) void { 161 | if (task._queue) |q| _ = q.remove(task); 162 | if (self.table[task._priority].ready_tasks.head == null) { 163 | self.clearReadyBit(task._priority); 164 | } 165 | self.addYeilded(task); 166 | } 167 | 168 | ///Set task as suspended 169 | pub fn suspendTask(self: *TaskControl, task: *Task) void { 170 | if (task._queue) |q| _ = q.remove(task); 171 | if (self.table[task._priority].ready_tasks.head == null) { 172 | self.clearReadyBit(task._priority); 173 | } 174 | self.addSuspended(task); 175 | } 176 | 177 | ///Remove task 178 | pub fn removeTask(self: *TaskControl, task: *Task) void { 179 | if (task._queue) |q| _ = q.remove(task); 180 | if (self.table[task._priority].ready_tasks.head == null) { 181 | self.clearReadyBit(task._priority); 182 | } 183 | } 184 | 185 | ///Add task to the active task queue 186 | fn addReady(self: *TaskControl, task: *Task) void { 187 | self.table[task._priority].ready_tasks.insertAfter(task, null); 188 | self.setReadyBit(task._priority); 189 | task._state = State.ready; 190 | task._timeout = 0; 191 | } 192 | 193 | ///Add task to the yielded task queue 194 | fn addYeilded(self: *TaskControl, task: *Task) void { 195 | self.table[task._priority].yielded_tasks.insertAfter(task, null); 196 | task._state = State.yeilded; 197 | } 198 | 199 | ///Add task to the suspended task queue 200 | fn addSuspended(self: *TaskControl, task: *Task) void { 201 | self.table[task._priority].suspended_tasks.insertAfter(task, null); 202 | task._state = State.suspended; 203 | } 204 | 205 | ///Pop the active task from its active queue 206 | pub fn popRunningTask(self: *TaskControl) ?*Task { 207 | const head = self.table[self.running_priority].ready_tasks.pop(); 208 | if (self.table[self.running_priority].ready_tasks.head == null) { 209 | self.clearReadyBit(self.running_priority); 210 | } 211 | 212 | return head; 213 | } 214 | 215 | ///Move the head task to the tail position of the active queue 216 | pub fn cycleActive(self: *TaskControl) void { 217 | if (self.running_priority < MAX_PRIO_LEVEL) { 218 | var task = self.table[self.running_priority].ready_tasks.head; 219 | if (self.table[self.running_priority].ready_tasks.headToTail()) { 220 | task.?._state = State.ready; 221 | } 222 | } 223 | } 224 | 225 | pub fn getRunningTask(self: *TaskControl) *Task { 226 | if (self.table[self.running_priority].ready_tasks.head) |running| { 227 | return running; 228 | } else { 229 | @panic("Running Task Null. Os not Started."); 230 | } 231 | } 232 | 233 | ///Set `next_task` to the highest priority task that is ready to run 234 | pub fn setNextRunningTask(self: *TaskControl) void { 235 | self.running_priority = @clz(self.ready_mask); 236 | next_task = self.table[self.running_priority].ready_tasks.head.?; 237 | } 238 | 239 | ///Returns true if the running task and `next_task` are different 240 | pub fn validSwitch(self: *TaskControl) bool { 241 | _ = self; 242 | return current_task != next_task; 243 | } 244 | 245 | ///Updates the delayed time for each sleeping task 246 | pub fn updateDelayedTasks(self: *TaskControl) void { 247 | for (&self.table) |*taskState| { 248 | var opt_task = taskState.yielded_tasks.head; 249 | while (opt_task) |task| { 250 | task._timeout -= 1; 251 | if (task._timeout == 0) { 252 | self.readyTask(task); 253 | } 254 | 255 | opt_task = task._to_tail; 256 | } 257 | } 258 | } 259 | 260 | pub fn addIdleTask(self: *TaskControl, idle_task: *Task) void { 261 | Arch.initStack(idle_task); 262 | self.table[IDLE_PRIORITY_LEVEL].ready_tasks.insertAfter(idle_task, null); 263 | } 264 | 265 | const priorityAdjust: [32]u5 = .{ 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; 266 | }; 267 | 268 | const TaskStateQ = struct { 269 | ready_tasks: TaskQueue = .{}, 270 | yielded_tasks: TaskQueue = .{}, 271 | suspended_tasks: TaskQueue = .{}, 272 | }; 273 | 274 | pub const TaskQueue = struct { 275 | head: ?*Task = null, 276 | tail: ?*Task = null, 277 | elements: u32 = 0, 278 | 279 | const Self = @This(); 280 | 281 | ///Insert `insert_node` before `target_node`. When `target_node` is null prepend to head 282 | pub fn insertBefore(self: *Self, insert_node: *Task, target_node: ?*Task) void { 283 | if (target_node) |t_node| { 284 | //insert before 285 | insert_node._to_head = t_node._to_head; 286 | insert_node._to_tail = t_node; 287 | t_node._to_head = insert_node; 288 | if (target_node == self.head) self.head = insert_node; 289 | if (insert_node._to_head) |insert_head| insert_head._to_tail = insert_node; 290 | } else { 291 | //prepend to head. 292 | if (self.head) |head| { 293 | insert_node._to_tail = head; 294 | head._to_head = insert_node; 295 | insert_node._to_head = null; //this should already be null. 296 | } else { 297 | self.tail = insert_node; 298 | } 299 | self.head = insert_node; 300 | } 301 | 302 | insert_node._queue = self; 303 | self.elements += 1; 304 | } 305 | 306 | ///Insert `insert_node` after `target_node`. When `target_node` is null append to head 307 | pub fn insertAfter(self: *Self, insert_node: *Task, target_node: ?*Task) void { 308 | if (target_node) |t_node| { 309 | //insert after 310 | insert_node._to_tail = t_node._to_tail; 311 | insert_node._to_head = t_node; 312 | t_node._to_tail = insert_node; 313 | if (t_node == self.tail) self.tail = insert_node; 314 | if (insert_node._to_tail) |insert_tail| insert_tail._to_head = insert_node; 315 | } else { 316 | //append to tail. 317 | if (self.tail) |tail| { 318 | insert_node._to_head = tail; 319 | tail._to_tail = insert_node; 320 | insert_node._to_tail = null; //this should already be null. 321 | } else { 322 | self.head = insert_node; 323 | } 324 | self.tail = insert_node; 325 | } 326 | 327 | insert_node._queue = self; 328 | self.elements += 1; 329 | } 330 | 331 | //Insert a task into the queue based on its priority 332 | pub fn insertSorted(self: *Self, insert_node: *Task) void { 333 | var search: ?*Task = self.tail; 334 | while (true) { 335 | if (search) |s| { 336 | if (insert_node._priority >= s._priority) { 337 | self.insertAfter(insert_node, s); 338 | break; 339 | } else { 340 | search = s._to_head; 341 | } 342 | } else { 343 | self.insertBefore(insert_node, search); 344 | break; 345 | } 346 | } 347 | } 348 | 349 | ///Pop the head node from the queue 350 | pub fn pop(self: *Self) ?*Task { 351 | const rtn = self.head orelse return null; 352 | self.head = rtn._to_tail; 353 | rtn._to_tail = null; 354 | self.elements -= 1; 355 | if (self.head) |new_head| { 356 | new_head._to_head = null; 357 | } else { 358 | self.tail = null; 359 | } 360 | rtn._queue = null; 361 | return rtn; 362 | } 363 | 364 | ///Returns true if the specified node is contained in the queue 365 | pub fn contains(self: *Self, node: *Task) bool { 366 | return node._queue == self; 367 | } 368 | 369 | ///Removes the specified task from the queue. Returns false if the node is not contained in the queue. 370 | pub fn remove(self: *Self, node: *Task) bool { 371 | var rtn = false; 372 | 373 | if (self.contains(node)) { 374 | if (self.head == self.tail) { //list of 1 375 | self.head = null; 376 | self.tail = null; 377 | } else if (self.head == node) { 378 | if (node._to_tail) |towardTail| { 379 | self.head = towardTail; 380 | towardTail._to_head = null; 381 | } 382 | } else if (self.tail == node) { 383 | if (node._to_head) |towardHead| { 384 | self.tail = towardHead; 385 | towardHead._to_tail = null; 386 | } 387 | } else { 388 | if (node._to_head) |towardHead| { 389 | towardHead._to_tail = node._to_tail; 390 | } 391 | if (node._to_tail) |towardTail| { 392 | towardTail._to_head = node._to_head; 393 | } 394 | } 395 | 396 | node._to_head = null; 397 | node._to_tail = null; 398 | 399 | self.elements -= 1; 400 | node._queue = null; 401 | rtn = true; 402 | } 403 | 404 | return rtn; 405 | } 406 | 407 | ///Move the head task to the tail position. 408 | pub fn headToTail(self: *Self) bool { 409 | var rtn = false; 410 | if (self.head != self.tail) { 411 | if (self.head != null and self.tail != null) { 412 | const temp = self.head; 413 | self.head.?._to_tail.?._to_head = null; 414 | self.head = self.head.?._to_tail; 415 | 416 | temp.?._to_tail = null; 417 | self.tail.?._to_tail = temp; 418 | temp.?._to_head = self.tail; 419 | self.tail = temp; 420 | rtn = true; 421 | } 422 | } 423 | 424 | return rtn; 425 | } 426 | }; 427 | 428 | pub fn taskTopRoutine() void { 429 | const task = task_control.getRunningTask(); 430 | const err = task._subroutine(); 431 | 432 | Arch.criticalStart(); 433 | task_control.removeTask(task); 434 | task._init = false; 435 | task._state = State.uninitialized; 436 | if (task._subroutineExitHandler) |exitHandler| { 437 | exitHandler(task, err); 438 | } 439 | Arch.criticalEnd(); 440 | 441 | Arch.runScheduler(); 442 | } 443 | -------------------------------------------------------------------------------- /test.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Semaphore = @import("source/synchronization/semaphore.zig").Semaphore; 3 | const Mutex = @import("source/synchronization/mutex.zig").Mutex; 4 | const OsTask = @import("source/task.zig"); 5 | const TestArch = @import("source/arch/test/test_arch.zig"); 6 | const OsCore = @import("source/os_core.zig"); 7 | const OS = @import("os.zig"); 8 | 9 | const expect = std.testing.expect; 10 | const task_control = &OsTask.task_control; 11 | const Task = OsTask.Task; 12 | const TaskQueue = OsTask.TaskQueue; 13 | 14 | var test_stack: [20]usize = [_]usize{0xDEADC0DE} ** 20; 15 | fn test_fn() !void {} 16 | 17 | var test_task1 = Task.create_task(.{ 18 | .name = "test1", 19 | .priority = 1, 20 | .stack = &test_stack, 21 | .subroutine = &test_fn, 22 | }); 23 | 24 | var test_task2 = Task.create_task(.{ 25 | .name = "test2", 26 | .priority = 2, 27 | .stack = &test_stack, 28 | .subroutine = &test_fn, 29 | }); 30 | 31 | var test_task3: Task = Task.create_task(.{ 32 | .name = "test3", 33 | .priority = 3, 34 | .stack = &test_stack, 35 | .subroutine = &test_fn, 36 | }); 37 | 38 | var test_task4 = Task.create_task(.{ 39 | .name = "test4", 40 | .priority = 4, 41 | .stack = &test_stack, 42 | .subroutine = &test_fn, 43 | }); 44 | 45 | var idle_task = Task.create_task(.{ 46 | .name = "idle_task", 47 | .priority = 0, //ignored 48 | .stack = &test_stack, 49 | .subroutine = &test_fn, 50 | }); 51 | 52 | fn task_setup() void { 53 | test_task1._init = false; 54 | test_task2._init = false; 55 | test_task3._init = false; 56 | test_task4._init = false; 57 | 58 | test_task1.init(); 59 | test_task2.init(); 60 | test_task3.init(); 61 | test_task4.init(); 62 | 63 | test_task1._SyncContext.aborted = false; 64 | test_task2._SyncContext.aborted = false; 65 | test_task3._SyncContext.aborted = false; 66 | test_task4._SyncContext.aborted = false; 67 | test_task1._SyncContext.timed_out = false; 68 | test_task2._SyncContext.timed_out = false; 69 | test_task3._SyncContext.timed_out = false; 70 | test_task4._SyncContext.timed_out = false; 71 | task_control.addIdleTask(&idle_task); 72 | OsCore.setOsStarted(); 73 | task_control.setNextRunningTask(); 74 | //Clear test arch flags 75 | _ = TestArch.schedulerRan(); 76 | _ = TestArch.contextSwitchRan(); 77 | } 78 | 79 | fn clearPointers() void { 80 | test_task1._to_head = null; 81 | test_task1._to_tail = null; 82 | test_task2._to_head = null; 83 | test_task2._to_tail = null; 84 | test_task3._to_head = null; 85 | test_task3._to_tail = null; 86 | test_task4._to_head = null; 87 | test_task4._to_tail = null; 88 | } 89 | 90 | ///////////////////////////////////////////// 91 | // Task Unit Tests // 92 | /////////////////////////////////////////// 93 | 94 | test "Task Suspend Test" { 95 | task_setup(); 96 | try test_task1.suspendMe(); 97 | task_control.setNextRunningTask(); 98 | try expect(task_control.running_priority == 2); 99 | } 100 | 101 | test "Task Resume Test" { 102 | task_setup(); 103 | try test_task1.suspendMe(); 104 | task_control.setNextRunningTask(); 105 | try test_task1.resumeMe(); 106 | task_control.setNextRunningTask(); 107 | try expect(task_control.running_priority == 1); 108 | } 109 | 110 | ///////////////////////////////////////////// 111 | // Task Queue Unit Tests // 112 | /////////////////////////////////////////// 113 | 114 | test "Task Queue Insert After Append 1 Node Test" { 115 | clearPointers(); 116 | 117 | var queue: TaskQueue = .{}; 118 | queue.insertAfter(&test_task1, null); 119 | 120 | try expect(queue.head.?._to_head == null); 121 | try expect(queue.tail.?._to_tail == null); 122 | try expect(queue.head == &test_task1); 123 | try expect(queue.tail == &test_task1); 124 | try expect(test_task1._to_head == null); 125 | try expect(test_task1._to_tail == null); 126 | } 127 | 128 | test "Task Queue Insert After Append 2 Nodes Test" { 129 | clearPointers(); 130 | var queue: TaskQueue = .{}; 131 | queue.insertAfter(&test_task1, null); 132 | queue.insertAfter(&test_task2, &test_task1); 133 | 134 | try expect(queue.head.?._to_head == null); 135 | try expect(queue.tail.?._to_tail == null); 136 | try expect(queue.head == &test_task1); 137 | try expect(queue.tail == &test_task2); 138 | try expect(test_task1._to_head == null); 139 | try expect(test_task1._to_tail == &test_task2); 140 | try expect(test_task2._to_head == &test_task1); 141 | try expect(test_task2._to_tail == null); 142 | } 143 | 144 | test "Task Queue Insert After Append 3 Nodes Test" { 145 | clearPointers(); 146 | 147 | var queue: TaskQueue = .{}; 148 | queue.insertAfter(&test_task1, null); 149 | queue.insertAfter(&test_task2, null); 150 | queue.insertAfter(&test_task3, null); 151 | 152 | try expect(queue.head.?._to_head == null); 153 | try expect(queue.tail.?._to_tail == null); 154 | try expect(queue.head == &test_task1); 155 | try expect(queue.tail == &test_task3); 156 | try expect(test_task1._to_head == null); 157 | try expect(test_task1._to_tail == &test_task2); 158 | try expect(test_task2._to_head == &test_task1); 159 | try expect(test_task2._to_tail == &test_task3); 160 | try expect(test_task3._to_head == &test_task2); 161 | try expect(test_task3._to_tail == null); 162 | } 163 | 164 | test "Task Queue Insert After 4 Nodes Test" { 165 | clearPointers(); 166 | 167 | var queue: TaskQueue = .{}; 168 | queue.insertAfter(&test_task1, null); 169 | queue.insertAfter(&test_task2, null); 170 | queue.insertAfter(&test_task3, null); 171 | queue.insertAfter(&test_task4, &test_task2); 172 | 173 | try expect(queue.head.?._to_head == null); 174 | try expect(queue.tail.?._to_tail == null); 175 | try expect(queue.head == &test_task1); 176 | try expect(queue.tail == &test_task3); 177 | try expect(test_task1._to_head == null); 178 | try expect(test_task1._to_tail == &test_task2); 179 | try expect(test_task2._to_head == &test_task1); 180 | try expect(test_task2._to_tail == &test_task4); 181 | try expect(test_task4._to_head == &test_task2); 182 | try expect(test_task4._to_tail == &test_task3); 183 | try expect(test_task3._to_head == &test_task4); 184 | try expect(test_task3._to_tail == null); 185 | } 186 | 187 | test "Task Queue Insert Before Prepend 1 node Test" { 188 | clearPointers(); 189 | 190 | var queue: TaskQueue = .{}; 191 | queue.insertBefore(&test_task1, null); 192 | 193 | try expect(queue.head.?._to_head == null); 194 | try expect(queue.tail.?._to_tail == null); 195 | try expect(queue.head == &test_task1); 196 | try expect(queue.tail == &test_task1); 197 | try expect(test_task1._to_head == null); 198 | try expect(test_task1._to_tail == null); 199 | } 200 | 201 | test "Task Queue Insert Before 2 nodes Test" { 202 | clearPointers(); 203 | 204 | var queue: TaskQueue = .{}; 205 | queue.insertBefore(&test_task1, null); 206 | queue.insertBefore(&test_task2, &test_task1); 207 | 208 | try expect(queue.head.?._to_head == null); 209 | try expect(queue.tail.?._to_tail == null); 210 | try expect(queue.head == &test_task2); 211 | try expect(queue.tail == &test_task1); 212 | try expect(test_task2._to_head == null); 213 | try expect(test_task2._to_tail == &test_task1); 214 | try expect(test_task1._to_head == &test_task2); 215 | try expect(test_task1._to_tail == null); 216 | } 217 | 218 | test "Task Queue Insert Before Prepend 3 Nodes Test" { 219 | clearPointers(); 220 | 221 | var queue: TaskQueue = .{}; 222 | queue.insertBefore(&test_task3, null); 223 | queue.insertBefore(&test_task2, null); 224 | queue.insertBefore(&test_task1, null); 225 | 226 | try expect(queue.head.?._to_head == null); 227 | try expect(queue.tail.?._to_tail == null); 228 | try expect(queue.head == &test_task1); 229 | try expect(queue.tail == &test_task3); 230 | try expect(test_task1._to_head == null); 231 | try expect(test_task1._to_tail == &test_task2); 232 | try expect(test_task2._to_head == &test_task1); 233 | try expect(test_task2._to_tail == &test_task3); 234 | try expect(test_task3._to_head == &test_task2); 235 | try expect(test_task3._to_tail == null); 236 | } 237 | 238 | test "Task Queue Insert Before 4 nodes Test" { 239 | clearPointers(); 240 | 241 | var queue: TaskQueue = .{}; 242 | queue.insertBefore(&test_task3, null); 243 | queue.insertBefore(&test_task2, null); 244 | queue.insertBefore(&test_task1, null); 245 | queue.insertBefore(&test_task4, &test_task2); 246 | 247 | try expect(queue.head.?._to_head == null); 248 | try expect(queue.tail.?._to_tail == null); 249 | try expect(queue.head == &test_task1); 250 | try expect(queue.tail == &test_task3); 251 | try expect(test_task1._to_head == null); 252 | try expect(test_task1._to_tail == &test_task4); 253 | try expect(test_task4._to_head == &test_task1); 254 | try expect(test_task4._to_tail == &test_task2); 255 | try expect(test_task2._to_head == &test_task4); 256 | try expect(test_task2._to_tail == &test_task3); 257 | try expect(test_task3._to_head == &test_task2); 258 | try expect(test_task3._to_tail == null); 259 | } 260 | 261 | test "Task Queue Insert Sorted 1 node Test" { 262 | clearPointers(); 263 | 264 | var queue: TaskQueue = .{}; 265 | queue.insertSorted(&test_task1); 266 | 267 | try expect(queue.head.?._to_head == null); 268 | try expect(queue.tail.?._to_tail == null); 269 | try expect(queue.head == &test_task1); 270 | try expect(queue.tail == &test_task1); 271 | try expect(test_task1._to_head == null); 272 | try expect(test_task1._to_tail == null); 273 | } 274 | 275 | test "Task Queue Insert Mixed Test" { 276 | clearPointers(); 277 | 278 | var queue: TaskQueue = .{}; 279 | 280 | queue.insertBefore(&test_task3, null); 281 | queue.insertAfter(&test_task4, &test_task3); 282 | queue.insertBefore(&test_task1, null); 283 | queue.insertAfter(&test_task2, &test_task1); 284 | 285 | try expect(queue.head.?._to_head == null); 286 | try expect(queue.tail.?._to_tail == null); 287 | try expect(queue.head == &test_task1); 288 | try expect(queue.tail == &test_task4); 289 | try expect(test_task1._to_head == null); 290 | try expect(test_task1._to_tail == &test_task2); 291 | try expect(test_task2._to_head == &test_task1); 292 | try expect(test_task2._to_tail == &test_task3); 293 | try expect(test_task3._to_head == &test_task2); 294 | try expect(test_task3._to_tail == &test_task4); 295 | try expect(test_task4._to_head == &test_task3); 296 | try expect(test_task4._to_tail == null); 297 | } 298 | 299 | test "Task Queue Insert Sorted 4 Nodes - 1 Test" { 300 | clearPointers(); 301 | 302 | var queue: TaskQueue = .{}; 303 | queue.insertSorted(&test_task1); 304 | queue.insertSorted(&test_task2); 305 | queue.insertSorted(&test_task3); 306 | queue.insertSorted(&test_task4); 307 | 308 | try expect(queue.head.?._to_head == null); 309 | try expect(queue.tail.?._to_tail == null); 310 | try expect(queue.head == &test_task1); 311 | try expect(queue.tail == &test_task4); 312 | try expect(test_task1._to_head == null); 313 | try expect(test_task1._to_tail == &test_task2); 314 | try expect(test_task2._to_head == &test_task1); 315 | try expect(test_task2._to_tail == &test_task3); 316 | try expect(test_task3._to_head == &test_task2); 317 | try expect(test_task3._to_tail == &test_task4); 318 | try expect(test_task4._to_head == &test_task3); 319 | try expect(test_task4._to_tail == null); 320 | } 321 | 322 | test "Task Queue Insert Sorted 4 Nodes - 2 Test" { 323 | clearPointers(); 324 | 325 | var queue: TaskQueue = .{}; 326 | queue.insertSorted(&test_task4); 327 | queue.insertSorted(&test_task3); 328 | queue.insertSorted(&test_task2); 329 | queue.insertSorted(&test_task1); 330 | 331 | try expect(queue.head.?._to_head == null); 332 | try expect(queue.tail.?._to_tail == null); 333 | try expect(queue.head == &test_task1); 334 | try expect(queue.tail == &test_task4); 335 | try expect(test_task1._to_head == null); 336 | try expect(test_task1._to_tail == &test_task2); 337 | try expect(test_task2._to_head == &test_task1); 338 | try expect(test_task2._to_tail == &test_task3); 339 | try expect(test_task3._to_head == &test_task2); 340 | try expect(test_task3._to_tail == &test_task4); 341 | try expect(test_task4._to_head == &test_task3); 342 | try expect(test_task4._to_tail == null); 343 | } 344 | 345 | test "Task Queue Insert Sorted 4 Nodes - 3 Test" { 346 | clearPointers(); 347 | 348 | var queue: TaskQueue = .{}; 349 | 350 | queue.insertSorted(&test_task3); 351 | queue.insertSorted(&test_task4); 352 | queue.insertSorted(&test_task1); 353 | queue.insertSorted(&test_task2); 354 | 355 | try expect(queue.head.?._to_head == null); 356 | try expect(queue.tail.?._to_tail == null); 357 | try expect(queue.head == &test_task1); 358 | try expect(queue.tail == &test_task4); 359 | try expect(test_task1._to_head == null); 360 | try expect(test_task1._to_tail == &test_task2); 361 | try expect(test_task2._to_head == &test_task1); 362 | try expect(test_task2._to_tail == &test_task3); 363 | try expect(test_task3._to_head == &test_task2); 364 | try expect(test_task3._to_tail == &test_task4); 365 | try expect(test_task4._to_head == &test_task3); 366 | try expect(test_task4._to_tail == null); 367 | } 368 | 369 | test "Task Queue Pop - 1 Test" { 370 | clearPointers(); 371 | 372 | var queue: TaskQueue = .{}; 373 | queue.insertSorted(&test_task1); 374 | queue.insertSorted(&test_task2); 375 | queue.insertSorted(&test_task3); 376 | queue.insertSorted(&test_task4); 377 | 378 | try expect(queue.elements == 4); 379 | var head = queue.pop(); 380 | try expect(head == &test_task1); 381 | head = queue.pop(); 382 | try expect(head == &test_task2); 383 | head = queue.pop(); 384 | try expect(head == &test_task3); 385 | head = queue.pop(); 386 | try expect(head == &test_task4); 387 | try expect(queue.elements == 0); 388 | } 389 | 390 | ///////////////////////////////////////////// 391 | // Mutex Unit Tests // 392 | /////////////////////////////////////////// 393 | 394 | test "Mutex Init/Deinit Test" { 395 | var mutex1 = Mutex.create_mutex("test_mutex1"); 396 | var mutex2 = Mutex.create_mutex("test_mutex1"); 397 | var mutex3 = Mutex.create_mutex("test_mutex1"); 398 | //init test 399 | try mutex1.init(); 400 | try mutex2.init(); 401 | try mutex3.init(); 402 | 403 | try expect(mutex1._syncContext._init == true); 404 | try expect(mutex1._syncContext._prev == &mutex2._syncContext); 405 | try expect(mutex1._syncContext._next == null); 406 | 407 | try expect(mutex2._syncContext._init == true); 408 | try expect(mutex2._syncContext._prev == &mutex3._syncContext); 409 | try expect(mutex2._syncContext._next == &mutex1._syncContext); 410 | 411 | try expect(mutex3._syncContext._init == true); 412 | try expect(mutex3._syncContext._prev == null); 413 | try expect(mutex3._syncContext._next == &mutex2._syncContext); 414 | 415 | //deinit test 416 | try mutex2.deinit(); 417 | try expect(mutex2._syncContext._init == false); 418 | try expect(mutex2._syncContext._prev == null); 419 | try expect(mutex2._syncContext._next == null); 420 | 421 | try expect(mutex3._syncContext._prev == null); 422 | try expect(mutex3._syncContext._next == &mutex1._syncContext); 423 | 424 | try expect(mutex1._syncContext._prev == &mutex3._syncContext); 425 | try expect(mutex1._syncContext._next == null); 426 | 427 | try mutex3.deinit(); 428 | try expect(mutex3._syncContext._init == false); 429 | try expect(mutex3._syncContext._prev == null); 430 | try expect(mutex3._syncContext._next == null); 431 | 432 | try expect(mutex1._syncContext._init == true); 433 | try expect(mutex1._syncContext._prev == null); 434 | try expect(mutex1._syncContext._next == null); 435 | 436 | try mutex1.deinit(); 437 | } 438 | 439 | test "Mutex Aquire Test" { 440 | task_setup(); 441 | var mutex1 = Mutex.create_mutex("test_mutex1"); 442 | mutex1._syncContext._init = true; 443 | 444 | try mutex1.acquire(.{ .timeout_ms = 0 }); 445 | try expect(mutex1.acquire(.{ .timeout_ms = 0 }) == OsCore.Error.MutexOwnerAquire); 446 | } 447 | 448 | test "Mutex Aquire Test 2" { 449 | task_setup(); 450 | var mutex1 = Mutex.create_mutex("test_mutex1"); 451 | mutex1._syncContext._init = true; 452 | 453 | try mutex1.acquire(.{ .timeout_ms = 0 }); 454 | try expect(!TestArch.schedulerRan()); 455 | try expect(mutex1._owner == &test_task1); 456 | 457 | try test_task1.suspendMe(); 458 | try expect(TestArch.schedulerRan()); 459 | task_control.setNextRunningTask(); 460 | try expect(task_control.running_priority == 2); 461 | 462 | try mutex1.acquire(.{ .timeout_ms = 0 }); 463 | try expect(TestArch.schedulerRan()); 464 | task_control.setNextRunningTask(); 465 | try expect(task_control.running_priority == 3); 466 | } 467 | 468 | test "Mutex Release Test" { 469 | task_setup(); 470 | var mutex1 = Mutex.create_mutex("test_mutex1"); 471 | try expect(mutex1.release() == OsCore.Error.InvalidMutexOwner); 472 | } 473 | 474 | test "Mutex Release Test 2" { 475 | task_setup(); 476 | var mutex1 = Mutex.create_mutex("test_mutex1"); 477 | mutex1._syncContext._init = true; 478 | 479 | try mutex1.acquire(.{ .timeout_ms = 0 }); 480 | try test_task1.suspendMe(); 481 | task_control.setNextRunningTask(); 482 | try expect(task_control.running_priority == 2); 483 | 484 | try expect(mutex1.release() == OsCore.Error.InvalidMutexOwner); 485 | } 486 | 487 | test "Mutex Release Test 3" { 488 | task_setup(); 489 | var mutex1 = Mutex.create_mutex("test_mutex1"); 490 | mutex1._syncContext._init = true; 491 | 492 | try mutex1.acquire(.{ .timeout_ms = 0 }); 493 | try expect(mutex1._owner == &test_task1); 494 | try mutex1.release(); 495 | try expect(mutex1._owner == null); 496 | try expect(!TestArch.schedulerRan()); 497 | } 498 | 499 | test "Mutex Abort Test" { 500 | task_setup(); 501 | var mutex1 = Mutex.create_mutex("test_mutex1"); 502 | mutex1._syncContext._init = true; 503 | 504 | try mutex1.acquire(.{ .timeout_ms = 0 }); 505 | try expect(mutex1._owner == &test_task1); 506 | try test_task1.suspendMe(); 507 | task_control.setNextRunningTask(); 508 | try expect(task_control.running_priority == 2); 509 | 510 | try mutex1.acquire(.{ .timeout_ms = 0 }); 511 | task_control.setNextRunningTask(); 512 | try expect(task_control.running_priority == 3); 513 | 514 | try mutex1.abortAcquire(&test_task2); 515 | try expect(TestArch.schedulerRan()); 516 | task_control.setNextRunningTask(); 517 | try expect(task_control.running_priority == 2); 518 | 519 | try expect(mutex1.acquire(.{ .timeout_ms = 0 }) == OsCore.Error.Aborted); 520 | } 521 | 522 | test "Mutex Abort Test 2" { 523 | task_setup(); 524 | var mutex1 = Mutex.create_mutex("test_mutex1"); 525 | mutex1._syncContext._init = true; 526 | 527 | try mutex1.acquire(.{ .timeout_ms = 0 }); 528 | task_control.setNextRunningTask(); 529 | try expect(mutex1.abortAcquire(&test_task2) == OsCore.Error.TaskNotBlockedBySync); 530 | } 531 | 532 | test "Mutex Timeout Test" { 533 | task_setup(); 534 | var mutex1 = Mutex.create_mutex("test_mutex1"); 535 | try mutex1.init(); 536 | mutex1._owner = &test_task4; 537 | 538 | try mutex1.acquire(.{ .timeout_ms = 1 }); 539 | task_control.setNextRunningTask(); 540 | try expect(task_control.running_priority == 2); 541 | 542 | OsCore.systemTick(); 543 | try expect(task_control.running_priority == 1); 544 | try expect(mutex1.acquire(.{}) == OsCore.Error.TimedOut); 545 | task_control.setNextRunningTask(); 546 | try mutex1.abortAcquire(&test_task1); 547 | 548 | //clean up 549 | try mutex1.deinit(); 550 | } 551 | 552 | test "Mutex Timeout Test 2" { 553 | task_setup(); 554 | var mutex1 = Mutex.create_mutex("test_mutex1"); 555 | var mutex2 = Mutex.create_mutex("test_mutex1"); 556 | try mutex1.init(); 557 | try mutex2.init(); 558 | mutex1._owner = &test_task4; 559 | mutex2._owner = &test_task4; 560 | 561 | try mutex1.acquire(.{ .timeout_ms = 1 }); 562 | task_control.setNextRunningTask(); 563 | try expect(task_control.running_priority == 2); 564 | try mutex2.acquire(.{ .timeout_ms = 1 }); 565 | task_control.setNextRunningTask(); 566 | try expect(task_control.running_priority == 3); 567 | 568 | OsCore.systemTick(); 569 | try expect(test_task1._state == OsTask.State.running); 570 | try expect(test_task1._SyncContext.timed_out); 571 | try expect(test_task2._state == OsTask.State.ready); 572 | try expect(test_task2._SyncContext.timed_out); 573 | 574 | //clean up 575 | try mutex1.deinit(); 576 | try mutex2.deinit(); 577 | } 578 | 579 | ///////////////////////////////////////////// 580 | // Semaphore Unit Tests // 581 | /////////////////////////////////////////// 582 | 583 | test "Semaphore Init/Deinit Test" { 584 | var semaphore1 = Semaphore.create_semaphore(.{ .name = "test_sem1", .inital_value = 1 }); 585 | var semaphore2 = Semaphore.create_semaphore(.{ .name = "test_sem2", .inital_value = 1 }); 586 | var semaphore3 = Semaphore.create_semaphore(.{ .name = "test_sem3", .inital_value = 1 }); 587 | //init test 588 | try semaphore1.init(); 589 | try semaphore2.init(); 590 | try semaphore3.init(); 591 | 592 | try expect(semaphore1._syncContext._init == true); 593 | try expect(semaphore1._syncContext._prev == &semaphore2._syncContext); 594 | try expect(semaphore1._syncContext._next == null); 595 | 596 | try expect(semaphore2._syncContext._init == true); 597 | try expect(semaphore2._syncContext._prev == &semaphore3._syncContext); 598 | try expect(semaphore2._syncContext._next == &semaphore1._syncContext); 599 | 600 | try expect(semaphore3._syncContext._init == true); 601 | try expect(semaphore3._syncContext._prev == null); 602 | try expect(semaphore3._syncContext._next == &semaphore2._syncContext); 603 | 604 | //deinit test 605 | try semaphore2.deinit(); 606 | try expect(semaphore2._syncContext._init == false); 607 | try expect(semaphore2._syncContext._prev == null); 608 | try expect(semaphore2._syncContext._next == null); 609 | 610 | try expect(semaphore3._syncContext._prev == null); 611 | try expect(semaphore3._syncContext._next == &semaphore1._syncContext); 612 | 613 | try expect(semaphore1._syncContext._prev == &semaphore3._syncContext); 614 | try expect(semaphore1._syncContext._next == null); 615 | 616 | try semaphore3.deinit(); 617 | try expect(semaphore3._syncContext._init == false); 618 | try expect(semaphore3._syncContext._prev == null); 619 | try expect(semaphore3._syncContext._next == null); 620 | 621 | try expect(semaphore1._syncContext._init == true); 622 | try expect(semaphore1._syncContext._prev == null); 623 | try expect(semaphore1._syncContext._next == null); 624 | 625 | try semaphore1.deinit(); 626 | } 627 | 628 | test "Semaphore Aquire Test" { 629 | task_setup(); 630 | var semaphore = Semaphore.create_semaphore(.{ .name = "test_sem", .inital_value = 1 }); 631 | semaphore._syncContext._init = true; 632 | 633 | try semaphore.wait(.{ .timeout_ms = 0 }); 634 | try expect(TestArch.schedulerRan() == false); 635 | try expect(semaphore._count == 0); 636 | try semaphore.wait(.{ .timeout_ms = 0 }); 637 | try expect(TestArch.schedulerRan() == true); 638 | } 639 | 640 | test "Semaphore Release Test" { 641 | task_setup(); 642 | var semaphore = Semaphore.create_semaphore(.{ .name = "test_sem", .inital_value = 0 }); 643 | try semaphore.post(.{}); 644 | 645 | try expect(semaphore._count == 1); 646 | try expect(TestArch.schedulerRan() == false); 647 | } 648 | 649 | test "Semaphore Release Test 2" { 650 | task_setup(); 651 | var semaphore = Semaphore.create_semaphore(.{ .name = "test_sem", .inital_value = 0 }); 652 | semaphore._syncContext._init = true; 653 | 654 | try semaphore.wait(.{ .timeout_ms = 0 }); 655 | task_control.setNextRunningTask(); 656 | try expect(task_control.running_priority == 2); 657 | try semaphore.post(.{}); 658 | try expect(semaphore._count == 0); 659 | try expect(TestArch.schedulerRan() == true); 660 | } 661 | 662 | test "Semaphore Release Test 3" { 663 | OsCore.setOsStarted(); 664 | try test_task1.suspendMe(); 665 | task_control.readyTask(&test_task2); 666 | task_control.setNextRunningTask(); 667 | try expect(task_control.running_priority == 2); 668 | task_control.readyTask(&test_task1); 669 | var semaphore = Semaphore.create_semaphore(.{ .name = "test_sem", .inital_value = 0 }); 670 | semaphore._syncContext._init = true; 671 | 672 | try semaphore.wait(.{ .timeout_ms = 0 }); 673 | try expect(TestArch.schedulerRan() == true); 674 | task_control.setNextRunningTask(); 675 | try expect(task_control.running_priority == 1); 676 | try semaphore.post(.{}); 677 | try expect(semaphore._count == 0); 678 | try expect(TestArch.schedulerRan() == false); 679 | } 680 | 681 | test "Semaphore Abort Test" { 682 | task_setup(); 683 | var semaphore = Semaphore.create_semaphore(.{ .name = "test_sem", .inital_value = 0 }); 684 | semaphore._syncContext._init = true; 685 | 686 | try semaphore.wait(.{ .timeout_ms = 0 }); 687 | task_control.setNextRunningTask(); 688 | try semaphore.abortAcquire(&test_task1); 689 | try expect(test_task1._SyncContext.aborted == true); 690 | try expect(TestArch.schedulerRan()); 691 | task_control.setNextRunningTask(); 692 | try expect(semaphore.wait(.{ .timeout_ms = 0 }) == OsCore.Error.Aborted); 693 | } 694 | 695 | test "Semaphore Abort Test2" { 696 | task_setup(); 697 | var semaphore = Semaphore.create_semaphore(.{ .name = "test_sem", .inital_value = 0 }); 698 | semaphore._syncContext._init = true; 699 | 700 | try semaphore.wait(.{ .timeout_ms = 0 }); 701 | task_control.setNextRunningTask(); 702 | try expect(semaphore.abortAcquire(&test_task2) == OsCore.Error.TaskNotBlockedBySync); 703 | } 704 | 705 | test "Semaphore timeout" { 706 | task_setup(); 707 | var semaphore = Semaphore.create_semaphore(.{ .name = "test_sem", .inital_value = 0 }); 708 | try semaphore.init(); 709 | 710 | try semaphore.wait(.{ .timeout_ms = 1 }); 711 | OsCore.systemTick(); 712 | try expect(task_control.running_priority == 1); 713 | try expect(semaphore.wait(.{}) == OsCore.Error.TimedOut); 714 | task_control.setNextRunningTask(); 715 | try semaphore.abortAcquire(&test_task1); 716 | 717 | //clean up 718 | try semaphore.deinit(); 719 | } 720 | 721 | test "Semaphore timeout2" { 722 | task_setup(); 723 | var semaphore = Semaphore.create_semaphore(.{ .name = "test_sem", .inital_value = 0 }); 724 | var semaphore2 = Semaphore.create_semaphore(.{ .name = "test_sem2", .inital_value = 0 }); 725 | 726 | try semaphore.init(); 727 | try semaphore2.init(); 728 | 729 | try semaphore.wait(.{ .timeout_ms = 1 }); 730 | task_control.setNextRunningTask(); 731 | try semaphore2.wait(.{ .timeout_ms = 1 }); 732 | task_control.setNextRunningTask(); 733 | try expect(task_control.running_priority == 3); 734 | 735 | OsCore.systemTick(); 736 | try expect(test_task1._state == OsTask.State.running); 737 | try expect(test_task1._SyncContext.timed_out); 738 | try expect(test_task2._state == OsTask.State.ready); 739 | try expect(test_task2._SyncContext.timed_out); 740 | } 741 | 742 | ///////////////////////////////////////////// 743 | // Event Group Unit Tests // 744 | /////////////////////////////////////////// 745 | 746 | ///////////////////////////////////////////// 747 | // Msg Queue Unit Tests // 748 | /////////////////////////////////////////// 749 | 750 | ///////////////////////////////////////////// 751 | // Os Time Unit Tests // 752 | /////////////////////////////////////////// 753 | 754 | ///////////////////////////////////////////// 755 | // Os Core Unit Tests // 756 | /////////////////////////////////////////// 757 | 758 | ///////////////////////////////////////////// 759 | // Os Api Unit Tests // 760 | /////////////////////////////////////////// 761 | --------------------------------------------------------------------------------