Page 1 of 1

Keyboard and Framebuffer issues

Posted: Mon May 27, 2024 6:07 am
by Senc39
https://github.com/Senc3951/MyOS
Hi, I have 2 questions about my OS I hope someone can answer.

1. When testing the keyboard driver, I noticed that if I spam keys, at first everything works and the letters are printed to the screen, however, after a couple of seconds it stops generating interrupts. upon further investigating I saw that if I print the letters only to the serial output and not to the screen (Framebuffer), it will still stop generating interrupts but after a longer time, so I guess it happens because the fb is slow and the kernel doesn't have time to send an EOI while another key is pressed. (located at kernel/src/dev/ps2/kbd, taken from xv6)

2. When initializing the framebuffer and clearing the screen, I saw that it takes around 1.5sec to clear the screen. To speed things up, I mapped it as WC but I can't see a difference. I saw that someone said that it happens because of qemu and on actual hardware it would be faster, but when experimenting with Limine bootloader, the fb reacted way faster although they used the same caching for all the memory (WC for fb, WB for rest). (cache init at kernel/src/mem/vmm, fb at kernel/src/dev/fb/screen)

Re: Keyboard and Framebuffer issues

Posted: Mon May 27, 2024 11:56 am
by thewrongchristian
Senc39 wrote:https://github.com/Senc3951/MyOS
Hi, I have 2 questions about my OS I hope someone can answer.

1. When testing the keyboard driver, I noticed that if I spam keys, at first everything works and the letters are printed to the screen, however, after a couple of seconds it stops generating interrupts. upon further investigating I saw that if I print the letters only to the serial output and not to the screen (Framebuffer), it will still stop generating interrupts but after a longer time, so I guess it happens because the fb is slow and the kernel doesn't have time to send an EOI while another key is pressed. (located at kernel/src/dev/ps2/kbd, taken from xv6)
Your interrupt handler will be running with further interrupts disabled, so doing potentially long running (relatively) operations like interpreting the scan code into a character, and echoing it to the screen, will mean your OS will be ignoring further, potentially high priority interrupts until you're done handling all this work.

Your interrupt handler wants to do the minimal amount of work. You want to read the scan code, queue it for further processing (some sort of software FIFO), then get out of interrupt context ASAP.

Once it's in the FIFO, you can schedule a thread to further process the scan code, do any interpretation, echo characters back out etc.

Note also that the PS2 controller also handles mouse input, and if not correctly configured and handled, pending mouse input will block further keyboard input from interrupting.
Senc39 wrote: 2. When initializing the framebuffer and clearing the screen, I saw that it takes around 1.5sec to clear the screen. To speed things up, I mapped it as WC but I can't see a difference. I saw that someone said that it happens because of qemu and on actual hardware it would be faster, but when experimenting with Limine bootloader, the fb reacted way faster although they used the same caching for all the memory (WC for fb, WB for rest). (cache init at kernel/src/mem/vmm, fb at kernel/src/dev/fb/screen)
WC makes no difference to QEMU.

WC works by writing lots of memory together in a single burst over PCI. Normally, when you write, say, a 32-bit word to a PA mapped to a PCI memory address, that 32-bit write will require a whole PCI transaction (bus arbitration, multiple layers of write buffers, synchronisation signals going over long motherboard traces etc.)

WC works by buffering multiple writes to successive PA into the cache, then writing all the changes to the PA range in a single, big burst of writes using PCI burst mode. This means one set of PCI control and arbitration to write a large number of individual writes, amortizing all the overhead across the large number of writes wrapped up in the combined write. This is a big win for framebuffer writes, where the actual order of the writes is not significant.

Obviously, outside of the context of framebuffers, and in the context of PCI device registers, writes to registers must be done in a predictable order. As writes to registers are often triggers to initiate some action, so other registers that act as arguments to the action must have been written previously.

Re: Keyboard and Framebuffer issues

Posted: Mon May 27, 2024 12:07 pm
by MichaelPetch
I noticed that it stops taking keyboard input after 128 characters which also seems to be the input buffer length in console.c:

Code: Select all

#define INPUT_BUF 128
. It also seems to be that after 128 characters it stops processing characters but keyboard interrupts are still being received. I noticed the interrupts being received when I ran with the extra option `-d int` in QFLAGS(config.mk).

Re: Keyboard and Framebuffer issues

Posted: Mon May 27, 2024 12:18 pm
by Senc39
thewrongchristian wrote:
Senc39 wrote:https://github.com/Senc3951/MyOS
Hi, I have 2 questions about my OS I hope someone can answer.

1. When testing the keyboard driver, I noticed that if I spam keys, at first everything works and the letters are printed to the screen, however, after a couple of seconds it stops generating interrupts. upon further investigating I saw that if I print the letters only to the serial output and not to the screen (Framebuffer), it will still stop generating interrupts but after a longer time, so I guess it happens because the fb is slow and the kernel doesn't have time to send an EOI while another key is pressed. (located at kernel/src/dev/ps2/kbd, taken from xv6)
Your interrupt handler will be running with further interrupts disabled, so doing potentially long running (relatively) operations like interpreting the scan code into a character, and echoing it to the screen, will mean your OS will be ignoring further, potentially high priority interrupts until you're done handling all this work.

Your interrupt handler wants to do the minimal amount of work. You want to read the scan code, queue it for further processing (some sort of software FIFO), then get out of interrupt context ASAP.

Once it's in the FIFO, you can schedule a thread to further process the scan code, do any interpretation, echo characters back out etc.

Note also that the PS2 controller also handles mouse input, and if not correctly configured and handled, pending mouse input will block further keyboard input from interrupting.
Senc39 wrote: 2. When initializing the framebuffer and clearing the screen, I saw that it takes around 1.5sec to clear the screen. To speed things up, I mapped it as WC but I can't see a difference. I saw that someone said that it happens because of qemu and on actual hardware it would be faster, but when experimenting with Limine bootloader, the fb reacted way faster although they used the same caching for all the memory (WC for fb, WB for rest). (cache init at kernel/src/mem/vmm, fb at kernel/src/dev/fb/screen)
WC makes no difference to QEMU.

WC works by writing lots of memory together in a single burst over PCI. Normally, when you write, say, a 32-bit word to a PA mapped to a PCI memory address, that 32-bit write will require a whole PCI transaction (bus arbitration, multiple layers of write buffers, synchronisation signals going over long motherboard traces etc.)

WC works by buffering multiple writes to successive PA into the cache, then writing all the changes to the PA range in a single, big burst of writes using PCI burst mode. This means one set of PCI control and arbitration to write a large number of individual writes, amortizing all the overhead across the large number of writes wrapped up in the combined write. This is a big win for framebuffer writes, where the actual order of the writes is not significant.

Obviously, outside of the context of framebuffers, and in the context of PCI device registers, writes to registers must be done in a predictable order. As writes to registers are often triggers to initiate some action, so other registers that act as arguments to the action must have been written previously.
Thanks, I got it now and will try to implement it

Re: Keyboard and Framebuffer issues

Posted: Mon May 27, 2024 12:20 pm
by Senc39
MichaelPetch wrote:I noticed that it stops taking keyboard input after 128 characters which also seems to be the input buffer length in console.c:

Code: Select all

#define INPUT_BUF 128
. It also seems to be that after 128 characters it stops processing characters but keyboard interrupts are still being received. I noticed the interrupts being received when I ran with the extra option `-d int` in QFLAGS(config.mk).
Maybe you are correct but it should still print to the serial output as they are two different things and it doesn't print there also

Re: Keyboard and Framebuffer issues

Posted: Mon May 27, 2024 12:47 pm
by MichaelPetch
In console.c I see:

Code: Select all

         default:
            if (c != 0 && g_input.e - g_input.r < INPUT_BUF)
            {
                c = (c == '\r') ? '\n' : c;
                g_input.buf[g_input.e++ % INPUT_BUF] = c;
                putc_all(c);
`putc_all` puts characters to the screen and the serial port but will only do so if you haven't filled the input buffer which is checked at the beginning of this code snippet. Do you ever take anything out of the keyboard buffer once you put a character in? This sort of goes to the other commenter who mentions where you should do processing. Your interrupt handler should act as a producer by stuffing characters in the buffer and there should be a consumer task that checks if there are characters in the buffer and then retrieves them and processes them (like putting them on the display etc). As you retrieve the characters from the buffer the buffer becomes less full allowing more characters to be added by the interrupt handler. Based on the code your keyboard buffer is essentially a circular queue.

Re: Keyboard and Framebuffer issues

Posted: Mon May 27, 2024 11:13 pm
by Senc39
MichaelPetch wrote:In console.c I see:

Code: Select all

         default:
            if (c != 0 && g_input.e - g_input.r < INPUT_BUF)
            {
                c = (c == '\r') ? '\n' : c;
                g_input.buf[g_input.e++ % INPUT_BUF] = c;
                putc_all(c);
`putc_all` puts characters to the screen and the serial port but will only do so if you haven't filled the input buffer which is checked at the beginning of this code snippet. Do you ever take anything out of the keyboard buffer once you put a character in? This sort of goes to the other commenter who mentions where you should do processing. Your interrupt handler should act as a producer by stuffing characters in the buffer and there should be a consumer task that checks if there are characters in the buffer and then retrieves them and processes them (like putting them on the display etc). As you retrieve the characters from the buffer the buffer becomes less full allowing more characters to be added by the interrupt handler. Based on the code your keyboard buffer is essentially a circular queue.
Oh I see that you are write as the xv6 takes input out of the buffer which I hadn't implemented yet