diff --git a/src/kernel/arch/x86/arch.zig b/src/kernel/arch/x86/arch.zig index 420d982..3d097bc 100644 --- a/src/kernel/arch/x86/arch.zig +++ b/src/kernel/arch/x86/arch.zig @@ -240,6 +240,7 @@ test "" { _ = @import("pic.zig"); _ = @import("isr.zig"); _ = @import("irq.zig"); + _ = @import("pit.zig"); _ = @import("syscalls.zig"); _ = @import("paging.zig"); } diff --git a/src/kernel/arch/x86/pit.zig b/src/kernel/arch/x86/pit.zig index b523355..71f842e 100644 --- a/src/kernel/arch/x86/pit.zig +++ b/src/kernel/arch/x86/pit.zig @@ -1,11 +1,70 @@ -const arch = @import("arch.zig"); -const assert = @import("std").debug.assert; +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"); const log = @import("../../log.zig"); -const panic = @import("../../panic.zig").panic; -// The port addresses of the PIT registers +/// 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. @@ -23,7 +82,7 @@ const COUNTER_2_REGISTER: u16 = 0x42; /// 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 marks for the different modes. +// The operational command word for the different modes. // // Bit 0: (BCP) Binary Counter. // 0: Binary. @@ -49,65 +108,65 @@ const COMMAND_REGISTER: u16 = 0x43; // 11: Illegal value. /// Have the counter count in binary (internally?). -const OCW_BINARY_COUNT_BINARY: u8 = 0x00; // xxxxxxx0 +const OCW_BINARY_COUNT_BINARY: u8 = 0x00; /// Have the counter count in BCD (internally?). -const OCW_BINARY_COUNT_BCD: u8 = 0x01; // xxxxxxx1 +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; // xxxx000x +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 +/// 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; // xxxx001x +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; // xxxx010x +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; // xxxx011x +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; // xxxx100x +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; // xxxx101x +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; // xx00xxxx +const OCW_READ_LOAD_LATCH: u8 = 0x00; /// Read or load the most significant bit only. -const OCW_READ_LOAD_LSB_ONLY: u8 = 0x10; // xx01xxxx +const OCW_READ_LOAD_LSB_ONLY: u8 = 0x10; /// Read or load the least significant bit only. -const OCW_READ_LOAD_MSB_ONLY: u8 = 0x20; // xx10xxxx +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; // xx11xxxx +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; // 00xxxxxx +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; // 01xxxxxx +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; // 10xxxxxx +const OCW_SELECT_COUNTER_2: u8 = 0x80; -// The divisor constant -const MAX_FREQUENCY: u32 = 1193180; +/// 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; @@ -118,17 +177,20 @@ 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 +/// The current frequency of counter 0 var current_freq_0: u32 = undefined; -// The current frequency of counter 1 +/// The current frequency of counter 1 var current_freq_1: u32 = undefined; -// The current frequency of counter 2 +/// The current frequency of counter 2 var current_freq_2: u32 = undefined; -var time_ms: u32 = undefined; -var time_under_1_ms: 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. @@ -140,96 +202,110 @@ 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); + return u8(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 comptime counter: u16 - The counter port to send the data to. -/// IN data: u8 - The data to send. +/// IN counter: CounterSelect - The counter port to send the data to. +/// IN data: u8 - The data to send. /// -inline fn sendDateToCounter(comptime counter: u16, data: u8) void { - assert(counter == COUNTER_0_REGISTER or counter == COUNTER_1_REGISTER or counter == COUNTER_2_REGISTER); - arch.outb(counter, data); +inline fn sendDataToCounter(counter: CounterSelect, data: u8) void { + arch.outb(counter.getRegister(), data); } -fn pitHandler(context: *arch.InterruptContext) void { - ticks += 1; +/// +/// The interrupt handler for the PIT. This will increment a counter for now. +/// +/// Arguments: +/// IN ctx: *arch.InterruptContext - Pointer to the interrupt context containing the contents +/// of the register at the time of the interrupt. +/// +fn pitHandler(ctx: *arch.InterruptContext) void { + ticks +%= 1; } /// /// Set up a counter with a tick rate and mode of operation. /// /// Arguments: -/// IN freq: u16 - The frequency that the counter operates at. -/// IN counter: u8 - Which counter is to be set up, either OCW_SELECT_COUNTER_0, -/// OCW_SELECT_COUNTER_1 or OCW_SELECT_COUNTER_2 only. -/// 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. +/// 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. /// -fn setupCounter(comptime counter: u8, freq: u32, mode: u8) void { - var reload_value: u32 = 0x10000; // 65536, the slowest possible frequency +/// 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 + var reload_value = u32(0x10000); + + // The lowest possible frequency is 18Hz. + // MAX_FREQUENCY / 18 > u16 N + // MAX_FREQUENCY / 19 < u16 Y if (freq > 18) { - // The fastest possible frequency if frequency is too high - reload_value = 1; - if (freq < MAX_FREQUENCY) { - reload_value = MAX_FREQUENCY / freq; - var rem: u32 = MAX_FREQUENCY % freq; - - // Is the remainder more than half - if (rem > MAX_FREQUENCY / 2) { - // Round up - reload_value += 1; - } + // Rounded integer division + reload_value = (MAX_FREQUENCY + (freq / 2)) / freq; + } else { + // The fastest possible frequency if frequency is too high + reload_value = 1; } } - // Get the u16 version as this is what will be loaded into the PIT - var reload_val_16: u16 = @truncate(u16, reload_value); - // Update the frequency with the actual one that the PIT will be using - var frequency: u32 = MAX_FREQUENCY / reload_value; - var rem: u32 = MAX_FREQUENCY % reload_value; + // Rounded integer division + const frequency = (MAX_FREQUENCY + (reload_value / 2)) / reload_value; - // Is the remainder more than half - if (rem > MAX_FREQUENCY / 2) { - // Round up - frequency += 1; - } + // Calculate the amount of nanoseconds between interrupts + time_ns = u32(1000000000) / frequency; - // Calculate the amount of time between IRQs - time_ms = reload_value * u32(1000); - time_under_1_ms = time_ms % MAX_FREQUENCY; - time_ms /= MAX_FREQUENCY; - - comptime const port: u16 = switch (counter) { - OCW_SELECT_COUNTER_0 => COUNTER_0_REGISTER, - OCW_SELECT_COUNTER_1 => COUNTER_1_REGISTER, - OCW_SELECT_COUNTER_2 => COUNTER_2_REGISTER, - else => unreachable, - }; + // Calculate the number of picoseconds, the left over from nanoseconds + time_under_1_ns = ((u32(1000000000) % frequency) * u32(1000) + (frequency / 2)) / frequency; + // Set the frequency for the counter being set up switch (counter) { - OCW_SELECT_COUNTER_0 => current_freq_0 = frequency, - OCW_SELECT_COUNTER_1 => current_freq_1 = frequency, - OCW_SELECT_COUNTER_2 => current_freq_2 = frequency, - else => unreachable, + .Counter0 => current_freq_0 = frequency, + .Counter1 => current_freq_1 = frequency, + .Counter2 => current_freq_2 = frequency, } - var command: u8 = mode | OCW_READ_LOAD_DATA | counter; + // 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); - sendCommand(command); - - sendDateToCounter(port, @truncate(u8, reload_val_16)); - sendDateToCounter(port, @truncate(u8, reload_val_16 >> 8)); + // 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) { - OCW_SELECT_COUNTER_0 => ticks = 0, - OCW_SELECT_COUNTER_1 => ram_ticks = 0, - OCW_SELECT_COUNTER_2 => speaker_ticks = 0, - else => unreachable, + .Counter0 => ticks = 0, + .Counter1 => ram_ticks = 0, + .Counter2 => speaker_ticks = 0, } } @@ -237,22 +313,43 @@ fn setupCounter(comptime counter: u8, freq: u32, mode: u8) void { /// A simple wait that used the PIT to wait a number of ticks. /// /// Arguments: -/// IN milliseconds: u32 - The number of ticks to wait. +/// IN ticks_to_wait: u32 - The number of ticks to wait. /// pub fn waitTicks(ticks_to_wait: u32) void { - const wait_ticks = ticks + ticks_to_wait; - while (ticks < wait_ticks) { + if (ticks > u32(maxInt(u32)) - ticks_to_wait) { + // Integer overflow + + // Calculate the 2 conditions + const wait_ticks1 = u32(maxInt(u32)) - ticks; + const wait_ticks2 = ticks_to_wait - wait_ticks1; + + while (ticks > wait_ticks1) { + arch.enableInterrupts(); + arch.halt(); + arch.disableInterrupts(); + } + + while (ticks < wait_ticks2) { + arch.enableInterrupts(); + arch.halt(); + arch.disableInterrupts(); + } + arch.enableInterrupts(); + } else { + const wait_ticks = ticks + ticks_to_wait; + while (ticks < wait_ticks) { + arch.enableInterrupts(); + arch.halt(); + arch.disableInterrupts(); + } arch.enableInterrupts(); - arch.halt(); - arch.disableInterrupts(); } - arch.enableInterrupts(); } /// /// Get the number of ticks that have passed when the PIT was initiated. /// -/// Return: +/// Return: u32 /// Number of ticks passed. /// pub fn getTicks() u32 { @@ -262,7 +359,7 @@ pub fn getTicks() u32 { /// /// Get the frequency the PIT is ticking at. /// -/// Return: +/// Return: u32 /// The frequency the PIT is running at /// pub fn getFrequency() u32 { @@ -274,12 +371,13 @@ pub fn getFrequency() u32 { /// pub fn init() void { log.logInfo("Init pit\n"); - // Set up counter 0 at 1000hz in a square wave mode counting in binary - // TODO: https://github.com/ziglang/zig/issues/557, Need type defined - const freq: u32 = 10000; - setupCounter(OCW_SELECT_COUNTER_0, freq, OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_BINARY_COUNT_BINARY); + // Set up counter 0 at 10000hz in a square wave mode counting in binary + const freq = u32(10000); + setupCounter(CounterSelect.Counter0, freq, OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_BINARY_COUNT_BINARY) catch |e| { + panic(@errorReturnTrace(), "Invalid frequency: {}\n", freq); + }; - log.logInfo("Set frequency at: {}Hz, real frequency: {}Hz\n", freq, getFrequency()); + log.logDebug("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) { @@ -292,4 +390,252 @@ pub fn init() void { }; log.logInfo("Done\n"); + + if (build_options.rt_test) runtimeTests(); +} + +test "sendCommand" { + arch.initTest(); + defer arch.freeTest(); + + const cmd = u8(10); + + arch.addTestParams("outb", COMMAND_REGISTER, cmd); + + sendCommand(cmd); +} + +test "readBackCommand" { + arch.initTest(); + defer arch.freeTest(); + + const cmd = u8(0xC2); + + arch.addTestParams("outb", COMMAND_REGISTER, cmd); + arch.addTestParams("inb", COUNTER_0_REGISTER, u8(0x20)); + + const actual = readBackCommand(CounterSelect.Counter0); + + expectEqual(u8(0x20), actual); +} + +test "sendDataToCounter" { + arch.initTest(); + defer arch.freeTest(); + + const data = u8(10); + + 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(); + + var freq = u32(0); + + // Reload value will be 0 (0x10000), the slowest speed for frequency less than 19 + const expected_reload_value = u16(0); + + // Slowest frequency the PIT can run at + 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; + 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(); + + 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; + + 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(99730), time_ns); + expectEqual(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(), "Waiting failed. difference: {}, previous_count: {}. Epsilon: {}\n", difference, previous_count, epsilon); + } + + log.logInfo("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(), "Waiting failed. difference: {}, previous_count: {}. Epsilon: {}\n", difference, previous_count, epsilon); + } + + log.logInfo("PIT: Tested wait ticks 2\n"); + + // Reset ticks + ticks = 0; +} + +/// +/// Check that when the PIT is initialised, counter 0 is set up properly. +/// +fn rt_initCounter_0() void { + 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(), + "Frequency not set properly. Hz: {}!={}, ns: {}!={}, ps: {}!= {}\n", + getFrequency(), + expected_hz, + time_ns, + expected_ns, + time_under_1_ns, + expected_ps, + ); + } + + 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(), "IRQ for PIT, IRQ number: {} is invalid", pic.IRQ_PIT); + }, + }; + + if (!irq_exists) { + panic(@errorReturnTrace(), "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(), "Operating mode don't not set properly. Found: {}, expecting: {}\n", actual_mode, expected_mode); + } + + log.logInfo("PIT: Tested init\n"); +} + +/// +/// Run all the runtime tests. +/// +fn runtimeTests() void { + rt_initCounter_0(); + rt_waitTicks(); + rt_waitTicks2(); } diff --git a/src/kernel/tty.zig b/src/kernel/tty.zig index 5f338a6..d3e4cff 100644 --- a/src/kernel/tty.zig +++ b/src/kernel/tty.zig @@ -250,7 +250,7 @@ fn putEntryAt(char: u8, x: u8, y: u8) TtyError!void { /// TtyError.OutOfBounds - If trying to move up more rows on a page. /// fn pagesMoveRowsUp(rows: u16) TtyError!void { - // Out of bounds check, also no need to move 0 rows + // Out of bounds check if (rows > ROW_TOTAL) { return TtyError.OutOfBounds; } @@ -284,13 +284,16 @@ fn pagesMoveRowsUp(rows: u16) TtyError!void { /// TtyError.OutOfBounds - If trying to move up more rows on a page. This shouldn't happen /// as bounds checks have been done. /// -fn scroll() TtyError!void { +fn scroll() void { // Added the condition in the if from pagesMoveRowsUp as don't need to move all rows if (row >= vga.HEIGHT and (row - vga.HEIGHT + 1) <= ROW_TOTAL) { const rows_to_move = row - vga.HEIGHT + 1; // Move rows up pages by temp, will usually be one. - try pagesMoveRowsUp(rows_to_move); + // TODO: Maybe panic here as we have the check above, so if this fails, then is a big problem + pagesMoveRowsUp(rows_to_move) catch |e| { + panic(@errorReturnTrace(), "Can't move {} rows up. Must be less than {}\n", rows_to_move, ROW_TOTAL); + }; // Move all rows up by rows_to_move var i = u32(0); @@ -333,14 +336,14 @@ fn putChar(char: u8) TtyError!void { '\n' => { column = 0; row += 1; - try scroll(); + scroll(); }, '\t' => { column += 4; if (column >= vga.WIDTH) { column -= @truncate(u8, vga.WIDTH); row += 1; - try scroll(); + scroll(); } }, '\r' => { @@ -363,7 +366,7 @@ fn putChar(char: u8) TtyError!void { if (column == vga.WIDTH) { column = 0; row += 1; - try scroll(); + scroll(); } }, } @@ -1203,7 +1206,7 @@ test "scroll row is less then max height" { incrementingPagesTesting(); // Call function - try scroll(); + scroll(); // Post test defaultVariablesTesting(0, 0, 0); @@ -1229,7 +1232,7 @@ test "scroll row is equal to height" { // Call function // Rows move up one - try scroll(); + scroll(); // Post test defaultVariablesTesting(0, vga.HEIGHT - 1, 0); @@ -1280,7 +1283,7 @@ test "scroll row is more than height" { // Call function // Rows move up 5 - try scroll(); + scroll(); // Post test defaultVariablesTesting(0, vga.HEIGHT - 1, 0); diff --git a/test/kernel/arch/x86/rt-test.py b/test/kernel/arch/x86/rt-test.py index 430ab6a..c9a15c3 100644 --- a/test/kernel/arch/x86/rt-test.py +++ b/test/kernel/arch/x86/rt-test.py @@ -10,7 +10,10 @@ def get_test_cases(TestCase): TestCase("ISR tests", [r"ISR: Tested registered handlers", r"ISR: Tested opened IDT entries"]), TestCase("IRQ init", [r"Init irq", r"Done"]), TestCase("IRQ tests", [r"IRQ: Tested registered handlers", r"IRQ: Tested opened IDT entries"]), - TestCase("PIT init", [r"Init pit", r".+", r"Done"]), + TestCase("PIT init", [r"Init pit"]), + TestCase("PIT init", [r".+"], r"\[DEBUG\] "), + TestCase("PIT init", [r"Done"]), + TestCase("PIT tests", [r"PIT: Tested init", r"PIT: Tested wait ticks", r"PIT: Tested wait ticks 2"]), TestCase("Paging init", [r"Init paging", r"Done"]), TestCase("Paging tests", [r"Paging: Tested accessing unmapped memory", r"Paging: Tested accessing mapped memory"]), TestCase("Syscalls init", [r"Init syscalls", r"Done"]), diff --git a/test/mock/kernel/arch_mock.zig b/test/mock/kernel/arch_mock.zig index 2b7dc5e..b8d0efb 100644 --- a/test/mock/kernel/arch_mock.zig +++ b/test/mock/kernel/arch_mock.zig @@ -91,3 +91,9 @@ pub fn init(mem_profile: *const MemProfile, allocator: *Allocator, comptime opti // When I come on to the mem.zig testing, I'll fix :) //return mock_framework.performAction("init", void, mem_profile, allocator); } + +// User defined mocked functions + +pub fn mock_disableInterrupts() void {} + +pub fn mock_enableInterrupts() void {}