How can I get uLisp to run on a Nordic nRF52840?


#1

Nordic’s nRF52840 chip is built to be low power and yet it contains a 32-bit 64Mhz ARM Cortex M4 CPU together with a 2.4ghz radio. It has 1 megabyte of flash and 512K of RAM. Nordic sells an nRF52840-DONGLE with USB for $10.

I’d like to use it for making wireless sensors, and the attraction of uLisp to me is that (I imagine) I could write an over-the-air uLisp code updater. Well, that would be the goal anyway. I expect (hope) it would be easier than writing a custom wireless bootloader in C.

I’ve already done something similar in micropython (https://github.com/rabbithat/NRF52840_MicroPython_OTA_Updates), but I think I may enjoy using uLisp more. I’m hoping that with uLisp a much smaller set of primitivs are needed in order get everything working.


#2

Great suggestion! The Nordic Semiconductor nRF51822 is already supported by the ARM version of uLisp, for the BBC Micro Bit. I don’t have any immediate plans to support other Nordic chips, but you could do the work yourself using this version as a starting point. Get back in touch if you need help.


#3

OK, so what exactly do I need to do to get it to work on the nRF52840? I had a quick look at: https://github.com/technoblogy/ulisp-arm/blob/master/ulisp-arm.ino

Do I just change the ram size constant, compile and upload that and, hey presto, it works? I don’t have any sense at all as to how much (or little) work it would be. What would be the minimum amount of work I would need to do to get it running and have basic input-out (i.e. see a REPL prompt and be able to type into it)?


#4

I’ll write up some brief notes later today about what you need to do.


#5

OK, sounds good. :)


#6

I’ve started a separate thread for this:


#7

OK, before I get started though I do have one question: does uLisp have a proper garbage collector? By that, I mean can it consolidate the heap if the heap becomes fragmented?

The reason I ask is that I got started with micropython only to discover that it did not have a proper garbage collector in that sense. Moreover, it appears that it never will.

Likewise, if uLisp does have a proper garbage collector, will I be able to lock down a memory block (so that it doesn’t move) so that it can be used as a radio buffer by the nRF52840 hardware?

This short thread explains what I mean: https://forum.micropython.org/viewtopic.php?f=2&t=5401


#8

First of all, there’s a fairly detailed description of the implementation of uLisp on the uLisp site; for example:

Garbage collection

The uLisp garbage collector doesn’t consolidate the heap; it doesn’t need to because all objects are the same size, so there’s no advantage in having it consolidated. Also, you can’t lock down a memory block in the way you describe. However, you could allocate it separately, in C, as an extension to the uLisp source.


#9

Thanks for the steering. In that case, I think I should first try to write such an extension for the BBC:microbit. If that goes without a hitch, then there’s hope.

The one gotcha I can see is that the sandeep library that you’re using doesn’t yet cover the nRF52840. However, it does cover the nRF52832, which may be good enough for now.

I think the good news here is that it looks like the uLisp C extensions are a lot easier to write than micropython C extensions, which are rather arcane.

It does seem like you’ve been at this for quite a while. That’s good, because you’ve presumably honed the code over a span of decades. As far as you know, is the uLisp code robust and solid? Or are there known bugs that effect the stability? In other words, is it truly production ready?


#10

It does seem like you’ve been at this for quite a while.
That’s good, because you’ve presumably honed the code over a span of decades.

I’ve been using Lisp for quite a while, but the first version of uLisp was only released a couple of years ago, in May 2016, so I can’t say that the code has been honed over decades.

As far as you know, is the uLisp code robust and solid? Or are there known bugs that effect the stability? In other words, is it truly production ready?

It’s not a commercial product, but something that I’ve written in my free time, as a hobby, and I make no claims about how robust or solid it is.

My aim was to develop a usable version of Lisp capable of running on the Arduino Uno, with just 32 Kbytes of program space and 2 Kbytes of RAM. To achieve that I had to make several compromises, as a result of which there are clearly limitations.

You’ve referred to MicroPython, but bear in mind that MicroPython requires 256 Kbytes of program space and 16 Kbytes of RAM to run, so that needs to be taken into account when comparing them.

Hope that helps.


#13

Good news: I have uLisp working now on Nordic’s nRF52832-DK. This should be a nice step-up from the BBC:microbit. :-)

uLisp 2.4 
4063> (gc)
Space: 0 bytes, Time: 2258 us

nil

4063>

#14

Great! Would you like to post some performance statistics; see:

Lisp for Microcontrollers - Performance


#15

Sure.

4015>  (for-millis () (print (tak 18 12 6))
7 
8102


3471> (pprint (fft '(1 1 1 1 0 0 0 0)))

((4.0 . 0)
  (1.0 . -2.41421)
  (0 . 0)
  (1.0 . -0.414212)
  (0 . 0)
  (0.999999 . 0.414215)
  (0 . 0)
  (0.999994 . 2.41421))

3471> (for-millis ()   (fft (let (z) (dotimes (x 32) (push 0 z)) (dotimes (x 32) (push 1 z)) z)))
737

3471> (save-image)
Error: save-image not available
3471> 

Unfortunately, because save-image wasn’t a part of the BBC:microbit, it’s missing from this nRF52832 build, which should have enough space to accomodate it. How best to add it? It’s important to have.


#16

Here’s a link to the code that works: https://github.com/rabbithat/uLisp_nRF52832

It includes the PEEK and POKE functions. It also allocates 256 bytes for the radio buffer; the print_rb function (also included) will both print and return the address of the radio buffer.

If you want, you can repost the code on your website for the convenience of others. Obviously feel free to merge the benchmark info with the results for the other boards that you benchmarked.

I plan to port my code for doing OTA updates to it, though because of the limited number of cells available I may write most of that in C and simply do a few C-extensions back into uLisp to provide the functionality.


#17

Thanks for the info.


#18

The problem is that Sandeep Mistry’s arduino-nRF5 library doesn’t include EEPROM emulation using flash, which is what save-image needs.


#19

I have partial good news which is that I can run the binary built for the nRF52832 on the nRF52840, and uLisp appears to work, at least in the limited testing that I’ve done so far.

The bad news is that I can’t seem to get it to accept a larger workspace, which the nRF52840 could provide. So, my guess is that there’s something about the arduino board definition used by the nRF52832 that’s getting in the way of that, and which triggers a compile error.

Anyhow, I think there’s hope that this might be made to work.


#20

What error do you get when you try and increase the amount of workspace?


#21

I found an even better way, which is to use platformIO to build uLisp, since platformIO does support the nRF52840, unlike the Sandeep Mistry library.

However, it encounters a similar limitation on the workspace size, so now I don’t believe it’s related to a board definition.

The platformIO build does seem to work if I keep the workspace size relatively small (i.e. below its true potential). Presently, it looks like:

uLisp 2.4
4999>

However, if I try to increase the workspace to 8096, which should be easily accomodated, I get this compile error:

Compiling .pioenvs\nrf52840_dk\src\main.cpp.o
Linking .pioenvs\nrf52840_dk\firmware.elf
c:/users/coolermaster/.platformio/packages/toolchain- 
gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none- 
eabi/bin/ld.exe: .pioenvs\nrf52840_dk\firmware.elf section `.bss' will not fit in 
region `RAM'
c:/users/coolermaster/.platformio/packages/toolchain- 
gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none- 
eabi/bin/ld.exe: region RAM overflowed with stack
c:/users/coolermaster/.platformio/packages/toolchain- 
gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none- 
eabi/bin/ld.exe: region `RAM' overflowed by 1072 bytes
collect2.exe: error: ld returned 1 exit status
*** [.pioenvs\nrf52840_dk\firmware.elf] Error 1

Based on this error message, my suspicion now is that the starting address for the uLisp stack is too close to the uLisp heap. Does that sound right to you? If so, which would be the right variable/constant to change?


#22

I found an even better way, which is to use platformIO to build uLisp

I’m not familiar with platformIO. Does it provide an Arduino core with support for the Arduino functions such as digitalWrite(), delay(), Wire, etc?

my suspicion now is that the starting address for the uLisp stack is too close to the uLisp heap.

uLisp doesn’t use the heap, just the return stack, and the starting address for that is allocated automatically; I don’t know how to change it. Not sure what to suggest.