Hello, if you have any need, please feel free to consult us, this is my wechat: wx91due
EEE8087 4W Rev. 1.6
This week we start work on the central components of an elementary real-time operating system (RTOS) that divides the processor's time between separate user tasks. These tasks will need to communicate with the system, and will request its services by means of a 'software interrupt'.
Implementation of Software Interrupts
There are 16 trap instructions available, numbered 0 to 15, and written
trap #0...trap #15
Controlling Interrupts
There is, however, an important difference between hardware and software interrupts. Hardware interrupts are in order of priority, with 7 being the highest priority and 1 the lowest. If two hardware interrupts occur at the same time, then the one at the higher priority will be accepted and the other one will be kept waiting until the first ISR has completed. If a hardware interrupt occurs shortly after another one, but while the ISR for the first interrupt is still in execution, then the processor will again compare the priorities of the two interrupts. If the new interrupt is of a higher priority, then it will interrupt the lower priority ISR. If the new interrupt is at a lower priority than the currently executing ISR, it will be kept waiting until that ISR completes.
Software interrupts do not behave in an analogous way. Since the processor can only execute one instruction at a time, it would be impossible for two software interrupts to occur at the same time, and unless a programmer includes a trap instruction within an ISR, there will also be no occasions on which a trap takes place during the processing of another trap. There is therefore no point in prioritising the software interrupts, and all 16 are at the same priority. There is, however, the question of the relative priority of the hardware and software interrupts. What if a hardware interrupt is raised at the same time as the processor is executing a software interrupt instruction? This is handled by assigning all the software interrupts to priority level 0. Processing of a software interrupt is therefore interruptible by a hardware interrupt at any of the priority levels 1 to 7.
Within your system, however, regardless of the type of interrupt being processed, you will want to prevent the acceptance of any other interrupt. Your system will therefore be completely uninterruptible. Once entered, it will always run to completion and then return to the user task that was running when the interrupt was raised. You will therefore need to disable interrupt acceptance, the procedure for which is explained now.
Using the simulator, examine the 16-bit status register. Bits 8, 9 and 10 (labelled 'INT') hold a 3-bit value that represents the interrupt priority mask. When an interrupt is accepted, the mask is set to the priority level of that interrupt. A hardware interrupt will only be accepted if its priority is greater than the current setting in the mask. Normally, the mask is set to 000 (decimal 0) thereby allowing the acceptance of any hardware interrupt. However, it will remain at zero during its response to a software interrupt, since that is the priority of these interrupts, and will thereby allow the hardware to interrupt the software ISR. If you wish to prevent this, then the following instruction, placed at the very start of a software ISR, sets the mask to binary 111 (decimal 7). Any hardware interrupts will now be disabled, and held pending until the mask is returned to zero.
The status register will have been automatically saved on the stack at the start of the interrupt servicing. On execution of the 'return from exception' instruction (RTE), it will be restored, and the mask reset to the zero value that it held previously, thereby allowing the acceptance of any hardware interrupt that might have been raised in the meantime and is currently pending.
If you want to enable hardware interrupts at any other time, the following instruction will set the mask to zero.
Work in groups of three on this question. Your submission should include the following.
Your software, including the RTOS and the test programmes you used to demonstrate it.
Submit the source code, not the assembler output listing.
Documentation: a .PDF file is preferable, otherwise .DOC.
The names and student numbers of all three group members should be shown on the software heading and on the front page of the documentation.
There are therefore two items to be submitted: a single software file, and the documentation. These items should be placed into a single zipped file, and uploaded to a Canvas submission point to be advised. The submission deadline is 2pm on Friday 17th January, 2025.
Each item will now be described in detail.
The work consists of writing a basic time-slicing system, along the lines of the one discussed in the lecture. It should allow the execution of several concurrent user tasks, with support for task scheduling and inter-task communication. An outline programme is provided on Canvas, but you will write the service routines (including reset), the scheduler, and the user tasks for each application.
Address |
|
2000H |
Programme code |
2C00H |
Data |
3000H |
Top-of-stack |
The following system calls should be supported by means of software interrupts. They can either each be allocated to a separate trap number, or (as in the demonstration system) they can all be called on the same trap, with one of the registers used to hold a value identifying the requested function. Some of the calls also require additional parameters in other registers.
Function: If the mutex variable is zero, and a task is waiting on the mutex, then that task is transferred to the ready list and the mutex remains at zero. If the mutex is zero and no task is waiting, the mutex is set to one. In either case, the requesting task remains on the ready list.
Function: The system is initialised: all internal variables are reset, and each TCB is marked as unused. A TCB for task T0 is then created, and T0 becomes the running task.
The system assumes that a default user task, T0, is present. The system runs this task immediately after a reset. It will need to be located at a predetermined address, which will be coded into the reset function.
A radiation monitor contains two devices that each generate a pulse each time a particle of ionising radiation is detected. The system alternately samples each detector for 100 ms, and records the count, a and b, from each device. It also keeps a running total of the two counts, c = a + b. The system measures the time since it started, and after 8 seconds displays the average count per second between the two detectors, that is, (2c / 2) / 8, or c / 8. (Because it monitors each detector for only half the time, there is an implicit assumption that the total for that detector is twice its actual count.) If at any time during the 8-second measurement interval the total count c exceeds a certain critical value, the system immediately displays a danger warning by lighting the RH LED, and then continues running for the remainder of the 8 seconds. The LH LED is used to indicate an internal error, as will be explained later.
It would be convenient to indicate a detection by pressing a button, but since we need to test this system in real-time, and it is not possible to press the buttons hundreds of times per second, we will simulate a fast arrival rate of detection pulses simply by programming the system to increment the two counters continuously. The programming is as follows.
Timer interrupts are set to 100ms. Three tasks run concurrently. After performing any initialisation, tasks 0 and 1 update the counters, either a and c, or b and c, using the following instruction sequence which is repeated continuously.
add.l #1,d0
move.l d0,c EEE8087 4W Rev. 1.6
At the end of this sequence, variable c is tested to determine whether it has exceeded the criticalvalue, and if so, the RH LED is lit to indicate a danger condition.
You will note that variable c is updated by both tasks, which are liable to interfere with each other. Amutex operation is therefore used to enforce exclusion and prevent simultaneous updates. A call tomutex wait should be placed before the three instructions that increment this variable, and a signal call at their end.
This is a much better arrangement than keeping two versions of the RTOS, with a different test programme in each, or pasting each test programme into the RTOS whenever it is required.
However, since both programmes will be assembled together, you will have to ensure that you use different identifier names within the two programmes. You could, for example, start all labels and variable names within prog1 with 'p1' and those in prog2 with 'p2'.
This consists of a user manual. It will explain your system to a user, and will therefore focus on what it does and how to use it. It will include a brief overview of how the system works internally, but only to an extent that is required for the programmer to use the system correctly. It should be structured as follows.
With normal typeface and spacing (such as used here) it would be reasonable to expect a length of no more than five or six pages. Submission in .PDF format is preferred, but .DOC(X) is also acceptable.
A system was demonstrated during the lecture. It recognises a hardware interrupt at level 1 from the timer. It also allows system calls by means of software interrupts, all of which have been allocated to trap 0. These system calls are programmed by placing a value that identifies the requested function into data register 0, and any other parameters as required by each of the individual functions in registers D1 onwards. For example, system call 1 is used to create a new task. Suppose that this new task is called T1, and that its top-of-stack is to be located at address 6000H. It would be programmed as follows.
The main data structure used in this system is a list of task control blocks (TCBs). Each TCBrepresents the state of one of the tasks. It contains a copy of all that task's registers, together with some items of control information including a flag that indicates whether the TCB is in use.
At any time, each of the tasks will be in one of three states: the currently running task, ready to run when its turn comes up, or unable to run because it is waiting for the occurrence of some event,which could be a signal operation on a mutex, the expiry of a time interval, or an I/O interrupt.
At initialisation, all the TCBs in the list are marked as unused. As each new task is started, one of the unused TCBs is allocated for it and marked as used. These TCBs are organised into two linked lists, in which each element contains a pointer to the next element. These lists are called 'ready' and 'waiting'. Two more data items consist of pointers to the first element in each list. The pointer rdytcb holds the address of the first element in the list of ready TCBs. The first element in this list is the task that actually is running. The linkage in this list is circular, that is, the last entry points back to the first, so making it easy to access each TCB in rotation. The pointer wttcb holds the address of the first element in the list of TCBs that are waiting. Each element will contain an indication of the event the task is waiting for. There is no need to access elements of this list in rotation, so the last element has its pointer set to zero.
The interrupt vectors are addresses of the code that will be executed as a result of an interrupt. The following three addresses are defined.
Address res is the location of the routine to which the processor branches when the it responds to a hardware reset. Address fltint is the location to which the processor branches following a timer interrupt at level 1, and flsint following a software interrupt. The address stk is the value that is loaded into the stack pointer following a hardware reset.
The first-level interrupt handler (FLIH) contains the code that services an interrupt. For convenience it is split into the common FLIH entry section that is executed immediately following an interrupt, and the FLIH service routines that carry out the processing specific to each type of interrupt.
FLIH entry
Hardware interrupts at level 1 are directed by the interrupt vector to enter the FLIH at fltint, while software interrupts arrive at flsint. The FLIH performs three main functions.
It takes the pointer to the TCB of the currently executing task, stored at rdytcb, and saves the values of the registers, including the PC and SR, within that TCB.
It also sets a value within a storage location, known as id, that identifies the source of the interrupt.
If an interrupt has been raised by the hardware timer, then id is set to 0. For a software interrupt,id is set to the value, from 1 onwards, of the system call function number. The id will subsequentlybe used to select the corresponding service routine for processing this interrupt.
Programming the above two operations requires particular care, because saving the value of the user's registers as they were at the time of the interrupt requires the use of certain registers itself. Registers D0 and A0 are in use for this purpose. These registers are therefore saved in temporary locations, before being transferred to their long-term holding locations within the TCB.
The other function performed by the FLIH is to disable interrupts, if this has not already happened. Alevel-1 hardware interrupt from the timer will have set the interrupt priority mask to 1, thereby preventing any further interrupts. A software interrupt will have left the mask at 0, which would allow the timer device to interrupt the processing of the software interrupt. Therefore the first action takenat the software interrupt entry point is to disable hardware interrupts by setting the mask to 7.
The service routines are arranged as a large switch statement, using id as the case variable. Each routine carries out one of the functions defined in the specification.
The scheduler examines the ready list, to which rdytcb points to the first element. This is the TCB of the task that was executing when the system was invoked, and which has just been interrupted.
The scheduler may make the decision as to which task will run next by doing nothing more than following the link in the current TCB to the next one in the chain. This will result in each ready task running in rotation, receiving an approximately equal amount of run time each. Alternatively, it would be possible to assign a priority to each task as it is created, by adding another parameter to the 'create task' system call. Higher priority tasks would then receive a larger proportion of the available run time.
The dispatcher reverses the action taken by the FLIH. Using the newly set value in rdytcb, it restores the registers of the selected task to the values that were stored when that task was interrupted. Careful housekeeping is again necessary, as this operation itself requires the use of registers D0 and A0. The dispatcher finishes by recreating the state of the stack as it was after the task was interrupted. The processor then uses a 'return from exception' instruction, as though it were returning from any normal interrupt, to transfer control back to the selected task.
An example of a user programme running under this system is shown here. It consists of two concurrent tasks. Task T0 calls the system to start task T1, then switches on the RH LED. Task T1 calls the system to wait for 3 timer intervals, then switches on the LH LED. From then on, the two tasks run alternately. If the timer is set to interrupt at one-second intervals, the result is that the RH LED lights immediately, then after 3 seconds the two LEDs start alternating.