Merge pull request #107 from SamTebbs33/pit-adding-tests
Added run time tests for PIT
This commit is contained in:
		
						commit
						e5e8939c3f
					
				
					 5 changed files with 469 additions and 110 deletions
				
			
		|  | @ -240,6 +240,7 @@ test "" { | |||
|     _ = @import("pic.zig"); | ||||
|     _ = @import("isr.zig"); | ||||
|     _ = @import("irq.zig"); | ||||
|     _ = @import("pit.zig"); | ||||
|     _ = @import("syscalls.zig"); | ||||
|     _ = @import("paging.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(); | ||||
| } | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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"]), | ||||
|  |  | |||
|  | @ -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 {} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Edward Dean
						Edward Dean