-
Notifications
You must be signed in to change notification settings - Fork 34
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
Mock for sleep and wdt #115
Conversation
Hi @PRosenb, thank you for taking the time to contribute this! Doubly so, since I'm not familiar with the use of these features. Related to that, my first suggestion is that you should add some comments with URLs for the API for these features and/or some examples of how they are used (so that I, as a maintainer, have an easy reference for whether the mocks match the expected usage). Whether it makes more sense to add them to the definitions of the mocks or to the examples is something I'll leave to you. I have a few other thoughts that i'll leave in-line |
#include <Arduino.h> | ||
|
||
unittest(check_ADCSRA_read_write) { | ||
ADCSRA = 123; |
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.
Where is ADCSRA
defined?
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 added explanations into this file
#include <Arduino.h> | ||
|
||
// just check if declaration compiles | ||
ISR (WDT_vect) { |
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 move this to interrupts.cpp
, and add a comment with some explanation of WDT_vect
.
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.
👍
|
||
// allows the production code to define an ISR method | ||
#define _VECTOR(N) __vector_ ## N | ||
#define ISR(vector, ...) \ |
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 add a link to the ISR()
docs. Also, what is the extern
doing here? What does this macro perform?
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 added an explanation there
|
||
#include <Godmode.h> | ||
|
||
#define WDTO_15MS 0 |
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 link to WDT docs
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.
👍
|
||
#include <Godmode.h> | ||
|
||
void sleep_enable() { |
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 link to sleep-mode docs
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 would like to tie the |
Thanks for your feedback @ianfixes, I updated the PR accordingly. |
Sort of. |
I've laid the foundation for this in #116, which I will merge shortly. With that done, I have a few comments on this.
|
Hi @ianfixes, thanks for working on this, looks like a lot of effort.. |
Aah, OK. I see from the docs (https://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html) that these files must be included. I'm a bit torn here, the entire I think it would be acceptable to create an empty file (well, a file with just a comment explaining this purpose) at For the actual implementation, put anything that relies on Godmode support into For stuff that doesn't need Godmode, use Yes, please delete the example sketches. The logic in there should be copied to unit tests though... You can (and should) keep what's in the |
One way to handle this could be to create a sub directory below #include "arduino_ci/sleep.h" That way it is quite clear what files are directly avr related and which files are testing frame work related (alternatively include from the parent directory). |
At the moment, it looks like the available options boil down to a single choice:
For the non-empty option, I don't see a better way to do it than what @PRosenb is already doing (except that I'd like some explicit comments in the file and a note in The empty-file option is mostly the same (comments in the files and a note in the README), but it keeps the mocks with the other mocks. I realize I'm making a big deal out of what looks like a very trivial thing, but over the course of developing this library I've had to refactor the entire mock system a few times... and it's very likely that I will have to do so again before the entire set of builtins is mocked. Keeping a very clear picture of where the mocks "live" (and confining them to only a few files) makes that job manageable. If/when we reach some level of maturity or receive some blessing from the core Arduino team for how we do this, I can relax that a bit. |
I would prefer to not go for the empty file option, because that would imply that including #include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h> because that's all what's needed. Of course I could add an include of Arduino.h and it would not be a disaster, but it feels wrong to impose constraints like that just because of some implementation detail in the test framework. (and yes, that Regarding the There also exists 16 and 32 bit versions of the diff --git a/cpp/arduino/AvrAdc.cpp b/cpp/arduino/AvrAdc.cpp
index 4699e8c..190fded 100644
--- a/cpp/arduino/AvrAdc.cpp
+++ b/cpp/arduino/AvrAdc.cpp
@@ -1,4 +1,7 @@
#include "AvrAdc.h"
-// mock storage to allow access to ADCSRA
-unsigned char sfr_store;
+// mock storage to allow access to ADCSRA and other registers accessed through the _SFR_MEM8/16/32 macros.
+uint8_t sfr8_store;
+uint16_t sfr16_store;
+uint32_t sfr32_store;
+
diff --git a/cpp/arduino/AvrAdc.h b/cpp/arduino/AvrAdc.h
index f5e2cd5..26f2824 100644
--- a/cpp/arduino/AvrAdc.h
+++ b/cpp/arduino/AvrAdc.h
@@ -1,9 +1,54 @@
#pragma once
+#include <stdint.h>
+
// ADCSRA is defined in the CPU specific header files
// like iom328p.h.
// It is liked to _SFR_MEM8 what does not exists in the test environment.
// Therefore we define _SFR_MEM8 here and provide it a storage
// location so that the test code can read/write on it.
-extern unsigned char sfr_store;
-#define _SFR_MEM8(mem_addr) sfr_store
+// While at it, also cover the 16 and 32 bit macros.
+
+extern uint8_t sfr8_store;
+extern uint16_t sfr16_store;
+extern uint32_t sfr32_store;
+
+#define _SFR_MEM8(mem_addr) sfr8_store
+#define _SFR_MEM16(mem_addr) sfr16_store
+#define _SFR_MEM32(mem_addr) sfr32_store
+
+// Notice that the above will map every single 8 bit register defined
+// with _SFR_MEM8 into the same memory location. For some testing that is
+// perfectly fine, but if you for instance are testing that a function
+// updates a register a certain way, that has a high chance of breaking
+// when more than one register is being used.
+//
+// Long term it would be better to provide a separate storage for
+// each register. In the real avr header files, _SFR_MEM8(mem_addr)
+// is wrapping _MMIO_BYTE(mem_addr) which expands to volatile memory
+// access of the memory address. The ADCSRA register might be defined as
+// _SFR_MEM8(0x7A). Other registers use higher address values like 0x80B,
+// so there is too much variation and too high values to use the memory
+// address as an index in an array.
+//
+// In theory it would be possible to use macro token concatenation and
+// generate unique variables like sfr_store_0X7A but it would fail for
+// hex char casing differences (0x7a vs 0X7A) and would either require
+// pre-declaring a huge amount variables for all the possible addresses
+// that could be used, or require some tool to parse the relevant header
+// files and generate code.
+//
+// In my opinion the most viable option to support distinct storage for
+// each register would be to lazily create register storages as they are
+// requested and store them in a linked list with the address as a key. So
+// the _SFR_MEM8 macro would then wrap to a function ala
+//
+// find_or_create_storage(mem_addr) {
+// node = find_in_list(mem_addr)
+// if (!node) {
+// node = create_node(mem_addr)
+// add_to_list(node)
+// }
+// return node
+// }
+ In addition I have a couple of updates to the |
An other option would be to create a new directory that is added to the include path. e.g. |
@hlovdal what I'm hearing re: @PRosenb I'm going to start a separate The shortlist of what will be ironed out in the
I will create issues for these momentarily, with an |
This is my first pull request to contribute code to this project. Constructive feedback is therefore very welcome to allow the project to develop consistently.
avr/sleep.h
with calls toGodmodeState
avr/wdt.h
with calls toGodmodeState
SleepDef
andWdtDef
GodmodeState
and reset them onreset()
define
for ISR to allow the production code to specify an ISR for wdtISR
in functionattachInterrupt()
as it conflicts with AVR'sISR
ADCSRA