Reply by Robert Wessel December 19, 20112011-12-19
On Mon, 19 Dec 2011 06:12:32 -0600, "RCIngham"
<robert.ingham@n_o_s_p_a_m.gmail.com> wrote:

>>On Fri, 16 Dec 2011 21:24:08 -0600, Les Cargill >><lcargill99@comcast.com> wrote: >> >>>Hans-Bernhard Br&#65533;ker wrote: >>>> On 16.12.2011 22:57, Don Y wrote: >>>> >>>>> Note that some implementations fail to clear that memory on startup! >>>> >>>> Such implementations would have to be classified as spectacularly >>>> broken. You would need a pretty strong excuse for using any such >>>> toolchain despite such failures. >>>> >>>>> I.e., "0x27" is just as likely to be an uninitialized value. >>>> >>>> No. It's possible, but nowhere near as likely. >>>> >>>> You can make some implementations not intialize the .bss region (or >>>> meddle with the startup to that end), but the ones where that happens >by >>>> accident are certainly _far_ outnumbered by the standard-conforming >ones. >>> >>>There is no such standard. It's a nicety of desktop dev. tools. >> >> >>It's a requirement of all C implementations, hosted or freestanding: >> >>From the C99 standard: >> >>"5.1.2 Execution environments >>(1) Two execution environments are defined: freestanding and hosted. >>In both cases, program startup occurs when a designated C function is >>called by the execution environment. All objects with static storage >>duration shall be initialized (set to their initial values) before >>program startup. The manner and timing of such initialization are >>otherwise unspecified. (...)" >> >>"6.7.8 Initialization >>(...) >>(10) If an object that has automatic storage duration is not >>initialized explicitly, its value is indeterminate. If an object that >>has static storage duration is not initialized explicitly, then: >>- if it has pointer type, it is initialized to a null pointer; >>- if it has arithmetic type, it is initialized to (positive or >>unsigned) zero; >>- if it is an aggregate, every member is initialized (recursively) >>according to these rules; >>- if it is a union, the first named member is initialized >>(recursively) according to these rules. >>(...)" >> >>Similar (actually slightly clearer) language exists in the C89 >>standard, but this was easier to quote. >> >>There is no wiggle room to allow a confirming implementation to avoid >>initializing what is commonly called BSS storage (aka static objects >>without explicit initializers). On many, probably most, >>implementations a simple clear of the area produces the required >>result. >> >>That there have been buggy implementations, or that some >>implementations allow you to modify the CRT startup code to avoid >>clearing BSS storage, is not in question, but in those cases you no >>longer have a "real" C environment. >> > >According to FOLDOC (which is usually right): >Block Started by Symbol (BSS) >The uninitialised data segment produced by Unix linkers. Objects in the bss >segment have only a name and a size but no value. > >Executable code is located in the code segment and initialised data in the >data segment. > >[http://foldoc.org/Block+Started+by+Symbol] > >Of course, that is Unix, not C.
That's true, but the *nix (and Windows) program loaders clear the BSS area to zeros, which, on most architectures, satisfies the requirements for non-explicitly initialized static variables in C (pointers to NULL, other values to unsigned zero or positive zero as appropriate). That's not true on all platforms, and on those the CRT* will require explicit code to clear the BSS area. IIRC, MS-DOS used to a specified amount of extra area when loading a MZ executable, which was often used for BSS storage, but it did not clear the area, so there was code in the CRT startup to do it. *In practice we usually consider the CRT and OS separately, but that's not true from the perspective of the standard, where there's just some amount of magic that must happen before the first line of main() is executed.
Reply by David Brown December 19, 20112011-12-19
On 19/12/11 13:52, Simon Clubley wrote:
> On 2011-12-19, David Brown<david@westcontrol.removethisbit.com> wrote: >> On 16/12/2011 21:01, Simon Clubley wrote: >>> >>> Sorry, bad wording. They are in a enumerated type; it's just that I set >>> the first state symbol in the type to start at one instead of zero. >>> >> >> That raises a good question - should you write code to handle undefined >> bad states, or should you write code that never gets into a bad state? >> > > I think you should try to do both. > > I try to write my code to the latter standard, but I assume I will not > always succeed. Therefore, I try to structure my code in such a way as > to try and force a internal consistancy check failure when I do make a > mistake. > > Simon. >
The problem with such a tactic is testing. You should not write code that you cannot, or have not, tested accurately - even if it is unlikely that it will run, it is still something else that could go wrong. And such error code will not run in a real system, unless you have made a mistake - so in the process of testing you should find and fix that mistake. But sometimes a simple, general "Stop the system, program error" mode can be appropriate. It can certainly be a help during development and debugging. In the case of uninitialised variables, of course, it is totally unnecessary. Static variables are always initialised - either explicitly, or implicitly zeroed by the C startup code. Automatic variables are either properly initialised, or your compiler's warning messages tell you of the problem (and if your compiler won't do that, use a different compiler).
Reply by Simon Clubley December 19, 20112011-12-19
On 2011-12-19, David Brown <david@westcontrol.removethisbit.com> wrote:
> On 16/12/2011 21:01, Simon Clubley wrote: >> >> Sorry, bad wording. They are in a enumerated type; it's just that I set >> the first state symbol in the type to start at one instead of zero. >> > > That raises a good question - should you write code to handle undefined > bad states, or should you write code that never gets into a bad state? >
I think you should try to do both. I try to write my code to the latter standard, but I assume I will not always succeed. Therefore, I try to structure my code in such a way as to try and force a internal consistancy check failure when I do make a mistake. Simon. -- Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP Microsoft: Bringing you 1980s technology to a 21st century world
Reply by RCIngham December 19, 20112011-12-19
>On Fri, 16 Dec 2011 21:24:08 -0600, Les Cargill ><lcargill99@comcast.com> wrote: > >>Hans-Bernhard Br&#65533;ker wrote: >>> On 16.12.2011 22:57, Don Y wrote: >>> >>>> Note that some implementations fail to clear that memory on startup! >>> >>> Such implementations would have to be classified as spectacularly >>> broken. You would need a pretty strong excuse for using any such >>> toolchain despite such failures. >>> >>>> I.e., "0x27" is just as likely to be an uninitialized value. >>> >>> No. It's possible, but nowhere near as likely. >>> >>> You can make some implementations not intialize the .bss region (or >>> meddle with the startup to that end), but the ones where that happens
by
>>> accident are certainly _far_ outnumbered by the standard-conforming
ones.
>> >>There is no such standard. It's a nicety of desktop dev. tools. > > >It's a requirement of all C implementations, hosted or freestanding: > >From the C99 standard: > >"5.1.2 Execution environments >(1) Two execution environments are defined: freestanding and hosted. >In both cases, program startup occurs when a designated C function is >called by the execution environment. All objects with static storage >duration shall be initialized (set to their initial values) before >program startup. The manner and timing of such initialization are >otherwise unspecified. (...)" > >"6.7.8 Initialization >(...) >(10) If an object that has automatic storage duration is not >initialized explicitly, its value is indeterminate. If an object that >has static storage duration is not initialized explicitly, then: >- if it has pointer type, it is initialized to a null pointer; >- if it has arithmetic type, it is initialized to (positive or >unsigned) zero; >- if it is an aggregate, every member is initialized (recursively) >according to these rules; >- if it is a union, the first named member is initialized >(recursively) according to these rules. >(...)" > >Similar (actually slightly clearer) language exists in the C89 >standard, but this was easier to quote. > >There is no wiggle room to allow a confirming implementation to avoid >initializing what is commonly called BSS storage (aka static objects >without explicit initializers). On many, probably most, >implementations a simple clear of the area produces the required >result. > >That there have been buggy implementations, or that some >implementations allow you to modify the CRT startup code to avoid >clearing BSS storage, is not in question, but in those cases you no >longer have a "real" C environment. >
According to FOLDOC (which is usually right): Block Started by Symbol (BSS) The uninitialised data segment produced by Unix linkers. Objects in the bss segment have only a name and a size but no value. Executable code is located in the code segment and initialised data in the data segment. [http://foldoc.org/Block+Started+by+Symbol] Of course, that is Unix, not C. --------------------------------------- Posted through http://www.EmbeddedRelated.com
Reply by Niklas Holsti December 19, 20112011-12-19
On 11-12-13 12:49 , Alessandro Basili wrote:
> On 12/13/2011 12:06 AM, Anony Mous wrote: >> On 12/12/2011 3:01 PM, Alessandro Basili wrote: >>> I just started to (re)design the software for an embedded application on >>> a very old DSP (ADSP21020 32bit floating point) and I was trying to look >>> for some good books on embedded software development since I wanted to >>> start it right from the beginning rather than chase it later on. > [...] > >> >> Not specifically on the ADSP21020, but the best embedded software book >> that I've read is Practical UML Statecharts in C/C++ by Miro Samek. >> http://www.state-machine.com/psicc2/index.php >> > > That sounds good, I actually wanted to handle the whole code through > hierarchical state machines. My biggest concern is the Time Analysis, or > a strategy to keep the execution time under control. The application > should not be very demanding in terms of computation but I still have to > serve serial port commands and I wanted to avoid the usage of the > interrupts.
It seems to me that these plans and concerns don't quite match up. State machines help to master logical and functional complexity. By reifying (part of) the state as state variables, it becomes easier to make the state evolve a step at a time, and to make major ad-hoc modifications to the state, for example to reset some part of the software to its initial state. But state machines do not help with timing in real-time systems. In my opinion, the proper tool for timing problems in complex systems is preemptive and prioritized execution, which includes interrupts and perhaps several threads. Before you exclude them from your design space, you should be sure that they are not needed. To do so, you must find and consider the response-time requirements for your SW. For example, if some I/O interface requires a SW response in 100 microseconds, you can avoid interrupts only by making sure that your SW polls the interface at that rate, which may force you to insert polling code within many unrelated functions and activities. As those functions evolve during SW development, you will have to maintain the polling code and ensure that the duration between polls is always small enough. This can be quite a burden, and must be done accurately if the SW is to be reliable. Hardware features, such as FIFOs, that relax response-time requirements help with this problem, but it seems (from other posts in this thread) that your system lacks such features, at least in some places. If the SW is built around some kind of event model, with a central event-handling loop as in some state-machine implementations, the polling can be centralized to the loop, as long as no event needs more than 100 microseconds to be processed. But to ensure that each event is processed rapidly enough, you may have to break up logical events into a kind of "micro-events", which adds states to the state machines. Alternatively, you can insert polling code within the processing of a single event, again adding complexity and fragility. Another way to try to systematize a non-preemptive design is to use a cyclic major-frame/minor-frame schedule. But this has the same problems of polling frequency, as illustrated by the daisy wheel printer software that Walter Banks described in one of his posts in this thread (2011-12-18). On 11-12-16 07:49 , Alessandro Basili wrote: > I do believe that for these kind of applications the complexity > of multi-tasking or multi-threading is not necessary and a > simple hierarchical state machine may get the job done, It may, but I consider that approach -- a single-thread, even with a state machine -- quite risky for the development process. If the real-time requirements turn out to be harder than can be supported in the straight-forward state machine, you will either have to complicate the state machine, as above, or add threads or interrupt handlers. When the timing requirements are stringent, multi-threading under a suitable kernel usually *removes* complexity from the application code. > but since I have to serve the serial port in a timeliness fashion > I'm not quite sure I would control the timing of the FSM. You are right to worry about this, especially if one of your aims is to speed up the serial communication. > I personally believe that interrupts should be setting flags > and that's it, That seems an artificial and problematic limitation. You would be using a high-priority, low-latency mechanism (the interrupt handler) just to send a slow message (the flag) to tell a low-priority, high-latency actor (the main thread) to do something, eventually, that in most cases should be done urgently. The division of labour between the interrupt handlers and the other threads should be driven by the response-time requirements. The standard approach is that the interrupt handler does whatever must be done at that level, and then the handler signals (triggers) a thread, at an appropriate priority level, to do the rest of the response to the interrupt. This thread may be a dedicated one, ensuring that it is not busy when the interrupt occurs. > in this way the synchronization is totally handled at the FSM level When the FSM gets around to it... > and I shouldn't chase funny combinations of interrupts occurring in > various moments (how would I test that???). Interrupts always occur in funny combinations, however you handle them. But you can usually write the handler to prevent nested interrupts, if that feels safer. > I think synchronization is really complex whenever you are down to the > multi-thread business and/or have multiple interrupt servicing. Multiple threads and multiple interrupts, with a proper kernel, can also simplify the system by isolating different functions from each other, especially for timing issues. > Given the old technology and luckily very few support for an OS (I > haven't found any), For the 21020 there used to be at least a kernel called Virtuoso, from Eonic Systems in Belgium. The company seems to exist still (www.eonic.com) but I found no mention of Virtuoso on their web. For this, and other questions on tools for the 21020, you can perhaps get some help from the European Space Agency. Philippe Armbruster (philippe.armbruster@esa.int) of the Data Systems division at ESA used to be in charge of the space-qualified European 21020 implementation, as I recall. > I was aiming to have a very simple, procedural design which > I believe would be much easier to test and to make it meet the specs. Easier to test -- yes. Easier to meet specs -- doubtful for the real-time specs. On 11-12-18 06:24 , Alessandro Basili wrote: > ... as I realized that I may be loosing > interrupts I started to wonder about a different mechanism, trying to > get under control the WCET of each function in order to meet timing, > even though how can I rely on my WCET analysis? Use an available static WCET-analysis tool for the 21020, and validate its parameters with some execution-time measurements on your system. Moreover, consider if your peripherals can block the 21020 by stealing memory-access cycles with DMA. This can sometimes have a considerable effect on execution time, and it can be hard to measure, since the DMA is episodic. -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ .
Reply by Alessandro Basili December 19, 20112011-12-19
On 12/18/2011 4:47 PM, Hans-Bernhard Br&#4294967295;ker wrote:
>> why you are so confident that if ld21k was created there *must* be an >> "ar21k" as well? > > Because they're built from the same source package, in a single go.
would you kindly have a look at the link I included instead of assuming the ar code *is included* in the binutils?
> >> Whoever started the effort of porting the binutils may have very well >> gotten tired in between and left the rest of the work to someone else. > > Can't happen. Either the binutils build was configured to support a > format, or it wasn't. There's no such thing as ld knowing about a > format, but not ar. They both use the same bfd library to handle object > file formats.
Your confidence is good on one hand but it maybe read as arrogance on another. I suggest you download the package from the link I posted and have a look yourself to the content. If you don't have time to do that I can understand, but if I were you I would not assume what can and cannot be in such a strong way.
Reply by David Brown December 19, 20112011-12-19
On 17/12/2011 08:01, Don Y wrote:
> Hi Rob, > > On 12/16/2011 5:55 PM, Rob Gaddi wrote: >> On Fri, 16 Dec 2011 15:45:58 -0700, Don Y wrote: >> >>> Where signedness is a real issue is when you try to use chars as "really >>> small ints" -- short shorts! There, it is best to define a type to >>> make this usage more visible (e.g., "small_counter") and, in that >>> typedef, you can make the signedness explicit. >> >> Isn't this why God and ISO have given us stdint.h? So that you can >> distinguish between an int8_t, a uint8_t, and a char? > > I don't like exposing basic types for "special needs". > If, for example, I have "user identifiers", I'd rather > have a uid_t that I can map onto <whatever> rather than > deciding uid's "will" fit in a uint8 -- only to discover, > later, that I *really* need them to be unsigned shorts > (then I have to look at each thing that could be a uid > and change its declaration to be "unsigned short"). > > If, instead, I treat them as "uid_t"s, I just change a > typedef. > > (I wish C was more strongly typed)
That's why we have typedef. typedef uint8_t small_counter; or even typedef uint_fast8_t small_counter; Then everyone is happy.
Reply by David Brown December 19, 20112011-12-19
On 16/12/2011 21:01, Simon Clubley wrote:
> On 2011-12-16, Vladimir Vassilevsky<nospam@nowhere.com> wrote: >> >> Simon Clubley wrote: >> >>> I find that the number of unsigned integers in my code is _vastly_ >>> greater overall than the number of signed integers. >>> >>> Personally, I think C should have made unsigned integers the default. >> >> Hell NO. >> >> Been burned many times with mixed signed/unsigned arithmetic, I consider >> all of the integers signed unless they explicitly meant to be unsigned. >> > > Is that more to do with how C handles signed/unsigned type conversions > or some issue around signed type conversions in general ? > > I think I know where you are coming from; I've seen reports of various > type conversion issues in C which surprised me, but given the type of > things I use C for (mainly low level work), I have not yet been caught > by them. > > Still, I know this is a issue that people have differing opinions on for > various reasons and I realise that not everyone prefers unsigned integers.
There are two common situations when signed/unsigned can cause trouble - when doing any multiplies or divides, or dealing with wrapparound or overflow. Unsigned arithmetic in C has clearly defined overflow semantics - it operates as mod 2^n arithmetic. But signed arithmetic has no such specifications, and the compiler can assume that it never overflows (since overflow is undefined, the compiler can do what it wants in that case). My preference these days is to be entirely explicit - I enable warnings on signed conversions, and explicitly cast as needed. I too use more unsigned types - but since everything is specified there is no "default" in my code.
> >>> BTW, on a related data representation note, another thing I like to do is >>> when I build something like a state machine is to start the numbers >>> assigned to the state symbols at value 1, instead of value 0 so that I >>> stand a greater chance of catching uninitialised state variables. >> >> Bad style. State variables should be special class or enumerated type. >> > > Sorry, bad wording. They are in a enumerated type; it's just that I set > the first state symbol in the type to start at one instead of zero. >
That raises a good question - should you write code to handle undefined bad states, or should you write code that never gets into a bad state?
> Simon. >
Reply by Walter Banks December 18, 20112011-12-18

Duane Mattern wrote:

> On 12/14/2011 3:41 PM, Walter Banks wrote: > > Alessandro > > I have worked on some major software disasters and one question that > > should be asked early is, "What works?" > > Is what works self contained enough for retention as a separate module? > > > > Software like anything else is subject to the same rules of reliability that > > anything is. That conceptually is useful in breaking apart the problem. > > > > In general that means that software components should be isolated > > except through well defined interfaces. Breaking large modules into > > several component parts will often improve overall reliability of the > > software significantly especially if all components are not called > > every time the module is called. (I have some interesting proof for > > this is anyone is interested) > > > > Samek's state machine work is interesting but has some problems > > as applications grow into real application size. In most state machine > > based applications system timing starts to become a problem and > > various bandaid solutions start to be applied, usually trading > > complexity for small minor timing fixes. Eventually the bandaid solutions > > will start to interfere with system dependencies and over all reliability > > will begin to drop. > > > > Regards, > > Walter Banks > > Byte Craft Limited > > Walter, > Samek's state machine book was a real eye opener for me, but if you > think that it > will break down with "larger" applications, can you suggest an > alternative approach > for handling multi-level state machines? I've used IBM's native UML > code generator > (iLogic Rational) and it was a bit verbose but worked well for the small > application > that I was testing it on. I would not want to stop using the UML > hierarchical > statecharts, so I need some mechanism by which to generate code from them. > If I don't have an IDE that does the UML code generation for me, > then I'll use Samek's approach until I find something better.
First I like Samek's book , it is well written and has a lot of good material in it. This issue is the problem that has been found in most large state machines. Every state is combination of all of the possible state combinations that can reach that point. As the state count increases the number of reliability series terms grows and the system reliability decreases. Reliable but not perfect code can tolerate some failure and the system will continue to function. At some point as the the code grows it becomes fragile where bug arrival rate and death rate are about equal because the application cannot tolerate the little but non fatal failures. Reducing the number of series terms in the reliability calculation does a lot for extending the system reliability as the system gets more complex. One test that we have used in applications is to assign some arbitrary reliability for each piece of straight line code (say 1) and then solve the system reliability. It is not a shining example objective reliability prediction but it does help identify the parts of an application that has potential reliability issues that should be focused on. Walter Banks Byte Craft Limited
Reply by Walter Banks December 18, 20112011-12-18

Alessandro Basili wrote:

> Even though I may agree with you that breaking large modules into small > ones may improve reliability, I cannot backup my opinion with anything > else than some personal experience and observations. It would be > interesting to see what kind of proof can be give the grounds to this > approach.
I have been having some private email discussions about this thread and an example that first focused me about the impact of standard reliability math on software was brought home in a early software project that I had a contract for. This project involved fixing daisy wheel printer software that some one had written that was implemented as a strange state machine that was losing characters both on paper and part of the serial protocol. The printer had evolved over time to have more features and support faster serial speeds. It had what appeared to be random failures resulting in increasing customer complaints. Among other things the printer was being used to print banking check masters. A total of maybe a 100 lines of code (out of about 20k lines) that changed the scheduler from truly round robin with about 75 items in the loop many of them duplicated because of there need to be serviced in less time than the loop time. Most items most of the time didn't execute but sometimes many did all contributing to the series communication irregularities and timing problems for everybody. The serial communication was separated from the printer parts and two co_routine loops were created. The implementation had no interrupts. Instead of executing all the possible printer functions each time I executed one printer function dropping the 75 count back to about 30 eliminating some boolean flags and duplicate and split functions. The printer would actually run faster than previously and about 30% of the processor (F8 it does go back) still available. Most of the code was unchanged (about 0.5% of the code was changed) The primary failure was timing and in a few cases random combinations of the order of execution of events. The two conflicting timing parts serial data and actual print functions were isolated so they would not interfere with each other. The fix actually took less than a week and rigorous testing on the site of a particular critical (and knowledgeable) customer confirmed the approach. Walter Banks Byte Craft Limited