Commit 3e9f9f3a authored by ROOL's avatar ROOL :robot:
Browse files

This commit was manufactured by cvs2git to create tag 'Kernel- 5_35-4_79_2_60'.

Sprout from HAL 2003-04-15 20:18:37 UTC Kevin Bracey <kbracey@gitlab.riscosopen.org> 'Version increased to dizzy 5.03'
Delete:
    Docs/HAL/ADisNote
    Docs/HAL/ARMop_API
    Docs/HAL/Entries
    Docs/HAL/HAL_API
    Docs/HAL/Init
    Docs/HAL/MoreEnts
    Docs/HAL/NewAPI
    Docs/HAL/NewCDV
    Docs/HAL/Notes
    Docs/HAL/OS_Hardware
    Docs/HAL/OpenBusAdapter
    Docs/HAL/Serial
    Docs/MemMaps/+Access,ffd
    Docs/MemMaps/+SrcIndex
    Docs/MemMaps/+SrcIndexO
    Docs/MemMaps/130
    Docs/MemMaps/258
    Docs/PrivDoc/+Access,ffd
    Docs/PrivDoc/+SrcIndex
    Docs/PrivDoc/+SrcIndexO
    Docs/PrivDoc/5thColumn/+Access,ffd
    Docs/PrivDoc/5thColumn/+SrcIndex
    Docs/PrivDoc/5thColumn/+SrcIndexO
    Docs/PrivDoc/5thColumn/Concept
    Docs/PrivDoc/MMPM
    Docs/PrivDoc/ScreenMode
    NewModes/Make,feb
    NewModes/NEWF2
    NewModes/NEWFORMAT
    NewModes/OldFormat
    NewModes/OldPSSrc
    NewModes/OldToNew,ffb
    NewModes/PSSrc
    Resources/UK/CmdHelp
    Resources/UK/Messages
    Resources/UK/Morris4/Messages
    Resources/UK/Omega/Messages
    Resources/UK/Ursula/Messages
    TestSrc/A600tlb
    TestSrc/Arm3
    TestSrc/Begin
    TestSrc/Cmos
    TestSrc/ErrorCount,ffb
    TestSrc/ExtCmd
    TestSrc/ExtIO
    TestSrc/InitModule
    TestSrc/Ioc
    TestSrc/LEDDelay
    TestSrc/MEMC1
    TestSrc/Mem1IOMD
    TestSrc/Mem1MEMC1
    TestSrc/Mem2
    TestSrc/Mem3
    TestSrc/Mem4
    TestSrc/Mem5
    TestSrc/ROMCard
    TestSrc/ShowIOMDRs
    TestSrc/TestMain
    TestSrc/ToggleLED
    TestSrc/Vidc
    h/HALDevice
    hdr/ARMops
    hdr/Copro15ops
    hdr/EnvNumbers
    hdr/ExportVals/!HowTo
    hdr/ExportVals/Makefile
    hdr/ExportVals/Mk,fd7
    hdr/ExportVals/s/GetVals
    hdr/ExportVals/values
    hdr/HALDevice
    hdr/HALEntries
    hdr/KernelWS
    hdr/KeyWS
    hdr/ModHand
    hdr/OSEntries
    hdr/Old/Arthur/PublicWS
    hdr/Old/Arthur/Space200
    hdr/Old/NewSpace
    hdr/Old/VickySpace
    hdr/Options
    hdr/PublicWS
    hdr/RISCOS
    hdr/Variables
    hdr/VduExt
    s/AMBControl/AMB
    s/AMBControl/Memory
    s/AMBControl/Options
    s/AMBControl/Workspace
    s/AMBControl/allocate
    s/AMBControl/deallocate
    s/AMBControl/growp
    s/AMBControl/growshrink
    s/AMBControl/main
    s/AMBControl/mapslot
    s/AMBControl/mapsome
    s/AMBControl/memmap
    s/AMBControl/readinfo
    s/AMBControl/service
    s/AMBControl/shrinkp
    s/ARM600
    s/ARMops
    s/Arthur2
    s/Arthur3
    s/ArthurSWIs
    s/ChangeDyn
    s/Convrsions
    s/End
    s/ExtraSWIs
    s/FlashROM
    s/GetAll
    s/HAL
    s/HeapMan
    s/HeapSort
    s/KbdResA1
    s/KbdResPC
    s/KbdResRCMM
    s/Kernel
    s/LibKern
    s/MEMC1
    s/MEMC2
    s/MOSDict
    s/MemInfo
    s/Middle
    s/ModHand
    s/MoreComms
    s/MoreSWIs
    s/Morris
    s/MsgCode
    s/NewIRQs
    s/NewReset
    s/Oscli
    s/PMF/Buffer
    s/PMF/Def
    s/PMF/IIC
    s/PMF/Internat
    s/PMF/KbdDrA1
    s/PMF/convdate
    s/PMF/i2cutils
    s/PMF/key
    s/PMF/mouse
    s/PMF/osbyte
    s/PMF/oseven
    s/PMF/osinit
    s/PMF/osword
    s/PMF/oswrch
    s/PMF/realtime
    s/SWINaming
    s/Super1
    s/SysComms
    s/TickEvents
    s/UnSqueeze
    s/Utility
    s/vdu/vdu23
    s/vdu/vdu5
    s/vdu/vducursoft
    s/vdu/vdudecl
    s/vdu/vdudriver
    s/vdu/vdufont
    s/vdu/vdufontl1
    s/vdu/vdugrafa
    s/vdu/vdugrafb
    s/vdu/vdugrafc
    s/vdu/vdugrafd
    s/vdu/vdugrafdec
    s/vdu/vdugrafe
    s/vdu/vdugraff
    s/vdu/vdugrafg
    s/vdu/vdugrafh
    s/vdu/vdugrafi
    s/vdu/vdugrafj
    s/vdu/vdugrafk
    s/vdu/vdugrafl
    s/vdu/vdugrafv
    s/vdu/vduhint
    s/vdu/vdumodes
    s/vdu/vdupal10
    s/vdu/vdupal20
    s/vdu/vdupalette
    s/vdu/vdupalxx
    s/vdu/vduplot
    s/vdu/vdupointer
    s/vdu/vduswis
    s/vdu/vduttx
    s/vdu/vduwrch
No related merge requests found
hdr/** gitlab-language=armasm linguist-language=armasm linguist-detectable=true
s/** gitlab-language=armasm linguist-language=armasm linguist-detectable=true
**/s/** gitlab-language=armasm linguist-language=armasm linguist-detectable=true
*,ffb gitlab-language=bbcbasic linguist-language=bbcbasic linguist-detectable=true
h/** gitlab-language=c linguist-language=c linguist-detectable=true
0 STATE (X) 0->X (8)
1 nWAIT 1->9
2 BRST 2->X (2)
3 nFIQ 3->X (13)
4 nIRQ 4->X (12)
5 nc (MAS[0]) 5->6
6 nMREQ 1 6->1
7 SEQ (X) 7->0
8 nRW 8->3
9 nBW (MAS[1]) 9->7
10 LOCK 10->X (10)
11 nTRANS (X) 11->5
12 nOPC (X) 12->4
13 nc 13->11 (DMA low)
14 ABE
15 DBE
Master clock: (MCLK down)
0 SEQ 0->7
1 nMREQ 1->6
2 nEXEC
Slave clock: (MCLK up)
3 nRW 16->8
4 nOPC 17->12
5 nTRANS 18->11
6 MAS[0] 19->5
7 MAS[1] 20->9
8 DBGACK
9 nWAIT 22->1
10 ABORT
11 DMA
12 BIGEND
13 CS0
14 CS1
15 CS2
16 CS3
17 CS4
18 CS5
19 CS6
20 CS7
0
0->X
\ No newline at end of file
12345678901234567890123456789012345678901234567890123456789012345678901234567890
mjs 12 Jan 2001 Early Draft
mjs 14 Feb 2001 XScale survey revised, ARMop reentrancy defined
RISC OS Kernel ARM core support
===============================
This document is concerned with the design of open ended support for
multiple ARM cores within the RISC OS kernel, as part of the work loosely
termed hardware abstraction. Note that the ARM core support is part of the
OS kernel, and so is not part of the hardware abstraction layer (HAL)
itself.
Background
----------
ARM core support (including caches and MMU) has historically been coded in a
tailored way for one or two specific variants. Since version 3.7 this has
meant just two variants; ARM 6/7 and StrongARM SA110. A more generic
approach is required for the next generation. This aims both to support
several cores in a more structured way, and to cover minor variants (eg.
cache size) with the same support code. The natural approach is to set up
run-time vectors to a set of ARM support routines.
Note that it is currently assumed that the ARM MMU architecture will not
change radically in future ARM cores. Hence, the kernel memory management
algorithms remain largely unchanged. This is believed to be a reasonable
assumption, since the last major memory management change was with Risc PC
and ARM 610 (when the on-chip MMU was introduced).
Note that all ARM support code must be 32-bit clean, as part of the 32-bit
clean kernel.
Survey of ARM core requirements
-------------------------------
At present, five broad ARM core types can be considered to be of interest;
ARM7 (and ARM6), ARM9, ARM10, StrongARM (SA1) and XScale. These divide
primarily in terms of cache types, and cache and TLB maintenance
requirements. They also span a range of defined ARM architecture variants,
which introduced variants for system operations (primarily coprocessor 15
instructions).
The current ARM architecture is version 5. This (and version 4) has some
open ended definitions to allow code to determine cache size and types from
CP15 registers. Hence, the design of the support code can hope to be at
least tolerant of near future variations that are introduced.
ARM7
----
ARM7 cores may be architecture 3 or 4. They differ in required coprocessor
15 operations for the same cache and TLB control. ARM6 cores are much the
same as architecture 3 ARM7. The general character of all these cores is of
unified write-through caches that can only be invalidated on a global basis.
The TLBs are also unified, and can be invalidated per entry or globally.
ARM9
----
ARM9 cores are architecture 4. We ignore ARM9 variants without an MMU. The
kernel can read cache size and features. The ARM 920 or 922 have harvard
caches, with writeback and writethrough capable data caches (on a page or
section granularity). Data and instruction caches can be invalidated by
individual lines or globally. The data cache can be cleaned by virtual
address or cache segment/index, allowing for efficient cache maintenance.
Data and instruction TLBs can be invalidated by entry or globally.
ARM10
-----
ARM 10 is architecture 5. Few details available at present. Likely to be
similar to ARM9 in terms of cache features and available operations.
StrongARM
---------
StrongARM is architecture 4. StrongARMs have harvard caches, the data cache
being writeback only (no writethrough option). The data cache can only be
globally cleaned in an indirect manner, by reading from otherwise unused
address space. This is inefficient because it requires external (to the
core) reads on the bus. In particular, the minimum cost of a clean, for a
nearly clean cache, is high. The data cache supports clean and invalidate by
individual virtual lines, so this is reasonably efficient for small ranges
of address. The data TLB can be invalidated by entry or globally.
The instruction cache can only be invalidated globally. This is inefficient
for cases such as IMBs over a small range (dynamic code). The instruction
TLB can only be invalidated globally.
Some StrongARM variants have a mini data cache. This is selected over the
main cache on a section or page by using the cachable/bufferable bits set to
C=1,B=0 in the MMU (this is not standard ARM architecture). The mini data
cache is writeback and must be cleaned in the same manner as the main data
cache.
XScale
------
XScale is architecture 5. It implements harvard caches, the data cache being
writeback or writethrough (on a page or section granularity). Data and
instruction caches can be invalidated by individual lines or globally. The
data cache can be fully cleaned by allocating lines from otherwise unused
address space. Unlike StrongARM, no external reads are needed for the clean
operation, so that cache maintenance is efficient.
XScale has a mini data cache. This is only available by using extension bits
in the MMU. This extension is not documented in the current manual for
architecture 5, but will presumably be properly recognised by ARM. It should
be a reasonably straightforward extension for RISC OS. The mini data cache
can only be cleaned by inefficient indirect reads as on StrongARM.
For XScale, the whole mini data cache can be configured as writethrough. The
most likely use for RISC OS is to map screen memory as mini cacheable, so
writethrough caching will be selected to prevent problems with delayed
screen update (and hence intricate screen/cache management code as in Ursula
for StrongARM). With writethrough configured, most operations can ignore the
mini cache, because invalidation by virtual address will invalidate mini or
main cache entries as appropriate.
Unfortunately, for global cache invalidatation, things are very awkward.
RISC OS cannot use the global cache invalidate operation (which globally
invalidates both data caches), unless it is very careful to 100% clean the
main cache with all interrupts (IRQs and FIQs) disabled. This is to avoid
fatal loss of uncleaned lines from the writeback main cache. Disabling
interrupts for the duration of a main cache clean is an unacceptable
latency. Therefore, reluctantly, RISC OS must do the equivalent of cleaning
the mini cache (slow physical reads) in order to globally invalidate it as a
side effect.
The instruction and data TLBs can each be invalidated by entry or globally.
Kernel ARM operations
---------------------
This section lists the definitions and API of the set of ARM operations
(ARMops) required by the kernel for each major ARM type that is to be
supported. Some operations may be very simple on some ARMs. Others may need
support from the kernel environment - for example, readable parameters that
have been determined at boot, or address space available for cache clean
operations.
The general rules for register usage and preservation in calling these
ARMops iare:
- any parameters are passed in r0,r1 etc. as required
- r0 may be used as a scratch register
- the routines see a valid stack via sp, at least 16 words are available
- lr is the return link as required
- on exit, all registers except r0 and lr must be preserved
Note that where register values are given as logical addresses, these are
RISC OS logical addresses. The equivalent ARM terminology is virtual address
(VA), or modified virtual address (MVA) for architectures with the fast
context switch extension.
Note also that where cache invalidation is required, it is implicit that any
associated operations for a particular ARM should be performed also. The
most obvious example is for an ARM with branch prediction, where it may be
necessary to invalidate a branch cache anywhere where instruction cache
invalidation is to be performed.
Any operation that is a null operation on the given ARM should be
implemented as a single return instruction:
MOV pc, lr
ARMop reentrancy
----------------
In general, the operations will be called from SVC mode with interrupts
enabled. However, some use of some operations from interrupt mode is
expected. Notably, it is desirable for the IMB operations to be
available from interrupt mode. Therefore, it is intended that all
implementations of all ARMops be reentrant. Most will be so with no
difficulty. For ARMs with writeback data caches, the cleaning algorithm
may have to be constructed carefully to handle reentrancy (and to avoid
turning off interrupts for the duration of a clean).
Cache ARMops
------------
-- Cache_CleanInvalidateAll
The cache or caches are to be globally invalidated, with cleaning of any
writeback data being properly performed.
entry: -
exit: -
Note that any write buffer draining should also be performed by this
operation, so that memory is fully updated with respect to any writeaback
data.
The OS only expects the invalidation to be with respect to instructions/data
that are not involved in any currently active interrupts. In other words, it
is expected and desirable that interrupts remain enabled during any extended
clean operation, in order to avoid impact on interrupt latency.
-- Cache_CleanAll
The unified cache or data cache are to be globally cleaned (any writeback data
updated to memory). Invalidation is not required.
entry: -
exit: -
Note that any write buffer draining should also be performed by this
operation, so that memory is fully updated with respect to any writeaback
data.
The OS only expects the cleaning to be with respect to data that are not
involved in any currently active interrupts. In other words, it is expected
and desirable that interrupts remain enabled during any extended clean
operation, in order to avoid impact on interrupt latency.
-- Cache_InvalidateAll
The cache or caches are to be globally invalidated. Cleaning of any writeback
data is not to be performed.
entry: -
exit: -
This call is only required for special restart use, since it implies that
any writeback data are either irrelevant or not valid. It should be a very
simple operation on all ARMs.
-- Cache_RangeThreshold
Return a threshold value for an address range, above which it is advisable
to globally clean and/or invalidate caches, for performance reasons. For a
range less than or equal to the threshold, a ranged cache operation is
recommended.
entry: -
exit: r0 = threshold value (bytes)
This call returns a value that the kernel may use to select between strategies
in some cache operations. This threshold may also be of use to some of the
ARM operations themselves (although they should typically be able to read
the parameter more directly).
The exact value is unlikely to be critical, but a sensible value may depend
on both the ARM and external factors such as memory bus speed.
-- WriteBuffer_Drain
Any writebuffers are to be drained so that any pending writes are guaranteed
completed to memory.
entry: -
exit: -
TLB ARMops
----------
-- TLB_InvalidateAll
The TLB or TLBs are to be globally invalidated.
entry: -
exit: -
-- TLB_InvalidateEntry
The TLB or TLBs are to be invalidated for the entry at the given logical
address.
entry: r0 = logical address of entry to invalidate (page aligned)
exit: -
The address will always be page aligned (4k).
IMB ARMops
----------
-- IMB_Full
A global instruction memory barrier (IMB) is to be performed.
entry: -
exit: -
An IMB is an operation that should be performed after new instructions have
been stored and before they are executed. It guarantees correct operation
for code modification (eg. something as simple as loading code to be
executed).
On some ARMs, this operation may be null. On ARMs with harvard architecture
this typically consists of:
1) clean data cache
2) drain write buffer
3) invalidate instruction cache
There may be other considerations such as invalidating branch prediction
caches.
-- IMB_Range
An instruction memory barrier (IMB) is to be performed over a logical
address range.
entry: r0 = logical address of start of range
r1 = logical address of end of range (exclusive)
Note that r0 and r1 are aligned on cache line boundaries
exit: -
An IMB is an operation that should be performed after new instructions have
been stored and before they are executed. It guarantees correct operation
for code modification (eg. something as simple as loading code to be
executed).
On some ARMs, this operation may be null. On ARMs with harvard architecture
this typically consists of:
1) clean data cache over the range
2) drain write buffer
3) invalidate instruction cache over the range
There may be other considerations such as invalidating branch prediction
caches.
Note that the range may be very large. The implementation of this call is
typically expected to use a threshold (related to Cache_RangeThreshold) to
decide when to perform IMB_Full instead, being faster for large ranges.
MMU mapping ARMops
------------------
-- MMU_Changing
The global MMU mapping is about to be changed.
entry: -
exit: -
The operation must typically perform the following:
1) globally clean and invalidate all caches
2) drain write buffer
3) globally invalidate TLB or TLBs
Note that it should not be necessary to disable IRQs. The OS ensures that
remappings do not affect currently active interrupts.
-- MMU_ChangingEntry
The MMU mapping is about to be changed for a single page entry (4k).
entry: r0 = logical address of entry (page aligned)
exit: -
The operation must typically perform the following:
1) clean and invalidate all caches over the 4k range of the page
2) drain write buffer
3) invalidate TLB or TLBs for the entry
Note that it should not be necessary to disable IRQs. The OS ensures that
remappings do not affect currently active interrupts.
-- MMU_ChangingUncached
The MMU mapping is about to be changed in a way that globally affects
uncacheable space.
entry: -
exit: -
The operation must typically globally invalidate the TLB or TLBs. The OS
guarantees that cacheable space is not affected, so cache operations are not
required. However, there may still be considerations such as fill buffers
that operate in uncacheable space on some ARMs.
-- MMU_ChangingUncachedEntry
The MMU mapping is about to be changed for a single uncacheable page entry
(4k).
entry: r0 = logical address of entry (page aligned)
exit: -
The operation must typically invalidate the TLB or TLBs for the entry. The
OS guarantees that cacheable space is not affected, so cache operations are
not required. However, there may still be considerations such as fill
buffers that operate in uncacheable space on some ARMs.
-- MMU_ChangingEntries
The MMU mapping is about to be changed for a contiguous range of page
entries (multiple of 4k).
entry: r0 = logical address of first page entry (page aligned)
r1 = number of page entries ( >= 1)
exit: -
The operation must typically perform the following:
1) clean and invalidate all caches over the range of the pages
2) drain write buffer
3) invalidate TLB or TLBs over the range of the entries
Note that it should not be necessary to disable IRQs. The OS ensures that
remappings do not affect currently active interrupts.
Note that the number of entries may be large. The operation is typically
expected to use a reasonable threshold, above which it performs a global
operation instead for speed reasons.
-- MMU_ChangingUncachedEntries
The MMU mapping is about to be changed for a contiguous range of uncacheable
page entries (multiple of 4k).
entry: r0 = logical address of first page entry (page aligned)
r1 = number of page entries ( >= 1)
exit: -
The operation must typically invalidate the TLB or TLBs over the range of
the entries. The OS guarantees that cacheable space is not affected, so
cache operations are not required. However, there may still be
considerations such as fill buffers that operate in uncacheable space on
some ARMs.
Note that the number of entries may be large. The operation is typically
expected to use a reasonable threshold, above which it performs a global
operation instead for speed reasons.
RISC OS and the "HAL"
=====================
RISC OS currently is tied to the IOMD20 and VIDC20 peripheral set,
descendents of the original IOC, MEMC and VIDC devices designed in parallel
with the original ARM. These devices provide a close fit with RISC OS, and
their functionality is well suited to general purpose and embedded systems,
but the continuing drive to reduce cost requires us to support other
peripheral sets on off-the-shelf ARM system on chips.
First targets for support are L7205/L7210 for Customer L and CL92xx (the new
ARM920T based devices) for Customer A. Enclosed are a summary of their
advantages and disadvantages over the ARM7500FE for our Information
Appliance designs.
L7205 CL92xx
+ Faster (50% or so) + Faster (400%+)
+ SDRAM support + SDRAM support
+ USB + USB
+ EIDE interface
+ Lots of GPIO
- No hardware cursor
- No floating point - Incompatible floating point
- No video DACs - No video DACs
- No PS/2 - No PS/2
- Bizarre MS-Windows video system
To support these devices, and others in the future, a simple HAL is to be
inserted underneath RISC OS. This will provide two functions. Firstly, it
will be responsible for initial system bootstrap, much like a PC BIOS, and
secondly it will provide simple APIs to allow hardware access.
The HAL APIs are a thin veneer on top of the hardware. They are designed to
act as replacements for all the hardware knowledge and manipulation performed
by the RISC OS Kernel, together with some APIs that will allow RISC OS driver
modules to become more hardware independent. No attempt will be made (at this
stage) to perform such tasks as separating the video drivers from the Kernel,
for example.
One tricky design decision is the amount of abstraction to aim for. Too
little, and the system is not flexible enough; too much and HAL design is
needlessly complicated for simple hardware. The present design tries to
err on the side of too little abstraction. Extra, more abstract APIs can
always be added later. So, initially, for example, the serial device API
will just provide discovery, some capability flags and the base address
of the UART register set. This will be sufficient for the vast majority
of devices. If new hardware comes along later that isn't UART compatible,
a new API can be defined. Simple hardware can continue to just report
UART base addresses.
The bulk of device driver implementation remains in RISC OS modules - the
difference is that the HAL will allow many device drivers to no longer
directly access hardware. For example, PS2Driver can now use HAL calls to
send and receive bytes through the PS/2 ports, and thus is no longer tied to
IOMD's PS/2 hardware. Similarly, interrupt masking and unmasking, as
performed by any device vector claimant, is now a HAL call. Note that HAL
calls are normally performed via a Kernel SWI - alternatively the Kernel
can return the address of specific Kernel routines. There is nothing to stop
specific drivers talking to hardware directly, as long as they accept that
this will tie them to specific devices.
This dividing line between the HAL and RISC OS driver modules is crucial. If
the HAL does everything, then we have achieved nothing - we have just as much
hardware dependent code - it's just in a different place. It is important to
place the dividing line as close to the hardware as possible, to make it easy
to design a HAL and to prevent large amounts of code duplication between
HALs for different platforms.
The Kernel remains responsible for the ARM's MMU and all other aspects of the
CPU core. The HAL requires no knowledge of details of ARM implementations,
and thus any HAL implementation should work on any processor from the ARM610
to the ARM940T or XScale.
OS independence
===============
Notionally, the HAL implementation is OS independent. It makes no assumptions
about the virtual memory map of the OS, and only uses the defined HAL->OS
entries. The HAL should not call RISC OS SWIs.
In practice, however, the HALs are unlikely to be used on anything other
than RISC OS, and many HALs are likely to be written. This makes it sensible
to place as much intelligence as possible within RISC OS itself, to prevent
duplicated effort.
Calling standards
=================
RISC OS and the HAL are two separate entities, potentially linked separately.
Thus some simple dynamic linking is required. This occurs via a hybrid of the
RISC OS module header and Shared C Library stubs. Each RISC OS/HAL entry is
given a unique (arbitrary) number, starting at 0. The offset to each entry is
given in an entry table. Calls can be made manually through this table, or
stubs could be created at run-time to allow high-level language calls.
Every entry (up to the declared maximum) must exist. If not implemented, a
failure response must be returned, or the call ignored, as appropriate.
To permit high-level language use in the future, the procedure call standard
in both directions is ATPCS, with no use of floating point, no stack limit
checking, no frame pointers, and no Thumb interworking. HAL code is expected
to be ROPI and RWPI (hence it is called with its static workspace base in
sb). The OS kernel is neither ROPI nor RWPI (except for the pre-MMU calls,
which are ROPI).
The HAL will always be called in a privileged mode - if called in an
interrupt mode, the corresponding interrupts will be disabled. The HAL should
not change mode. HAL code should work in both 26-bit and 32-bit modes (but
should assume 32-bit configuration).
Header formats
==============
The OS is linked to run at a particular base address. At present, the address
will be at <n>MB + 64KB. This allows a HAL of up to 64K to be placed at the
bottom of a ROM below the OS, and the whole thing to be section-mapped.
However, if a different arrangement is used, the system will still work
(albeit slightly less efficiently).
The OS starts with a magic word - this aids probing and location of images.
Following that is a defined header format:
Word 0: Magic word ("OSIm" - &6D49534F)
Word 1: Flags (0)
Word 2: Image size
Word 3: Offset from base to entry table
Word 4: Number of entries available
The HAL itself may have whatever header is required to start the system. For
example on ARM7500 16->32 bit switch code is required, and on the 9500 parts
a special ROM header and checksum must be present. Instead of a header,
a pointer to the HAL descriptor is passed to the OS in the OS_Start call:
Word 0: Flags
bit 0 => uncachable workspace (32K) required
bits 1-31 reserved
Word 1: Offset from descriptor to start of HAL (will be <= 0)
Word 2: HAL size
Word 3: Offset from descriptor to entry table
Word 4: Number of entries available
Word 5: Static workspace required
Each of the HAL and the OS must be contiguous within physical memory.
RISC OS entry points from HAL init
==================================
Entry 0:
void RISCOS_InitARM(unsigned int flags)
flags: reserved - sbz
On entry:
SVC mode
MMU and caches off
IRQs and FIQs disabled
No RAM or stack used
On exit:
Instruction cache may be on
Usage:
This routine must be called once very early on in the HAL start-up, to accelerate the
CPU for the rest of HAL initialisation. Typically, it will just enable the instruction
cache (if possible on the ARM in use), and ensure that the processor is in 32-bit
configuration and mode.
Some architecture 4 (and later) ARMs have bits in the control register that affect
the hardware layer - eg the iA and nF bits in the ARM920T. These are the HAL's
responsibility - the OS will not touch them. Conversely, the HAL should not touch the
cache, MMU and core configuration bits (currently bits 0-14).
On architecture 3, the control register is write only - the OS will set bits 11-31 to
zero.
Likewise, such things as the StrongARM 110's register 15 (Test, Clock and Idle Control)
are the HAL's responsibility. The OS does not know about the configuration of the
system, so cannot program such registers.
This entry may not be called after RISCOS_Start.
Entry 1:
void *RISCOS_AddRAM(unsigned int flags, void *start, void *end, uintptr_t sigbits, void *ref)
flags
bit 0: video memory (only first contiguous range will be used)
bits 8-11: speed indicator (arbitrary, higher => faster)
other bits reserved (SBZ)
start
start address of RAM (inclusive) (no alignment requirements)
end
end address of RAM (exclusive) (no alignment requirements, but must be >= start)
sigbits
significant address bit mask (1 => this bit of addr decoded, 0 => this bit ignored)
ref
reference handle (NULL for first call)
Returns ref for next call
On entry:
SVC32 mode
MMU and data cache off
IRQs and FIQs disabled
Other notes:
This entry point must be the first call from the HAL to RISC OS following a hardware
reset. It may be called as many times as necessary to give all enumerate RAM that
is available for general purpose use. It should only be called to declare video
memory if the video memory may be used as normal RAM when in small video modes.
To permit software resets:
The HAL must be non-destructive of any declared RAM outside the first 4K of the first
block.
The stack pointer should be initialised 4K into the first block, or in some non-
declared RAM.
Must present memory in a fixed order on any given system.
Current limitations:
The first block must be at least 256K and 16K aligned. (Yuck)
Block coalescing only works well if RAM banks are added in ascending address order.
RISC OS will use RAM at the start of the first block as initial workspace. Max usage
is 16 bytes per block + 32 (currently 8 per block + 4). This limits the number of
discontiguous blocks (although RISC OS will concatanate contiguous blocks where
possible).
This call must not be made after RISCOS_Start.
Entry 2:
void RISCOS_Start(unsigned int flags, int *riscos_header, int *hal_entry_table, void *ref)
flags
bit 0: power on reset
bit 1: CMOS reset inhibited (eg protection link on Risc PC)
bit 2: perform a CMOS reset (if bit 1 clear and bit 0 set - eg front panel
button held down on an NC)
On entry:
SVC32 mode
MMU and data cache off
IRQs and FIQs disabled
Usage:
This routine must be called after all calls to RISCOS_AddRAM have been completed.
It does not return. Future calls back to the HAL are via the HAL entry table, after
the MMU has been enabled.
Entry 3:
void *RISCOS_MapInIO(unsigned int flags, void *phys, unsigned int size)
flags: bit 2 => make memory bufferable
phys: physical address to map in
size: number of bytes of memory to map in
Usage:
This routine is used to map in IO memory for the HAL's usage. Normally it would
only be called during HAL_Init(). Once mapped in the IO space cannot be released.
It returns the resultant virtual address corresponding to phys, or 0 for failure.
Failure can only occur if no RAM is available for page tables, or if the virtual
address space is exhausted.
void *RISCOS_AccessPhysicalAddress(unsigned int flags, void *phys, void **oldp)
flags: bit 2 => make memory bufferable
other bits must be zero
phys: physical address to access
oldp: pointer to location to store old state (or NULL)
On entry:
Privileged mode
MMU on
FIQs on
Re-entrant
On exit:
Returns logical address corresponding to phys
Usage:
Arranges for the physical address phys to be mapped in to logical memory.
In fact, the whole megabyte containing "phys" is mapped in (ie if phys =
&12345678, then &12300000 to &123FFFFF become available). The memory is
supervisor access only, non-cacheable, non-bufferable by default, and will
remain available until the next call to RISCOS_Release/AccessPhysicalAddress
(although interrupt routines or subroutines may temporarily map in something
else).
When finished, the user should call RISCOS_ReleasePhysicalAddress.
void RISCOS_ReleasePhysicalAddress(void *old)
old: state returned from a previous call to RISCOS_AccessPhysicalAddress
On entry:
MMU on
FIQs on
Re-entrant
Usage:
Call with the a value output from a previous RISCOS_ReleasePhysicalAddress.
Example:
void *old;
unsigned int *addr = (unsigned int *) 0x80005000;
unsigned int *addr2 = (unsigned int *) 0x90005000;
addr = (unsigned int *) RISCOS_AccessPhysicalAddress(addr, &old);
addr[0] = 3; addr[1] = 5;
addr2 = (unsigned int *) RISCOS_AccessPhysicalAddress(addr2, NULL);
*addr2 = 7;
RISCOS_ReleasePhysicalAddress(old);
HAL entries
===========
void HAL_Start(int *riscos_header)
This diff is collapsed.
Arrange correct ROM image
POST
Initialise memory system
ROM timings, width
Reset screen
Disable interrupts
Start timers
Size memory
Set up table describing memory layout
Set up video DMA
Time CPU
CONT / CONT_Break
InitMEMC (in: r1 = 0 -> Reset, 1 -> Break)
Check for 7500 vs IOMD
Program CPU, MEM and IO clocks
Set ROM timings
Set ROM width
Set up VRAM refresh
Set up peripheral timings
Set up sound format
Ensure MMU off and caches
Set up VIDC
Disable interrupts in IOC
Start timer 0
MemSize (out: r0 = page size, r1 = memory size, r2 = MEMC CR)
Set up RAM width
Find memory - create a table of (addr,len) pairs (in first memory found)
Find VRAM - if none take from first block
Start filling in page zero (in first block)
Set up video DMA registers
Allocate L1PT, and some L2PT, and soft CAM
Turn on MMU and caches
TimeCPU (out: r0 = peak RAM speed in kHz)
Put in extra pages: cursor, system heap
Start keyboard scanning
If POR or FX 200
Clear memory
Check processor type
Fill in processor vectors
Read CMOS
Fill in SWI dispatch table
Wait for keyboard (up to two seconds)
If (POR AND R/T/Del/Copy)
Reset CMOS
Goto Hard Reset
IF (POR or CannotReset or SysHeapCorrupt or CAM map nonsense or Ctrl pressed)
Clear the CAM
Set it up
InitDynamicAreas
Create system dynamic areas
InitVectors
Clear SWI hash table
Clear POR bit
Else
Do the soft reset stuff
Re-initialise kernel
If (hard reset)
Init variables
Initialise modules
PostInit
Set mode
Print "RISC OS"
Service_Reset
Shut all files
Beep if hard reset
If numpad key down
reconfigure monitor, change mode
print "monitor type reconfigured"
Check shift
Do boot
Else check *
Else enter language
\ No newline at end of file
Initialisation
==============
HAL_Init(unsigned int *riscos_header)
Will be called after the MMU is turned on, before any other entries points.
The HAL workspace will be filled with zeroes.
Interrupts
==========
The HAL must provide the ability to identify, prioritise and mask IRQs, and the ability
to mask FIQs. RISC OS supplies the ARM's processor vectors, and on an IRQ calls the HAL
to request the identity of the highest priority interrupt.
IRQ and FIQ device numbers are arbitrary, varying from system to system. They should be
arranged to allow quick mappings to and from hardware registers, and should ideally
be packed, starting at 0.
HAL_IRQEnable
HAL_IRQDisable
HAL_IRQClear
HAL_IRQSource (get highest priority asserted IRQ)
HAL_IRQDisableAll
HAL_FIQEnable
HAL_FIQDisable
HAL_FIQClear
HAL_FIQDisableAll
Interrupt specifications are generally described by a 3-word structure.
The 3 words correspond directly to the contents of registers R0,R3 and R4
on entry to OS_ClaimDeviceVector.
typedef struct irq_descriptor
{
int device;
union {
struct {
unsigned char *addr;
int maskandpolarity;
} bit;
struct {
int (*forme)(void *handle);
void *handle;
} func;
} sub;
} irq_descriptor;
OS_ClaimDeviceVector changes:
R3 and R4 must always be supplied. Set R3=R4=0 to claim "all" of an interrupt.
Bit 31 of the device number indicates that a routine is being supplied instead
of an address and a mask.
When supplying a bit mask, your handler is called if
([addr] AND maskandpolarity) EOR (maskandpolarity >> 8)
is nonzero. This is a RISC OS 3.8+ backwards-compatible extension to the original
check:
[addr] AND maskandpolarity
When supplying a routine, your handler is called if
forme(handle)
returns nonzero.
if
Timers
======
The HAL must supply at least one timer capable of generating periodic
interrupts. Each timer should generate a separate logical interrupt, and the
interrupt must be latched. The timers must either be variable rate (period is
a multiple of a basic granularity), or be fixed rate (period = 1*granularity).
Optionally, the timer should be capable of reporting the time until the
next interrupt, in units of the granularity.
int HAL_Timers(void)
Returns number of timers. Timers are numbered from 0 upwards. Timer 0
must exist.
int HAL_TimerDevice(int timer)
Returns device number of timer n
unsigned int HAL_TimerGranularity(int timer)
Returns basic granularity of timer n in ticks per second.
unsigned int HAL_TimerMaxPeriod(int timer)
Returns maximum period of the timer, in units of Granularity. Will be 1
for a fixed rate timer.
void HAL_TimerSetPeriod(int timer, unsigned int period)
Sets period of timer n. If period > 0, the timer will generate interrupts every
(period / granularity) seconds. If period = 0, the timer may be stopped.
This may not be possible on some hardware, so the corresponding interrupt
should be masked in addition to calling this function with period 0.
If period > maxperiod, behaviour is undefined.
unsigned int HAL_TimerPeriod(int timer)
Reads period of timer n. This should be the actual period in use by the
hardware, so if for example period 0 was requested and impossible, the
actual current period should be reported.
unsigned int HAL_TimerReadCountdown(int timer)
Returns the time until the next interrupt in units of granularity, rounded down.
If not available, 0 is returned.
Counter
=======
The HAL must supply a counter that varies rapidly, appropriate for use for
sub-millisecond timing. On many systems, this counter will form part of
timer 0 - as such it is not required to operate when timer 0 is not running.
On other systems, the periodic timers may have no readable latch, and a
separate unit will be required.
The counter should count down from (period-1) to 0 continuously.
unsigned int HAL_CounterRate(void)
Returns the rate of the counter in ticks per second. Typically will
equal HAL_TimerGranularity(0).
unsigned int HAL_CounterPeriod(void)
Returns the period of the counter, in ticks. Typically will equal
HAL_TimerPeriod(0).
unsigned int HAL_CounterRead(void)
Reads the current counter value. Typically will equal
HAL_TimerReadCountdown(0).
unsigned void HAL_CounterDelay(unsigned int microseconds)
Delay for at least the specified number of microseconds.
Non-volatile memory
===================
The HAL should provide at least 240 bytes of non-volatile memory. If no
non-volatile memory is available, the HAL may provide fake NVRAM contents
suitable for RISC OS - however, it is preferable that the HAL just state that
NVRAM is not available, and RISC OS will act as though a CMOS reset has been
performed every reset.
NVRAM is typically implemented as an IIC device, so the calls are permitted
to be slow, and to enable interrupts. The HAL is not expected to cache
contents.
If the HAL has no particular knowledge of NVMemory, then it may
just say that "NVMemory is on IIC", and the OS will probe for CMOS/EEPROM
devices on the IIC bus.
unsigned int HAL_NVMemoryType(void)
Returns a flags word describing the NVMemory
bits 0-7: 0 => no NVMemory available
1 => NVMemory may be available on the IIC bus
2 => NVMemory is available on the IIC bus, and the
device characteristics are known
3 => the HAL provides NVMemory access calls.
bit 8: NVMemory has a protected region at the end
bit 9: Protected region is software deprotectable
bit 10: Memory locations 0-15 are readable
bit 11: Memory locations 0-15 are writeable
If bits 0-7 are 0 or 1 no other NVMemory calls need be available,
and bits 8-31 should be zero.
If bits 0-7 are 2, Size, ProtectedSize, Protection and IICAddress calls must
be available.
If bits 0-7 are 3, all calls except IICAddress must be available.
unsigned int HAL_NVMemorySize(void)
Returns the number of bytes of non-volatile memory available. Bytes 0-15
should be included in the count, so for example a Philips PCF8583
CMOS/RTC device (as used in the Archimedes and Risc PC) would be described
as a 256-byte device, with locations 0-15 not readable. More complex
arrangements would have to be abstracted out by the HAL providing its own
NVMemory access calls.
This is to suit the current RISC OS Kernel, which does not use bytes 0-15.
unsigned int HAL_NVMemoryProtectedSize(void)
Returns the number of bytes of NVMemory that are protected. These should
be at the top of the address space. The OS will not attempt to write
to those locations without first requesting deprotection (if available).
Returns 0 if bit 8 of the flags is clear.
void HAL_NVMemoryProtection(bool)
Enables (if true) or disables if (false) the protection of the software
protectable region. Does nothing if bits 8 and 9 not both set.
unsigned int HAL_NVMemoryIICAddress(void)
Returns a word describing the addressing scheme of the NVRAM.
bits 0-7: IIC address
This will always be on bus zero.
int HAL_NVMemoryRead(unsigned int addr, void *buffer, unsigned int n)
Reads n bytes of memory from address addr onwards into the buffer supplied.
Returns the number of bytes successfully read. Under all normal circumstances
the return value will be n - if it is not, a hardware failure is implied.
Behaviour is undefined if the address range specified is outside the NVMemory,
or inside bytes 0-15, if declared unavailable.
int HAL_NVMemoryWrite(unsigned int addr, void *buffer, unsigned int n)
Write n bytes of memory into address addr onwards from the buffer supplied.
Returns the number of bytes successfully written. Under all normal circumstances
the return value will be n - if it is not, a hardware failure is implied.
Behaviour is undefined if the address range specified is outside the NVMemory.
Writes inside the a protected region should be ignored.
I²C bus
=======
Many hardware designs have an I²C bus. Often, it is used only to place non-
volatile memory on, but in other systems TV tuners, TV modulators,
microcontrollers, and arbitrary expansion cards may be fitted.
Low-level and high level APIs are defined. An arbitrary number of buses are
supported, and each can be controlled by either the low or high level API.
The OS should normally only use one fixed API on each bus - mixing APIs may
not have good results.
The low-level API requires the OS to control the two lines of the bus
directly. The high-level API currently covers version 2.1 of the I²C
protocol, and allows high-level transactions to be performed.
It is expected that a HAL will always provide the low-level API on each
bus, where possible in hardware. Using this, the OS can provide Fast mode
single or multi-master operation. The HAL may wish to provide the high-level API
where a dedicated I²C port with hardware assistance is available; this will
further permit High-speed and slave operation.
As it is possible that some HAL APIs (eg NVMemory), although abstracted at
this API layer, are still actually an I²C device, a matching set of high-level
I²C calls are provided in the OS. These give the HAL access to the OS I²C engine,
which will make low-level HAL calls. This saves the HAL from implementing the
full I²C protocol. To illustrate this diagramatically:
+----------+ NVMem_Read +------------+ NVMemoryRead +------------+
| | ---------> | | ------------> | |
| App | | OS | IICTransmit | HAL |
| | | | <------------ | |
| | | | IICSetLines | |
| | | | ------------> | |
+----------+ +------------+ +------------+
int HAL_IICBuses(void)
Returns the number of IIC buses on the system.
unsigned int HAL_IICType(int bus)
Returns a flag word describing the specified IIC bus.
bit 0: Bus supplies the low-level API
bit 1: Bus supplies the high-level API
bit 2: High-level API supports multi-master operation
bit 3: High-level API supports slave operation
bit 4: High-level API supports background operation
bit 16: Bus supports Fast (400kbps) operation
bit 17: Bus supports High-speed (3.4Mbps) operation
bits 20-31: Version number of I²C supported by high-level API, * 100.
Low level API
-------------
The low-level calls should be instantaneous. Interrupt status may not be altered.
The following structure is used:
typedef struct { int SDA, SCL } IICLines;
Note the "__value_in_regs" keyword, which signifies that the binary ABI expects
SDA and SCL to be returned in registers a1 and a2.
__value_in_regs IICLines HAL_IICSetLines(int bus, IICLines lines)
Sets the SDA and SCL lines on the specified bus. A 0 value represents
logic LOW, 1 logic HIGH. The function then reads back and returns
the values present on the bus, to permit arbitration.
__value_in_regs IICLines HAL_IICReadLines(int bus);
Reads the state of the IIC lines on the specified bus, without changing
their state.
High level API
--------------
The high-level interface process a single transfer at a time (from the
initial START to the STOP). It is designed to support background operations.
irq_descriptor HAL_IICDevice(int bus);
Returns the interrupt specification for the bus. This is not meaningful
if bit 4 of the flags word above is not set. The OS will claim the interrupt
and call HAL_IICMonitorTransfer() each time it occurs.
#define IICSTATUS_COMPLETED 0
#define IICSTATUS_INPROGRESS 1 /* transfer proceeding in background */
#define IICSTATUS_NOACK 2 /* slave failed to acknowledge */
#define IICSTATUS_BUSY 3 /* IIC system busy (call back later) */
#define IICSTATUS_SLAVE 4 /* reserved for slave operations */
#define IICSTATUS_ERROR 5 /* other error prevented completion */
typedef struct iic_transfer
{
unsigned addr:8;
unsigned :22;
unsigned checksumonly:1;
unsigned nostart:1;
union
{ unsigned checksum;
void *data;
} d;
unsigned len;
} iic_transfer;
int HAL_IICTransfer(int bus, unsigned n, iic_transfer transfer[static n]);
Initiates an IIC transfer. The transfer shall progress in the background
if bit 4 is set, in which case the normal return should be IICSTATUS_INPROGRESS.
The OS will call HAL_IICMonitorTransfer each time an interrupt occurs - this
will allow the HAL to progress through the transfer if it's not totally automatic.
If the transfer happens in the foreground, return values are as for
IICMonitorTransfer (see below).
If an IIC transfer is currently in progress, the call may return BUSY and the
caller should retry later - although if background transfers are supported it may
queue the transfer and return INPROGRESS. If another master is driving the bus,
it should silently wait until the bus is free (in the background or foreground as
appropriate). If we lose arbitration, the transfer should be retried when the bus
becomes free.
transfer[] is an array of n transfer descriptors. Each descriptor describes part
of the transfer. The direction of the subtransfer is determined by the least
significant bit of addr. If nostart is 0, a START is first transmitted followed
by addr, otherwise the data flow continues where the previous subtransfer
left off. nostart must be 0 for the first subtransfer.
For writes, len bytes of data are read from "data" and transmitted. For reads,
len bytes are received and written to "data", unless "checksumonly" is 1, in which
case the len received bytes are summed and the (32-bit) sum stored in checksum.
If background transfers are in use, the transfer[] array and the data blocks must
remain available in unpaged memory for the duration of the transfer.
IICTransfer is re-entrant, but may return BUSY if re-entered (see above).
int HAL_IICMonitorTransfer(int bus);
Will be called on every interrupt, and should return the status of the transfer
currently in progress. If no transfer is in progress, the call should return
COMPLETED.
If the transfer is still in progress, INPROGRESS is returned.
If the slave failed to acknowledge (either the address or any later transmitted
byte), NOACK is returned.
If we have been addressed as a slave, the call returns SLAVE. More details to
be confirmed.
BUSY is not a valid response.
This will only be called in response to an IIC interrupt, with interrupts disabled.
The interrupt shall be cleared by this call.
Unknown return codes will be ignored.
Machine ID
==========
unsigned int64 HAL_MachineID(void)
Returns a 64-bit unique machine identifier,this may later be used to
form the ethernet MAC address but otherwise has no great significance on non
networked machines.
The top 8 bits are a CRC,based on the same algorithm the original DS2401
used - if the CRC fails zero will be substituted
ControllerAddress
=================
void *HAL_ControllerAddress(unsigned controller)
Asks the HAL where various controllers might or might not be.
Podule manager uses this information to determine at run time whether or not
to bother doing anything.
Returns r0=logical address of the chosen controller,or zero
0 = EASI card access speed control
1 = EASI space(s)
2 = VIDC1
3 = VIDC20
4 = S space base (IOMD,podules,NICs,blah blah)
5 = Extension ROM(s)
Matrix Keyboard
===============
Many devices provide a matrix keyboard interface. The following calls
provide access to it. Interrupt driven operation, or high-level calls will be
defined later.
int HAL_MatrixColumns(void)
Returns the number of columns available via the matrix interface.
Columns are numbered from 0 to <num columns>-1.
unsigned int HAL_MatrixScan(int column).
Returns a bitfield describing which rows are active on the specified column.
Any timing issues, or the driving of the matrix between calls are left to
the HAL.
Touchscreen
===========
PDA-type devices usually have a touchscreen as their primary pointing device.
This API provides a simple interface to a touchscreen. The calls are described
in terms of a resistive touchscreen, but other technologies should be mappable
onto it. Interrupt operation is yet to be defined.
unsigned int HAL_TouchscreenType(void)
Returns a flags word indicating the type of touchscreen.
bits 0-7: Touchscreen type 0=>none, 1=>resistive
bit 8: Interrupt operation supported
bit 9: Calibration not required
bits 10-15: Reserved
bits 16-21: Bits of precision available
bits 22-31: Reserved
"Calibration not required" indicates that the raw values returned map linearly
onto the screen display area to a usable accuracy as follows:
X,Y (00000000,00000000) = bottom left of display area
X,Y (FFFFFFFF,FFFFFFFF) = top right of display area
Pres 00000000-1FFFFFFF = no touch
Pres 20000000-3FFFFFFF = light touch
Pres 3FFFFFFF-7FFFFFFF = touch
Pres 80000000-FFFFFFFF = firm touch
unsigned int HAL_TouchscreenMeasure(int meas)
Performs a touchscreen measurement. Measurements are:
0 = X position
1 = Y position
2 = pressure
3 = X resistance
4 = Y resistance
"X" and "Y" need not actually be X and Y - rotation can be dealt with by
calibration.
All values are returned as unsigned 32-bit values in the range &00000000-&FFFFFFFF.
If using, for example, a 10-bit DAC, the 10-bit value read should be placed at the
top of the returned word. Ideally, the 10 bits should be replicated in lower bits
(ABCDEFGH IJABCDEF GHIJABCD EFGHIJAB) to ensure the returned values fully span
the 32-bit range.
Resistance measurements can be used to compensate for large pressed areas causing
shorts - subtract the instantaneous resistance from the instantaneous precision.
(I think).
Serial ports
============
The RS232 serial UART is a fundamental peripheral on most current hardware. All
Overview
========
The HAL introduces the new concept of a "device". A device is a logical
representation of a component of hardware. Each active devices is uniquely
identified by a constant pointer to a device descriptor. The descriptor is
a structure which contains information about the device and a series of entry
points to perform usually simple operations on the device. Devices can be
provided by the Bootloader, or by RISC OS modules.
Devices provided outside the Bootloader are, in principle, hot swappable,
although it is up to device drivers using it whether they can support this.
Throughout this document, device descriptors are described in terms of C,
although the scheme maps naturally to assembler or C++. All device calls use
the base ATPCS calling standard (R0-R3 arguments/return values, R4-R11
preserved, R12 corrupted), to permit straightforward use from C or assembler.
From C:
XXXDevice->Activate(XXXDevice);
A simple call to a activate a device from assembler might look like:
LDR R0, XXXDevice
MOV LR, PC
LDR PC, [R0, #DevEntry_Activate] ; R0-R3,R12 corrupted
If an assembler device driver module is using a lot of device calls, it
might be preferable to move the workspace pointer from the traditional R12
to R11.
The device descriptor
=====================
The device descriptor starts with a fixed format header, as described
below. Following this header are more function pointers providing device-specific
calls.
struct device
{
uint16_t type;
uint16_t id;
uint32_t location;
uint32_t version;
const char *description;
void *address;
uint32_t reserved[3];
bool (*Activate)(struct device *);
void (*Deactivate)(struct device *);
void (*Reset)(struct device *);
int32_t (*Sleep)(struct device *, int32_t state);
int32_t devicenumber;
bool (*TestIRQ)(struct device *);
uint32_t reserved[2];
};
struct serial
{
struct device dev;
uint8_t (*ReadByte)(struct serial *);
void (*WriteByte)(struct serial *, uint8_t c);
// private data
}
Hence, the first device specific function pointer is offset 32 bytes from the
device pointer.
Type
----
The type word describes what the device is.
Bits 15-8: Top level type (eg video, sound, system peripheral, comms port)
Defined values are: 1 = video
2 = sound
3 = system peripheral
4 = comms port
Bits 7-0: Second level type (eg VDU display, 16-bit PCM sound output,
interrupt controller, UART). Allocated independently within each top
level type.
This half-word, together with the version number, indicate which device specific calls
are available.
ID
--
16-bit product code - a unique identifier for this particular device.
Location
--------
The location describes the location of the device in terms of the bus architecture
of the computer. Again, it is grouped by bytes.
Bits 31-28: Bus type
0 => processor (0 = core, 1 = coprocessor)
1 => main system bus (0 = AHB, 1 = ASB, 2 = PXBus)
2 => peripheral bus (0 = APB)
3 => expansion bus (0 = Acorn Expansion Card, 1 = ISA, 2 = PCI)
4 => serial bus (0 = AC-Link)
Bits 27-24: Bus sub-type (see above)
Bits 23-16: Bus number
Bits 15-8: Card number (PCI, expansion card etc) / chip select number
Bits 7-0: Unit number
Version
-------
The version describes the version of the device API implemented. It consists of a
major version number (bits 31-16) and a minor version number (bits 15-0). A change in
major version number indicates an incompatible change of API. An increase in the
minor version number indicates backwards-compatible extensions to the API (eg
extra functions).
Description
-----------
A textual description of the device. This should be English, human-readable and
Basic Latin (characters &20-&7E). Descriptors along the lines of those output by
*Podules are expected. For example:
National Semiconductor 16C550 UART
Philips ISP1160 USB host controller
Acorn IOMD21 PS/2 keyboard
Acorn VIDC20
Intel SA-1110 DMA controller
Address
-------
This field may either be 0, or may be a pointer to the base address of the
memory-mapped registers of the device in question. Drivers should not
normally use this field to directly poke the device. If they do, they must be
absolutely certain that there is no other way to achieve the effect, and that
the device type word is known to them. What exactly the address points to
depends on the exact device in question.
Activate
--------
A device driver must call the Activate entry point before using a device. A
success/failure indication is returned: 1 indicates successful activation, 0 indicates
unsuccessful. Devices may ignore activate/deactivate calls, count them, or
may alternatively provide full locking to allow only one activation. Typically this
would be called by in a driver's module initialisation routine. Alternatively, it might
be called just before a DeviceFS device is opened for access.
Deactivate
----------
A device driver must call the Deactivate entry point when it has finished
using a device.
Reset
-----
The Kernel will call the Reset entry point of every device on the system
before performing a software reset (eg OS_Reset or Ctrl-Break), after it has
issued Service_PreReset. All devices must enter a quiescent state.
Sleep
-----
This call reads or sets a device's power-down state. If the second parameter is -1,
then the current state is returned; otherwise the second parameter must be a value in
the range 0-255 giving sleepiness (0 = full power, 255 = off) and the old sleepiness
is returned. Note that the value returned does not have to match the last value
programmed: for example, if a device cannot power down, it will always return 0.
DeviceNumber
------------
If this is -1, then the device has no associated interrupt. Otherwise, bits 0-30 give
the device number and bit 31 flags that the device vector is shared, ie this is the R0
that should be passed to OS_ClaimDeviceVector. If bit 31 is set then the TestIRQ
routine must be used to determine whether the vector should be claimed or passed on.
TestIRQ
-------
Returns 0 if the device is not interrupting, or 1 if the device is interrupting.
When DeviceNumber is -1, this must be a null pointer.
Creation and removal of devices
===============================
Devices are declared by calling the HAL->OS call OS_AddDevice or SWI OS_Hardware 2.
SWI OS_Hardware 2 (SWI &7A)
---------------------------
On entry: R0 -> device descriptor
R8 = 2
On exit: All registers preserved
void OS_AddDevice(uint32_t flags, struct device *d);
void HAL_InitDevices(uint32_t flags);
Declare a new device to the system. OS_AddDevice must not be called until
HAL_InitDevices is called.
Devices are removed by calling OS_Hardware 3. There is no HAL->OS equivalent.
SWI OS_Hardware 3 (SWI &7A)
---------------------------
On entry: R0 -> device descriptor
R8 = 3
On exit: All registers preserved
The Kernel tracks all present devices, issuing service calls as devices come and go, and
providing a call to enumerate devices of a particular type.
SWI OS_Hardware 4 (SWI &7A)
---------------------------
On entry: R0 bits 0-15 = type to match
bits 16-31 = maximum major version number to match
R1 = 0 to start an enumeration, else preserved from last call
R8 = 4
On exit: R1 = -1 if there are no (more) devices of this type
R2 -> device descriptor (undefined if R1 = -1)
Other registers preserved
Service_Hardware (Service Call &D9)
------------------------------------
On entry: R0 bits 0-7 = sub-reason code, bits 8-31 flags (undefined, ignore)
R1 = reason code (&D9)
R2 -> device
On exit: Depends on sub-reason code
Sub-reason code 0: Device added
On exit: All registers must be preserved
Sub-reason code 1: Device being removed
On exit: R1 = 0 => we object to device being removed
R0 -> error block
other registers must be preserved
else all registers must be preserved
New ClaimDeviceVector behaviour
R3 and R4 are meaningless, except for podule IRQ and FIQ-as-IRQ, which
keep the same behaviour as previously. (This is currently done in the Kernel,
but should be deferred to the Podule Manager).
For other claimants, if bit 31 of the R0 is set in the OS_ClaimDeviceVector
call, the interrupt will be passed on to earlier claimants unless your
routine claims the vector. This behaviour is then the same as IrqV claimants.
It is up to you to determine whether your device has interrupted. If it has,
you should service it, and claim the vector (by pulling the return address
off the stack). If not, pass the vector along. If no handlers claim the
vector, then the OS knows that there is no device driver able to handle the
interrupt being asserted, so it will mask off that line.
If you do not claim the vector, you must preserve R0 and R3. R1,R2,R12 may
be corrupted.
It is critical that your claiming or not is purely on the basis of whether
your card is interrupting, and is accurate. Not claiming when your card is
interrupting, or claiming when it isn't can both cause incorrect system
behaviour.
Entry into RISC OS:
POST check (if any) complete
CPU & memory systems at full speed
MMU off, SVC32 mode, IRQs+FIQs disabled
All interrupts masked
I/O timings set up
DRAM refresh running
Video system stabilised (off?)
Information passed:
Table of (addr,len) pairs of RAM
Address + amount of VRAM
Memory speed?
CPU speed?
Entry point to HAL
Questions:
How to clear RAM without logical copy? Do we NEED a logical copy?
Yes we do - but logical copy will NOT be contiguous.
Physical Size Logical - offset
F0000000 01000000 80000000 70000000
F1000000 01000000 81000000 70000000
60000000 00001000 82000000 22000000 - fast SRAM - how to signal?
02000000 00200000 80000000 7FE00000
10000000 01700000 80200000 70200000
11B00000 02500000 81900000 6FE00000
14000000 04000000 83E00000 6FE00000
02000000 00200000 82000000 80000000
10000000 01700000 90000000 80000000
11B00000 02500000 91B00000 80000000
14000000 04000000 94000000 80000000
Memory Map
00000000 16K Kernel workspace
00004000 16K Scratch space
00008000 Mem-32K Application memory
0xxxxxxx 3840M-Mem Dynamic areas
F0000000 160M I/O space (growing downwards if necessary)
FA000000 1M HAL workspace
FA100000 8K IRQ stack
FA200000 32K SVC stack
FA300000 8K ABT stack
FA400000 8K UND stack
FAE00000 1M Reserved for physical memory accesses
FAF00000 256k reserved for DCache cleaner address space (eg. StrongARM)
FAF40000 64k kernel buffers (for long command lines, size defined by KbuffsMaxSize)
FAFE8000 32K HAL workspace
FAFF0000 32K "Cursor/System/Sound" block (probably becoming just "System")
FAFF8000 32K "Nowhere"
FB000000 4M L2PT
FB400000 16K L1PT
FB404000 4M-16K System heap
FB800000 8M Soft CAM
FC000000 64M ROM
26-bit system:
00000000 16K Kernel workspace
00004000 16K Scratch space
00008000 28M-32K Application memory
01C00000 32K SVC stack
01C08000 2M-32K System heap
01F00000 32K Cursor/System/Sound
01F08000 32K "Nowhere"
02100000 15M Module area
03000000 8M I/O space
03800000 8M ROM
04000000 2G-64M Dynamic areas
80000000 512M Logical copy of physical space
A0000000 1280M Dynamic areas
F0000000 224M I/O space (growing downwards if necessary)
FE000000 1M HAL workspace
FE100000 8K ABT stack
FE200000 8K UND stack
FF000000 4M L2PT + embedded L1PT
FF800000 8M Soft CAM
"Soft" resets
Entry through HAL - full HAL initialisation.
HAL must not destroy (much) memory.
RISC OS detects intact memory and makes the reset "soft".
RAM page tables reconstructed from CAM.
Other page tables reconstructed through HAL.
"Break"
RISC OS calls HAL to shut down, then shuts off MMU, and calls HAL_Reset code.
HAL then re-enters RISC OS in the usual fashion.
SWI OS_Hardware (SWI &7A)
-------------------------
On entry: R0-R7 parameters
R8 = reason code (bits 0-7) and flags (bits 8-31)
R9 = hardware call number
On exit: depends on flags
This SWI provides access to the hardware layer. Conceptually, it is
similar to accessing the hardware registers directly in earlier versions
of RISC OS - whereever possible OS routines should be used in preference.
This call is primarily designed for the use of device drivers - for example
the PS2Driver module makes PS2 hardware calls using this interface.
Making hardware calls to devices normally managed by the Kernel is liable to
cause the same problems as poking the hardware. However, making hardware
calls is of course preferable to actually accessing the hardware directly.
Use this interface with caution.
SWI OS_Hardware 0 (SWI &7A)
---------------------------
On entry: R0-R7 parameters for hardware routine
R8 = 0
R9 = hardware call number
On exit: R0-R3 updated by call
R4-R9 preserved.
This SWI calls a HAL routine. HAL routines internally are ATPCS, so R0-R3 are
passed in as a1-a4, and R4-R7 are pushed on to the stack. The a1-a4 on exit
from the routine are passed back in R0-R3.
If the HAL routine is not available, an error is returned. Actual HAL
routines do not return RISC OS errors - any possible failure will be
indicated in a call-specific manner.
SWI OS_Hardware 1 (SWI &7A)
---------------------------
On entry: R8 = 1
R9 = hardware call number
On exit: R0 = routine address
R1 = static base value for routine
This call looks up the address of a HAL routine. If it does not exist, an
error is returned. Otherwise, the address of the routine is returned in R0.
Calls made to the routine should be in a privileged mode, with R9 (sb) set to
the static base value returned by this SWI. Refer to the HAL documentation
for more details of calling conditions.
G F D B 9 7 5 3 1 Gnd?
G J E C A 8 6 4 2 0
Aux
X
X
G 1 3 5 7 9 B D F x
0 2 4 6 8 A C E J x
FIQ
aux
14 pfiq
12 preq
15 pirq
13 rclk
Cont
3 Fiq
14 abe
9 nbw
15 dBE
1 wait
4 irq
8 nrw
2 brst
10 lock
clk mclk
0 state? (lk1)
7 seq? lk2
12 nopc? lk3
11 ntrans lk4
6 mreq
5 n/c
13 n/c
CLK to AUX link connects MCLK to AUX clock
Features
Bit 0: FIFOs available
int ReceiveByte(int port, int *status)
Returns the next byte from the FIFO (if enabled) or the holding register.
If status is non-NULL, the line status associated with the byte is
read (see LineStatus). The return value is only meaningful if a
received byte is available (bit 0 of *status will be set).
void TransmitByte(int port, int byte)
int LineStatus(int port)
Bit 0: Receiver Data Ready
Bit 1: Overrun Error
Bit 2: Parity Error
Bit 3: Framing Error
Bit 4: Break Error
Bit 5: Transmitter Holding Register Empty
Bit 6: Transmitter Empty (including FIFO)
Bit 7: FIFO contains a Parity, Framing or Break error
Parity, Framing and Break errors are associated with each byte received.
Whether the values reported here are associated with the last byte
read using ReceiveByte or with the next byte to be read is undefined.
You should request the status using ReceiveByte to ensure accurate
identification of bytes with errors.
Error bits are cleared whenever status is read, using either LineStatus
or ReceiveByte with status non-NULL.
int InterruptEnable(int port, int eor, int mask)
Enables interrupts. Bits are:
Bit 0: Received Data Available (and Character Timeout)
Bit 1: Transmitter Holding Register Empty
Bit 2: Received Line Status
Bit 3: Modem Status
Returns previous state.
int Rate(int port, int baud16)
Sets the rate, in units of 1/16 of a baud. Returns the previous rate.
Use -1 to read.
int Format(int port, int format)
Bits 0-1: Bits per word 0=>5, 1=>6, 2=>7, 3=>8
Bit 2: Stop length 0=>1, 1=>2 (1.5 if 5 bits)
Bit 3: Parity enabled
Bits 4-5: Parity: 0 => Odd (or disabled)
1 => Even
2 => Mark (parity bit = 1)
3 => Space (parity bit = 0)
Returns previous format. -1 to read.
void FIFOSize(int *rx, int *tx)
Returns the size of the RX and TX FIFOs. Either parameter may be NULL.
Note that the size of the TX FIFO is the total amount of data that can
be sent immediately when the Transmitter Holding Register Empty
status holds. (So an unusual UART that had a transmit threshold
should return total FIFO size minus threshold).
void FIFOClear(int port, int flags)
Clears the input FIFO (if bit 0 set) and the output FIFO (if bit 1 set).
int FIFOEnable(int port, int enable)
Enables or disables the RX and TX FIFOs: 0 => disable, 1 => enable
-1 => read status. Returns previous status.
int FIFOThreshold(int port, int threshold)
Sets the receive threshold level for the FIFO RX interrupt. Normally
available values are 1,4,8 and 14 bytes. Returns previous value.
-1 to read.
int InterruptID(int port)
Returns the highest priority interrupt currently asserted. In order
of priority:
3 => Receiver Line Status (Cleared by ReceiveByte/LineStatus)
2 => Received Data Available (Cleared by reading enough data)
6 => Character Timeout (received data waiting)
1 => Transmitter Holding Register Empty (Cleared by this call)
0 => Modem Status (Cleared by ModemStatus)
-1 => No Interrupt
The Modem Status interrupt occurs when the CTS, DSR or DCD inputs
change, or when RI goes from high to low (ie bits 0 to 3 of ModemStatus
are set).
int Break(int port, int enable)
Activates (1) or deactivates (0) a break condition. -1 to read,
returns previous state.
int ModemControl(int port, int eor, int mask)
Modifies the modem control outputs.
Bit 0: DTR
Bit 1: RTS
Note that these are logical outputs, although the physical pins may be inverted.
So 1 indicates a request to send.
int ModemStatus(int port)
Reads the modem status inputs.
Bit 0: CTS changed since last call
Bit 1: DSR changed since last call
Bit 2: RI changed from high to low since last call
Bit 3: DCD changed since last call
Bit 4: CTS
Bit 5: DSR
Bit 6: RI
Bit 7: DCD
Note that these are logical inputs, although the physical pins may be inverted.
So 1 indicates a Clear To Send condition.
int Device(int port)
File deleted
directory
130 fff 0 Mike_Stephens 2bf39672 31060a66 0
258 fff 0 Mike_Stephens 2bf395c1 31060a66 0
directory
130 fff 1 Mike_Stephens 2bf39672 31060a66 0
258 fff 1 Mike_Stephens 2bf395c1 31060a66 0
000 App space
01C System heap/svc stack
01E Soft CAM copy/und stack
01F Sound/pointer/random
020 RMA
02C L2PT and L1PT
030 I/O
038 ROM
040 Screen
060 Free pool
0E2 Sprites
164 Font cache
1E6 RAM disc
268 Another area 1
2EA Another area 2
36C Another area 3
3EE Another area 4
470 Another area 5
4F2 Another area 6
574 Another area 7
5F6 Another area 8
678 Another area 9
6FA Another area 10
77C Another area 11
7FE Nothing
800 Phys space copy
A00 Another area 12
A82 Another area 13
B04 Another area 14
B86 Another area 15
C08 Another area 16
C8A Another area 17
D0C Another area 18
D8E Another area 19
E10 Another area 20
E92 Another area 21
F14 Another area 22
F96 Nothing
000 App space
01C System heap/svc stack
01E Soft CAM copy/und stack
01F Sound/pointer/random
020 RMA
02C L2PT and L1PT
030 I/O
038 ROM
040 Screen
060 Free pool
162 Sprites
264 Font cache
366 RAM disc
468 Another area 1
56A Another area 2
66C Another area 3
76E Nothing
800 Phys space copy
A00 Another area 4
B02 Another area 5
C03 Another area 6
D04 Another area 7
E05 Another area 8
F06 Nothing
File deleted
directory
5thColumn 1000 0 Mike_Stephens 2e05be22 30fd2d55 0
MMPM fff 0 Mike_Stephens 2c53f7f8 31060a6b 0
ScreenMode fff 0 Mike_Stephens 2c4d19fc 31060a6b 0
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment