pluto/src/kernel/arch/x86/pit.zig

639 lines
21 KiB
Zig
Raw Normal View History

const std = @import("std");
const maxInt = std.math.maxInt;
const builtin = @import("builtin");
const is_test = builtin.is_test;
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const expectError = std.testing.expectError;
const build_options = @import("build_options");
const mock_path = build_options.arch_mock_path;
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig");
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("../../panic.zig").panic;
const irq = @import("irq.zig");
const pic = @import("pic.zig");
/// The enum for selecting the counter
const CounterSelect = enum {
/// Counter 0.
Counter0,
/// Counter 1.
Counter1,
/// Counter 2.
Counter2,
///
/// Get the register port for the selected counter.
///
/// Arguments:
/// IN counter: CounterSelect - The counter to get the register port.
///
/// Return: u16
/// The register port for the selected counter.
///
pub fn getRegister(counter: CounterSelect) u16 {
return switch (counter) {
.Counter0 => COUNTER_0_REGISTER,
.Counter1 => COUNTER_1_REGISTER,
.Counter2 => COUNTER_2_REGISTER,
};
}
///
/// Get the operational control work for the selected counter.
///
/// Arguments:
/// IN counter: CounterSelect - The counter to get the operational control work.
///
/// Return: u16
/// The operational control work for the selected counter.
///
pub fn getCounterOCW(counter: CounterSelect) u8 {
return switch (counter) {
.Counter0 => OCW_SELECT_COUNTER_0,
.Counter1 => OCW_SELECT_COUNTER_1,
.Counter2 => OCW_SELECT_COUNTER_2,
};
}
};
/// The error set that can be returned from PIT functions
const PitError = error{
/// The frequency to be used for a counter is invalid. This would be if the frequency is less
/// than 19 or greater than MAX_FREQUENCY.
InvalidFrequency,
};
/// The port address for the PIT data register for counter 0. This is going to be used as the
/// system clock.
const COUNTER_0_REGISTER: u16 = 0x40;
/// The port address for the PIT data register for counter 1. This was used for refreshing the DRAM
/// chips. But now is unused and unknown use, so won't use.
const COUNTER_1_REGISTER: u16 = 0x41;
/// The port address for the PIT data register for counter 2. Connected to the PC speakers, we'll
/// use this for the speakers.
const COUNTER_2_REGISTER: u16 = 0x42;
/// The port address for the PIT control word register. Used to tell the PIT controller what is
/// about to happen. Tell what data is going where, lower or upper part of it's registers.
const COMMAND_REGISTER: u16 = 0x43;
// The operational command word for the different modes.
//
// Bit 0: (BCP) Binary Counter.
// 0: Binary.
// 1: Binary Coded Decimal (BCD).
// Bit 1-3: (M0, M1, M2) Operating Mode. See above sections for a description of each.
// 000: Mode 0: Interrupt or Terminal Count.
// 001: Mode 1: Programmable one-shot.
// 010: Mode 2: Rate Generator.
// 011: Mode 3: Square Wave Generator.
// 100: Mode 4: Software Triggered Strobe.
// 101: Mode 5: Hardware Triggered Strobe.
// 110: Undefined; Don't use.
// 111: Undefined; Don't use.
// Bits 4-5: (RL0, RL1) Read/Load Mode. We are going to read or send data to a counter register.
// 00: Counter value is latched into an internal control register at the time of the I/O write operation.
// 01: Read or Load Least Significant Byte (LSB) only.
// 10: Read or Load Most Significant Byte (MSB) only.
// 11: Read or Load LSB first then MSB.
// Bits 6-7: (SC0-SC1) Select Counter. See above sections for a description of each.
// 00: Counter 0.
// 01: Counter 1.
// 10: Counter 2.
// 11: Illegal value.
/// Have the counter count in binary (internally?).
const OCW_BINARY_COUNT_BINARY: u8 = 0x00;
/// Have the counter count in BCD (internally?).
const OCW_BINARY_COUNT_BCD: u8 = 0x01;
/// The PIT counter will be programmed with an initial COUNT value that counts down at a rate of
/// the input clock frequency. When the COUNT reaches 0, and after the control word is written,
/// then its OUT pit is set high (1). Count down starts then the COUNT is set. The OUT pin remains
/// high until the counter is reloaded with a new COUNT value or a new control work is written.
const OCW_MODE_TERMINAL_COUNT: u8 = 0x00;
/// The counter is programmed to output a pulse every curtain number of clock pulses. The OUT pin
/// remains high as soon as a control word is written. When COUNT is written, the counter waits
/// until the rising edge of the GATE pin to start. One clock pulse after the GATE pin, the OUT
/// pin will remain low until COUNT reaches 0.
const OCW_MODE_ONE_SHOT: u8 = 0x02;
/// The counter is initiated with a COUNT value. Counting starts next clock pulse. OUT pin remains
/// high until COUNT reaches 1, then is set low for one clock pulse. Then COUNT is reset back to
/// initial value and OUT pin is set high again.
const OCW_MODE_RATE_GENERATOR: u8 = 0x04;
/// Similar to PIT_OCW_MODE_RATE_GENERATOR, but OUT pin will be high for half the time and low for
/// half the time. Good for the speaker when setting a tone.
const OCW_MODE_SQUARE_WAVE_GENERATOR: u8 = 0x06;
/// The counter is initiated with a COUNT value. Counting starts on next clock pulse. OUT pin remains
/// high until count is 0. Then OUT pin is low for one clock pulse. Then resets to high again.
const OCW_MODE_SOFTWARE_TRIGGER: u8 = 0x08;
/// The counter is initiated with a COUNT value. OUT pin remains high until the rising edge of the
/// GATE pin. Then the counting begins. When COUNT reaches 0, OUT pin goes low for one clock pulse.
/// Then COUNT is reset and OUT pin goes high. This cycles for each rising edge of the GATE pin.
const OCW_MODE_HARDWARE_TRIGGER: u8 = 0x0A;
/// The counter value is latched into an internal control register at the time of the I/O write
/// operations.
const OCW_READ_LOAD_LATCH: u8 = 0x00;
/// Read or load the most significant bit only.
const OCW_READ_LOAD_LSB_ONLY: u8 = 0x10;
/// Read or load the least significant bit only.
const OCW_READ_LOAD_MSB_ONLY: u8 = 0x20;
/// Read or load the least significant bit first then the most significant bit.
const OCW_READ_LOAD_DATA: u8 = 0x30;
/// The OCW bits for selecting counter 0. Used for the system clock.
const OCW_SELECT_COUNTER_0: u8 = 0x00;
/// The OCW bits for selecting counter 1. Was for the memory refreshing.
const OCW_SELECT_COUNTER_1: u8 = 0x40;
/// The OCW bits for selecting counter 2. Channel for the speaker.
const OCW_SELECT_COUNTER_2: u8 = 0x80;
/// The divisor constant
const MAX_FREQUENCY: u32 = 1193182;
/// The number of ticks that has passed when counter 0 was initially set up.
var ticks: u32 = 0;
/// The number of tick that has passed when counter 1 was initially set up.
var ram_ticks: u32 = 0;
/// The number of tick that has passed when counter 2 was initially set up.
var speaker_ticks: u32 = 0;
/// The current frequency of counter 0
var current_freq_0: u32 = undefined;
/// The current frequency of counter 1
var current_freq_1: u32 = undefined;
/// The current frequency of counter 2
var current_freq_2: u32 = undefined;
/// The number of nanoseconds between interrupts.
var time_ns: u32 = undefined;
/// The number of nanoseconds to be added the to time_ns for time between interrupts.
var time_under_1_ns: u32 = undefined;
///
/// Send a command to the PIT command register.
///
/// Arguments:
/// IN cmd: u8 - The command to send to the PIT.
///
inline fn sendCommand(cmd: u8) void {
arch.outb(COMMAND_REGISTER, cmd);
}
///
/// Read the current mode of the selected counter.
///
/// Arguments:
/// IN counter: CounterSelect - The counter to read the mode the counter is operating in.
///
/// Return: u8
/// The mode the counter is operating in. Use the masks above to get each part.
///
inline fn readBackCommand(counter: CounterSelect) u8 {
sendCommand(0xC2);
2019-11-10 13:35:08 +01:00
return 0x3F & arch.inb(counter.getRegister());
}
///
/// Send data to a given counter. Will be only one of the 3 counters as is an internal function.
///
/// Arguments:
/// IN counter: CounterSelect - The counter port to send the data to.
/// IN data: u8 - The data to send.
///
inline fn sendDataToCounter(counter: CounterSelect, data: u8) void {
arch.outb(counter.getRegister(), data);
}
///
/// The interrupt handler for the PIT. This will increment a counter for now.
///
/// Arguments:
Initial scheduler Fix TSS Also change to .{} syntax where appropriate. Added the SS segment Fixed spelling Refactoring GDT Multitasking working for now WIP scheduler Refactored Bitmap a bit WIP still Task switching working Handlers return the stack pointer that will be used to restore the tasks stack, normal handlers will return the same stack pointer it was called with where task switching will return the stack pointer of the next task and restore its state using the interrupt stub. Initial scheduler done Created a stage 2 init task Change u32 to usize Move Task to arch specific WIP WIP2 Removed esp from task, replaced with stack_pointer Removed the debug logs Fixed init task stack Change pickNextTask to pointer manipulation This allows less allocations so faster switching Temporary enable interrupts for some runtime tests PIT and RTC need interrupts enabled to run their runtime tests Renamed schedule => pickNextTask, comptime bitmap for pids not task init And some other stuff: No pub for the task anymore Use the leak detector allocator Fmt Fix unit tests And some other stuff :P PR review Moved Task out of arch and have the stack init in the arch file Mocking clean up Removed commented code Renamed createTask to scheduleTask where the user will have to provide a task to schedule Removed redundant pub in log runtime test Removed global allocator for scheduler Cleaner assembly in paging Fmt Added new Scheduler test mode Added new test mode to CI Removed one of the prints Added doc comment, task test for i386 Removed test WIP Runtime tests work Have a global set in one task and reacted to in another. Also test that local variables are preserved after a task switch. Removed new lines Increased line length Move the allocation of the bool above the task creation
2020-07-18 23:46:24 +02:00
/// IN ctx: *arch.CpuState - Pointer to the interrupt context containing the contents
/// of the register at the time of the interrupt.
///
Initial scheduler Fix TSS Also change to .{} syntax where appropriate. Added the SS segment Fixed spelling Refactoring GDT Multitasking working for now WIP scheduler Refactored Bitmap a bit WIP still Task switching working Handlers return the stack pointer that will be used to restore the tasks stack, normal handlers will return the same stack pointer it was called with where task switching will return the stack pointer of the next task and restore its state using the interrupt stub. Initial scheduler done Created a stage 2 init task Change u32 to usize Move Task to arch specific WIP WIP2 Removed esp from task, replaced with stack_pointer Removed the debug logs Fixed init task stack Change pickNextTask to pointer manipulation This allows less allocations so faster switching Temporary enable interrupts for some runtime tests PIT and RTC need interrupts enabled to run their runtime tests Renamed schedule => pickNextTask, comptime bitmap for pids not task init And some other stuff: No pub for the task anymore Use the leak detector allocator Fmt Fix unit tests And some other stuff :P PR review Moved Task out of arch and have the stack init in the arch file Mocking clean up Removed commented code Renamed createTask to scheduleTask where the user will have to provide a task to schedule Removed redundant pub in log runtime test Removed global allocator for scheduler Cleaner assembly in paging Fmt Added new Scheduler test mode Added new test mode to CI Removed one of the prints Added doc comment, task test for i386 Removed test WIP Runtime tests work Have a global set in one task and reacted to in another. Also test that local variables are preserved after a task switch. Removed new lines Increased line length Move the allocation of the bool above the task creation
2020-07-18 23:46:24 +02:00
fn pitHandler(ctx: *arch.CpuState) usize {
ticks +%= 1;
Initial scheduler Fix TSS Also change to .{} syntax where appropriate. Added the SS segment Fixed spelling Refactoring GDT Multitasking working for now WIP scheduler Refactored Bitmap a bit WIP still Task switching working Handlers return the stack pointer that will be used to restore the tasks stack, normal handlers will return the same stack pointer it was called with where task switching will return the stack pointer of the next task and restore its state using the interrupt stub. Initial scheduler done Created a stage 2 init task Change u32 to usize Move Task to arch specific WIP WIP2 Removed esp from task, replaced with stack_pointer Removed the debug logs Fixed init task stack Change pickNextTask to pointer manipulation This allows less allocations so faster switching Temporary enable interrupts for some runtime tests PIT and RTC need interrupts enabled to run their runtime tests Renamed schedule => pickNextTask, comptime bitmap for pids not task init And some other stuff: No pub for the task anymore Use the leak detector allocator Fmt Fix unit tests And some other stuff :P PR review Moved Task out of arch and have the stack init in the arch file Mocking clean up Removed commented code Renamed createTask to scheduleTask where the user will have to provide a task to schedule Removed redundant pub in log runtime test Removed global allocator for scheduler Cleaner assembly in paging Fmt Added new Scheduler test mode Added new test mode to CI Removed one of the prints Added doc comment, task test for i386 Removed test WIP Runtime tests work Have a global set in one task and reacted to in another. Also test that local variables are preserved after a task switch. Removed new lines Increased line length Move the allocation of the bool above the task creation
2020-07-18 23:46:24 +02:00
return @ptrToInt(ctx);
}
///
/// Set up a counter with a tick rate and mode of operation.
///
/// Arguments:
/// IN counter: CounterSelect - Which counter is to be set up.
/// IN freq: u32 - The frequency that the counter operates at. Any frequency that
/// is between 0..18 (inclusive) or above MAX_FREQUENCY will return
/// an error.
/// IN mode: u8 - The mode of operation that the counter will operate in. See
/// the modes definition above to chose which mode the counter is
/// to run at.
///
/// Error: PitError:
/// PitError.InvalidFrequency - If the given frequency is out of bounds. Less than 19 or
/// greater than MAX_FREQUENCY.
///
fn setupCounter(counter: CounterSelect, freq: u32, mode: u8) PitError!void {
if (freq < 19 or freq > MAX_FREQUENCY) {
return PitError.InvalidFrequency;
}
// 65536, the slowest possible frequency. Roughly 19Hz
2019-11-10 13:35:08 +01:00
var reload_value: u32 = 0x10000;
// The lowest possible frequency is 18Hz.
// MAX_FREQUENCY / 18 > u16 N
// MAX_FREQUENCY / 19 < u16 Y
if (freq > 18) {
if (freq < MAX_FREQUENCY) {
// Rounded integer division
reload_value = (MAX_FREQUENCY + (freq / 2)) / freq;
} else {
// The fastest possible frequency if frequency is too high
reload_value = 1;
}
}
// Update the frequency with the actual one that the PIT will be using
// Rounded integer division
const frequency = (MAX_FREQUENCY + (reload_value / 2)) / reload_value;
// Calculate the amount of nanoseconds between interrupts
2019-11-10 13:35:08 +01:00
time_ns = 1000000000 / frequency;
// Calculate the number of picoseconds, the left over from nanoseconds
2019-11-10 13:35:08 +01:00
time_under_1_ns = ((1000000000 % frequency) * 1000 + (frequency / 2)) / frequency;
// Set the frequency for the counter being set up
switch (counter) {
.Counter0 => current_freq_0 = frequency,
.Counter1 => current_freq_1 = frequency,
.Counter2 => current_freq_2 = frequency,
}
// Get the u16 version as this is what will be loaded into the PIT
// If truncating 0x10000, this will equal 0, which is the slowest.
const reload_val_16 = @truncate(u16, reload_value);
// Send the set up command to the PIT
sendCommand(mode | OCW_READ_LOAD_DATA | counter.getCounterOCW());
sendDataToCounter(counter, @truncate(u8, reload_val_16));
sendDataToCounter(counter, @truncate(u8, reload_val_16 >> 8));
// Reset the counter ticks
switch (counter) {
.Counter0 => ticks = 0,
.Counter1 => ram_ticks = 0,
.Counter2 => speaker_ticks = 0,
}
}
///
/// A simple wait that used the PIT to wait a number of ticks.
///
/// Arguments:
/// IN ticks_to_wait: u32 - The number of ticks to wait.
///
pub fn waitTicks(ticks_to_wait: u32) void {
2019-11-10 13:35:08 +01:00
if (ticks > maxInt(u32) - ticks_to_wait) {
// Integer overflow
// Calculate the 2 conditions
2019-11-10 13:35:08 +01:00
const wait_ticks1 = maxInt(u32) - ticks;
const wait_ticks2 = ticks_to_wait - wait_ticks1;
while (ticks > wait_ticks1) {
arch.halt();
}
while (ticks < wait_ticks2) {
arch.halt();
}
} else {
const wait_ticks = ticks + ticks_to_wait;
while (ticks < wait_ticks) {
arch.halt();
}
}
}
///
/// Get the number of ticks that have passed when the PIT was initiated.
///
/// Return: u32
/// Number of ticks passed.
///
pub fn getTicks() u32 {
return ticks;
}
///
/// Get the frequency the PIT is ticking at.
///
/// Return: u32
/// The frequency the PIT is running at
///
pub fn getFrequency() u32 {
return current_freq_0;
}
///
/// Initialise the PIT with a handler to IRQ 0.
///
pub fn init() void {
std.log.info(.pit, "Init\n", .{});
defer std.log.info(.pit, "Done\n", .{});
// Set up counter 0 at 10000hz in a square wave mode counting in binary
2019-11-10 13:35:08 +01:00
const freq: u32 = 10000;
setupCounter(CounterSelect.Counter0, freq, OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_BINARY_COUNT_BINARY) catch |e| {
2020-01-01 20:12:36 +01:00
panic(@errorReturnTrace(), "Invalid frequency: {}\n", .{freq});
};
std.log.debug(.pit, "Set frequency at: {}Hz, real frequency: {}Hz\n", .{ freq, getFrequency() });
// Installs 'pitHandler' to IRQ0 (pic.IRQ_PIT)
irq.registerIrq(pic.IRQ_PIT, pitHandler) catch |err| switch (err) {
error.IrqExists => {
2020-01-01 20:12:36 +01:00
panic(@errorReturnTrace(), "IRQ for PIT, IRQ number: {} exists", .{pic.IRQ_PIT});
},
error.InvalidIrq => {
2020-01-01 20:12:36 +01:00
panic(@errorReturnTrace(), "IRQ for PIT, IRQ number: {} is invalid", .{pic.IRQ_PIT});
},
};
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
test "sendCommand" {
arch.initTest();
defer arch.freeTest();
2019-11-10 13:35:08 +01:00
const cmd: u8 = 10;
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ COMMAND_REGISTER, cmd });
sendCommand(cmd);
}
test "readBackCommand" {
arch.initTest();
defer arch.freeTest();
2019-11-10 13:35:08 +01:00
const cmd: u8 = 0xC2;
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ COMMAND_REGISTER, cmd });
arch.addTestParams("inb", .{ COUNTER_0_REGISTER, @as(u8, 0x20) });
const actual = readBackCommand(CounterSelect.Counter0);
2019-11-10 13:35:08 +01:00
expectEqual(@as(u8, 0x20), actual);
}
test "sendDataToCounter" {
arch.initTest();
defer arch.freeTest();
2019-11-10 13:35:08 +01:00
const data: u8 = 10;
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ COUNTER_0_REGISTER, data });
sendDataToCounter(CounterSelect.Counter0, data);
}
test "setupCounter lowest frequency" {
arch.initTest();
defer arch.freeTest();
const counter = CounterSelect.Counter0;
const port = counter.getRegister();
2019-11-10 13:35:08 +01:00
var freq: u32 = 0;
// Reload value will be 0 (0x10000), the slowest speed for frequency less than 19
2019-11-10 13:35:08 +01:00
const expected_reload_value: u16 = 0;
// Slowest frequency the PIT can run at
2019-11-10 13:35:08 +01:00
const expected_freq: u32 = 19;
const mode = OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_BINARY_COUNT_BINARY;
const command = mode | OCW_READ_LOAD_DATA | counter.getCounterOCW();
while (freq <= 18) : (freq += 1) {
// arch.addTestParams("outb", COMMAND_REGISTER, command, port, @truncate(u8, expected_reload_value), port, @truncate(u8, expected_reload_value >> 8));
expectError(PitError.InvalidFrequency, setupCounter(counter, freq, mode));
// expectEqual(u32(0), ticks);
// expectEqual(expected_freq, current_freq_0);
// expectEqual(expected_freq, getFrequency());
// // These are the hard coded expected values. Calculated externally to check the internal calculation
// expectEqual(u32(52631578), time_ns);
// expectEqual(u32(947), time_under_1_ns);
}
// Reset globals
time_ns = 0;
current_freq_0 = 0;
ticks = 0;
}
test "setupCounter highest frequency" {
arch.initTest();
defer arch.freeTest();
const counter = CounterSelect.Counter0;
const port = counter.getRegister();
// Set the frequency above the maximum
const freq = MAX_FREQUENCY + 10;
// Reload value will be 1, the fastest speed for frequency greater than MAX_FREQUENCY
const expected_reload_value = 1;
// Slowest frequency the PIT can run at
const expected_freq = MAX_FREQUENCY;
const mode = OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_BINARY_COUNT_BINARY;
const command = mode | OCW_READ_LOAD_DATA | counter.getCounterOCW();
// arch.addTestParams("outb", COMMAND_REGISTER, command, port, @truncate(u8, expected_reload_value), port, @truncate(u8, expected_reload_value >> 8));
expectError(PitError.InvalidFrequency, setupCounter(counter, freq, mode));
// expectEqual(u32(0), ticks);
// expectEqual(expected_freq, current_freq_0);
// expectEqual(expected_freq, getFrequency());
// // These are the hard coded expected values. Calculated externally to check the internal calculation
// expectEqual(u32(838), time_ns);
// expectEqual(u32(95), time_under_1_ns);
// Reset globals
time_ns = 0;
current_freq_0 = 0;
ticks = 0;
}
test "setupCounter normal frequency" {
arch.initTest();
defer arch.freeTest();
const counter = CounterSelect.Counter0;
const port = counter.getRegister();
// Set the frequency to a normal frequency
const freq = 10000;
const expected_reload_value = 119;
2019-11-10 13:35:08 +01:00
const expected_freq: u32 = 10027;
const mode = OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_BINARY_COUNT_BINARY;
const command = mode | OCW_READ_LOAD_DATA | counter.getCounterOCW();
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ COMMAND_REGISTER, command, port, @truncate(u8, expected_reload_value), port, @truncate(u8, expected_reload_value >> 8) });
setupCounter(counter, freq, mode) catch unreachable;
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 0), ticks);
expectEqual(expected_freq, current_freq_0);
expectEqual(expected_freq, getFrequency());
// These are the hard coded expected values. Calculated externally to check the internal calculation
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 99730), time_ns);
expectEqual(@as(u32, 727), time_under_1_ns);
// Reset globals
time_ns = 0;
current_freq_0 = 0;
ticks = 0;
}
///
/// Test that waiting a number of ticks and then getting the number of ticks match.
///
fn rt_waitTicks() void {
const waiting = 1000;
const epsilon = 2 * getFrequency() / 10000;
const previous_count = getTicks();
waitTicks(waiting);
const difference = getTicks() - waiting;
if (previous_count + epsilon < difference or previous_count > difference + epsilon) {
panic(@errorReturnTrace(), "FAILURE: Waiting failed. difference: {}, previous_count: {}. Epsilon: {}\n", .{ difference, previous_count, epsilon });
}
std.log.info(.pit, "Tested wait ticks\n", .{});
}
///
/// Test that waiting a number of ticks and then getting the number of ticks match. This version
/// checks for the ticks wrap around.
///
fn rt_waitTicks2() void {
// Set the ticks to 16 less than the max
const waiting = 1000;
const epsilon = 2 * getFrequency() / 10000;
ticks = 0xFFFFFFF0;
const previous_count = getTicks() - 0xFFFFFFF0;
waitTicks(waiting);
// maxInt(u32) - u32(0xFFFFFFF0) = 15
const difference = getTicks() + 15 - waiting;
if (previous_count + epsilon < difference or previous_count > difference + epsilon) {
panic(@errorReturnTrace(), "FAILURE: Waiting failed. difference: {}, previous_count: {}. Epsilon: {}\n", .{ difference, previous_count, epsilon });
}
// Reset ticks
ticks = 0;
std.log.info(.pit, "Tested wait ticks 2\n", .{});
}
///
/// Check that when the PIT is initialised, counter 0 is set up properly.
///
fn rt_initCounter_0() void {
2019-11-10 13:35:08 +01:00
const expected_ns: u32 = 99730;
const expected_ps: u32 = 727;
const expected_hz: u32 = 10027;
if (time_ns != expected_ns or time_under_1_ns != expected_ps or getFrequency() != expected_hz) {
panic(@errorReturnTrace(), "FAILURE: Frequency not set properly. Hz: {}!={}, ns: {}!={}, ps: {}!= {}\n", .{
getFrequency(),
expected_hz,
time_ns,
expected_ns,
time_under_1_ns,
expected_ps,
2020-01-01 20:12:36 +01:00
});
}
var irq_exists = false;
irq.registerIrq(pic.IRQ_PIT, pitHandler) catch |err| switch (err) {
error.IrqExists => {
// We should get this error
irq_exists = true;
},
error.InvalidIrq => {
panic(@errorReturnTrace(), "FAILURE: IRQ for PIT, IRQ number: {} is invalid", .{pic.IRQ_PIT});
},
};
if (!irq_exists) {
panic(@errorReturnTrace(), "FAILURE: IRQ for PIT doesn't exists\n", .{});
}
const expected_mode = OCW_READ_LOAD_DATA | OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_SELECT_COUNTER_0 | OCW_BINARY_COUNT_BINARY;
const actual_mode = readBackCommand(CounterSelect.Counter0);
if (expected_mode != actual_mode) {
panic(@errorReturnTrace(), "FAILURE: Operating mode don't not set properly. Found: {}, expecting: {}\n", .{ actual_mode, expected_mode });
}
std.log.info(.pit, "Tested init\n", .{});
}
///
/// Run all the runtime tests.
///
Initial scheduler Fix TSS Also change to .{} syntax where appropriate. Added the SS segment Fixed spelling Refactoring GDT Multitasking working for now WIP scheduler Refactored Bitmap a bit WIP still Task switching working Handlers return the stack pointer that will be used to restore the tasks stack, normal handlers will return the same stack pointer it was called with where task switching will return the stack pointer of the next task and restore its state using the interrupt stub. Initial scheduler done Created a stage 2 init task Change u32 to usize Move Task to arch specific WIP WIP2 Removed esp from task, replaced with stack_pointer Removed the debug logs Fixed init task stack Change pickNextTask to pointer manipulation This allows less allocations so faster switching Temporary enable interrupts for some runtime tests PIT and RTC need interrupts enabled to run their runtime tests Renamed schedule => pickNextTask, comptime bitmap for pids not task init And some other stuff: No pub for the task anymore Use the leak detector allocator Fmt Fix unit tests And some other stuff :P PR review Moved Task out of arch and have the stack init in the arch file Mocking clean up Removed commented code Renamed createTask to scheduleTask where the user will have to provide a task to schedule Removed redundant pub in log runtime test Removed global allocator for scheduler Cleaner assembly in paging Fmt Added new Scheduler test mode Added new test mode to CI Removed one of the prints Added doc comment, task test for i386 Removed test WIP Runtime tests work Have a global set in one task and reacted to in another. Also test that local variables are preserved after a task switch. Removed new lines Increased line length Move the allocation of the bool above the task creation
2020-07-18 23:46:24 +02:00
pub fn runtimeTests() void {
// Interrupts aren't enabled yet, so for the runtime tests, enable it temporary
arch.enableInterrupts();
defer arch.disableInterrupts();
rt_initCounter_0();
rt_waitTicks();
rt_waitTicks2();
}