AdaCore / Ada_Drivers_Library

Ada source code and complete sample GNAT projects for selected bare-board platforms supported by GNAT.
BSD 3-Clause "New" or "Revised" License
248 stars 144 forks source link

Start MicroBit v2 support #385

Open Fabien-Chouteau opened 3 years ago

aiunderstand commented 3 years ago

Hi Fabien, thanks for this commit. I noticed that several functions were not implemented yet such as only addressing the port 0.x range, display, IMU, touch logo, pin assignment with breadboard expansion, ADC, PWM. I have implemented them in my repo and if you want I can send a PR https://github.com/aiunderstand/ADA_Drivers_Library. I am in the process of converting the working (but commented out) examples in the Text_Scrolling project into separate examples like you did with the V1.

Unfortunately that is not all that is needed to make it work with the internal hardware debugger. GNAT studio is using an old PyOCD version, so I have to instruct my students to install Python (with path added to environment!) and pip install pyocd to install the latest version. That one supports the nRF52833 used in the Microbit v2. For last years class I ported the Arduino Nano BLE Sense based on the nRF52_DK (not shared yet as it needs polishing) and that required a newer version of PyOCD as well because it misses J-Link support in the 0.25 version. We also had to create custom debug shield to make debugging with J-link possible, so I was happy to see you started the Microbit V2 port.

Finally, do you know how to force upload a binary using GNAT studio? When students have a brand new microbit v2 and successfully compiled a .bin they cannot upload it the first time, with the tool complaining it cannot erase sectors (see screenshot). You have to manually upload it by dragging and dropping the .bin to your Microbit drive. After you have done that once, you can use GNAT studio to upload .bin like normal. Thanks! pythonError

aiunderstand commented 3 years ago

Just as a heads-up: most microbitv1 examples have now been ported to v2, the internal speakers works and is set as the default output in the music example. The servo and ble beacon example also work. https://github.com/aiunderstand/ADA_Drivers_Library/tree/master/examples/MicroBit_v2

As an ADA novice, I am now exploring tasking and the ravenscar profile. I have added the nrf52833 to the bb-runtimes repo, so I can run .\build_rts.py --output=C:\GNAT\2021-arm-elf\arm-eabi\lib\gnat --build nrf52833 -v --force. Is there a standard ravenscar-full and ravenscar-sfp test that I can run to see if the implementation is correct? I basically copied the settings from the nrf52840, checked the nrf52833 manual on the register and interrupt table and added the port 1.x base address and various missing interrupt vectors (eg. in handler.s), updated flash/ram in the memory map. I also created new ravenscar profiles (see https://github.com/aiunderstand/ADA_Drivers_Library/tree/master/boards/MicroBit_v2) based on the zfp profile but removed the crt0.s and link.ld. Is this the right approach or should I generate these profiles from bb-runtimes or something entirely different? Thanks

aiunderstand commented 3 years ago

Link to fork of bb-runtimes and rough attempt to implement nrf52833 https://github.com/aiunderstand/bb-runtimes

Fabien-Chouteau commented 3 years ago

Just as a heads-up: most microbitv1 examples have now been ported to v2, the internal speakers works and is set as the default output in the music example. The servo and ble beacon example also work. https://github.com/aiunderstand/ADA_Drivers_Library/tree/master/examples/MicroBit_v2

That's great @aiunderstand :+1:

If you want to open a pull-request on this repo you have to fork using the GitHub interface and not create a new repo from scratch like you did.

As an ADA novice, I am now exploring tasking and the ravenscar profile. I have added the nrf52833 to the bb-runtimes repo, so I can run .\build_rts.py --output=C:\GNAT\2021-arm-elf\arm-eabi\lib\gnat --build nrf52833 -v --force. Is there a standard ravenscar-full and ravenscar-sfp test that I can run to see if the implementation is correct?

You can start with a basic loop:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Real_Time; use Ada.Real_Time;
procedure Main is
begin
   loop
      Put_Line ("Hello");
      delay until Clock + Seconds (1);
   end loop;
end Main;
aiunderstand commented 3 years ago

1) Thanks for the feedback, I now properly forked it and added my commit (same link) . 2) Do you have any advice on force uploading a binary using GNAT studio (see question above) or should I create a separate Issue? 3) I have also added a commit on my forked bb-runtimes creating a full and sfp ravenscar for the microbit_v2 using the example above. It runs, but weirdly only after I press debug mode and press run. It will not output to the serial monitor after a normal upload. Could it be missing startup code?

aiunderstand commented 3 years ago

For 2) See pyocd/pyOCD#1212

aiunderstand commented 3 years ago

For 3) Small update. I found out that in setup_board.adb the LFLCK was defined as an external clock source, which the micro:bit v2 does not have, hence it was only running when I was running it via debug mode (using the clock of the KL27Z). Changing it to "RC" runs the basic loop example. I have updated https://github.com/aiunderstand/bb-runtimes for inspection. In the next commit I will base the changes on the SVD2ADA repo.

As I am new to ADA and ADL i'd like some feedback on how to continue. My goal is to make the ZFP examples, so the basic capabilities of the board, work with ZFP/Light, Ravenscar and Jorvik.

1) I have noticed in the micro:bit V1 examples that there are 2 setups. The examples from in the ADL all use the zfp-cortex-m0 profile and target ZFP. If you switch from zfp-cortex-m0 to microbit-zfp (and remove the reference to link.ld and crt0) the example such as scrolling_text don't work anymore, resulting in

0x0000460c in __gnat_irq_trap () .

The other setup is by clicking in GNAT on New Project and selecting the Scrolling Text demo. This template uses the ravenscar-sfp-microbit profile and does not depend on the microbit ZFP board directory, but rather on a minimum set of files to support main.adb. This template can switch to ravenscar-full-microbit but also cannot switch to zfp-microbit.

Any idea why the zfp-microbit profile does not work in both microbit v1 setups? Does zfp startup code require different startup and linking code than ravenscar/jorvik? If so, any documentation to point to? I dont plan to do much with MicrobitV1 as its flash memory is too small for ravenscar profiles, so it is mostly for understanding.

2) Probably related to 1, most ZFP examples seem to use the AdaCore startup-gen repo to generate a crt0.S and link.ld file for basic startup code. Can I also use that for ravenscar profiles? Is it correct that the current best practice is to use those files instead of start-rom.S + start-common.S + handler.S and common-rom.ld + memory-map_variant.ld ? I tried to replace those files with crt0.s and link.ld but then ravenscar profiles fail to correctly start.

3) I tried to have both the Ada.Real_Time facilities and Microbit.Time facilities run using ravenscar-sfp so that I have absolute (periodic) and relative (one-shot) time. With ravenscar-full I can simply use "delay 0.4" for relative time, but I'd like to use another hardware timer for it, eg. use Microbit.Time. In https://github.com/AdaCore/bb-runtimes/pull/12 @damaki used RTC0 for "delay until" and RTC1 to drive a LED using a protected object. Is it correct that I should convert Microbit.Time to use protect objects to enable relative time in ravenscar, such as Time.Delay_Ms(100)? I have read this paper http://www.dit.upm.es/~str/papers/pdf/zamorano&01a.pdf on how things should work, but let me know if you have a better or more up to date resource.

4) Probably related to 3, in https://github.com/AdaCore/Ada_Drivers_Library/pull/326 @Fabien-Chouteau wrote that ZFP drivers (should) work for ravenscar-sfp, but any driver that relies on an RTC implementation breaks, like Microbit.Time and Microbit.IOs (analog write/pwm), generating unhandled interrupts. Ideally the MicroBit.Time package detects if Ravenscar is used (with a pragma?) so all references to MicroBit.Time.Delay_ms use the Ada.Real_time clock. I am not sure what to do with Analog Write in the MicroBit.IOs package.

5) Should I organize the examples in two different examples folders such as ZFP and Ravenscar directories? I have started doing that in my latest commit in https://github.com/aiunderstand/ADA_Drivers_Library/tree/master/examples/MicroBit_v2 , but I doubt this a good practice as there is a lot of code redundancy.

Thanks for all the help!

Fabien-Chouteau commented 3 years ago

Hello @aiunderstand,

I did all the preparation work for the micro:bit v2 in this pull-request, I highly recommend that you start from this.

It uses startup-gen and the zfp-cortex-m4f run-time so you don't have to worry about that, and you don't have to make a BSP in the bb-runtimes repo. The trade-off is the Ravenscar run-times are not available, but you don't need Ravenscar to start using the board or make drivers.

The only thing that you have to focus on is the content of this folder: https://github.com/AdaCore/Ada_Drivers_Library/tree/master/boards/MicroBit/src that you have to copy and adapt for the micro:bit v2 (except for zfp subfolder): https://github.com/AdaCore/Ada_Drivers_Library/tree/microbit_v2/boards/MicroBit_v2/src .

Cheers,

aiunderstand commented 3 years ago

Hi @Fabien-Chouteau ,

I started from that repo, see the OP. I managed to to get the ravenscar and jorvik profile to work, see my comment on Sep 24th. I based my latest commit on SVD2ADA and the nrf52833.svd and updated the bb-runtimes and ADL accordingly. I created a ravenscar example directory for the microbit 2 to show that the timing is accurate with delay and delay until (in case of jorvik). I still have 3 questions left from above:

1) Why do we need 2 different startup codes for ZFP and Ravenscar. Is there documentation what features must be implemented for ravencar compliency? Most of the startup code is identical. I look at the bb-runtime files for microbit v1 ravenscar profile and Damaki's work on the NRF52 to make it work for the nrf52833, but I feel the startup code is not efficient and could have more comments.

2) See https://github.com/aiunderstand/Ada_Drivers_Library/tree/master/boards/MicroBit_v2/src/full I created a protected object that starts RTC 1 with a lower priority than RTC0 that is used by ravenscar's delay until routine. With this implementation I can run two different clocks without blocking calls as well as demonstrate how to implement and access different hardware timers. The code from microbit-timewithrtc1.adb is based on microbit-time.adb except now works with ravenscar by wrapping it in a protected object. The code works but since I couldn't find any example I am not sure if my implementation is compatible with ADA coding style.

3) I managed to enable all the basic facilities like digital in/out, analog in/out, accelerometer, music, bluetooth to work with ravenscar and added a few ravenscar specific examples like tasking to help students with response time analysis. The problem I have is with the organization of the files. Some examples require a specific profile like zfp, ravenscar or jorvik (eg tasking or relative delay). There is much overlap in the examples of microbit v1 and v2. In my current code base, microbit v1 and v2 bite each other so I need to refactor a bit before I can submit a PR.

Fabien-Chouteau commented 3 years ago
  • Why do we need 2 different startup codes for ZFP and Ravenscar. Is there documentation what features must be implemented for ravencar compliency? Most of the startup code is identical. I look at the bb-runtime files for microbit v1 ravenscar profile and Damaki's work on the NRF52 to make it work for the nrf52833, but I feel the startup code is not efficient and could have more comments.

In bb-runtimes the startup code for ZFP and Ravenscar is the same.

  • See https://github.com/aiunderstand/Ada_Drivers_Library/tree/master/boards/MicroBit_v2/src/full I created a protected object that starts RTC 1 with a lower priority than RTC0 that is used by ravenscar's delay until routine. With this implementation I can run two different clocks without blocking calls as well as demonstrate how to implement and access different hardware timers. The code from microbit-timewithrtc1.adb is based on microbit-time.adb except now works with ravenscar by wrapping it in a protected object. The code works but since I couldn't find any example I am not sure if my implementation is compatible with ADA coding style.

Besides some indentation issues, the Delay_Ms function should not use the assembly "wait for interrupt" instruction: https://github.com/aiunderstand/Ada_Drivers_Library/blob/db0e7eedf5ec5b2bd43f4e146c3d50ba57cd05a6/boards/MicroBit_v2/src/full/microbit-timewithrtc1.adb#L15

Since you have Ravenscar available, you should use protected entries to stop a task until an event occurs: See "Synchronization" here: https://blog.adacore.com/theres-a-mini-rtos-in-my-language.

Also, there is already an implementation of HAL.Time.Delays that can be used with a Ravenscar run-time: https://github.com/aiunderstand/Ada_Drivers_Library/blob/master/middleware/src/ravenscar-common/ravenscar_time.ads

  • I managed to enable all the basic facilities like digital in/out, analog in/out, accelerometer, music, bluetooth to work with ravenscar and added a few ravenscar specific examples like tasking to help students with response time analysis. The problem I have is with the organization of the files. Some examples require a specific profile like zfp, ravenscar or jorvik (eg tasking or relative delay). There is much overlap in the examples of microbit v1 and v2. In my current code base, microbit v1 and v2 bite each other so I need to refactor a bit before I can submit a PR.

About Ravenscar vs ZFP, it's ok to pick only one and make that the supported run-time. It looks like Ravenscar is important for you so you can focus on this and forget ZFP.

aiunderstand commented 2 years ago

Thanks for the reply @Fabien-Chouteau. In the next few months I will spend some development time on polishing my work for a PR. This should enable full v2 support incl ravenscar and zfp, various drivers such as radio, adc, pwm, gpio 1.x and many examples. My students had great fun with these features last year and build interesting robot projects and applications, as well learn a thing or two about timing analysis and tasking in practice.

With regards to your feedback some follow-up questions:

In bb-runtimes the startup code for ZFP and Ravenscar is the same.

Sure, in cortexm.py the startup-rom, handler and linker code is the same for all three runtimes for the microbit V1 board but the generated runtime for zfp (zfp-microbit) does not work while zfp-cortex-m0 based on crt0 and link.ld does work. Take for example the Microbit V1 scrolling_text example from the ADL and change the runtime from zfp-cortex-m0 to zfp-microbit. Only with the zfp-microbit runtime will it report the error mentioned above as soon as an interrupt is used (eg the init code of microbit-time using RTC1).

Both the linker code and startup code are quite similar but not exactly the same between the two runtimes. Is there a bug in zfp-microbit?

Besides some indentation issues, the Delay_Ms function should not use the assembly "wait for interrupt" instruction: https://github.com/aiunderstand/Ada_Drivers_Library/blob/db0e7eedf5ec5b2bd43f4e146c3d50ba57cd05a6/boards/MicroBit_v2/src/full/microbit-timewithrtc1.adb#L15

Since you have Ravenscar available, you should use protected entries to stop a task until an event occurs: See "Synchronization" here: https://blog.adacore.com/theres-a-mini-rtos-in-my-language.

Also, there is already an implementation of HAL.Time.Delays that can be used with a Ravenscar run-time: https://github.com/aiunderstand/Ada_Drivers_Library/blob/master/middleware/src/ravenscar-common/ravenscar_time.ads

Thanks this is exactly what I needed to learn.

About Ravenscar vs ZFP, it's ok to pick only one and make that the supported run-time. It looks like Ravenscar is important for you so you can focus on this and forget ZFP.

Yes Ravenscar is important for the course, but usually I start with the basics and thus with ZFP. I will fork this repo and add a ravenscar-full (jorvik) and zfp (light) category for the examples and boards since they are so fundamentally different (eg. language features, drivers using ada.real-time).

Fabien-Chouteau commented 2 years ago

Awesome @aiunderstand, can't wait to see the results :)

aiunderstand commented 1 year ago

@Fabien-Chouteau just as a heads-up: I added I2C support for external I2C devices and a new motor driver for 4 wheel drive project (the DFR0548). Three notes:

Start, h80 [ h40 | WR ], h00, h00, Restart, h80 [ h40 | WR ], Stop

While I expect something like

Start, h80 [ h40 | WR ], h00, h00, Stop

There is no argument to the procedure in the low level TWI package to change the repeated stop condition. Should the repeated stop condition be made explicit like in codal v2 or is there a reason to keep that option hidden? It certainly works for communicating with the internal IMU or with devices that don't do fancy tricks like auto-increasing the address every time a byte is received.

Fabien-Chouteau commented 1 year ago
  • The old version designed for the mb v1 uses a shared I2C bus. The NRF52833 has two I2C peripherals so I used one for the internal I2C and one for the external bus. It works well, but I am not too happy about the interface using Initialize or InitializeExt to differentiate between the two. Do you have suggestions for better a name/API?

It's great to have the two separated. What I would do is to not expose the internal I2C at all. You can declare it in the private part of the MicroBit package for instance.

* It wasn't really documented but the I2C implementation in Ada expects a shift_left of the I2C address in the datasheet to work. This is relevant as many will try to use the address in the datasheet.

Indeed there is a problem here. We don't expect the R/W bit in the address. There has been discussions on this and the problem is that changing the behavior will break everything that is working so far...

* The NRF-TWI implementation uses a I2C communication pattern which seems inefficient, though I am inexperienced with I2C drivers. I used an Analog Discovery 2 device to spy on the I2C communication and Mem_Write result in for example

Again I think this is a question of what API HAL.I2C should be expose.