Consider a simple C program `flash.c' which flashes the LED. Such a program can be written as follows:
#include <lanai3_def.h>
main(){
while(1){LED = RTC>>18;}
}
This program can be compiled using the following command:
> lanai3-gcc -o flash flash.c
One can also compile the program into an intermediate object file `flash.o' using the following command:
> lanai3-gcc -c flash.c
The object file can then be linked to produce the executable `flash' using the following command:
> lanai3-gcc -o flash flash.o
More generally, just about anything that can be done with `gcc' can be done with `lanai3-gcc'. These features are documented at http://www.myri.com/scs/L3/doc/gcc_toc.html . Although one can invoke `ld' and `as' directly, we, at Myricom, have always found it more convenient to let `lanai3-gcc' invoke them for us.
The easiest way to build C++ programs is to name the source file `file.C' and then compile them just like C files.
There are other alternatives, which are described in the `gcc' documentation, which may be found at http://www.myri.com/scs/L3/doc/gcc_toc.html .
Consider a simple assembly program `flash.s' which flashes the LED. Such a program can be written as follows:
.global _main
_main:
ld [0xffffff34],%r14
nop
sha %r14,-18,%r13
bt _main
st %r13,[0xffffff94]
The symbol `_main' marks the entry point of your program.
This program can be built using the following command:
> lanai3-gcc -o flash flash.s
One can also compile the program into an intermediate object file `flash.o' using the following command:
> lanai3-gcc -c flash.s
The object file can then be linked to produce the executable `flash' using the following command:
> lanai3-gcc -o flash flash.o
More generally, just about anything that can be done with `gcc' can be done with `lanai3-gcc'. These features are documented at http://www.myri.com/scs/L3/doc/gcc_toc.html . Although one can invoke `ld' and `as' directly, we, at Myricom, have always found it more convenient to let `lanai3-gcc' invoke them for us.
A CLOCK_VAL is a number used to set various adjustable parameters
inside the LANai. The adjustable parameters are used to compensate
for variations in the fabrication process used to make the LANai chips.
You shouldn't need to know what clock value is needed, because the command
> lload -v version_number file
and
> l3clock -v version_number file
automatically sets the clock value to the recommended value. However, for those who are curious, the current recommended clock values for LANai3 chips are
0xE0000000
0x01400000
0x11070000
A "unit number" is a number indicating a particular LANai board in a machine. The numbers will range from 0 to N-1 where N is the number of LANai board in the machine. The lower the slot number of the slot in which the LANai is installed, the lower the unit number.
LANai executables, produced by the LANai compiler `lanai3-gcc' are loaded into the lanai an run using the `lload' command. For example, the `flash' program would be loaded into a LANai3.2 board with unit number 0 using the following command:
> l3clock -u 0 -v 3.2 > lload -u 0 file
If the unit number is zero, as is the case if there is only one LANai board in your machine, then you can omit the `-u 0' option. Also, the `l3clock' command need only be run once to set the clock value; subsequent invokations of `lload' do not require `l3clock' to be invoked again.
The anatomy of memory containing a loaded program is as follows:
+-+ <- Top of memory (128K (higher for L4.X boards)) | | }- stack (grows down) ~~~ <- Floating boundary | | }- unused memory +-+ <- _L3_end_loaded_memory (a.k.a "L3_end_loaded_memory" in C source) | | }- Loaded program +-+ <- address 0
The "loaded program" consists of the following packed chunks of data, which are placed at increasing adresses:
By default, each program linked by the compiler is linked with `crt0.o' and `libgcc.a'. `crt0.o' initializes the stack and calls `main()'. To move the stack, you must replace `crt0.o' and tell the compiler not to use the default version. You can replace `crt0.o' with your own version using the following code, changing the stack pointer to suite your needs:
.global start
start:
mov 0x20000,%sp ! stack pointer at 128K
mov %sp,%fp ! frame pointer
add %pc,8,%rca
bt _main !call _main
st %rca,[--%sp] !push return address during shadow
bt . ! trap in infinite loop
nop
You can then link this new `crt0.s' with your own program using the following commands:
> lanai3-gcc -c crt0.s > lanai3-gcc -o your_file_name -nostdlib crt0.o your_object_files -lgcc
`crt0.o' MUST be the first object file or library mentioned in the
final linking command to cause the code in crt0.o to be placed at
address 0 in the LANai, so this code will be the first code executed
after a reset. The `-nostdlib' option tells the compiler not to
link with the default `crt0.o' and `libgcc.a' files. The
`-lgcc' option tells the compile that despite the `-nostdlib'
option, we do want to link with this library, which is required to
resolve the reference to __main() that occurs implicitly in every
C program with the function main().
The simplest way to reserve some memory at a fixed location is to lower the stack from the top of memory as described in the preceeding section. If you do so, the memory between the top of the stack and the top of memory will be free to be used however you desire. Care should be taken not to lower the stack so much that it intersects program memory.
However, If you are attempting to store data at a fixed location to
communicate with a host program, you should consider using the function
lanai_read_symbol_table(filename) from
`libLanaiDevice.c', which allows you to read the symbol table from
a LANai executable. You can then use
lanai_symbol_value(table, name) to determine the
location of any symbol name. The interface is defined in
`lanai_device.h' and `src/arch/dev/files.c'. Using
symbolic addresses this way can greatly simplify code maintenance.
Unless you require data alignment larger than 8 bytes, a generally
superior alternative to storing data at a fixed location to share the
data with a host application is to place all the data you wish to share
in a data structure and then pass a pointer to the data structure to the
host during initialization. This can be done, for example, by having the
LANai program store a pointer to the data structure in the SMP
register (which was chosen because it is at a known location, readable
from the host, and unused during initialization), set the
HOST_SIG_BIT of ISR, and wait for the HOST_SIG_BIT
to be cleared. The host would wait for the HOST_SIG_BIT to be
set, read the pointer from SMP and clear the HOST_SIG_BIT.
If this solution is used, you will probably want to clear the
EIMR register from the host side to prevent the host from being
interrupted when the HOST_SIG_BIT of ISR is set.
The space between the symbol _L3_end_loaded_memory
(L3_end_loaded_memory in C programs) and the stack is free. You
may do anything you want with it as long as you leave enough space free
for the stack to grow as big as needed.
From C, simply use the marco defined for each special register.
In assembly language, you will generally use an instruction of the form "ld [0xffffff??],%r?".
If you choose to define a symbol to access the special registers, care must be taken that the symbol can be resolved a assembly time. Otherwise, for fullword loads, the assembler will assume that the load should be performed using a "Special Load Store" instructions, the special register address will be out of range of this instruction, and you will get an error when linking. The "Special Load Store" is used by default because it can address the entire range of memory (excepting the special registers), and is, consequently, perfectly suited to handle fullword relocations anywhere they are really needed.
The following simple example program switches between two contexts: one that turns the LED on and another that turns the LED off:
#include <lanai4_def.h>
void
user ()
{
while (1)
{
LED = 1; /* Turn on LED */
while (RTC & 1<<17); /* Pause */
RESUME_USER; /* Switch to system context */
}
}
void
main ()
{
IMR = 0; /* Turn of interrupts */
START_USER (user, 64*1024); /* Set up user context to run user()
with user stack growing down from
64K */
while (1)
{
LED = 0; /* Turn off LED */
while (! (RTC & 1<<17)); /* Pause */
RESUME_USER; /* Switch to user context */
}
}
You can simply use the following header file and assembly source for setjmp/longjmp.
Here is the source to `setjmp.h':
#ifndef _setjmp_h_
#define _setjmp_h_
typedef struct jmp_buf
{
int fp;
int sp;
int ra;
} jmp_buf[1];
int setjmp (jmp_buf env);
void longjmp (jmp_buf env, int val);
#endif
Here is the source to `setjmp.s':
.text .globl _setjmp _setjmp: ld 4[%sp],%r14 ! Load pointer to jmp_buf from argument list add %sp,0x8,%sp ! pop return addr and arguments ! Save values for longjmp return. st %fp,[%r14++] st %sp,[%r14++] st %rca,[%r14++] ! Return 0 mov %rca, %pc mov 0,%rv .globl _longjmp _longjmp: ld 4[%sp],%r14 ! Load pointer to jmp_buf from argument list ld 8[%sp],%rv ! Set return value as specified by caller ld 8[%r14],%rca ! Read return address from jmp_buf ld 4[%r14],%sp ! Read stack pointer from jmp_buf ld [%r14],%fp ! Read frame pointer from jmp_buf mov %rca,%pc ! Return (to address from setjmp) nop ! Could not fill branch shadow