![]() |
![]() |
|
| Introduction | ||
![]() |
![]() |
|
![]() |
In the recent time, the Atari Coldfire Project was not publicly present very much. Thus, some people are sceptical about the current state of the project. As I cannot speak for all members of the project --- each one dedicated to a different field of the project --- I will present my activies and views in regard to create a Coldfire-based operating system in this document. I have to disappoint those of you, who expect the presentation of a ready-to-use operating system. This goal is not reached, yet. The time frame needed to build this operating system depends mainly on my spare time. Concretely, this document describes my experiences with the MCF5407 Coldfire board. The board was supplied by Gerald Kupris from Motorola to enable us to develop our operating system before our custom hardware will be ready. A lot of thanks for this great opportunity! I also use this document as a personal notebook about implementation-related issues. Thus, do not wonder about the level of detail in some sections. The the goals of my work are to get MiNT to run on the Coldfire and to try out ideas about operating system architectures. The most of the stuff I did with the board so far were experiments to learn about the Coldfire and are not immediately related to Atari. So please do not wonder about some weird things, described here. |
![]() |
![]() |
![]() |
![]() |
![]() |
|
| Where to get the source code? | ||
![]() |
![]() |
|
![]() |
All things, I describe within this document are publicly available under the terms of the GNU General Public Licence Version 2. I use a Subversion repository for development. Subversion is a revision control system, which is similar to CVS but offers some advanced features (for example, moving and copying of files) that CVS lacks of. Further information about Subversion is available at http://subversion.tigris.org. There is a very detailed description of this tool available at http://svnbook.red-bean.com. My Subversion repository is hosted at http://os.inf.tu-dresden.de/~nf2/svn. You can browse the repository using your web browser. You can check out the Coldfire related stuff by using the instruction:
svn checkout http://os.inf.tu-dresden.de/~nf2/svn/trunk/acp
All revision information, such as log messages, are world-readable, too. |
![]() |
![]() |
![]() |
![]() |
![]() |
|
| Cypress PCI bridge | ||
![]() |
![]() |
|
![]() |
The MCF5407 evaluation board features a PCI connector. This is a great chance for me to learn about programming PCI devices. My concrete goal was to drive a graphics card. The following sections contain technical stuff that I learned from my experiments or retrieved from various sources (documentation, uClinux kernel, internet).
The memory area from Building the PCI address spaceBefore PCI resources can be accessed, we must map them to the PCI address space, which has nothing to do with the host's address space. We can set all bits and subsequently read the memory base address registers of the PCI device's configuration space to determine the type and size of the provided resources (this is a standard way which is described in Markus Fichtenbauer's PCI article http://acp.atari.org/files/pci-bios/index.html). The lowest bit of an base address register indicates if the entry belongs to an I/O (bit0 = 1) or memory resource (bit0 = 0). The number of the other lower bits which are set to zero provide the information about the size of the resource. I/O and memory resources can be mapped to PCI addresses by writing the desired start addresses to their corresponding memory base registers in the PCI configuration block of the PCI device.
Additionally, the access to I/O and memory resources must be enabled by
setting the bits 0 and 1 of the DEVCTRL ( PCI window
The Cypress PCI bridge supports three types of PCI transactions which can
be performed with the help of the physical base address register (
The physical base address register must be set
to
The physical base address register must be set to the
desired base address of the PCI address space where we mapped the
I/O resource (see section Building the PCI address space). Additionally,
the lowest two bits must be set to
We have to do the same as for I/O transactions except
that the lowest two bits of the physical base address register must be
set to
After that, the defined configuration, I/O or memory space of the connected PCI
card is available at the PCI address space window (at Endian
The Cypress PCI bridge seem to perform a conversion to big-endian.
I guess this can be configured using the Local Bus Configuration
Register ( |
![]() |
![]() |
![]() |
![]() |
![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Video Device Driver Environment | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
After a half-successful attempt to hack the Matrox driver code from the Linux-2.2 kernel (after 2-3 days of intensive digging in the driver code I even got the Matrox to display a blue screen) I decided to give the Device Driver Environment (DDE) approach a go. Instead of modifying the original driver code and inducing new bugs I decided to let the original driver code of Linux-2.4.20 run in an emulation enviroment. A small emulation code layer provides all functions needed to let the driver "think" it is at home in the Linux kernel. The following steps were needed to perform this task: Choosing the driver sourcesThe needed driver source code files had to be identified. I choose the files shown in table 1. Not all of them are actually needed to drive a Mystique but including them simplifies later support for other Matrox models. As you can see, the ported driver code consists of ca. 9000 lines (without comments).
Compiling the driverI had to determine the needed Linux-kernel header files. This was easy:
grep -h "^#include" *.c *.h | sort | uniq
The more difficult job was to decide which header files of the Linux kernel could
be used in their original form and which ones had to be replaced by own
implementations or fakes. Table 2 shows an overview over the
unchanged and modified/faked header files. This step was - by far - the
most time intensive one. I also had to determine the right defines to
choose to produce a driver that actually does something useful (the
configuration can be found in
Linking and the implementation of the driver environment
In order to link an executable binary, all unresolved symbols had to
be implemented (or at least faked). At first I created dummy implementations
using a
Changes of the original driver codeThe original driver code had to be slightly changed to make it run on the Coldfire board. Due to the DDE approach these changes were very small (nearly non-existent):
Firing the bullet
After successfull compilation and linking I had to find the proper way
to jump into the drivers code. To my very surprise all I had to do was
to call the function Experiments with the ATI Rage driver from Linux-2.4.20I enhanced the video device driver environment to provide all functionality needed for the aty driver from Linux-2.4.20. The driver starts up and successfully probes my 3D Rage (GT) card. It also initialises a default video mode but I have not managed to display a descent picture on screen. In the meanwhile I discovered that the power consumption of more recent graphics boards is just too high for the Coldfire evaluation board. I experimented with ATI Radeon, Matrox G450, ATI Rage LX and various other graphics cards, but the Matrox 1064 was the only one with a low-enough power consumption. Modifications of the Linux driver
If the Coldfire executes the function |
![]() |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
![]() |
![]() |
![]() |
|
| Interrupt-based driver for Wacom Artpad | ||
![]() |
![]() |
|
![]() |
The MCF5407 evaluation board features two serial ports --- one is used for debugging and the other is available for custom use. Since I needed an input device for my further work, I decided to implement a driver for a Wacom Artpad, that lied around somewhere. The nice thing about the Wacom Artpad is its simple communication protocol. Since the driver had to work interrupt-driven, I had to discover how to program interrupts on the Coldfire, which was a fairly easy walk. The initialitation of the serial interfaces is well described by the source code of the Motorola dBUG monitor and the uCLinux source code. |
![]() |
![]() |
![]() |
![]() |
![]() |
|
| DOpE window server | ||
![]() |
![]() |
|
![]() |
I ported the DOpE window system to the Coldfire board. DOpE is a window system that I develop for the real-time operating system DROPS. It is my current research project at the university. Information about DOpE are available on my private website:
Basically, I needed the port of DOpE to the Coldfire for ego-reasons ;-) |
![]() |
![]() |
![]() |
![]() |
![]() |
|
| MicroAPL CF68K Emulation Library | ||
![]() |
![]() |
|
![]() |
One fundamental thing for running Atari applications on the Coldfire is a working emulation of MC68k instructions that are not present in the Coldfire. MicroAPL provides a solution for this problem: The cf68klib is a virtual machine that can either execute usermode MC68k instructions only, or virtualizes a whole MC68k CPU including supervisor mode! The exception vector table and all interrupts are also virtualized by cf68klib such that a whole operating system could be executed within the virtual machine. After a weekend of studdying the documentation and experimenting, I successfully started up the virtual machine and executed instructions that are not natively implemented in the Coldfire CPU! This is a big step for getting MiNT to run on this platform. |
![]() |
![]() |
![]() |
![]() |
![]() |
|
| Porting MiNT to the Coldfire | ||
![]() |
![]() |
|
![]() |
There are two possible ways of how to port the FreeMiNT kernel to the Coldfire:
I decided to give the second approach a go. For executing the MiNT kernel, we first must relocate the TOS executable --- TOS executables can be loaded at any address in memory. Thus, when absolute references within the executable are used by the program, these references must be adapted to the actual position of the executable in memory. I just deploy the relocation routine that I used in many demos and it works well on the Coldfire. Actually, even the relocation routine runs inside the cf68klib virtual machine because it uses some non-Coldfire MC68k instructions. Providing an execution environment to MiNTThe MiNT kernel image is a normal TOS executable and thus, it relies on the environment, provided by TOS --- such as a valid basepage containing all information about the segments, environment string and arguments of the program. I implemened a basepage initialization routine. The address of the basepage is then passed to MiNT. Now, with the basepage provided, we can jump into the MiNT kernel.
We must let MiNT think to be at home at TOS by providing the TOS functions
that are actually needed by MiNT.
The first things we see from the MiNT kernel are some attempts to call
GEMDOS functions via the In result we get some nice GEMDOS traps such as Mshrink, Fsfirst and Cconws. Yes! There are the first boot messages of MiNT! Trap handler with gccSadly, the way of how arguments are passed to TOS via the known TOS syscall bindings is not compatible with the function calling conventions used by gcc. TOS programs use to push 16bit and 32bit arguments onto the stack without any gaps in-between. In contrast, gcc alignes all arguments to 32bit-even addresses by leaving 2-byte gaps between two 16bit arguments. According to Peter Barada, the maintainer of the Coldfire-gcc, there is no way to prevent the 4-byte stack alignment of gcc. Thus, we need to convert the syscall frame to a valid gcc call frame. This conversion is called marshalling (slightly inspired by the terminology used for IDL compilers). For this, I created a small tool that generates the needed conversion code for specified syscall signatures. In the meanwhile, Frank Naumann told me a way of how to work around the 32-bit stack alignment. All one need to do is to pass the parameters not directly but using a structure. Thus, MiNT applies its alignment policy for aligning struct elements when constructing the structure in memory. The default policy for that is 16bit alignment - exactly what we need. So we could implement the trap dispatch functions simply by using
struct setexc_args { short vecnum, long handler; };
static long setexc(struct setexc_args args) {
...
}
instead of
static long setexc(short vecnum, long handler) {
...
}
Anyway, I kept using the generated stubs because I do not want the kernel to work directly on the user stack pointer. In this case any malicious user program could crash the system by setting its stack pointer to an illegal address before calling the kernel. Telling MiNT some sweet liesDuring startup, MiNT determines on which kind of hardware platform and TOS-version it runs and takes this information in account for its later operation. To make MiNT behave as we like it, we tell MiNT some (bogus) information. Later, we can build-in Coldfire-specific support into the MiNT kernel. One information source, MiNT takes into account, is the cookie jar. This is a central point in the system that provides so-called cookies_, which hold information for certain system properties and interfaces to software extensions of the operating system. On the Atari the cookie jar is created by TOS and filled with information about the machine type. In our execution environment we also create such a cookie jar and supply the following cookies: COOKIE__MCH
This is the machine type. We define its value to be
This cookie specifies the installed video hardware.
We set this cookie to the value
This cookie holds the information about the availability of a PMMU (Programable
Memory Management Unit).
Since the Coldfire does not feature a PMMU, we need to make MiNT to keep its
hands away from PMMU related things and thus, set this cookie to Timer initialization
One thing, MiNT relies on, is a 200Hz timer that operates on the exception
vector
timer_200hz_vec:
bsr timer_200hz_start | init next timer interrupt
addq.l #1,JIFFIES_200HZ | increment jiffies value
move.l TIMER_C_VEC,-(%sp) | branch to timer-c exception handler
rts
The subroutine Now, with a timer provided by us, MiNT passes its "calibrate delay loop" successfully. Argh! What do we see there? 2.54 BogoMIPS? This was really not the performance, I expected! In fact, it is only half as fast as a Falcon. Instead of whining, I checked the cache of the Coldfire and found it switched off. After activating the cache we get 151.55 BogoMIPS. This was the value, I expected from the 150Mhz Coldfire processor. In fact, the cf68klib does not affect this value because the benchmarking function does not use any instructions, that must be emulated. Character output via BIOS
At the very first stage of MiNT's startup, it uses GEMDOS for textual
ouput. After it takes over the GEMDOS, BIOS and XBIOS traps, however, it
switches over to BIOS-based textual output. In fact, it does not use
BIOS traps for printing characters (one trap exception per character would
be damn slow) but it uses the vectors Keyboard inputCurrently, MiNT does not feature a custom keyboard driver but relies on the BIOS for keyboard input. This is quite nice for us because it gives us the opportunity to easily emulate a keyboard by our custom BIOS routines that can use the serial terminal as a virtual keyboard (the Coldfire evaluation board does not feature a keyboard connector).
Keyboard events are passed from the BIOS to MiNT via a ring-buffer,
called TOS services that are needed by MiNTThe MiNT kernel relies on a number of TOS system variables and TOS system calls during its bootup. System variables
TOS uses the memory area from 0x4f2)
Pointer to a structure that describes the installed
operating system (TOS). MiNT determines the installed TOS version
by reading the xcostat ( 0x55e + 0x8)
The eight variables at xconout ( 0x57e + 0x8)
Similar to getbpb ( 0x472)
This is a vector to a function that returns the BIOS parameter block. MiNT executes this routine. Thus, we need to install at least a dummy routine there. _longframe ( 0x59e)
This variable describes the format of the exception stack frame. It is evaluated by the Timer-C handler of MiNT. We just have to make sure that this variable is set to zero. _timr_ms ( 0x442)
This value describes the period of the VBL timer interrupt. Normally, it is set to 20 (20 miliseconds) - so we do the same. This system variable is used by MiNT's VBL handler to schedule context switches. _p_cookiesThis variable contains the pointer to the cookie jar. As described in section Telling MiNT some sweet lies, we provide a cookie jar to MiNT. _hz_200 ( 0x4ba)
This variable is also called jiffies counter. Its value is incremented on each Timer-C interrupt. MiNT does not increment this value by itself but relies on TOS to increment this variable. System callsIn the execution environment, we handle trap 1 (GEMDOS), trap 2 (GEM), trap 13 (BIOS) and trap 14 (XBIOS). By default, we do nothing and just return 0. Anyhow, some system calls must be dispatched by the execution environment. This section is a brief summary of the system calls that must be handled specifically to make MiNT behave nicely. GEMDOS callsmxallocMxalloc is used by MiNT to obtain its memory from TOS. MiNT takes all memory it can get by subsequentially calling this function. All received memory blocks are then handled over to MiNT's memory manager. We only return one chunk of ST-RAM memory. cconwsThis function is used during the first stage of startup for printing the very first boot messages. We just forward all characters to the serial connection. tgettimeTgettime is used during the startup for waiting for the users input. We just return a counter value that is incremented on each call to make MiNT proceed with its startup. superThis function is used by MiNT for entering the supervisor mode and thus, to take over control over the machine. Ok, so be it. BIOS callsbconoutThis function is used by MiNT for its textual output after the very first startup stage is finished. We just forward all characters to the serial console. XBIOS callssupexec
This XBIOS call is used to execute one subroutine in supervisor
mode. MiNT uses
We just return a -1 to make MiNT believe that we do not
feature any This function returns a pointer to a key table structure. This structure contains mapping tables from hardware keycodes to ASCII values. Right now, the tables are bogus but, at least, they do exist. iorec
GEM callsappl_init
MiNT calls Modifications of the MiNT kernelThanks to the approach of the execution environment, I only needed some very minor modifications of the MiNT kernel. Actually, I had to modify only two lines of code. ;-) CPU detection
Although the cf68klib emulates an MC68030 CPU, MiNT detects an MC68040 for
some reason.
I did not examine this issue any further and set the Incompatibility of the
|
![]() |
![]() |
![]() |
![]() |
![]() |
|
| Various other things | ||
![]() |
![]() |
|
![]() |
During my life together with the Coldfire evaluation board I did a lot of experiments, which I did not described in detail. Among these things were the attempt to port the MiNT kernel to run natively on the Coldfire CPU, programming the hardware acceleration features of the Matrox 1064, building a toolchain for gcc-3.3.2 and creating a build system for my Coldfire developments based on GNU make. |
![]() |
![]() |
![]() |
![]() |
![]() |
|
| The next things to do | ||
![]() |
![]() |
|
![]() |
This is roughly a list of my next steps on the path to a proper TOS- compatible OS on the Coldfire:
Once I have MiNT running at a usable degree, I will try out some OS architectural ideas leading to the direction of a microkernel (similar to L4). My vision is to have a microkernel-based multi-server operating system running with MiNT as single-server side-by-side. (quite similar to L4Linux running on L4). |
![]() |
![]() |
![]() |
![]() |
![]() |
|
| Document history | ||
![]() |
![]() |
|
![]() |
changes at 2004-03-14
fixed references, added table of contents changes at 2004-06-13 added section Porting MiNT to the Coldfire changes at 2004-06-14 added section Where to get the source code? changes at 2004-07-10 complemented section Trap handler with gcc with Frank's solution, added sections Telling MiNT some sweet lies, Timer initialization, Character output via BIOS, Keyboard input, TOS services that are needed by MiNT and Modifications of the MiNT kernel |
![]() |
![]() |
![]() |