rpicsim 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +36 -0
- data/Gemfile +10 -0
- data/Introduction.md +64 -0
- data/LICENSE.txt +24 -0
- data/README.md +66 -0
- data/docs/ChangeLog.md +10 -0
- data/docs/Contributing.md +30 -0
- data/docs/Debugging.md +96 -0
- data/docs/DefiningSimulationClass.md +84 -0
- data/docs/DesignDecisions.md +48 -0
- data/docs/HowMPLABXIsFound.md +14 -0
- data/docs/IntegrationTesting.md +15 -0
- data/docs/IntroductionToRSpec.md +203 -0
- data/docs/IntroductionToRuby.md +90 -0
- data/docs/KnownIssues.md +204 -0
- data/docs/Labels.md +39 -0
- data/docs/MakingTestsRunFaster.md +29 -0
- data/docs/Manual.md +38 -0
- data/docs/PersistentExpectations.md +100 -0
- data/docs/Pins.md +143 -0
- data/docs/PreventingCallStackOverflow.md +94 -0
- data/docs/QuickStartGuide.md +85 -0
- data/docs/RSpecIntegration.md +119 -0
- data/docs/RamWatcher.md +46 -0
- data/docs/Running.md +129 -0
- data/docs/SFRs.md +71 -0
- data/docs/Stubbing.md +161 -0
- data/docs/SupportedCompilers.md +5 -0
- data/docs/SupportedDevices.md +14 -0
- data/docs/SupportedMPLABXVersions.md +12 -0
- data/docs/SupportedOperatingSystems.md +3 -0
- data/docs/UnitTesting.md +9 -0
- data/docs/Variables.md +140 -0
- data/lib/rpicsim.rb +15 -0
- data/lib/rpicsim/call_stack_info.rb +306 -0
- data/lib/rpicsim/flaws.rb +76 -0
- data/lib/rpicsim/instruction.rb +178 -0
- data/lib/rpicsim/label.rb +28 -0
- data/lib/rpicsim/memory.rb +29 -0
- data/lib/rpicsim/memory_watcher.rb +98 -0
- data/lib/rpicsim/mplab.rb +138 -0
- data/lib/rpicsim/mplab/mplab_assembly.rb +102 -0
- data/lib/rpicsim/mplab/mplab_device_info.rb +40 -0
- data/lib/rpicsim/mplab/mplab_disassembler.rb +23 -0
- data/lib/rpicsim/mplab/mplab_instruction.rb +32 -0
- data/lib/rpicsim/mplab/mplab_memory.rb +33 -0
- data/lib/rpicsim/mplab/mplab_nmmr_info.rb +21 -0
- data/lib/rpicsim/mplab/mplab_observer.rb +12 -0
- data/lib/rpicsim/mplab/mplab_pin.rb +61 -0
- data/lib/rpicsim/mplab/mplab_processor.rb +30 -0
- data/lib/rpicsim/mplab/mplab_program_file.rb +35 -0
- data/lib/rpicsim/mplab/mplab_register.rb +25 -0
- data/lib/rpicsim/mplab/mplab_sfr_info.rb +21 -0
- data/lib/rpicsim/mplab/mplab_simulator.rb +102 -0
- data/lib/rpicsim/pin.rb +61 -0
- data/lib/rpicsim/program_counter.rb +19 -0
- data/lib/rpicsim/program_file.rb +160 -0
- data/lib/rpicsim/register.rb +78 -0
- data/lib/rpicsim/rspec.rb +11 -0
- data/lib/rpicsim/rspec/be_predicate.rb +12 -0
- data/lib/rpicsim/rspec/helpers.rb +48 -0
- data/lib/rpicsim/rspec/persistent_expectations.rb +53 -0
- data/lib/rpicsim/rspec/sim_diagnostics.rb +51 -0
- data/lib/rpicsim/search.rb +20 -0
- data/lib/rpicsim/sim.rb +702 -0
- data/lib/rpicsim/stack_trace.rb +32 -0
- data/lib/rpicsim/variable.rb +236 -0
- data/lib/rpicsim/version.rb +3 -0
- metadata +114 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
Quick-start guide
|
2
|
+
====
|
3
|
+
|
4
|
+
This guide should help you get started with RPicSim.
|
5
|
+
It is assumed that you are familiar with some PIC firmware development environment and are able to compile your firmware to a COF or HEX file.
|
6
|
+
By the end of this guide, you will have a suite of automated simulator-based tests for the firmware.
|
7
|
+
|
8
|
+
Installing prerequisites
|
9
|
+
----
|
10
|
+
|
11
|
+
First, on a computer running Windows, install RPicSim and the software it requires:
|
12
|
+
|
13
|
+
1. Install [MPLAB X](http://www.microchip.com/pagehandler/en-us/family/mplabx/). RPicSim uses the Microchip Java classes from MPLAB X.
|
14
|
+
2. Install the latest version of [JRuby](http://jruby.org/).
|
15
|
+
3. Run the command `jgem install rpicsim rspec`. This will install the latest versions of RPicSim and [RSpec](http://rspec.info/) from [RubyGems.org](http://rubygems.org/).
|
16
|
+
|
17
|
+
Setting up your directories
|
18
|
+
----
|
19
|
+
|
20
|
+
You should set up your PIC development environment so that it creates a COF or HEX file inside a directory named "dist".
|
21
|
+
The file does not need to be at the top level of "dist"; it can be in any subdirectory inside "dist".
|
22
|
+
This requirement is due to a limitation in the MPLAB X code.
|
23
|
+
|
24
|
+
Next, make a directory called "spec" for the tests you are going to write. You can put that directory anywhere, but my preferred directory structure looks like this:
|
25
|
+
|
26
|
+
project_dir
|
27
|
+
|-- firmware
|
28
|
+
| |-- asm and c source files
|
29
|
+
| `-- dist
|
30
|
+
`-- spec
|
31
|
+
|-- *_spec.rb
|
32
|
+
`-- spec_helper.rb
|
33
|
+
|
34
|
+
Writing your first test
|
35
|
+
----
|
36
|
+
|
37
|
+
The convention for RSpec is that all the specs live in the "spec" directory and have a name ending with `_spec.rb`. In the spec directory, create a file named `firmware_spec.rb`. You can rename it later. In `firmware_spec.rb`, write:
|
38
|
+
|
39
|
+
|
40
|
+
!!!ruby
|
41
|
+
require_relative 'spec_helper'
|
42
|
+
|
43
|
+
describe "the firmware" do
|
44
|
+
before do
|
45
|
+
start_sim MySim
|
46
|
+
end
|
47
|
+
|
48
|
+
specify "program counter changes every step for the first 100 steps" do
|
49
|
+
100.times do
|
50
|
+
last_pc_value = pc.value
|
51
|
+
step
|
52
|
+
expect(pc.value).to_not eq last_pc_value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
This sets up a dummy test that runs the simulation for 100 steps and verifies that the program counter (the address of the next instruction to execute) changes each time. This test would fail if the firmware went into a one-instruction loop in the first 100 instructions.
|
58
|
+
|
59
|
+
To someone who is new to Ruby, RSpec, and RPicSim, understanding the code above might be pretty hard.
|
60
|
+
More information can be found by reading further in this manual; this page is just meant to help you get started, and a long explanation of this code would slow down people who already know what they are doing but just need a quick reminder of how to get started.
|
61
|
+
|
62
|
+
We have not yet told RPicSim where to find the firmware file. To do this, make a new file named `spec_helper.rb` in the `spec` directory. In `spec/spec_helper.rb`, write:
|
63
|
+
|
64
|
+
!!!ruby
|
65
|
+
require 'rpicsim/rspec'
|
66
|
+
|
67
|
+
class MySim < RPicSim::Sim
|
68
|
+
device_is "PIC10F322"
|
69
|
+
filename_is File.dirname(__FILE__) + "/../firmware/dist/default/production/firmware_dir.production.cof"
|
70
|
+
end
|
71
|
+
|
72
|
+
Edit the `device_is` and `filename_is` lines to match your actual device and the path to its COF file. The file specified here can either be COF or HEX, but COF is recommended because it allows convenient access to the variables, functions, and labels defined in the firmware.
|
73
|
+
|
74
|
+
Eventually you should rename the `MySim` class to something more specific. I like to name the simulation class by concatenating the project name with `Sim`.
|
75
|
+
|
76
|
+
To run the spec, go to your shell and run the command `rspec` from the directory that contains `spec`. In the example directory structure above, you would need to be inside the `project_dir` directory when you run `rspec`. If all goes well, the output from `rspec` should look like:
|
77
|
+
|
78
|
+
.
|
79
|
+
|
80
|
+
Finished in 0.006 seconds
|
81
|
+
1 example, 0 failures
|
82
|
+
|
83
|
+
RSpec is telling us that it found our one example that tests the program counter for the first 100 steps, and that it passed. You now have have an automated simulator-based test for your firmware and you are ready to add more.
|
84
|
+
|
85
|
+
More information about how to use RPicSim can be found in the other sections of {file:Manual.md this manual}.
|
@@ -0,0 +1,119 @@
|
|
1
|
+
RSpec integration
|
2
|
+
====
|
3
|
+
|
4
|
+
RPicSim has optional code that allows you to integrated it nicely with RSpec.
|
5
|
+
RPicSim's RSpec integration is mainly designed to help you write shorter, less verbose tests, but it also provides helpful error messages when a test fails.
|
6
|
+
Most of the example code in this manual assumes that you have the RSpec integration enabled.
|
7
|
+
|
8
|
+
Turning on RSpec integration
|
9
|
+
----
|
10
|
+
|
11
|
+
To enable the RSpec integration, simply put this line in your `spec_helper.rb`:
|
12
|
+
|
13
|
+
!!!ruby
|
14
|
+
require 'rpicsim/rspec'
|
15
|
+
|
16
|
+
The features that this gives you are documented below.
|
17
|
+
|
18
|
+
|
19
|
+
Helper methods
|
20
|
+
----
|
21
|
+
|
22
|
+
Requiring "rpicsim/rspec" causes the {RPicSim::RSpec::Helpers} module to get included (i.e. mixed in) to all of your RSpec examples.
|
23
|
+
|
24
|
+
This module provides the {RPicSim::RSpec::Helpers#start_sim start_sim} method and the methods described on the {file:PersistentExpectations.md persistent expectations page}.
|
25
|
+
You can call `start_sim` in an example or a before hook to start a new simulation.
|
26
|
+
The simulation object can then be accessed with by typing `sim` in your examples.
|
27
|
+
|
28
|
+
### Basic shortcuts
|
29
|
+
|
30
|
+
Unless you disable them, calling `start_sim` will also give you access to over a dozen basic shortcut methods like `pin` and `run_to` in your RSpec examples.
|
31
|
+
The full list of basic shortcuts can be found in {RPicSim::Sim::BasicShortcuts::ForwardedMethods}.
|
32
|
+
You can call these by simply typing a method name in an RSpec example:
|
33
|
+
|
34
|
+
!!!ruby
|
35
|
+
run_to :foo # equivalent to sim.run_to :foo
|
36
|
+
|
37
|
+
|
38
|
+
### Firmware-specific shortcuts
|
39
|
+
|
40
|
+
Unless you disable them, you will get access to firmware-specific shortcuts defined by the simulation.
|
41
|
+
These shortcuts correspond to items defined with {RPicSim::Sim::ClassDefinitionMethods#def_var def_var}, {RPicSim::Sim::ClassDefinitionMethods#def_flash_var def_flash_var} and {RPicSim::Sim::ClassDefinitionMethods#def_pin def_pin}.
|
42
|
+
|
43
|
+
For example, if your {file:DefiningSimulationClass.md simulation class} defines a pin named `main_output`, then you can just write code like this in your RSpec examples:
|
44
|
+
|
45
|
+
!!!ruby
|
46
|
+
expect(main_output).to be_driving_high
|
47
|
+
|
48
|
+
You can disable the firmware-specific shortcuts in your RSpec examples, but they will still be available on the simulation object itself (e.g. `sim.main_output`).
|
49
|
+
|
50
|
+
### Configuring shortcuts
|
51
|
+
|
52
|
+
RPicSim provides a custom RSpec configuration option called `sim_shortcuts` that can either be set to `:all` (default), `:basic`, or `:none`.
|
53
|
+
|
54
|
+
If you just want to use the basic shortcuts and not the firmware-specific shortcuts, add the following code to your `spec_helper.rb`:
|
55
|
+
|
56
|
+
!!!ruby
|
57
|
+
RSpec.configure do |config|
|
58
|
+
config.sim_shortcuts = :basic
|
59
|
+
end
|
60
|
+
|
61
|
+
To turn off all the shortcuts, use:
|
62
|
+
|
63
|
+
!!!ruby
|
64
|
+
RSpec.configure do |config|
|
65
|
+
config.sim_shortcuts = :none
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
Diagnostic information
|
70
|
+
----
|
71
|
+
|
72
|
+
If an error happens in a test (either from an expectation failing or from a general exception being raised), RPicSim augments the default output of RSpec in order to provide additional information about the state of the simulation.
|
73
|
+
When an RSpec example fails, the output you get will look something like this:
|
74
|
+
|
75
|
+
!!!plain
|
76
|
+
................................................F.....
|
77
|
+
|
78
|
+
Failures:
|
79
|
+
|
80
|
+
1) FooWidget when exposed to 1.5 ms pulses behaves correctly
|
81
|
+
Failure/Error: run_cycles 1500*4
|
82
|
+
expected INTCON to satisfy block
|
83
|
+
# ./lib/rpicsim/rspec/persistent_expectations.rb:29:in `check_expectations'
|
84
|
+
# ./lib/rpicsim/rspec/persistent_expectations.rb:27:in `check_expectations'
|
85
|
+
# ./lib/rpicsim/rspec/helpers.rb:25:in `start_sim'
|
86
|
+
# ./lib/rpicsim/sim.rb:574:in `step'
|
87
|
+
# ./lib/rpicsim/sim.rb:716:in `run_to_cycle_count'
|
88
|
+
# ./lib/rpicsim/sim.rb:708:in `run_cycles'
|
89
|
+
# ./spec/foo_widget_spec.rb:10:in `(root)'
|
90
|
+
|
91
|
+
Simulation cycle count: 78963
|
92
|
+
|
93
|
+
Simulation stack trace:
|
94
|
+
0x01A0 = startMotor
|
95
|
+
0x0044 = motorService+0x14
|
96
|
+
0x0B12 = mainLoop+0x2
|
97
|
+
0x008C = start2
|
98
|
+
|
99
|
+
Finished in 4.55 seconds
|
100
|
+
44 examples, 1 failure
|
101
|
+
|
102
|
+
Failed examples:
|
103
|
+
|
104
|
+
rspec ./spec/example/nice_error_spec.rb:8 # FooWidget when exposed to 1.5ms pulses behaves correctly
|
105
|
+
|
106
|
+
In this example, we had a {file:PersistentExpectations.md persistent expectation} asserting something about the INTCON SFR and and at some point in a lengthy integration test our expectation failed.
|
107
|
+
The "Simulation cycle count" shows us the value of {RPicSim::Sim#cycle_count} at the time that the error happened.
|
108
|
+
The "Simulation stack trace" shows us what addresses were on the device's call stack.
|
109
|
+
(Actually, the call stack stores the addresses the process will return to, but this stack trace shows the addresses where calls occurred, which is one or two less than the return address.)
|
110
|
+
This information can help when you are {file:Debugging.md debugging} issues.
|
111
|
+
|
112
|
+
|
113
|
+
Better RSpec error messages
|
114
|
+
----
|
115
|
+
|
116
|
+
RPicSim also overrides some of RSpec's error messages to be better.
|
117
|
+
|
118
|
+
For example, instead of just saying an error message like "expected driving_high? to return true, got false", RSpec will actually say what object it called `driving_high?` on.
|
119
|
+
This feature is important when you are using {file:PersistentExpectations.md persistent expectations} and want to know which expectation failed, because the stack trace will not help.
|
data/docs/RamWatcher.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
RAM watcher
|
2
|
+
====
|
3
|
+
|
4
|
+
When writing a {file:UnitTesting.md unit test} for some part of your firmware, you should probably test any RAM variable that the code is supposed to write to, and make sure that it contains the correct value after the code executes.
|
5
|
+
However, you should also try to make sure that your code does not write to any other parts of memory; it should only write to the places that you were expecting it to write to.
|
6
|
+
|
7
|
+
RPicSim's _ram watcher_ lets you see all the places in RAM (including SFRs) that were written by your code.
|
8
|
+
It detects writes to RAM even if the underlying RAM value didn't change.
|
9
|
+
For example, if your program runs a `clrf x` instruction, the RAM watcher will detect this even if `x` was already equal to 0.
|
10
|
+
|
11
|
+
Also, it detects writes from instructions like "`movf x, F`", which is usually not desired.
|
12
|
+
That instruction affects the STATUS register and allows you to see if `x` is zero, but it should not affect `x` if it is a normal variable in RAM.
|
13
|
+
However, that instruction technically counts as a read from `x` and a write of the same value back to `x`, so the RAM watcher detects the write and will report it.
|
14
|
+
|
15
|
+
Please note that the RAM watcher works well in MPLAB X 1.85 and 1.90 but the latest versions of MPLAB X have an issue that makes the RAM watcher useless.
|
16
|
+
For more information, see {file:KnownIssues.md}.
|
17
|
+
|
18
|
+
The RAM watcher has two important methods:
|
19
|
+
|
20
|
+
* The {RPicSim::MemoryWatcher#writes writes} method provides a hash representing all the writes that have been recorded.
|
21
|
+
Each key of the hash is the name of the variable or SFR that was written to, or just the address that was written to if the write was to an unrecognized location in memory.
|
22
|
+
The values of the hash are the final value that the item had after the last write.
|
23
|
+
If a subroutine under test writes to the same variable twice, the RAM watcher will only report about the last write.
|
24
|
+
* The {RPicSim::MemoryWatcher#clear clear} method erases all previous records.
|
25
|
+
|
26
|
+
For example, to test the 16-bit addition routine from the {file:Variables.md Variables page} with the RAM watcher, you could write:
|
27
|
+
|
28
|
+
!!!ruby
|
29
|
+
it "adds x to y and stores the result in z" do
|
30
|
+
x.value = 70
|
31
|
+
y.value = 22
|
32
|
+
step; sim.ram_watcher.clear
|
33
|
+
run_subroutine :addition, cycle_limit: 100
|
34
|
+
expect(sim.ram_watcher.writes).to eq({z: 92})
|
35
|
+
end
|
36
|
+
|
37
|
+
The third line in the example above, which clears the RAM watcher's records after stepping once, is necessary because the RAM watcher actually detects the writes that occurred to RAM in the lines above, even though those writes came from Ruby code.
|
38
|
+
|
39
|
+
The RAM watcher is an instance of {RPicSim::MemoryWatcher}.
|
40
|
+
|
41
|
+
Filters
|
42
|
+
----
|
43
|
+
|
44
|
+
The {RPicSim::MemoryWatcher} class contains some special code to filter out reports about registers that are always changing, like `PCL` and `STATUS`.
|
45
|
+
It also contains some special code to filter out spurious reports of writes that happen on the very first step of the simulation.
|
46
|
+
Both of these filters might need to be updated to properly support your particular PIC (and your version of MPLAB X).
|
data/docs/Running.md
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
Running the simulation
|
2
|
+
====
|
3
|
+
|
4
|
+
Any useful microcontroller simulation needs to run for some number of steps and then stop. RPicSim provides a variety of ways to specify what code to run and when to stop running. This page is a guide to the following methods of {RPicSim::Sim}:
|
5
|
+
|
6
|
+
* {RPicSim::Sim#step step}
|
7
|
+
* {RPicSim::Sim#run_steps run_steps}
|
8
|
+
* {RPicSim::Sim#run_cycles run_cycles}
|
9
|
+
* {RPicSim::Sim#run_to_cycle_count run_to_cycle_count}
|
10
|
+
* {RPicSim::Sim#run_to run_to}
|
11
|
+
* {RPicSim::Sim#run_subroutine run_subroutine}
|
12
|
+
* {RPicSim::Sim#goto goto}
|
13
|
+
|
14
|
+
Each of these methods has a delegator provided by RPicSim's {file:RSpecIntegration.md RSpec integration}, so in your RSpec examples you can just call them by writing something like `step` and that will have the same effect as `sim.step`.
|
15
|
+
This applies to all the methods listed above, not just `step`.
|
16
|
+
|
17
|
+
Single-stepping
|
18
|
+
----
|
19
|
+
|
20
|
+
The {RPicSim::Sim#step} method is the most basic way to run the simulation.
|
21
|
+
It executes a single instruction.
|
22
|
+
The program counter will be updated to point to the next instruction, and the {RPicSim::Sim#cycle_count cycle count} will be increased by the number of cycles that the instruction took.
|
23
|
+
This method might do some interesting things at times when the CPU is stalled (i.e. during a flash write) or during sleep and that behavior has not been characterized.
|
24
|
+
|
25
|
+
The `step` method is the most basic way to run a simulation, and all the `run_*` methods described here call `step` in order to actually run the simulation.
|
26
|
+
|
27
|
+
If you want to run a bit of code after each step, see {RPicSim::Sim#every_step} and {file:PersistentExpectations.md Persistent expectations}.
|
28
|
+
|
29
|
+
Running for a set time
|
30
|
+
----
|
31
|
+
|
32
|
+
RPicSim provides several different ways to run the simulation for a set amount of "time".
|
33
|
+
|
34
|
+
The {RPicSim::Sim#run_steps run_steps} method just runs the `step` method the specified number of times:
|
35
|
+
|
36
|
+
!!!ruby
|
37
|
+
run_steps 10 # runs the simulation for 10 steps
|
38
|
+
|
39
|
+
The {RPicSim::Sim#run_cycles run_cycles} method runs the simulation until a certain number of instruction cycles have elapsed, using {RPicSim::Sim#cycle_count}:
|
40
|
+
|
41
|
+
!!!ruby
|
42
|
+
run_cycles 20 # runs the simulation for approximately 20 instruction cycles
|
43
|
+
|
44
|
+
The {RPicSim::Sim#run_to_cycle_count run_to_cycle_count} method is similar to `run_cycles`, but it takes as an argument the total number of cycles since the simulation was started, and it runs up to that point:
|
45
|
+
|
46
|
+
!!!ruby
|
47
|
+
run_to_cycle_count 1000 # runs the simulation until the total cycle count is 1000
|
48
|
+
|
49
|
+
Regarding time accuracy: Certain instructions take two cycles and there is no way to stop the simulation in the middle of an instruction, so the simulation will sometimes run one cycle longer than requested when calling one of the methods described on this page.
|
50
|
+
Therefore, if you need to test something with high time-precision (like a software serial library) you might need to do something more complex using {RPicSim::Sim#cycle_count} and {RPicSim::Sim#step}.
|
51
|
+
|
52
|
+
|
53
|
+
Running until a condition is met
|
54
|
+
----
|
55
|
+
|
56
|
+
The most versatile method for running a simulation is {RPicSim::Sim#run_to run_to}.
|
57
|
+
|
58
|
+
For its first argument, the `run_to` method takes either a single condition or an array of conditions.
|
59
|
+
A condition can be many different things as shown in the examples below.
|
60
|
+
The `run_to` method will run the simulation until one of the conditions is met and then stop.
|
61
|
+
|
62
|
+
The second argument to `run_to` is an optional hash of options.
|
63
|
+
It is recommended to always specify the `cycle_limit` option, which limits how the long simulation
|
64
|
+
can run, in order to avoid an accidental infinite loop in your tests.
|
65
|
+
If the limit is exceeded, an exception is raised.
|
66
|
+
|
67
|
+
For example, to run to a label named "apple":
|
68
|
+
|
69
|
+
!!!ruby
|
70
|
+
run_to :apple, cycle_limit: 100
|
71
|
+
|
72
|
+
To run until either the "step2" label is reached or the current subroutine returns:
|
73
|
+
|
74
|
+
!!!ruby
|
75
|
+
run_to [ :step2, :return ], cycle_limit: 200
|
76
|
+
|
77
|
+
When `run_to` finishes, it will return the object representing the condition that was met.
|
78
|
+
This can be helpful in tests:
|
79
|
+
|
80
|
+
!!!ruby
|
81
|
+
result = run_to [ :step2, :return ], cycle_limit: 200
|
82
|
+
expect(result).to eq :step2
|
83
|
+
|
84
|
+
To run until an arbitrary condition is met:
|
85
|
+
|
86
|
+
!!!ruby
|
87
|
+
run_to Proc.new { wreg.value == 2 }, cycle_limit: 300
|
88
|
+
|
89
|
+
To run to a particular address:
|
90
|
+
|
91
|
+
!!!ruby
|
92
|
+
run_to 0x2000, cycle_limit: 300
|
93
|
+
|
94
|
+
To finish running a subroutine and assert that it takes between 10000 and 11000 cycles to finish:
|
95
|
+
|
96
|
+
!!!ruby
|
97
|
+
run_to :return, cycles: 10000..11000
|
98
|
+
|
99
|
+
For the complete, formal documentation of `run_to`, see {RPicSim::Sim#run_to}.
|
100
|
+
|
101
|
+
|
102
|
+
Running a small piece of code
|
103
|
+
----
|
104
|
+
|
105
|
+
It is possible to use {RPicSim::Sim#goto} and {RPicSim::Sim#run_to} together to just run a small piece of the firmware without regard for the rest.
|
106
|
+
This can be useful for unit tests.
|
107
|
+
For example, this code moves the devices's program counter to point to the address of the "loopStart" label and then runs the simulation until it reaches loopEnd:
|
108
|
+
|
109
|
+
goto :loopStart
|
110
|
+
run_to :loopEnd, cycle_limit: 400
|
111
|
+
|
112
|
+
|
113
|
+
Running a subroutine or function
|
114
|
+
----
|
115
|
+
|
116
|
+
One of the most useful methods for unit tests is {RPicSim::Sim#run_subroutine}. This method is the easiest way to test a subroutine or function in your program in isolation from other things. It runs the given subroutine until it returns (e.g. with a RETURN or RETLW instruction).
|
117
|
+
|
118
|
+
The first argument should be a label name (or any valid argument to {RPicSim::Sim#location_address}), and the second argument is an optional hash of options that supports the same options as {RPicSim::Sim#run_to run_to}.
|
119
|
+
|
120
|
+
For example, to test a subroutine that drives the `main_output` pin high:
|
121
|
+
|
122
|
+
run_subroutine :drivePinHigh, cycle_limit: 20
|
123
|
+
main_output.should be_driving_high
|
124
|
+
|
125
|
+
In this example, `main_output` is a pin alias, as described in the {file:Pins.md Pins page}.
|
126
|
+
|
127
|
+
Some subroutine values might store input or output values in RAM. To test those subroutines, you will need to be able to read and write RAM as described in the {file:Variables.md Variables page}.
|
128
|
+
|
129
|
+
Some subroutine values might store input or output values in SFRs. To test those subroutines, you will need to be able to read and write SFRs as described in the {file:SFRs.md SFRs page}.
|
data/docs/SFRs.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
SFRs
|
2
|
+
====
|
3
|
+
|
4
|
+
The Special Function Registers (SFRs) on a microcontroller enable the firmware to interact with the microcontroller's peripherals and talk to the outside world.
|
5
|
+
RPicSim supports reading and writing these SFRs from Ruby.
|
6
|
+
This can be useful if you want to put your PIC into a particular state and see how a part of your firmware responds.
|
7
|
+
Each SFR is represented as an instance of the {RPicSim::Register} class.
|
8
|
+
|
9
|
+
|
10
|
+
Getting a Register object
|
11
|
+
----
|
12
|
+
|
13
|
+
The {RPicSim::Sim#sfr} method can be called on your simulation object to retrieve a {RPicSim::Register} object:
|
14
|
+
|
15
|
+
!!!ruby
|
16
|
+
sim.sfr(:LATA) # => returns a Register object
|
17
|
+
|
18
|
+
If you are using RPicSim's {file:RSpecIntegration.md RSpec integration}, the `sfr` method inside an example automatically redirects to the `@sim` object:
|
19
|
+
|
20
|
+
!!!ruby
|
21
|
+
it "works" do
|
22
|
+
sfr(:LATA) # => returns a Register object
|
23
|
+
end
|
24
|
+
|
25
|
+
The first argument of {RPicSim::Sim#sfr} should be a symbol containing the name of the SFR.
|
26
|
+
The name comes from the MPLAB X code, but it should match the name given in the microcontroller's datasheet.
|
27
|
+
|
28
|
+
|
29
|
+
Using a register
|
30
|
+
----
|
31
|
+
|
32
|
+
Once you have obtained the {RPicSim::Register Register} object using one of the methods above, you can read and write the value of the SFR using the `value` attribute:
|
33
|
+
|
34
|
+
!!!ruby
|
35
|
+
sfr(:LATA).value = 0x7B
|
36
|
+
expect(sfr(:LATA).value).to eq 0x7B
|
37
|
+
|
38
|
+
|
39
|
+
Protected bits
|
40
|
+
----
|
41
|
+
|
42
|
+
When you write to the register with {RPicSim::Register#value=}, you are probably writing to it in the same way that the simulated microcontroller would write to it.
|
43
|
+
This means that some bits might not be writable or might have restrictions on what value can be written to them.
|
44
|
+
For example, the TO and PD bits of the STATUS register on the PIC10F322 are not writable by the microcontroller.
|
45
|
+
|
46
|
+
To get around this, you can use {RPicSim::Register#memory_value=} instead, which should allow you to write to any of the bits.
|
47
|
+
|
48
|
+
|
49
|
+
Peripheral updating
|
50
|
+
----
|
51
|
+
|
52
|
+
The MPLAB X code contains various objects that simulate the peripherals on a chip, such as the ADC.
|
53
|
+
It has not been determined whether writing to SFRs using the {RPicSim::Register} object updates the simulation of those peripherals in the proper way.
|
54
|
+
Also, whether the peripherals get updated might depend on whether the `value` or the `memory_value` attribute is used for writing.
|
55
|
+
|
56
|
+
|
57
|
+
Non-memory-mapped registers
|
58
|
+
----
|
59
|
+
|
60
|
+
The MPLAB X code considers "SFRs" to only be the special registers that have an address in memory.
|
61
|
+
The special registers without a memory address are called Non-Memory-Mapped Registers (NMMRs).
|
62
|
+
To access these registers, you can use {RPicSim::Sim#nmmr} which is similar to {RPicSim::Sim#sfr}.
|
63
|
+
|
64
|
+
On some chips, WREG and STKPTR are SFRs and on other chips they are NMMRs. To make it easier to access these two registers, RPicSim provides the methods {RPicSim::Sim#wreg} and {RPicSim::Sim#stkptr}. Those methods can be called directly in RSpec examples thanks to RPicSim's {file:RSpecIntegration.md RSpec integration}:
|
65
|
+
|
66
|
+
!!!ruby
|
67
|
+
it "sets W to 5" do
|
68
|
+
expect(wreg.value).to eq 5
|
69
|
+
end
|
70
|
+
|
71
|
+
To access other registers without worrying about what type they are, you can use {RPicSim::Sim#sfr_or_nmmr}.
|