[ell-i-developers] Re: ENC28J60 <-> SPI optimised -- not sure about the approach

  • From: Pekka Nikander <pekka.nikander@xxxxxx>
  • To: ell-i-developers@xxxxxxxxxxxxx
  • Date: Mon, 17 Mar 2014 22:32:00 +0200

> I'm note sure whether such an optimisation is the right approach, even though 
> it saves memory.
> 
> The underlying problem is how dynamic the current Arduino SPI library is, 
> preferring to use a dynamic SPI.begin() method that allows the chip select 
> pin to be declared at runtime.  That leads to the necessity of compiling in 
> the Arduino PIN table, i.e. a data structure that defines each physical MCU 
> pin each Arduino PIN is.  That takes about 0.5 kb.  Before SPI all of our 
> code was carefully written so that the table could be optimised away at 
> compile time.  With the SPI.begin() interface that is no longer possible, at 
> least not without link-time constant propagation.  And I think that wouldn't 
> help either, I doubt even LLVM is able to do large enough constant inferences 
> to be able to do that.
> 
> The latest commit in feature-coap-temp now connects the ENC28J60 code 
> directly to our own low-level SPI API, written in C, allowing the dynamic 
> Arduino SPI interface to be bypassed.  While that saves memory, it also makes 
> our ENC28J60 incompatible with other Arduino-compatible stacks.

I came to the conclusion that we most probably want both the dynamic, Arduino 
style C++ interface and a lower-level, Ell-i style statically allocated C 
interface.  Hence, I refactored the SPI library quite heavily, structuring it 
better from this point of view.  I also updated the ENC28J60 slightly, to use 
the new SPI library.  The code compiles and is now pushed to the 
feature-coap-temp branch; this time I didn't have energy to test whether it 
works or not.  If you find out that it doesn't, please let me know immediately.

I'm not completely happy with the ELL-i style C interface to SPI, yet, though.  
The main thorn is the need to define the slave select pin as a static const 
member variable:

https://github.com/Ell-i/Runtime/blob/feature-coap-temp/stm32/libraries/CoAP/arch/enc28j60/ENC28J60Class.h#L35

If I define it as a non-static const member variable, then the pinMode and 
digitalWrite functions won't get optimised away with constant propagation, 
leasing to code bloat with some 800 bytes due to the compiler including the 
GPIOPIN table into the object code:

https://github.com/Ell-i/Runtime/blob/feature-coap-temp/stm32/libraries/CoAP/arch/enc28j60/ENC28J60Class.h#L35

That we don't want; we want the table to be optimised away at the compile time. 
 And that requires that the slave select pin used in pinMode and digitalWrite 
is a compile-time constant.  I still need to contemplate how that could be done 
without a static member variable.  

Most probably I will need to inline more of the ENC28J60 code so that it gets 
compiled along with the user program, as the user program will have all the 
required knowledge there, including the const ENC28J60 instance, etc.  But I 
haven't figured out how exactly to do it, yet.  Will continue another day, 
don't know if I'll have time tomorrow or on Wednesday.

Another smaller issue is that the current enc28j60 interface takes quite a lot 
of space, about 1800 bytes.  I have some ideas how to optimise that, but it 
needs to wait until I get the C-style SPI interface ready first.

I think that we could also create two ENC28J60<->SPI interfaces, one using this 
lower-level C-style API, and another using the dynamic Arduino-style C++ API.  
How to make the difference between those at compile time still needs to be 
figured out, though.  In any case, it looks like that we'll need some more 
link-time intelligence for our makefiles anyway.

--Pekka


Other related posts: