rpicsim 0.1.0 → 0.2.1

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 +4 -4
  2. data/.yardopts +1 -1
  3. data/Gemfile +7 -6
  4. data/Introduction.md +3 -1
  5. data/README.md +2 -2
  6. data/docs/ChangeLog.md +6 -0
  7. data/docs/Contributing.md +1 -1
  8. data/docs/DefiningSimulationClass.md +11 -10
  9. data/docs/HowMPLABXIsFound.md +1 -1
  10. data/docs/IntegrationTesting.md +1 -1
  11. data/docs/IntroductionToRSpec.md +8 -5
  12. data/docs/IntroductionToRuby.md +2 -2
  13. data/docs/KnownIssues.md +46 -57
  14. data/docs/Labels.md +5 -4
  15. data/docs/Manual.md +1 -1
  16. data/docs/Memories.md +70 -0
  17. data/docs/PersistentExpectations.md +31 -2
  18. data/docs/Pins.md +5 -7
  19. data/docs/PreventingCallStackOverflow.md +4 -6
  20. data/docs/QuickStartGuide.md +5 -5
  21. data/docs/RSpecIntegration.md +2 -2
  22. data/docs/RamWatcher.md +22 -11
  23. data/docs/Running.md +4 -6
  24. data/docs/Stubbing.md +4 -4
  25. data/docs/SupportedDevices.md +2 -11
  26. data/docs/SupportedMPLABXVersions.md +1 -0
  27. data/docs/SupportedOperatingSystems.md +3 -2
  28. data/docs/UnitTesting.md +1 -1
  29. data/docs/Variables.md +81 -25
  30. data/lib/rpicsim.rb +0 -12
  31. data/lib/rpicsim/call_stack_info.rb +43 -47
  32. data/lib/rpicsim/composite_memory.rb +53 -0
  33. data/lib/rpicsim/flaws.rb +34 -22
  34. data/lib/rpicsim/instruction.rb +204 -48
  35. data/lib/rpicsim/label.rb +4 -4
  36. data/lib/rpicsim/memory.rb +44 -23
  37. data/lib/rpicsim/memory_watcher.rb +14 -22
  38. data/lib/rpicsim/mplab.rb +38 -119
  39. data/lib/rpicsim/mplab/mplab_assembly.rb +23 -18
  40. data/lib/rpicsim/mplab/mplab_device_info.rb +9 -9
  41. data/lib/rpicsim/mplab/mplab_disassembler.rb +5 -6
  42. data/lib/rpicsim/mplab/mplab_instruction.rb +87 -16
  43. data/lib/rpicsim/mplab/mplab_loader.rb +106 -0
  44. data/lib/rpicsim/mplab/mplab_memory.rb +19 -6
  45. data/lib/rpicsim/mplab/mplab_nmmr_info.rb +4 -4
  46. data/lib/rpicsim/mplab/mplab_observer.rb +15 -10
  47. data/lib/rpicsim/mplab/mplab_pin.rb +3 -3
  48. data/lib/rpicsim/mplab/mplab_processor.rb +5 -5
  49. data/lib/rpicsim/mplab/mplab_program_file.rb +29 -17
  50. data/lib/rpicsim/mplab/mplab_register.rb +5 -5
  51. data/lib/rpicsim/mplab/mplab_sfr_info.rb +4 -4
  52. data/lib/rpicsim/mplab/mplab_simulator.rb +27 -30
  53. data/lib/rpicsim/pin.rb +6 -6
  54. data/lib/rpicsim/program_counter.rb +3 -3
  55. data/lib/rpicsim/program_file.rb +39 -81
  56. data/lib/rpicsim/rspec/be_predicate.rb +1 -1
  57. data/lib/rpicsim/rspec/helpers.rb +1 -1
  58. data/lib/rpicsim/rspec/persistent_expectations.rb +17 -2
  59. data/lib/rpicsim/rspec/sim_diagnostics.rb +5 -5
  60. data/lib/rpicsim/search.rb +1 -1
  61. data/lib/rpicsim/sim.rb +153 -228
  62. data/lib/rpicsim/stack_pointer.rb +41 -0
  63. data/lib/rpicsim/stack_trace.rb +1 -1
  64. data/lib/rpicsim/storage/memory_integer.rb +235 -0
  65. data/lib/rpicsim/{register.rb → storage/register.rb} +18 -18
  66. data/lib/rpicsim/variable.rb +25 -211
  67. data/lib/rpicsim/variable_set.rb +93 -0
  68. data/lib/rpicsim/version.rb +2 -2
  69. metadata +9 -4
  70. data/docs/SFRs.md +0 -71
@@ -32,7 +32,17 @@ To remove a persistent expectation, specify a matcher of `nil`:
32
32
  !!!ruby
33
33
  expecting main_output => nil
34
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.
35
+ If `expecting` is given a block, expectations will only be valid for the duration of the block:
36
+
37
+ !!!ruby
38
+ # Verify that the main output stays high for 10 cycles.
39
+ expecting main_output => be_driving_high do
40
+ # The expectation will be checked within the block.
41
+ run_cycles 10
42
+ end
43
+ # The expectation will not be checked here.
44
+
45
+ The persistent expectations will not be checked immediately when they are added, but they will be checked after every step of the simulation.
36
46
  You can also check them at any time by calling `check_expecations` inside your RSPec example.
37
47
 
38
48
  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:
@@ -67,8 +77,27 @@ The following RSpec example tests that the main output pin is held low (after gi
67
77
 
68
78
  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
79
  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.
80
+ We can rewrite that using block arguments instead of explicitly clearing the expectation:
81
+
82
+ !!!ruby
83
+ it "mirrors the main input onto the main output pin" do
84
+ run_cycles 120 # Give the device time to start up.
85
+
86
+ expecting main_output => be_driving_low do
87
+ run_cycles 800
88
+ end
89
+
90
+ main_input.set true
91
+
92
+ # Give the device time to detect the change in the input.
93
+ run_cycles 200
94
+
95
+ expecting main_output => be_driving_high do
96
+ run_cycles 800
97
+ end
98
+ end
70
99
 
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:
100
+ If you need to repeat this patten many times in your tests, you might consider adding a method in your `spec_helper.rb` to help you do it:
72
101
 
73
102
  !!!ruby
74
103
  def transition(opts={})
@@ -23,8 +23,6 @@ The first argument of {RPicSim::Sim#pin} should be the name of the pin as a symb
23
23
  The allowed names come from the MPLAB X code, but they should match the names given in the PIC datasheet.
24
24
  For example, the PIC10F322 pin RA1 can be referred to by many names, including `:RA1`, `:PWM2`, `:AN1`, and `:NCO1CLK`.
25
25
 
26
- RPicSim does not model the GND and VDD pins.
27
-
28
26
  Pin aliases
29
27
  ----
30
28
 
@@ -46,19 +44,19 @@ This makes `:main_output` be an alias for `:RA1`. You can now access the Pin ob
46
44
  !!!ruby
47
45
  pin(:main_output)
48
46
 
49
- Defining a pin alias also adds a "shortcut" method by the same name. This means that you can access the pin like this:
47
+ Defining a pin alias also adds a new method by the same name. This means that you can access the pin like this:
50
48
 
51
49
  !!!ruby
52
50
  sim.main_output
53
51
 
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:
52
+ Shortcuts for these methods are also available in RSpec thanks to RPicsim's {file:RSpecIntegration.md RSpec integration}, so you can simply write `main_output` instead of `sim.main_output` in any of your RSpec examples:
55
53
 
56
54
  !!!ruby
57
55
  it "drives the main output high" do
58
56
  expect(main_output).to be_driving_high
59
57
  end
60
58
 
61
- Note that since the shortcuts are available in many places, your pin names might conflict with names defined in other places.
59
+ Note that since these methods are available in many places, your pin names might conflict with names defined in other places.
62
60
 
63
61
 
64
62
  Pin methods
@@ -102,8 +100,8 @@ In `spec/spec_helper.rb`, we make a simulation class that points to the compiled
102
100
  require 'rpicsim/rspec'
103
101
 
104
102
  class PinMirror < RPicSim::Pic
105
- device_is "PIC10F322"
106
- filename_is File.dirname(__FILE__) + "../firmware/dist/firmware.cof"
103
+ use_device "PIC10F322"
104
+ use_file File.dirname(__FILE__) + "../firmware/dist/firmware.cof"
107
105
 
108
106
  def_pin :main_input, :RA0
109
107
  def_pin :main_output, :RA1
@@ -4,7 +4,6 @@ Preventing call stack overflow
4
4
  PIC microcontrollers feature a stack implemented in hardware that keeps track of return addresses for subroutine calls.
5
5
  Every time a `CALL` instruction is executed, the return address is pushed onto the stack.
6
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
7
 
9
8
  The call stack has a limited depth that depends on the device you are using.
10
9
  If your program has too many levels of nested subroutines then the call stack could overflow.
@@ -25,7 +24,7 @@ Here is an example:
25
24
  call_stack_info[0].max_depth be <= 5
26
25
  end
27
26
 
28
- specify "ISR code uses no more than 2 levels" do
27
+ specify "ISR code uses no more than 1 level" do
29
28
  call_stack_info[4].max_depth.should be <= 1
30
29
  end
31
30
  end
@@ -73,8 +72,6 @@ The {RPicSim::CallStackInfo} class traverses all possible paths through that gra
73
72
  Limitations
74
73
  ----
75
74
 
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
75
  The algorithm is pessimistic:
79
76
 
80
77
  * 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.
@@ -88,7 +85,8 @@ However, there are some things that can mess up the algorithm in a bad way and g
88
85
  * 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
86
  Be careful about this, because a computed jump might be generated automatically by a C compiler.
90
87
  * 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.
88
+ * It does not account for the effect of PUSH and POP instructions on the call stack depth.
91
89
 
92
- This code is not suitable (yet) for any firmware that uses a computed jump or paged program memory.
90
+ This code is not suitable (yet) for any firmware that uses a computed jump, paged program memory, or PUSH or POP instructions.
93
91
 
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.
92
+ 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.
@@ -8,7 +8,7 @@ By the end of this guide, you will have a suite of automated simulator-based tes
8
8
  Installing prerequisites
9
9
  ----
10
10
 
11
- First, on a computer running Windows, install RPicSim and the software it requires:
11
+ First, on a computer running Windows, Linux, or Mac OS X, install RPicSim and the software it requires:
12
12
 
13
13
  1. Install [MPLAB X](http://www.microchip.com/pagehandler/en-us/family/mplabx/). RPicSim uses the Microchip Java classes from MPLAB X.
14
14
  2. Install the latest version of [JRuby](http://jruby.org/).
@@ -65,13 +65,13 @@ We have not yet told RPicSim where to find the firmware file. To do this, make
65
65
  require 'rpicsim/rspec'
66
66
 
67
67
  class MySim < RPicSim::Sim
68
- device_is "PIC10F322"
69
- filename_is File.dirname(__FILE__) + "/../firmware/dist/default/production/firmware_dir.production.cof"
68
+ use_device "PIC10F322"
69
+ use_file File.dirname(__FILE__) + "/../firmware/dist/default/production/firmware_dir.production.cof"
70
70
  end
71
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.
72
+ Edit the `use_device` and `use_file` 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
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`.
74
+ Eventually you should rename the `MySim` class to something more specific, such as the concatenation of the project name with "Sim".
75
75
 
76
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
77
 
@@ -23,7 +23,7 @@ Requiring "rpicsim/rspec" causes the {RPicSim::RSpec::Helpers} module to get inc
23
23
 
24
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
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.
26
+ The simulation object can then be accessed through a method named `sim` in your examples.
27
27
 
28
28
  ### Basic shortcuts
29
29
 
@@ -38,7 +38,7 @@ You can call these by simply typing a method name in an RSpec example:
38
38
  ### Firmware-specific shortcuts
39
39
 
40
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}.
41
+ These shortcuts correspond to items defined with {RPicSim::Sim::ClassDefinitionMethods#def_var def_var} and {RPicSim::Sim::ClassDefinitionMethods#def_pin def_pin}.
42
42
 
43
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
44
 
@@ -2,9 +2,9 @@ RAM watcher
2
2
  ====
3
3
 
4
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.
5
+ However, it is also helpful to 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
6
 
7
- RPicSim's _ram watcher_ lets you see all the places in RAM (including SFRs) that were written by your code.
7
+ RPicSim's _RAM watcher_ lets you see all the places in RAM (including SFRs) that were written by your code.
8
8
  It detects writes to RAM even if the underlying RAM value didn't change.
9
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
10
 
@@ -15,12 +15,17 @@ However, that instruction technically counts as a read from `x` and a write of t
15
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
16
  For more information, see {file:KnownIssues.md}.
17
17
 
18
- The RAM watcher has two important methods:
18
+ To create a new RAM watcher object, call {RPicSim::Sim#new_ram_watcher}. There is a shortcut for this method, so if you are using RPicSim's {file:RSpecIntegration.md RSpec integration} then you can just write:
19
+
20
+ !!!ruby
21
+ ram_watcher = new_ram_watcher
22
+
23
+ The resulting object is an instance of the {RPicSim::MemoryWatcher} class and has two important methods:
19
24
 
20
25
  * The {RPicSim::MemoryWatcher#writes writes} method provides a hash representing all the writes that have been recorded.
21
26
  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.
27
+ The values of the hash are the final value that the variable had after the last write.
28
+ If a variable is written to more than once, the RAM watcher will only report about the last write.
24
29
  * The {RPicSim::MemoryWatcher#clear clear} method erases all previous records.
25
30
 
26
31
  For example, to test the 16-bit addition routine from the {file:Variables.md Variables page} with the RAM watcher, you could write:
@@ -29,18 +34,24 @@ For example, to test the 16-bit addition routine from the {file:Variables.md Var
29
34
  it "adds x to y and stores the result in z" do
30
35
  x.value = 70
31
36
  y.value = 22
32
- step; sim.ram_watcher.clear
37
+ step
38
+ ram_watcher = new_ram_watcher
33
39
  run_subroutine :addition, cycle_limit: 100
34
- expect(sim.ram_watcher.writes).to eq({z: 92})
40
+ expect(ram_watcher.writes).to eq({z: 92})
35
41
  end
36
42
 
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.
43
+ The third line in the example above advances the simulation by one step.
44
+ That initial step is necessary for two reasons:
45
+
46
+ * Without it, the RAM watcher would report the writes to the `x` and `z` variables performed above, even though those writes came from Ruby code.
47
+ * Without it, the RAM watcher would report spurious writes to several registers such as INTCON and LATA.
48
+ On the first step of the simulation, the MPLAB X code reports writes to several registers that were not caused by the firmware.
49
+ We can avoid seeing them by taking a single step before creating the RAM watcher.
38
50
 
39
51
  The RAM watcher is an instance of {RPicSim::MemoryWatcher}.
40
52
 
41
53
  Filters
42
54
  ----
43
55
 
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).
56
+ The {RPicSim::MemoryWatcher} class contains some special code to filter out reports about registers that very frequently change, like `PCL` and `STATUS`.
57
+
@@ -19,8 +19,7 @@ Single-stepping
19
19
 
20
20
  The {RPicSim::Sim#step} method is the most basic way to run the simulation.
21
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.
22
+ The {RPicSim::Sim#pc 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.
24
23
 
25
24
  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
25
 
@@ -120,10 +119,9 @@ The first argument should be a label name (or any valid argument to {RPicSim::Si
120
119
  For example, to test a subroutine that drives the `main_output` pin high:
121
120
 
122
121
  run_subroutine :drivePinHigh, cycle_limit: 20
123
- main_output.should be_driving_high
122
+ expect(main_output).to be_driving_high
124
123
 
125
124
  In this example, `main_output` is a pin alias, as described in the {file:Pins.md Pins page}.
126
125
 
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}.
126
+ Some subroutine values might store input or output values in RAM, either in user-defined variables or in special function registers (SFRs).
127
+ To test those subroutines, you can read and write RAM as described in the {file:Variables.md Variables page}.
@@ -21,7 +21,7 @@ To stub a method in the most basic way, you can do something like this:
21
21
  The example above just alters our simulation so that whenever the `foo` subroutine is called, instead of running as normal it will return immediately using {RPicSim::Sim#return}.
22
22
 
23
23
  The example above can be expanded in many ways:
24
- You might read and write from {file:Variables.md variables} and {file:SFRs.md SFRs}.
24
+ You might read and write from {file:Variables.md variables}.
25
25
  You might record information about how the subroutine was called.
26
26
 
27
27
 
@@ -110,9 +110,9 @@ In `spec/spec_helper.rb`, we make a simulation class that points to the compiled
110
110
  require 'rpicsim/rspec'
111
111
 
112
112
  class LongDelay < RPicSim::Sim
113
- device_is "PIC10F322"
114
- filename_is File.dirname(__FILE__) + "../firmware/dist/firmware.cof"
115
- def_var :hot, :u8
113
+ use_device "PIC10F322"
114
+ use_file File.dirname(__FILE__) + "../firmware/dist/firmware.cof"
115
+ def_var :hot, :uint8
116
116
  end
117
117
 
118
118
  In `spec/cooldown_spec.rb`, we stub the `bigDelay` routine and test `cooldown` to make sure it calls `bigDelay` the right number of times:
@@ -1,14 +1,5 @@
1
1
  # Supported devices
2
2
 
3
- RPicSim aims to support all 8-bit PIC microcontrollers. No support is planned for 16-bit or 32-bit PIC microcontrollers because the author does not have an interest in using those devices. It might be easy to add support for them, but the code that handles {file:SFRs.md SFRs} would have to be adjusted to allow SFRs with more than 8 bits.
3
+ RPicSim aims to support all 8-bit PIC microcontrollers. No support is planned for 16-bit or 32-bit PIC microcontrollers because we do not have an interest in using those devices.
4
4
 
5
- RPicSim relies on MPLAB X code to perform the actual simulation, and MPLAB X abstracts away most of the differences between PICs.
6
-
7
- The 8-bit PICs have {http://www.microchip.com/pagehandler/en-us/family/8bit/architecture/home.html four different architectures}:
8
-
9
- - Baseline
10
- - Midrange
11
- - Enhanced Midrange
12
- - PIC18
13
-
14
- There is currently some code in {RPicSim::ProgramFile#instruction} that only works with baseline and midrange PICs, but it should be easy to expand it to other PICs. This code is only used to calculate call stack depth information for users who want that feature; it is not used for actual simulations.
5
+ RPicSim relies on MPLAB X code to perform the actual simulation, and MPLAB X abstracts away most of the differences between different PIC microcontrollers.
@@ -6,6 +6,7 @@ RPicSim uses code in MPLAB X to actually perform the PIC simulation. Therefore,
6
6
  - 1.90
7
7
  - 1.95
8
8
  - 2.00
9
+ - 2.05
9
10
 
10
11
  The different versions of MPLAB X have different problems that affect the simulation. For more information, see the {file:KnownIssues.md Known issues page}.
11
12
 
@@ -1,3 +1,4 @@
1
- # Supported Operating Systems
1
+ # Supported operating systems
2
2
 
3
- RPicSim currently only supports Windows. However, it should be easy to get it working on Linux or Mac OS X: probably the only code that would need to be changed is the code in `mplab_x.rb` that helps find the MPLAB X directory automatically and the code that detects what version of MPLAB X is being used.
3
+ RPicSim supports recent versions of Windows, Linux, and Mac OS X.
4
+ RPicSim should work on any computer that can run both MPLAB X and JRuby.
@@ -5,5 +5,5 @@ A _unit test_ is a test for a relatively small piece of a code, which tests that
5
5
  Unit testing is simply the practice of writing and running units tests to go along with your code.
6
6
 
7
7
  The {file:Running.md Running page} explains how to run small portions of your code using RPicSim.
8
- RPicSim allows you to access {file:Variables.md variables} and {file:SFRs SFRs}, so you can put the simulation into the desired state before running the code and then test that the simulation is in the right state after the code has executed.
8
+ RPicSim allows you to access {file:Variables.md variables} and {file:Registers Registers}, so you can put the simulation into the desired state before running the code and then test that the simulation is in the right state after the code has executed.
9
9
  RPicSim can also be used for {file:Stubbing.md stubbing subroutines} so that you can simply test the behavior of one subroutine instead of all the subroutines it calls.
@@ -1,23 +1,22 @@
1
1
  Variables
2
2
  ====
3
3
 
4
- RPicSim allows you to read and write from simulated variables stored in RAM or flash, which can be useful for {file:UnitTesting.md unit testing}. Variables are represented as Ruby objects that are instances of a subclass of {RPicSim::Variable}.
4
+ RPicSim uses the {RPicSim::Variable} class to let you access simulated program variables stored in RAM, program memory, or EEPROM, as well as Special Function Registers, which can be useful for {file:UnitTesting.md unit testing}.
5
5
 
6
- To access a variable, RPicSim needs to know the name it will be called in your Ruby code, what data type it is (e.g. 16-bit unsigned integer), and its address in memory.
7
- In most cases, RPicSim can deduce the address by looking at the symbol table in your COF file, so you will not need to
8
- type the address.
9
- However, RPicSim cannot deduce the data type of a variable, so any variables used need to be explicitly defined beforehand.
6
+ To access a variable, RPicSim needs to know the name it will be called in your Ruby code, what type of memory it is stored in, what data type it is (e.g. 16-bit unsigned integer), and its address in memory.
7
+ This information is deduced in different ways for the different types of variables described below.
10
8
 
11
- Defining a RAM variable
9
+ User-defined variables
12
10
  ----
13
-
14
- RAM variables that you want to access from Ruby must be defined in the {file:DefiningSimulationClass.md simulation class} using {RPicSim::Sim::ClassDefinitionMethods#def_var def_var}. For example:
11
+ For variables defined in your firmware, RPicSim can usually deduce the address by looking at the symbol table in your COF file, so you will not need to type the address.
12
+ However, RPicSim cannot deduce the data type of a variable, so any variables used need to be explicitly defined in the {file:DefiningSimulationClass.md simulation class} using {RPicSim::Sim::ClassDefinitionMethods#def_var def_var}.
13
+ For example:
15
14
 
16
15
  !!!ruby
17
16
  class MySim < RPicSim::Sim
18
17
  #...
19
18
 
20
- def_var :counter, :u8
19
+ def_var :counter, :uint8
21
20
 
22
21
  end
23
22
 
@@ -26,12 +25,13 @@ The first argument to `def_var` specifies what to call the variable in Ruby code
26
25
  !!!ruby
27
26
  sim.var(:counter)
28
27
 
29
- Each variable also has a "shortcut" method by the same name. This means that you can access the variable like this:
28
+ Each variable also has a method on the simulation object by the same name.
29
+ This means that you can access the variable like this:
30
30
 
31
31
  !!!ruby
32
32
  sim.counter
33
33
 
34
- The shortcuts are also available in RSpec thanks to RPicsim's {file:RSpecIntegration.md RSpec integration}, so you can simply write `counter` in any of your RSpec examples:
34
+ A shortcut is also available in RSpec thanks to RPicsim's {file:RSpecIntegration.md RSpec integration}, so you can simply write `counter` in any of your RSpec examples:
35
35
 
36
36
  !!!ruby
37
37
  it "drives the main output high" do
@@ -45,12 +45,12 @@ In the example above, RPicSim will look in your firmware's COF file for a RAM sy
45
45
  You can use the `symbol` option to specify what symbol in the symbol table marks the location of the variable. For example:
46
46
 
47
47
  !!!ruby
48
- def_var :counter, :u8, symbol: :_counter
48
+ def_var :counter, :uint8, symbol: :_counter
49
49
 
50
50
  The example above shows how you could access a variable from a C compiler (which will generally be prefixed with an underscore) without having to type the underscore in your tests.
51
51
  More generally, the `symbol` option allows you to call a variable one thing in your firmware and call it a different thing in your tests.
52
52
 
53
- RPicSim will raise an exception if it cannot find the specified symbol in the symbol table. To troubleshoot this, you might print the list of variables that RPicSim found:
53
+ RPicSim will raise an exception if it cannot find the specified symbol in the symbol table. To troubleshoot this, you might print the list of symbols that RPicSim found:
54
54
 
55
55
  !!!ruby
56
56
  p sim.class.program_file.var_addresses.keys
@@ -58,17 +58,55 @@ RPicSim will raise an exception if it cannot find the specified symbol in the sy
58
58
  You can use the `address` option to specify an arbitrary address instead of using the symbol table. For example:
59
59
 
60
60
  !!!ruby
61
- def_var :counter, :u8, address: 0x63
61
+ def_var :counter, :uint8, address: 0x63
62
+
63
+ Variables are assumed to be in RAM by default, but you can specify that they are in program memory or EEPROM using the `memory` option.
64
+
65
+ !!!ruby
66
+ def_var :settings, :word, memory: :program_memory
67
+ def_var :checksum, :uint16, memory: :eeprom
68
+
69
+ ### Program memory on non-PIC18 devices
70
+
71
+ On non-PIC18 devices, program memory is made up of words that are 12 bits or 14 bits wide.
72
+
73
+ The type of address used for program memory of these devices is called a _word address_ because it specifies the number of a word instead of the number of a byte. For example, a word address of `1` would correspond to the second word in program memory.
62
74
 
75
+ To access all the bits of a particular word, you can define your variable to be of the +:word+ type as shown in the example above.
76
+ If you specify any of the integer types like :uint8 or :int16, the bytes that comprise that variable will live in the least-significant 8 bits of one or more words in program memory.
77
+ The upper bits of the words will not be changed when writing to the variable.
63
78
 
64
- Defining Flash variables
79
+ This behavior is useful because if you store an integer in program memory as 1 to 4 consecutive RETLW instructions, you can read and write from it in Ruby without changing the bits that make those words be RETLW instructions.
80
+
81
+
82
+ Accessing special function registers
65
83
  ----
66
84
 
67
- Flash (program space) variables work the same way as RAM variables except:
85
+ The Special Function Registers (SFRs) on a microcontroller enable the firmware to interact with the microcontroller's peripherals and talk to the outside world.
86
+ The {RPicSim::Sim#reg} method can be called on your simulation object to retrieve a {RPicSim::Variable} object:
87
+
88
+ !!!ruby
89
+ sim.reg(:LATA) # => returns a Variable object
90
+
91
+ If you are using RPicSim's {file:RSpecIntegration.md RSpec integration}, the `reg` method inside an example automatically redirects to the `@sim` object:
92
+
93
+ !!!ruby
94
+ it "works" do
95
+ reg(:LATA) # => returns a Variable object
96
+ end
97
+
98
+ The first argument of {RPicSim::Sim#reg} should be a symbol containing the name of the SFR.
99
+ The name comes from the MPLAB X code, but we expect it to match the name given in the microcontroller's datasheet.
68
100
 
69
- * They are defined with {RPicSim::Sim::ClassDefinitionMethods#def_flash_var def_flash_var}.
70
- * The set of allowed data types for the second argument of `def_flash_var` is different, and you can see the documentation by clicking the link above.
71
- * Flash variables cannot be accessed with {RPicSim::Sim#var}, but can be accessed with {RPicSim::Sim#flash_var}
101
+ Note that the MPLAB X code considers "SFRs" to only be the special registers that have an address in memory.
102
+ The special registers without a memory address are called Non-Memory-Mapped Registers (NMMRs).
103
+ For example, on some chips, WREG and STKPTR are NMMRs.
104
+ You can access NMMRs in exactly the same way as SFRs:
105
+
106
+ !!!ruby
107
+ it "sets W to 5" do
108
+ expect(reg(:WREG).value).to eq 5
109
+ end
72
110
 
73
111
 
74
112
  Using a variable
@@ -81,6 +119,24 @@ Once you have defined a variable and accessed it using one of the methods above,
81
119
  expect(counter.value).to eq 0x6A
82
120
 
83
121
 
122
+ Protected bits
123
+ ----
124
+
125
+ When you write to a register with {RPicSim::Variable#value=}, you are (according to our understanding of MPLAB X) writing to it in the same way that the simulated microcontroller would write to it.
126
+ This means that some bits might not be writable or might have restrictions on what value can be written to them.
127
+ For example, the TO and PD bits of the STATUS register on the PIC10F322 are not writable by the microcontroller.
128
+
129
+ To get around this, you can use {RPicSim::Variable#memory_value=} instead, which should allow you to write to any of the bits.
130
+
131
+
132
+ Peripheral updating
133
+ ----
134
+
135
+ The MPLAB X code contains various objects that simulate the peripherals on a chip, such as the ADC.
136
+ We have not determined whether writing to SFRs using the {RPicSim::Variable} object updates the simulation of those peripherals in the proper way.
137
+ Also, whether the peripherals get updated might depend on whether the `value` or the `memory_value` attribute is used for writing.
138
+
139
+
84
140
  Addition example
85
141
  ----
86
142
 
@@ -114,11 +170,11 @@ In `spec/spec_helper.rb`, we make a simulation class that points to the compiled
114
170
  require 'rpicsim/rspec'
115
171
 
116
172
  class Addition < RPicSim::Sim
117
- device_is "PIC10F322"
118
- filename_is File.dirname(__FILE__) + "../firmware/dist/firmware.cof"
119
- def_var :x, :u16
120
- def_var :y, :u16
121
- def_var :z, :u16
173
+ use_device "PIC10F322"
174
+ use_file File.dirname(__FILE__) + "../firmware/dist/firmware.cof"
175
+ def_var :x, :uint16
176
+ def_var :y, :uint16
177
+ def_var :z, :uint16
122
178
  end
123
179
 
124
180
  In `spec/addition_spec.rb`, we write a simple unit test that writes to `x` and `y`, runs the `addition` subroutine, and checks that the correct result is stored in `z`:
@@ -131,7 +187,7 @@ In `spec/addition_spec.rb`, we write a simple unit test that writes to `x` and `
131
187
  start_sim Addition
132
188
  end
133
189
 
134
- it "can add 70 + 22"
190
+ it "can add 70 + 22" do
135
191
  x.value = 70
136
192
  y.value = 22
137
193
  run_subroutine :addition, cycle_limit: 100