Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers/at86rf2xx: add support for ATmegaRF MCUs #12537

Merged
merged 5 commits into from Oct 28, 2019

Conversation

benpicco
Copy link
Contributor

Contribution description

This is based on the work by @Josar

The ATmega128RFA1 and ATmega256RFR2 MCUs contain a version of the AT86RF2xx radio directly mapped to memory. It behaves mostly like the AT86RF231.

Unlike on the SPI based radios, all radio events can generate a dedicated interrupt on the CPU.
Unfortunately, taking advantage of this would require factorizing all the interrupt handler code, so instead the interrupt state register is emulated.

The radio on the ATmega256RFR2 is almost identical to the one on the ATmega128RFA, it adds automatic re-transmissions and optional additional interrupt events.

Since I don't have a ATmega256RFR2 device, I omitted the re-transmissions.

Testing procedure

Unfortunately examples/gnrc_networking does not fit onto the microduino-corerf, so I did all my testing with examples/default.

If you happen to have a RCB256RFR2-xpro or Jiminy-Mega256RFR2, it should fit there.

I did txtsnd between the microduino-corerf and a same54-xpro equipped with an at86rf233 module (samr21-xpro should work as well, but I didn't have one handy right now), so I could also confirm that the driver also still works for the SPI based radios.

I changed the transmit power with ifconfig set power and saw the change in RSSI on the receiver.
I tried ifconfig set state sleep and ifconfig set state idle to confirm no packet is being received in the sleep state. Power measurements were not done.

Issues/PRs references

based on #9172

@benpicco benpicco added Area: network Area: Networking Type: new feature The issue requests / The PR implemements a new feature for RIOT Platform: AVR Platform: This PR/issue effects AVR-based platforms Area: drivers Area: Device drivers CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Oct 21, 2019
@maribu
Copy link
Member

maribu commented Oct 22, 2019

Unfortunately, taking advantage of this would require factorizing all the interrupt handler code, so instead the interrupt state register is emulated.

Do you intend to do this later on?

I personally think that some bigger refactoring of the at86rf2xx driver is needed anyway, as it doesn't fulfill the quality requirements stated in the wiki:

test for device connectivity, e.g. does a SPI/I2C slave react

If a boards boots without device connected to the SPI bus, the driver loops endlessly blocking RIOT.

This device descriptor MUST contain all the state data of a device. By this, we are not limited on the number of instances of the driver we can run concurrently.

This is also not fulfilled. There can be multiple instances of the driver interacting with transceivers if the are the very same model. But in the driver a lot of #ifdef MODULE_AT86RF<model> is present, so it can only handle one model at a time. I think splitting the driver into at86rf2xx_common (holding everything that can be shared), at86rf233, at86rf231, and at86rf212b is the only reasonable way to fulfill this requirement without code duplication. And as a side effect I think getting rid of a many #ifdefs will make the code cleaner.

So with that in mind: I guess that this refactoring (that in my opinion is unavoidable anyway) could make it much easier to provide an interrupt handler optimized for the periph versions of the AT86RF2xx. (Maybe some more splitting could be required, e.g. adding additionally at86rf2xx_spi_common for holding the interrupt handler code specific to the SPI versions.)

Copy link
Member

@maribu maribu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks good to me.

drivers/at86rf2xx/at86rf2xx.c Show resolved Hide resolved
drivers/at86rf2xx/at86rf2xx.c Show resolved Hide resolved
drivers/at86rf2xx/at86rf2xx_internal.c Show resolved Hide resolved
makefiles/pseudomodules.inc.mk Show resolved Hide resolved
@benpicco
Copy link
Contributor Author

The cleanup of the driver you are suggesting sounds tempting. Maybe it would even be possible to integrate support for at86rf215 which has a different state machine, but similar register access functions.

But that is a wholly different undertaking - maybe if the tasklet work is finalized that would be a good reason to convert the driver, but I don't want to promise anything 😉

@maribu
Copy link
Member

maribu commented Oct 22, 2019

The cleanup of the driver you are suggesting sounds tempting.

Please notify me if/when you start working on this. I promise to notify you when we start doing so. It would be shame if we would duplicate the effort.

@benpicco
Copy link
Contributor Author

benpicco commented Oct 24, 2019

btw: with some tweaks to gnrc_minimal you can even ping the node 😄

microduino-corerf
2019-10-25 00:46:13,536 # main(): This is RIOT! (Version: 2020.01-devel-267-gca0fc-at86rfmega)
2019-10-25 00:46:13,537 # RIOT network stack example application
2019-10-25 00:46:13,538 # My address is fe80::863d:27a0:3c22:843e
samr21-xpro
2019-10-25 00:46:17,715 # 12 bytes from fe80::863d:27a0:3c22:843e: icmp_seq=0 ttl=64 rssi=-71 dBm time=8.095 ms
2019-10-25 00:46:18,715 # 12 bytes from fe80::863d:27a0:3c22:843e: icmp_seq=1 ttl=64 rssi=-70 dBm time=8.095 ms
2019-10-25 00:46:19,714 # 12 bytes from fe80::863d:27a0:3c22:843e: icmp_seq=2 ttl=64 rssi=-70 dBm time=7.776 ms
2019-10-25 00:46:19,715 # 
2019-10-25 00:46:19,717 # --- ff02::1 PING statistics ---
2019-10-25 00:46:19,722 # 3 packets transmitted, 3 packets received, 0% packet loss
2019-10-25 00:46:19,726 # round-trip min/avg/max = 7.776/7.988/8.095 ms

@maribu
Copy link
Member

maribu commented Oct 25, 2019

Hmm. I just tried to test it. With both examples/default and examples/gnrc_minimal the Microduino CoreRF is in a boot loop for me.

2019-10-25 13:08:05,149 # main(): This is RIOT! (Version: 20�
2019-10-25 13:08:05,226 # main(): This is RIOT! (Version: 20�
2019-10-25 13:08:05,304 # main(): This is RIOT! (Version: 20�
2019-10-25 13:08:05,381 # main(): This is RIOT! (Version: 20�

examples/saul (which doesn't use the driver) still works fine for me. I have to note that I'm using a avr-gcc in version 9.2.0, so that e.g. any memory corruption bugs that do not go havoc with the old version due to different generated memory layout might be an issue for me. (I cannot 100% rule out that this is an issue with my toolchain, but so far I never had any issue.)

@benpicco
Copy link
Contributor Author

benpicco commented Oct 25, 2019

Hmm, with the latest Ubuntu I still only get gcc-avr 5.4.0

edit: I found a random binary of avr-gcc (GCC) 9.2.0 and with that it's also working.
Could it be a power issue?

@maribu
Copy link
Member

maribu commented Oct 25, 2019

Oha! It seems that there is no LDO on the Microduino CoreRF. I was powering it through the 5V pin (or I believed so!), but I think I can not find and LDO on the chipboard. Likely the chip was powered via current leaking from the terminal in case of examples/saul, but the power was not enough to turn on the transceiver. Let me check if it works when powered with 3.3V on the 3V3 pin.

@maribu
Copy link
Member

maribu commented Oct 25, 2019

OK. Powering with 3.3V using the 3V3 pin on the Microduino CoreRF did the trick.

Two issues discovered during testing:

  • Both my Microduino CoreRF got the same l2 address (84:3E, Long HWaddr: 84:3D:27:A0:3C:22:84:3E). Maybe something is needed to initialize the L2 address from the CPU ID or so?
  • txtsnd in examples/default does not work for me. I changed the address of one board via ifconfig 4 set addr to a different and tried to send to that node, the other does not display the received message.

@benpicco
Copy link
Contributor Author

benpicco commented Oct 25, 2019

Both my Microduino CoreRF got the same l2 address (84:3E, Long HWaddr: 84:3D:27:A0:3C:22:84:3E). Maybe something is needed to initialize the L2 address from the CPU ID or so?

Hm, my suspicion is that cpuid is kinda bogus.

Can you post the output of tests/periph_cpuid for your nodes? For me it's

CPUID: 0x1e 0xa7 0x01 0x1f 0x83 0x04 0x1e 0xa7

txtsnd in examples/default does not work for me. I changed the address of one board via ifconfig 4 set addr to a different and tried to send to that node, the other does not display the received message.

This works for me:

short address

samr21-xpro

2019-10-26 01:25:44,108 #  txtsnd 4 84:3E aaaaaaa

microduino-corerf

2019-10-26 01:25:44,116 #  PKTDUMP: data received:
2019-10-26 01:25:44,175 # ~~ SNIP  0 - size:   7 byte, type: NETTYPE_UNDEF (0)
2019-10-26 01:25:44,176 # 00000000  61  61  61  61  61  61  61
2019-10-26 01:25:44,177 # ~~ SNIP  1 - size:  12 byte, type: NETTYPE_NETIF (-1)
2019-10-26 01:25:44,178 # if_pid: 4  rssi: -84  lqi: 97
2019-10-26 01:25:44,179 # flags: 0x0
2019-10-26 01:25:44,179 # src_l2addr: F9:1A
2019-10-26 01:25:44,180 # dst_l2addr: 84:3E
2019-10-26 01:25:44,181 # ~~ PKT    -  2 snips, total size:  19 byte

long address

samr21-xpro

2019-10-26 01:25:54,525 #  txtsnd 4 84:3D:27:A0:3C:22:84:3E bbbbbbb

microduino-corerf

2019-10-26 01:25:54,587 # PKTDUMP: data received:
2019-10-26 01:25:54,588 # ~~ SNIP  0 - size:   7 byte, type: NETTYPE_UNDEF (0)
2019-10-26 01:25:54,590 # 00000000  62  62  62  62  62  62  62
2019-10-26 01:25:54,591 # ~~ SNIP  1 - size:  18 byte, type: NETTYPE_NETIF (-1)
2019-10-26 01:25:54,592 # if_pid: 4  rssi: -83  lqi: 97
2019-10-26 01:25:54,592 # flags: 0x0
2019-10-26 01:25:54,593 # src_l2addr: F9:1A
2019-10-26 01:25:54,594 # dst_l2addr: 84:3D:27:A0:3C:22:84:3E
2019-10-26 01:25:54,595 # ~~ PKT    -  2 snips, total size:  25 byte

new long addr

samr21-xpro

2019-10-26 01:26:12,973 #  txtsnd 4 11:22:33:44:55:66:77:88 ccccccc

microduino-corerf

2019-10-26 01:26:01,277 # ifconfig 4 set addr_long 11:22:33:44:55:66:77:88
2019-10-26 01:26:01,279 # success: set long address on interface 4 to 11:22:33:44:55:66:77:88
2019-10-26 01:26:13,035 #  PKTDUMP: data received:
2019-10-26 01:26:13,036 # ~~ SNIP  0 - size:   7 byte, type: NETTYPE_UNDEF (0)
2019-10-26 01:26:13,037 # 00000000  63  63  63  63  63  63  63
2019-10-26 01:26:13,039 # ~~ SNIP  1 - size:  18 byte, type: NETTYPE_NETIF (-1)
2019-10-26 01:26:13,040 # if_pid: 4  rssi: -84  lqi: 97
2019-10-26 01:26:13,040 # flags: 0x0
2019-10-26 01:26:13,041 # src_l2addr: F9:1A
2019-10-26 01:26:13,042 # dst_l2addr: 11:22:33:44:55:66:77:88
2019-10-26 01:26:13,043 # ~~ PKT    -  2 snips, total size:  25 byte

new short addess

samr21-xpro

2019-10-26 01:26:43,907 #  txtsnd 4 23:42 ddddddd

microduino-corerf

2019-10-26 01:26:27,091 # ifconfig 4 set addr 23:42
2019-10-26 01:26:27,093 # success: set (short) address on interface 4 to 23:42
2019-10-26 01:26:43,918 #  PKTDUMP: data received:
2019-10-26 01:26:43,966 # ~~ SNIP  0 - size:   7 byte, type: NETTYPE_UNDEF (0)
2019-10-26 01:26:43,967 # 00000000  64  64  64  64  64  64  64
2019-10-26 01:26:43,968 # ~~ SNIP  1 - size:  12 byte, type: NETTYPE_NETIF (-1)
2019-10-26 01:26:43,969 # if_pid: 4  rssi: -84  lqi: 97
2019-10-26 01:26:43,970 # flags: 0x0
2019-10-26 01:26:43,970 # src_l2addr: F9:1A
2019-10-26 01:26:43,971 # dst_l2addr: 23:42
2019-10-26 01:26:43,972 # ~~ PKT    -  2 snips, total size:  19 byt

@maribu
Copy link
Member

maribu commented Oct 26, 2019

It was too late for me to think properly yesterday. I'll test again with antennas plugged into the pigtail connector. That should help...

I saw btw. no code to initialise the L2 address. It seems the SPI versions initialise it implicitly on boot up. Maybe that is not true for the memory mapped version?

@benpicco
Copy link
Contributor Author

I saw btw. no code to initialise the L2 address.

Its right there.
If not initialized the IEEE/long address would be all 0s and the short address would be 0xffff.

@herjulf
Copy link
Contributor

herjulf commented Oct 26, 2019

Hi,
I've tried it out the PR on two AtMega256RFR2 boards, I'm RIOT newcomer. Anyway. The boards have a chip to get a unique EUI64 address had read out that first and of course a platform port for RIOT. Result: With gnrc_networking: ping6 and RPL works. Cool.

ping6 2001:db8::fec2:3d00:0:2a25
12 bytes from 2001:db8::fec2:3d00:0:2a25: icmp_seq=0 ttl=64 rssi=-54 dBm time=13.792 m
s
12 bytes from 2001:db8::fec2:3d00:0:2a25: icmp_seq=1 ttl=64 rssi=-53 dBm time=11.872 m
s
12 bytes from 2001:db8::fec2:3d00:0:2a25: icmp_seq=2 ttl=64 rssi=-53 dBm time=13.792 m
s

--- 2001:db8::fec2:3d00:0:2a25 PING statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 11.872/13.152/13.792 ms
> nib route 2001:db8::/64 dev #7 2001:db8::fec2:3d00:0:2a25/128 via fe80::fec2:3d00:0:2a25 dev #7
Board info: radio-sensors.com
Will test more. Thanks for fixing this...

@maribu
Copy link
Member

maribu commented Oct 26, 2019

OK. With antennas it also works on the Microduino CoreRF (ATmega128RFA1).

For the record: With two microduino-corerf boards and example/saul I was able to confirm that basic low level networking is working via txtsnd as expected.

@herjulf confirmed that that the driver also works on the ATmega256RFR2. By being able to use ping6 in his test, also integration into GNRC was successfully tested.

The issue with the same L2 address on both my Microduino CoreRF boards is unrelated, as luid_get() is used to generate them.

@maribu maribu added the Reviewed: 3-testing The PR was tested according to the maintainer guidelines label Oct 26, 2019
drivers/Makefile.dep Outdated Show resolved Hide resolved
drivers/at86rf2xx/include/at86rf2xx_internal.h Outdated Show resolved Hide resolved
drivers/at86rf2xx/include/at86rf2xx_internal.h Outdated Show resolved Hide resolved
#if defined(MODULE_AT86RFA1) || defined(MODULE_AT86RFR2)
static const uint8_t at86rf2xx_params[] =
{
0 /* dummy value */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, maybe this byte of ROM could be saved some other way. But that could be something to tackle when refactoring the at86rf2xx driver.

@maribu maribu added Reviewed: 1-fundamentals The fundamentals of the PR were reviewed according to the maintainer guidelines Reviewed: 2-code-design The code design of the PR was reviewed according to the maintainer guidelines Reviewed: 4-code-style The adherence to coding conventions by the PR were reviewed according to the maintainer guidelines Reviewed: 5-documentation The documentation details of the PR were reviewed according to the maintainer guidelines labels Oct 26, 2019
@maribu
Copy link
Member

maribu commented Oct 26, 2019

Please squash! (Also squash the requested changes on indent directly.)

Citing the CI:

	Warning: Commit message is longer than 50 (but < 72) characters:
	    "examples/default: add microduino-corerf to BOARD_PROVIDES_NETIF"****

maybe this could be reworded to comply?

@herjulf
Copy link
Contributor

herjulf commented Oct 26, 2019

If there is more things to test I'll help. I dunno about RIOT code style yet but with a Linux background the code does not not look alarming bad. ifdef's are hard to avoid on the MCU operating systems. I'll assume we'll all like to avoid two takes of the same radio code. IMO most important that it is not break any existing platform

I see there is work to do for the driver both HW AES and RPC...
http://www.atmel.com/images/atmel-42356-smart-reduced-power-consumption-techniques_applicationnote_at02594.pdf)

benpicco and others added 4 commits October 26, 2019 23:10
The ATmega128RFA1 and ATmega256RFR2 contain a version of this IP
on the MCU.

The radio core behaves mostly like a at86rf231, but all registers
are mapped to memory and radio states can directly generate interrupts
on the CPU.

The ATmega256RFR2 adds support for automatic retransmissions.
This has not been implemented yet.

Co-authored-by: Josua Arndt <jarndt@ias.rwth-aachen.de>
@miri64
Copy link
Member

miri64 commented Oct 26, 2019

Citing the CI:

	Warning: Commit message is longer than 50 (but < 72) characters:
	    "examples/default: add microduino-corerf to BOARD_PROVIDES_NETIF"****

maybe this could be reworded to comply?

This warning is based on the most commonly understood Git guidelines (just google 50/72 rule + at least with EDITOR=vim and syntax on is also what the commit message editor recommends per default). However, with our guidelines on commit messages I rarely find myself able to stay within the 50 rule (the 72 rule is often enough with some creativity). Should we maybe just remove this warning as it seems to confuse people more than it is helpful.

Now that the radio is enabled, some tests do not fit onto the MCU
anymore.
@benpicco
Copy link
Contributor Author

benpicco commented Oct 26, 2019

@herjulf Thank you for testing!

Feel free to submit a PR to add support for the avr-rss2. It's always good to have a board to go with a CPU so CI can make sure nothing breaks accidentally. SHT25 and DS18B20 should already have drivers in RIOT.
If you want to take a shot at implementing AES or Reduced Power Consumption you are more than welcome.

For me this was more of a curiosity - the chips are easy to support by the existing driver and the work was almost done, so I didn't want to let it wither away. Good to hear this is actually useful to someone! 😃

@herjulf
Copy link
Contributor

herjulf commented Oct 27, 2019

I'll be happy to do a PR for the board. I have get into the RIOT architecture/design and reprogram myself a bit first. Newer boards have BME280 this also supported in RIOT. Wrote that driver for Contiki as BME680. But let focus on this PR with radio support come to an end.
HW AES is nice but even in SW AES can do wire rate at 802.15.4. Otherwise the ATECC608A is interesting for DTLS etc. Got some chips and can communicate at least. Hooking up into TLS/DTLS is another thing.
BTW if you want some AtMega256RFR2 boards for testing/development I can send to you. Just send me your address.

@herjulf
Copy link
Contributor

herjulf commented Oct 27, 2019

Hmm I'm into looking how to pass an address to the radio module. I'll assume this a more general question not related to this PR. Anyway in at86rf2xx_reset we fable some address via luid_get(). In the case I have there is already an unique address (from EUI64MAC chip) to be assigned. So the luid_get() should not happen. Any input here?
Thanks.

@maribu
Copy link
Member

maribu commented Oct 27, 2019

@herjulf: Indeed, with every reset a new L2 address is generated. To me, it is also logically a strange place to generate the L2 in the reset function rather than in init.

However, this is bug is not related to this PR and should be fixed in a separate PR. If someone starts to work on a PR please notify me, otherwise I'll try to find time for it tomorrow.

@benpicco did you use the code of this PR for the samr21-xpro, so that no regression for the AT86RF233 showed up during testing? That would be the last box to tick before merging. I could also test tomorrow, if not.

@benpicco
Copy link
Contributor Author

benpicco commented Oct 27, 2019

@herjulf it would indeed make sense to have a custom interface for this.
Currently all drivers use luid_get() to source an address. The function relies on a unique CPU ID.

If you just want a unique address but don't care about what the address is, the simplest solution would be to add a way to seed the luid module with data other than the CPU ID.

If you want the exact same address as you have on the EUI64MAC chip, a general eui64_get() function would be handy that falls back to luid_get() if no external source is provided.

@maribu yes that was the whole point of choosing that board. 😄

@maribu maribu added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Oct 28, 2019
Copy link
Member

@maribu maribu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK. Diff between prior and post the force push only contains the requested alignment changes (so white space only changes, that do not require retesting) and all the unrelated PRs merged in the meantime due to reparenting.

@maribu maribu merged commit 4cf2151 into RIOT-OS:master Oct 28, 2019
@benpicco benpicco deleted the at86rfmega branch October 28, 2019 08:26
@benpicco
Copy link
Contributor Author

Thank you all for the review and testing!

@benpicco
Copy link
Contributor Author

To me, it is also logically a strange place to generate the L2 in the reset function rather than in init.

@maribu since the reset function also always changes the lowest two bits of the first byte to 0b10 and luid_get() will only increment the first byte, the L2 address will only change with every third reset!

2019-10-28 12:59:59,925 # 0: d2 ae 0c 1b 20 54 05 8f 
2019-10-28 12:59:59,929 # 1: d2 ae 0c 1b 20 54 05 8f 
2019-10-28 12:59:59,931 # 2: d2 ae 0c 1b 20 54 05 8f 
2019-10-28 12:59:59,933 # 3: d6 ae 0c 1b 20 54 05 8f 
2019-10-28 12:59:59,944 # 4: d6 ae 0c 1b 20 54 05 8f 
2019-10-28 12:59:59,948 # 5: d6 ae 0c 1b 20 54 05 8f 
2019-10-28 12:59:59,952 # 6: d6 ae 0c 1b 20 54 05 8f 
2019-10-28 12:59:59,953 # 7: da ae 0c 1b 20 54 05 8f 
2019-10-28 12:59:59,954 # 8: da ae 0c 1b 20 54 05 8f 
2019-10-28 12:59:59,954 # 9: da ae 0c 1b 20 54 05 8f 
    uint8_t luid[8];
    for (unsigned i = 0; i < 10; ++i) {
        luid_get(luid, sizeof(luid));

        /* make sure we mark the address as non-multicast and not globally unique */
        luid[0] &= ~(0x01);
        luid[0] |=  (0x02);

        printf("%d: ", i);
        for (unsigned j = 0; j < sizeof(luid); ++j) {
            printf("%02x ", luid[j]);
        }
        puts("");
    }

@herjulf
Copy link
Contributor

herjulf commented Oct 28, 2019

So yes _init() should be a better home for address initialization rather than reset() especially as luid_get() returns different addresses.
And the idea to have eui64_get() with as first option for EUIMAC chips etc and luid_get() as alternative seems like one solution.
Good to see the PR come through. Thanks.

@miri64
Copy link
Member

miri64 commented Oct 28, 2019

See also #9656 on that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: drivers Area: Device drivers Area: network Area: Networking CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: AVR Platform: This PR/issue effects AVR-based platforms Reviewed: 1-fundamentals The fundamentals of the PR were reviewed according to the maintainer guidelines Reviewed: 2-code-design The code design of the PR was reviewed according to the maintainer guidelines Reviewed: 3-testing The PR was tested according to the maintainer guidelines Reviewed: 4-code-style The adherence to coding conventions by the PR were reviewed according to the maintainer guidelines Reviewed: 5-documentation The documentation details of the PR were reviewed according to the maintainer guidelines Type: new feature The issue requests / The PR implemements a new feature for RIOT
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants