On 2020-06-05 06:45, David Brown wrote:> On 05/06/2020 11:56, Phil Hobbs wrote: >> On 2020-06-05 05:48, Tom Gardner wrote: >>> On 05/06/20 10:25, Phil Hobbs wrote: >>>> On 2020-06-05 05:07, Tom Gardner wrote: >>>>> On 05/06/20 09:45, Phil Hobbs wrote: >>>>>> On 2020-06-05 04:05, David Brown wrote: >>>>>>> On 05/06/2020 04:24, jlarkin@highlandsniptechnology.com wrote: >>>>>>>> On Thu, 4 Jun 2020 22:30:14 +0200, David Brown >>>>>>>> <david.brown@hesbynett.no> wrote: >>>>>>>> >>>>>>>> >>>>>>>>>> >>>>>>>>>> That's what I have to do, use an oscilloscope to show >>>>>>>>>> programmers what >>>>>>>>>> their code does. You can often relate the pulse widths to paths >>>>>>>>>> through the code. >>>>>>>>>> >>>>>>>>> >>>>>>>>> A scope can be a fine tool for measuring code performance. I >>>>>>>>> like to >>>>>>>>> make sure there are a few test pins on our boards that can be >>>>>>>>> connected >>>>>>>>> to an oscilloscope precisely for measuring critical code. Not all >>>>>>>>> programmers understand how to instrument code for such >>>>>>>>> measurements, >>>>>>>>> unfortunately. >>>>>>>> >>>>>>>> Raise a port pin at the start of the routine, and drop it at the >>>>>>>> end. >>>>>>>> Maybe invert it a couple of times, at interesting points. >>>>>>> >>>>>>> And how do you know that you are raising it at the /real/ start >>>>>>> of the routine, and dropping it at the /real/ end of the routine? >>>>>>> I've seen people write code like : >>>>>>> >>>>>>> ... >>>>>>> set_pin_hi_macro(); >>>>>>> x = big_calculation(); >>>>>>> set_pin_lo_macro(); >>>>>>> ... >>>>>>> >>>>>>> without realising that the compiler (and processor, for more >>>>>>> advanced cpus) can re-order a lot of that and make the timing >>>>>>> seriously unrealistic. >>>>>> >>>>>> All the embedded stuff I recall doing in C/C++ defines the various >>>>>> port registers as volatile, so the compiler can't play games like >>>>>> that. >>>>>> >>>>>> How would you ever make embedded code work if the compiler could >>>>>> randomly optimize away or reorder port accesses? You could never >>>>>> even poll a hardware flag. >>>>> >>>>> I await David Brown's expert comment, but from >>>>> https://en.cppreference.com/w/c/language/volatile >>>>> >>>>> "This means that within a single thread of execution, a volatile >>>>> access cannot be optimized out or reordered relative to another >>>>> visible side effect that is separated by a sequence point from the >>>>> volatile access." >>>> >>>> The end of a statement is a sequence point. >>> >>> That helps, provided the code is written in a sane >>> /simple/ way. >>> >>> I'm it is possible to create cases which are >>> problematic. I've seen some remarkably bad code >>> in my time :( >> >> Me too, but DB was just blowing smoke at JL with that silly example. >> > > No. It was a side track, admittedly, but my logic is sound and matches > real-world cases. The fact that you don't understand exactly how > volatile works shows exactly my point - a lot of programmers /think/ > they understand volatile, but get it subtly wrong. (Perhaps when you've > read my longer reply, you'll see the point - I did not give many details > in my first reply to John.)You're blowing smoke at me now. Your example was incorrect--if the port register is declared volatile, the compiler is not at liberty to re-order code across sequence points such as the semicolon at the end of the statements. Thus the pin will go high before the 'big calculation' and low afterwards. Other things that interrupt the program flow (e.g. interrupts and context switches) can make the interval longer, but that would most often occur during the big calculation, so it's a real measurement. Cheers Phil Hobbs -- Dr Philip C D Hobbs Principal Consultant ElectroOptical Innovations LLC / Hobbs ElectroOptics Optics, Electro-optics, Photonics, Analog Electronics Briarcliff Manor NY 10510 http://electrooptical.net http://hobbs-eo.com
KiCad Spice, Anyone Tried It?
Started by ●June 2, 2020
Reply by ●June 5, 20202020-06-05
Reply by ●June 5, 20202020-06-05
On 2020-06-05 06:39, David Brown wrote:> On 05/06/2020 11:25, Phil Hobbs wrote: >> On 2020-06-05 05:07, Tom Gardner wrote: >>> On 05/06/20 09:45, Phil Hobbs wrote: >>>> On 2020-06-05 04:05, David Brown wrote: >>>>> On 05/06/2020 04:24, jlarkin@highlandsniptechnology.com wrote: >>>>>> On Thu, 4 Jun 2020 22:30:14 +0200, David Brown >>>>>> <david.brown@hesbynett.no> wrote: >>>>>> >>>>>> >>>>>>>> >>>>>>>> That's what I have to do, use an oscilloscope to show >>>>>>>> programmers what >>>>>>>> their code does. You can often relate the pulse widths to paths >>>>>>>> through the code. >>>>>>>> >>>>>>> >>>>>>> A scope can be a fine tool for measuring code performance. I >>>>>>> like to >>>>>>> make sure there are a few test pins on our boards that can be >>>>>>> connected >>>>>>> to an oscilloscope precisely for measuring critical code. Not all >>>>>>> programmers understand how to instrument code for such measurements, >>>>>>> unfortunately. >>>>>> >>>>>> Raise a port pin at the start of the routine, and drop it at the end. >>>>>> Maybe invert it a couple of times, at interesting points. >>>>> >>>>> And how do you know that you are raising it at the /real/ start of >>>>> the routine, and dropping it at the /real/ end of the routine? >>>>> I've seen people write code like : >>>>> >>>>> ... >>>>> set_pin_hi_macro(); >>>>> x = big_calculation(); >>>>> set_pin_lo_macro(); >>>>> ... >>>>> >>>>> without realising that the compiler (and processor, for more >>>>> advanced cpus) can re-order a lot of that and make the timing >>>>> seriously unrealistic. >>>> >>>> All the embedded stuff I recall doing in C/C++ defines the various >>>> port registers as volatile, so the compiler can't play games like that. >>>> >>>> How would you ever make embedded code work if the compiler could >>>> randomly optimize away or reorder port accesses? You could never >>>> even poll a hardware flag. >>> >>> I await David Brown's expert comment, but from >>> https://en.cppreference.com/w/c/language/volatile >>> >>> "This means that within a single thread of execution, a volatile >>> access cannot be optimized out or reordered relative to another >>> visible side effect that is separated by a sequence point from the >>> volatile access." >> >> The end of a statement is a sequence point. >> > > The relevant of this is if you have: > > volatile int v1, v2; > > then you are not guaranteed anything about the number or order of reads > if you write: > > x = v1 + v2 + v2 + v1; > > Each of "v1" and "v2" will be read, but they can be read once or twice > and in any order, since there is no sequence point between their accesses. > > >Yeah, but your example had semicolons. Keep going, bro, you're digging a nice deep hole. Cheers Phil Hobbs -- Dr Philip C D Hobbs Principal Consultant ElectroOptical Innovations LLC / Hobbs ElectroOptics Optics, Electro-optics, Photonics, Analog Electronics Briarcliff Manor NY 10510 http://electrooptical.net http://hobbs-eo.com
Reply by ●June 5, 20202020-06-05
On 2020-06-05 07:00, Tom Gardner wrote:> On 05/06/20 09:28, Phil Hobbs wrote: >> On 2020-06-05 03:38, Tom Gardner wrote: >>> On 05/06/20 02:51, Phil Hobbs wrote: >>>> On 2020-06-04 12:42, Tom Gardner wrote: >>>>> On 04/06/20 17:22, jlarkin@highlandsniptechnology.com wrote: >>>>>> On Thu, 4 Jun 2020 16:10:06 +0100, Tom Gardner >>>>>> <spamjunk@blueyonder.co.uk> wrote: >>>>>> >>>>>>> On 04/06/20 00:59, John Larkin wrote: >>>>>>>> On Thu, 4 Jun 2020 00:25:35 +0100, Tom Gardner >>>>>>>> <spamjunk@blueyonder.co.uk> wrote: >>>>>>>> >>>>>>>>> On 03/06/20 21:21, John Larkin wrote: >>>>>>>>>> On Wed, 3 Jun 2020 17:33:30 +0100, Tom Gardner >>>>>>>>>> <spamjunk@blueyonder.co.uk> wrote: >>>>>>>>>>> >>>>>>>>>>> The similarities outweigh the differences. >>>>>>>>>> >>>>>>>>>> The similarities of that statement, six times, are tedious. >>>>>>>>> >>>>>>>>> Your misapprehensions on this topic are legion >>>>>>>>> and repetitive. >>>>>>>>> >>>>>>>>> >>>>>>>>>> Do you design electronics? >>>>>>>>> >>>>>>>>> I did, for decades. >>>>>>>>> >>>>>>>>> I also designed and implemented server-side software >>>>>>>>> for decades. >>>>>>>>> >>>>>>>>> Do you implement such software? >>>>>>>>> >>>>>>>>> (I believe you have said you leave that to others.) >>>>>>>> >>>>>>>> I wrote two compilers and three pre-emptive RTOSs and maybe a >>>>>>>> hundred >>>>>>>> embedded programs, >>>>>>> It is worth pointing out that all of those examples are >>>>>>> (almost certainly) very different to general purpose software >>>>>>> and, especially, to application servers and the applications >>>>>>> that run within them. >>>>>> >>>>>> What's not general about an RTOS? Thousands of units were sold. >>>>> >>>>> It is a single specialised program than can be used in >>>>> different end equipment. >>>>> >>>>> There's far more to software and software development >>>>> than that. >>>>> >>>>> >>>>> >>>>>>> Ditto your organisation and the organisations that write/maintain >>>>>>> such codebases. >>>>>>> >>>>>>> Thus it is unsurprising that you do not understand modern >>>>>>> medium/large scale software and software practices. Especially >>>>>>> the congruences with medium/large scale hardware. >>>>>> >>>>>> This is an electronic design group. I deal with the software that >>>>>> makes electronic instruments work. My relationship to servers is that >>>>>> I have to talk to them, feed them javascript and UDP packets and >>>>>> such. >>>>> >>>>> No doubt. >>>>> >>>>> But it confirms that you have no experience of creating >>>>> large-scale software systems using modern techniques. >>>>> >>>>> It is therefore unsurprising that you haven't understood >>>>> the congruences with hardware. >>>> >>>> Congruences between large scale software "using modern techniques" >>>> and hardware? Interesting! Since you're obviously expert in both, >>>> I'd like to learn more. >>> >>> More of a jack of all trades and master of none, by choice. >>> That has advantages and disadvantages. >>> >>> >>>> What are they, exactly? >>> >>> That would take a /long/ time to discuss, especially if >>> half the concepts are unfamiliar to the audience. >>> To take /one/ example >>> - how application server frameworks are designed >>> and implemented >>> - design patterns in the applications' components >>> and the way they are composed to form an application, >>> - design patterns used to communicate with the outside >>> world, >>> - antipatterns found in politics, development and design >>> >>> It is a good topic for a conversation in pubs. >>> >>> I normally start it by provocatively asking someone to >>> distinguish between hardware and software. They normally >>> claim it is obvious and come up with a definition. Then >>> I introduce them to examples that don't fit their definition. >>> Rinse and repeat. > >>> Sometimes it can take a long time to realise what the >>> proponents of a particular technique are doing, because >>> they use unfamiliar terms to describe things that are >>> standard in other fields. >>> >>> Some examples: >>> - Jackson's Structured Programming >>> - Harel StateCharts >>> - many of the more esoteric UML diagrams >>> - Hatley and Pirbhai's Strategies for Realtime Systems >>> Specification, as successfully used for the Boeing 777 >>> - and, obviously, object oriented programming >> >> So where are the applications to hardware? Even one detailed example >> would be interesting. > > Not application so much as a lack of difference between > hardware and software. > > Harel StateCharts are a formalisation of finite state > machine diagrams, which can obviously be implemented > in hardware, software, or a combination of the two. > > UML diagrams are so legion that it is embarassing. > The "Unified" is not far off the superset of diagrams > from other methodologies. The the well known ones are > class hierarchy diagrams, there are also "swimlane > interaction" diagrams, and some that are hierarchical > structural compositions of software components - just > like hierarchical schematics complete with i/o ports > connected by wires. > > Hatley and Pirbhai is particularly interesting. > They have two parallel decomposition hierarchies: > specification and implementation. Any of the > subcomponents can be specified in any way convenient, > e.g. FSMs. > > Every element in the specification hierarchy must > have one or more corresponding entities in the > implementation hierarchy, and they can be hardware, > software, mechanical etc. > > While the specification hierarchy is almost completely > executable (and there may be tools to do that), the > only part that Hatley And Pirbhai automated was a > database that tracked the complex mappings between > the various parts of the specification and > implementation hierarchy components. In aerospace > trackability is rather important. > > In the enterprise arena, many patterns are recognisable, > some even with accurately suggestive names such as > "filter", "multiplexer", "demultiplexer", "event", > "asynchronous X", "synchronous X", "control bus", > "message bus", "channel". > > One of many starting points is > https://www.enterpriseintegrationpatterns.com/patterns/messaging/index.htmlOkay, so you don't actually have any hardware examples. A pity. Cheers Phil Hobbs -- Dr Philip C D Hobbs Principal Consultant ElectroOptical Innovations LLC / Hobbs ElectroOptics Optics, Electro-optics, Photonics, Analog Electronics Briarcliff Manor NY 10510 http://electrooptical.net http://hobbs-eo.com
Reply by ●June 5, 20202020-06-05
On 2020-06-05 06:24, David Brown wrote:> On 05/06/2020 11:57, Phil Hobbs wrote: >> On 2020-06-05 05:54, Phil Hobbs wrote: >>> On 2020-06-05 05:40, David Brown wrote: >>>> On 05/06/2020 10:54, Phil Hobbs wrote: >>>>> On 2020-06-04 22:58, whit3rd wrote: >>>>>> On Thursday, June 4, 2020 at 9:14:43 AM UTC-7, >>>>>> jla...@highlandsniptechnology.com wrote: >>>>>>> On Thu, 4 Jun 2020 16:10:35 +0100, Tom Gardner >>>>>>> <spamjunk@blueyonder.co.uk> wrote: >>>>>>> >>>>>>>> On 04/06/20 15:26, David Brown wrote: >>>>>> >>>>>>>>> If someone asked me to make a 5th order polynomial for >>>>>>>>> calibration, I'd say >>>>>>>>> "No. Let's look at the system and see how to handle it >>>>>>>>> /properly/". Any >>>>>>>>> polynomial above 3rd order is likely (not guaranteed, but >>>>>>>>> likely) to be wrong - >>>>>>>>> over-fitted, unstable, or both. >>>>>> >>>>>>> The official ITS thermocouple voltage-temperature equations range >>>>>>> from >>>>>>> 8th order to 14th order for various thermocouples. We make both >>>>>>> thermocouple acquisition and simulation products. If anyone >>>>>>> refused to >>>>>>> implement the ITS equations, of course I'd fire them, for >>>>>>> stupidity if >>>>>>> nothing else. >>>>>> >>>>>> But the ITS equations are a temperature scale, that apply to all >>>>>> thermocouples >>>>>> of a type. Calibration includes instrument peculiarities that >>>>>> aren't known >>>>>> except after construction of the whole instrument. Not >>>>>> all calibrations have continuous, well-behaved functional nature, >>>>>> but all polynomials do. >>>>>> >>>>>> The general class of calibrations isn't automatically fit to a >>>>>> polynomial. >>>>>> >>>>> >>>>> Any actual thermocouple pair has a well-behaved, gently nonlinear >>>>> characteristic V(T1,T2) that is a good match to any well-designed >>>>> polynomial, spline, or rational-function fit technique. (You can >>>>> even use rational trig functions, knock yourself out.) >>>>> >>>>> It really isn't rocket science to fit a smooth nonlinearity of at >>>>> most a few tens of percent over a wide temperature range, to any >>>>> accuracy remotely required for temperature measurements. >>>>> >>>>> Of course, if you do the calibration sufficiently badly, you can >>>>> make problems for yourself, but they aren't specifically >>>>> mathematical problems. >>>>> >>>> >>>> My impression of Whit's post was that the calibration in a real >>>> system might be for compensating for tolerances of bias resistors, >>>> parasitic effects of ESD protection devices, and other board-level >>>> effects. The calibration is likely to remain a smooth function that >>>> (unless you've really made a mess of the hardware), but it may no >>>> longer be a good fit for the polynomial picked by the thermocouple >>>> manufacturer. So fitting to a cubic spline (or other suitable >>>> function, as you mentioned) based on real-life board measurements is >>>> going to make more sense than trying to force usage of the original >>>> ITS polynomial. (If you are not basing it on measurements, it is >>>> not calibration.) >>>> >>> >>> We hope that the published coefficients are based on some actual >>> calibration, because AFAIK there's no sufficiently accurate >>> first-principles calculation available that applies to real devices. >>> >>> Resistor errors, offset voltages, leakage, and so on are normally >>> smallish smooth effects of the sort that cause no problems for >>> polynomial fitting. >>> >>> >>> I claim that polynomial fits get into trouble with: >>> >>> discontinuities in value or low-order derivatives; >>> >>> asymptotes (a biggie); >>> >>> large changes in local behaviour, e.g. localized oscillations; >>> >>> crappy fitting procedures, e.g. use of noisy data; >>> >>> full stop. >>> >> >> And ill-conditioned basis sets, of course, but that's not a >> fundamental problem with polynomials per se. >> > > Agreed. > > You might add over-fitting to the list, but that is a problem of > misunderstanding the use of polynomials rather than the polynomial itself. > > And even with good basis sets, high-order polynomials can be harder to > evaluate well on limited processors than low-order splines (cubic or > linear). Again, that is not a fundamental problem with polynomials, but > it is a practical issue. > > A single polynomial will also be difficult to fit if there is a curve > which simply doesn't match well with a reasonable degree polynomial. An > exponential decay will look smooth, but need a surprisingly high degree > polynomial to get a good approximation. You'll also have trouble with a > curve with distinct regions, which can occur as different physical > effects dominate in different parts of the graph. > > None of this excludes the possibility of using a single high-order > polynomial. But it can make it a lot less practical in comparison to a > low-order spline. > >We were talking about thermocouples and so forth, which are mildly perturbed straight lines that need only engineering accuracy. Polynomials work fine for that. Cheers Phil Hobbs -- Dr Philip C D Hobbs Principal Consultant ElectroOptical Innovations LLC / Hobbs ElectroOptics Optics, Electro-optics, Photonics, Analog Electronics Briarcliff Manor NY 10510 http://electrooptical.net http://hobbs-eo.com
Reply by ●June 5, 20202020-06-05
On 05/06/2020 13:59, Phil Hobbs wrote:> On 2020-06-05 06:45, David Brown wrote: >> On 05/06/2020 11:56, Phil Hobbs wrote: >>> On 2020-06-05 05:48, Tom Gardner wrote: >>>> On 05/06/20 10:25, Phil Hobbs wrote: >>>>> On 2020-06-05 05:07, Tom Gardner wrote: >>>>>> On 05/06/20 09:45, Phil Hobbs wrote: >>>>>>> On 2020-06-05 04:05, David Brown wrote: >>>>>>>> On 05/06/2020 04:24, jlarkin@highlandsniptechnology.com wrote: >>>>>>>>> On Thu, 4 Jun 2020 22:30:14 +0200, David Brown >>>>>>>>> <david.brown@hesbynett.no> wrote: >>>>>>>>> >>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> That's what I have to do, use an oscilloscope to show >>>>>>>>>>> programmers what >>>>>>>>>>> their code does. You can often relate the pulse widths to paths >>>>>>>>>>> through the code. >>>>>>>>>>> >>>>>>>>>> >>>>>>>>>> A scope can be a fine tool for measuring code performance. I >>>>>>>>>> like to >>>>>>>>>> make sure there are a few test pins on our boards that can be >>>>>>>>>> connected >>>>>>>>>> to an oscilloscope precisely for measuring critical code. Not >>>>>>>>>> all >>>>>>>>>> programmers understand how to instrument code for such >>>>>>>>>> measurements, >>>>>>>>>> unfortunately. >>>>>>>>> >>>>>>>>> Raise a port pin at the start of the routine, and drop it at >>>>>>>>> the end. >>>>>>>>> Maybe invert it a couple of times, at interesting points. >>>>>>>> >>>>>>>> And how do you know that you are raising it at the /real/ start >>>>>>>> of the routine, and dropping it at the /real/ end of the >>>>>>>> routine? I've seen people write code like : >>>>>>>> >>>>>>>> ... >>>>>>>> set_pin_hi_macro(); >>>>>>>> x = big_calculation(); >>>>>>>> set_pin_lo_macro(); >>>>>>>> ... >>>>>>>> >>>>>>>> without realising that the compiler (and processor, for more >>>>>>>> advanced cpus) can re-order a lot of that and make the timing >>>>>>>> seriously unrealistic. >>>>>>> >>>>>>> All the embedded stuff I recall doing in C/C++ defines the >>>>>>> various port registers as volatile, so the compiler can't play >>>>>>> games like that. >>>>>>> >>>>>>> How would you ever make embedded code work if the compiler could >>>>>>> randomly optimize away or reorder port accesses? You could never >>>>>>> even poll a hardware flag. >>>>>> >>>>>> I await David Brown's expert comment, but from >>>>>> https://en.cppreference.com/w/c/language/volatile >>>>>> >>>>>> "This means that within a single thread of execution, a volatile >>>>>> access cannot be optimized out or reordered relative to another >>>>>> visible side effect that is separated by a sequence point from the >>>>>> volatile access." >>>>> >>>>> The end of a statement is a sequence point. >>>> >>>> That helps, provided the code is written in a sane >>>> /simple/ way. >>>> >>>> I'm it is possible to create cases which are >>>> problematic. I've seen some remarkably bad code >>>> in my time :( >>> >>> Me too, but DB was just blowing smoke at JL with that silly example. >>> >> >> No. It was a side track, admittedly, but my logic is sound and >> matches real-world cases. The fact that you don't understand exactly >> how volatile works shows exactly my point - a lot of programmers >> /think/ they understand volatile, but get it subtly wrong. (Perhaps >> when you've read my longer reply, you'll see the point - I did not >> give many details in my first reply to John.) > > You're blowing smoke at me now. > > Your example was incorrect--if the port register is declared volatile, > the compiler is not at liberty to re-order code across sequence points > such as the semicolon at the end of the statements. Thus the pin will > go high before the 'big calculation' and low afterwards.I'm sorry, but you are /completely/ wrong here. In practice, as I noted, a compiler is unlikely to re-order these things unless it sees an advantage to it. But it is without doubt free to do so. Only observable behaviour has an order in C - things without observable behaviour have no order. Here is a variation on the original code: extern volatile unsigned char pin; unsigned int t = 1; void testmults(void) { pin = 1; for (unsigned int x = 1; x <= 5; x++) { t *= x; } pin = 0; } The assembly generated by gcc -O2 (x86-64) here is : testmults: imul eax, DWORD PTR t[rip], 120 mov BYTE PTR pin[rip], 1 mov BYTE PTR pin[rip], 0 mov DWORD PTR t[rip], eax ret t: .long 1 The compiler has turned the code into: void testmults(void) { unsigned int tmp = t * (1 * 2 * 3 * 4 * 5); pin = 1; pin = 0; t = tmp; }> > Other things that interrupt the program flow (e.g. interrupts and > context switches) can make the interval longer, but that would most > often occur during the big calculation, so it's a real measurement. >That is of course true, and useful to remember when measuring speed - but a side-track here.
Reply by ●June 5, 20202020-06-05
On 05/06/2020 14:01, Phil Hobbs wrote:> On 2020-06-05 06:39, David Brown wrote: >> On 05/06/2020 11:25, Phil Hobbs wrote: >>> On 2020-06-05 05:07, Tom Gardner wrote: >>>> On 05/06/20 09:45, Phil Hobbs wrote: >>>>> On 2020-06-05 04:05, David Brown wrote: >>>>>> On 05/06/2020 04:24, jlarkin@highlandsniptechnology.com wrote: >>>>>>> On Thu, 4 Jun 2020 22:30:14 +0200, David Brown >>>>>>> <david.brown@hesbynett.no> wrote: >>>>>>> >>>>>>> >>>>>>>>> >>>>>>>>> That's what I have to do, use an oscilloscope to show >>>>>>>>> programmers what >>>>>>>>> their code does. You can often relate the pulse widths to paths >>>>>>>>> through the code. >>>>>>>>> >>>>>>>> >>>>>>>> A scope can be a fine tool for measuring code performance. I >>>>>>>> like to >>>>>>>> make sure there are a few test pins on our boards that can be >>>>>>>> connected >>>>>>>> to an oscilloscope precisely for measuring critical code. Not all >>>>>>>> programmers understand how to instrument code for such >>>>>>>> measurements, >>>>>>>> unfortunately. >>>>>>> >>>>>>> Raise a port pin at the start of the routine, and drop it at the >>>>>>> end. >>>>>>> Maybe invert it a couple of times, at interesting points. >>>>>> >>>>>> And how do you know that you are raising it at the /real/ start of >>>>>> the routine, and dropping it at the /real/ end of the routine? >>>>>> I've seen people write code like : >>>>>> >>>>>> ... >>>>>> set_pin_hi_macro(); >>>>>> x = big_calculation(); >>>>>> set_pin_lo_macro(); >>>>>> ... >>>>>> >>>>>> without realising that the compiler (and processor, for more >>>>>> advanced cpus) can re-order a lot of that and make the timing >>>>>> seriously unrealistic. >>>>> >>>>> All the embedded stuff I recall doing in C/C++ defines the various >>>>> port registers as volatile, so the compiler can't play games like >>>>> that. >>>>> >>>>> How would you ever make embedded code work if the compiler could >>>>> randomly optimize away or reorder port accesses? You could never >>>>> even poll a hardware flag. >>>> >>>> I await David Brown's expert comment, but from >>>> https://en.cppreference.com/w/c/language/volatile >>>> >>>> "This means that within a single thread of execution, a volatile >>>> access cannot be optimized out or reordered relative to another >>>> visible side effect that is separated by a sequence point from the >>>> volatile access." >>> >>> The end of a statement is a sequence point. >>> >> >> The relevant of this is if you have: >> >> volatile int v1, v2; >> >> then you are not guaranteed anything about the number or order of >> reads if you write: >> >> x = v1 + v2 + v2 + v1; >> >> Each of "v1" and "v2" will be read, but they can be read once or twice >> and in any order, since there is no sequence point between their >> accesses. >> >> >> > > Yeah, but your example had semicolons. Keep going, bro, you're digging > a nice deep hole. >Semicolons and other sequence points are relevant for the abstract machine. They don't impose an ordering on the generated code, as long as the final results match between the abstract machine and the real assembly at points of "observable behaviour" - and then only the orders and values matter, not timing. This is known as the "as if" rule of C. Yes, I'll keep going - I believe you'll understand in the end. I've posted an example that you can test yourself on <https://godbolt.org>, and I hope you will try that before replying.
Reply by ●June 5, 20202020-06-05
On 2020-06-05 08:18, David Brown wrote:> On 05/06/2020 13:59, Phil Hobbs wrote: >> On 2020-06-05 06:45, David Brown wrote: >>> On 05/06/2020 11:56, Phil Hobbs wrote: >>>> On 2020-06-05 05:48, Tom Gardner wrote: >>>>> On 05/06/20 10:25, Phil Hobbs wrote: >>>>>> On 2020-06-05 05:07, Tom Gardner wrote: >>>>>>> On 05/06/20 09:45, Phil Hobbs wrote: >>>>>>>> On 2020-06-05 04:05, David Brown wrote: >>>>>>>>> On 05/06/2020 04:24, jlarkin@highlandsniptechnology.com wrote: >>>>>>>>>> On Thu, 4 Jun 2020 22:30:14 +0200, David Brown >>>>>>>>>> <david.brown@hesbynett.no> wrote: >>>>>>>>>> >>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> That's what I have to do, use an oscilloscope to show >>>>>>>>>>>> programmers what >>>>>>>>>>>> their code does. You can often relate the pulse widths to paths >>>>>>>>>>>> through the code. >>>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> A scope can be a fine tool for measuring code performance. I >>>>>>>>>>> like to >>>>>>>>>>> make sure there are a few test pins on our boards that can be >>>>>>>>>>> connected >>>>>>>>>>> to an oscilloscope precisely for measuring critical code. >>>>>>>>>>> Not all >>>>>>>>>>> programmers understand how to instrument code for such >>>>>>>>>>> measurements, >>>>>>>>>>> unfortunately. >>>>>>>>>> >>>>>>>>>> Raise a port pin at the start of the routine, and drop it at >>>>>>>>>> the end. >>>>>>>>>> Maybe invert it a couple of times, at interesting points. >>>>>>>>> >>>>>>>>> And how do you know that you are raising it at the /real/ start >>>>>>>>> of the routine, and dropping it at the /real/ end of the >>>>>>>>> routine? I've seen people write code like : >>>>>>>>> >>>>>>>>> ... >>>>>>>>> set_pin_hi_macro(); >>>>>>>>> x = big_calculation(); >>>>>>>>> set_pin_lo_macro(); >>>>>>>>> ... >>>>>>>>> >>>>>>>>> without realising that the compiler (and processor, for more >>>>>>>>> advanced cpus) can re-order a lot of that and make the timing >>>>>>>>> seriously unrealistic. >>>>>>>> >>>>>>>> All the embedded stuff I recall doing in C/C++ defines the >>>>>>>> various port registers as volatile, so the compiler can't play >>>>>>>> games like that. >>>>>>>> >>>>>>>> How would you ever make embedded code work if the compiler could >>>>>>>> randomly optimize away or reorder port accesses? You could >>>>>>>> never even poll a hardware flag. >>>>>>> >>>>>>> I await David Brown's expert comment, but from >>>>>>> https://en.cppreference.com/w/c/language/volatile >>>>>>> >>>>>>> "This means that within a single thread of execution, a volatile >>>>>>> access cannot be optimized out or reordered relative to another >>>>>>> visible side effect that is separated by a sequence point from >>>>>>> the volatile access." >>>>>> >>>>>> The end of a statement is a sequence point. >>>>> >>>>> That helps, provided the code is written in a sane >>>>> /simple/ way. >>>>> >>>>> I'm it is possible to create cases which are >>>>> problematic. I've seen some remarkably bad code >>>>> in my time :( >>>> >>>> Me too, but DB was just blowing smoke at JL with that silly example. >>>> >>> >>> No. It was a side track, admittedly, but my logic is sound and >>> matches real-world cases. The fact that you don't understand exactly >>> how volatile works shows exactly my point - a lot of programmers >>> /think/ they understand volatile, but get it subtly wrong. (Perhaps >>> when you've read my longer reply, you'll see the point - I did not >>> give many details in my first reply to John.) >> >> You're blowing smoke at me now. >> >> Your example was incorrect--if the port register is declared volatile, >> the compiler is not at liberty to re-order code across sequence points >> such as the semicolon at the end of the statements. Thus the pin will >> go high before the 'big calculation' and low afterwards. > > I'm sorry, but you are /completely/ wrong here. > > In practice, as I noted, a compiler is unlikely to re-order these things > unless it sees an advantage to it. But it is without doubt free to do > so. Only observable behaviour has an order in C - things without > observable behaviour have no order. > > Here is a variation on the original code: > > > extern volatile unsigned char pin; > > unsigned int t = 1; > > void testmults(void) { > pin = 1; > > for (unsigned int x = 1; x <= 5; x++) { > t *= x; > } > > pin = 0; > } > > The assembly generated by gcc -O2 (x86-64) here is : > > testmults: > imul eax, DWORD PTR t[rip], 120 > mov BYTE PTR pin[rip], 1 > mov BYTE PTR pin[rip], 0 > mov DWORD PTR t[rip], eax > ret > t: > .long 1 > > > The compiler has turned the code into: > > void testmults(void) { > unsigned int tmp = t * (1 * 2 * 3 * 4 * 5); > pin = 1; > pin = 0; > t = tmp; > } > > > > > >> >> Other things that interrupt the program flow (e.g. interrupts and >> context switches) can make the interval longer, but that would most >> often occur during the big calculation, so it's a real measurement. >> > > That is of course true, and useful to remember when measuring speed - > but a side-track here. >That's because the compiler optimized away the whole loop. If your big_calculation() wound up doing nothing, and so was optimized away like that, then your example above >>>>>>>>> set_pin_hi_macro(); >>>>>>>>> x = big_calculation(); >>>>>>>>> set_pin_lo_macro(); would correctly show that this was the case. Keep digging, man! Cheers Phil Hobbs -- Dr Philip C D Hobbs Principal Consultant ElectroOptical Innovations LLC / Hobbs ElectroOptics Optics, Electro-optics, Photonics, Analog Electronics Briarcliff Manor NY 10510 http://electrooptical.net http://hobbs-eo.com
Reply by ●June 5, 20202020-06-05
On 05/06/2020 13:26, Tom Gardner wrote:> > Thanks, David. I knew you would have chapter and verse(!) > at your fingertips! > > Such considerations lead me to believe that most programmers > that think they know C, actually don't. > > It also makes me believe that C has become part of the > problem(s) that implementers face. I would prefer it had > remained part of the solution.It is difficult to get the kind of flexibility and efficiency that C and C compilers can offer, without also having these kinds of risks and complications. It's usually not hard to get this kind of thing right - it just involves making sure that your calculation depends on a volatile read (after the pin high) to get started, and generates a volatile write with the result (before the pin low). The compiler can still move unrelated unobservable calculations inside the measurement period, but that's fairly unlikely to be a big issue. In general, as long as you remember that C doesn't have any timing information, and the generated code only matches your source code on observable behaviour, you'll be fine. (I don't know of any other language, other than assembly, that guarantees more than that. Even XC on XMOS does not, IIRC - it lets you check some aspects of timing, but does not give you control of the details of execution.) I showed in another post an example of where gcc re-arranges code around the volatile accesses: extern volatile unsigned char pin; unsigned int t = 1; void testmults(void) { pin = 1; for (unsigned int x = 1; x <= 5; x++) { t *= x; } pin = 0; } This generates: testmults: imul eax, DWORD PTR t[rip], 120 mov BYTE PTR pin[rip], 1 mov BYTE PTR pin[rip], 0 mov DWORD PTR t[rip], eax ret It is possible to get the ordering you want using empty inline assembly in gcc, avoiding the cost of actual volatile reads or writes (other than for "pin", of course): void testmults2(void) { pin = 1; asm volatile ("" : "=g" (t)); for (unsigned int x = 1; x <= 5; x++) { t *= x; } asm volatile("" :: "" (t)); pin = 0; } testmults2: mov BYTE PTR pin[rip], 1 imul eax, DWORD PTR t[rip], 120 mov DWORD PTR t[rip], eax mov BYTE PTR pin[rip], 0 ret The first "asm" says "do nothing, but t might be written" - thus the read of t for the calculation must come after the assembly line (which is volatile, and so ordered with the "pin = 1"). The second "asm" says "do nothing, using t as an input" - thus requiring t to be calculated before the assembly line is "executed". These sorts of things are fun, but a bit advanced for most users. It's usually safer to use "volatile" a bit more often, even if it costs an extra few cycles. <https://godbolt.org/z/mhsQwU>
Reply by ●June 5, 20202020-06-05
On 05/06/2020 14:24, Phil Hobbs wrote:> On 2020-06-05 08:18, David Brown wrote: >> On 05/06/2020 13:59, Phil Hobbs wrote: >>> On 2020-06-05 06:45, David Brown wrote: >>>> On 05/06/2020 11:56, Phil Hobbs wrote: >>>>> On 2020-06-05 05:48, Tom Gardner wrote: >>>>>> On 05/06/20 10:25, Phil Hobbs wrote: >>>>>>> On 2020-06-05 05:07, Tom Gardner wrote: >>>>>>>> On 05/06/20 09:45, Phil Hobbs wrote: >>>>>>>>> On 2020-06-05 04:05, David Brown wrote: >>>>>>>>>> On 05/06/2020 04:24, jlarkin@highlandsniptechnology.com wrote: >>>>>>>>>>> On Thu, 4 Jun 2020 22:30:14 +0200, David Brown >>>>>>>>>>> <david.brown@hesbynett.no> wrote: >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>>> That's what I have to do, use an oscilloscope to show >>>>>>>>>>>>> programmers what >>>>>>>>>>>>> their code does. You can often relate the pulse widths to >>>>>>>>>>>>> paths >>>>>>>>>>>>> through the code. >>>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> A scope can be a fine tool for measuring code performance. >>>>>>>>>>>> I like to >>>>>>>>>>>> make sure there are a few test pins on our boards that can >>>>>>>>>>>> be connected >>>>>>>>>>>> to an oscilloscope precisely for measuring critical code. >>>>>>>>>>>> Not all >>>>>>>>>>>> programmers understand how to instrument code for such >>>>>>>>>>>> measurements, >>>>>>>>>>>> unfortunately. >>>>>>>>>>> >>>>>>>>>>> Raise a port pin at the start of the routine, and drop it at >>>>>>>>>>> the end. >>>>>>>>>>> Maybe invert it a couple of times, at interesting points. >>>>>>>>>> >>>>>>>>>> And how do you know that you are raising it at the /real/ >>>>>>>>>> start of the routine, and dropping it at the /real/ end of the >>>>>>>>>> routine? I've seen people write code like : >>>>>>>>>> >>>>>>>>>> ... >>>>>>>>>> set_pin_hi_macro(); >>>>>>>>>> x = big_calculation(); >>>>>>>>>> set_pin_lo_macro(); >>>>>>>>>> ... >>>>>>>>>> >>>>>>>>>> without realising that the compiler (and processor, for more >>>>>>>>>> advanced cpus) can re-order a lot of that and make the timing >>>>>>>>>> seriously unrealistic. >>>>>>>>> >>>>>>>>> All the embedded stuff I recall doing in C/C++ defines the >>>>>>>>> various port registers as volatile, so the compiler can't play >>>>>>>>> games like that. >>>>>>>>> >>>>>>>>> How would you ever make embedded code work if the compiler >>>>>>>>> could randomly optimize away or reorder port accesses? You >>>>>>>>> could never even poll a hardware flag. >>>>>>>> >>>>>>>> I await David Brown's expert comment, but from >>>>>>>> https://en.cppreference.com/w/c/language/volatile >>>>>>>> >>>>>>>> "This means that within a single thread of execution, a volatile >>>>>>>> access cannot be optimized out or reordered relative to another >>>>>>>> visible side effect that is separated by a sequence point from >>>>>>>> the volatile access." >>>>>>> >>>>>>> The end of a statement is a sequence point. >>>>>> >>>>>> That helps, provided the code is written in a sane >>>>>> /simple/ way. >>>>>> >>>>>> I'm it is possible to create cases which are >>>>>> problematic. I've seen some remarkably bad code >>>>>> in my time :( >>>>> >>>>> Me too, but DB was just blowing smoke at JL with that silly example. >>>>> >>>> >>>> No. It was a side track, admittedly, but my logic is sound and >>>> matches real-world cases. The fact that you don't understand >>>> exactly how volatile works shows exactly my point - a lot of >>>> programmers /think/ they understand volatile, but get it subtly >>>> wrong. (Perhaps when you've read my longer reply, you'll see the >>>> point - I did not give many details in my first reply to John.) >>> >>> You're blowing smoke at me now. >>> >>> Your example was incorrect--if the port register is declared >>> volatile, the compiler is not at liberty to re-order code across >>> sequence points such as the semicolon at the end of the statements. >>> Thus the pin will go high before the 'big calculation' and low >>> afterwards. >> >> I'm sorry, but you are /completely/ wrong here. >> >> In practice, as I noted, a compiler is unlikely to re-order these >> things unless it sees an advantage to it. But it is without doubt >> free to do so. Only observable behaviour has an order in C - things >> without observable behaviour have no order. >> >> Here is a variation on the original code: >> >> >> extern volatile unsigned char pin; >> >> unsigned int t = 1; >> >> void testmults(void) { >> pin = 1; >> >> for (unsigned int x = 1; x <= 5; x++) { >> t *= x; >> } >> >> pin = 0; >> } >> >> The assembly generated by gcc -O2 (x86-64) here is : >> >> testmults: >> imul eax, DWORD PTR t[rip], 120 >> mov BYTE PTR pin[rip], 1 >> mov BYTE PTR pin[rip], 0 >> mov DWORD PTR t[rip], eax >> ret >> t: >> .long 1 >> >> >> The compiler has turned the code into: >> >> void testmults(void) { >> unsigned int tmp = t * (1 * 2 * 3 * 4 * 5); >> pin = 1; >> pin = 0; >> t = tmp; >> } >> >> > > That's because the compiler optimized away the whole loop. If your >Look again. It combined the loop - it did not optimise away the calculation. Please tell me you are not imagining that the C language has different rules for a "small" calculation and a "big" calculation?
Reply by ●June 5, 20202020-06-05
On 05/06/2020 15:13, David Brown wrote:> On 05/06/2020 14:24, Phil Hobbs wrote: >> On 2020-06-05 08:18, David Brown wrote: >>> On 05/06/2020 13:59, Phil Hobbs wrote: >>>> On 2020-06-05 06:45, David Brown wrote: >>>>> On 05/06/2020 11:56, Phil Hobbs wrote: >>>>>> On 2020-06-05 05:48, Tom Gardner wrote: >>>>>>> On 05/06/20 10:25, Phil Hobbs wrote: >>>>>>>> On 2020-06-05 05:07, Tom Gardner wrote: >>>>>>>>> On 05/06/20 09:45, Phil Hobbs wrote: >>>>>>>>>> On 2020-06-05 04:05, David Brown wrote: >>>>>>>>>>> On 05/06/2020 04:24, jlarkin@highlandsniptechnology.com wrote: >>>>>>>>>>>> On Thu, 4 Jun 2020 22:30:14 +0200, David Brown >>>>>>>>>>>> <david.brown@hesbynett.no> wrote: >>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>>>> >>>>>>>>>>>>>> That's what I have to do, use an oscilloscope to show >>>>>>>>>>>>>> programmers what >>>>>>>>>>>>>> their code does. You can often relate the pulse widths to >>>>>>>>>>>>>> paths >>>>>>>>>>>>>> through the code. >>>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>>> A scope can be a fine tool for measuring code performance. >>>>>>>>>>>>> I like to >>>>>>>>>>>>> make sure there are a few test pins on our boards that can >>>>>>>>>>>>> be connected >>>>>>>>>>>>> to an oscilloscope precisely for measuring critical code. >>>>>>>>>>>>> Not all >>>>>>>>>>>>> programmers understand how to instrument code for such >>>>>>>>>>>>> measurements, >>>>>>>>>>>>> unfortunately. >>>>>>>>>>>> >>>>>>>>>>>> Raise a port pin at the start of the routine, and drop it at >>>>>>>>>>>> the end. >>>>>>>>>>>> Maybe invert it a couple of times, at interesting points. >>>>>>>>>>> >>>>>>>>>>> And how do you know that you are raising it at the /real/ >>>>>>>>>>> start of the routine, and dropping it at the /real/ end of >>>>>>>>>>> the routine? I've seen people write code like : >>>>>>>>>>> >>>>>>>>>>> ... >>>>>>>>>>> set_pin_hi_macro(); >>>>>>>>>>> x = big_calculation(); >>>>>>>>>>> set_pin_lo_macro(); >>>>>>>>>>> ... >>>>>>>>>>> >>>>>>>>>>> without realising that the compiler (and processor, for more >>>>>>>>>>> advanced cpus) can re-order a lot of that and make the timing >>>>>>>>>>> seriously unrealistic. >>>>>>>>>> >>>>>>>>>> All the embedded stuff I recall doing in C/C++ defines the >>>>>>>>>> various port registers as volatile, so the compiler can't play >>>>>>>>>> games like that. >>>>>>>>>> >>>>>>>>>> How would you ever make embedded code work if the compiler >>>>>>>>>> could randomly optimize away or reorder port accesses? You >>>>>>>>>> could never even poll a hardware flag. >>>>>>>>> >>>>>>>>> I await David Brown's expert comment, but from >>>>>>>>> https://en.cppreference.com/w/c/language/volatile >>>>>>>>> >>>>>>>>> "This means that within a single thread of execution, a >>>>>>>>> volatile access cannot be optimized out or reordered relative >>>>>>>>> to another visible side effect that is separated by a sequence >>>>>>>>> point from the volatile access." >>>>>>>> >>>>>>>> The end of a statement is a sequence point. >>>>>>> >>>>>>> That helps, provided the code is written in a sane >>>>>>> /simple/ way. >>>>>>> >>>>>>> I'm it is possible to create cases which are >>>>>>> problematic. I've seen some remarkably bad code >>>>>>> in my time :( >>>>>> >>>>>> Me too, but DB was just blowing smoke at JL with that silly example. >>>>>> >>>>> >>>>> No. It was a side track, admittedly, but my logic is sound and >>>>> matches real-world cases. The fact that you don't understand >>>>> exactly how volatile works shows exactly my point - a lot of >>>>> programmers /think/ they understand volatile, but get it subtly >>>>> wrong. (Perhaps when you've read my longer reply, you'll see the >>>>> point - I did not give many details in my first reply to John.) >>>> >>>> You're blowing smoke at me now. >>>> >>>> Your example was incorrect--if the port register is declared >>>> volatile, the compiler is not at liberty to re-order code across >>>> sequence points such as the semicolon at the end of the statements. >>>> Thus the pin will go high before the 'big calculation' and low >>>> afterwards. >>> >>> I'm sorry, but you are /completely/ wrong here. >>> >>> In practice, as I noted, a compiler is unlikely to re-order these >>> things unless it sees an advantage to it. But it is without doubt >>> free to do so. Only observable behaviour has an order in C - things >>> without observable behaviour have no order. >>> >>> Here is a variation on the original code: >>> >>> >>> extern volatile unsigned char pin; >>> >>> unsigned int t = 1; >>> >>> void testmults(void) { >>> pin = 1; >>> >>> for (unsigned int x = 1; x <= 5; x++) { >>> t *= x; >>> } >>> >>> pin = 0; >>> } >>> >>> The assembly generated by gcc -O2 (x86-64) here is : >>> >>> testmults: >>> imul eax, DWORD PTR t[rip], 120 >>> mov BYTE PTR pin[rip], 1 >>> mov BYTE PTR pin[rip], 0 >>> mov DWORD PTR t[rip], eax >>> ret >>> t: >>> .long 1 >>> >>> >>> The compiler has turned the code into: >>> >>> void testmults(void) { >>> unsigned int tmp = t * (1 * 2 * 3 * 4 * 5); >>> pin = 1; >>> pin = 0; >>> t = tmp; >>> } >>> >>> >> >> That's because the compiler optimized away the whole loop. If your >> > > Look again. It combined the loop - it did not optimise away the > calculation. > > Please tell me you are not imagining that the C language has different > rules for a "small" calculation and a "big" calculation? >Perhaps you think that using a function call matters: extern volatile unsigned char pin; unsigned int t = 1; unsigned int big_calc(unsigned int x) { unsigned int y = x * x; unsigned int z = x * y; return y * y + 2 * z + 3 * y + 4; } void foo(void) { pin = 1; t = big_calc(t); pin = 0; } foo: mov eax, DWORD PTR t[rip] mov BYTE PTR pin[rip], 1 mov BYTE PTR pin[rip], 0 mov edx, eax imul edx, eax lea eax, [rdx+3+rax*2] imul eax, edx add eax, 4 mov DWORD PTR t[rip], eax ret No loops are removed, and "big_calc" is written as a function call, with an externally visible definition including several statements, semicolons, and local variables. You can see the "pin = 1; pin = 0;" assembly within the generated assembly. Do I /still/ have to dig, or are you ready to accept that compilers can re-order statements and non-volatile accesses around volatile accesses, as long as all observable behaviour is correctly ordered?