rpicsim 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +36 -0
  3. data/Gemfile +10 -0
  4. data/Introduction.md +64 -0
  5. data/LICENSE.txt +24 -0
  6. data/README.md +66 -0
  7. data/docs/ChangeLog.md +10 -0
  8. data/docs/Contributing.md +30 -0
  9. data/docs/Debugging.md +96 -0
  10. data/docs/DefiningSimulationClass.md +84 -0
  11. data/docs/DesignDecisions.md +48 -0
  12. data/docs/HowMPLABXIsFound.md +14 -0
  13. data/docs/IntegrationTesting.md +15 -0
  14. data/docs/IntroductionToRSpec.md +203 -0
  15. data/docs/IntroductionToRuby.md +90 -0
  16. data/docs/KnownIssues.md +204 -0
  17. data/docs/Labels.md +39 -0
  18. data/docs/MakingTestsRunFaster.md +29 -0
  19. data/docs/Manual.md +38 -0
  20. data/docs/PersistentExpectations.md +100 -0
  21. data/docs/Pins.md +143 -0
  22. data/docs/PreventingCallStackOverflow.md +94 -0
  23. data/docs/QuickStartGuide.md +85 -0
  24. data/docs/RSpecIntegration.md +119 -0
  25. data/docs/RamWatcher.md +46 -0
  26. data/docs/Running.md +129 -0
  27. data/docs/SFRs.md +71 -0
  28. data/docs/Stubbing.md +161 -0
  29. data/docs/SupportedCompilers.md +5 -0
  30. data/docs/SupportedDevices.md +14 -0
  31. data/docs/SupportedMPLABXVersions.md +12 -0
  32. data/docs/SupportedOperatingSystems.md +3 -0
  33. data/docs/UnitTesting.md +9 -0
  34. data/docs/Variables.md +140 -0
  35. data/lib/rpicsim.rb +15 -0
  36. data/lib/rpicsim/call_stack_info.rb +306 -0
  37. data/lib/rpicsim/flaws.rb +76 -0
  38. data/lib/rpicsim/instruction.rb +178 -0
  39. data/lib/rpicsim/label.rb +28 -0
  40. data/lib/rpicsim/memory.rb +29 -0
  41. data/lib/rpicsim/memory_watcher.rb +98 -0
  42. data/lib/rpicsim/mplab.rb +138 -0
  43. data/lib/rpicsim/mplab/mplab_assembly.rb +102 -0
  44. data/lib/rpicsim/mplab/mplab_device_info.rb +40 -0
  45. data/lib/rpicsim/mplab/mplab_disassembler.rb +23 -0
  46. data/lib/rpicsim/mplab/mplab_instruction.rb +32 -0
  47. data/lib/rpicsim/mplab/mplab_memory.rb +33 -0
  48. data/lib/rpicsim/mplab/mplab_nmmr_info.rb +21 -0
  49. data/lib/rpicsim/mplab/mplab_observer.rb +12 -0
  50. data/lib/rpicsim/mplab/mplab_pin.rb +61 -0
  51. data/lib/rpicsim/mplab/mplab_processor.rb +30 -0
  52. data/lib/rpicsim/mplab/mplab_program_file.rb +35 -0
  53. data/lib/rpicsim/mplab/mplab_register.rb +25 -0
  54. data/lib/rpicsim/mplab/mplab_sfr_info.rb +21 -0
  55. data/lib/rpicsim/mplab/mplab_simulator.rb +102 -0
  56. data/lib/rpicsim/pin.rb +61 -0
  57. data/lib/rpicsim/program_counter.rb +19 -0
  58. data/lib/rpicsim/program_file.rb +160 -0
  59. data/lib/rpicsim/register.rb +78 -0
  60. data/lib/rpicsim/rspec.rb +11 -0
  61. data/lib/rpicsim/rspec/be_predicate.rb +12 -0
  62. data/lib/rpicsim/rspec/helpers.rb +48 -0
  63. data/lib/rpicsim/rspec/persistent_expectations.rb +53 -0
  64. data/lib/rpicsim/rspec/sim_diagnostics.rb +51 -0
  65. data/lib/rpicsim/search.rb +20 -0
  66. data/lib/rpicsim/sim.rb +702 -0
  67. data/lib/rpicsim/stack_trace.rb +32 -0
  68. data/lib/rpicsim/variable.rb +236 -0
  69. data/lib/rpicsim/version.rb +3 -0
  70. metadata +114 -0
data/docs/Labels.md ADDED
@@ -0,0 +1,39 @@
1
+ Labels
2
+ ====
3
+
4
+ RPicSim automatically processes the symbol table in your firmware's COF file.
5
+ Any symbol in program space is called a _label_ and RPicSim stores a {RPicSim::Label} object to represent it.
6
+
7
+ For an assembly program, RPicSim labels correspond to any assembly label defined in code space.
8
+ These will usually correspond to subroutines and goto targets but could also be variables stored in flash.
9
+ In firmware assembled by MPASM, all labels are exported publicly by default, so they will all be available to RPicSim.
10
+
11
+ For a C program, RPicSim labels usually correspond to functions.
12
+
13
+ Getting a Label object
14
+ ---
15
+
16
+ To get a Label object, call {RPicSim::Sim#label} and pass it the name of the label as the first argument:
17
+
18
+ !!!ruby
19
+ sim.label(:loopStart) # => returns a Label object
20
+
21
+ C compilers will generally put an underscore at the beginning of any labels they generate. For example, to get the address of a C function named `foo`, you might have to access a label named `_foo` using code like this:
22
+
23
+ !!!ruby
24
+ sim.label(:_foo)
25
+
26
+ If RPicSim cannot find the label you want to use, you might troubleshoot it by printing out a list of all the known labels:
27
+
28
+ !!!ruby
29
+ p sim.class.program_file.labels.keys
30
+
31
+
32
+ Using a Label object
33
+ ----
34
+
35
+ You can basically only do three things with a Label object:
36
+
37
+ * Get its name by calling {RPicSim::Label#name}.
38
+ * Get its address by calling {RPicSim::Label#address}.
39
+ * Pass it or its name as an argument to some of the methods for {file:Running.md running} the simulation.
@@ -0,0 +1,29 @@
1
+ Making tests run faster
2
+ ====
3
+
4
+ In general, an RPicSim simulation runs thousands of times slower than the device it simulates.
5
+ Simulating how your firmware behaves over time spans longer than a few milliseconds can be very slow.
6
+ This page contains some tips for making your tests run faster.
7
+
8
+ First of all, {file:UnitTesting.md unit tests} are generally very fast, so try to use unit tests as much as possible.
9
+
10
+ Consider {file:Stubbing.md stubbing} routines in your firmware that take a long time to run if the exact behavior of those routines is not important for the test. This is explained on the {file:Stubbing.md Stubbing page}.
11
+
12
+ If your tests run slowly because the firmware is waiting for a simulated timer to advance, you might use techniques similar to stubbing in order to advance the timer by hundreds of counts at once without having simulate hundreds of processor cycles.
13
+
14
+ For the specs that are slow, use RSpec metadata to mark them as slow.
15
+ This allows you to prevent those tests from running during normal development.
16
+ For example, you could mark an individual example as slow like this:
17
+
18
+ !!!ruby
19
+ it "can run for a second", slow: true do
20
+ run_cycles 4_000_000
21
+ end
22
+
23
+ You can also mark an entire context or describe block as slow using the same syntax.
24
+ To exclude the slow tests, run `rspec` with this command:
25
+
26
+ !!!plain
27
+ rspec -t '~slow'
28
+
29
+ There is nothing special about the word "slow" in the example above; you can make up your own metadata tags to fit your application.
data/docs/Manual.md ADDED
@@ -0,0 +1,38 @@
1
+ RPicSim manual
2
+ ====
3
+
4
+ This is the table of contents for the RPicSim manual.
5
+
6
+ * Overview
7
+ * {file:Introduction.md}
8
+ * {file:ChangeLog.md Change log}
9
+ * {file:QuickStartGuide.md Quick-start guide}
10
+ * {file:SupportedDevices.md Supported devices}
11
+ * {file:SupportedCompilers.md Supported compilers}
12
+ * {file:SupportedMPLABXVersions.md Supported MPLAB X versions}
13
+ * {file:SupportedOperatingSystems.md Supported operating systems}
14
+ * Basic usage
15
+ * {file:IntroductionToRuby.md Introduction to Ruby}
16
+ * {file:DefiningSimulationClass.md Defining your simulation class}
17
+ * {file:Pins.md}
18
+ * {file:Labels.md}
19
+ * {file:Running.md Running the simulation}
20
+ * {file:Variables.md}
21
+ * {file:SFRs.md}
22
+ * {file:RamWatcher.md RAM watcher}
23
+ * {file:PreventingCallStackOverflow.md Preventing call stack overflow}
24
+ * Testing firmware with RPicSim and RSpec
25
+ * {file:IntroductionToRSpec.md Introduction to RSpec}
26
+ * {file:UnitTesting.md Unit testing}
27
+ * {file:IntegrationTesting.md Integration testing}
28
+ * {file:RSpecIntegration.md RSpec integration}
29
+ * {file:PersistentExpectations.md Persistent expectations}
30
+ * Tips
31
+ * {file:Stubbing.md}
32
+ * {file:MakingTestsRunFaster.md Making tests run faster}
33
+ * {file:Debugging.md}
34
+ * More about RPicSim
35
+ * {file:HowMPLABXIsFound.md How MPLAB X is Found}
36
+ * {file:KnownIssues.md Known issues}
37
+ * {file:DesignDecisions.md Design decisions}
38
+ * {file:Contributing.md}
@@ -0,0 +1,100 @@
1
+ Persistent expectations
2
+ ====
3
+
4
+ An RSpec example usually consists of some code to set up the situation being tested, and some code called an _expectation_ that defines the expected outcome.
5
+ As discussed in the {file:Pins.md Pins page}, to write an expectation that the main output pin is driving high you could write:
6
+
7
+ !!!ruby
8
+ expect(main_output).to be_driving_high
9
+
10
+ This is fine, but it is not a great way to test firmware because (unless you put it in a loop or method) it only runs once, at one particular cycle of the simulation. It will not catch any accidental glitches on the main output pin that occur at a later time.
11
+
12
+ RPicSim helps to address this by adding a new feature to RSpec examples called "persistent expectations".
13
+ Persistent expectations are implemented in the module {RPicSim::RSpec::PersistentExpectations}, which is part of RPicSim's {file:RSpecIntegration.md RSpec integration}.
14
+
15
+ Usage
16
+ ----
17
+
18
+ To set a persistent expectation, call the {RPicSim::RSpec::PersistentExpectations#expecting expecting} method inside an RSpec example or a before/after hook:
19
+
20
+ !!!ruby
21
+ expecting main_output => be_driving_high
22
+
23
+ The argument to `expecting` is a hash with the objects being tested as the keys, and the matchers they are being tested against as the values. You can specify multiple persistent expectations on different objects:
24
+
25
+ !!!ruby
26
+ expecting main_output => be_driving_high, error_output => be_drving_low
27
+
28
+ You can not specify multiple persistent expectations that apply to the same object. If you specify a persistent expectation for an object that already had one, the latest one you specify will override the previous one.
29
+
30
+ To remove a persistent expectation, specify a matcher of `nil`:
31
+
32
+ !!!ruby
33
+ expecting main_output => nil
34
+
35
+ The persistent expectations will not be checked right away when they are added but they will be checked after every step of the simulation.
36
+ You can also check them at any time by calling `check_expecations` inside your RSPec example.
37
+
38
+ Persistent expectations, when combined with RSpec's `satisfy` matcher, are very powerful. If `counter` is a {RPicSim::Variable variable} in your simulation, you could use this code to ensure that `counter` never goes above 120:
39
+
40
+ !!!ruby
41
+ expecting counter => satisfy { |c| c.value <= 120 }
42
+
43
+ Persistent expectations are implemented in a straightforward way: the expectations are stored in a hash that is an instance variable of the RSpec example, and the expectations are checked after every step via a hook that is registered with {RPicSim::Sim#every_step} when the simulation is started.
44
+
45
+ Example
46
+ ----
47
+
48
+ The following RSpec example tests that the main output pin is held low (after giving the device some time to start up), but then it goes high after the main input goes high:
49
+
50
+ !!!ruby
51
+ it "mirrors the main input onto the main output pin" do
52
+ run_cycles 120 # Give the device time to start up.
53
+
54
+ expecting main_output => be_driving_low
55
+ run_cycles 800
56
+
57
+ main_input.set true
58
+
59
+ # Turn off the persistent expectation temporarily to give the device
60
+ # time to detect the change in the input.
61
+ expecting main_output => nil
62
+ run_cycles 200
63
+
64
+ expecting main_output => be_driving_high
65
+ run_cycles 800
66
+ end
67
+
68
+ In the above example, we removed the persistent expectation on `main_output` temporarily because the device was in a transitionary period and we didn't know exactly when the transition would happen.
69
+ We chose to stop monitoring the pin for the duration of the transition and then start monitoring it later, at which point we expect the pin to be in its new state.
70
+
71
+ If you need to repeat this patten many times in your tests, you might consider adding a method in `spec_helper.rb` to help you do it:
72
+
73
+ !!!ruby
74
+ def transition(opts={})
75
+ opts = opts.dup
76
+ cycles = opts.delete(:cycles) || 50
77
+ opts.keys.each { |k| expectations.delete k }
78
+ run_cycles cycles
79
+ expectations.merge! opts
80
+ check_expectations
81
+ end
82
+
83
+ Then the test above could become:
84
+
85
+ !!!ruby
86
+ it "mirrors the main input onto the main output pin" do
87
+ run_cycles 120 # Give the device time to start up.
88
+
89
+ expecting main_output => be_driving_low
90
+ run_cycles 800
91
+
92
+ main_input.set true
93
+
94
+ transition main_output => be_driving_high
95
+ run_cycles 800
96
+ end
97
+
98
+ The `transition` method above does not check anything about the main output during the transition time, so unfortunately it might miss any glitches that happen during that time.
99
+ Also, it is not very general.
100
+ For these reasons, it has not been integrated into the RPicSim code and you will need to copy it to your `spec_helper.rb` file yourself if you want to use it.
data/docs/Pins.md ADDED
@@ -0,0 +1,143 @@
1
+ Pins
2
+ ====
3
+
4
+ The only way a PIC microcontroller can have an effect on the world is through its pins, so pins are an important part of a PIC simulation.
5
+ RPicSim exposes the modeling of pins that the MPLAB X simulator provides.
6
+ Each {RPicSim::Sim} simulation object contains a collection of {RPicSim::Pin} objects, one for each external pin of the device.
7
+ Using a Pin object, you can detect whether the pin is an input or an output.
8
+ If it is an output, you can detect whether the output is driving high or driving low.
9
+ If it is an input, you can set the simulated value of the input.
10
+
11
+ Getting a Pin object
12
+ ----
13
+
14
+ The {RPicSim::Sim#pin} method can be called on your simulation object to retrieve a {RPicSim::Pin} object.
15
+ If you are using RPicSim's {file:RSpecIntegration.md RSpec integration}, the `pin` method inside an example automatically redirects to the simulation object so you can use it easily like this:
16
+
17
+ !!!ruby
18
+ it "works" do
19
+ pin(:RA1) # => returns a Pin object
20
+ end
21
+
22
+ The first argument of {RPicSim::Sim#pin} should be the name of the pin as a symbol.
23
+ The allowed names come from the MPLAB X code, but they should match the names given in the PIC datasheet.
24
+ For example, the PIC10F322 pin RA1 can be referred to by many names, including `:RA1`, `:PWM2`, `:AN1`, and `:NCO1CLK`.
25
+
26
+ RPicSim does not model the GND and VDD pins.
27
+
28
+ Pin aliases
29
+ ----
30
+
31
+ To make your tests readable and protect against future schematic changes, you should try to refer to pins by an application-specific name like "main output" instead of a datasheet name like RA1. RPicSim provides a feature to help you do this called a _pin alias_.
32
+
33
+ Within your {file:DefiningSimulationClass.md simulation class definition}, call {RPicSim::Sim::ClassDefinitionMethods#def_pin def_pin} to define your pins.
34
+ For example:
35
+
36
+ !!!ruby
37
+ class MySim < RRicSim::Sim
38
+ #...
39
+
40
+ def_pin :main_output, :RA1
41
+
42
+ end
43
+
44
+ This makes `:main_output` be an alias for `:RA1`. You can now access the Pin object by passing `:main_output` as the argument to {RPicSim::Sim#pin}:
45
+
46
+ !!!ruby
47
+ pin(:main_output)
48
+
49
+ Defining a pin alias also adds a "shortcut" method by the same name. This means that you can access the pin like this:
50
+
51
+ !!!ruby
52
+ sim.main_output
53
+
54
+ The shortcuts are also available in RSpec thanks to RPicsim's {file:RSpecIntegration.md RSpec integration}, so you can simply write `main_output` in any of your RSpec examples:
55
+
56
+ !!!ruby
57
+ it "drives the main output high" do
58
+ expect(main_output).to be_driving_high
59
+ end
60
+
61
+ Note that since the shortcuts are available in many places, your pin names might conflict with names defined in other places.
62
+
63
+
64
+ Pin methods
65
+ ----
66
+
67
+ Once you have a Pin object, you can call any of the methods listed in {RPicSim::Pin} on it. These methods allow you to ask about the state of the Pin and to set the simulated input value of an input pin.
68
+
69
+
70
+ Issues
71
+ ----
72
+
73
+ The modelling of Pins provided by the MPLAB X simulator is fairly new and there are still some bugs in it.
74
+ For example, you might need to clear the ANSELx bit of a pin in your firmware before trying to set its output value, or else the simulator will mistakenly think your pin is driving low.
75
+ For more information, see the {file:KnownIssues.md Known issues page}.
76
+
77
+
78
+ PinMirror example
79
+ ----
80
+
81
+ This section contains a simple example showing how to apply the information above and use {RPicSim::Pin} objects.
82
+
83
+ Here is a minimal MPASM assembly program for the PIC10F322 that continuously reads the value from an input pin (RA0) and copies it to an output pin (RA1):
84
+
85
+ !!!plain
86
+ #include p10F322.inc
87
+ __config(0x3E06)
88
+ code 0
89
+ clrf ANSELA
90
+ bcf TRISA, 1
91
+ loopStart
92
+ btfss PORTA, 0
93
+ bcf LATA, 1
94
+ btfsc PORTA, 0
95
+ bsf LATA, 1
96
+ goto loopStart
97
+ end
98
+
99
+ In `spec/spec_helper.rb`, we make a simulation class that points to the compiled COF file and defines some pin aliases:
100
+
101
+ !!!ruby
102
+ require 'rpicsim/rspec'
103
+
104
+ class PinMirror < RPicSim::Pic
105
+ device_is "PIC10F322"
106
+ filename_is File.dirname(__FILE__) + "../firmware/dist/firmware.cof"
107
+
108
+ def_pin :main_input, :RA0
109
+ def_pin :main_output, :RA1
110
+ end
111
+
112
+ In `spec/pin_mirror_spec.rb`, we write a simple test that changes the input and makes sure that the output changes accordingly:
113
+
114
+ !!!ruby
115
+ require_relative 'spec_helper'
116
+
117
+ describe "PinMirror" do
118
+ before do
119
+ start_sim PinMirror
120
+ end
121
+
122
+ it "continuously mirrors" do
123
+ main_input.set false
124
+ run_cycles 10
125
+ expect(main_output).to be_driving_low
126
+
127
+ run_cycles 10
128
+ expect(main_output).to be_driving_low
129
+
130
+ main_input.set true
131
+ run_cycles 10
132
+ expect(main_output).to be_driving_high
133
+
134
+ run_cycles 10
135
+ expect(main_output).to be_driving_high
136
+
137
+ main_input.set false
138
+ run_cycles 10
139
+ expect(main_output).to be_driving_low
140
+ end
141
+ end
142
+
143
+ The calls to {RPicSim::Sim#run_cycles} are needed to give the simulated device enough time to react to the change on its input.
@@ -0,0 +1,94 @@
1
+ Preventing call stack overflow
2
+ ====
3
+
4
+ PIC microcontrollers feature a stack implemented in hardware that keeps track of return addresses for subroutine calls.
5
+ Every time a `CALL` instruction is executed, the return address is pushed onto the stack.
6
+ Every time a `RETURN` or similar instruction is executed, the return address is popped off of the stack.
7
+ The PIC datasheets tend to refer to this as the "stack", but in RPicSim it is known as the _call stack_ in order to make it clear that this is the stack that relates to the `CALL` instruction and it is different from any kind of stack a C compiler might place in RAM for local variables.
8
+
9
+ The call stack has a limited depth that depends on the device you are using.
10
+ If your program has too many levels of nested subroutines then the call stack could overflow.
11
+ Depending on the device, a call stack overflow could cause a reset or incorrect program execution.
12
+ Therefore, it is important to avoid a call stack overflow.
13
+
14
+ RPicSim can trace all the possible paths of execution for a PIC program in order to calculate what the maximum possible call stack depth is.
15
+ You can easily add this to your RSpec tests.
16
+ Here is an example:
17
+
18
+ !!!ruby
19
+ describe "call stack" do
20
+ subject(:call_stack_info) do
21
+ RPicSim::CallStackInfo.hash_from_program_file(MySim.program_file, [0, 4])
22
+ end
23
+
24
+ specify "mainline code uses no more than 5 levels" do
25
+ call_stack_info[0].max_depth be <= 5
26
+ end
27
+
28
+ specify "ISR code uses no more than 2 levels" do
29
+ call_stack_info[4].max_depth.should be <= 1
30
+ end
31
+ end
32
+
33
+ This example is for a PIC10F322 program.
34
+ The mainline code's entry point is at address 0, and the first RSpec example makes sure that the maximum depth of subroutine calls in the mainline code is 5.
35
+ The firmware uses an interrupt and the interrupt vector is at address 4.
36
+ The second RSpec example makes sure that the ISR uses at most one level of the call stack (the ISR itself can only call one subroutine).
37
+
38
+ If the two tests above pass, then we can calculate the maximum amount of call stack space that might ever be used. If the mainline code is at its deepest point and it is interrupted by an ISR that happens to reach its deepest point, then the call stack would have:
39
+
40
+ * 5 levels used by the mainline code.
41
+ * 1 level used by the processor itself to store the address to return to when the interrupt is done.
42
+ * 1 level used by the ISR code.
43
+
44
+ The means there can be at most 7 things stored on the call stack, which is safely below the PIC10F322's limit of 8.
45
+
46
+ Suppose RPicSim tells you that your mainline code could take 5 levels of the stack and you are not sure why.
47
+ The {RPicSim::CallStackInfo#worst_case_code_paths_filtered_report} can produce a set of example code paths that demonstrate how the maximum depth could potentially happen.
48
+ To see the report, just add a line like this:
49
+
50
+ !!!ruby
51
+ p call_stack_info[0].worst_case_code_paths_filtered_report
52
+
53
+ The report will be a series of code paths that look something like this:
54
+
55
+ !!!plain
56
+ CodePath:
57
+ Instruction(0x0000, GOTO 0x20)
58
+ Instruction(0x0024 = start2, CALL 0x40)
59
+ Instruction(0x0040 = foo, CALL 0x41)
60
+ Instruction(0x0041 = goo, CALL 0x60)
61
+ Instruction(0x0060 = hoo, CALL 0x80)
62
+ Instruction(0x0080 = ioo, CALL 0x100)
63
+ Instruction(0x0100 = joo, CLRF 0x7)
64
+
65
+ This example code path shows five CALL instructions that could potentially be nested.
66
+
67
+ How it works
68
+ ----
69
+
70
+ The {RPicSim::ProgramFile} class uses the MPLAB X disassembler to provide a graph with every reachable instruction in the firmware.
71
+ The {RPicSim::CallStackInfo} class traverses all possible paths through that graph from a given entry point to calculate the maximum possible call stack depth at every point in the graph.
72
+
73
+ Limitations
74
+ ----
75
+
76
+ The code for disassembling in {RPicSim::ProgramFile} currently only works with the midrange and baseline PIC instruction sets. However, it should be easy to expand it to the other instruction sets.
77
+
78
+ The algorithm is pessimistic:
79
+
80
+ * It does not try to track the runtime values of any of your program's variables in order to predict which code paths will happen.
81
+ * It cannot handle recursive functions because there is no way for it to figure out the maximum level of recursion.
82
+ * It does not know when interrupts are enabled.
83
+
84
+ All of those things are OK because they can only cause the algorithm to give an answer that is more pessimistic than reality; the reported maximum call stack depth might be higher than what is actually possible.
85
+
86
+ However, there are some things that can mess up the algorithm in a bad way and give you incorrect results:
87
+
88
+ * If you write to the PC register in order to do a computed jump, the algorithm does not currently detect that and will not correctly consider code paths coming from that instruction.
89
+ Be careful about this, because a computed jump might be generated automatically by a C compiler.
90
+ * Similarly, it cannot handle jumps on devices that have paged memory. In order to determine where a jump actually goes, it would need to know what page is selected by looking at the history of the program's execution.
91
+
92
+ This code is not suitable (yet) for any firmware that uses a computed jump or paged program memory.
93
+
94
+ This code only checks the hardware call stack; it does not check any kind of data stack that your compiler might use to store the values of local variables. Checking that kind of stack is important, but would be much harder.