Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Arm development systems

28 views
Skip to first unread message

Phil Hobbs

unread,
Feb 7, 2012, 8:31:23 PM2/7/12
to
I'm looking at starting an angel-funded effort to build some interesting
small sensor devices, for which I'm going to need to do some Cortex M3
programming for real. Since that sort of job needs speed more than
money, I'd like to get a full-function development system with a decent
JTAG-based debugger, real support, and the whole 012 yards.

All you folks with the several $k development systems: What are you
using, how do you like it, and why?

Thanks

Phil Hobbs
--
Dr Philip C D Hobbs
Principal Consultant
ElectroOptical Innovations LLC
Optics, Electro-optics, Photonics, Analog Electronics

160 North State Road #203
Briarcliff Manor NY 10510
845-480-2058

hobbs at electrooptical dot net
http://electrooptical.net

Tim Wescott

unread,
Feb 7, 2012, 8:53:26 PM2/7/12
to
On Tue, 07 Feb 2012 20:31:23 -0500, Phil Hobbs wrote:

> I'm looking at starting an angel-funded effort to build some interesting
> small sensor devices, for which I'm going to need to do some Cortex M3
> programming for real. Since that sort of job needs speed more than
> money, I'd like to get a full-function development system with a decent
> JTAG-based debugger, real support, and the whole 012 yards.
>
> All you folks with the several $k development systems: What are you
> using, how do you like it, and why?

Gnu tool chain, Eclipse IDE, $50 JTAG dongle from SparkFun. It works as
well as many $10K development systems that I've used. Support is a bit
harder to come by, but I've gotten some pretty dreadful support from the
suppliers of those high-dollar tool chains, too.

--
My liberal friends think I'm a conservative kook.
My conservative friends think I'm a liberal kook.
Why am I not happy that they have found common ground?

Tim Wescott, Communications, Control, Circuits & Software
http://www.wescottdesign.com

John Larkin

unread,
Feb 7, 2012, 9:37:11 PM2/7/12
to
On Tue, 07 Feb 2012 20:31:23 -0500, Phil Hobbs
<pcdhSpamM...@electrooptical.net> wrote:

>I'm looking at starting an angel-funded effort to build some interesting
>small sensor devices, for which I'm going to need to do some Cortex M3
>programming for real. Since that sort of job needs speed more than
>money, I'd like to get a full-function development system with a decent
>JTAG-based debugger, real support, and the whole 012 yards.
>
>All you folks with the several $k development systems: What are you
>using, how do you like it, and why?
>
>Thanks
>
>Phil Hobbs

We use the CodeRed stuff: IDE, jtag, dev boards. Seems OK. They do
answer our questions.




--

John Larkin, President Highland Technology Inc
www.highlandtechnology.com jlarkin at highlandtechnology dot com

Precision electronic instrumentation
Picosecond-resolution Digital Delay and Pulse generators
Custom timing and laser controllers
Photonics and fiberoptic TTL data links
VME analog, thermocouple, LVDT, synchro, tachometer
Multichannel arbitrary waveform generators

Jon Elson

unread,
Feb 8, 2012, 12:37:10 AM2/8/12
to
Phil Hobbs wrote:

> I'm looking at starting an angel-funded effort to build some interesting
> small sensor devices, for which I'm going to need to do some Cortex M3
> programming for real. Since that sort of job needs speed more than
> money, I'd like to get a full-function development system with a decent
> JTAG-based debugger, real support, and the whole 012 yards.
>
> All you folks with the several $k development systems: What are you
> using, how do you like it, and why?
Well, this may be totally wrong for your needs, but have you
looked at the BeagleBoard? it is completely awesome, 3" square,
3 W max (more like barely 2) and you can plug in a keyboard, mouse
and XDVI video screen, and add a USB ethernet adapter. I run
Linux on it. Uses a CF card for "disk".
It has about 20 pins of available GPIO.

No development kit needed at all, as long as you don't crash the
system. I have made several TCP server appliances for remote
control of devices using it.

Jon

hamilton

unread,
Feb 8, 2012, 12:49:53 AM2/8/12
to
On 2/7/2012 6:31 PM, Phil Hobbs wrote:
> I'm looking at starting an angel-funded effort to build some interesting
> small sensor devices, for which I'm going to need to do some Cortex M3
> programming for real. Since that sort of job needs speed more than
> money, I'd like to get a full-function development system with a decent
> JTAG-based debugger, real support, and the whole 012 yards.
>
> All you folks with the several $k development systems: What are you
> using, how do you like it, and why?
>
> Thanks
>
> Phil Hobbs

I like Keil.

It just works and if there is a problem, there is someone to call.

don

PS: I have used IAR as well, but the IDE is a little quirky for my tastes.

Don Y

unread,
Feb 8, 2012, 1:33:03 AM2/8/12
to
Hi Phil,

On 2/7/2012 6:31 PM, Phil Hobbs wrote:
> I'm looking at starting an angel-funded effort to build some interesting
> small sensor devices, for which I'm going to need to do some Cortex M3
> programming for real. Since that sort of job needs speed more than
> money, I'd like to get a full-function development system with a decent
> JTAG-based debugger, real support, and the whole 012 yards.

Radix 7? ------------------------------------------^^^ ;-)

> All you folks with the several $k development systems: What are you
> using, how do you like it, and why?

This depends on what your actual goal is. If you are expecting
others to use the tools, then you probably want to find tools
that are "affordable and available" to those folks. E.g., I
try to use "free" tools for anything open-source that I develop
(it doesn't seem fair to force the sorts of people in that camp
to purchase "proprietary" tools). For hardware, I'm a bit less
of an idealist (its too constraining, for me).

You also have to consider how easily folks will be able to
get effective *support*. If they each need a service contract
to get answers to their questions/problems with "BogusWare 5000",
then you've essentially made support unavailable.

I try to document problems with the tools that I'm using and
work around those problems. Often, "fixes" just change the set
of problems that you have to DISCOVER and work-around (I'd rather
live with a known set of problems than a brand new batch of
yet-to-be-discovered problems!)

Finally, you have to consider available skillsets and talent base.
You could opt for the "ideal" tool -- only to discover that folks
have been bottom feeding off "free tools" and don't have the
skills, experience or resources to effectively utilize the "gift"
you've saddled them with!

Sort of like giving a glass of fine cognac to someone who's
spent a lifetime drinking 3.2 beer... :-/

MK

unread,
Feb 8, 2012, 4:17:55 AM2/8/12
to
On 08/02/2012 01:31, Phil Hobbs wrote:
> I'm looking at starting an angel-funded effort to build some interesting
> small sensor devices, for which I'm going to need to do some Cortex M3
> programming for real. Since that sort of job needs speed more than
> money, I'd like to get a full-function development system with a decent
> JTAG-based debugger, real support, and the whole 012 yards.
>
> All you folks with the several $k development systems: What are you
> using, how do you like it, and why?
>
> Thanks
>
> Phil Hobbs

I use the Keil (part of ARM) development system. I just forked out for
the upgrade to the pro system which gives you libraries for USB,
Ethernet, Flash/SD CArd/FAT files etc.
They seem pricey compared with "free" stuff but you get support, every
manufacturer of silicon (AFAIK) has Keil friendly example stuff. I also
suspect that the relationship with ARM means they know what's round the
corner better than most.
You get to use their scheduler (small RTOS) which works quite well.

They do their own debugging tools and they seem to work OK.

I would much rather use the Keil IDE than any other I've tried.

Michael Kellett

John Walliker

unread,
Feb 8, 2012, 5:32:30 AM2/8/12
to
On Feb 8, 5:37 am, Jon Elson <el...@pico-systems.com> wrote:

> Well, this may be totally wrong for your needs, but have you
> looked at the BeagleBoard?  it is completely awesome, 3" square,

It is also worth looking at the new BeagleBone which is smaller and
cheaper than the BeagleBoard and has lots of access to the cpu i/o
pins. It has ethernet and USB on board.

Digikey and Farnell stock it.

John

Nico Coesel

unread,
Feb 8, 2012, 8:45:51 AM2/8/12
to
Tim Wescott <t...@seemywebsite.com> wrote:

>On Tue, 07 Feb 2012 20:31:23 -0500, Phil Hobbs wrote:
>
>> I'm looking at starting an angel-funded effort to build some interesting
>> small sensor devices, for which I'm going to need to do some Cortex M3
>> programming for real. Since that sort of job needs speed more than
>> money, I'd like to get a full-function development system with a decent
>> JTAG-based debugger, real support, and the whole 012 yards.
>>
>> All you folks with the several $k development systems: What are you
>> using, how do you like it, and why?
>
>Gnu tool chain, Eclipse IDE, $50 JTAG dongle from SparkFun. It works as
>well as many $10K development systems that I've used. Support is a bit
>harder to come by, but I've gotten some pretty dreadful support from the
>suppliers of those high-dollar tool chains, too.

I'd go this route as well. Eclipse beats any IDE I've ever used hands
down (including MS Visual Studio).

--
Failure does not prove something is impossible, failure simply
indicates you are not using the right tools...
nico@nctdevpuntnl (punt=.)
--------------------------------------------------------------

Rich Webb

unread,
Feb 8, 2012, 8:32:02 AM2/8/12
to
On Tue, 07 Feb 2012 20:31:23 -0500, Phil Hobbs
<pcdhSpamM...@electrooptical.net> wrote:

>I'm looking at starting an angel-funded effort to build some interesting
>small sensor devices, for which I'm going to need to do some Cortex M3
>programming for real. Since that sort of job needs speed more than
>money, I'd like to get a full-function development system with a decent
>JTAG-based debugger, real support, and the whole 012 yards.
>
>All you folks with the several $k development systems: What are you
>using, how do you like it, and why?

Rowley CodeWorks here. It's gcc-based but with their own libs so LGPL
issues are avoided. Includes their own RTOS (CrossWorks Tasking Library
aka ctl). Supports a bazillion JTAG interfaces (well, lots) including
the more-or-less standard Segger J-Link, the inexpensive Olimex USBs,
and their own CrossConnect. It supports CM3 SWD with the CrossConnect,
Olimex, and Amontec JTAGs. Has a reasonable IDE and debug environment.
30-day free evaluation period. It's been pretty bulletproof IME.

--
Rich Webb Norfolk, VA

Phil Hobbs

unread,
Feb 8, 2012, 10:29:39 AM2/8/12
to
Nico Coesel wrote:
>
> Tim Wescott <t...@seemywebsite.com> wrote:
>
> >On Tue, 07 Feb 2012 20:31:23 -0500, Phil Hobbs wrote:
> >
> >> I'm looking at starting an angel-funded effort to build some interesting
> >> small sensor devices, for which I'm going to need to do some Cortex M3
> >> programming for real. Since that sort of job needs speed more than
> >> money, I'd like to get a full-function development system with a decent
> >> JTAG-based debugger, real support, and the whole 012 yards.
> >>
> >> All you folks with the several $k development systems: What are you
> >> using, how do you like it, and why?
> >
> >Gnu tool chain, Eclipse IDE, $50 JTAG dongle from SparkFun. It works as
> >well as many $10K development systems that I've used. Support is a bit
> >harder to come by, but I've gotten some pretty dreadful support from the
> >suppliers of those high-dollar tool chains, too.
>
> I'd go this route as well. Eclipse beats any IDE I've ever used hands
> down (including MS Visual Studio).
>

I hate the Visual Studio editor--any proper programmer's editor should
be line-oriented, whereas VS's is just like Notepad. Its debugger is
pretty nice, though--much nicer than any gdb frontend. (It isn't as
good as the IBM VisualAge debugger by a long shot, but that's crying
over spilt milk.)

I'm not a big fan of any of the gnu debuggers, and the Eclipse-based
1-wire systems I'm most familiar with don't allow setting watchpoints,
which IMO are pretty vital when doing choreography with multiple
interrupt levels.

Is the Embedded Trace Macrocell support worth the tool price?

Do the proprietary tools have any significant advantage in code size?
(Chip pricing seems more sensitive to flash size than to anything else.)

For cost reasons I'm probably going to use the ST ARMs, e.g. the
STM32L151, which I can get for under $3 in onesies. That's assuming
that their ADCs and DACs aren't too horrible. I can bandage them with a
table lookup, provided that they don't have a lot of missing codes.

This is going to be a fun one if it comes together.

Cheers

Phil Hobbs

unread,
Feb 8, 2012, 10:35:25 AM2/8/12
to
Thanks, those look like fun. The gizmos I'm doing are going to be small
and handheld, so Snoopy & Co. are a bit big and power hungry. Target
power is 2xAA cells.

Cheers

Phil Hobbs

unread,
Feb 8, 2012, 10:50:31 AM2/8/12
to
Don Y wrote:
>
> Hi Phil,
>
> On 2/7/2012 6:31 PM, Phil Hobbs wrote:
> > I'm looking at starting an angel-funded effort to build some interesting
> > small sensor devices, for which I'm going to need to do some Cortex M3
> > programming for real. Since that sort of job needs speed more than
> > money, I'd like to get a full-function development system with a decent
> > JTAG-based debugger, real support, and the whole 012 yards.
>
> Radix 7? ------------------------------------------^^^ ;-)

My amp goes up to 11, so I needed an extra yard too. ;)
All true. In this case it's all proprietary anyway, at least until Won
Hung Lo reverse-engineers it, so I'm looking for whatever will make my
life the easiest. When you work by yourself, getting stuck on some
stupid problem gets expensive, fast.

Cheers

Mikko OH2HVJ

unread,
Feb 8, 2012, 11:59:45 AM2/8/12
to
Phil Hobbs <pcdhSpamM...@electrooptical.net> writes:

> All you folks with the several $k development systems: What are you
> using, how do you like it, and why?

We're using CodeRed stuff, works great and has reasonable pricing.

--
Mikko OH2HVJ

Nico Coesel

unread,
Feb 8, 2012, 1:12:37 PM2/8/12
to
Phil Hobbs <pcdhSpamM...@electrooptical.net> wrote:

>Nico Coesel wrote:
>>
>> Tim Wescott <t...@seemywebsite.com> wrote:
>>
>> >On Tue, 07 Feb 2012 20:31:23 -0500, Phil Hobbs wrote:
>> >
>> >> I'm looking at starting an angel-funded effort to build some interesting
>> >> small sensor devices, for which I'm going to need to do some Cortex M3
>> >> programming for real. Since that sort of job needs speed more than
>> >> money, I'd like to get a full-function development system with a decent
>> >> JTAG-based debugger, real support, and the whole 012 yards.
>> >>
>> >> All you folks with the several $k development systems: What are you
>> >> using, how do you like it, and why?
>> >
>> >Gnu tool chain, Eclipse IDE, $50 JTAG dongle from SparkFun. It works as
>> >well as many $10K development systems that I've used. Support is a bit
>> >harder to come by, but I've gotten some pretty dreadful support from the
>> >suppliers of those high-dollar tool chains, too.
>>
>> I'd go this route as well. Eclipse beats any IDE I've ever used hands
>> down (including MS Visual Studio).
>>
>
>I hate the Visual Studio editor--any proper programmer's editor should
>be line-oriented, whereas VS's is just like Notepad. Its debugger is
>pretty nice, though--much nicer than any gdb frontend. (It isn't as
>good as the IBM VisualAge debugger by a long shot, but that's crying
>over spilt milk.)

Did you ever use Eclipse? It works nicely as a gdb frontend.

>I'm not a big fan of any of the gnu debuggers, and the Eclipse-based
>1-wire systems I'm most familiar with don't allow setting watchpoints,
>which IMO are pretty vital when doing choreography with multiple
>interrupt levels.

I prefer a logic analyzer and I/O pins for checking that sort of
real-time stuff. There really isn't much use for a debugger on
embedded platforms. I you want to debug code (verify whether it works)
it is much more comfortable to do this on a PC. The nice thing about
Eclipse + gcc is that you can write code to run on a PC and use the
same code on an embedded platform.

>Is the Embedded Trace Macrocell support worth the tool price?
>
>Do the proprietary tools have any significant advantage in code size?
>(Chip pricing seems more sensitive to flash size than to anything else.)

AFAIK the compiler made by ARM is the best for code density. GCC comes
second. Most vendors sell you a GCC compiler these days. Codesourcery
has a free lite version.

>For cost reasons I'm probably going to use the ST ARMs, e.g. the
>STM32L151, which I can get for under $3 in onesies. That's assuming
>that their ADCs and DACs aren't too horrible. I can bandage them with a
>table lookup, provided that they don't have a lot of missing codes.

When I do the math I mostly end up with NXP's ARMs. I did a design
once with an ARM from ST (STR700 series) and its a complete joke
compared to NXP devices. And don't get misguided by the price. NXP
devices can run full speed from flash. ST devices can't.

Don Y

unread,
Feb 8, 2012, 1:14:31 PM2/8/12
to
Hi Phil,

On 2/8/2012 8:50 AM, Phil Hobbs wrote:

> All true. In this case it's all proprietary anyway, at least until Won
> Hung Lo reverse-engineers it, so I'm looking for whatever will make my
> life the easiest. When you work by yourself, getting stuck on some
> stupid problem gets expensive, fast.

But that can also be an *advantage* of self-employment (depending on
time table, etc.): if you're "stuck", do something else (plant a
tree, play with the dogs, etc.) until your mind has a chance to
"reset" (In a 9-to-5, you just beat your head against the wall
until closing time -- and are usually anxious because you'll have
to face the same problem in another 15 hours...)

<shrug>

Tauno Voipio

unread,
Feb 8, 2012, 1:32:47 PM2/8/12
to
On 8.2.12 3:31 , Phil Hobbs wrote:
> I'm looking at starting an angel-funded effort to build some interesting
> small sensor devices, for which I'm going to need to do some Cortex M3
> programming for real. Since that sort of job needs speed more than
> money, I'd like to get a full-function development system with a decent
> JTAG-based debugger, real support, and the whole 012 yards.
>
> All you folks with the several $k development systems: What are you
> using, how do you like it, and why?
>
> Thanks
>
> Phil Hobbs

Get a TI/Stellaris evaluation kit. It comes with
tool chain, and the board can be used as a JTAG
dongle for another board (in addition to program
itself vai USB).

I'm using a LM3S6965 Evaluation Kit. It includes some
buttons, a display and an Ethernet controller.

The good point with an evaluation kit is that you
can start up some software on a known good hardware
platform before going to your own.

--

Tauno Voipio


Don Y

unread,
Feb 8, 2012, 2:19:19 PM2/8/12
to
Hi Nico,

On 2/8/2012 11:12 AM, Nico Coesel wrote:
> Phil Hobbs<pcdhSpamM...@electrooptical.net> wrote:
>
>> Nico Coesel wrote:

>> I'm not a big fan of any of the gnu debuggers, and the Eclipse-based
>> 1-wire systems I'm most familiar with don't allow setting watchpoints,
>> which IMO are pretty vital when doing choreography with multiple
>> interrupt levels.
>
> I prefer a logic analyzer and I/O pins for checking that sort of
> real-time stuff. There really isn't much use for a debugger on
> embedded platforms. I you want to debug code (verify whether it works)
> it is much more comfortable to do this on a PC. The nice thing about
> Eclipse + gcc is that you can write code to run on a PC and use the
> same code on an embedded platform.

That's not often the case. You can get a good idea if the *algorithms*
that you are using will work and iron out many of the bugs. But,
since the code you will ultimately be generating (unless your target
is a PC!) will have different characteristics (code/data sizes,
endian-ness, timing, etc), you can easily be fooled into missing BIG
problems (depending on your level of coding discipline).

"Crap! chars are *signed*, here!"
"Whaddya mean, no MUL opcode?"
"Yikes! Why is it stuck endlessly servicing IRQ's?"
"Ouch! I had no idea stack penetration would be so deep!"
"Ah, every time I tickle this output, this other thing happens..."
etc.

I like embedding a standalone debugger *in* the device that lets me
watch execution of a single thread, peek/poke random memory locations,
etc. Debugging multithreaded applications on a PC is just not
practical when you can't realistically simulate the I/O's (that
are *driving* the various tasks).

[Note that such a debugger need not be feature rich. You aren't
relying on it for *all* of your debugging needs!]

But, if I "kick" a device and it doesn't "scream", I can peek at
the "kick detector" to see if it SAW that kick. Then, poke at
the "scream emitter" to verify that it WILL scream when commanded.
Is there a problem in one or the other? Is the problem in the
communication subsystem? Is the OS not scheduling the proper
tasks? Is a task waiting on some resource that I *thought*
would be available? etc.

These are hard to examine in a desktop "emulation"/simulation.

I'd much prefer talking to a debugger than passively watching
an instruction trace on a logic analyzer (especially when the
trigger event could be in one thread and the "reply of interest"
in another thread thousands of cycles away).

I guess if your coding style is to treat things in "big units",
you can get more done on a desktop. I like to split things into
very fine units for maximal concurrency/sharing. That tends to
make my solutions more dependent on the final platform for
proper execution (e.g., producer and consumer need to co-operate
in order to see real functionality).

Phil Hobbs

unread,
Feb 8, 2012, 2:57:01 PM2/8/12
to
On 02/08/2012 01:12 PM, Nico Coesel wrote:
> Phil Hobbs<pcdhSpamM...@electrooptical.net> wrote:
>
>> Nico Coesel wrote:
>>>
>>> Tim Wescott<t...@seemywebsite.com> wrote:
>>>
>>>> On Tue, 07 Feb 2012 20:31:23 -0500, Phil Hobbs wrote:
>>>>
>>>>> I'm looking at starting an angel-funded effort to build some interesting
>>>>> small sensor devices, for which I'm going to need to do some Cortex M3
>>>>> programming for real. Since that sort of job needs speed more than
>>>>> money, I'd like to get a full-function development system with a decent
>>>>> JTAG-based debugger, real support, and the whole 012 yards.
>>>>>
>>>>> All you folks with the several $k development systems: What are you
>>>>> using, how do you like it, and why?
>>>>
>>>> Gnu tool chain, Eclipse IDE, $50 JTAG dongle from SparkFun. It works as
>>>> well as many $10K development systems that I've used. Support is a bit
>>>> harder to come by, but I've gotten some pretty dreadful support from the
>>>> suppliers of those high-dollar tool chains, too.
>>>
>>> I'd go this route as well. Eclipse beats any IDE I've ever used hands
>>> down (including MS Visual Studio).
>>>
>>
>> I hate the Visual Studio editor--any proper programmer's editor should
>> be line-oriented, whereas VS's is just like Notepad. Its debugger is
>> pretty nice, though--much nicer than any gdb frontend. (It isn't as
>> good as the IBM VisualAge debugger by a long shot, but that's crying
>> over spilt milk.)
>
> Did you ever use Eclipse? It works nicely as a gdb frontend.

I've used Eclipse, I just think it's klunky compared with Visual Studio
or VisualAge. I've begun experimenting with ZeroBugs on Linux, which I
quite like so far. It doesn't use gdb at all.

>
>> I'm not a big fan of any of the gnu debuggers, and the Eclipse-based
>> 1-wire systems I'm most familiar with don't allow setting watchpoints,
>> which IMO are pretty vital when doing choreography with multiple
>> interrupt levels.
>
> I prefer a logic analyzer and I/O pins for checking that sort of
> real-time stuff. There really isn't much use for a debugger on
> embedded platforms. I you want to debug code (verify whether it works)
> it is much more comfortable to do this on a PC. The nice thing about
> Eclipse + gcc is that you can write code to run on a PC and use the
> same code on an embedded platform.

Mainline code, sure. Interrupt-driven code is harder, and I'm
interested in being able to find things like memory corruption bugs that
are hard to do with a logic analyzer unless it's a fancy new one that
can look inside the chip via ETM etc. That probably isn't in the budget.

>
>> Is the Embedded Trace Macrocell support worth the tool price?
>>
>> Do the proprietary tools have any significant advantage in code size?
>> (Chip pricing seems more sensitive to flash size than to anything else.)
>
> AFAIK the compiler made by ARM is the best for code density. GCC comes
> second. Most vendors sell you a GCC compiler these days. Codesourcery
> has a free lite version.

That's good wisdom, thanks. Processor cost seems to be driven mostly by
the size of the flash, so tighter code is worth real money.

>
>> For cost reasons I'm probably going to use the ST ARMs, e.g. the
>> STM32L151, which I can get for under $3 in onesies. That's assuming
>> that their ADCs and DACs aren't too horrible. I can bandage them with a
>> table lookup, provided that they don't have a lot of missing codes.
>
> When I do the math I mostly end up with NXP's ARMs. I did a design
> once with an ARM from ST (STR700 series) and its a complete joke
> compared to NXP devices. And don't get misguided by the price. NXP
> devices can run full speed from flash. ST devices can't.

Hmm, interesting. Thanks for the heads-up about the internal flash
needing a wait state. It does have prefetch, which should help some.

What other things were inferior about it? It does look as though NXP is
better integrated with the lower-priced tools such as Code Red.

The applications I'm looking at really benefit from the two separate
DACs, so I can ping-pong them during data acquisition. I don't think
NXP has that.

I'd like to stay with Code Red if possible, to be able to interoperate
with some colleagues who use it, but their ST support seems pretty thin.

Fred Bartoli

unread,
Feb 8, 2012, 3:41:08 PM2/8/12
to
Phil Hobbs a écrit :
Then have a look at ADuc7023. Four 12b DACs, one 1MSPS 12b ADC with 12
inputs MUX.
At 40MHz it's not a speedy one, but it's $3/1K with real DACs/ADC. Also
provide a bit of PLA which can be useful.


--
Thanks,
Fred.

Nico Coesel

unread,
Feb 8, 2012, 4:00:54 PM2/8/12
to
Don Y <th...@isnotme.com> wrote:

>Hi Nico,
>
>On 2/8/2012 11:12 AM, Nico Coesel wrote:
>> Phil Hobbs<pcdhSpamM...@electrooptical.net> wrote:
>>
>>> Nico Coesel wrote:
>
>>> I'm not a big fan of any of the gnu debuggers, and the Eclipse-based
>>> 1-wire systems I'm most familiar with don't allow setting watchpoints,
>>> which IMO are pretty vital when doing choreography with multiple
>>> interrupt levels.
>>
>> I prefer a logic analyzer and I/O pins for checking that sort of
>> real-time stuff. There really isn't much use for a debugger on
>> embedded platforms. I you want to debug code (verify whether it works)
>> it is much more comfortable to do this on a PC. The nice thing about
>> Eclipse + gcc is that you can write code to run on a PC and use the
>> same code on an embedded platform.
>
>That's not often the case. You can get a good idea if the *algorithms*
>that you are using will work and iron out many of the bugs. But,
>since the code you will ultimately be generating (unless your target
>is a PC!) will have different characteristics (code/data sizes,
>endian-ness, timing, etc), you can easily be fooled into missing BIG
>problems (depending on your level of coding discipline).
>
>"Crap! chars are *signed*, here!"

We are talking about 32 bit platforms.

>"Whaddya mean, no MUL opcode?"

RTFM

>"Yikes! Why is it stuck endlessly servicing IRQ's?"
>"Ouch! I had no idea stack penetration would be so deep!"

There are tools for analysing stack depth before deploying code. Its
quite bad if you don't think about that before starting on the
software.

>"Ah, every time I tickle this output, this other thing happens..."
>etc.

Learn how to program :-) Often it takes a good review of your code to
figure out why things don't work.

>I like embedding a standalone debugger *in* the device that lets me
>watch execution of a single thread, peek/poke random memory locations,
>etc. Debugging multithreaded applications on a PC is just not
>practical when you can't realistically simulate the I/O's (that
>are *driving* the various tasks).

Usually the layer that actually does I/O is very small. What goes up
to the algorithms is easely simulated on a PC. Time doesn't matter in
the digital domain. Once you get the algorithms right you can see if
it works in a controller and optimise if necessary.

A few years ago I developed an echo canceller for a client. I started
with getting the algorithm to work properly on a PC. That gave me a
known good implementation. Then I started to optimise so it would meet
the timing requirement. The first floating point version was about 15
times too slow. The final (mostly fixed point) version was fast enough
and still had the same quality as the initial version.

Phil Hobbs

unread,
Feb 8, 2012, 4:04:55 PM2/8/12
to
Nice part, thanks. Blows the budget, unfortunately, particularly when I
can get most of that inside the uC with a bit of bandaging.

lang...@fonz.dk

unread,
Feb 8, 2012, 3:49:57 PM2/8/12
to
On 8 Feb., 19:12, n...@puntnl.niks (Nico Coesel) wrote:
STR7 is old school ARM7TDMI not cortex (I know NXP also had
acceleration on ARM7TDMI)

STM32L1 doesn't have a flash accelerator but it can read flash in
64bit
words and is really meant for low power not speed

STM32F2/F4 have flash accelerators like NXP, and run full speed
from flash

-Lasse

lang...@fonz.dk

unread,
Feb 8, 2012, 4:06:08 PM2/8/12
to
On 8 Feb., 02:31, Phil Hobbs <pcdhSpamMeSensel...@electrooptical.net>
wrote:
> I'm looking at starting an angel-funded effort to build some interesting
> small sensor devices, for which I'm going to need to do some Cortex M3
> programming for real.   Since that sort of job needs speed more than
> money, I'd like to get a full-function development system with a decent
> JTAG-based debugger, real support, and the whole 012 yards.
>
> All you folks with the several $k development systems: What are you
> using, how do you like it, and why?
>

when I was involved in ARM development at a big company we used
codewarriors
compiler, and a Lauterbach TRACE32 jtag debugger it is expensive but
very fast
and the user interface can do so many things and be programmed to do
more than
you can imagine

We also had their tracer that logs program flow so you can basically
rewind
and replay what happen

but it massive overkill unless you are doing something very
complicated, I know
of tons of code that has be programmed and debugged with nothing but a
serial port
and a software debugger

With the current Cortexs that have rom bootloaders for usb/rs232/can,
etc.
all you really need is a computer and GCC

-Lasse

Don Y

unread,
Feb 8, 2012, 4:27:03 PM2/8/12
to
Hi Nico,

On 2/8/2012 2:00 PM, Nico Coesel wrote:
> Don Y<th...@isnotme.com> wrote:

>>>> I'm not a big fan of any of the gnu debuggers, and the Eclipse-based
>>>> 1-wire systems I'm most familiar with don't allow setting watchpoints,
>>>> which IMO are pretty vital when doing choreography with multiple
>>>> interrupt levels.
>>>
>>> I prefer a logic analyzer and I/O pins for checking that sort of
>>> real-time stuff. There really isn't much use for a debugger on
>>> embedded platforms. I you want to debug code (verify whether it works)
>>> it is much more comfortable to do this on a PC. The nice thing about
>>> Eclipse + gcc is that you can write code to run on a PC and use the
>>> same code on an embedded platform.
>>
>> That's not often the case. You can get a good idea if the *algorithms*
>> that you are using will work and iron out many of the bugs. But,
>> since the code you will ultimately be generating (unless your target
>> is a PC!) will have different characteristics (code/data sizes,
>> endian-ness, timing, etc), you can easily be fooled into missing BIG
>> problems (depending on your level of coding discipline).
>>
>> "Crap! chars are *signed*, here!"
>
> We are talking about 32 bit platforms.

Signedness of chars is independent of machine width.

>> "Whaddya mean, no MUL opcode?"
>
> RTFM

I guess you've never been tasked with writing software for
a hardware platform that hasn't yet been defined? :>

>> "Yikes! Why is it stuck endlessly servicing IRQ's?"
>> "Ouch! I had no idea stack penetration would be so deep!"
>
> There are tools for analysing stack depth before deploying code. Its
> quite bad if you don't think about that before starting on the
> software.

Do you know what the actual distribution of IRQ's will be on
your platform? Esp when (as above) the hardware might not
exist at the time you develop the code? Do you know what
level of support for floats will be provided in the hardware
vs. "helper routines"?

Moving to actual iron is rarely a case of just changing the
name of the compiler invoked on the command line.

>> "Ah, every time I tickle this output, this other thing happens..."
>> etc.
>
> Learn how to program :-) Often it takes a good review of your code to
> figure out why things don't work.

"Perfect code" won't help you if an output is directly/indirectly
routed to an IRQ (either by mis-design or a layout problem). You
can simulate it all day on a PC and never *expect* it.

>> I like embedding a standalone debugger *in* the device that lets me
>> watch execution of a single thread, peek/poke random memory locations,
>> etc. Debugging multithreaded applications on a PC is just not
>> practical when you can't realistically simulate the I/O's (that
>> are *driving* the various tasks).
>
> Usually the layer that actually does I/O is very small. What goes up
> to the algorithms is easely simulated on a PC. Time doesn't matter in
> the digital domain.

Don't do much RT work, eh? :>

As I said previously, a lot depends on your coding style.
Asked to write a task to copy a file, most folks would
implement this in a single thread using pseudo-synchronous
I/O:

while (source not empty) {
buffer <- read(source)
buffer -> write(sink)
}

*I* would write it as three threads:

thread1: parse source and sink specifiers, signal errors
setup source and sink devices (files, pipes, etc.)
allocate buffer sized per needs of device types
create producer and consumer threads
wait for them to finish
interact with user to report status, as required
handle signals to release resources, if needed

thread2: wait for space in buffer
buffer <- read(source)
lather, rinse, repeat

thread3: wait for data in buffer
buffer -> write(sink)
lather, rinse, repeat

[note that the single threaded example omits many of these
issues which would still need to be addressed -- in "spaghetti
code"]

My solution is harder to emulate "well" on a PC. Do you
have support for threading? Can the device interfaces be
emulated accurately? How much time will it take for me
to design the emulation environment? How many bugs will
*it* have?

But, mine is a lot easier to get right "first time" (when you
look at *all* of the "practical" issues that arise).

If you constrain yourself to working on the PC, you limit
the types of solutions you can reasonably pursue.

lang...@fonz.dk

unread,
Feb 8, 2012, 6:02:23 PM2/8/12
to
On 8 Feb., 22:27, Don Y <t...@isnotme.com> wrote:
> Hi Nico,
>
> On 2/8/2012 2:00 PM, Nico Coesel wrote:
>
>
>
>
>
>
>
>
>
> > Don Y<t...@isnotme.com>  wrote:
if that is how you would solve a simple task in an embedded system,
I'm beginning to understand why MCUs now come with megabytes of flash
and runs a 100's of MHz...

-Lasse

Don Y

unread,
Feb 8, 2012, 6:25:55 PM2/8/12
to
On 2/8/2012 4:02 PM, lang...@fonz.dk wrote:

> if that is how you would solve a simple task in an embedded system,
> I'm beginning to understand why MCUs now come with megabytes of flash
> and runs a 100's of MHz...

It's a simple task only if you are willing to accept "simple
solutions"! :>

What happens if the source or sink stalls "indefinitely"?
I.e., a disk that won't spin up? A serial port that has been
"paced off"? etc. How do you *kill* the task? Or, do you let
it hold those resources indefinitely? (cycle power? reboot?
gee, that's an elegant solution! :> )

What happens if/when the user REPEATS the request? Do you now
have *two* instances running -- exhibiting the same problem? Can
your design even *tolerate* two instances of the same task??
(i.e., have you statically allocated a *single* copy buffer in
your design and now see that buffer as "in use") Or, does it
manifest to the user as a different problem (e.g., you've run out
of resources)

How do you tell the user what is happening -- since the
consumer/producer (which the simple implementation fits into
that single consumer-producer thread) can be blocking waiting
for data/space? How do you *ask* the task what is happening?

Do you code the algorithm to handle large block devices and
assign resources on that scale, regardless of the needs of
the actual devices involved in the operation? Does that
then limit the number of concurrent instances of this task
that you can support -- even if the devices in those
instances are serial ports (that don't need to buffer huge
amounts of data)?

Do you write (and maintain) different versions of the same
(conceptually) task to handle those different cases?

Decomposing tasks almost always makes things easier and more
efficient -- unless you are willing to live with simple
solutions and the problems that inevitably follow, "in practice".

Do you think your iPhone has one giant "loop" that runs
continuously?

Embedded systems use more complex hardware because embedded
systems now do a lot more than they used to. Often, more than
*desktop* systems!

Nico Coesel

unread,
Feb 8, 2012, 6:28:38 PM2/8/12
to
Don Y <th...@isnotme.com> wrote:

>Hi Nico,
>
>On 2/8/2012 2:00 PM, Nico Coesel wrote:
>> Don Y<th...@isnotme.com> wrote:
>
>>>>> I'm not a big fan of any of the gnu debuggers, and the Eclipse-based
>>>>> 1-wire systems I'm most familiar with don't allow setting watchpoints,
>>>>> which IMO are pretty vital when doing choreography with multiple
>>>>> interrupt levels.
>>>>
>>>> I prefer a logic analyzer and I/O pins for checking that sort of
>>>> real-time stuff. There really isn't much use for a debugger on
>>>> embedded platforms. I you want to debug code (verify whether it works)
>>>> it is much more comfortable to do this on a PC. The nice thing about
>>>> Eclipse + gcc is that you can write code to run on a PC and use the
>>>> same code on an embedded platform.
>>>
>>> That's not often the case. You can get a good idea if the *algorithms*
>>> that you are using will work and iron out many of the bugs. But,
>>> since the code you will ultimately be generating (unless your target
>>> is a PC!) will have different characteristics (code/data sizes,
>>> endian-ness, timing, etc), you can easily be fooled into missing BIG
>>> problems (depending on your level of coding discipline).
>>>
>>> "Crap! chars are *signed*, here!"
>>
>> We are talking about 32 bit platforms.
>
>Signedness of chars is independent of machine width.

I've never come across unsigned chars in the past 15 years.

>>> "Whaddya mean, no MUL opcode?"
>>
>> RTFM
>
>I guess you've never been tasked with writing software for
>a hardware platform that hasn't yet been defined? :>

I port a lot. Does that count?

>> Learn how to program :-) Often it takes a good review of your code to
>> figure out why things don't work.
>
>"Perfect code" won't help you if an output is directly/indirectly
>routed to an IRQ (either by mis-design or a layout problem). You
>can simulate it all day on a PC and never *expect* it.

Perhaps but since it behaves unlike the reference implementation
you'll quickly notice

>>> I like embedding a standalone debugger *in* the device that lets me
>>> watch execution of a single thread, peek/poke random memory locations,
>>> etc. Debugging multithreaded applications on a PC is just not
>>> practical when you can't realistically simulate the I/O's (that
>>> are *driving* the various tasks).
>>
>> Usually the layer that actually does I/O is very small. What goes up
>> to the algorithms is easely simulated on a PC. Time doesn't matter in
>> the digital domain.
>
>Don't do much RT work, eh? :>

I do lots of it.

>As I said previously, a lot depends on your coding style.
>Asked to write a task to copy a file, most folks would
>implement this in a single thread using pseudo-synchronous
>I/O:
>
> while (source not empty) {
> buffer <- read(source)
> buffer -> write(sink)
> }
>
>*I* would write it as three threads:
>
>thread1: parse source and sink specifiers, signal errors
> setup source and sink devices (files, pipes, etc.)
> allocate buffer sized per needs of device types
> create producer and consumer threads
> wait for them to finish
> interact with user to report status, as required
> handle signals to release resources, if needed
>
>thread2: wait for space in buffer
> buffer <- read(source)
> lather, rinse, repeat
>
>thread3: wait for data in buffer
> buffer -> write(sink)
> lather, rinse, repeat

I would never ever go multi-threaded on a microcontroller. Too much
obfustication and overhead. I use consecutive non-blocking tasks which
are extremely predictable regarding performance and stack usage. Don't
make things more complicated than they need to be.

Nico Coesel

unread,
Feb 8, 2012, 7:13:24 PM2/8/12
to
Finding memory corruption bugs is hard to do on an embedded platform.
Better be ahead of them. I add plenty of boundary checks and pointer
validations. There are tools like Valgrind to find leaks, boundary
errors, etc on a PC platform.

A nice feature of ARM devices is that they have exceptions. So when
you run into a pointer trying to write to the flash or read where
there is no memory you'll get a notification. In debug builds I let
the software print a stack dump and a small backtrace. All of this
does take some inline assembler code. In release builds I just let an
exception reset the device. The Cortex devices also offer a division
by zero exception.

>>> Is the Embedded Trace Macrocell support worth the tool price?
>>>
>>> Do the proprietary tools have any significant advantage in code size?
>>> (Chip pricing seems more sensitive to flash size than to anything else.)
>>
>> AFAIK the compiler made by ARM is the best for code density. GCC comes
>> second. Most vendors sell you a GCC compiler these days. Codesourcery
>> has a free lite version.
>
>That's good wisdom, thanks. Processor cost seems to be driven mostly by
>the size of the flash, so tighter code is worth real money.

Depends on the number of units you intend to ship. Succesfull niche
market products allow for customer specific solutions which may need
more memory than expected.

>>> For cost reasons I'm probably going to use the ST ARMs, e.g. the
>>> STM32L151, which I can get for under $3 in onesies. That's assuming
>>> that their ADCs and DACs aren't too horrible. I can bandage them with a
>>> table lookup, provided that they don't have a lot of missing codes.
>>
>> When I do the math I mostly end up with NXP's ARMs. I did a design
>> once with an ARM from ST (STR700 series) and its a complete joke
>> compared to NXP devices. And don't get misguided by the price. NXP
>> devices can run full speed from flash. ST devices can't.
>
>Hmm, interesting. Thanks for the heads-up about the internal flash
>needing a wait state. It does have prefetch, which should help some.
>
>What other things were inferior about it? It does look as though NXP is

In the STR700 everything is mediocre.

Phil Hobbs

unread,
Feb 8, 2012, 7:29:56 PM2/8/12
to
For the same package size and MCU series, all the chips are
pin-compatible, so I can just specify the next larger size for that
customer. They'd be paying a premium anyway. The things I'm looking at
doing are cost-sensitive, moderately high volume things--maybe 100k
units per year if all goes well. A buck extra on the processor gets to
be important money at that point, so paying $10k for the fancy Keil
system might make good sense. (Assuming I have some outside development
money.) The Code Red ones are only $1100 or so including a JTAG module,
but they don't support the embedded trace module. They told me they
were developing it, and that it was due to ship Real Soon Now
^H^H^H^H^H^H^H^H^H^H^H later this year. Assuming it doesn't go up by a
factor of 10, Code Red plus one of those Seeger JTrace gizmos looks
about right. I might get an STM32 demo board and try the demo version
of Code Red on it. I really don't want to be stuck with just one
vendor's chips.

Martin Riddle

unread,
Feb 8, 2012, 7:59:12 PM2/8/12
to

"Phil Hobbs" <pcdhSpamM...@electrooptical.net> wrote in message
news:4F31D06B...@electrooptical.net...
> I'm looking at starting an angel-funded effort to build some
> interesting
> small sensor devices, for which I'm going to need to do some Cortex M3
> programming for real. Since that sort of job needs speed more than
> money, I'd like to get a full-function development system with a
> decent
> JTAG-based debugger, real support, and the whole 012 yards.
>
> All you folks with the several $k development systems: What are you
> using, how do you like it, and why?
>
> Thanks
>
> Phil Hobbs
> --
> Dr Philip C D Hobbs
> Principal Consultant
> ElectroOptical Innovations LLC
> Optics, Electro-optics, Photonics, Analog Electronics
>
> 160 North State Road #203
> Briarcliff Manor NY 10510
> 845-480-2058
>
> hobbs at electrooptical dot net
> http://electrooptical.net

The Codered stuff look good. The lite version has a limited code
capability.
If that works for you then great.

Keil is a little pricey, but judging from their old 8051 compiler, you
wont find many bugs.

As for Open stuff, theres DS-5 (for Linux) from the ARM site. And the
coocox tools look promising www.coocox.org


Cheers


Don Y

unread,
Feb 8, 2012, 8:04:35 PM2/8/12
to
Hi Nico,
Um, *Arm* (isn't that what this thread was about? :> ). MIPS.

>>>> I like embedding a standalone debugger *in* the device that lets me
>>>> watch execution of a single thread, peek/poke random memory locations,
>>>> etc. Debugging multithreaded applications on a PC is just not
>>>> practical when you can't realistically simulate the I/O's (that
>>>> are *driving* the various tasks).
>>>
>>> Usually the layer that actually does I/O is very small. What goes up
>>> to the algorithms is easely simulated on a PC. Time doesn't matter in
>>> the digital domain.
>>
>> Don't do much RT work, eh? :>
>
> I do lots of it.

Then you know that time *does* matter in the digital domain!
Actually, well written multitasking programs are smaller,
simpler and more robust than equivalent single-threaded
programs. Otherwise, you end up having to do lots of
spin-waiting and checking unrelated "things" while you
are busy with something else (or, worse yet, push all of
those things into ISR's, needlessly).

*Think* about how you would write that file copy task
to handle the real-world issues that I mentioned. You'll
end up with a real mess of spaghetti code -- assuming you
don't ALSO have to do "other things" while copying!

[I'm serious, here! Are you going to put timeouts on
the non-blocking calls? What's keeping track of time
for you? Can the output device work *while* the input
device is working? Or, does one stall while you service
the other? Do you embed code in your spin-wait loops
to poll the user to see what he might want? Or, to
tell him what's happening? etc.]

I wouldn't consider writing a NON-multitasking application
(unless it was a trivial "high school project").

Nico Coesel

unread,
Feb 8, 2012, 9:46:01 PM2/8/12
to
Don Y <th...@isnotme.com> wrote:

>Hi Nico,
>
>
>>>>> I like embedding a standalone debugger *in* the device that lets me
>>>>> watch execution of a single thread, peek/poke random memory locations,
>>>>> etc. Debugging multithreaded applications on a PC is just not
>>>>> practical when you can't realistically simulate the I/O's (that
>>>>> are *driving* the various tasks).
>>>>
>>>> Usually the layer that actually does I/O is very small. What goes up
>>>> to the algorithms is easely simulated on a PC. Time doesn't matter in
>>>> the digital domain.
>>>
>>> Don't do much RT work, eh? :>
>>
>> I do lots of it.
>
>Then you know that time *does* matter in the digital domain!

Thats timing. What I mean is that it doesn't matter how fast (or slow)
a PC processes input/output data for an algorithm. You don't need a
realtime 'simulation' when developing algorithms.
A hate to burst your bubble but whenever multithreading comes into
play things get difficult. Even for very experienced programmers. In
most cases multi-threading works on a single CPU because of luck. I've
seen too many programs crash and burn when the multi-core PC cpu's
came onto the market.

In your example you have 3 seperate pieces of code that work closely
together on one task. What if thread 3 gets a timeslice before thread
2? In that case you'll always have excess buffer space filled (and
memory wasted) . You more or less have to make sure thread 2 is always
activated before thread 3 so the buffer space filled in thread 2 has a
big chance getting flushed in thread 3 immediately. And what if thread
3 doesn't get enough timeslices?

Not to mention chances of deadlocks.

>programs. Otherwise, you end up having to do lots of
>spin-waiting and checking unrelated "things" while you
>are busy with something else (or, worse yet, push all of
>those things into ISR's, needlessly).

Nope. I'd write it like this:

run_copy
{
if (data in source and buffer is not full) -> copy from source
if (output ready and buffer not empty) -> write to output
}

The function run_copy is called continuously from a main loop in a
list of several run_do_something functions. Actually these functions
can be considered threads. They don't run in parallel but in series
which doesn't matter for execution time because you only have so much
CPU time to work with.

The biggest advantage is that the whole system is more predictable and
you can use (almost) all of the stack for each task.

>[I'm serious, here! Are you going to put timeouts on
>the non-blocking calls? What's keeping track of time
>for you? Can the output device work *while* the input
>device is working?

Ofcourse. As soon as there is nothing more to be done for now, the
non-blocking function (task) exits. Thats the whole point of a
non-blocking function!

>Or, does one stall while you service
>the other? Do you embed code in your spin-wait loops
>to poll the user to see what he might want? Or, to
>tell him what's happening? etc.]

No. If you write all the tasks as non-blocking functions you have no
problems with spin locks. User interaction is just another task in the
sequential list of tasks.

I've worked (with others) on huge projects (producing >700kB binaries)
that operate this way for timing critical systems. Writing
non-blocking tasks requires a certain state of mind though. Once you
get used to it, it makes life a whole lot simpler.

Don Y

unread,
Feb 8, 2012, 11:16:50 PM2/8/12
to
Hi Nico,

On 2/8/2012 7:46 PM, Nico Coesel wrote:
> Don Y<th...@isnotme.com> wrote:
>
>> Hi Nico,
>>
>>
>>>>>> I like embedding a standalone debugger *in* the device that lets me
>>>>>> watch execution of a single thread, peek/poke random memory locations,
>>>>>> etc. Debugging multithreaded applications on a PC is just not
>>>>>> practical when you can't realistically simulate the I/O's (that
>>>>>> are *driving* the various tasks).
>>>>>
>>>>> Usually the layer that actually does I/O is very small. What goes up
>>>>> to the algorithms is easely simulated on a PC. Time doesn't matter in
>>>>> the digital domain.
>>>>
>>>> Don't do much RT work, eh? :>
>>>
>>> I do lots of it.
>>
>> Then you know that time *does* matter in the digital domain!
>
> Thats timing. What I mean is that it doesn't matter how fast (or slow)
> a PC processes input/output data for an algorithm. You don't need a
> realtime 'simulation' when developing algorithms.

In RT designs, time *is* a factor in the correctness of the algorithm.
That is the essence of RT.
MultiPROCESSING is different from multiTHREADING. It is relatively easy
to write a safe multithreaded program by following simple rules.

People who can't just haven't learned *how*.

> In your example you have 3 seperate pieces of code that work closely
> together on one task. What if thread 3 gets a timeslice before thread

So what? If there is no data in the buffer, it yield's the
processor. If there *is* data, it processes it. That's what it is
SUPPOSED to do.

If thread 2 gets a timeslice before thread 3, then it will read
in more data IF THERE IS SPACE IN THE BUFFER. Otherwise, it will yield
the processor (assuming the OS doesn't support event notifications).

Each of the two worker threads concentrates on just one aspect of
the task.

> 2? In that case you'll always have excess buffer space filled (and

No, the example you cited would cause the buffer to be "more empty"
(the consumer is running)

> memory wasted) .

Whether the buffer is 90% full or 9% full, the same amount of memory
is used by the buffer.

In a single threaded design, you would completely *fill* the buffer
(is that "wasted memory"?) and then completely EMPTY it (so the memory
is sitting there, idle -- is THAT wasted memory?)

> You more or less have to make sure thread 2 is always
> activated before thread 3 so the buffer space filled in thread 2 has a
> big chance getting flushed in thread 3 immediately.

You don't understand how multitasking works. Thread 2 runs as often
as there is space available in the buffer. Thread 3 runs as often as
there is data in the buffer.

[Note that I did not say *when* there is/isn't data -- just "averages"]

Thread 3 can be activated 10,000 times for every *one* time
thread 2 is activated. Doesn't affect the algorithm's correctness.

Imagine thread 3 services a 110 baud UART and thread 2 services
a modern disk drive. Disk fills the buffer via thread 2 in *one*
invocation of that thread (for modest size buffers). Thread 2
can't do anything until there is space in the buffer. That relies on
thread 3 pulling data OUT of the buffer.

But, at 110 baud, it takes 100ms just to move *one* byte out of the
buffer. To move a full sector out of the buffer (assuming you are using
the block device for the disk) will take 50 seconds. *Then*, there is
enough room for thread 2 to put another sector worth of data into
the buffer. And, immediately start waiting -- for another 50 seconds.

> And what if thread 3 doesn't get enough timeslices?

Thread 2 won't *do* anything more unless thread 3 empties out space
in the buffer. The multithreaded approach allows for more parallelism.
I.e., device I/O associated with thread 2 can happen while thread 3 is
running (and vice versa)

What if your single threaded implementation doesn't get enough
timeslices (time)?

> Not to mention chances of deadlocks.

Where is the chance for deadlock? There is a single shared resource,
the buffer. Since only one owner can hold that resource, it can't
be deadlocked by a dependence on another resource -- no "deadly
EMBRACE" (since there is no other "party" holding a resource).

With two or more resources, you just make sure you take all of
the resources that you need in a fixed order -- so that anyone
else taking that same set of resources takes them in the
same order (which means, if you managed to acquire resource
X before the other task, then he will have to wait for X and
won't have a chance of taking *Y* -- which you also need -- before
you get it).

It's just a matter of programming discipline. Like making sure
you POP as many things as you PUSH.

>> programs. Otherwise, you end up having to do lots of
>> spin-waiting and checking unrelated "things" while you
>> are busy with something else (or, worse yet, push all of
>> those things into ISR's, needlessly).
>
> Nope. I'd write it like this:
>
> run_copy
> {
> if (data in source and buffer is not full) -> copy from source

*One* byte at a time? What if the next byte isn't ready yet?
(e.g., imagine pulling bytes out of a UART receiver or pushing
them into a Tx register)

> if (output ready and buffer not empty) -> write to output
> }
>
> The function run_copy is called continuously from a main loop in a
> list of several run_do_something functions. Actually these functions
> can be considered threads. They don't run in parallel but in series
> which doesn't matter for execution time because you only have so much
> CPU time to work with.

The multitasking executive formalizes your "big loop".

You have:
while (FOREVER) {
if (enable_run_copy) { run_copy() };
if (enable_run_GUI) { run_GUI() };
if (enable_run_PID) { run_PID() };
...
}

(since you don't want to run_whatever if "whatever" doesn't
need to be done!)

And, when you want to have a second instance of run_copy?

while(FoREVER) {
if (enable_run_copy1) { run_copy1() };
if (enable_run_copy2) { run_copy2() };
if (enable_run_GUI) { run_GUI() };
if (enable_run_PID) { run_PID() };
...
}

Of course, making sure that run_copy2 uses a different buffer than
run_copy1, different I/O descriptors, etc.

You also have to keep track of each run_whatever's state *in* that
routine. E.g., if you can't finish *all* of run_whatever in a
single invocation, you have to store information in the routine
(static variables) that allow the routine to remember where it was
and restart from that point.

E.g., if you are printing 30 address labels, you don't want to
tie up the whole CPU waiting for all 30 to be printed. So, you
have to remember which label you printed last and how *much*
of that label you managed to spit out to the printer before
the printer signalled "busy" and you decided not to wait
(because that would tie up the CPU at the expense of other tasks)

> The biggest advantage is that the whole system is more predictable and
> you can use (almost) all of the stack for each task.

I worked for a company that had a very efficient multitasking executive
that worked exactly that way. Task switch was 7us on a Z80 (i.e.,
a handful of opcodes!). It is a LOT harder to code in that
environment. Tuning the system is hit or miss as you add
"tasks":

while(FoREVER) {
if (enable_run_copy1) { run_copy1() };
if (enable_run_copy2) { run_copy2() };
if (enable_run_GUI) { run_GUI() };
if (enable_run_PID) { run_PID() };
if (enable_run_motor1) { run_motor1() };
if (enable_run_motor2) { run_motor2() };
if (enable_run_barcode){ run_barcode() };
if (enable_printer) { run_printer() };
if (enable_run_scanner){ run_scanner() };
if (enable_run_comm1) { run_comm1() };
if (enable_run_comm2) { run_comm2() };
if (enable_run_power) { run_power() };
...
}

Ooops! We're missing some barcode scans. Need to cut the time between
run_barcode() invocations:

while(FoREVER) {
if (enable_run_barcode){ run_barcode() };
if (enable_run_copy1) { run_copy1() };
if (enable_run_copy2) { run_copy2() };
if (enable_run_GUI) { run_GUI() };
if (enable_run_PID) { run_PID() };
if (enable_run_motor1) { run_motor1() };
if (enable_run_motor2) { run_motor2() };
if (enable_run_barcode){ run_barcode() };
if (enable_printer) { run_printer() };
if (enable_run_scanner){ run_scanner() };
if (enable_run_comm1) { run_comm1() };
if (enable_run_comm2) { run_comm2() };
if (enable_run_power) { run_power() };
...
}

Ooops! Labels are taking forever to come off the printer.
Need to give run_printer more CPU time:

while(FoREVER) {
if (enable_run_barcode){ run_barcode() };
if (enable_run_copy1) { run_copy1() };
if (enable_run_copy2) { run_copy2() };
if (enable_run_GUI) { run_GUI() };
if (enable_run_PID) { run_PID() };
if (enable_run_motor1) { run_motor1() };
if (enable_run_motor2) { run_motor2() };
if (enable_run_barcode){ run_barcode() };
if (enable_printer) { run_printer() };
if (enable_printer) { run_printer() };
if (enable_printer) { run_printer() };
if (enable_printer) { run_printer() };
if (enable_run_scanner){ run_scanner() };
if (enable_run_comm1) { run_comm1() };
if (enable_run_comm2) { run_comm2() };
if (enable_run_power) { run_power() };
...
}

Etc.

I've got a paper I wrote on the technique. I've used it
on some small PICs but its ancient technology from a time
when processors were considerably more crippled (like PICs!)
(though I know many products that were designed with this
system under-the-hood that are still in use, today!)

>> [I'm serious, here! Are you going to put timeouts on
>> the non-blocking calls? What's keeping track of time
>> for you? Can the output device work *while* the input
>> device is working?
>
> Ofcourse. As soon as there is nothing more to be done for now, the
> non-blocking function (task) exits. Thats the whole point of a
> non-blocking function!
>
>> Or, does one stall while you service
>> the other? Do you embed code in your spin-wait loops
>> to poll the user to see what he might want? Or, to
>> tell him what's happening? etc.]
>
> No. If you write all the tasks as non-blocking functions you have no
> problems with spin locks. User interaction is just another task in the
> sequential list of tasks.

You're spinning around the main loop. Same difference.

User is prompted for an ID number. You don't want to block
waiting for the slow human. So, run_GUI has to *remember* that
it is "waiting for an ID number" -- so that the next time
around the main loop, you don't start the "run_GUI" task
at the beginning but, instead, resume *in* the "get_ID_number"
portion of the task.

[Is this a function? If so, it has stuff piled on the stack
so you can't *just* get back to this point. Instead, that
"function" has to return -- so run_GUI() can return -- but
remember that *it* should receive control of the processor
the next time run_GUI() is invoked.]

Once a digit has been typed in, you will eventually process it.
Then, "remember" that you have accepted one digit -- and are
still waiting for more (or ENTER).

Once the ID is complete AND entered, get_ID_number is complete
and run_GUI can move on to the next step -- looking up the
name associated with that ID number -- which it will then
print on the printer, etc.

In the multitasking approach, you just write the code that
you want to run without worrying about keeping track of
where you have to "get back to". You have a virtual processor
at your disposal.

> I've worked (with others) on huge projects (producing>700kB binaries)
> that operate this way for timing critical systems. Writing
> non-blocking tasks requires a certain state of mind though. Once you
> get used to it, it makes life a whole lot simpler.

You can't provide tight controls on timing because timing changes
each time you change the contents of your main loop. Or, the
code in one of those functions (what happens in run_compute_pi?
how many digits do you compute before you arbitrarily decide
to let some other task have a chance at the CPU? Is the time
required for each digit constant? If it varies, then the
time in any particular instance of run_compute_pi can vary. This
affects *when* the other run_whatever routines are invoked, etc.

So, time critical things move into ISRs -- which makes the
application brittle. Or, you get stuck in this juggling
of "if () {run()}" invocations.

With a multitasking design, a task *can* block taking no
temporal resources. It need not do anything out-of-the-ordinary
to remember what it was doing when most recently active. It
just sits *in* the statement that it was executing at the
time of the task switch.

It concentrates on the task at hand instead of accommodating
other tasks via a particular set of switching mechanisms.

Don Y

unread,
Feb 9, 2012, 12:36:31 AM2/9/12
to
Hi Nico,

On 2/8/2012 9:16 PM, Don Y wrote:

> I've got a paper I wrote on the technique. I've used it
> on some small PICs but its ancient technology from a time
> when processors were considerably more crippled (like PICs!)
> (though I know many products that were designed with this
> system under-the-hood that are still in use, today!)

I *think* you can find this at:
<http://www.mediafire.com/?a3h3qtrk446608f>
My first time using that service so I welcome feedback if
you encounter any problems fetching it (I can't make it available
from any of my servers, directly)

Note the example is in ASM but the concepts remain the same
(i.e., no saved state, etc.) Coding in a HLL means that
you have to exit ALL functions before moving to the next task
(as that would violate stack protocol)

Jan Panteltje

unread,
Feb 9, 2012, 5:25:33 AM2/9/12
to
On a sunny day (Wed, 08 Feb 2012 23:28:38 GMT) it happened ni...@puntnl.niks
(Nico Coesel) wrote in <4f330025....@news.kpn.nl>:

>I've never come across unsigned chars in the past 15 years.

Ever use Linux?
Most video buffers are unsigned char*,
and compiling other peoples code generates trillions of gcc warnings
of assigning a signed char to an unsigned...
Did you write that?
:-)
??
LOL

I freaking had to fix every one of them to get clean compiles.


Nico Coesel

unread,
Feb 9, 2012, 5:47:16 AM2/9/12
to
Jan Panteltje <pNaonSt...@yahoo.com> wrote:

>On a sunny day (Wed, 08 Feb 2012 23:28:38 GMT) it happened ni...@puntnl.niks
>(Nico Coesel) wrote in <4f330025....@news.kpn.nl>:
>
>>I've never come across unsigned chars in the past 15 years.
>
>Ever use Linux?
>Most video buffers are unsigned char*,
>and compiling other peoples code generates trillions of gcc warnings
>of assigning a signed char to an unsigned...
>Did you write that?

You're missing the point. We are talking about systems where a char
type is unsigned. Not 'unsigned char' but a char without sign.

Nico Coesel

unread,
Feb 9, 2012, 6:29:00 AM2/9/12
to
Don Y <th...@isnotme.com> wrote:

>Hi Nico,
>
>On 2/8/2012 7:46 PM, Nico Coesel wrote:
>> Don Y<th...@isnotme.com> wrote:
>>
>>> Hi Nico,
>>>
>>>
>>>>>>> I like embedding a standalone debugger *in* the device that lets me
>
>> memory wasted) .
>
>Whether the buffer is 90% full or 9% full, the same amount of memory
>is used by the buffer.

Never used dynamic buffers I assume? Been there done that.

>
>> Not to mention chances of deadlocks.
>
>Where is the chance for deadlock? There is a single shared resource,
>the buffer. Since only one owner can hold that resource, it can't
>be deadlocked by a dependence on another resource -- no "deadly
>EMBRACE" (since there is no other "party" holding a resource).

When systems get more complex.


>>> programs. Otherwise, you end up having to do lots of
>>> spin-waiting and checking unrelated "things" while you
>>> are busy with something else (or, worse yet, push all of
>>> those things into ISR's, needlessly).
>>
>> Nope. I'd write it like this:
>>
>> run_copy
>> {
>> if (data in source and buffer is not full) -> copy from source
>
>*One* byte at a time? What if the next byte isn't ready yet?

Where do I say one byte at a time?

>> if (output ready and buffer not empty) -> write to output
>> }
>>
>> The function run_copy is called continuously from a main loop in a
>> list of several run_do_something functions. Actually these functions
>> can be considered threads. They don't run in parallel but in series
>> which doesn't matter for execution time because you only have so much
>> CPU time to work with.
>
>The multitasking executive formalizes your "big loop".
>
>You have:
>while (FOREVER) {
> if (enable_run_copy) { run_copy() };
> if (enable_run_GUI) { run_GUI() };
> if (enable_run_PID) { run_PID() };
> ...
>}
>
>(since you don't want to run_whatever if "whatever" doesn't
>need to be done!)

The test on what needs to be run is inside the run_do... functions.

>And, when you want to have a second instance of run_copy?
>
>while(FoREVER) {
> if (enable_run_copy1) { run_copy1() };
> if (enable_run_copy2) { run_copy2() };
> if (enable_run_GUI) { run_GUI() };
> if (enable_run_PID) { run_PID() };
> ...
>}
>
>Of course, making sure that run_copy2 uses a different buffer than
>run_copy1, different I/O descriptors, etc.
>
>You also have to keep track of each run_whatever's state *in* that
>routine. E.g., if you can't finish *all* of run_whatever in a
>single invocation, you have to store information in the routine
>(static variables) that allow the routine to remember where it was
>and restart from that point.

You'll need to retain that information anyway. You're overcomplicating
things.

>>> [I'm serious, here! Are you going to put timeouts on
>>> the non-blocking calls? What's keeping track of time
>>> for you? Can the output device work *while* the input
>>> device is working?
>>
>> Ofcourse. As soon as there is nothing more to be done for now, the
>> non-blocking function (task) exits. Thats the whole point of a
>> non-blocking function!
>>
>>> Or, does one stall while you service
>>> the other? Do you embed code in your spin-wait loops
>>> to poll the user to see what he might want? Or, to
>>> tell him what's happening? etc.]
>>
>> No. If you write all the tasks as non-blocking functions you have no
>> problems with spin locks. User interaction is just another task in the
>> sequential list of tasks.
>
>You're spinning around the main loop. Same difference.
>
>User is prompted for an ID number. You don't want to block
>waiting for the slow human. So, run_GUI has to *remember* that
>it is "waiting for an ID number" -- so that the next time
>around the main loop, you don't start the "run_GUI" task
>at the beginning but, instead, resume *in* the "get_ID_number"
>portion of the task.

Every GUI I've worked with is event driven. So you just poll for a
flag 'input done in ID field', read the contents and carry on. An RTOS
hides that polling for you but the net result is the same.

>> I've worked (with others) on huge projects (producing>700kB binaries)
>> that operate this way for timing critical systems. Writing
>> non-blocking tasks requires a certain state of mind though. Once you
>> get used to it, it makes life a whole lot simpler.
>
>You can't provide tight controls on timing because timing changes
>each time you change the contents of your main loop. Or, the
>code in one of those functions (what happens in run_compute_pi?
>how many digits do you compute before you arbitrarily decide
>to let some other task have a chance at the CPU? Is the time
>required for each digit constant? If it varies, then the
>time in any particular instance of run_compute_pi can vary. This
>affects *when* the other run_whatever routines are invoked, etc.

I agree there are limits to the serial task system. At one point I was
asked to try and implement PolarSSL into an ARM controller. SSL does
some hefty encryption that took a long time. I abandoned that project
but if I had continued I would have used an RTOS.

Nico Coesel

unread,
Feb 9, 2012, 6:46:17 AM2/9/12
to
"lang...@fonz.dk" <lang...@fonz.dk> wrote:

>>
>> If you constrain yourself to working on the PC, you limit
>> the types of solutions you can reasonably pursue.
>>
>
>if that is how you would solve a simple task in an embedded system,
>I'm beginning to understand why MCUs now come with megabytes of flash
>and runs a 100's of MHz...

You are missing the point. What I'm saying is that there are many
debugging, code quality and profiling tools available for a PC which
make software development on a PC very easy. Besides that there is no
hassle with JTAG dongles, limited breakpoint capabilities, etc.
IMHO it is a good idea to develop and test complex chunks of code on a
PC before running them in a microcontroller. A lot of my embedded
software was born on a PC.

I never typed anything about writing bloated software.

Jan Panteltje

unread,
Feb 9, 2012, 7:04:09 AM2/9/12
to
On a sunny day (Thu, 09 Feb 2012 10:47:16 GMT) it happened ni...@puntnl.niks
(Nico Coesel) wrote in <4f33a432....@news.kpn.nl>:

>Jan Panteltje <pNaonSt...@yahoo.com> wrote:
>
>>On a sunny day (Wed, 08 Feb 2012 23:28:38 GMT) it happened ni...@puntnl.niks
>>(Nico Coesel) wrote in <4f330025....@news.kpn.nl>:
>>
>>>I've never come across unsigned chars in the past 15 years.
>>
>>Ever use Linux?
>>Most video buffers are unsigned char*,
>>and compiling other peoples code generates trillions of gcc warnings
>>of assigning a signed char to an unsigned...
>>Did you write that?
>
>You're missing the point. We are talking about systems where a char
>type is unsigned. Not 'unsigned char' but a char without sign.

OK, I have no experience with ARM and gcc, so are you saying that
gcc ARM has only unsigned char?

In fact I do not like ARM, I believe in more done in hardware.
I have noticed an AMD announcement that they will also do ARM systems on a chip.
What has the world come to?
:-)

More bloat software expected.




lang...@fonz.dk

unread,
Feb 9, 2012, 7:13:49 AM2/9/12
to
On 9 Feb., 12:46, n...@puntnl.niks (Nico Coesel) wrote:
> "langw...@fonz.dk" <langw...@fonz.dk> wrote:
>
> >> If you constrain yourself to working on the PC, you limit
> >> the types of solutions you can reasonably pursue.
>
> >if that is how you would solve a simple task in an embedded system,
> >I'm beginning to understand why MCUs now come with megabytes of flash
> >and runs a 100's of MHz...
>
> You are missing the point. What I'm saying is that there are many
> debugging, code quality and profiling tools available for a PC which
> make software development on a PC very easy. Besides that there is no
> hassle with JTAG dongles, limited breakpoint capabilities, etc.
> IMHO it is a good idea to develop and test complex chunks of code on a
> PC before running them in a microcontroller. A lot of my embedded
> software was born on a PC.
>
> I never typed anything about writing bloated software.
>

My comment was directed at Don wanting to use three threads to move
data form point A to point B

-Lasse

Phil Hobbs

unread,
Feb 9, 2012, 7:21:07 AM2/9/12
to
Real blocking, i.e. with no timeout, is a recipe for a flaky product.
So one has to maintain state and keep general track of stuff anyway.

Once you become sufficiently paranoid about deadlock and timing holes,
the main remaining difficulty with multithread is debugging it. (I've
been writing multithreaded apps since 1992, and got my first SMP machine
in about 1996, an early IBM Intellistation with dual Pentium Pros.)

If you have a good debugger, one that can at least bring up a source
window for each thread at each breakpoint, you can find stuff pretty
readily. Otherwise it's just iterated code reading and printf(). I've
never used multithread in embedded code, though--this is actually my
first foray into MCUs bigger than a PIC or an ATMega.

In embedded code for those little MCUs, I usually put timing-sensitive
stuff in a timer ISR and do the housekeeping loop thing in main(). That
way I'm always within a cycle or two of having deterministic timing.
With the ARM, I haven't figured out which method is best yet--I'm
usually doing finely-interleaved control and data acq, so DMA isn't very
suitable.

Cheers

Nico Coesel

unread,
Feb 9, 2012, 8:28:51 AM2/9/12
to
Jan Panteltje <pNaonSt...@yahoo.com> wrote:

>On a sunny day (Thu, 09 Feb 2012 10:47:16 GMT) it happened ni...@puntnl.niks
>(Nico Coesel) wrote in <4f33a432....@news.kpn.nl>:
>
>>Jan Panteltje <pNaonSt...@yahoo.com> wrote:
>>
>>>On a sunny day (Wed, 08 Feb 2012 23:28:38 GMT) it happened ni...@puntnl.niks
>>>(Nico Coesel) wrote in <4f330025....@news.kpn.nl>:
>>>
>>>>I've never come across unsigned chars in the past 15 years.
>>>
>>>Ever use Linux?
>>>Most video buffers are unsigned char*,
>>>and compiling other peoples code generates trillions of gcc warnings
>>>of assigning a signed char to an unsigned...
>>>Did you write that?
>>
>>You're missing the point. We are talking about systems where a char
>>type is unsigned. Not 'unsigned char' but a char without sign.
>
>OK, I have no experience with ARM and gcc, so are you saying that
>gcc ARM has only unsigned char?

No. I was saying I never came across a C compiler where the 'char'
type is the same as an 'unsigned char' type.

Phil Hobbs

unread,
Feb 9, 2012, 8:46:27 AM2/9/12
to
Nico Coesel wrote:
>
> Jan Panteltje <pNaonSt...@yahoo.com> wrote:
>
> >On a sunny day (Thu, 09 Feb 2012 10:47:16 GMT) it happened ni...@puntnl.niks
> >(Nico Coesel) wrote in <4f33a432....@news.kpn.nl>:
> >
> >>Jan Panteltje <pNaonSt...@yahoo.com> wrote:
> >>
> >>>On a sunny day (Wed, 08 Feb 2012 23:28:38 GMT) it happened ni...@puntnl.niks
> >>>(Nico Coesel) wrote in <4f330025....@news.kpn.nl>:
> >>>
> >>>>I've never come across unsigned chars in the past 15 years.
> >>>
> >>>Ever use Linux?
> >>>Most video buffers are unsigned char*,
> >>>and compiling other peoples code generates trillions of gcc warnings
> >>>of assigning a signed char to an unsigned...
> >>>Did you write that?
> >>
> >>You're missing the point. We are talking about systems where a char
> >>type is unsigned. Not 'unsigned char' but a char without sign.
> >
> >OK, I have no experience with ARM and gcc, so are you saying that
> >gcc ARM has only unsigned char?
>
> No. I was saying I never came across a C compiler where the 'char'
> type is the same as an 'unsigned char' type.
>

Even different gcc ports have different defaults, e.g. iirc the avr and
ppc versions are unsigned by default. I always add the appropriate
compiler flag to the makefile--nail the problem to the floor and move
on.

Jan Panteltje

unread,
Feb 9, 2012, 9:39:41 AM2/9/12
to
On a sunny day (Thu, 09 Feb 2012 13:28:51 GMT) it happened ni...@puntnl.niks
(Nico Coesel) wrote in <4f33c97e....@news.kpn.nl>:

>Jan Panteltje <pNaonSt...@yahoo.com> wrote:
>
>>On a sunny day (Thu, 09 Feb 2012 10:47:16 GMT) it happened ni...@puntnl.niks
>>(Nico Coesel) wrote in <4f33a432....@news.kpn.nl>:
>>
>>>Jan Panteltje <pNaonSt...@yahoo.com> wrote:
>>>
>>>>On a sunny day (Wed, 08 Feb 2012 23:28:38 GMT) it happened ni...@puntnl.niks
>>>>(Nico Coesel) wrote in <4f330025....@news.kpn.nl>:
>>>>
>>>>>I've never come across unsigned chars in the past 15 years.
>>>>
>>>>Ever use Linux?
>>>>Most video buffers are unsigned char*,
>>>>and compiling other peoples code generates trillions of gcc warnings
>>>>of assigning a signed char to an unsigned...
>>>>Did you write that?
>>>
>>>You're missing the point. We are talking about systems where a char
>>>type is unsigned. Not 'unsigned char' but a char without sign.
>>
>>OK, I have no experience with ARM and gcc, so are you saying that
>>gcc ARM has only unsigned char?
>
>No. I was saying I never came across a C compiler where the 'char'
>type is the same as an 'unsigned char' type.

OK, that I can understand.
Thanks.

Gerhard Hoffmann

unread,
Feb 9, 2012, 12:10:05 PM2/9/12
to
On 02/09/2012 01:29 AM, Phil Hobbs wrote:

> For the same package size and MCU series, all the chips are
> pin-compatible, so I can just specify the next larger size for that
> customer. They'd be paying a premium anyway. The things I'm looking at
> doing are cost-sensitive, moderately high volume things--maybe 100k
> units per year if all goes well. A buck extra on the processor gets to
> be important money at that point, so paying $10k for the fancy Keil
> system might make good sense. (Assuming I have some outside development
> money.) The Code Red ones are only $1100 or so including a JTAG module,
> but they don't support the embedded trace module. They told me they
> were developing it, and that it was due to ship Real Soon Now
> ^H^H^H^H^H^H^H^H^H^H^H later this year. Assuming it doesn't go up by a
> factor of 10, Code Red plus one of those Seeger JTrace gizmos looks
> about right. I might get an STM32 demo board and try the demo version
> of Code Red on it. I really don't want to be stuck with just one
> vendor's chips.


Have you seen this one?

Looks interesting for small embedded projects.



<http://en.wikipedia.org/wiki/Raspberry_Pi>

It even might be self-supporting.

regards, Gerhard

Phil Hobbs

unread,
Feb 9, 2012, 12:26:06 PM2/9/12
to
Looks like fun, but it's never going to fit a handheld form factor or
run on two AA batteries. I'm trying to fit almost-almost-everything
inside the processor, so it'll be a 100-pin LQFP on a really small
board, probably with the analog stuff on the back and an aluminized
paper shield.

Don Y

unread,
Feb 9, 2012, 1:30:30 PM2/9/12
to
Hi Phil,

On 2/9/2012 5:21 AM, Phil Hobbs wrote:
> Don Y wrote:

>> With a multitasking design, a task *can* block taking no
>> temporal resources. It need not do anything out-of-the-ordinary
>> to remember what it was doing when most recently active. It
>> just sits *in* the statement that it was executing at the
>> time of the task switch.
>>
>> It concentrates on the task at hand instead of accommodating
>> other tasks via a particular set of switching mechanisms.
>
> Real blocking, i.e. with no timeout, is a recipe for a flaky product.
> So one has to maintain state and keep general track of stuff anyway.

Think about what you've said: blocking WITH a timeout is OK.
There are two ways of doing this: one is to spin on a timer
while affecting a non-blocking call; the other is to use a
blocking call and let SOMETHING ELSE (effectively) spin on that
timer (i.e., the OS) RELEASING your block when the timer
expires.

In the first case (Nico's case as well as the example in the
PDF I posted), the task assumes responsibility for deciding
when to give up on the spin-wait. Presumably, it can consult a
timer maintained by "someone" (including itself) to determine
when enough time has been burned waiting.

In the second case, you have a mechanism to cleanly release
the blocking state. As such, a CONTROL THREAD (thread1 in
my example) can just as easily release that block (in addition
to a timeout *or* in PLACE of a timeout).

This allows an *algorithm* to determine when a blocking thread
is released. Time, user killing task, phase of moon, etc.
And, *how* that algorithm goes about this is nicely documented
by the API. Not ad hoc measures ("Let's set a flag and have the
spinning task examine the flag to see if it should stop spinning"
"No, why don't we just ZERO the timer that the task is already
waiting on and get the 'release' FOR FREE?" "What if we just
disable the task and restart it?" etc.)

OS's are all about moving common used mechanisms into a formal
structure that can be exploited by tasks. Forcing EACH task
to assume the details of these mechanisms themselves (e.g.,
tracking time, *implementing* timeouts, etc.) makes extra
work for them and represents opportunities for error to
creep in.

> Once you become sufficiently paranoid about deadlock and timing holes,
> the main remaining difficulty with multithread is debugging it. (I've
> been writing multithreaded apps since 1992, and got my first SMP machine
> in about 1996, an early IBM Intellistation with dual Pentium Pros.)

You write *any* program with debugging in mind. E.g., to debug the
file copy example I posed, I would debug each thread independently
in a "friendly environment" (a PC, ICE, mainframe, etc.). The
read and write can be redirected to real files -- or the "console".
The role of the missing worker thread (consumer or producer)
can be faked -- with something that appears to remove or insert
data from/to the buffer. Etc.

> If you have a good debugger, one that can at least bring up a source
> window for each thread at each breakpoint, you can find stuff pretty
> readily. Otherwise it's just iterated code reading and printf(). I've

Implement black boxes ("flight recorders") and push status into them
with an "#if DEBUG" enabled macro (this allows them to be removed
later, if you don't want to ship the product with that). Being
internal mechanisms (i.e., no costly I/O), they have minimal impact on
performance. And, they can easily be sized to cover more or less
event-time.

I have a set of debug macros that allow me to switch between
this sort of internal black box *or* spit "progress reports"
out a virtual serial port (when using an emulator, I arrange
for that serial port to display color-coded messages on the
console:
task1: started
task5: waiting on file
task3: terminating
task1: allocated 4332 byte buffer at 0x3008
task1: spawning slave task 'slave1'
slave1: started
task2: got timer event
task5: wrote 238 bytes
So, as I bring tasks on-line, I can see that they appear to
work ("Hey, where's task6? Did I forget to start it?")
Then, as I get confidence with them, I can just turn off
their individual progress reporting leaving only the tasks
of interest to me.

With the multitasking framework, I can have a task that
actually snapshots black boxes and passes their contents to
me *while* the system is running. "It's just another task.
No big deal."

> never used multithread in embedded code, though--this is actually my
> first foray into MCUs bigger than a PIC or an ATMega.
>
> In embedded code for those little MCUs, I usually put timing-sensitive
> stuff in a timer ISR and do the housekeeping loop thing in main(). That

The problem with that approach is things tend to creep into the ISRs
until the temporal size of the ISR becomes an "issue". I like ISR's
to be "slicker'n snot" -- do the absolute minimum that *could* be
done (which is often different from the minimum that your implementation
NEEDS to be done) and get the hell out of there.

E.g., one of the nastiest tasks to write is a barcode decoder that
processes "live video" (i.e., a pulse train). You can't ignore it
(unless you have a user interface that only allows barcodes to
be scanned at certain times: push this button, then scan barcode)
so it's a live input that can steal resources from you at ANY time.

The time between edges (which are the significant events in that
video stream) can be VERY short. E.g., a 0.007" nominal bar width
scanned at 100 ips (which is within the manual scanning range of
your GRANDMOTHER) means events happening at 70us intervals
(assuming no ink bleed -- which could make a space narrower).

Not only do you have interrupts at that frequency, but you also have
to *process* that data at a comparable average rate -- otherwise
data comes in faster than you can process/discard it!

When I process barcodes, ALL the ISR does is take a snapshot of
the system timer (to mark "when the edge was noticed") as well as
a snapshot of the "how-long-has-this-ISR-been-pending" timer,
if the hardware supports it (this lets me LATER offset the
recorded "when the edge was noticed" timestamp to determine "when
the edge OCCURRED"). Then, set the ISR to look for the *opposite*
edge and you're done.

I don't waste clock cycles computing the "time since last edge"
(which is what the algorithm will ultimately want) because some
background task can do that OUTSIDE the interrupt context -- as
long as I provide it with the timestamps on which to operate.

In the late 70's, I designed using the "foreground background"
style. It was *always* hard adding new features (when you think
about user interfaces and multiple concurrent "machine/mechanism"
activities). Trying to get timely responses from multiple
*competing* activities/requirements leads you to more brittle
solutions. To "hard" RT instead of a more robust "soft" RT
approach. (i.e., "if I *don't* get this done in time, the product
is broken. I need a faster CPU. I need another ISR. etc.")

> way I'm always within a cycle or two of having deterministic timing.

No doubt your algorithms are driven off a clock. ISR trips,
you do what needs to be done *then*, that *instant*, and then
hope the background figures out what you need for "next time".

If the background can't RELIABLY keep up (because you underestimated
the resources required for some other aspect of the product that
is operating concurrently), you move more of that background
activity into the ISR. This leaves less time for the background...
which means less gets done, increasing the tendency to NEED to
move more into the ISR, etc.

[In the barcode example, a user could INTENTIONALLY run a
barcode label across the scanner at very high rates of speed
*continuously* -- like shaking a can of paint. The system
would grind to a halt as resources were shifted to recording
and processing those edges. When the user's arm got tired
(it takes very little time to tire when you are doing that sort
of thing), the system just picked up where it left off.]

I worked on a LORAN-C position plotter in ~1980. Because of
resource limitations (back then, 2716's were $50/each... having
*6* of them -- a whole 12KB! -- in one product was extravagant!),
you couldn't come up with sophisticated control solutions (IIRC,
we had 2x128 bytes of RAM and used much of that for the
floating point computations required to project the hyperbolic
grid geometry onto an oblate Earth model to convert to lat-lon).

So, things ended up in ISRs for all of the above reasons -- you
couldn't control the X&Y stepping motors for the pen recorder
in a background task at independent rates at a fast enough rate.
So, you moved the decision making (reduced to Bresenham's) INTO
the ISR that actually steps the motors. You don't want the motors
to drive the mechanism into the limits so you have that ISR watch
the end-of-travel sensors. And, since those could be faulty (salt
water on a ship's bridge), you have to track where you *think*
the mechanism might be ("Gee, the mechanism is only 20,000
steps wide and I've already moved 25,000 steps without seeing
the other side! Am I just grinding gears? Has a belt broken?

You can't decode keystrokes in the background (too sluggish) so
you have the keypad scanning ISR do the key decoding, as well.
While you're scanning keys, you might as well do the display
multiplexing. And, of course, you have to periodically obtain
the current LORAN coordinates so you can figure out where you
*are* (usually done every 10 GRI's). Etc.

[Recall, we're talking about a 2 or 3MHz CPU I.e., memory
cycle times of about a microsecond :> ]

It just doesn't make sense to saddle yourself with imposing and
maintaining all of this "mechanism" at a time when resources are
so plentiful (in all but HUGE volume products -- mice, keyboards,
etc.)

> With the ARM, I haven't figured out which method is best yet--I'm
> usually doing finely-interleaved control and data acq, so DMA isn't very
> suitable.

If your control decisions are tied to your *immediate* observations,
you have to ensure that you can process the data in time to make
that decision. (Or, can have a "safe", default decision that works
"well enough" -- less than ideal control)

When designing control loops, I try to decouple the sampling process
from the control/actuate process. If they must run in lock step, I
arrange for the sampling function (DMA, ISR, etc.) to update the
control -- but N sample periods "later". (i.e., read sense4 and output
control3; process sense4 to compute control4; queue control4 to wait
for ISR; read sense5 and output control4; etc.). I.e., it looks like
a fixed, predicatable lag in the control.

If I can't guarantee that I will have enough time in EVERY "sample
interval" to compute the next control value, (to accommodate momentary
overload by something EVEN MORE IMPORTANT), then I might increase the
fixed delay to *two* sample periods with the expectation that I might
not be able to get the first control value computed in the first
interval... BUT, I will be able to get it AND the next computed in the
interval that follows!

(of course, this all depends on the process being controlled, field
characteristics, etc.)

Anyway...

You can use an ISR to drive those "events": "time to make the
donuts... NOW!!!". *Or*, you can have an event driven preemptive
multitasking system that virtualizes that ISR. (many MTOS's
claim to be preemptive but are really only pseudo-preemptive)

I.e., the timer ISR can signal a timer event. This can "make ready"
all tasks waiting on that event. *You* decide which of these is
"most important" (priority value) and the OS activates it. The time
(latency) from ISR to task activation is fixed (if you design the
control structures correctly), regardless of how many tasks are waiting.

Whether you update the control variable in that task *or* move
the actual:
output(control_value);
into an ISR and use the timer event to restart the "compute next
control value" task is a question of how much jitter you can tolerate
in that "setting" time. (i.e., if you can tolerate *none*, drive it
with DMA! That's how I like to drive high performance stepping
motor designs where you need to impose an acceleration profile)

I am convinced that this sort of approach GREATLY simplifies design
and maintenance. I suspect I could write the LORAN plotter
application, from scratch, in far less time without changing the
algorithms (assuming I had the extra RAM to support the control
structures) just by NOT having to manually juggle so many issues
at the same time. ("We need to mark an X on the chart at this point.
Tell the motors to stop tracking the vessel's motion. Lift the pen.
Move over a little. Drop the pen. Move diagonally. Lift. Move.
Drop. Move. Lift. Go back to where we were. Let the motors resume
tracking the vessel's motion. -- ALL in the ISR!") Every new feature
was a new challenge. :<

(In its defense, it was the state of the art, at the time)

Phil Hobbs

unread,
Feb 9, 2012, 5:14:20 PM2/9/12
to
On 02/09/2012 01:30 PM, Don Y wrote:
> Hi Phil,
>
> On 2/9/2012 5:21 AM, Phil Hobbs wrote:
>> Don Y wrote:
>
>>> With a multitasking design, a task *can* block taking no
>>> temporal resources. It need not do anything out-of-the-ordinary
>>> to remember what it was doing when most recently active. It
>>> just sits *in* the statement that it was executing at the
>>> time of the task switch.
>>>
>>> It concentrates on the task at hand instead of accommodating
>>> other tasks via a particular set of switching mechanisms.
>>
>> Real blocking, i.e. with no timeout, is a recipe for a flaky product.
>> So one has to maintain state and keep general track of stuff anyway.
>
> Think about what you've said: blocking WITH a timeout is OK.
> There are two ways of doing this: one is to spin on a timer
> while affecting a non-blocking call; the other is to use a
> blocking call and let SOMETHING ELSE (effectively) spin on that
> timer (i.e., the OS) RELEASING your block when the timer
> expires.
>
> In the first case (Nico's case as well as the example in the
> PDF I posted), the task assumes responsibility for deciding
> when to give up on the spin-wait. Presumably, it can consult a
> timer maintained by "someone" (including itself) to determine
> when enough time has been burned waiting.

You don't use spinlocks for individual tasks on a uniprocessor, at least
I don't. (A state-machine design with one big overall loop doing
housekeeping is the ticket.)

When there's an operating system available, you use blocking calls with
timeout parameters, e.g. select(). When there isn't, you poll once, set
the relevant interrupt enable, and go back to the housekeeping routine.
In the ISR, you set a flag to say that, for instance, there's a
character available in the UART register. Tasks in the housekeeping
loop check to see if there's a flag set that gives them a job to do, and
if not, return immediately, so that loop spins fast.

Or the main loop can consist of a function--basically a thread scheduler
on a diet--that looks at the flags, manages the state transitions, and
calls the relevant service functions. When you have to go that far, a
lightweight RTOS starts looking pretty good, though I've never used one
myself.

>
> In the second case, you have a mechanism to cleanly release
> the blocking state. As such, a CONTROL THREAD (thread1 in
> my example) can just as easily release that block (in addition
> to a timeout *or* in PLACE of a timeout).

If there's a way of making the call return unsuccessfully, that's
functionally the same. You have to have error recovery someplace, and
that requires saving state.

>
> This allows an *algorithm* to determine when a blocking thread
> is released. Time, user killing task, phase of moon, etc.
> And, *how* that algorithm goes about this is nicely documented
> by the API. Not ad hoc measures ("Let's set a flag and have the
> spinning task examine the flag to see if it should stop spinning"
> "No, why don't we just ZERO the timer that the task is already
> waiting on and get the 'release' FOR FREE?" "What if we just
> disable the task and restart it?" etc.)

Anyone who's worried about ad hoc stuff should swear off embedded
programming. ;)

>
> OS's are all about moving common used mechanisms into a formal
> structure that can be exploited by tasks. Forcing EACH task
> to assume the details of these mechanisms themselves (e.g.,
> tracking time, *implementing* timeouts, etc.) makes extra
> work for them and represents opportunities for error to
> creep in.

Sure. At least above some threshold of complexity. For my stuff, the
reason I'm using a MCU at all is that I need to interleave control and
data acq, which means maintaining control of the interrupts to attain
timing coherence. That's hard with a RTOS, no?

>
>> Once you become sufficiently paranoid about deadlock and timing holes,
>> the main remaining difficulty with multithread is debugging it. (I've
>> been writing multithreaded apps since 1992, and got my first SMP machine
>> in about 1996, an early IBM Intellistation with dual Pentium Pros.)
>
> You write *any* program with debugging in mind. E.g., to debug the
> file copy example I posed, I would debug each thread independently
> in a "friendly environment" (a PC, ICE, mainframe, etc.). The
> read and write can be redirected to real files -- or the "console".
> The role of the missing worker thread (consumer or producer)
> can be faked -- with something that appears to remove or insert
> data from/to the buffer. Etc.

Good luck debugging clusterized simulators thread-by-thread. My
parallel FDTD code would execute half a time step and then croak. If
you have threads doing essentially unrelated things, so that you have N
programs running concurrently, that can work fine.

>
>> If you have a good debugger, one that can at least bring up a source
>> window for each thread at each breakpoint, you can find stuff pretty
>> readily. Otherwise it's just iterated code reading and printf(). I've
>
> Implement black boxes ("flight recorders") and push status into them
> with an "#if DEBUG" enabled macro (this allows them to be removed
> later, if you don't want to ship the product with that). Being
> internal mechanisms (i.e., no costly I/O), they have minimal impact on
> performance. And, they can easily be sized to cover more or less
> event-time.

For events that you're expecting. Hitting a memory corruption bug isn't
that friendly, unless you have a mechanism like mudflap that maintains a
copy of the call stack.

>
> I have a set of debug macros that allow me to switch between
> this sort of internal black box *or* spit "progress reports"
> out a virtual serial port (when using an emulator, I arrange
> for that serial port to display color-coded messages on the
> console:
> task1: started
> task5: waiting on file
> task3: terminating
> task1: allocated 4332 byte buffer at 0x3008
> task1: spawning slave task 'slave1'
> slave1: started
> task2: got timer event
> task5: wrote 238 bytes
> So, as I bring tasks on-line, I can see that they appear to
> work ("Hey, where's task6? Did I forget to start it?")
> Then, as I get confidence with them, I can just turn off
> their individual progress reporting leaving only the tasks
> of interest to me.

Sure. Probably most of us who have done serious multiprocessor work
have those.

>
> With the multitasking framework, I can have a task that
> actually snapshots black boxes and passes their contents to
> me *while* the system is running. "It's just another task.
> No big deal."

That can be useful. In my simulator code, each daughter process has a
bidirectional socket connection to a supervisor process (which is also
doing its own computation). That way if something goes wrong in thread
M running on host N, the overall supervisor process on the front-end
host gets all the info and can enforce an orderly shutdown of the whole
multi-host run.

>
>> never used multithread in embedded code, though--this is actually my
>> first foray into MCUs bigger than a PIC or an ATMega.
>>
>> In embedded code for those little MCUs, I usually put timing-sensitive
>> stuff in a timer ISR and do the housekeeping loop thing in main(). That
>
> The problem with that approach is things tend to creep into the ISRs
> until the temporal size of the ISR becomes an "issue". I like ISR's
> to be "slicker'n snot" -- do the absolute minimum that *could* be
> done (which is often different from the minimum that your implementation
> NEEDS to be done) and get the hell out of there.

Yup. For serial, I usually just cram the character into a circular
buffer, set a flag, and exit. You have to update the overall state
machine, but I like to do that with a wrapper function around all of the
housekeeping routines, so there's no code duplication.
>
> E.g., one of the nastiest tasks to write is a barcode decoder that
> processes "live video" (i.e., a pulse train). You can't ignore it
> (unless you have a user interface that only allows barcodes to
> be scanned at certain times: push this button, then scan barcode)
> so it's a live input that can steal resources from you at ANY time.
>
> The time between edges (which are the significant events in that
> video stream) can be VERY short. E.g., a 0.007" nominal bar width
> scanned at 100 ips (which is within the manual scanning range of
> your GRANDMOTHER) means events happening at 70us intervals
> (assuming no ink bleed -- which could make a space narrower).
>
> Not only do you have interrupts at that frequency, but you also have
> to *process* that data at a comparable average rate -- otherwise
> data comes in faster than you can process/discard it!
>
> When I process barcodes, ALL the ISR does is take a snapshot of
> the system timer (to mark "when the edge was noticed") as well as
> a snapshot of the "how-long-has-this-ISR-been-pending" timer,
> if the hardware supports it (this lets me LATER offset the
> recorded "when the edge was noticed" timestamp to determine "when
> the edge OCCURRED"). Then, set the ISR to look for the *opposite*
> edge and you're done.

Sounds like a good approach.
>
> I don't waste clock cycles computing the "time since last edge"
> (which is what the algorithm will ultimately want) because some
> background task can do that OUTSIDE the interrupt context -- as
> long as I provide it with the timestamps on which to operate.
>
> In the late 70's, I designed using the "foreground background"
> style. It was *always* hard adding new features (when you think
> about user interfaces and multiple concurrent "machine/mechanism"
> activities). Trying to get timely responses from multiple
> *competing* activities/requirements leads you to more brittle
> solutions. To "hard" RT instead of a more robust "soft" RT
> approach. (i.e., "if I *don't* get this done in time, the product
> is broken. I need a faster CPU. I need another ISR. etc.")

For something like that, I'd probably want to use multiple processors.
Fast unis are harder to manage and make the gizmo more brittle and
harder to expand.
>
>> way I'm always within a cycle or two of having deterministic timing.
>
> No doubt your algorithms are driven off a clock. ISR trips,
> you do what needs to be done *then*, that *instant*, and then
> hope the background figures out what you need for "next time".

Normally the background produces a list of things for the ISR to do, so
when it does one, it just increments a pointer and then exits.

>
> If the background can't RELIABLY keep up (because you underestimated
> the resources required for some other aspect of the product that
> is operating concurrently), you move more of that background
> activity into the ISR. This leaves less time for the background...
> which means less gets done, increasing the tendency to NEED to
> move more into the ISR, etc.

Eventually your approach will run out of resources too. Threads are not
a silver bullet, especially when you have to contend with a brain-dead
thread scheduler like Linux's. (*)

>
> [In the barcode example, a user could INTENTIONALLY run a
> barcode label across the scanner at very high rates of speed
> *continuously* -- like shaking a can of paint. The system
> would grind to a halt as resources were shifted to recording
> and processing those edges. When the user's arm got tired
> (it takes very little time to tire when you are doing that sort
> of thing), the system just picked up where it left off.]

Okay, but that isn't an argument in favour of multiple threads, just
lean ISRs.

>
> I worked on a LORAN-C position plotter in ~1980. Because of
> resource limitations (back then, 2716's were $50/each... having
> *6* of them -- a whole 12KB! -- in one product was extravagant!),
> you couldn't come up with sophisticated control solutions (IIRC,
> we had 2x128 bytes of RAM and used much of that for the
> floating point computations required to project the hyperbolic
> grid geometry onto an oblate Earth model to convert to lat-lon).
>
> So, things ended up in ISRs for all of the above reasons -- you
> couldn't control the X&Y stepping motors for the pen recorder
> in a background task at independent rates at a fast enough rate.
> So, you moved the decision making (reduced to Bresenham's) INTO
> the ISR that actually steps the motors. You don't want the motors
> to drive the mechanism into the limits so you have that ISR watch
> the end-of-travel sensors. And, since those could be faulty (salt
> water on a ship's bridge), you have to track where you *think*
> the mechanism might be ("Gee, the mechanism is only 20,000
> steps wide and I've already moved 25,000 steps without seeing
> the other side! Am I just grinding gears? Has a belt broken?
>
> You can't decode keystrokes in the background (too sluggish) so
> you have the keypad scanning ISR do the key decoding, as well.

Must have been a _slow_ housekeeping loop. Mine typically loop at
kilohertz rates, sometimes faster. Of course I'm not using a 1702.

> While you're scanning keys, you might as well do the display
> multiplexing. And, of course, you have to periodically obtain
> the current LORAN coordinates so you can figure out where you
> *are* (usually done every 10 GRI's). Etc.
>
> [Recall, we're talking about a 2 or 3MHz CPU I.e., memory
> cycle times of about a microsecond :> ]
>
> It just doesn't make sense to saddle yourself with imposing and
> maintaining all of this "mechanism" at a time when resources are
> so plentiful (in all but HUGE volume products -- mice, keyboards,
> etc.)

If I had all that foofaraw to worry about, I might well use an RTOS too.
But that's not enough to justify your blanket trashing of all
housekeeping loops.

>
>> With the ARM, I haven't figured out which method is best yet--I'm
>> usually doing finely-interleaved control and data acq, so DMA isn't very
>> suitable.
>
> If your control decisions are tied to your *immediate* observations,
> you have to ensure that you can process the data in time to make
> that decision. (Or, can have a "safe", default decision that works
> "well enough" -- less than ideal control)

Normally not, but sometimes I may need to wait for a polygon edge to
cross a laser beam, or deal with an explosion of junk data caused by a
beam glinting off a bolt head, or that kind of stuff. Usually a
moderate-size circular buffer is fine.
>
> When designing control loops, I try to decouple the sampling process
> from the control/actuate process. If they must run in lock step, I
> arrange for the sampling function (DMA, ISR, etc.) to update the
> control -- but N sample periods "later". (i.e., read sense4 and output
> control3; process sense4 to compute control4; queue control4 to wait
> for ISR; read sense5 and output control4; etc.). I.e., it looks like
> a fixed, predicatable lag in the control.

That's where a separate small processor can make a lot of sense.
As I said, I think everyone agrees that there's some threshold above
which having an OS makes sense. I just don't agree that the threshold
is as low as you claim.

Cheers

Phil Hobbs

(*) My simulator runs on both Windows and Linux clusters. It's about
20% faster on Windows because in Linux there's no way to express the
fact that one thread is the most important in the process, except to
make all threads real-time and therefore bring the machine to its knees.
Ergo you can't control the communications latency between hosts, ergo
they spend a bunch of time waiting for the next host over to get its act
together. I'd be quite happy for all the compute threads to run at a
niceness of +19 and the comms threads at +18, but nooooo, Czar Linus
doesn't think I can be trusted with such power. Brain dead. (See, I
get to rant a bit too.) ;)

John Larkin

unread,
Feb 9, 2012, 6:01:03 PM2/9/12
to
Most of our products have a main run-forever loop and a periodic
interrupt service routine, and sometimes a serial interrupt. The main
loop just does whatever it can, with some things running in response
to a period-interrupt flag, and some (like a command parser) when the
serial IRQ sets a flag to indicate that a buffer is ready. The main
loop can also have a table-driven dispatcher near the end, to run some
state machines more or less often than others. IRQs can do that, too.

No program states are saved anywhere because nothing is ever
suspended. Everything just blasts through state machines.

It's nice to have a few counters that the periodic IRQ ticks down to
zero. The main loop can use these for various handy things.

The problem with an RTOS is the task switching time and the need to
manage a bunch of stacks. And the very tools available can actually
complicate system design.

Even a dinky ARM, like an LPC1754, can run a pretty hairy ISR at 100
KHz.

--

John Larkin, President
Highland Technology, Inc

jlarkin at highlandtechnology dot com
http://www.highlandtechnology.com

Precision electronic instrumentation
Picosecond-resolution Digital Delay and Pulse generators
Custom laser controllers
Photonics and fiberoptic TTL data links
VME thermocouple, LVDT, synchro acquisition and simulation

Don Y

unread,
Feb 9, 2012, 6:59:32 PM2/9/12
to
Hi Phil,

On 2/9/2012 3:14 PM, Phil Hobbs wrote:

>>>> With a multitasking design, a task *can* block taking no
>>>> temporal resources. It need not do anything out-of-the-ordinary
>>>> to remember what it was doing when most recently active. It
>>>> just sits *in* the statement that it was executing at the
>>>> time of the task switch.
>>>>
>>>> It concentrates on the task at hand instead of accommodating
>>>> other tasks via a particular set of switching mechanisms.
>>>
>>> Real blocking, i.e. with no timeout, is a recipe for a flaky product.
>>> So one has to maintain state and keep general track of stuff anyway.
>>
>> Think about what you've said: blocking WITH a timeout is OK.
>> There are two ways of doing this: one is to spin on a timer
>> while affecting a non-blocking call; the other is to use a
>> blocking call and let SOMETHING ELSE (effectively) spin on that
>> timer (i.e., the OS) RELEASING your block when the timer
>> expires.
>>
>> In the first case (Nico's case as well as the example in the
>> PDF I posted), the task assumes responsibility for deciding
>> when to give up on the spin-wait. Presumably, it can consult a
>> timer maintained by "someone" (including itself) to determine
>> when enough time has been burned waiting.
>
> You don't use spinlocks for individual tasks on a uniprocessor, at least

The point I was making addresses Nico's (and the PDF's) "non-blocking"
approach. You test and wait. Whether that wait is an OS provided
service or your implicit "return to mainline code" (go do something
else -- while "spinning" -- and come back and TEST again, later)

*You* are doing the testing, explicitly. And, *you* are deciding when
to give up -- by examining an "elapsed time", etc.

This is a common activity. A common *service* that an OS can provide!

> I don't. (A state-machine design with one big overall loop doing
> housekeeping is the ticket.)
>
> When there's an operating system available, you use blocking calls with
> timeout parameters, e.g. select(). When there isn't, you poll once, set
> the relevant interrupt enable, and go back to the housekeeping routine.

So, each timeout requires an ISR? Let an OS virtualize that for you.
HUNDREDS of timeouts from a single "service". "Tickle me in 23.5 ms"
"Unblock this function call if it hasn't completed in 30ms", etc.

> In the ISR, you set a flag to say that, for instance, there's a
> character available in the UART register. Tasks in the housekeeping loop
> check to see if there's a flag set that gives them a job to do, and if
> not, return immediately, so that loop spins fast.

Again, this makes *you* do all the testing. If, instead, you
tell an OS, "wake me when this THING (event) happens", then
*you* don't have to code for it.

> Or the main loop can consist of a function--basically a thread scheduler
> on a diet--that looks at the flags, manages the state transitions, and
> calls the relevant service functions. When you have to go that far, a
> lightweight RTOS starts looking pretty good, though I've never used one
> myself.
>
>> In the second case, you have a mechanism to cleanly release
>> the blocking state. As such, a CONTROL THREAD (thread1 in
>> my example) can just as easily release that block (in addition
>> to a timeout *or* in PLACE of a timeout).
>
> If there's a way of making the call return unsuccessfully, that's
> functionally the same. You have to have error recovery someplace, and
> that requires saving state.

You not only need a mechanism for *reporting* the "error exit",
but you also need a mechanism for pulling attention (execution)
away from whatever statement(s) is doing the actual block
(whether it is waiting for an IRQ, an event flag, etc.)

>> This allows an *algorithm* to determine when a blocking thread
>> is released. Time, user killing task, phase of moon, etc.
>> And, *how* that algorithm goes about this is nicely documented
>> by the API. Not ad hoc measures ("Let's set a flag and have the
>> spinning task examine the flag to see if it should stop spinning"
>> "No, why don't we just ZERO the timer that the task is already
>> waiting on and get the 'release' FOR FREE?" "What if we just
>> disable the task and restart it?" etc.)
>
> Anyone who's worried about ad hoc stuff should swear off embedded
> programming. ;)

That's the appeal of working in a structured environment.
There are "established ways" of doing these things. You
*don't* have to think about "tricks" to coax the implementation
to do something for which you recently discovered a need.
"Gee, the file copy hangs indefinitely. How do I *abort*
it?" or, "Crap! it's an endless STREAM that I've told it
to copy. I surely don't want to wait around for THAT!"

>> OS's are all about moving common used mechanisms into a formal
>> structure that can be exploited by tasks. Forcing EACH task
>> to assume the details of these mechanisms themselves (e.g.,
>> tracking time, *implementing* timeouts, etc.) makes extra
>> work for them and represents opportunities for error to
>> creep in.
>
> Sure. At least above some threshold of complexity. For my stuff, the
> reason I'm using a MCU at all is that I need to interleave control and
> data acq, which means maintaining control of the interrupts to attain
> timing coherence. That's hard with a RTOS, no?

Depends on what guarantees you want from the RTOS (note that
I've only been talking about MTOS's, so far). Instead of
picking hardware resources to drive your algorithms (ISR's,
etc.), you pick appropriate VIRTUAL resources for the same
thing. The RTOS gives you guarantees -- just like the
silicon gives you guarantees -- about how long you will have
to wait before your code is activated in a particular set of
conditions (e.g., the hardware says IRQ latency is X *once*
your IRQ is enabled and of sufficient priority and the executing
instruction has completed execution, etc.)

>>> Once you become sufficiently paranoid about deadlock and timing holes,
>>> the main remaining difficulty with multithread is debugging it. (I've
>>> been writing multithreaded apps since 1992, and got my first SMP machine
>>> in about 1996, an early IBM Intellistation with dual Pentium Pros.)
>>
>> You write *any* program with debugging in mind. E.g., to debug the
>> file copy example I posed, I would debug each thread independently
>> in a "friendly environment" (a PC, ICE, mainframe, etc.). The
>> read and write can be redirected to real files -- or the "console".
>> The role of the missing worker thread (consumer or producer)
>> can be faked -- with something that appears to remove or insert
>> data from/to the buffer. Etc.
>
> Good luck debugging clusterized simulators thread-by-thread. My parallel
> FDTD code would execute half a time step and then croak. If you have
> threads doing essentially unrelated things, so that you have N programs
> running concurrently, that can work fine.

You have to design with decoupling in mind. I.e., the file copy
example will work regardless of which thread you choose to
"single step". It just works very SLOWLY! :> (like debugging
a processor design at DC).

One of the projects I am working on currently has dozens of
processors coupled loosely or tightly. Before I started the
design (both the hardware and software ends of it), I thought
about how I would *economically* be able to debug algorithms
where one part of the algorithm was executing on one CPU
and another was executing on another CPU both under the control
of a *third* CPU, etc.

(It is *dizzying* to sit between three workstations talking
to three different processes on three different nodes running
three different pieces of code ... and trying to keep track of
what's happening, where! But, the code's expectations of
timeliness have been crafted so that I can do this without
breaking it)

>>> If you have a good debugger, one that can at least bring up a source
>>> window for each thread at each breakpoint, you can find stuff pretty
>>> readily. Otherwise it's just iterated code reading and printf(). I've
>>
>> Implement black boxes ("flight recorders") and push status into them
>> with an "#if DEBUG" enabled macro (this allows them to be removed
>> later, if you don't want to ship the product with that). Being
>> internal mechanisms (i.e., no costly I/O), they have minimal impact on
>> performance. And, they can easily be sized to cover more or less
>> event-time.
>
> For events that you're expecting. Hitting a memory corruption bug isn't
> that friendly, unless you have a mechanism like mudflap that maintains a
> copy of the call stack.

Of course! But the black box gives you a cheap way of seeing how far
you got -- and logging values of interest -- without seriously impacting
performance (which could alter the correctness of an RT algorithm).

It's the equivalent of attaching a logic analyzer to trace "whatever".
But, since you can execute *code* to decide what (and when!) to store,
you aren't as constrained as you are with a passive LA approach.

>> With the multitasking framework, I can have a task that
>> actually snapshots black boxes and passes their contents to
>> me *while* the system is running. "It's just another task.
>> No big deal."
>
> That can be useful. In my simulator code, each daughter process has a
> bidirectional socket connection to a supervisor process (which is also
> doing its own computation). That way if something goes wrong in thread M
> running on host N, the overall supervisor process on the front-end host
> gets all the info and can enforce an orderly shutdown of the whole
> multi-host run.

My current OS lets external agents "control" tasks via mechanisms
that I (or the task) can pass to those agents. So, I can look
inside a running task and watch it execute, kill it, free its
resources, etc.

(of course, this is a protected interface)

>>> never used multithread in embedded code, though--this is actually my
>>> first foray into MCUs bigger than a PIC or an ATMega.
>>>
>>> In embedded code for those little MCUs, I usually put timing-sensitive
>>> stuff in a timer ISR and do the housekeeping loop thing in main(). That
>>
>> The problem with that approach is things tend to creep into the ISRs
>> until the temporal size of the ISR becomes an "issue". I like ISR's
>> to be "slicker'n snot" -- do the absolute minimum that *could* be
>> done (which is often different from the minimum that your implementation
>> NEEDS to be done) and get the hell out of there.
>
> Yup. For serial, I usually just cram the character into a circular
> buffer, set a flag, and exit. You have to update the overall state
> machine, but I like to do that with a wrapper function around all of the
> housekeeping routines, so there's no code duplication.

For (traditional) serial ports, I have a variety of strategies based
on the performance level required and resources available to me.

Outgoing data is placed in a shared TxFIFO. That action implicitly (or
explicitly) notifies a housekeeping task that is enabled whenever the
transmitter is idle. This task primes the transmitter with the
"oldest" character enqueued (e.g., I/O need not be character at a
time) and enables the TxEmpty interrupt. Once completed, this task
dies (it will be reenabled when/if the transmit FIFO ever empties).

Thereafter, each TxIRQ pulls a character from the TxFIFO and places it
in the transmitter. So, the transmitter runs at the full data rate.
When the TxFIFO empties (i.e., a TxIRQ finds nothing in the buffer),
the TxIRQ is disabled and the transmitter housekeeping task is
reenabled (so it can watch for an appropriate "write()")

If I have lower performance requirements, the TxIRQ is not used and
a background task feeds the transmitter directly (obviously limiting
how quickly data can be transmitted).

Hardware handshaking and software flow control complicate this
process. In some cases, this is embedded *in* the ISR -- which
complicates the ISR and makes things more brittle -- while other
cases can move this to a background task (e.g., "Ooops! Buffer
is filling up. Let's signal the other end of the link to stop
sending!"). Break generation and other synchronous control
signalling options present other opportunities for variation
(e.g., do you *queue* these as "commands" that the ISR interprets:
"generate a long BREAK at this point in the data stream" or stall
the ISR at the appropriate point and move that functionality into
a background task, etc.)

Reception is handled similarly. ISR receives an incoming character
and places it in a shared (but separate) RxFIFO -- along with the
status codes (overrun, parity, etc.). The RxIRQ always watches for
RxFIFO overrun -- which it reports as yet another "status code" tied
to the character that caused it. Pacing is handled in the same
variety of ways above.

Any task wanting to *consume* a received character waits on the
RxFIFO and removes as many characters as it needs -- blocking if
need be (and requested).

I.e., the serial device looks like a genuine "service" that the
system manages. (In some designs, there is actually a cdevsw
sitting above this access, etc. and a task open()ing such a
device can cause other devices wantint it to automatically wait)

>> I don't waste clock cycles computing the "time since last edge"
>> (which is what the algorithm will ultimately want) because some
>> background task can do that OUTSIDE the interrupt context -- as
>> long as I provide it with the timestamps on which to operate.
>>
>> In the late 70's, I designed using the "foreground background"
>> style. It was *always* hard adding new features (when you think
>> about user interfaces and multiple concurrent "machine/mechanism"
>> activities). Trying to get timely responses from multiple
>> *competing* activities/requirements leads you to more brittle
>> solutions. To "hard" RT instead of a more robust "soft" RT
>> approach. (i.e., "if I *don't* get this done in time, the product
>> is broken. I need a faster CPU. I need another ISR. etc.")
>
> For something like that, I'd probably want to use multiple processors.
> Fast unis are harder to manage and make the gizmo more brittle and
> harder to expand.

<frown> I begged for a second processor on the first such machine.
There were subsystems that ran continuously eating up huge parts
of the CPU (data acquisition). They seemed ideal for "spinning off".
But, we had a philosophy of pushing the processor into overload
instead of adding hardware.

In one instance, I had 8 oscillators that I had to sample (count)
continuously (moving things into the frequency domain is a favorite
trick for trading off low data rate data acquisition). The oscillators
ran at a nominal ~100KHz. I had configured 8 *16b* counters to
accumulate these counts. And, had to *justify* this over using
8 *8b* counters and "counting the overflows in software".

Remember, this is in the generation of the 2-3MHz 8b CPUs. I think
the carry out ISRs would have eaten up ~50% of the CPU by themselves!

(Can you spell "Resource Starved"? :> )

>> If the background can't RELIABLY keep up (because you underestimated
>> the resources required for some other aspect of the product that
>> is operating concurrently), you move more of that background
>> activity into the ISR. This leaves less time for the background...
>> which means less gets done, increasing the tendency to NEED to
>> move more into the ISR, etc.
>
> Eventually your approach will run out of resources too. Threads are not
> a silver bullet, especially when you have to contend with a brain-dead
> thread scheduler like Linux's. (*)

Of course! I'm an advocate of multitasking for COMPLEXITY REDUCTION.
Make it easier to get the implementation *right*.

>> [In the barcode example, a user could INTENTIONALLY run a
>> barcode label across the scanner at very high rates of speed
>> *continuously* -- like shaking a can of paint. The system
>> would grind to a halt as resources were shifted to recording
>> and processing those edges. When the user's arm got tired
>> (it takes very little time to tire when you are doing that sort
>> of thing), the system just picked up where it left off.]
>
> Okay, but that isn't an argument in favour of multiple threads, just
> lean ISRs.

But you extend the same philosphy upwards into the higher levels
of the application. I.e., cut the application into little pieces
that are easy to "get right" instead of trying to push them all
together into a monolithic block of code.

E.g., note the barcode example in the PDF I posted. Several *trivial*
(fast, lean, easy to get right!) tasks in addition to the ISR *just*
to "decode" a barcode label. And that doesn't assign any *meaning*
to it (just makes it available to the application as a "barcode" event)

>> I worked on a LORAN-C position plotter in ~1980. Because of
>> resource limitations (back then, 2716's were $50/each... having
>> *6* of them -- a whole 12KB! -- in one product was extravagant!),
>> you couldn't come up with sophisticated control solutions (IIRC,
>> we had 2x128 bytes of RAM and used much of that for the
>> floating point computations required to project the hyperbolic
>> grid geometry onto an oblate Earth model to convert to lat-lon).
>>
>> So, things ended up in ISRs for all of the above reasons -- you
>> couldn't control the X&Y stepping motors for the pen recorder
>> in a background task at independent rates at a fast enough rate.
>> So, you moved the decision making (reduced to Bresenham's) INTO
>> the ISR that actually steps the motors. You don't want the motors
>> to drive the mechanism into the limits so you have that ISR watch
>> the end-of-travel sensors. And, since those could be faulty (salt
>> water on a ship's bridge), you have to track where you *think*
>> the mechanism might be ("Gee, the mechanism is only 20,000
>> steps wide and I've already moved 25,000 steps without seeing
>> the other side! Am I just grinding gears? Has a belt broken?
>>
>> You can't decode keystrokes in the background (too sluggish) so
>> you have the keypad scanning ISR do the key decoding, as well.
>
> Must have been a _slow_ housekeeping loop.

Yes. Because damn near EVERYTHING was happening in ISR's! If
you can't get around to "polling" (housekeeping loop) the keypad
at a decent frequency, you won't "see" key closures (membrane
keypad so you can't count on a key being "held down")

> Mine typically loop at
> kilohertz rates, sometimes faster. Of course I'm not using a 1702.
>
>> While you're scanning keys, you might as well do the display
>> multiplexing. And, of course, you have to periodically obtain
>> the current LORAN coordinates so you can figure out where you
>> *are* (usually done every 10 GRI's). Etc.
>>
>> [Recall, we're talking about a 2 or 3MHz CPU I.e., memory
>> cycle times of about a microsecond :> ]
>>
>> It just doesn't make sense to saddle yourself with imposing and
>> maintaining all of this "mechanism" at a time when resources are
>> so plentiful (in all but HUGE volume products -- mice, keyboards,
>> etc.)
>
> If I had all that foofaraw to worry about, I might well use an RTOS too.
> But that's not enough to justify your blanket trashing of all
> housekeeping loops.

I'm not "trashing all housekeeping loops". Rather, I'm praising the
benefits of using more structured programming environments. I could
grow my own vegetables and raise my own beef. Or, I could go to the
grocery and *buy* those things. What do I want to spend my (limited)
time doing? How much risk do I want to take on (what happens if the
few cattle get sick and die? do I go hungry??)

The executive I outlined in that PDF was used for 100KB+ binaries.
WRITTEN IN ASM! Granted, it was more than 25 years ago (the first
pass of the article bears a 1987 date and it *followed* the actual
use of the technique) but that doesn't mean there weren't better
ways of doing things at that time. Folks would argue about whether
to use 0315 or 0xCD -- I would ask, "What's wrong with 'Call'?"

(Amusing that one of our products hosted a BASIC interpreter...
*under* that crippled multitasking framework! Talk about making
life hard for yourself... :< )

>> I am convinced that this sort of approach GREATLY simplifies design
>> and maintenance. I suspect I could write the LORAN plotter
>> application, from scratch, in far less time without changing the
>> algorithms (assuming I had the extra RAM to support the control
>> structures) just by NOT having to manually juggle so many issues
>> at the same time. ("We need to mark an X on the chart at this point.
>> Tell the motors to stop tracking the vessel's motion. Lift the pen.
>> Move over a little. Drop the pen. Move diagonally. Lift. Move.
>> Drop. Move. Lift. Go back to where we were. Let the motors resume
>> tracking the vessel's motion. -- ALL in the ISR!") Every new feature
>> was a new challenge. :<
>>
>> (In its defense, it was the state of the art, at the time)
>
> As I said, I think everyone agrees that there's some threshold above
> which having an OS makes sense. I just don't agree that the threshold is
> as low as you claim.

I look at it as, "When would you be willing to GIVE UP the MUL
opcode? Or JSR/BAL? etc." I.e., they all exist to make life
easier. None are *required*. In an industry (software) where
everything is so expensive and quality is often dubious, it
seems silly NOT to do everything that you can to improve your
outcome!

(esp when so much of this can be reused/leveraged for other
projects!)

Of course, YMMV! ;-)

Phil Hobbs

unread,
Feb 9, 2012, 7:37:16 PM2/9/12
to
You just crossed the line from fandom to irrationality. Of course not,
silly.

>
>> In the ISR, you set a flag to say that, for instance, there's a
>> character available in the UART register. Tasks in the housekeeping loop
>> check to see if there's a flag set that gives them a job to do, and if
>> not, return immediately, so that loop spins fast.
>
> Again, this makes *you* do all the testing. If, instead, you
> tell an OS, "wake me when this THING (event) happens", then
> *you* don't have to code for it.
>
>> Or the main loop can consist of a function--basically a thread scheduler
>> on a diet--that looks at the flags, manages the state transitions, and
>> calls the relevant service functions. When you have to go that far, a
>> lightweight RTOS starts looking pretty good, though I've never used one
>> myself.
>>
>>> In the second case, you have a mechanism to cleanly release
>>> the blocking state. As such, a CONTROL THREAD (thread1 in
>>> my example) can just as easily release that block (in addition
>>> to a timeout *or* in PLACE of a timeout).
>>
>> If there's a way of making the call return unsuccessfully, that's
>> functionally the same. You have to have error recovery someplace, and
>> that requires saving state.
>
> You not only need a mechanism for *reporting* the "error exit",
> but you also need a mechanism for pulling attention (execution)
> away from whatever statement(s) is doing the actual block
> (whether it is waiting for an IRQ, an event flag, etc.)

Only if you do it your way. The normal approach doesn't need that--you
just poll and move on.

>
>>> This allows an *algorithm* to determine when a blocking thread
>>> is released. Time, user killing task, phase of moon, etc.
>>> And, *how* that algorithm goes about this is nicely documented
>>> by the API. Not ad hoc measures ("Let's set a flag and have the
>>> spinning task examine the flag to see if it should stop spinning"
>>> "No, why don't we just ZERO the timer that the task is already
>>> waiting on and get the 'release' FOR FREE?" "What if we just
>>> disable the task and restart it?" etc.)
>>
>> Anyone who's worried about ad hoc stuff should swear off embedded
>> programming. ;)
>
> That's the appeal of working in a structured environment.
> There are "established ways" of doing these things. You
> *don't* have to think about "tricks" to coax the implementation
> to do something for which you recently discovered a need.
> "Gee, the file copy hangs indefinitely. How do I *abort*
> it?" or, "Crap! it's an endless STREAM that I've told it
> to copy. I surely don't want to wait around for THAT!"
>

But that's a manufactured problem. If you don't wait, you don't have to
worry. Just keep going, and the next timer tick or UART interrupt will
let you know what to do.

>>> OS's are all about moving common used mechanisms into a formal
>>> structure that can be exploited by tasks. Forcing EACH task
>>> to assume the details of these mechanisms themselves (e.g.,
>>> tracking time, *implementing* timeouts, etc.) makes extra
>>> work for them and represents opportunities for error to
>>> creep in.
>>
>> Sure. At least above some threshold of complexity. For my stuff, the
>> reason I'm using a MCU at all is that I need to interleave control and
>> data acq, which means maintaining control of the interrupts to attain
>> timing coherence. That's hard with a RTOS, no?
>
> Depends on what guarantees you want from the RTOS (note that
> I've only been talking about MTOS's, so far). Instead of
> picking hardware resources to drive your algorithms (ISR's,
> etc.), you pick appropriate VIRTUAL resources for the same
> thing. The RTOS gives you guarantees -- just like the
> silicon gives you guarantees -- about how long you will have
> to wait before your code is activated in a particular set of
> conditions (e.g., the hardware says IRQ latency is X *once*
> your IRQ is enabled and of sufficient priority and the executing
> instruction has completed execution, etc.)

AFAIK it only gives you _maximum_ wait times. For timing coherence,
wait times that are too short are just as bad.
>
>>>> Once you become sufficiently paranoid about deadlock and timing holes,
>>>> the main remaining difficulty with multithread is debugging it. (I've
>>>> been writing multithreaded apps since 1992, and got my first SMP
>>>> machine
>>>> in about 1996, an early IBM Intellistation with dual Pentium Pros.)
>>>
>>> You write *any* program with debugging in mind. E.g., to debug the
>>> file copy example I posed, I would debug each thread independently
>>> in a "friendly environment" (a PC, ICE, mainframe, etc.). The
>>> read and write can be redirected to real files -- or the "console".
>>> The role of the missing worker thread (consumer or producer)
>>> can be faked -- with something that appears to remove or insert
>>> data from/to the buffer. Etc.
>>
>> Good luck debugging clusterized simulators thread-by-thread. My parallel
>> FDTD code would execute half a time step and then croak. If you have
>> threads doing essentially unrelated things, so that you have N programs
>> running concurrently, that can work fine.
>
> You have to design with decoupling in mind. I.e., the file copy
> example will work regardless of which thread you choose to
> "single step". It just works very SLOWLY! :> (like debugging
> a processor design at DC).

How would you do that in the simulator? It works only in a restricted
problem domain.

>
> One of the projects I am working on currently has dozens of
> processors coupled loosely or tightly. Before I started the
> design (both the hardware and software ends of it), I thought
> about how I would *economically* be able to debug algorithms
> where one part of the algorithm was executing on one CPU
> and another was executing on another CPU both under the control
> of a *third* CPU, etc.
>
> (It is *dizzying* to sit between three workstations talking
> to three different processes on three different nodes running
> three different pieces of code ... and trying to keep track of
> what's happening, where! But, the code's expectations of
> timeliness have been crafted so that I can do this without
> breaking it)

Try a few hundred processors.

>
>>>> If you have a good debugger, one that can at least bring up a source
>>>> window for each thread at each breakpoint, you can find stuff pretty
>>>> readily. Otherwise it's just iterated code reading and printf(). I've
>>>
>>> Implement black boxes ("flight recorders") and push status into them
>>> with an "#if DEBUG" enabled macro (this allows them to be removed
>>> later, if you don't want to ship the product with that). Being
>>> internal mechanisms (i.e., no costly I/O), they have minimal impact on
>>> performance. And, they can easily be sized to cover more or less
>>> event-time.
>>
>> For events that you're expecting. Hitting a memory corruption bug isn't
>> that friendly, unless you have a mechanism like mudflap that maintains a
>> copy of the call stack.
>
> Of course! But the black box gives you a cheap way of seeing how far
> you got -- and logging values of interest -- without seriously impacting
> performance (which could alter the correctness of an RT algorithm).
>
> It's the equivalent of attaching a logic analyzer to trace "whatever".
> But, since you can execute *code* to decide what (and when!) to store,
> you aren't as constrained as you are with a passive LA approach.

Modern MCU development systems do that for you already. There are
silicon resources in the ARM for all of that--no coding required.
Sure. But you can just as easily do that in a housekeeping loop, like
everybody else does.
Okay, sure, lots of words describing what everybody does in one way or
another.
>
>>> I don't waste clock cycles computing the "time since last edge"
>>> (which is what the algorithm will ultimately want) because some
>>> background task can do that OUTSIDE the interrupt context -- as
>>> long as I provide it with the timestamps on which to operate.
>>>
>>> In the late 70's, I designed using the "foreground background"
>>> style. It was *always* hard adding new features (when you think
>>> about user interfaces and multiple concurrent "machine/mechanism"
>>> activities). Trying to get timely responses from multiple
>>> *competing* activities/requirements leads you to more brittle
>>> solutions. To "hard" RT instead of a more robust "soft" RT
>>> approach. (i.e., "if I *don't* get this done in time, the product
>>> is broken. I need a faster CPU. I need another ISR. etc.")
>>
>> For something like that, I'd probably want to use multiple processors.
>> Fast unis are harder to manage and make the gizmo more brittle and
>> harder to expand.
>
> <frown> I begged for a second processor on the first such machine.
> There were subsystems that ran continuously eating up huge parts
> of the CPU (data acquisition). They seemed ideal for "spinning off".
> But, we had a philosophy of pushing the processor into overload
> instead of adding hardware.
>

I think that's a dumb philosophy. CPU power is dirt cheap, programming
resources and (especially) field failures aren't.

> In one instance, I had 8 oscillators that I had to sample (count)
> continuously (moving things into the frequency domain is a favorite
> trick for trading off low data rate data acquisition). The oscillators
> ran at a nominal ~100KHz. I had configured 8 *16b* counters to
> accumulate these counts. And, had to *justify* this over using
> 8 *8b* counters and "counting the overflows in software".
>
> Remember, this is in the generation of the 2-3MHz 8b CPUs. I think
> the carry out ISRs would have eaten up ~50% of the CPU by themselves!
>
> (Can you spell "Resource Starved"? :> )

I used to sum linear and logarithmic detectors so I could get more
dynamic range out of a 10-bit ADC. Back in the day you had to do things
like that, but we're talking about 2012.

>
>>> If the background can't RELIABLY keep up (because you underestimated
>>> the resources required for some other aspect of the product that
>>> is operating concurrently), you move more of that background
>>> activity into the ISR. This leaves less time for the background...
>>> which means less gets done, increasing the tendency to NEED to
>>> move more into the ISR, etc.
>>
>> Eventually your approach will run out of resources too. Threads are not
>> a silver bullet, especially when you have to contend with a brain-dead
>> thread scheduler like Linux's. (*)
>
> Of course! I'm an advocate of multitasking for COMPLEXITY REDUCTION.
> Make it easier to get the implementation *right*.
>
>>> [In the barcode example, a user could INTENTIONALLY run a
>>> barcode label across the scanner at very high rates of speed
>>> *continuously* -- like shaking a can of paint. The system
>>> would grind to a halt as resources were shifted to recording
>>> and processing those edges. When the user's arm got tired
>>> (it takes very little time to tire when you are doing that sort
>>> of thing), the system just picked up where it left off.]
>>
>> Okay, but that isn't an argument in favour of multiple threads, just
>> lean ISRs.
>
> But you extend the same philosphy upwards into the higher levels
> of the application. I.e., cut the application into little pieces
> that are easy to "get right" instead of trying to push them all
> together into a monolithic block of code.

That just hides the interactions and makes them harder to figure out
when they occur. A housekeeping loop that's too slow is blatantly
obvious, but latent race conditions that only show up when the system is
stressed are not obvious. Obvious is good. Obvious is worth a lot.
(Did I mention that obvious problems are better than non-obvious ones?)
Your fanboy style is unbalanced enough that you could have fooled me.

> Rather, I'm praising the
> benefits of using more structured programming environments. I could
> grow my own vegetables and raise my own beef. Or, I could go to the
> grocery and *buy* those things. What do I want to spend my (limited)
> time doing? How much risk do I want to take on (what happens if the
> few cattle get sick and die? do I go hungry??)
>
> The executive I outlined in that PDF was used for 100KB+ binaries.
> WRITTEN IN ASM! Granted, it was more than 25 years ago (the first
> pass of the article bears a 1987 date and it *followed* the actual
> use of the technique) but that doesn't mean there weren't better
> ways of doing things at that time. Folks would argue about whether
> to use 0315 or 0xCD -- I would ask, "What's wrong with 'Call'?"
>
> (Amusing that one of our products hosted a BASIC interpreter...
> *under* that crippled multitasking framework! Talk about making
> life hard for yourself... :< )

Right, but again, this is 2012.

>
>>> I am convinced that this sort of approach GREATLY simplifies design
>>> and maintenance. I suspect I could write the LORAN plotter
>>> application, from scratch, in far less time without changing the
>>> algorithms (assuming I had the extra RAM to support the control
>>> structures) just by NOT having to manually juggle so many issues
>>> at the same time. ("We need to mark an X on the chart at this point.
>>> Tell the motors to stop tracking the vessel's motion. Lift the pen.
>>> Move over a little. Drop the pen. Move diagonally. Lift. Move.
>>> Drop. Move. Lift. Go back to where we were. Let the motors resume
>>> tracking the vessel's motion. -- ALL in the ISR!") Every new feature
>>> was a new challenge. :<
>>>
>>> (In its defense, it was the state of the art, at the time)
>>
>> As I said, I think everyone agrees that there's some threshold above
>> which having an OS makes sense. I just don't agree that the threshold is
>> as low as you claim.
>
> I look at it as, "When would you be willing to GIVE UP the MUL
> opcode? Or JSR/BAL? etc." I.e., they all exist to make life
> easier. None are *required*. In an industry (software) where
> everything is so expensive and quality is often dubious, it
> seems silly NOT to do everything that you can to improve your
> outcome!
>
> (esp when so much of this can be reused/leveraged for other
> projects!)

That's up to you, of course. To me, it looks like the case of the guy
that only had a hammer.

Cheers

Phil Hobbs
>
> Of course, YMMV! ;-)

Nico Coesel

unread,
Feb 10, 2012, 8:40:29 AM2/10/12
to
At what cost? The guru-level programmers I know avoid multi-threading
like the plague. There are too many pitfalls especially when novice
programmers need to be able to work on the code as well. At some point
you will want to move on in the company and not being held back by
ancient projects only you understand.

Like I said before. An RTOS can be usefull if you have tasks which
take very long to complete and need to be interrupted to process other
tasks. One can still argue whether the long tasks could or should be
divided into shorter tasks so they can be serialized.

In a lot of cases (signal processing for example) there are only two
tasks: signal processing and I/O handling (reading switches, user
input). In such cases I run the signal processing task entirely inside
an interrupt and the rest from main. This basically gives me two
independent threads without any OS overhead.

Phil Hobbs

unread,
Feb 10, 2012, 11:26:36 AM2/10/12
to
That might be a bit strong, at least on PCs. Nobody wants to go back to
the pre-WinNT days of single-threaded GUIs, when a badly behaved user
interface could effectively freeze up the machine. (Are you listening,
Mozilla???)

And there are lots of other reasons for multithreading. For instance, I
have a serial comms class that I've used for 15 years, that has a
high-priority thread encapsulated inside it to handle the hardware
interaction and make it look like a UART with an infinitely deep
buffer. (Doesn't work in Linux, of course, on account of that stupid
thread scheduler.) And in the multicore world, there's no other way of
harnessing that performance in a single process.

>
> Like I said before. An RTOS can be usefull if you have tasks which
> take very long to complete and need to be interrupted to process other
> tasks. One can still argue whether the long tasks could or should be
> divided into shorter tasks so they can be serialized.
>
> In a lot of cases (signal processing for example) there are only two
> tasks: signal processing and I/O handling (reading switches, user
> input). In such cases I run the signal processing task entirely inside
> an interrupt and the rest from main. This basically gives me two
> independent threads without any OS overhead.
>

That has all the instability problems of normal threading, though, with
none of the guarantees--you usually don't have a separate stack in the
ISR, for instance, and functions called from an ISR have to be
reentrant.

Cheers

Phil Hobbs

Nico Coesel

unread,
Feb 10, 2012, 1:21:05 PM2/10/12
to
Thats more like a design flaw. I'm talking about having more than one
thread in an application. Sometimes you can't go around having more
than one thread.

>high-priority thread encapsulated inside it to handle the hardware
>interaction and make it look like a UART with an infinitely deep
>buffer. (Doesn't work in Linux, of course, on account of that stupid
>thread scheduler.) And in the multicore world, there's no other way of
>harnessing that performance in a single process.

Linux has several schedulers these days. Ever tried to use a different
one?

>> input). In such cases I run the signal processing task entirely inside
>> an interrupt and the rest from main. This basically gives me two
>> independent threads without any OS overhead.
>>
>
>That has all the instability problems of normal threading, though, with
>none of the guarantees--you usually don't have a separate stack in the
>ISR, for instance, and functions called from an ISR have to be
>reentrant.

Actually on ARM7TDMI you have seperate stacks for normal IRQ, fast
IRQ, exceptions and your application. On Cortex you can choose to have
a seperate IRQ stack.

Don Y

unread,
Feb 10, 2012, 2:20:35 PM2/10/12
to
Hi Phil,

>>> I don't. (A state-machine design with one big overall loop doing
>>> housekeeping is the ticket.)
>>>
>>> When there's an operating system available, you use blocking calls with
>>> timeout parameters, e.g. select(). When there isn't, you poll once, set
>>> the relevant interrupt enable, and go back to the housekeeping routine.
>>
>> So, each timeout requires an ISR? Let an OS virtualize that for you.
>> HUNDREDS of timeouts from a single "service". "Tickle me in 23.5 ms"
>> "Unblock this function call if it hasn't completed in 30ms", etc.
>
> You just crossed the line from fandom to irrationality. Of course not,
> silly.

You said:

"you poll once, set the relevant interrupt enable, and go back
to the housekeeping routine"

This begged the question, "So what happens when some OTHER aspect of
your product requires a CONCURRENT timeout?" E.g., you're blinking
a light (1 sec on, 1 sec off), waiting for user input (with an "accept
default if not overriden in X seconds" and implementing a timeout on a
disk access. Do you "set the relevant interrupt enable" for each of
those three independent activities?

"Of course not, silly!"

Such an approach would lead to serious constraints on your design
*or* having to deal with unavailable resources ("Sorry, I can't blink
the light right now. Please try again, later.")

My question is based on actual experience. I've encountered systems
where a hardware timer *was* used for all timing related "events" -- but
not in the "typical" manner (of virtualizing "software timers").

Instead, the timer was programmed to expire at the nearest
dead-line/event and the timer ISR then reloaded the (hardware)
timer from a delta queue. Much *finer* control of time (though
often unnecessary) but at a higher cost.

>>> Or the main loop can consist of a function--basically a thread scheduler
>>> on a diet--that looks at the flags, manages the state transitions, and
>>> calls the relevant service functions. When you have to go that far, a
>>> lightweight RTOS starts looking pretty good, though I've never used one
>>> myself.
>>>
>>>> In the second case, you have a mechanism to cleanly release
>>>> the blocking state. As such, a CONTROL THREAD (thread1 in
>>>> my example) can just as easily release that block (in addition
>>>> to a timeout *or* in PLACE of a timeout).
>>>
>>> If there's a way of making the call return unsuccessfully, that's
>>> functionally the same. You have to have error recovery someplace, and
>>> that requires saving state.
>>
>> You not only need a mechanism for *reporting* the "error exit",
>> but you also need a mechanism for pulling attention (execution)
>> away from whatever statement(s) is doing the actual block
>> (whether it is waiting for an IRQ, an event flag, etc.)
>
> Only if you do it your way. The normal approach doesn't need that--you
> just poll and move on.

What makes "polling" the "normal approach"? Do you really think
your cell phone polls everything that "might be/not-be ready"?

>>>> This allows an *algorithm* to determine when a blocking thread
>>>> is released. Time, user killing task, phase of moon, etc.
>>>> And, *how* that algorithm goes about this is nicely documented
>>>> by the API. Not ad hoc measures ("Let's set a flag and have the
>>>> spinning task examine the flag to see if it should stop spinning"
>>>> "No, why don't we just ZERO the timer that the task is already
>>>> waiting on and get the 'release' FOR FREE?" "What if we just
>>>> disable the task and restart it?" etc.)
>>>
>>> Anyone who's worried about ad hoc stuff should swear off embedded
>>> programming. ;)
>>
>> That's the appeal of working in a structured environment.
>> There are "established ways" of doing these things. You
>> *don't* have to think about "tricks" to coax the implementation
>> to do something for which you recently discovered a need.
>> "Gee, the file copy hangs indefinitely. How do I *abort*
>> it?" or, "Crap! it's an endless STREAM that I've told it
>> to copy. I surely don't want to wait around for THAT!"
>
> But that's a manufactured problem. If you don't wait, you don't have to
> worry. Just keep going, and the next timer tick or UART interrupt will
> let you know what to do.

*You* have to implement policy and mechanism. You should only
have to worry about *policy*. If you are implementing mechanism
in each individual instance, you carry a higher cost for that.
Did you remember to check to see if power is failing so you
can abort the wait *early*? Where *else* do I need to inject
the code to do that?

>>>> OS's are all about moving common used mechanisms into a formal
>>>> structure that can be exploited by tasks. Forcing EACH task
>>>> to assume the details of these mechanisms themselves (e.g.,
>>>> tracking time, *implementing* timeouts, etc.) makes extra
>>>> work for them and represents opportunities for error to
>>>> creep in.
>>>
>>> Sure. At least above some threshold of complexity. For my stuff, the
>>> reason I'm using a MCU at all is that I need to interleave control and
>>> data acq, which means maintaining control of the interrupts to attain
>>> timing coherence. That's hard with a RTOS, no?
>>
>> Depends on what guarantees you want from the RTOS (note that
>> I've only been talking about MTOS's, so far). Instead of
>> picking hardware resources to drive your algorithms (ISR's,
>> etc.), you pick appropriate VIRTUAL resources for the same
>> thing. The RTOS gives you guarantees -- just like the
>> silicon gives you guarantees -- about how long you will have
>> to wait before your code is activated in a particular set of
>> conditions (e.g., the hardware says IRQ latency is X *once*
>> your IRQ is enabled and of sufficient priority and the executing
>> instruction has completed execution, etc.)
>
> AFAIK it only gives you _maximum_ wait times. For timing coherence, wait
> times that are too short are just as bad.

The minimum is ZERO from the time of the triggering event.
You happily live with that same zero when the triggering
event is a hardware interrupt. So, what's the difference
if that same zero comes from a VIRTUAL interrupt?

You wouldn't set up your interrupt to trigger some unpredictable
amount of time before the time at which you *wanted* to begin
your activity. So, don't have the virtual interrupt trigger
before that time and you don't have a problem!

The problem with the "virtual machine" that multitasking
environments provide is that the timescales are different.
You can (reasonably) guarantee that an ISR will be invoked
within microseconds of the actual "hardware event" that
tugged on the IRQ line. You can see the (small number) of
other such "activities" that might be competing for the
"foreground".

But, in the MTOS environment, those competing entities can
be many more and many more complex. Instead of a handful
of "interrupt users", you can have scores of "tasking
users". If you've not considered how those tasks co-operate,
it is hard to predict what might be competing for specific
resources (e.g., the CPU) at specific times.

This is akin to not keeping track of which IRQ's might be
active (and at which priorities) at any given time.

Work in an environment with scores of (active) IRQ's and
you face a similar problem.

[But that doesn't mean it can't be managed. That's why
interrupts AND tasks have "priorities"]

>>>>> Once you become sufficiently paranoid about deadlock and timing holes,
>>>>> the main remaining difficulty with multithread is debugging it. (I've
>>>>> been writing multithreaded apps since 1992, and got my first SMP
>>>>> machine
>>>>> in about 1996, an early IBM Intellistation with dual Pentium Pros.)
>>>>
>>>> You write *any* program with debugging in mind. E.g., to debug the
>>>> file copy example I posed, I would debug each thread independently
>>>> in a "friendly environment" (a PC, ICE, mainframe, etc.). The
>>>> read and write can be redirected to real files -- or the "console".
>>>> The role of the missing worker thread (consumer or producer)
>>>> can be faked -- with something that appears to remove or insert
>>>> data from/to the buffer. Etc.
>>>
>>> Good luck debugging clusterized simulators thread-by-thread. My parallel
>>> FDTD code would execute half a time step and then croak. If you have
>>> threads doing essentially unrelated things, so that you have N programs
>>> running concurrently, that can work fine.
>>
>> You have to design with decoupling in mind. I.e., the file copy
>> example will work regardless of which thread you choose to
>> "single step". It just works very SLOWLY! :> (like debugging
>> a processor design at DC).
>
> How would you do that in the simulator? It works only in a restricted
> problem domain.

File copy? (assuming no disk) Replace write() and read() with dummy
routines that move bytes to/from a large FIFO (or the console, etc.).
Watch the contents of that FIFO as the program executes. Or, break
execution and have a look at periodic/random intervals.

It depends on the nature of the dummy data that you feed in. E.g,
if your read() merely grabs the current time of day (to the second)
and returns that as the data read from the "disk", just look
through the FIFO and verify that the most recent "writes" resemble
the time displayed by the clock on your wall.

>> One of the projects I am working on currently has dozens of
>> processors coupled loosely or tightly. Before I started the
>> design (both the hardware and software ends of it), I thought
>> about how I would *economically* be able to debug algorithms
>> where one part of the algorithm was executing on one CPU
>> and another was executing on another CPU both under the control
>> of a *third* CPU, etc.
>>
>> (It is *dizzying* to sit between three workstations talking
>> to three different processes on three different nodes running
>> three different pieces of code ... and trying to keep track of
>> what's happening, where! But, the code's expectations of
>> timeliness have been crafted so that I can do this without
>> breaking it)
>
> Try a few hundred processors.

That depends on the type of application hosted on those processors.
E.g., you would debug a symmetric (or nearly symmetric) algorithm
different than one in which each processor was doing something
specific/different.

E.g., when the home automation system is complete, here, there
will be upwards of 70 processors running at any given time.
Most, small little "motes" (CPU's are cheap!). If I notice that
the system is failing to announce "arrivals" properly (i.e.,
someone just rang the front doorbell), I would *not* watch the
mote that monitors the doorbell "button" to notice the contact
closure. Nor would I watch that closure being debounced,
recognized by the daemon charged with watching it. Nor any of
the various mechanisms that would result in its transfer to
the "automation controller".

I wouldn't watch the daemons in the controller recognize that
incoming event. Nor trace the execution of the DBMS as it
determines how to respond/notify that event. Nor, the speech
synthesizer gluing together diphones to tell me what's just
happened.

I wouldn't look at the steps that the localizer undertakes to
figure out where I *am* (physically) in the house. Nor would
I watch the audio be packetized for transmission *to* "me".

[Nor any of the other little boxes that have to participate
in this process]

Instead, I'd monitor the log (black box) at the controller
while pressing the doorbell. "Ah! It recognized the incoming
event. So, I know the sensing mote, network fabric, etc.
on that side of the application appears to be functioning.
Now, check the black box for the localizer to see where it
*thinks* I'm located. Hmmm, that explains the problem; it
thinks I'm in the back yard and has routed the announcement
to the transmitting mote that services the back yard!
Obviously too far away from my current location for my
earpiece to pick it up! I guess I'll need to focus on
the localizer subsystem..."

When I designed the system, I considered how difficult it would
be to debug a "process" (in the abstract sense) that was
distributed over multiple, physically separate, nodes. I
know I (personally) can't juggle more than three "contexts"
effectively in my head. So, I discouraged functionality
from involving more than three devices AT A TIME.

>>>>> If you have a good debugger, one that can at least bring up a source
>>>>> window for each thread at each breakpoint, you can find stuff pretty
>>>>> readily. Otherwise it's just iterated code reading and printf(). I've
>>>>
>>>> Implement black boxes ("flight recorders") and push status into them
>>>> with an "#if DEBUG" enabled macro (this allows them to be removed
>>>> later, if you don't want to ship the product with that). Being
>>>> internal mechanisms (i.e., no costly I/O), they have minimal impact on
>>>> performance. And, they can easily be sized to cover more or less
>>>> event-time.
>>>
>>> For events that you're expecting. Hitting a memory corruption bug isn't
>>> that friendly, unless you have a mechanism like mudflap that maintains a
>>> copy of the call stack.
>>
>> Of course! But the black box gives you a cheap way of seeing how far
>> you got -- and logging values of interest -- without seriously impacting
>> performance (which could alter the correctness of an RT algorithm).
>>
>> It's the equivalent of attaching a logic analyzer to trace "whatever".
>> But, since you can execute *code* to decide what (and when!) to store,
>> you aren't as constrained as you are with a passive LA approach.
>
> Modern MCU development systems do that for you already. There are
> silicon resources in the ARM for all of that--no coding required.

Yes, this goes back to earlier x86 implementations, etc. But, using
those requires you to either run the application at reduced speed
*or* limit how much "history" you can track.

The former isn't always possible (without moving effort into a
"good simulation"). The latter requires you to be able to
identify a suitable trigger that will be proximate to the
activity of interest.

The black box approach lets you litter your code with "log writes",
decide how many resources you want to devote to the caching of
those writes, and then let the code "just run". You can push lots
of extra detail into the black box that you can later chose to ignore.
Or, selectively disable reports that are not of interest to remove
some of that clutter (and effectively increase the size of the
FIFO).

In resource starved environments, you can change the macros that
implement this so that they simply push bytes to a fixed address
(even if that address is already "allocated" to something)
and monitor that address (with a logic analyzer of an external
"WOM").

>> For (traditional) serial ports, I have a variety of strategies based
>> on the performance level required and resources available to me.
>>
>> Outgoing data is placed in a shared TxFIFO. That action implicitly (or
>> explicitly) notifies a housekeeping task that is enabled whenever the
>> transmitter is idle. This task primes the transmitter with the
>> "oldest" character enqueued (e.g., I/O need not be character at a
>> time) and enables the TxEmpty interrupt. Once completed, this task
>> dies (it will be reenabled when/if the transmit FIFO ever empties).
>>
>> Thereafter, each TxIRQ pulls a character from the TxFIFO and places it
>> in the transmitter. So, the transmitter runs at the full data rate.
>> When the TxFIFO empties (i.e., a TxIRQ finds nothing in the buffer),
>> the TxIRQ is disabled and the transmitter housekeeping task is
>> reenabled (so it can watch for an appropriate "write()")
>>
> Sure. But you can just as easily do that in a housekeeping loop, like
> everybody else does.

If you move it to a background activity, then you run the risk of
the UART exhausting its available data (buffer) when the comm rate
exceeds the rate at which your "loop" can get around to re-servicing
it. It also means you have to dick with the interrupt mask more
often (since it is the entity that enforces atomic access to the
shared resource, in that case)

>>>> In the late 70's, I designed using the "foreground background"
>>>> style. It was *always* hard adding new features (when you think
>>>> about user interfaces and multiple concurrent "machine/mechanism"
>>>> activities). Trying to get timely responses from multiple
>>>> *competing* activities/requirements leads you to more brittle
>>>> solutions. To "hard" RT instead of a more robust "soft" RT
>>>> approach. (i.e., "if I *don't* get this done in time, the product
>>>> is broken. I need a faster CPU. I need another ISR. etc.")
>>>
>>> For something like that, I'd probably want to use multiple processors.
>>> Fast unis are harder to manage and make the gizmo more brittle and
>>> harder to expand.
>>
>> <frown> I begged for a second processor on the first such machine.
>> There were subsystems that ran continuously eating up huge parts
>> of the CPU (data acquisition). They seemed ideal for "spinning off".
>> But, we had a philosophy of pushing the processor into overload
>> instead of adding hardware.
>
> I think that's a dumb philosophy. CPU power is dirt cheap, programming
> resources and (especially) field failures aren't.

When it's NOT your name on the building, you usually don't have
much choice! :> Even folks coming from engineering backgrounds
seem to *quickly* forget the sort of issues that affect design -- once
they move into managerial positions. This is especially true of
new technologies (putting processors *into* consumer products
was a relatively novel idea in the 70's)

>>>> [In the barcode example, a user could INTENTIONALLY run a
>>>> barcode label across the scanner at very high rates of speed
>>>> *continuously* -- like shaking a can of paint. The system
>>>> would grind to a halt as resources were shifted to recording
>>>> and processing those edges. When the user's arm got tired
>>>> (it takes very little time to tire when you are doing that sort
>>>> of thing), the system just picked up where it left off.]
>>>
>>> Okay, but that isn't an argument in favour of multiple threads, just
>>> lean ISRs.
>>
>> But you extend the same philosphy upwards into the higher levels
>> of the application. I.e., cut the application into little pieces
>> that are easy to "get right" instead of trying to push them all
>> together into a monolithic block of code.
>
> That just hides the interactions and makes them harder to figure out

How does it "hide" them? You know where a given task's inputs come
from (you know where the characters that you receive from your
interrupt driven UART come from!). Why would you suddenly become
"undisciplined" in tracking the relationships of tasks? Don't
you know what the various (physical) processors in a multiprocessing
application are doing? The relationships between their data?

> when they occur. A housekeeping loop that's too slow is blatantly
> obvious, but latent race conditions that only show up when the system is
> stressed are not obvious. Obvious is good. Obvious is worth a lot. (Did
> I mention that obvious problems are better than non-obvious ones?)

>>> Mine typically loop at
>>> kilohertz rates, sometimes faster. Of course I'm not using a 1702.
>>>
>>>> While you're scanning keys, you might as well do the display
>>>> multiplexing. And, of course, you have to periodically obtain
>>>> the current LORAN coordinates so you can figure out where you
>>>> *are* (usually done every 10 GRI's). Etc.
>>>>
>>>> [Recall, we're talking about a 2 or 3MHz CPU I.e., memory
>>>> cycle times of about a microsecond :> ]
>>>>
>>>> It just doesn't make sense to saddle yourself with imposing and
>>>> maintaining all of this "mechanism" at a time when resources are
>>>> so plentiful (in all but HUGE volume products -- mice, keyboards,
>>>> etc.)
>>>
>>> If I had all that foofaraw to worry about, I might well use an RTOS too.
>>> But that's not enough to justify your blanket trashing of all
>>> housekeeping loops.
>>
>> I'm not "trashing all housekeeping loops".
>
> Your fanboy style is unbalanced enough that you could have fooled me.

No. Use what you *need* to get the job done. But, don't use
less than you *can* thinking you're saving something!

You wouldn't use small signal transistors when you could use
an op amp to solve the same problem (unless you could reduce
the problem to a few discretes *and* had significant cost pressure
on you to do so).

You wouldn't use fixed BINARY point (i.e., not just natural integers)
math when you could use floating point (unless you could reduce
the problem to whole integers and had significant resource pressure
to do so).

People seem to shy away from OS's in embedded applications more
often than they should. I haven't been able to figure out if
this is due to a lack of experience, being at the mercy of an
OS vendor or just "not wanting to move out of their comfort zone".

I've had to "fix" too many products over the years that someone
"threw together" with a pile of spaghetti code absent any real
structure that a more formal environment could have provided.
Almost always the foreground-background split. It works fine -- until
it doesn't. Until the customer wants a feature that it didn't
anticipate. Until the load on the processor is increased and
the solution fails to scale.

Many years ago, I had to recover some sources for a product that
the owner had "misplaced". So, I was faced with a reverse engineering
task *and* a "product enhancement" task.

The enhancement was trivial, conceptually. Of course, clients
see *everything* as trivial and expect that to be reflected in
what they will have to *pay*! :>

The product itself wasn't complex. Something that I could have
knocked off "in no time".

I bid a comfortable number for the documentation recovery, and the
"enhancements". I was spot on regarding how much effort it would
take to reverse engineer the code. And, the effort it would
require for me to document that code (remember, there are no
names for variables, functions, entry points, etc.).

But, the code that was revealed was *so* poorly written that the
enhancements proved to be painful to implement. Since the
product was already designed and deployed, I couldn't change the
resources available to the application (damn near every *byte*
of ROM and RAM was spoken for. And changing the processor
would have been pure fantasy!)

So, I was stuck with a bad implementation that I now had to
"fit in". What had looked like a walk in the park turned into
a project from hell.

"*Will* I be able to make this work in this framework??"

(thankfully, it was only a few months)


> > Rather, I'm praising the
>> benefits of using more structured programming environments. I could
>> grow my own vegetables and raise my own beef. Or, I could go to the
>> grocery and *buy* those things. What do I want to spend my (limited)
>> time doing? How much risk do I want to take on (what happens if the
>> few cattle get sick and die? do I go hungry??)
>>
>> The executive I outlined in that PDF was used for 100KB+ binaries.
>> WRITTEN IN ASM! Granted, it was more than 25 years ago (the first
>> pass of the article bears a 1987 date and it *followed* the actual
>> use of the technique) but that doesn't mean there weren't better
>> ways of doing things at that time. Folks would argue about whether
>> to use 0315 or 0xCD -- I would ask, "What's wrong with 'Call'?"
>>
>> (Amusing that one of our products hosted a BASIC interpreter...
>> *under* that crippled multitasking framework! Talk about making
>> life hard for yourself... :< )
>
> Right, but again, this is 2012.

You glossed over the example. Imagine how *you* would add a
"scripting language" to the "one big loop" design approach.
Of course, you *can* do it -- as evidenced by the fact that we
ran a BASIC interpreter on this bizarre platform *alongside*
(concurrent with) the other tasks that ran the instrument.

But, the design of that interpreter was needlessly (?)
complicated by the crippled environment. I.e., it had
to EXPLICITLY relinquish control of the processor (preserving
NOTHING on the stack -- even a record of "what it was
doing at the time") routinely to ensure the other tasks
weren't delayed in their processing.

Do you interpret a single scripted statement? How long
will that take? Do you constrain the user so that he
can;t write "expensive" statements? No transcendentals
in expressions, etc.? Do you interpret *part* of a statement
and (manually) keep track of where you were in that process
("OK, I have computed the cosine of the angle. Next time I
get a chance, I'll compute 2*a*b. Then, the time after that,
I'll work on squaring a. And squaring b, the time after
that! Then, put it all together and tackle the square root.
*Then* i can advance to the next statement in the script...")

When you have a "virtual machine" (afforded by a multitasking
environment), you concentrate on the problem you are solving
(interpreting the script) and NOT the mechanism of "sharing the
processor".
When I'm driving *nails*, I surely don't want to heft a LARGE ROCK
if I've got a hammer nearby! ;)

Let's just agree to disagree. If your solution works for you, great.
Mine works for me. Let's just each avoid *maintaining* the other's
projects! :>

Phil Hobbs

unread,
Feb 10, 2012, 2:32:42 PM2/10/12
to
Me too. If you have one thread in a GUI app, then if the program gets
blocked on e.g. a slow network socket, the GUI become unresponsive.
Having a separate thread for the GUI is a very great help to good
application design.

>
> >high-priority thread encapsulated inside it to handle the hardware
> >interaction and make it look like a UART with an infinitely deep
> >buffer. (Doesn't work in Linux, of course, on account of that stupid
> >thread scheduler.) And in the multicore world, there's no other way of
> >harnessing that performance in a single process.
>
> Linux has several schedulers these days. Ever tried to use a different
> one?

AFAIK there's the original heavyweight one, where a thread is actually a
separate process, and the lightweight pthreads one. Do you know of one
where I can have realtime and normal threads in one process, or even set
different priorities for different threads in a user process? The
pthreads docs imply that you can, but you actually can't--none of the
priority or thread scheduling options (round robin, priority, etc)
actually works for a user application, despite what the docs say.

I'd (reluctantly) be willing to run the simulator as root, so as to be
able to have realtime threads, but as soon as you make one thread
real-time, they're all real-time, *even the compute-bound ones*. Having
compute-bound real-time threads makes the computer totally unresponsive
to user input.

All I want is to be able to express the notion that some threads don't
need as fast response as others. I can do that on every OS except
Linux.

>
> >> input). In such cases I run the signal processing task entirely inside
> >> an interrupt and the rest from main. This basically gives me two
> >> independent threads without any OS overhead.
> >>
> >
> >That has all the instability problems of normal threading, though, with
> >none of the guarantees--you usually don't have a separate stack in the
> >ISR, for instance, and functions called from an ISR have to be
> >reentrant.
>
> Actually on ARM7TDMI you have seperate stacks for normal IRQ, fast
> IRQ, exceptions and your application. On Cortex you can choose to have
> a seperate IRQ stack.

Fair enough, that helps a lot. They still have to be reentrant, though.

Phil Hobbs

unread,
Feb 10, 2012, 3:04:06 PM2/10/12
to
There's no such thing as a concurrent timeout on a real life
uniprocessor, regardless of whether you're using an RTOS or not. You
just have to adjust. That's why it's silly.

> My question is based on actual experience. I've encountered systems
> where a hardware timer *was* used for all timing related "events" -- but
> not in the "typical" manner (of virtualizing "software timers").
>
> Instead, the timer was programmed to expire at the nearest
> dead-line/event and the timer ISR then reloaded the (hardware)
> timer from a delta queue. Much *finer* control of time (though
> often unnecessary) but at a higher cost.

Your average ARM has, like, 8 separate timers. It's pretty unlikely
that you're going to run out.

<snip>
> >> You not only need a mechanism for *reporting* the "error exit",
> >> but you also need a mechanism for pulling attention (execution)
> >> away from whatever statement(s) is doing the actual block
> >> (whether it is waiting for an IRQ, an event flag, etc.)
> >
> > Only if you do it your way. The normal approach doesn't need that--you
> > just poll and move on.
>
> What makes "polling" the "normal approach"? Do you really think
> your cell phone polls everything that "might be/not-be ready"?

I don't mean sitting there checking its email every 10 microseconds. I
mean "you go look, and then if there's nothing to do, set the interrupt
enable and move on".
This is embedded code we're talking about, not a big-iron accounting
system. "Policy" and "mechanism" are concepts imported from bigger
iron.
It isn't _constant_, which makes it entirely useless for my purposes.
Try running an FFT on data taken with nondeterministic timing, and
you'll see what I mean. If I control the interrupts, my maximum timing
uncertainty is equal to the execution time difference between the
longest and shortest instructions, i.e. 1 or 2 processor cycles at
worst, and I can use three lines of assembler to iron that out if I
really need to.

>
> You wouldn't set up your interrupt to trigger some unpredictable
> amount of time before the time at which you *wanted* to begin
> your activity. So, don't have the virtual interrupt trigger
> before that time and you don't have a problem!

You obviously don't do a lot of data acq.

>
> The problem with the "virtual machine" that multitasking
> environments provide is that the timescales are different.
> You can (reasonably) guarantee that an ISR will be invoked
> within microseconds of the actual "hardware event" that
> tugged on the IRQ line. You can see the (small number) of
> other such "activities" that might be competing for the
> "foreground".

Microseconds might as well be years. Even with a 1 kHz signal, 1-us
jitter will limit the noise floor to -60 dBc or worse.

>
> But, in the MTOS environment, those competing entities can
> be many more and many more complex. Instead of a handful
> of "interrupt users", you can have scores of "tasking
> users". If you've not considered how those tasks co-operate,
> it is hard to predict what might be competing for specific
> resources (e.g., the CPU) at specific times.
>
> This is akin to not keeping track of which IRQ's might be
> active (and at which priorities) at any given time.
>
> Work in an environment with scores of (active) IRQ's and
> you face a similar problem.
>
> [But that doesn't mean it can't be managed. That's why
> interrupts AND tasks have "priorities"]

Again, we're in violent agreement that RTOSes are useful for certain
things, especially once the task becomes very complex. You just want to
use it for everything, which I think is insane.

<snip>>
> > How would you do that in the simulator? It works only in a restricted
> > problem domain.
>
> File copy? (assuming no disk) Replace write() and read() with dummy
> routines that move bytes to/from a large FIFO (or the console, etc.).
> Watch the contents of that FIFO as the program executes. Or, break
> execution and have a look at periodic/random intervals.

No, not file copy, fine grained interaction. Sim threads have to
exchange data thousands of times in even a short run. You can't debug
that your way. No way, no how.
<snip>

> > I think that's a dumb philosophy. CPU power is dirt cheap, programming
> > resources and (especially) field failures aren't.
>
> When it's NOT your name on the building, you usually don't have
> much choice! :> Even folks coming from engineering backgrounds
> seem to *quickly* forget the sort of issues that affect design -- once
> they move into managerial positions. This is especially true of
> new technologies (putting processors *into* consumer products
> was a relatively novel idea in the 70's)

Still a dumb philosophy, then and now. Repeating the mistake doesn't
improve it.
<snip>

> >> But you extend the same philosphy upwards into the higher levels
> >> of the application. I.e., cut the application into little pieces
> >> that are easy to "get right" instead of trying to push them all
> >> together into a monolithic block of code.
> >
> > That just hides the interactions and makes them harder to figure out
>
> How does it "hide" them? You know where a given task's inputs come
> from (you know where the characters that you receive from your
> interrupt driven UART come from!).
<snip>

The example I gave was a latent race condition. Those are all over the
place in embedded systems, even in well-designed code--the buffer of a
UART is an example. You have to get there before it fills up. Change
the system parameters, e.g. the baud rate, and it craters. That's a lot
harder to find in threaded code. That's a simple example, but there are
lots and lots of others. Housekeeping loops make that stuff a *lot*
easier to spot.> >> I'm not "trashing all housekeeping loops".
> >
> > Your fanboy style is unbalanced enough that you could have fooled me.
>
> No. Use what you *need* to get the job done. But, don't use
> less than you *can* thinking you're saving something!

So your original data copying example that started this whole exchange
couldn't safely be done without an RTOS? Give me a break. Fanboyism,
pure and simple.

>
> You wouldn't use small signal transistors when you could use
> an op amp to solve the same problem (unless you could reduce
> the problem to a few discretes *and* had significant cost pressure
> on you to do so).

You're nuts. Of course I would--transistors are a great deal more
predictable. I do discrete designs just about every day.

>
> You wouldn't use fixed BINARY point (i.e., not just natural integers)
> math when you could use floating point (unless you could reduce
> the problem to whole integers and had significant resource pressure
> to do so).

Of course I would. Accounting s/w has worked in units of $0.01 forever.

>
> People seem to shy away from OS's in embedded applications more
> often than they should. I haven't been able to figure out if
> this is due to a lack of experience, being at the mercy of an
> OS vendor or just "not wanting to move out of their comfort zone".

You don't think it's a bad thing to be dependent on vendors? I sure do.
People go out of business, get aquired by rivals and killed off, and so
on and so forth. Look at the EDA world.

<snip>

> But, the code that was revealed was *so* poorly written that the
> enhancements proved to be painful to implement. Since the
> product was already designed and deployed, I couldn't change the
> resources available to the application (damn near every *byte*
> of ROM and RAM was spoken for. And changing the processor
> would have been pure fantasy!)
>
> So, I was stuck with a bad implementation that I now had to
> "fit in". What had looked like a walk in the park turned into
> a project from hell.
>
> "*Will* I be able to make this work in this framework??"
>
> (thankfully, it was only a few months)

So it was crappy code. So what? All RTOS code is picture-perfect?
>
<snip>
> > Right, but again, this is 2012.
>
> You glossed over the example. Imagine how *you* would add a
> "scripting language" to the "one big loop" design approach.
> Of course, you *can* do it -- as evidenced by the fact that we
> ran a BASIC interpreter on this bizarre platform *alongside*
> (concurrent with) the other tasks that ran the instrument.
>

I'm not particularly interested in the example, since it illustrates
what we already agree on--namely that above some complexity threshold,
in the absence of time coherence requirements, RTOSes can be useful.
But that's not what we disagree about. There's no way I'm going to try
putting a scripting language in my little handheld measuring gizmo.
<snip>

> > That's up to you, of course. To me, it looks like the case of the guy
> > that only had a hammer.
>
> When I'm driving *nails*, I surely don't want to heft a LARGE ROCK
> if I've got a hammer nearby! ;)
>
> Let's just agree to disagree. If your solution works for you, great.
> Mine works for me. Let's just each avoid *maintaining* the other's
> projects! :>

Fair enough.

Cheers

Phil Hobbs

Jan Panteltje

unread,
Feb 11, 2012, 6:15:39 AM2/11/12
to
On a sunny day (Fri, 10 Feb 2012 14:32:42 -0500) it happened Phil Hobbs
<pcdhSpamM...@electrooptical.net> wrote in
<4F3570DA...@electrooptical.net>:

>Me too. If you have one thread in a GUI app, then if the program gets
>blocked on e.g. a slow network socket, the GUI become unresponsive.
>Having a separate thread for the GUI is a very great help to good
>application design.

It is often a must.

What I often do is have some live widgets, for example a VU meter,
or a scope, or signal strength, or some graphics display.
The real acquisition runs in one thread, and sets memory locations
and flags that the GUI thread then uses to update the display when it can.

The exception is if you can have non blocking for example ioctls(),
in that case the GUI thread can acquire data itself.

This uses a GUI thread, an internet thread, an audio thread, and a color thread (to send UPD color control data):
http://panteltje.com/panteltje/xpequ/index.html
The GUI thread just updates the GUI with data made available from the other threads.

This one only has the GUI thread, and uses non-blocking ioctls()
http://panteltje.com/pub/xdipo.gif
The signal level meter and the pie signal to noise display are alive in real time.

I use xforms to program these things, fast, and small code size.

For things approaching this complexity level maybe the best is just run Linux on
a small board.
Then you can program in C and use all your existing libraries and code.
Add an FPGA (maybe with Linux in it) to do the time critical things.

Especially if you have the space and only expect to sell a few,
then the hardware cost is not that much of an issue relative to the development cost in hours.

Well, my 2 cycles worth.

josephkk

unread,
Feb 12, 2012, 10:16:28 PM2/12/12
to
A whole lot depends on the environment you are working with. Sometimes
quick and clean (including overlapping buffer areas) beats out fully safe
and controllable. In hard real time environments, fully thought out and
documented trade-offs are involved.

?-)

josephkk

unread,
Feb 12, 2012, 10:23:00 PM2/12/12
to
On Wed, 08 Feb 2012 18:04:35 -0700, Don Y <th...@isnotme.com> wrote:

>Hi Nico,
>
>On 2/8/2012 4:28 PM, Nico Coesel wrote:
>> Don Y<th...@isnotme.com> wrote:
>>
>>> Hi Nico,
>>>
>>> On 2/8/2012 2:00 PM, Nico Coesel wrote:
>>>> Don Y<th...@isnotme.com> wrote:
>>>
>>>>>>> I'm not a big fan of any of the gnu debuggers, and the Eclipse-based
>>>>>>> 1-wire systems I'm most familiar with don't allow setting watchpoints,
>>>>>>> which IMO are pretty vital when doing choreography with multiple
>>>>>>> interrupt levels.
>>>>>>
>>>>>> I prefer a logic analyzer and I/O pins for checking that sort of
>>>>>> real-time stuff. There really isn't much use for a debugger on
>>>>>> embedded platforms. I you want to debug code (verify whether it works)
>>>>>> it is much more comfortable to do this on a PC. The nice thing about
>>>>>> Eclipse + gcc is that you can write code to run on a PC and use the
>>>>>> same code on an embedded platform.
>>>>>
>>>>> That's not often the case. You can get a good idea if the *algorithms*
>>>>> that you are using will work and iron out many of the bugs. But,
>>>>> since the code you will ultimately be generating (unless your target
>>>>> is a PC!) will have different characteristics (code/data sizes,
>>>>> endian-ness, timing, etc), you can easily be fooled into missing BIG
>>>>> problems (depending on your level of coding discipline).
>>>>>
>>>>> "Crap! chars are *signed*, here!"
>>>>
>>>> We are talking about 32 bit platforms.
>>>
>>> Signedness of chars is independent of machine width.
>>
>> I've never come across unsigned chars in the past 15 years.
>
>Um, *Arm* (isn't that what this thread was about? :> ). MIPS.
>
>>>>> I like embedding a standalone debugger *in* the device that lets me
>>>>> watch execution of a single thread, peek/poke random memory locations,
>>>>> etc. Debugging multithreaded applications on a PC is just not
>>>>> practical when you can't realistically simulate the I/O's (that
>>>>> are *driving* the various tasks).
>>>>
>>>> Usually the layer that actually does I/O is very small. What goes up
>>>> to the algorithms is easely simulated on a PC. Time doesn't matter in
>>>> the digital domain.
>>>
>>> Don't do much RT work, eh? :>
>>
>> I do lots of it.
>
>Then you know that time *does* matter in the digital domain!
>
>programs. Otherwise, you end up having to do lots of
>spin-waiting and checking unrelated "things" while you
>are busy with something else (or, worse yet, push all of
>those things into ISR's, needlessly).
>
>*Think* about how you would write that file copy task
>to handle the real-world issues that I mentioned. You'll
>end up with a real mess of spaghetti code -- assuming you
>don't ALSO have to do "other things" while copying!
>
>[I'm serious, here! Are you going to put timeouts on
>the non-blocking calls? What's keeping track of time
>for you? Can the output device work *while* the input
>device is working? Or, does one stall while you service
>the other? Do you embed code in your spin-wait loops
>to poll the user to see what he might want? Or, to
>tell him what's happening? etc.]
>
>I wouldn't consider writing a NON-multitasking application
>(unless it was a trivial "high school project").

So do you use your own RT kernel or a purchased one? Is it a rate
monotonic scheduler of a more sophisticated one?

?-)

josephkk

unread,
Feb 13, 2012, 12:00:54 AM2/13/12
to
On Thu, 09 Feb 2012 07:21:07 -0500, Phil Hobbs
<pcdhSpamM...@electrooptical.net> wrote:

>Don Y wrote:
>>
>> Hi Nico,
>>
>> On 2/8/2012 7:46 PM, Nico Coesel wrote:
>> > Don Y<th...@isnotme.com> wrote:
>> >
>> >> Hi Nico,
>> >>

<Snip>
You may want to look into rate monotonic schedulers.

?-)

Phil Hobbs

unread,
Feb 13, 2012, 12:14:30 AM2/13/12
to
What would that do for me? AFAICT all that does is schedule shorter
tasks sooner. It doesn't give me deterministic timing, unless I'm
missing something.

josephkk

unread,
Feb 14, 2012, 10:37:33 PM2/14/12
to
That (deterministic timing) is exactly what it is supposed to do. Of
course there are limits in what the mechanism can do.

Cheers

?-)

Phil Hobbs

unread,
Feb 15, 2012, 10:59:05 AM2/15/12
to
You obviously don't do any data acq. Once processor cycle is too much
jitter for many, many things.

josephkk

unread,
Feb 16, 2012, 12:56:14 AM2/16/12
to
On Wed, 15 Feb 2012 10:59:05 -0500, Phil Hobbs
Well not for a long time (back when the CA3308 was new). If jitter is
that sensitive you need to move some of that into an FPGA.

At what CPU frequency? 4 MHz? 200 MHz? Beyond 200 MHz processor speed
gets too decoupled from the bus transactions to exhibit anything like CPU
clock correspondence.

>
>Cheers
>
>Phil Hobbs

?-)

Phil Hobbs

unread,
Feb 16, 2012, 9:02:33 AM2/16/12
to
No, you just need to use interrupts and count cycles. In the timer ISR,
you look at the timer count and jump over the right number of NOPs to
align the timing before triggering the ADC. On a modern processor it's
never more than one or two of them, unless you have something nasty like
a zillion-cycle hardware divide.

Spehro Pefhany

unread,
Feb 16, 2012, 9:12:29 AM2/16/12
to
Modern high-speed processors decouple the CPU from the peripherals
with a bus which has its own clock timing and latencies.

http://www.arm.com/products/system-ip/amba/amba-open-specifications.php






Best regards,
Spehro Pefhany
--
"it's the network..." "The Journey is the reward"
sp...@interlog.com Info for manufacturers: http://www.trexon.com
Embedded software/hardware/analog Info for designers: http://www.speff.com

John Devereux

unread,
Feb 16, 2012, 9:14:38 AM2/16/12
to
The Cortex M3 seems to be much nicer for irqs than the older ARMs - even
though the latter have the separate FIQ for fast response,

Also I gather the M0 is designed to be more "deterministic" with respect
to instruction execution times.


--

John Devereux

Spehro Pefhany

unread,
Feb 16, 2012, 9:19:43 AM2/16/12
to
On Thu, 16 Feb 2012 14:14:38 +0000, the renowned John Devereux
<jo...@devereux.me.uk> wrote:

>
>The Cortex M3 seems to be much nicer for irqs than the older ARMs - even
>though the latter have the separate FIQ for fast response,
>
>Also I gather the M0 is designed to be more "deterministic" with respect
>to instruction execution times.

The M0 core is more of an 8051 replacement, no? So us (we?) old farts
that are used to deterministic behavior..

Is there something equivalent to the Mplab "stopwatch" function in an
ARM IDE? It would be nice to get a rough idea of how long, say, a
multi-channel floating point Chebychev polynomial calculation takes to
run (in a simulation environment, it's easy enough to do if you have
hardware sitting on the desk).

Phil Hobbs

unread,
Feb 16, 2012, 9:23:53 AM2/16/12
to
Okay, so you're more modern than I am. Be that way. ;) In any case,
you don't need an FPGA to get coherent timing.

Rich Webb

unread,
Feb 16, 2012, 9:59:27 AM2/16/12
to
On Thu, 16 Feb 2012 09:19:43 -0500, Spehro Pefhany
<spef...@interlogDOTyou.knowwhat> wrote:

>On Thu, 16 Feb 2012 14:14:38 +0000, the renowned John Devereux
><jo...@devereux.me.uk> wrote:
>
>>
>>The Cortex M3 seems to be much nicer for irqs than the older ARMs - even
>>though the latter have the separate FIQ for fast response,
>>
>>Also I gather the M0 is designed to be more "deterministic" with respect
>>to instruction execution times.
>
>The M0 core is more of an 8051 replacement, no? So us (we?) old farts
>that are used to deterministic behavior..
>
>Is there something equivalent to the Mplab "stopwatch" function in an
>ARM IDE? It would be nice to get a rough idea of how long, say, a
>multi-channel floating point Chebychev polynomial calculation takes to
>run (in a simulation environment, it's easy enough to do if you have
>hardware sitting on the desk).

Rowley's CrossWorks does include an "ARM Simulator" as a target (i.e.,
one loads and runs the simulator in the same way one would load/run
through a J-Link JTAG).

From the user manual:
"The ARM Simulator target interface provides access to CrossStudio's ARM
instruction set simulator (ISS). The ISS simulates the ARM V4T, ARM
V5TE, ARM V6-M and ARM V7-M instruction sets as defined in appropriate
ARM Architecure Reference Manuals. The ARM architecture, core type and
memory endian to be simulated are specified by the project's code
generation properties.

The ISS supports MCR and MRC access to the 16 primary registers of the
System Control coprocessor (CP15) as defined in the ARM Architecture
Reference Manual. The MMU is simulated but the cache isn't. The ISS
supports MCR and MRC access to the Debug Communication Channel (CP14) as
defined in the ARM7TDMI Technical Reference Manual. The ISS supports a
limited subset of VFP instructions (CP10 and CP11) that enable C
programs that use the VFP to execute.

The ISS implements a 3 word instruction pre-fetch buffer."

I'd imagine that some of the other large vendors provide a similar
functionality but I don't have any direct experience with them.

--
Rich Webb Norfolk, VA

John Devereux

unread,
Feb 16, 2012, 10:18:36 AM2/16/12
to
Spehro Pefhany <spef...@interlogDOTyou.knowwhat> writes:

> On Thu, 16 Feb 2012 14:14:38 +0000, the renowned John Devereux
> <jo...@devereux.me.uk> wrote:
>
>>
>>The Cortex M3 seems to be much nicer for irqs than the older ARMs - even
>>though the latter have the separate FIQ for fast response,
>>
>>Also I gather the M0 is designed to be more "deterministic" with respect
>>to instruction execution times.
>
> The M0 core is more of an 8051 replacement, no? So us (we?) old farts
> that are used to deterministic behavior..
>
> Is there something equivalent to the Mplab "stopwatch" function in an
> ARM IDE? It would be nice to get a rough idea of how long, say, a
> multi-channel floating point Chebychev polynomial calculation takes to
> run (in a simulation environment, it's easy enough to do if you have
> hardware sitting on the desk).

Dunno (but looks like Rich does downthread).

I use a development board, a pio pin and a scope. Or if I am feeling
sophisticated I may use an internal timer!

I was impressed by the software floating point on gcc / cortex M3, I
posted the results here or on CAE recently. Can't find it now but my
simple benchmark gave:

Float operation benchmarks

Double Precision:
0.415us / 49.852 cycles /multiply
0.378us / 45.403 cycles /add
2.414us / 289.702 cycles /divide
Single Precision:
0.194us / 23.350 cycles /multiply
0.250us / 30.052 cycles /add
0.610us / 73.202 cycles / divide




--

John Devereux

Nico Coesel

unread,
Feb 16, 2012, 12:15:08 PM2/16/12
to
Phil Hobbs <pcdhSpamM...@electrooptical.net> wrote:

>josephkk wrote:
>>
>> On Wed, 15 Feb 2012 10:59:05 -0500, Phil Hobbs
>> <pcdhSpamM...@electrooptical.net> wrote:
>>
>> >On 02/14/2012 10:37 PM, josephkk wrote:
>> >> On Mon, 13 Feb 2012 00:14:30 -0500, Phil Hobbs
>> >> <pcdhSpamM...@electrooptical.net> wrote:
>> >>
>> >>>
>>
>> ?-)
>
>No, you just need to use interrupts and count cycles. In the timer ISR,
>you look at the timer count and jump over the right number of NOPs to
>align the timing before triggering the ADC. On a modern processor it's

Hmm, that sounds cumbersome. I use a synchronous bus or continously
running SPI for that sort of things. Let the hardware do the timing.

k...@att.bizzzzzzzzzzzz

unread,
Feb 16, 2012, 1:29:24 PM2/16/12
to
On Thu, 16 Feb 2012 09:23:53 -0500, Phil Hobbs
How do you get coherent timing when the CPU itself is indeterminate?

Phil Hobbs

unread,
Feb 16, 2012, 2:49:54 PM2/16/12
to
It isn't. Timer interrupts get serviced with a jitter equal to the
difference between the lengths of the longest and shortest instructions.
All you need to do is to iron that out.

Spehro Pefhany

unread,
Feb 16, 2012, 3:11:20 PM2/16/12
to
On Thu, 16 Feb 2012 15:18:36 +0000, John Devereux
<jo...@devereux.me.uk> wrote:

>
>
>Dunno (but looks like Rich does downthread).
>
>I use a development board, a pio pin and a scope. Or if I am feeling
>sophisticated I may use an internal timer!

Yeah, that's pretty much what I've used. If the timer is simulated
that should work okay, but it's not as convenient as the 'stopwatch'.

>I was impressed by the software floating point on gcc / cortex M3, I
>posted the results here or on CAE recently. Can't find it now but my
>simple benchmark gave:
>
>Float operation benchmarks
>
> Double Precision:
> 0.415us / 49.852 cycles /multiply
> 0.378us / 45.403 cycles /add
> 2.414us / 289.702 cycles /divide
> Single Precision:
> 0.194us / 23.350 cycles /multiply
> 0.250us / 30.052 cycles /add
> 0.610us / 73.202 cycles / divide

That's not bad. Once again, the importance of avoiding unnecessary
divides is indicated.

k...@att.bizzzzzzzzzzzz

unread,
Feb 16, 2012, 6:47:45 PM2/16/12
to
On Thu, 16 Feb 2012 14:49:54 -0500, Phil Hobbs
...which can be pretty large in itself. You were talking about using NOP
(loops) before.

Phil Hobbs

unread,
Feb 16, 2012, 8:36:17 PM2/16/12
to
If you don't have (or don't use) a slow hardware divide, it's usually
between one and three instruction cycles--not a big issue in most
cases. Probably a worst-case delay of six or seven cycles, which is
very cheap in the circumstances.

Phil Hobbs

unread,
Feb 16, 2012, 8:42:48 PM2/16/12
to
Nico Coesel wrote:
>
> Phil Hobbs <pcdhSpamM...@electrooptical.net> wrote:
>
> >josephkk wrote:
> >>
> >> On Wed, 15 Feb 2012 10:59:05 -0500, Phil Hobbs
> >> <pcdhSpamM...@electrooptical.net> wrote:
> >>
> >> >On 02/14/2012 10:37 PM, josephkk wrote:
> >> >> On Mon, 13 Feb 2012 00:14:30 -0500, Phil Hobbs
> >> >> <pcdhSpamM...@electrooptical.net> wrote:
> >> >>
> >> >>>
> >>
> >> ?-)
> >
> >No, you just need to use interrupts and count cycles. In the timer ISR,
> >you look at the timer count and jump over the right number of NOPs to
> >align the timing before triggering the ADC. On a modern processor it's
>
> Hmm, that sounds cumbersome. I use a synchronous bus or continously
> running SPI for that sort of things. Let the hardware do the timing.

If you just want to do continuous data acq, that works fine. I'm
usually interleaving it with control stuff, though. And once you have
the six lines of ASM, it works forever.

k...@att.bizzzzzzzzzzzz

unread,
Feb 16, 2012, 9:38:21 PM2/16/12
to
On Thu, 16 Feb 2012 20:36:17 -0500, Phil Hobbs
Or, depending on the processor, .3 to 10. Then there are the processors with
decoupled/asynchronous I/O... Super-scalars...

Again, you mentioned NOP timing, which is just nuts with a modern CPU
architecture.

John Devereux

unread,
Feb 17, 2012, 4:39:32 AM2/17/12
to
Yes. My first "benchmark" attempt came up like this:

Double Precision:
0.270us / 32.406 cycles /multiply
0.238us / 28.652 cycles /add
0.234us / 28.152 cycles /divide
Single Precision:
0.181us / 21.723 cycles /multiply
0.134us / 16.101 cycles /add
0.130us / 15.602 cycles / divide

Unfortunately I posted it before I realised my dumb benchmarking loop
was driving some of the variables to zero. So the FP routines could and
did exit early!

--

John Devereux

Phil Hobbs

unread,
Feb 17, 2012, 10:17:43 AM2/17/12
to
I dug into this a bit more, and discovered that NXP claims a fixed
12-cycle interrupt latency, so it looks as though they solved this
problem already. STM32F and L both say "maximum 12 cycles".

So obviously PICs still rule for timing. ;)

John Larkin

unread,
Feb 17, 2012, 11:20:27 AM2/17/12
to
On Fri, 17 Feb 2012 10:17:43 -0500, Phil Hobbs
The LPC3250 has *one* interrupt vector; the front-end of the ISR has
to figure out whodunnit. This is PDP-8 technology.


--

John Larkin, President Highland Technology Inc
www.highlandtechnology.com jlarkin at highlandtechnology dot com

Precision electronic instrumentation
Picosecond-resolution Digital Delay and Pulse generators
Custom timing and laser controllers
Photonics and fiberoptic TTL data links
VME analog, thermocouple, LVDT, synchro, tachometer
Multichannel arbitrary waveform generators

Rich Webb

unread,
Feb 17, 2012, 11:54:51 AM2/17/12
to
On Fri, 17 Feb 2012 08:20:27 -0800, John Larkin
<jjla...@highNOTlandTHIStechnologyPART.com> wrote:

>On Fri, 17 Feb 2012 10:17:43 -0500, Phil Hobbs
><pcdhSpamM...@electrooptical.net> wrote:

[deeply nested snippage]
>>I dug into this a bit more, and discovered that NXP claims a fixed
>>12-cycle interrupt latency, so it looks as though they solved this
>>problem already. STM32F and L both say "maximum 12 cycles".
>>
>>So obviously PICs still rule for timing. ;)
>>
>>Cheers
>>
>>Phil Hobbs
>
>The LPC3250 has *one* interrupt vector; the front-end of the ISR has
>to figure out whodunnit. This is PDP-8 technology.

It's broadly an architectural issue with some generations of the ARM
core. The Hitex "Insider's Guide" series has a good summary, if I may
insert a small portion (rather than try to paraphrase and get it wrong)
from their "Insider's Guide to the STM32 ARM(R) Based Microcontroller":

"One of the key improvements of the Cortex core over the earlier ARM
CPUs is its interrupt structure and exception handling. The ARM7 and
ARM9 CPUs had two interrupt lines: the fast interrupt and the general
purpose interrupt line. These two interrupt lines had to support all of
the interrupt sources within a given manufacturer’s microcontroller. How
this was done varied according to the implementation, so while the
techniques used were broadly the same, the implementation differed
between manufacturers. The ARM7 and ARM9 interrupt structure suffers
from two further problems. Firstly it is not deterministic; the time
taken to terminate or abort an instruction under execution when the
interrupt occurs is variable. This may not be a problem for many
applications, but it is a big issue in real-time control.

...

"The Nested Vector Interrupt Controller is a standard unit within the
Cortex core. This means that all Cortex-based microcontrollers will have
the same interrupt structure, regardless of manufacturer. Thus
application code and operating systems can be easily ported from one
microcontroller to another and the programmer does not need to learn a
whole new set of registers. The NVIC is also designed to have a very low
interrupt latency. This is both a feature of the NVIC itself and of the
Thumb-2 instruction set which allows multi-cycle instructions such as
load and store multiple to be interruptible. This interrupt latency is
also deterministic, with several advanced interrupt handling features
that support real-time applications. ... The STM32 NVIC has been
synthesised with a maximum of 43 maskable interrupt lines.

...

"When an interrupt is raised by a peripheral, the NVIC will start the
Cortex CPU serving the interrupt. As the Cortex CPU enters its interrupt
mode, it will push a set of registers onto the stack. Importantly this
is done in microcode, so there is no instruction overhead in the
application code. While the stack frame is being saved, the starting
address of the interrupt service routine is fetched on the instruction
bus. Thus the time taken from the interrupt being raised to reaching the
first instruction in the interrupt routine is just 12 cycles."

So it appears that the 12 cycle behavior should be available on all
Cortex cores. I'll admit that I haven't really probed this for myself,
as in my current application space a few microseconds either way are
invisible.

Phil Hobbs

unread,
Feb 17, 2012, 12:40:37 PM2/17/12
to
My early stuff has started to get reinvented too. ;)

k...@att.bizzzzzzzzzzzz

unread,
Feb 17, 2012, 12:56:52 PM2/17/12
to
On Fri, 17 Feb 2012 10:17:43 -0500, Phil Hobbs
Doesn't do anything, but it's sure consistent! ;-)

0 new messages