diff -r 578be2adaf3e -r 307f4279f433 Adaptation/GUID-E0DCBDCF-C056-53E5-A375-778327F848E4.dita --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adaptation/GUID-E0DCBDCF-C056-53E5-A375-778327F848E4.dita Fri Oct 15 14:32:18 2010 +0100 @@ -0,0 +1,408 @@ + + + + + +Asic Class TutorialProvides a work through tutorial that allows you to port +an Asic implementation to the template variant. +

This tutorial +describes how to implement the Asic class. This is a pure virtual +interface that is defined and called by the Kernel, but which must +be implemented by the ASSP/Variant. The tutorial assumes that the +ASSP/Variant is split into an ASSP layer and a Variant layer.

+

For a minimal port, it isn't necessary to provide implementations +for the entire Asic class to be able to test that +the kernel boots, provided that those functions that are not fully +implemented have a dummy function so that the code will build.

+

The Asic class is defined in..\e32\include\kernel\arm\assp.h. For reference, the definition is:

+class Asic + { +public: + // initialisation + virtual TMachineStartupType StartupReason()=0; + virtual void Init1()=0; + virtual void Init3()=0; + + // debug + virtual void DebugOutput(TUint aChar)=0; + + // power management + virtual void Idle()=0; + + // timing + virtual TInt MsTickPeriod()=0; + virtual TInt SystemTimeInSecondsFrom2000(TInt& aTime)=0; + virtual TInt SetSystemTimeInSecondsFrom2000(TInt aTime)=0; + virtual TUint32 NanoWaitCalibration()=0; + + // HAL + virtual TInt VariantHal(TInt aFunction, TAny* a1, TAny* a2)=0; + + // Machine configuration + virtual TPtr8 MachineConfiguration()=0; + }; +

Taking the template port as a concrete example, the ASSP layer +implementation of the Asic class is defined and +implemented by the TemplateAssp class, and the Variant +implemention is defined and implemented by the Template class.

+
Asic::Init1() +implementation

Entry conditions

    +
  • called in the +context of the initial (null) thread

  • +
  • interrupts are +disabled

  • +
  • there is no +kernel heap

  • +
  • memory management +functions are not available.

  • +

What the function should do

This is called during +stage 1 of kernel initialisation.

In this function, you need +to:

    +
  • initialise the +real time clock

  • +
  • initialise the +interrupt dispatcher before CPU interrupts are enabled.

  • +
  • set the threshold +values for cache maintenance. You can set separate values for:

      +
    • purging (invalidating) +a cache

    • +
    • cleaning a cache

    • +
    • flushing (i.e. +cleaning and invalidating) a cache.

    • +

    You use the Cache::SetThresholds() interface +to set these values.

    As an example of what the threshold values +mean, if you purge a memory region from cache, and the size of that +region is greater than the threshold value, then the entire cache +is purged. If the size of that region is less than or equal to to +the threshold value, then only the region is purged.

    The threshold +values are platform specific, and you need to choose your values based +on your own performance measurements. Symbian cannot make recommendations. +If you choose not to set your own values, Symbian platform supplies +a set of default values, which are set by Cache::Init1().

    Note that there is also a Cache::GetThresholds() interface that you may find useful.

  • +
  • set up the RAM +zones. For details, see the RAM Zone Tutorial.

  • +

Typically, you would also initialise any memory devices not +initialised by the bootstrap. Any other initialisation that must happen +early on should also be done here.

The kernel calls the Variant's Init1() function. On the template port, this is the Variant +layer's Init1(), i.e. the functions Template::Init1(). The source for this is in ...\template_variant\specific\variant.cpp.

void Template::Init1() + { + __KTRACE_OPT(KBOOT,Kern::Printf("Template::Init1()")); + + // + // TO DO: (mandatory) + // + // Configure Memory controller and Memrory Bus parameters (in addition to what was done in the Bootstrap) + // + __KTRACE_OPT(KBOOT,Kern::Printf("Memory Configuration done")); + + // + // TO DO: (optional) + // + // Inform the kernel of the RAM zone configuration via Epoc::SetRamZoneConfig(). + // For devices that wish to reduce power consumption of the RAM IC(s) the callback functions + // RamZoneCallback() and DoRamZoneCallback() will need to be implemented and passed + // to Epoc::SetRamZoneConfig() as the parameter aCallback. + // The kernel will assume that all RAM ICs are fully intialised and ready for use from boot. + // + + // + // TO DO: (optional) + // + // Initialise other critical hardware functions such as I/O interfaces, etc, not done by Bootstrap + // + // if CPU is Sleep-capable, and requires some preparation to be put in that state (code provided in Bootstrap), + // the address of the idle code is writen at this location by the Bootstrap + // e.g. + // iIdleFunction=*(TLinAddr*)((TUint8*)&Kern::SuperPage()+0x1000); + // + TemplateAssp::Init1(); + }

The last line is a call into the ASSP layer, +which is implemented as shown below. On the template port, it is the +ASSP layer that initialises the interrupt dispatcher and the real +time clock. The source for this is in ...\template_assp\assp.cpp:

EXPORT_C void TemplateAssp::Init1() + { + __KTRACE_OPT(KBOOT,Kern::Printf("TemplateAssp::Init1()")); + // + // TO DO: (optional) + // + TemplateInterrupt::Init1(); // initialise the ASSP interrupt controller + + // + // TO DO: (optional) + // + // Initialises any hardware blocks which require early initialisation, e.g. enable and power the LCD, set up + // RTC clocks, disable DMA controllers. etc. + // + } + +

TemplateInterrupt::Init1(); is +static function that initialises the interrupt dispatcher. See Interrupt Layer Initialisation.

+
Asic::Init3() +implementation

Entry conditions

    +
  • called in the +context of the supervisor thread

  • +
  • the kernel is +ready to handle interrupts

  • +
  • the kernel heap +and memory management system is fully functional.

  • +

What the function should do

This is called during +stage 3 of kernel initialisation.

In this function, you need +to:

    +
  • enable interrupt +sources

  • +
  • start the millisecond +tick timer.

  • +
  • Optionally, +replace the implementation used by Kern::NanoWait().

  • +

Any other general initialisation can also be done here.

As an example, on the template port, the function is implemented +in the Variant layer, by Template::Init3().

Millisecond tick timer

The kernel expects that the +kernel's tick handler routine will be called at a fixed microsecond +period, the value of which is returned by the implementation of Asic::MsTickPeriod() function. The Init3() function must be implemented to start this. See Kernel Timers for background information.

The template implementation +is as follows:

EXPORT_C void TemplateAssp::Init3() + { + __KTRACE_OPT(KBOOT,Kern::Printf("TemplateAssp::Init3()")); + + TTemplate::Init3(); + + NTimerQ& m=*(NTimerQ*)NTimerQ::TimerAddress(); + iTimerQ=&m; + // + // TO DO: (mandatory) + // + // If Hardware Timer used for System Ticks cannot give exactly the period required store the initial rounding value + // here which is updated every time a match occurs. Note this leads to "wobbly" timers whose exact period change + // but averages exactly the required value + // e.g. + // m.iRounding=-5; + // + + TInt r=Interrupt::Bind(KIntIdOstMatchMsTimer,MsTimerTick,&m); // bind the System Tick interrupt + if (r!=KErrNone) + Kern::Fault("BindMsTick",r); + + // + // TO DO: (mandatory) + // + // Clear any pending OST interrupts and enable any OST match registers. + // If possible may reset the OST here (to start counting from a full period). Set the harwdare to produce an + // interrupt on full count + // + + r=Interrupt::Enable(KIntIdOstMatchMsTimer); // enable the System Tick interrupt + if (r!=KErrNone) + Kern::Fault("EnbMsTick",r); + + // + // TO DO: (optional) + // + // Allocate physical RAM for video buffer, as per example below. However with some hardware, the Video Buffer + // may not reside in main System memory, it may be dedicated memory. + // + // EXAMPLE ONLY + TInt vSize=VideoRamSize(); + r=Epoc::AllocPhysicalRam(2*vSize,TemplateAssp::VideoRamPhys); + if (r!=KErrNone) + Kern::Fault("AllocVRam",r); + }

Servicing the timer interrupt

The timer interrupt +service routine is required only to call the Ntimer::TickQ() function and perform any housekeeping necessary to ensure that the +handler itself is called again after the time reported by the MsTickPeriod() routine. Since the handler is called frequently, +it is written in assembler for the fastest execution.

__NAKED__ void MsTimerTick(TAny* aPtr) + { + // Service 1ms tick interrupt + asm("ldr ip, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iRounding)); + asm("ldr r2, __KHwBaseOst "); + asm("adds ip, ip, #2 "); + asm("ldr r3, __KOst1000HzTickMatchIncrement "); + asm("subcs ip, ip, #5 "); + asm("str ip, [r0, #%a0]" : : "i" _FOFF(NTimerQ,iRounding)); + asm("addcs r3, r3, #1 "); + asm("mov r1, #%a0" : : "i" ((TInt)(1<<KHwOstMatchMsTimer))); + asm("str r1, [r2, #0x14] "); // clear interrupt + asm("ldr r1, [r2, #%a0]" : : "i" ((TInt)KHwOstMatchMsTimer*4)); // r1=old match value + asm("add r1, r1, r3 "); // step match value on + asm("ldr ip, [r2, #0x10] "); // r3=system timer value + asm("str r1, [r2, #%a0]" : : "i" ((TInt)KHwOstMatchMsTimer*4)); + asm("cmp ip, r1 "); // compare to next match value + +#ifdef _DEBUG + asm("addpl r1, ip, #10 "); // in DEBUG if timer>match value, set match value to timer + a bit + asm("strpl r1, [r2, #%a0]" : : "i" ((TInt)KHwOstMatchMsTimer*4)); + asm("b Tick__7NTimerQ "); // call interrupt handler anyway +#else + asm("bmi Tick__7NTimerQ "); // if timer<match value, OK - call interrupt handler +#endif + + // otherwise we are late for the next tick so force a data abort exception... + asm("mvn r2, #0x10000002 "); // r2=0xeffffffd + asm("str r2, [r2] "); // die + + // Constant data embedded in code. + asm("__KOst1000HzTickMatchIncrement: "); + asm(".word %a0" : : "i" ((TInt)KOst1000HzTickMatchIncrement)); + asm("__KHwBaseOst: "); + asm(".word %a0" : : "i" ((TInt)KHwBaseOst)); + }

Note that it is a requirement that the timer +period should be an integral number of microseconds, even if the exact +period is not 1000us. It is always possible to add code to the interrupt +handler to achieve this average so that over a large number of ticks, +the deviation from this average will tend to 0, by adjusting the exact +number of ticks from tick to tick. See also Timers

NanoWait() implementation

Kern::NanoWait() is a function that can be called if you want to wait for very short +periods of time within the kernel. You call this function and specify +the number of nanoseconds. The function is, in effect, a shell that +uses default implementation code provided by the generic platform. +You can provide your own implementation in your port, and register +this with the platform. This allows the wait functionality to be implemented +in the best possible way for your platform, possibly by using a hardware +timer whose frequency is independent of the CPU frequency.

To replace the default implementation, you need to:

    +
  • code your own +function. This has the same signature as Kern::NanoWait():

    void AsicImpl::DoNanoWait(TUint32 aInterval) +    { +    // Wait for aInterval nanoseconds +    }

    where AsicImpl is the class that is ultimately derived from Asic.

  • +
  • register this +implementation by adding the following call into your Asic::Init3() function:

    Kern::SetNanoWaitHandler(AsicImpl::DoNanoWait);
  • +

You can see where this goes by looking at the template port +at: ...\base\cedar\template\template_assp\template_assp.cpp

+
Asic::DebugOutput() +implementation

It is worth implementing this early so that +it is possible to get trace output to see what the kernel is doing. +This function is passed one character at a time. Normally this is +sent to a UART, though it can be output through any convenient communications +channel.

On the template port, this is implemented in the +Variant layer, by Template::DebugOutput() in ...\template_variant\specific\variant.cpp.

+
Asic::Idle() +implementation

If no power management has been implemented, +then this function is called when the system is to idle to allow power +saving. This function can just return, until power management is implemented. +Once power management has been implemented, then idling behaviour +will be handled by the power controller, i.e. the Variant's implementation +of the DPowerController class

+
Asic::MsTickPeriod() +implementation

This function is used to return the number +of microseconds per tick. To avoid timing drift, a tick frequency +should be chosen that gives a round number of microseconds per tick. +The function can return zero until the tick timer has been implemented.

On the template port, this function is implemented in the ASSP +layer, and can be found in the source file ...\template_assp\assp.cpp. It is a simple function that just returns the value.

EXPORT_C TInt TemplateAssp::MsTickPeriod() + { + // + // TO DO: (mandatory) + // + // Return the OST tick period (System Tick) in microseconds ( 10E-06 s ). + // + return 1000; // EXAMPLE ONLY + } +

See also Timers.

+
Asic::SystemTimeInSecondsFrom2000() + implementation

This is a function that the kernel +uses to get the system time. Its signature is

Tint SystemTimeInSecondsFrom2000(Tint& aTime);

An implementation must set the aTime reference +to the number of seconds that have elapsed since the start of the +year 2000. This is a positive number; a negative number is interpreted +as time before 2000.

For the template reference board, the +implementation is as follows:

EXPORT_C TInt TemplateAssp::SystemTimeInSecondsFrom2000(TInt& aTime) + { + aTime=(TInt)TTemplate::RtcData(); + __KTRACE_OPT(KHARDWARE,Kern::Printf("RTC READ: %d",aTime)); + return KErrNone; + } +

Until a real time clock is implemented, this function +can just return KErrNone.

This function +calls the register access functions in the TTemplate class. See ...\template_assp\template_assp.cpp for implementation details.

Note that tracing output is +provided when the KHARDWARE bit in the kerneltrace flags is set for +the debug build.

+
Asic::SetSystemTimeInSecondsFrom2000() +implementation

This is a function that the kernel uses +to set the system time. Its signature is

Tint SetSystemTimeInSecondsFrom2000(Tint aTime);

This sets the real time clock to the number of seconds that have +elapsed since the start of the year 2000. This is a positive number; +a negative number is interpreted as time before 2000.

For +the template reference board, the implementation is as follows:

EXPORT_C TInt TemplateAssp::SetSystemTimeInSecondsFrom2000(TInt aTime) + { + // + // TO DO: (optional) + // + // Check if the RTC is running and is stable + // + __KTRACE_OPT(KHARDWARE,Kern::Printf("Set RTC: %d",aTime)); + TTemplate::SetRtcData(aTime); + __KTRACE_OPT(KHARDWARE,Kern::Printf("RTC: %d",TTemplate::RtcData())); + return KErrNone; + } +

Note that tracing output is provided when the KHARDWARE +bit in the kerneltrace flags is set for the debug build. In this function, +the trace output shows the value passed in from the kernel and then +shows the value read back from the real time clock for verification.

+
Asic::NanoWaitCalibration() + implementation

The function Kern::NanoWait() can be called if you want to wait for very short periods of time +within the kernel. You call this function and specify the number of +nanoseconds. You can either use the default implementation of this +function, or you can provide your own.

The default implementation +provided by Symbian platform that Kern::NanoWait() uses is a busy loop that is calibrated by calling Asic::NanoWaitCalibration(). NanoWaitCalibration() should return the number +of nanoseconds taken to execute 2 machine cycles. This is obviously +dependent on the CPU clock speed, so if variants are likely to run +at different speeds, then this should be implemented in the Variant +layer.

This approach cannot always take into account factors +such as processor frequency scaling. An alternative approach is for +the Variant to supply its own implementation to be used by Kern::NanoWait(). Note that you do not replace Kern::NanoWait() itself as this is a shell function that results in a call to the +the implementation. See Asic::Init3() for detail on how to replace the implementation.

On the template port, Asic::NanoWaitCalibration() is implemented in the ASSP layer, and not in the Variant layer, +and can be found in the source file ...\template_assp\assp.cpp. It is a simple function that just returns the value.

EXPORT_C TUint32 TemplateAssp::NanoWaitCalibration() + { + // + // TO DO: (mandatory) + // + // Return the minimum time in nano-seconds that it takes to execute the following code: + // nanowait_loop: + // subs r0, r0, r1 + // bhi nanowait_loop + // + // If accurate timings are required by the Base Port, then it should provide it's own implementation + // of NanoWait which uses a hardware counter. (See Kern::SetNanoWaitHandler) + // + + return 0; // EXAMPLE ONLY + } +
+
Asic::VariantHal() +implementation

You might find it useful to review User-Side Hardware +Abstraction Technology first.

This is the HAL handler +for the HAL group THalFunctionGroup::EHalGroupVariant.

+
Asic::MachineConfiguration() + implementation

This returns a TPtr8 descriptor representing an area containing machine configuration +information.

The address of this object is obtained by calling Kern::MachineConfig(). However, the Variant (either the +ASSP layer or the Variant layer or both) is responsible for the content.

In the template port, the function is implemented in the Variant +layer:

TPtr8 Template::MachineConfiguration() + { + return TPtr8((TUint8*)&Kern::MachineConfig(),sizeof(TActualMachineConfig),sizeof(TActualMachineConfig)); + } +

Here, the machine configuration information is represented +by an object of type TTemplateMachineConfig, which +derives from TMachineConfig. In effect, TMachineConfig represents information that is common to all, while the Variant +can extend this to contain whatever information is appropriate.

Note that TActualMachineConfig is a typedef +for TTemplateMachineConfig.

+
Asic::StartupReason() +implementation

If a startup reason is available from hardware +or a preserved RAM location, it should be returned by the function. +The default is to return EStartupColdReset.

On the template port, this is implemented in the ASSP layer:

EXPORT_C TMachineStartupType TemplateAssp::StartupReason() + { + __KTRACE_OPT(KBOOT,Kern::Printf("TemplateAssp::StartupReason")); + #ifdef _DEBUG // REMOVE THIS + TUint s = Kern::SuperPage().iHwStartupReason; + __KTRACE_OPT(KBOOT,Kern::Printf("CPU page value %08x", s)); + #endif // REMOVE THIS + // + // TO DO: (mandatory) + // + // Map the startup reason read from the Super Page to one of TMachineStartupType enumerated values + // and return this + // + return EStartupCold; // EXAMPLE ONLY + } +
+
+ +ASSP/Variant Architecture +
\ No newline at end of file