Open Nathan-Stohs opened 9 years ago
We have never wanted the high power mode.
Wake up time and is much slower and wake up jitter is much higher at 48 MHz … because of the PLL.
Kenneth W. Parker, Ph.D.
From: Nathan Stohs [mailto:notifications@github.com] Sent: Thursday, January 29, 2015 5:53 PM To: Samraksh/MF Subject: [MF] .NOW default power mode? (#238)
Currently the .NOW defaults to high power mode (48 or 64 MHz). Is this really what we want? Now with optimized builds we can get... acceptable... performance at 8 MHz.
I think the original plan was for the developer to aggressively manage this via setting the power mode in C#. We have not really pushed that philosophy.
So do we still want to default to low or high? The argument for low is that this is the mode to quick sleep latency because in high power mode we have to re-tune the PLL often. It is also more efficient. The argument for high is that low power is just too slow.
— Reply to this email directly or view it on GitHubhttps://github.com/Samraksh/MF/issues/238.
I also vote for low power. Perhaps with a Debug.Print message on startup mentioning that fact to remind the user.
Of course, some existing time-sensitive apps & tests might now fail with this new default ...
It’s not an election. Starting up with the PLL on is a bug.
It’s a bug we’ve had to live with at times, but the idea that it’s somehow not a bug is incompatible with the entire rest of the system design.
Kenneth W. Parker, Ph.D.
P.S. I have eMail going back about 12 years on this point
From: William Leal [mailto:notifications@github.com] Sent: Thursday, January 29, 2015 7:31 PM To: Samraksh/MF Cc: Kenneth W. Parker Subject: Re: [MF] .NOW default power mode? (#238)
I also vote for low power. Perhaps with a Debug.Print message on startup mentioning that fact to remind the user.
Of course, some existing time-sensitive apps & tests might now fail with this new default ...
— Reply to this email directly or view it on GitHubhttps://github.com/Samraksh/MF/issues/238#issuecomment-72132907.
I'll start flipping on low-power by default, starting where its safe and working my way up. There actually are architectural problems, especially when sleep and deep sleep are added in, but most or all of them have workarounds or at least can work if we assume some action from the C# programmer.
Low-power (8 MHz) will only apply release builds. Debug builds by default have to be high-power (at least 48 MHz). The MF debug protocol is extremely brittle, the slowdown is enough to make VS debugging and Tinybooter/MFDeploy fail unless the build is fully optimized and even then it needed my custom fast USART receive buffering code. (This is why optimized builds were a blocker).
For the record, in C#, the command to switch power modes is ChangePowerLevel(PowerLevel level)
which is part of Microsoft.SPOT.Hardware
and defined in Uptime.cs
. It has a callback but it doesn't seem too useful as it just fires right away before the change... Anyway I post this info here because I had to go to some pains to figure it out. (Its actually somewhat hard to find out what C# is mapped to a particular piece of C++). This of course also goes in the formal user materials.
Great! I want to expand on Nathan's post.
Hunting:
Hunting pattern:
ChangePowerLevel(PowerLevel level)
maps to spot_hardware_native_Microsoft_SPOT_Hardware_PowerState.cpp's Library_spot_hardware_native_Microsoft_SPOT_Hardware_PowerState::InternalChangePowerLevel___STATIC__VOID__MicrosoftSPOTHardwarePowerLevel
which calls the HAL interface CPU_ChangePowerLevel(POWER_LEVEL level)
which will reside either in a stub or in a target architecture's device driver. For STM32F10x, our pwr
driver's netmf_pwr_functions.cpp implements CPU_ChangePowerLevel(POWER_LEVEL level)
directly with register manipulation. It's also common to see the netmf_*_functions.cpp files as glue code into the STMicro BSP.Question:
Notes:
POWER_LEVEL
( managed PowerLevel
) map to High
/Medium
/Low
options. We have the power to customize that with 253 more levels and maintain compatibility with MS SPOT API because it's a byte-width enum with only 3 members.
SleepLevel
's Awake
, SelectiveOff
, Sleep
, DeepSleep
, and Off
.MicroFrameworkPK_v4_3\Test
folder and these include examples of how to use the SPOT SDK. For instance, TestPower.cs
.Proposed solution to serial during low power state:
PortDefinition pd = PortDefinition.CreateInstanceForSerial( val, @"\\.\" + val, 115200 );
. So we can change that to a lower value, do auto-negotiation, or accept a parameter to make it user-specifiable from MFDeploy.
The PSDK compiled help manual in the Documentation folder is a good starting place...
I hadn't had much luck with this before, but I hadn't tried the grep trick instead of windows search. Will try, thanks.
Hunting pattern: ....
Yes, this is exactly the technique I used. Lots of reverse searching. It works but is tedious.
Does the callback not seem useful because...?
I say its not useful because it fires immediately and is synchronous, or at least that's what it looks like to me from Uptime.cs
. Meaning that the driver is not driving the callback, its the user calling himself and nothing more. As you say, it would be useful if the driver could notify the user of power changes (especially if its automated) but its not in the API and so would be an extension. We probably do need to do this, the MS power interface is inadequate to fully express what we need, but that is probably an R13 thing.
Native POWER_LEVEL ( managed PowerLevel) map to High/Medium/Low options...
Good point.
Oberon Micro's STM32 port does not include the ability to change power level. The only other published architecture to use PowerLevel is the MC9328MXL.
Yeah, I'm not sure anybody does, the low-power space here is still wide open despite our lack of agility. I've seen some references made to it in other projects but its always a "maybe later" sort of thing. As alluded to previously, I attribute this to the relatively impoverished MF power interface. This is what MF is notorious for, its an OS designed by CS guys that has to run on hardware designed by ECE guys, while they live in the same domain their MOs and expectations are very different. (Ken would probably say this problem is industry wide). To get the power performance this chip is capable of, you have to play by the rules of the ECE guys. So that means a set of power extensions and parts of the engine re-written.
Microsoft.SPOT.Hardware.Debugger.dll sets the desktop connection's baud rate to 115200...
The problem isn't the baud rate, I don't think we are actually losing bytes (or at least haven't since I re-wrote it), its that the serial processing engine behind it is sort of awful. Its the CS vs ECE thing again. MF gets a byte of data, tosses it around across O(n) data structures, tries to parse it a couple of times with strcmp(), then gives up and puts it back on the queue. For every single byte. In other words it sort of ignores processing time. So what my customized serial code ("turbo mode") did was to pay some respect to the need to actually truck data around.
That said, lowering the baud rate may actually give us more breathing room as a side effect, by just forcing the whole process to go slower. But its not because the UART hardware can't handle it. This also forces us to distribute our own MF deploy and this dll etc... but I notice that others (Netduino) already gave up on MFDeploy and developed their own tools, so we aren't the first to figure this out.
And as you say it would be nice in some circumstances to increase the baud rate... however this is actually limited by our clock accuracy. 115200 is the best the HSI can do at 8 MHz out of the box.
Specifically, switching to a high power mode requires during the PLL on, which does take a long time (e.g., 2 ms).
The intent was to support the following modes:
· Off. The real time clock continues, but start up requires memory reload.
· Sleep. Startup is fast. Memory is not lost, all else is off.
· Normal Run Mode. No PLL, default pre-fetch.
· Low Power Numerical. Turn off most pre-fetch to maximize numerical performance per watt when everything is in cache. Turn on PLL it the numerical energy efficiency is significantly different at a high speed than at the default speed.
· High Power. Slow. Forget about efficiency, just go fast. Absolut maxim speed is not required, but would be ideal.
It is assumed that one would need to go to high power mode in order to preforming some IO operations. Returning an error seems natural. Automatically jumping into high power mode and then jumping back again unlikely to be OK.
Adjusting the clock rate automatically without telling the user is OK as long as you remain in High Power mode. For example you could slow down for curtain IO activates that don’t work at maximum clock rate.
Kenneth W. Parker, Ph.D.
From: Michael McGrath [mailto:notifications@github.com] Sent: Monday, February 2, 2015 9:39 PM To: Samraksh/MF Cc: Kenneth W. Parker Subject: Re: [MF] .NOW default power mode? (#238)
Great! I want to expand on Nathan's post.
Hunting: · The PSDK compiled help manual in the Documentation folder is a good starting place for discovering C# functionality and names that reveal native mappings. Use grep instead of Windows search. If you have the build environment mapped into an IDE, then offline, manual tracing also eases some burden and helps quickly tease out call chains (i.e., use F12 in VS and ctrl + [shift] + g in eclipse).
Hunting pattern: · The managed and native function names are similar -- Uptime.cs's ChangePowerLevel(PowerLevel level) maps to spot_hardware_native_Microsoft_SPOT_Hardware_PowerState.cpp's Library_spot_hardware_native_Microsoft_SPOT_Hardware_PowerState::InternalChangePowerLevel_STATICVOID__MicrosoftSPOTHardwarePowerLevel which calls the HAL interface CPU_ChangePowerLevel(POWER_LEVEL level) which will reside either in a stub or in a target architecture's device driver. For STM32F10x, our pwrdriver's netmf_pwr_functions.cpp implements CPU_ChangePowerLevel(POWERLEVEL level) directly with register manipulation. It's also common to see the netmf*_functions.cpp files as glue code into the STMicro BSP.
Question: · Does the callback not seem useful because...? Is there something that needs to happen to make it useful? o I assume customers, especially those with external peripherals, would want to be notified of frequency and power changes. o The callback hooks are also useful for profiling during automated tests (i.e., notify TestSystem that you are entering low power; TestSystem measures consumption, latency, etc.
Notes:
· Native POWER_LEVEL ( managed PowerLevel) map to High/Medium/Low options. We have the power to customize that with 253 more levels and maintain compatibility with MS SPOT API because it's a byte-width enum with only 3 members. o Similar idea for SleepLevel's Awake, SelectiveOff, Sleep, DeepSleep, and Off.
· Oberon Micro's STM32 port does not include the ability to change power level. The only other published architecture to use PowerLevel is the MC9328MXL.
· Microsoft has many tests in the MicroFrameworkPK_v4_3\Test folder and these include examples of how to use the SPOT SDK. For instance, TestPower.cs.
Proposed solution to serial during low power state: · Microsoft.SPOT.Hardware.Debugger.dll sets the desktop connection's baud rate to 115200 inside Stream.cs line 649 PortDefinition pd = PortDefinition.CreateInstanceForSerial( val, @".file:///\." + val, 115200 );. So we can change that to a lower value, do auto-negotiation, or accept a parameter to make it user-specifiable from MFDeploy. o Caveat: I'm not sure whether Visual Studio will use our new dll if we just replace the official SDK dll. o Caveat: our users will need to have our compiled desktop DLLs and our customized MFDeploy, but this would happen anyway for some of my extensions. Without our custom dll, they would experience what Nathan encountered. o Benefit: potential for debugging at faster than 115.2kbps.
— Reply to this email directly or view it on GitHubhttps://github.com/Samraksh/MF/issues/238#issuecomment-72582170.
Switching to High Power Mode may take long enough so that it should not block. Although, this adds burden to the user, so we could at least discuss this.
There are two commodity businesses:
· Software as a kind of Hardware, that is software developed and deployed with the hardware life cycle processes (e.g., TinyOS, Arduino, etcetera).
· Infrastructure Dominated Systems, that is systems where most of the hardware exists only to support the infrastructure (i.e., not to support the application). Raspberry Pi is a good example.
In order to make money you must avoid both. As a company we are primarily interested in bridging the gap between these two camps. That is, can you have an actual software life cycle without the infrastructure “wagging the dog”.
Kenneth W. Parker, Ph.D.
From: Nathan Stohs [mailto:notifications@github.com] Sent: Tuesday, February 3, 2015 1:31 PM To: Samraksh/MF Cc: Kenneth W. Parker Subject: Re: [MF] .NOW default power mode? (#238)
Does the callback not seem useful because...?
I say its not useful because it fires immediately and is synchronous, or at least that's what it looks like to me from Uptime.cs. Meaning that the driver is not driving the callback, its the user calling himself and nothing more. As you say, it would be useful if the driver could notify the user of power changes (especially if its automated) but its not in the API and so would be an extension. We probably do need to do this, the MS power interface is inadequate to fully express what we need, but that is probably an R13 thing.
Native POWER_LEVEL ( managed PowerLevel) map to High/Medium/Low options...
Good point.
Oberon Micro's STM32 port does not include the ability to change power level. The only other published architecture to use PowerLevel is the MC9328MXL.
Yeah, I'm not sure anybody does, the low-power space here is still wide open despite our lack of agility. I've seen some references made to it in other projects but its always a "maybe later" sort of thing. As alluded to previously, I attribute this to the relatively impoverished MF power interface. This is what MF is notorious for, its an OS designed by CS guys that has to run on hardware designed by ECE guys, while they live in the same domain their MOs and expectations are very different. (Ken would probably say this problem is industry wide). To get the power performance this chip is capable of, you have to play by the rules of the ECE guys. So that means a set of power extensions and parts of the engine re-written.
Microsoft.SPOT.Hardware.Debugger.dll sets the desktop connection's baud rate to 115200...
The problem isn't the baud rate, I don't think we are actually losing bytes (or at least haven't since I re-wrote it), its that the serial processing engine behind it is sort of awful. Its the CS vs ECE thing again. MF gets a byte of data, tosses it around across O(n) data structures, tries to parse it a couple of times with strcmp(), then gives up and puts it back on the queue. For every single byte. In other words it sort of ignores processing time. So what my customized serial code ("turbo mode") did was to pay some respect to the need to actually truck data around.
That said, lowering the baud rate may actually give us more breathing room as a side effect, by just forcing the whole process to go slower. But its not because the UART hardware can't handle it. This also forces us to distribute our own MF deploy and this dll etc... but I notice that others (Netduino) already gave up on MFDeploy and developed their own tools, so we aren't the first to figure this out.
And as you say it would be nice in some circumstances to increase the baud rate... however this is actually limited by our clock accuracy. 115200 is the best the HSI can do at 8 MHz out of the box.
— Reply to this email directly or view it on GitHubhttps://github.com/Samraksh/MF/issues/238#issuecomment-72706410.
What I was trying to say but got across poorly here wasn't that I wanted to go out and write a bunch of STM32F1 platform stuff. But rather the issue is that the MF .NET power management model is to simplistic, and we need to a) write a bunch platform independent extensions and b) make the MF Execution Engine more power aware.
Consider that, defacto, the entirety of the MF power API consists of two commands. The implementation of CPU_Sleep()
(note the lack of parameters) which is only called internally by the MF execution engine, I think in one place, and ChangePowerLevel(PowerLevel level)
which is the only C# level power command but at least has a parameter we can overload for more options.
Furthermore the PAL usage of CPU_Sleep()
is about as simple as it gets, it's just tossed in the main task loop without any nuance or intelligence regarding what is actually going on in the system. This matters because CPU_Sleep()
, to work optimally, needs some awareness of the higher-level state. For instance, if we are in the middle of a UART operation (and MF will try to sleep between individual bytes), we can't really go to sleep (stop mode) because that shuts off the UART clock, so we have to settle for light sleep instead.
This is not to say that there is no workaround or other way to handle situations such as the above, which we do, but what we end up with is a bunch of platform dependent hacks around this small interface for every peripheral instead of a real architecture, which in turn only increases the pressure for the hardware to wag the dog.
I claim the present MF power interface is too impoverished to implement something that fully leverages power management on any platform. The MF HAL layer has enough information to make things work, but not intelligently. On the other hand, it's not like this is a disaster either, its an effective enough shoehorn that you can probably get about 70% of ideal out of it.
… posted with the assumption that some of this is incorrect, in the spirit of furthering the conversation. I don’t disagree with anything that Ken or Nathan previously wrote.
Because Nathan mentioned it, the following is my interpretation of CPU_Sleep()
.
CPU_Sleep(SLEEP_LEVEL level, UINT64 wakeEvents)
is called in pal\AsyncProcCall\Completions.cpp’s HAL_COMPLETION::WaitForInterrupts(Expire,sleeveless,wakeEvents)
, which is called by pal\events\events.cpp’s Events_WaitForEvents(sleepLevel, WakeupSystemEvents, Timeout_Milliseconds)
when there are no pending HAL continuations.
Events_WaitForEvents(sleepLevel,WakeupSystemEvents,Timeout_Milliseconds)
may be entered by calling its smaller signature, Events_WaitForEvents(WakeupSystemEvents,TimeoutMilliseconds) that defaults to using SLEEP_LEVEL__SLEEP. The two-argument version is called by a variety of applications and tests, but in most cases we should only care about pal\COM\ComDirector.cpp’s DebuggerPort_Write(ComPortNum,Data,size)
function. If USART_Write
returns 0 and interrupts are disabled, the write exits (*actually, it tries 100 more times in the loop before exiting because USART_DRIVER::Write uses a GLOBAL_LOCK(irq) both inside a for loop and before setting the usart interrupt tx enable. The semantics of GlobalLock are supposed to preserve the interrupt enable/disable state, but on April 17, 2013, Nived committed “Checking in timer changes for rollover bug” which changed the Restore() call (which was supposed to only enable interrupts if releasing the original GlobalLock (i.e., restoring state)) to “cpsie i” which enables interrupts. Actually, we’ve never had a Microsoft-spec GlobalLock… see https://github.com/Samraksh/MF/blob/fe515c11031c0ff4c606edd88c2b076c6d9a0b37/MicroFrameworkPK_v4_0/Solutions/STM32F10x/DeviceCode/drivers/globallock/STM32F10x_globallock.cpp and https://github.com/Samraksh/MF/blob/1999720951e73073795e436f7af5f3d0f6dd147f/MicroFrameworkPK_v4_3/DeviceCode/Targets/Native/STM32F10x/DeviceCode/cores/arm/Common/GlobalLock/gcc-thumb2/SmartPtr_thumb2.cpp . So the Microsoft USART code is broken in that DebuggerPort_Write might loop 100 times trying to write with interrupts disabled, but our GlobalLock code has always had incorrect semantics that would have caused interrupts to be re-enabled. So the bugs cancel each other out and cause problems elsewhere. ) But if USART_Write
returns 0 and interrupts are enabled, DebuggerPort_Write calls Events_WaitForEvents(0,1)
which is supposed to sleep for 1 millisecond independent of events.Events_WaitForEvents(sleepLevel,WakeupSystemEvents,Timeout_Milliseconds)
is also entered via CLR\Core\Execution.cpp’s CLR_RT_ExecutionEngine::WaitSystemEvents(powerLevel,events,timeExpire)
, which is called by either CLR_RT_ExecutionEngine::WaitForActivity(powerLevel,events,timeout_ms)
or CLR_RT_ExecutionEngine::DebuggerLoop()
after ProcessHardware() and UpdateTime(). The function WaitForActivity(powerLevel,events,timeout_ms)
allows for CLR_HW_Hardware.PowerLevel Active, SelectiveOff, Sleep, DeepSleep, and Off. So while the HAL separates power and sleep, the CLR layer’s TinyCLR_Hardware conflates power and sleep. WaitForActivity(powerLevel,events,timeout_ms)
may be called by either the user program with Microsoft.SPOT.Debugger.PowerState.Sleep
, or with CLR_RT_ExecutionEngine::WaitForActivity()
which first tries to restart CLR user threads that were waiting for an c_Event_IdleCPU
event before calling WaitForActivity with SLEEP_LEVEL__SLEEP and the time until the next process needs to launch. CLR_RT_ExecutionEngine::WaitForActivity()
is called by CLR_RT_ExecutionEngine::Execute(entryPointArgs,maxContextSwitch)
when there are no ready threads. And Execute is called by CLRStartup, and CLRStartup is called by ApplicationEntryPoint.CLR_RT_ExecutionEngine::WaitForActivity()
uses SLEEP_LEVEL__SLEEP as the default sleep level, and the `CLR_RT_ExecutionEngine::DebuggerLoop() uses SLEEP_LEVEL__SLEEP as the default sleep level during debugging. The latter could be a bug if our default sleep is too low/slow (hence Nathan preventing sleep in debug mode, although we should just prevent sleep when the debugger is attached).USART_Driver::PowerSave
which is never used. So even PAL power management is disorganized or not present.
Currently the .NOW defaults to high power mode (48 or 64 MHz). Is this really what we want? Now with optimized builds we can get... acceptable... soft of... performance at 8 MHz.
I think the original plan was for the developer to aggressively manage this via setting the power mode in C#. We have not really pushed that philosophy.
So do we still want to default to low or high? The argument for low is that this is the mode to quick sleep latency because in high power mode we have to re-tune the PLL after every wakeup. It is also more efficient. The argument for high is that low power is just too slow. In fact it might be so slow that it swamps the PLL startup penalty.