200 likes | 217 Views
Transmitter Interrupts. Review of Receiver Interrupts How to Handle Transmitter Interrupts? Code to fix TX error Critical Regions Text: Tanenbaum 2.3.2. Review of Interrupts. Com Port. Bus. Review of x86 Receiver Interrupts.
E N D
Transmitter Interrupts • Review of Receiver Interrupts • How to Handle Transmitter Interrupts? • Code to fix TX error • Critical Regions Text: Tanenbaum 2.3.2
Review of Interrupts Com Port Bus
Review of x86 Receiver Interrupts • UART receives a character and signals an interrupt on a bus line. • Programmable Interrupt Controller(PIC) detects the signal. If no pending interrupts are pending, it services the interrupt immediately. • If another one is in progress or a higher priority one is pending, the device is ignored. It continues to assert the interrupt signal until it is serviced. • To handle the interrupt, the controller puts an interrupt vector number (e.g 0x23 for COM2) on the data bus and asserts a signal (INTR) to interrupt the CPU.
x86 Receiver Interrupts (cont’d) • CPU stops what it is doing and starts doing something else. • CPU uses the interrupt vector number to index the IDT (interrupt descriptor table) and find the interrupt vector (i.e. address of the interrupt service routine) • CPU runs the service routine which includes writing a certain value(EOI) to the interrupt controller’s I/O port to ack the interrupt
Example Program for COM2 receiver interrupts • Look at http://www.cs.umb.edu/~cheungr/cs444/CS444_UART_Receiver_interrupts.htm • Steps 1-5: initialization steps • Step 6: wait forever loop • Steps 10-13: shut down interrupts • Steps 7-9: interrupt handler, need the envelope routine • pic_end_int(), pic_enable_irq(), pic_disable_irq() are functions implemented in the SAPC library in directory $pclibsrc • Look at $pcinc for include files
How About Transmitter Interrupts? • Details on programming UART registers • slide 14 of http://www.cs.umb.edu/~bobw/CS341/DataBook/c8652.pdf • Use Programmed I/OSet up a loop for the number of characters in the string check the transmit-ready(THRE) bit of the LSR Register if on, write the char to UART_TX register otherwise, loop • Use Interrupt-driven I/O
Transmitter Interrupts • Each interrupt signals readiness to output another byte of data • The transmitter ISR gives another byte to output • Issues to consider: • How to distinguish the Tx from Rx interrupts since they share one interrupt line(e.g. IRQ3 for COM2)? • what to do when data runs out? • How to start/restart the Tx interrupt?
How to Distinguish the Interrupts? • One interrupt service routine to perform both Tx and Rx functions • Check the UART’s LSR to see if Data Ready (DR) bit or the Transmit Holding Register(THRE) bit is set. Then execute the corresponding function. • Any one of the 3 situations can happen: • Both Tx and Rx interrupts are ready • Only Tx interrupt is ready • Only Rx interrupt is ready
What to Do when Tx Data Runs Out ? • THRE bit set to 1 when the UART hardware is ready to accept a new character for transmission. • THRE bit set to 0 after UART_Tx register is loaded with a new character. • If there is a Tx interrupt, dequeue a character and output it. • Shut down the Tx interrupt when queue is empty. • Rx interrupts need to be on continuously. Tx interrupts go on and off depending on output availability.
The Transmitter Strategy • Set up interrupt gate in IDT, but leave Tx interrupt off in the UART. • To load a string (n chars) into queue: • 1. Load the first part of the string into the output buffer (i.e. enqueue the chars until the queue is full (Qmax)). • 2. Turn Tx interrupt on in the UART_IER. • 3. While the interrupt is ongoing, loop over the enqueuing of the rest of the chars. • 4. Return when all chars have been enqueued. • To output the chars in the queue: • 1. Check if the queue is empty. • 2. If there are chars, dequeue one and output it to UART_TX. • 3. If there none, shut down Tx interrupt in the UART_IER.
Code Modifications to fix VM error • The virtual machine does not emulate exactly all the hardware functions • For the transmitter, you have to constantly kick start the transmitter interrupt to generate more transmit interrupts, • e.g. in the transmitter ISR code: c = this_buffer[cursor++]; /* get next character from app */ outpt(COM1_BASE+UART_IER,0); /* disable all interrupts */ outpt(COM1_BASE+UART_IER, UART_IER_THRI); /* kickstart TX interrupt */ outpt(COM1_BASE+UART_TX, c); /* output the character */
An Example • write(TTY1, “123456789”, 9) with nchar = 9 and Qmax = 6 • 1. “123456” put in queue during the setup in ttywrite • 2. “123” output ,one by one, by ISR • 3. “789” get loaded in the queue in ttywrite • 4. ttywrite returns after all nchar in queue • 5. ttywrite has returned, but 6 chars “456789” are still in the queue. They are outputted one by one by ISR
Outb: 1 2 3 4 5 6 7 8 9 ISR: ttywrite: setup loop of enq’s testio: ttywrite rest of testio Timeline Representation Note: 1. Still have a busy loop in ttywrite, i.e. loop of enq’s 2. OK for hw1. Fix it later using a scheduler 3. Could have a race condition due to concurrent activities on modifying the queues
The Race Condition • 2 concurrent activities occur when executing ttywrite • ttywrite looping on enqueues • intermittent ISR executing a dequeue • working with the same queue (tty->tbuf) • Interrupt can occur between 2 machine instructions. • in ttywriteif (enqueue( &(tty->tbuf), buf[i])) i++; • during enqueue function, ISR calling dequeue function can mess up the queue data structure
How to Avoid the Race Condition • This part of program is called critical region or critical section when it accesses shared memory. • Use mutual exclusion to make sure one part of program is using the shared variable while the other part is excluded from doing the same thing. • Four conditions to provide mutual exclusion: • No two processes simultaneously in critical region • No assumptions made about speeds or numbers of CPUs • No process running outside its critical region may block another process • No process must wait forever to enter its critical region
Mutual Exclusion with Busy Waiting • Disabling Interrupts • Locked Variables • Strict Alternation • Peterson’s Solution • Test and Set Lock (TSL)
Turning off Interrupts in ttywrite • We can turn off interrupts during the execution of enqueue and turn them on when enqueue is done • How to do it:cli(); /* disable interrupts in CPU by setting IF=0 in EFLAGS*/enqueue(…); /* critical region code */sti(); /* or set_eflags(save_eflags) to re-enable interrupts */
Turning off Interrupts in printf • Important to turn off interrupts before using printf • Prevent part of the code to do programmed I/O and another one doing interrupt-driven I/O using the Tx of the UART • kprintf(…) is the printf routine that turns off interrupts:int saved_eflags; save_eflags = get_eflags(); cli(); printf(…); /* SAPC-library printf uses programmed I/O */set_eflags(saved_eflags);
Echoes • Characters to be echoed are generated in the input interrupt handler • One copy is enqueued in the input queue and another in an output echo queue • When the Tx ISR runs, it should look first in the echo queue. The user sees the echo as soon as possible