1 / 35

CS 450

CS 450. Module R5. Next Week. Reminder: R3 & R4 is due next Friday. No documentation due. You do not need to turn in a copy of your source code. Remember to update your manuals accordingly. We’ll be using the same meeting time as modules R1 and R2, unless there is a new conflict. R5.

totie
Download Presentation

CS 450

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CS 450 Module R5

  2. Next Week • Reminder: R3 & R4 is due next Friday. • No documentation due. You do not need to turn in a copy of your source code. Remember to update your manuals accordingly. • We’ll be using the same meeting time as modules R1 and R2, unless there is a new conflict.

  3. R5 • Due on Friday April 1, 2011 (3 weeks from today) • No docs due until R6. Just update your current manuals as you go. • R5 is “stand-alone.” It does not depend on the rest of your code, at all! In fact, you will test R5 by compiling it by itself, without the rest of your code • IMPORTANT: My website has all the files and documentation for this module. We’ll be using Mrs. Hayhurst’s version of R5.

  4. R5 Overview • You’ll be writing an interrupt-driven serial port driver that communicates with another terminal • Interrupts allow communication between terminals to happen at any time • Instead of running MPX on two computers, you’ll be emulate the other terminal with PuTTY • See the R5 manual on my website for details

  5. Special Concerns with I/O Interrupts • I/O interrupts can happen at any time (since they are caused by external events). • I/O interrupts must therefore be disabled when they might cause big problems. One of these problems is that the interrupt will affect the stack. • The 8086 architecture has instructions to enable/disable maskable interrupts. You can access these using the Turbo C functions enable() and disable(). • Interrupts should be disabled for the absolute shortest time possible.

  6. Ring Buffers • You’ll be writing a ring buffer to hold characters • A ring buffer is essentially a FIFO circular list that maintains two pointers: • One for where to place new characters • One for where to start reading characters already in the buffer • This buffer is used when data is received but no process has requested data. • Read chapter 10 of your textbook (to be posted) for a detailed description of how the ring buffer works, and what control variables are needed and what they are used for.

  7. Event Flags • Event flags are used to indicate the completion of an operation. • In this module, I/O operation will have an associated event flag. Your functions will “set” this event flat by giving it a value of 1. This will indicate that the event (I/O operation) has been serviced and completed. • Once the completion of an I/O operation has been appropriately handled, your functions will “clear” the event flag by giving it a value of 0.

  8. R5: Overview • Data Structures • Device Control Block (DCB): stores information about the device • 3 Interrupt handler functions • A first-level interrupt handler to determine the cause of the interrupt, which will then call either the input or output interrupt handlers • A second-level handler to handle input (called by first-level handler) • A second-level handler to handle output (also called by first-level handler) • Other Functions (use the EXACT same prototypes defined later) • com_open: initializes the serial port* • com_close: closes the serial port* • com_read: loads input into the buffer* • com_write: initiates the transfer of data to the serial port*

  9. Data Structure:Device Control Block(DCB) • DCB’s store information about the device. Your text book has a more in depth discussion of DCB’s. Information you must put in your DCB is: • Flag indicating whether the device is open or closed • Pointer to the event flag associated with the device. Should be of type (int *). • Status code- possible values are IDLE, READING, WRITING. • Addresses and counters associated with the requestor’s input buffer. For example: • char* in_buff; - pointer the requestor’s buffer (where read data is will be placed) • int *in_count; - counter of the maximum number of chars that can be placed in the buffer • int in_done; -counter for how many characters have been placed in the requestor’s buffer • Addresses and counters associated with the requestor’s output buffer • Same as above, but separate variables needed for output • An array to be used as the ring buffer, with the associated input index, output index, and counter. • char[size] ring_buffer; • int ring_buffer_in; -index where you will write the next character to the ring buffer • int ring_buffer_out; -index where you will remove the next character from the buffer • int ring_buffer_count; -count of how many character stored but not read from the buffer

  10. Com_open int com_open(int *eflag_p, int baud_rate); • Parameters • eflag_p: pointer to an integer event flag within the calling program • baud_rate: integer value representing the desired baud rate • Returns (you may want to define symbolic constants for these return values) • 0 if no error • -101 if invalid even flag pointer • -102 if invalid baud rate divisor • -103 if port already open • Generally, this routine will: • Initialize the DCB • Set the interrupt vector to the new handler address • Computer and store the baud rate divisor • Set other characteristics • Enable all of the necessary interrupts

  11. Com_open outline I • Check parameters • Check that the eflag_p is not null, that baud rate is not negative, and that the device is open • Return appropriate error code if one of these checks fails • Set the device to OPEN • Save the event flag, so your device driver can edit it • Set event_flag = eflag_p • Set the device’s status to IDLE • Initialize the DCB ring buffer variables • Set ringbuffer-head, -tail, -count = 0 • Save the old interrupt handler • Use getvect… see the R5 manual for more information. • Install the new handler • setvect(COM_INT_ID, &com_int); --where com_int is the name of your interrupt handler • Compute the baud rate divisor • See the R5 manual for the equation

  12. Com_open outline II • Store 0x80 in the line control register, this will allow you to change the BRD registers • Set the baud rate divisor in the registers • outportb(COM1_BRD_LSB, brd&0xFF); • outportb(“, (brd >> 8) & 0xFF); • Store 0x03 in the line control register • Enable the appropriate level (for COM1) in the PIC Mask register • Store 0x08 in the modem control register • This will enable serial interrupts • Store 0x01 in the interrupt enable register • This will enable input ready interrupts

  13. Com_close int com_close(void); • Returns • 0 if no error • -201 if serial port not open • Outline • Check that the port is already open (return error code if not) • Close the DCB • Disable the appropriate level in the PIC mask register • Check the manual for code • Loading 0x00 into the Modem Status and Interrupt Enable registers • This will disable serial interrupts • Restore the original interrupt vector • Use setvect- check the manual

  14. Com_read int com_read(char * buf_p, int *count_p); • Parameters • buf_p: far pointer to the starting address of the buffer to receive the input characters • count_p: address of an integer count value indicating the number of characters to be read • Returns • 0 if no error • -301 if port not open • -302 if invalid buffer address • -303 if invalid count address or count value • -304 if device is busy

  15. Com_read- summary • Com_readwill initialize the reading of data received on the serial port.It first checks the ring buffer. • If it contains data, com_read will copy that data to the requestor’s buffer until the requestor’s buffer is full or the ring buffer is exhausted. • If the ring buffer does not contain data (or not enough data to fill the requestor’s buffer), com_read will “reserve” the device by leaving the device’s status at READING.

  16. COM_READ: outline I • Check parameters • Check that the device is OPEN and IDLE, and that buffer and count pointers (parameters) are not null • If any of these checks fail, return an error message • Initialize the input buffer • Save the address of the requestor’s buffer (parameter) to the input buffer in the DCB. This will allow you to copy data into the requestor’s buffer directly from the DCB. You also need to save the address of the count (parameter) to the DCB’s input count variable • Set the input done count to 0 • Clear the event flag- indicates that an operation is beginning • Disable interrupts, set device’s status to READING; now the system will know that a READ operation is under way

  17. COM_READ: outline II • If the ring buffer contains characters that have not yet been read (you can check this by checking the ring buffer count field in the DCB), copy characters from the ring buffer to the requestor’s buffer until either the ring buffer is exhausted, the requestor’s buffer is full, or a ‘\r’ character is encountered (this mean that a return was pressed which indicates string termination) • Re-enable interrupts • If the requestor’s input buffer has not been filled (check this by checking if the input done is less than the input buffer size), return 0. This will keep the device’s status at READING. Since the status is READING, when a character is received later on, your interrupt handler will know to put the character in the requestor’s buffer, not the ring buffer. • If you have not returned from the com_read function by this point, that means that the input buffer is filled and READ operation is over with. You now need to perform some wrap up functions to the read: • You need to null terminate the input buffer (add a \0 to the end of the buffer). • Set status to IDLE and set the event flag to indicate event completion • Return the actual count of characters you placed in the input buffer (this is stored in the input done variable in your DCB) to the input count variable in the DCB… this lets the calling process know how many character you placed in the buffer.

  18. Overview of reading a string using the com_port driver • A process calls com_read and passes it a buffer and the number of characters to read • com_read checks if there is unread data in the ring buffer (data is stored here when no process is requesting). If there is data, it is transferred to the proc’s buffer • If the requestor’s buffer is filled, com_read indicates that the I/O operation is completed and that the device is free. If the requstor’s buffer is not filled, com_readwill leave the device in a state indicated that more data is needed. • At some point, another byte will be written to the serial port, which will trigger your first level interrupt handler. The first level interrupt handler will then call the second level handler for com_read. • The second level handler will check if there is a process still in need of data by checking the device’s status. If the status indicates this, this handler will transfer the byte to the requestor’s buffer. If not, this handler will transfer the byte to the ring buffer so it can be retrieved later. • Once the requestor’s buffer is filled, the second level interrupt handler will indicate the I/O operation is complete.

  19. Com_write int com_write(char * buf_p, int *count_p); • Parameters • buf_p: pointer to the starting address of the buffer containing the block of characters to be written • count_p: address of the integer count value indicating the number of characters to be written • Return • 0 if no error • -401 if serial port not open • -402 is invalid buffer address • -403 if invalid count address or count value • -404 if device busy

  20. Com_write: Summary • Com_writeis called to copy data from a buffer (the requestor’s output buffer- sent as a parameter) to the com_port. • The com_write function installs the requestor’s buffer into the DCB, but itself only writes a single character to the com_port. • Once that character is written to the device, an interrupt is generated which will trigger your interrupt handler, which will call the second level interrupt handler which will cause another character to be written, which will generate another interrupt, and another character will be written, etc.. until all characters in the buffer have been written.

  21. COM_WRITE: OUTLINE • Check the parameters • Check that the device is open and idle and that the buffer and count parameters are not null. • Return appropriate error code if one of these checks fail • Install the output buffer in the DCB • Save the address of the requestor’s buffer in the DCB’s output buffer pointer, as well as the count. Set the output done count to 0 • Set status to WRITING • Clear the event flag- this indicates that an operation is in progress • Write the first character to the com port • outportb(COM1_BASE, *DCB.outputbuff) • Advance the outputbuffer (outputbuff++) so that when com_write is called again the next character will be written, and increment the outputdone counter to keep track of how many characters you have written. • Enable write interrupts- so when the character you write in the step above actually gets written, the interrupt generated by that event will be seen and captured by the CPU and your interrupt handler will be called. mask = inportb(COM1_INT_EN); mask = mask | 0x02; outportb(COM1_INT_EN,mask); • Return 0

  22. Overview of writing a character string using the driver • A process calls com_write with a string to write • If the device is free, com_write will flag the device as busy, then write the first character in the buffer to the serial port • Once written, an interrupt is generated. This triggers the first-level interrupt handler. The handler determines the interrupt’s cause was a byte being written, then triggers the second-level interrupt handler for writing. • If there are any more bytes left to be written, the second level handler will write the next one. If there are no bytes left to be written, the second level handler will indicate that the I/O operation is completed.

  23. First Level Interrupt Handler- Summary • This interrupt handler will be called when an I/O interrupt on the com port is generated. It will determine the cause of the interrupt (either a read or a write), and will call the appropriate second level interrupt handler

  24. Interrupt Handler: Outline void interrupt name_it(); • Name it whatever you like. • Outline: • If port is not open, clear the interrupt by sending the end of interrupt code to the PIC command register and return. You clear the interrupt to indicate that it has been handled • Otherwise, use inport b to read an int from the Interrupt ID Register to determine the cause of the interrupt. • The value in the interrupt ID register indicates what type of interrupt was generated, either a read or a write. A description of what bits in this value contains the information we need is in the manual. However, one way to find the value is to perform a bit wise and operation (&) between the integer value from the interrupt ID register and 0x07: • If 2, call the write second level interrupt handler • If 4, call the read second level interrupt handler • Clear the interrupt by sending EOI to the PIC command Register

  25. Second Level Interrupt- Read/Input: Summary This interrupt is called when a read interrupt is generated (this means a single character has been placed in the input register). If the current status of the device is READING, this means that a process is currently requesting data from the com port- therefore you will place the character in the requestor’s input buffer. If the status is not READING, no READ operation is currently underway, so you should store the character in the ring buffer.

  26. Second Level Interrupt- Read/Input: Outline void name_it(); • Name is not important… but do not use the “interrupt” keyword! • Outline • Read a character from the input register • If current status is NOT reading, no process is currently requesting data, so we add to the ring buffer • If the ring buffer is full, discard the character and return; • If the ring buffer is not full, store the character in the ring buffer and increment the ring buffer indexes • Put the character in the ring buffer at the index indicated by the “ring buffer in” attribute in the DCB • If current status is reading- a process is currently requesting data so we: • Store the character in the input buffer and increment the count • Check to see if we have finished reading- this happens if the character you just read was a ‘\r’ OR if the input buffer has been filled. If this has occurred: • Null terminate the Input buffer • Set device status to idle • Set the event flag- this indicates that an operation has been completed • Set input count = input done, so the calling function knows how many characters you placed into the buffer

  27. Second Level Interrupt- Write/Output: Summary This interrupt is generated after a character has been successfully written to the com port. It will check if the output buffer in the DCB has been completely copied. If it has, it will perform some cleanup operations and indicate that the operation is complete. If there is still data in the buffer that needs to be copied, it will copy the next character to the output register, which will generate another interrupt, which will cause this procedure to be repeated (and thus the next character written) until all data has been written.

  28. Second Level Interrupt- Write/Output void name_it(); • Name is not important… again do not use the “interrupt” keyword! • Outline • If the current status is NOT writing, Ignore the interrupt and return. • If the requestor’s output buffer has not been completely copied • Copy the next character to the output register and increment the appropriate counts • Return • Otherwise, all characters have been transferred- so we clear the interrupt • Reset the status to idle. • Set the event flag to indicate completion of an operation • Disable write interrupts by clearing bit 1 in the interrupt enable register. mask = inportb(COM1_INT_EN); mask = mask & ~0x02; outportb(COM1_INT_EN ,mask);

  29. Dos.h Functions You’ll Need • unsigned char inportp(int port_id); • Reads a byte from the specified I/0 port. Port_id identifies the port. • void outportb (int port_id, unsigned char value) • Writes a byte of data (value) to the specified I/O port. • void enable(void); • Enables all maskable interrupts. • void disable(void) • Disables all maskable interrupts. • void interrupt (*getvect(int int_ID))(void); • Gets the value presently stored in an interrupt vector. This should be the address of the current handler for that interrupt. • void setvect(int int_ID, void interrupt(*handler)(void)); • Sets a specific interrupt cevtore to point to a designated handler.

  30. Device Registers You’ll Need • Programmable Interrupt Controller (PIC) • I/O interrupts are sent here, instead of to the CPU • Has up to 8 input wires, instead of just the one on the CPU • PIC Mask Register • Control/status register that contains a bit for each of the PIC inputs • 0 represents an enabled interrupt • 1 represents a disabled (or masked) interrupt • Access the PIC Mask Register using port number 21h (0x21) • We will need to modify a particular bit without changing the others. • Look in the Project Manual for sample code to show you how to do this. • PIC Command Register • Located at port 0x20. • You will need to send the end-of-interrupt (EOI) code to this register after each PIC Interrupt. The value of this code is: 0x20. • The purpose of this is to tell the PIC to turn off the current interrupt.

  31. Device Registers You’ll Need • Asynchronous Communications Controller (ACC) • Input register (8 bit) – where a byte received on the serial port are placed • Output register (8 bit) – where you place a byte that you want to write to the serial port • Baud rate divisor (16 bit) separated into two parts: MSB (8 bit) & LSB (8 bit) – used to compute the speed of the transfer • Interrupt Enable (8 bit) - • Line Control (8 bit) – parity and number of data bits • Modem Control (8 bit) • Interrupt ID (8 bit) – what interrupt has occurred • Line Status (8 bit) – ready status of the port for input and output • Modem Status (8 bit) – status values

  32. Addresses You Need- All for COM1

  33. Testing and Demonstration • Two test programs are provided on my website: • TESTCOMW.C- tests the serial port output • TESTCOMR.C- tests the output ability but also prompts for and reads input lines. • To execute R5, create a Turbo C project containing your R5.C file, MPX_SUPT.C, and one of the test programs, and compile/run. Make sure to have a terminal connected to COM 2 open- see the setup guide on the website for more info on this. • If you are not using Turbo C, you must include MPX_SUPT.H and link your R5 executable with MPX_SUPT.C and one of the test programs. However, your R5 may work by just including the support header file and not linking- you will have to try this out. However, for this module, you must use the support programs TESTCOMW and TESTCOMR. These are the only acceptable way to test your driver.

  34. Hints • Read the Project Manual and Chapters 9 and 10 in your text. They will give you a much better understanding of these topics as well as some sample source code. • Remember, R5 does not depend, at all, on what you have done in previous modules. R5 is a completely independent portion of the project. If you are compiling/linking any code other than your R5 code and the support software, you are doing it wrong. • Com_open, com_close, com_read, and com_write must have the exact prototypes specified in this power point file and in the project manual in order for the support software to work correctly. However, the first and second level interrupt handlers can be named anything convenient. • Implement com_open and com_close first, then com_write, the first level interrupt handler, and the write interrupt handler. Get your writes to work before you begin to work on reading, which is more complicated. • Do not use printf statements in the interrupt handlers. • Check that your interrupt vectors are being set and cleared properly- this is the most common cause of crashes or hang ups.

  35. Due Date • Remember, Module R5 is due Friday April 1, 2011. • That is 3 weeks from today.

More Related