-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Canbus: api changes #3466
Conversation
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. |
Just saw this, I'll take a look! |
There was a problem hiding this 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
ports/atmel-samd/reset.c
Outdated
@@ -32,6 +32,7 @@ | |||
void reset(void) { | |||
filesystem_flush(); | |||
NVIC_SystemReset(); | |||
for (;;) {} // unreachable |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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:
circuitpython/ports/atmel-samd/reset.h
Line 39 in dfdfbc4
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.
There was a problem hiding this comment.
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.
dfdfbc4
to
964b7a5
Compare
I revised this PR to get rid of the NORETURN stuff, now in #3469 |
There was a problem hiding this 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.
shared-bindings/canio/Message.c
Outdated
@@ -170,36 +144,80 @@ STATIC const mp_obj_property_t canio_message_extended_obj = { | |||
(mp_obj_t)&mp_const_none_obj}, | |||
}; | |||
|
|||
//| class RemoteTransmissionRequest: |
There was a problem hiding this comment.
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.
shared-module/canio/Message.c
Outdated
{ | ||
self->id = id; | ||
self->size = size; | ||
self->rtr = rtr; | ||
self->rtr = !data; |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
.
shared-bindings/canio/Listener.h
Outdated
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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);
There was a problem hiding this comment.
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
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.
2256258
to
809225f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the case on the SPI implementations MCP2515 and MCP2518.
There was a problem hiding this 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.
Conflict fixed and also got a straggling (read-only) change request addressed |
There was a problem hiding this 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!
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:
address
toid
in Match