Skip to content

Canbus: api changes #3466

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

Merged
merged 20 commits into from
Sep 30, 2020
Merged

Canbus: api changes #3466

merged 20 commits into from
Sep 30, 2020

Conversation

jepler
Copy link

@jepler jepler commented Sep 24, 2020

Thanks to @siddacious for finding various problems and suggesting improvements to the API.

Net doc changes from this PR: https://gist.github.com/jepler/37722372347f6847278a67db3cc718c5

Revised loopback test program, passes on SAM E54: https://gist.github.com/d8aedc6b12615970531f54a2f35b9b41

High level summary:

  • renamed Listener.read -> receive to match CAN.send
  • removed Listener.readinto (no zero-allocation CAN reading)
  • added RemoteTransmissionRequest class (no voodoo switching between rtr and non-rtr message types)
  • Renamed address to id in Match

@jepler
Copy link
Author

jepler commented Sep 24, 2020

If the noreturn stuff doesn't work, I'll pull it out. However, it DOES become relevant when I start work on the stm32 CAN implementation, it got here by slight accident.

@siddacious
Copy link

Just saw this, I'll take a look!

siddacious
siddacious previously approved these changes Sep 24, 2020
Copy link

@siddacious siddacious left a comment

Choose a reason for hiding this comment

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

Looks good to me; the empty for loops are confusing but I don't think they're hurting anything

@@ -32,6 +32,7 @@
void reset(void) {
filesystem_flush();
NVIC_SystemReset();
for (;;) {} // unreachable

Choose a reason for hiding this comment

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

Does this and the others do something?

Copy link
Author

Choose a reason for hiding this comment

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

Since e.g., NVIC_SystemReset never returns, this creates an unreachable 2 byte opcode that jumps to itself. However, this allows the compiler to conclude that reset() never returns, as promised. A more correct change would be to actually let gcc understand that NVIC_SystemReset does not return, but I don't know how to do that.

Copy link
Collaborator

Choose a reason for hiding this comment

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

There is a macro NORETURN which expands to __atribute__((noreturn)), which is described on this page:
https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-noreturn-function-attribute

Copy link
Author

@jepler jepler Sep 25, 2020

Choose a reason for hiding this comment

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

Yes, I should have used the macro instead of the __attribute__ here where I added it:

void reset(void) __attribute__((noreturn));

gcc is complaning because of the NEW noreturn attribute; when it looks at the function definition it thinks it detects the function DOES return, because NVIC_SystemReset is not noreturn.

Copy link
Collaborator

Choose a reason for hiding this comment

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

because NVIC_SystemReset is not noreturn.

That is the kind of thing to fix in the asf4 repo, I'd say.

@jepler
Copy link
Author

jepler commented Sep 25, 2020

I revised this PR to get rid of the NORETURN stuff, now in #3469

@jepler jepler requested a review from siddacious September 25, 2020 17:40
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

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

Thanks! I like the address -> id switch but would like to see Message/RTR object split done completely. Otherwise, it's irregular to everything else in shared-bindings.

@@ -170,36 +144,80 @@ STATIC const mp_obj_property_t canio_message_extended_obj = {
(mp_obj_t)&mp_const_none_obj},
};

//| class RemoteTransmissionRequest:
Copy link
Member

Choose a reason for hiding this comment

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

Please put this in a separate file. It'll be hard to find otherwise.

{
self->id = id;
self->size = size;
self->rtr = rtr;
self->rtr = !data;
Copy link
Member

Choose a reason for hiding this comment

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

Please just split Message and RTR fully. It's easier understand a consistent structure.

@@ -47,7 +47,7 @@ typedef struct {
void common_hal_canio_listener_construct(canio_listener_obj_t *self, canio_can_obj_t *can, size_t nmatch, canio_match_obj_t **matches, float timeout);
void common_hal_canio_listener_check_for_deinit(canio_listener_obj_t *self);
void common_hal_canio_listener_deinit(canio_listener_obj_t *self);
bool common_hal_canio_listener_readinto(canio_listener_obj_t *self, canio_message_obj_t *message);
bool common_hal_canio_listener_receiveinto(canio_listener_obj_t *self, canio_message_obj_t *message);
Copy link
Member

Choose a reason for hiding this comment

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

No need to have these functions here because they are in shared-bindings/canio/Listener.h.

void common_hal_canio_listener_construct(canio_listener_obj_t *self, canio_can_obj_t *can, size_t nmatch, canio_match_obj_t **matches, float timeout);
void common_hal_canio_listener_check_for_deinit(canio_listener_obj_t *self);
void common_hal_canio_listener_deinit(canio_listener_obj_t *self);
bool common_hal_canio_listener_receiveinto(canio_listener_obj_t *self, canio_message_obj_t *message);
Copy link
Member

Choose a reason for hiding this comment

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

I'd just have this as receive and return an mp_obj_t. That way you can have two separate structure for Message and RTR.

Copy link
Author

Choose a reason for hiding this comment

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

That won't work out, because the type of message must be determined by the hardware.

Copy link
Author

Choose a reason for hiding this comment

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

Let me amplify on a hastily written answer.

When calling read() or next(), the software has to commit to taking the next messge that has arrived on this FIFO, regardless of whether it is a RTR or not. This is true of all 3 HW implementations I've looked at (SAM E, STM, and ESP32S2).

Having the common_hal routine read into a single structure which can accommodate either type of message is the simplest way I see to do it. Adding a "peek" routine, a union type, or other complication is a needless complication, compared to having an "rtr" field in the C struct.

This implementation detail is transparent to the user, because the two types become distinct Python types with distinct behaviors (one has a data property, one has a size property). As we've discussed in other contexts, the 8 bytes of storage set aside for the message that have to be allocated regardless of whether it's an RTR are also not going to break the bank, it's still within that single GC heap block.

I can change it if it's important, but I don't think it'll be an improvement. If you do want me to change it, please show me what the common_hal APIs should be called and what their parameter lists should look like so that I deliver something that is satisfactory.

Copy link
Author

Choose a reason for hiding this comment

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

I went ahead and wrote what I think you want. It adds about 300 lines of largely duplicate code, because

  • now common hal's implementation listener's receive needs two copies of the code, to read the HW registers into two different structures
  • and an additional common hal send remote transmission request API is now needed, which duplicates copying the new rtr structure into the hw registers

This also amounts to about 300 extra bytes of firmware, but for me it is the duplicated code that is the most objectionable.

e.g., this had to be added, while in the existing function RTR is hard-coded as false instead of as message->rtr:

+void common_hal_canio_can_send_remote_transmission_request(canio_can_obj_t *self, canio_remote_transmission_request_obj_t *remote_transmissi
on_request)
+{
+    maybe_auto_restart(self);
+
+    // We have just one dedicated TX buffer, use it!
+    canio_can_tx_buffer_t *ent = &self->state->tx_buffer[0];
+
+    ent->txb0.bit.ESI = false;
+    ent->txb0.bit.XTD = remote_transmission_request->extended;
+    ent->txb0.bit.RTR = true;
+    if (remote_transmission_request->extended) {
+        ent->txb0.bit.ID = remote_transmission_request->id;
+    } else {
+        ent->txb0.bit.ID = remote_transmission_request->id << 18; // short addresses are left-justified
+    }
+
+    ent->txb1.bit.MM = 0; // "remote_transmission_request marker"
+    ent->txb1.bit.EFC = 0; // don't store fifo events to event queue
+    ent->txb1.bit.FDF = 0; // Classic CAN format
+    ent->txb1.bit.BRS = 0; // No bit rate switching
+    ent->txb1.bit.DLC = remote_transmission_request->size;
+
+    // TX buffer add request
+    self->hw->TXBAR.reg = 1;
+
+    // wait 8ms (hard coded for now) for TX to occur
+    uint64_t deadline = port_get_raw_ticks(NULL) + 8;
+    while (port_get_raw_ticks(NULL) < deadline && !(self->hw->TXBTO.reg & 1)) {
+        RUN_BACKGROUND_TASKS;
+    }
+}
+

changed/added internal APIs are:

-bool common_hal_canio_listener_receiveinto(canio_listener_obj_t *self, canio_message_obj_t *message);
+mp_obj_t common_hal_canio_listener_receive(canio_listener_obj_t *self);

and

-void common_hal_canio_can_send(canio_can_obj_t *self, canio_message_obj_t *message);
+void common_hal_canio_can_send_message(canio_can_obj_t *self, canio_message_obj_t *message);
+void common_hal_canio_can_send_remote_transmission_request(canio_can_obj_t *self, canio_remote_transmission_request_obj_t *remote_transmission_request);

and

-bool common_hal_canio_message_get_rtr(const canio_message_obj_t *self);
-void common_hal_canio_message_set_rtr(canio_message_obj_t *self, bool rtr);
-void common_hal_canio_remote_transmission_request_set_length(canio_message_obj_t *self, size_t length);

Copy link
Author

Choose a reason for hiding this comment

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

The comment "+ ent->txb1.bit.MM = 0; // "remote_transmission_request marker"" is just wrong btw

@jepler jepler requested a review from tannewt September 28, 2020 22:21
This prepares for creating a separate RemoteTransmissionRequest class
It reuses most of canio.Message's implementation, and structure
…cture

This already begins obscuring things, because now there are two sets of
shared-module functions for manipulating the same structure, e.g.,
common_hal_canio_remote_transmission_request_get_id and
common_hal_canio_message_get_id
0 should actually indicate a "match everything" filter which you otherwise
couldn't indicate with any single Match object, and an
all-address-bits-set number should indicate a "match single address"
filter.  Use an optional/default None argument to do the job.
Copy link

@siddacious siddacious left a comment

Choose a reason for hiding this comment

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

image

I believe the above properties are also read-only; can you clarify that in the docs?

tannewt
tannewt previously approved these changes Sep 29, 2020
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

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

This looks good to me! Thank you for iterating on this.

siddacious
siddacious previously approved these changes Sep 29, 2020
@jepler jepler dismissed stale reviews from siddacious and tannewt via c129c8f September 30, 2020 01:15
@jepler
Copy link
Author

jepler commented Sep 30, 2020

Conflict fixed and also got a straggling (read-only) change request addressed

Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

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

Looks good! Thank you!

@tannewt tannewt merged commit 2ac2f62 into adafruit:main Sep 30, 2020
@jepler jepler deleted the canbus-api-changes branch November 3, 2021 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants