DigitalWriteFast library for Uno32?

WestfW
Posts: 148
Joined: Wed May 25, 2011 12:17 am

Re: DigitalWriteFast library for Uno32?

Post by WestfW » Thu Oct 27, 2011 12:39 am

libraries from vendors can be provided in precompiled form
ahh! that makes sense. It even sounds valuable, though I'm not sure how well it would work out in practice (even with similar peripheral sets.) I used to get really uncomfortable thinking what would actually be required to meet the requirement of "user-linkable with new versions of LGPL libraries" in embedded environments.

Can the compilers be given a "hint" that a given linker section has a constant base address on a 64k boundry? It might even be implementable with the current compiler and some "creative" uses of existing features (heh. Adding "paging" at source level makes the final code smaller...) Sorta like:

Code: Select all

#define SFRPAGE ((((unsigned int)&LATBSET) & 0xFFFF0000))
#define xLATBSET *((volatile unsigned int *)((SFRPAGE) + (((unsigned int)&LATBSET) & 0xFFFF)))
#define xLATBCLR *((volatile unsigned int *)((SFRPAGE) + (((unsigned int)&LATBCLR) & 0xFFFF)))
(alas, this doesn't quite work. gcc doesn't seem to understand that the masking doesn't end up dividing the (unresolved) externals into upper and lower 16bit halves, so it ends up doing actual "andi" operations. I *think* I have the expression correct enough that it should work... Inline assembler could probably do it...)

WestfW
Posts: 148
Joined: Wed May 25, 2011 12:17 am

Re: DigitalWriteFast library for Uno32?

Post by WestfW » Thu Oct 27, 2011 6:18 am

Any way you could make this work on MAX32?
Done. The second one is easier.

These are "slightly" tested, which is to say that they compile, pin13 blinks using the "fast" versions (and pinMode to set to output), the "last" pin works (pin43 (2nd LED) on Uno32, pin85 on Max32, and several random pins in between will flash an LED.

KurtE
Posts: 64
Joined: Wed Nov 16, 2011 11:24 pm

Re: DigitalWriteFast library for Uno32?

Post by KurtE » Sat Nov 19, 2011 7:54 pm

Hi, I am new here so please forgive me, if I am asking an obvious question. I have been programming for a long time and over the last year or so I have been playing around with Arduinos including the some prototype boards that will be coming out at Lynxmotion

Awhile ago I ported the Lynxmotion hexapod code (Phoenix) over to the Arduino environment and thought it might be fun to try it out on the Uno32. But the first thing I ran into was several of the libraries I was using have not been ported over to the Uno32, like the PSX_LIB library for the PS2 by Bill Porter. The problem is that he is using his own macros and the like to set and clear the IO lines. So I thought I would take a stab at this.

Recently on the Arduino, I was wanting to control two WII nunchucks on one board, but they both have the same I2C address, I found the software I2C library was too slow, so I built my own as the DigitalWriteFast. The issue I had with using DigitalWriteFast was I wanted to pass in pin numbers to the init function and DigitalWriteFast only works with constants. So what I did instead was to un-roll the digital Write, that my init code would do something like:

Code: Select all

// Some globals or class variables...
volatile uint32_t	*SCLlatchPort;
uint16_t SCLPinBit;
...

// Init code 
    uint8_t				port;

	port	=	digitalPinToPort(SCLpin);
	SCLPinBit =	digitalPinToBitMask(SCLPin);
	SCLlatchPort=	portOutputRegister(port);
// may need to grab data to read ... like wise for SDA
Then when I needed to to change SCL to low, I would just do something like:

Code: Select all

*SCLlatchPort & = ~SCLPinBit
Or if I needed to go high I would:

Code: Select all

SCLlatchPort  |= SCLPinBit
Note: these were done through macros or inline functions...

So the question is, would this work on the Uno32? Also would the set and clear turn out to be atomic operations? If not what do I need to do to make them atomic? Ran into earlier problems with PS2 library was not atomic and it walked over the changes that the Servo library was making during interrupts.

Again sorry if I went too far off topic here or if the question(s) are obvious.

Thanks Again
Kurt

mikael
Posts: 3
Joined: Sat Nov 19, 2011 5:58 pm

Re: DigitalWriteFast library for Uno32?

Post by mikael » Tue Nov 22, 2011 1:12 am

I've tested WestfW DigitalWriteFast library in my atempt to port the arduino glcd lib for chipkit.
digitalWriteFast works perfectly, but pinModeFast and digitalReadFast seem not work as expected.
Tested with board Max32, and i've used pin 71-83.

WestfW
Posts: 148
Joined: Wed May 25, 2011 12:17 am

Re: DigitalWriteFast library for Uno32?

Post by WestfW » Wed Nov 23, 2011 8:08 am

pinModeFast and digitalReadFast seem not work as expected.
Rats! Any specifics? I thought my testing covered pinModeFast(), but I'll admit that digitalReadFast() didn't get much more that a "compiles and doesn't crash" sort of test.

bperrybap
Posts: 48
Joined: Sat Nov 19, 2011 8:45 pm

Re: DigitalWriteFast library for Uno32?

Post by bperrybap » Sat Dec 24, 2011 1:13 am

Please Oh Please, can we not wander down the digitalWriteFast() path on this platform?
There is no need to have a separate functions for "fast" vs regular.

It is better to use appropriate macro wrappers around functions
so that if the arguments are constants you get the fast direct port i/o and if
not, it calls a function (ex: the digitalWrite() function) to do the mappings runtime.

That way there is one and only one API and if you use constants,
things just happen a lot faster.

I never understood why the Arduino development team wouldn't accept
this solution.

Take a look at what Paul has done with his Teensy core. If you use constants,
with his core, you automagically get the faster code.

IMHO, this is the way things should work.

--- bill

WestfW
Posts: 148
Joined: Wed May 25, 2011 12:17 am

Re: DigitalWriteFast library for Uno32?

Post by WestfW » Sat Dec 24, 2011 3:25 am

It is better to use appropriate macro wrappers
In fact, this implementation is exactly that, and is based off the discussions that have occurred in the Arduino Forums. However, lacking complete testing or official blessings, they have to have SOME other name!

Code: Select all

#ifndef digitalWriteFast
#define digitalWriteFast(P, V) \
do {								\
    if (__builtin_constant_p(P) && __builtin_constant_p(V)) {		\
	if (V) {							\
	    *(_dpin_to_set_macro(P)) = _dpin_to_bitmask_macro(P);	\
	} else {							\
	    *(_dpin_to_clr_macro(P))  = _dpin_to_bitmask_macro(P);	\
	}								\
    } else  digitalWrite((P), (V));					\
}while (0)
#endif  //#ifndef digitalWriteFast
(The big part of the effort is coming up with the _dpin_to_xxx macros. In this case, they are simple but long and ugly cascaded ternary expressions built from the pins_xxx file using editor macros. (said macros being included in the source, for prosperity...)

bperrybap
Posts: 48
Joined: Sat Nov 19, 2011 8:45 pm

Re: DigitalWriteFast library for Uno32?

Post by bperrybap » Sat Dec 24, 2011 5:01 am

I know and understand the DigitalWriteFast code.
(I participated in the long discussion over the Arduino forum).

I have 1700 lines of macros and inline functions that I created myself
for the Arduino AVR based glcd library that not only maps individual pins for port i/o but can detect
adjacent pins in a port and do multiple pin updates concurrently - nibble and byte
when possible.

What I'm trying to say is lets *not* intentionally to go down the DigitalWriteFast path again
on this platform except for testing the implementation.
IMHO, DigitalWriteFast is solving the problem in the wrong way by
doing it at the wrong level.

Instead, I'd like to see us push to get the final working solution incorporated into
the real core code instead of having to live with a less optimal solution
that is forced into using alternate API names to get the faster code.

In other words, I believe that the optimization should be totally transparent to the user.
The user should not have to use a different set of APIs to get faster code when
the core code make it happen automatically.

I understand the usefulness of going through an interim stage while all the various
macros are sorted out. But after that, I think it makes sense to be moved into the core code
so everything benefits from it automatically.


--- bill

User avatar
Jacob Christ
Posts: 723
Joined: Sat May 28, 2011 6:34 am
Location: Southern California
Contact:

Re: DigitalWriteFast library for Uno32?

Post by Jacob Christ » Tue Dec 27, 2011 2:12 pm

bperrybap wrote: What I'm trying to say is lets *not* intentionally to go down the DigitalWriteFast path again on this platform except for testing the implementation.

IMHO, DigitalWriteFast is solving the problem in the wrong way by
doing it at the wrong level.
Bill,

Although I don't have much experience with the Arduino or the community I agree completely with you. Also, in working with the maintainers I would say that they are very open too implementation suggestions, so much so that just posting a suggested code change as an issue in the repo will get it implemented.

https://github.com/chipKIT32/chipKIT32-MAX

Jacob
PONTECH Quick240 an industrial form factor platform for chipKIT and Arduino users.

WestfW
Posts: 148
Joined: Wed May 25, 2011 12:17 am

Re: DigitalWriteFast library for Uno32?

Post by WestfW » Wed Feb 15, 2012 10:45 am

So, remember this really ugly implementation of DigitalWriteFast? It relied on gcc being able to optimize away long and complex ternary statements containing constants. It was fast, but it relied on awful-looking C macros that were completely separate from the main pins_arduino.c structures, and thus subject to maintenance nightmares.

Well, it turns out that gcc will also optimize indexing into static arrays with a constant, so we can get ALMOST as fast an implementation, using the code that already exists. No maintenance nightmare, and a pretty closely parallel implementation, and no particularly ugly macros.

variants/xxx/Board_Data.c gets a slight set of modifications so that it can make either global arrays (for pins_arduino.c) or static arrays that disappear when only indexed by constants:

Code: Select all

+#if defined(OPT_BOARD_DATA_STATIC)
+#define MAYBESTATIC static
+#else
+#define MAYBESTATIC
+#endif
 
 /* ------------------------------------------------------------ */
 /*                                     Data Tables                                                                     */
@@ -56,7 +61,7 @@
 ** the TRIS register for the port. This is used for setting the
 ** pin direction.
 */
-const uint32_t port_to_tris_PGM[] = {
+MAYBESTATIC const uint32_t port_to_tris_PGM[] = {
        NOT_A_PORT,                             //index value 0 is not used
 
 #if defined(_PORTA)
@@ -108,7 +113,7 @@
 /* This table is used to map the digital pin number to the port
 ** containing that pin.
 */
-const uint8_t digital_pin_to_port_PGM[] = {
+MAYBESTATIC const uint8_t digital_pin_to_port_PGM[] = {
And then fastio.h gets code to include the arrays, and uses inline functions that closely parallel wiring_digital.c. It even gets to include some of the sanity checking:

Code: Select all

#define OPT_BOARD_DATA_STATIC 1
#define OPT_BOARD_DATA 1
#define OPT_BOARD_INTERNAL 1
#include <p32xxxx.h>
#include <WProgram.h>
#include <Board_Data.c>

/*
 * This looks a lot like digitalWrite, but uses the static arrays and is inline.
 * when called with constants, it should optimize down to the single instruct.
 */
static inline void _dwf(uint8_t pin, uint8_t val)
{
    p32_ioport *	iop;
    unsigned int		port;
    unsigned int		bit;

	//* Get the port number for this pin.
	if ((pin >= NUM_DIGITAL_PINS) ||
	    ((port = digitalPinToPort(pin)) == NOT_A_PIN))
	{
		return;
	}

	//* Obtain pointer to the registers for this io port.
	iop = (p32_ioport *)portRegisters(port);

	//* Obtain bit mask for the specific bit for this pin.
	bit = digitalPinToBitMask(pin);

	//* Set the pin state
	if (val == LOW)
	{
		iop->lat.clr = bit;
	}
	else
	{
		iop->lat.set = bit;
	}
}


#define digitalWriteFast(P, V)  \
    if (__builtin_constant_p(P) && __builtin_constant_p(V)) {	\
	_dwf(P, V);						\
    } else {							\
	digitalWrite((P), (V));					\
    }
This makes me feel a lot warmer and fuzzier than the previous implementation. It ought to be MUCH easier to add to the different variants, and could have "partial" support added to the core with no impact...

Post Reply