Operating System-Class 11-March 2nd

 

Handout: PollingIOHandout

 

Ref: Notes on using C to access hardware registers

 

The (32-bit) x86 architecture specifies 32-bit memory addresses and, separately, 16-bit i/o port numbers.  A device is assigned a little block of i/o port numbers for communication over the bus.  For example

 

Serial port I/O --- with hardware

COM1 port: 0x3f8 + …… (8 ports in all)

COM2 port: 0x2f8 + …… (8 ports in all)

-- 0x3f8/0x2f8 is data port, +5 LSR is status register

 

Assuming the port hardware is initialized (say by Tutor), we can do polling I/O(no interrupts) using port 0x2f8(data)/0x2fd(LSR).

 

“polling loop”

                                while((DR bit of LSR register) == 0)

input 1 byte                        ;                                                                                             “polling the DR bit”

                                inb from data port: byte is in %al

 

                                while((THRE bit of LSR register) == 0)

output 1 byte                    ;

                                /* here when transmitter is ready for another byte */

                                outb to data port

 

In demo last class: see demo script

-- saw COM1 LSR with DR=0: Tutor>pd 3fd  showed 60, so bit 0 = DR = 0

-- used a second input mtip process to send a char  ‘A’ on COM1

-- saw DR=1 after that: Tutor>pd 3fd showed 61, so bit 0 = DR = 1

-- saw the char itself in data: Tutor>pd 3f8 showed 41 = ‘A’

and after this, DR=0 again, because the hardware senses the read of the ‘A’.

 

Luckily we can avoid programming in assembler by writing a C-callable assembler function once and for all to do each of these instructions.  These are in the SAPC support library, prototyped in $pcinc/cpu.h:

 

     ch = inpt(port)

     outpt(port, ch)

 

Here ch is an 8-bit quantity, usually an unsigned char.  For example, to output ‘A’ to COM2,

       outpt(0x2f8, ‘A’);

 

Or we can use Tutor, where all numbers are in hex:

              Tutor>ps 2f8 41               <---port-set command, uses outb %al, %dx

 

But we don’t want to use wired-in numbers like this in real programs.  We could write:

       outpt(COM2_BASE, ‘A’);

This is better but still not perfect—base addresses are usually accompanied with offsets to say which port of the set is being used:

from serial.h (see handout):  offsets for a PC serial port device

 

#define UART_RX         0   /* receiver reg */

#define UART_TX         0   /* transmit reg */

#define UART_IER        1   /* interrupt enable reg */      

#define UART_LSR        5   /* line status reg */

 

So we end up with

 

                 outpt(COM2_BASE + UART_TX, ‘A’);   /* output ‘A’ to COM2 */

                                          /* using  i/o port 0x2f8 + 0 = 0x2f8 */

 

Similarly, if we know there’s a character ready to be read in from COM2:

         ch = inpt (COM2_BASE + UART_RX);  /* input char from COM2 */

                                          /* using  i/o port 0x2f8 + 0 = 0x2f8 */

 

output: normally do polling on THRE bit

-- like polling loop on DR bit

-- in echo.c, we don’t bother, because we expect the transmitter to be ready, after the delay of receiving

-- just output the char: outpt(conport + UART_TX, c)

 

 

0x2f8: one port, 2 actions, 2 device registers involved

-- receive a char with inb: using device’s receiver data register

-- send a char with outb: using device’s transmit (holding) register

 

receive: bus cycle reading device regs “read cycle”

send: bus cycle writing device regs “write cycle”

To the device, these are quite different, so it has no trouble handling them with different registers within it.

Compare to memory read/write:

1. memory

                read: address A

                write: address A

2. device register

                read: one device register

                write: another device register

This is common! I/O ports are small in number, serial port gets 8 ports.

Note: can’t write the receiver register or read from the transmit register. This is a design simplification for the device.

 

Devices we need for CS644:

serial, timer, pic (programmable interrupt controller)

-- serial: I/O device

-- timer: see Love book p157-162, need timer interrupts for time sharing!

-- pic: for interrupt

 

cpu.h <-- intpt & outpt prototypes

gate.h <-- gate for interrupt handling

stdio.h <-- CONSOLE definition prototypes for C lib

-- helpers: sysapi.h(), ctype.h

 

The Big Picture: the bus provides communication for all the participants:

Here we put the CPU at the end of the bus because it controls the bus. Another way to show it is with the CPU hanging off the bus like another device, since it is also using the bus protocol to communicate. If we have multiple CPUs, they all hang off the one bus in an ordinary PC.

 

 

 

 

 

 

 

 

 

 

 

 

 


inb and outb: CPU instructions that cause communication over the bus to devices.