CS452 - Real-Time Programming

Frequently Posted News (FPN)

Debugging

Here is something that might help you with your debugging. When you
start a program executing it starts by putting on the monitor some
output using bwio. (My programs always print "hello, world." as their
first action just so that I am confident that they are actually
running.)

After that three things can happen.

  1. Execution falls of the end or reaches a return statement at the top
     level. In this case it should go gracefully back to the RedBoot
     prompt.

  2. Execution goes into an infinite loop. In this case, unless there is
     output in the loop, which there almost never is, everything goes
     dead.  There's nothing to do but to push the reset button and try
     to figure out how far execution went before the problem occurred.

  3. In the most interesting case a line of random output appears at the
     bottom of the monitor screen, and if you type at the monitor
     keyboard more lines of gibberish appear.

     In the middle of the gibberish in the first line that appears is an
     are eight hex digits side by side, usually starting with two
     zeros. This is the address of the instruction that was being
     executed when your program crashed.

     To find the instruction look at you load map. It contains all the
     functions in the load image, with a hex number that is the start
     of each. Find the largest starting point less than the crashing
     instruction and subtract it from the address of the starting
     instruction. Divide by four. If the result is n, then The
     instruction is the nth instruction in that function.

     Look at the assembly language of that function and count down n
     instructions. This is the instruction that was executing when
     something terrible happened.

GCC

gcc does a variety of weird things, and this is one of them. Unless you
use appropriate flags to suppress this behaviour it will compile in a
function called memcpy to transfer blocks of bytes (Up to 65 the last
time I checked) from one location to another. Then it will complain if
you do not make memcpy available at link time.

You have two choices: you can use the flag that suppresses memcpy, or
you can provide a memcpy. The signature of memcpy is

void memcpy( char *destaddr, char *srcaddr, int len )

The simplest implementation is something like

while ( len-- ) *destaddr++ = *srcaddr++;

So why does memcpy exist at all? Most processors have a method of
speeding up the transfer of block of memory from one location to
another. The goal is to minimize the number of instruction fetches,
which usually requires something very processor specific. In the ARM
architecture ldm and stm (load and store multiple) moves 4n contiguous
bytes into n registers using the bus in block transfer mode, which moves
two words per bus cycle. Thus we get 4n bytes transferred for two
instruction fetches plus n/2 bus cycles. (The only gotcha is that the
memory transferred must be word-aligned, but you can use Duff's device
to clean up odd bytes at the beginning or end if you want to move
non-word-aligned data.)

How you handle this doesn't matter very much; there's plenty of CPU, but
it may be a matter of pride to do it in a cool way.


On Sep 18, 3:24 pm, Matt Negulescu <mnegu...@student.cs.uwaterloo.ca>
wrote:
> On Fri, 18 Sep 2009, matthew woolman wrote:
> > Cherry wrote:
> >> On Sep 18, 11:35 am, Ryan Morris <ryanmorris...@gmail.com> wrote:
> >>> On Sep 18, 11:32 am, Ryan Morris <ryanmorris...@gmail.com> wrote:
>
> >>>> Hello,
> >>>> My situation is that 'gcc' recognizes the -mcpu=arm920t command and is
> >>>> compiling my code but 'as' does not and fails.  Any thoughts?
> >>>> Thanks,
> >>>> Ryan
> >>> I should also mention that all I'm doing to attempting to compile
> >>> professor Cowan's sample iotest.c code after having logged into
> >>> linux.student.cs.uwaterloo.ca and having added the appropriate items
> >>> to my PATH variable.
>
> >> I tried to compile Prof. Cowan's iotest.c just now and it worked fine.
> >> Here's what I did:
>
> >> - "echo $PATH" and I have "/u5/l4ho/bin:/u/wbcowan/gnuarm-4.0.2/
> >> libexec/gcc/arm-elf/4.0.2:/u/wbcowan/gnuarm-4.0.2/arm-elf/bin:/
> >> software/.admin/bins/bin:/bin:/usr/bin:/usr/bin/X11". You could check
> >> your PATH with mine.
>
> >> - "cp -r /u/wbcowan/cs452/io ./"
>
> >> - Then I went to "test" directory and run "make". I didn't see any
> >> error messages and I have .elf file generated.
>
> >> So what error message did you get?
>
> >> Thanks,
>
> >> Cherry
> > Try adding arm paths to the begining of your path rather than the end.
>
> Indeed, this is a path problem. It must be at the end. Let me know if you still
> have trouble with this and I can help you out.

Yep, the arm business needs to be added at the front of the path.
Thanks for your help!



Kernel 1

Aron Visontai <avisonta@student.cs.uwaterloo.ca> wrote:
> Cherry wrote:
>> On Sep 30, 2:08 pm, Aron Visontai <aviso...@student.cs.uwaterloo.ca>
>> wrote:
>>> Based on the algorithm given in class it seems like starvation is
>>> inevitable for tasks of lower priority. If there is at least one task of
>>> higher priority it will have to Exit() before any of the lower priority
>>> tasks can execute. I know that we will eventually have tasks that are
>>> blocked by I/O and maybe even timesliced which would solve the starvation
>>> problem but we obviously don't have (or need) these abilities for part1.
>>>
>>> I just want to make sure that it is okay to have starvation for kernel
>>> part1.
>>>
>>> Aron
>> 
>> Starvation does happen when the scheduling system is not pre-emptive,
>> theoretically. But in our kernel and applications, tasks give up cpu
>> when they make system calls, or being blocked or call Pass(). So as
>> long as your high priority tasks aren't infinite loops doing nothing,
>> they will eventually give up cpu and let lower priority tasks run.
>> 
>> 
>> Thanks,
>> 
>> Cherry

> Right but wouldn't the high priority task just get rescheduled again 
> because it has the highest priority? Or is there something special that 
> happens to the priority queue after getting the next scheduled task?

The answer above is correct but perhaps it's worth giving a little
perspective. At the end of the first part of the kernel your kernel
can't do very much, but you should still want to test it, and we also
want to test it while marking.

To do so I added the Pass( ) and Exit( ) primitives, which allow you to
see if your scheduling and task creation are working. With the right
application programs, described in the assignment, the kernel does not
go into an infinite loop; with other application programs it certainly
could go into an infinite loop, but that is not an issue in practice
because the two added primitives are only used for testing the kernel.

What makes an application program right is that it is not based on an
infinite loop, but terminates with Exit( ) after a finite amount of
time. I won't say more: you are supposed to work out how the application
given shares the processor among tasks in order to know if your kernel
is scheduling correctly.

Bill

UART

I read the documentation as saying that with the fifo turned off the
UART acts as though the fifo is not there, which is interpreted as there
being a fifo that has one entry.

In a UART without a fifo, there are two internal registers in the
transmitter, a hold register and a transmit register. The contents of
the transmit register are unstable because it is the register in which
shifting is occurring. The contents of the hold register are stable.
When the transmission of the byte in the transmit register is complete
and the hold register contains a byte the byte is transferred to the
transmit register and transmission starts. The hold register is empty.

It varies, but usually data is transferred from the bus to the hold
register using the bus clock, and is transferred to the transmit
register using the UART clock. The transmit interrupt is asserted when
the transfer from the hold register to the transmit register occurs, so
you wouldn't be surprised to find the BUSY bit set because it indicates
that there is currently something being transmitted.

As I read the documentation, a disabled fifo has a single location
available, analogous to the hold register. The interrupt should be
asserted when the fifo is less than half full which is the same as the
hold register containing zero bytes.

The BUSY bit is asserted when there are zero bytes inthe fifo (= no
byte in teh hold register) and no byte in the process of being
transmitted from the transmit register.

The TXFE bit is asserted when the hold register is empty; the TXFF bit
is asserted when the hold register is full. Thus, with the fifos
disabled, if you want to transmit three bytes you should see the
following sequence. (Assume that the initial state is not transmitting
anything.

1. BUSY, TXFF negated, TXFE asserted, TIS asserted, interrupt asserted.

2. Write 1st byte to data (=hold) register.
2a. TXFF asserted, BUSY, TXFE, TIS, interrupt negated.

3. UART clock ticks, 1st byte transferred to transmit register.
3a. TXFE, BUSY, TIS, interrupt asserted, TXFF negated.

4. It's now safe to write. Write 2nd byte to data (=hold) register.
4a. TXFF, BUSY asserted, TIS, TXFE, interrupt negated.

5. Transmission of 1st byte completed, transfer 2nd byte to transmit register.
5a. TXFE, BUSY, TIS, interrupt asserted, TXFF negated.

6. It's now safe to write. Write 3rd byte to data (=hold) register.
6a. TXFF, BUSY asserted, TIS, TXFE, interrupt negated.

7. Transmission of 2nd byte completed, transfer 3rd byte to transmit register.
7a. TXFE, BUSY, TIS, interrupt asserted, TXFF negated.

8. Transmission of 3rd byte completed.
8a. TXFE, TIS, interrupt asserted, TXFF, BUSY negated.

The 2a state is not visible if the transfer from hold to transmit is
clocked by the bus clock. The 2a state may be sometimes, but not always
visible, if the transfer is clocked by the UART clock.


Return to: