rpicsim 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -4,7 +4,7 @@ module RPicSim
4
4
  # The name of the label from the firmware.
5
5
  # @return (Symbol)
6
6
  attr_reader :name
7
-
7
+
8
8
  # The address/value of the label from the firmware.
9
9
  # @return (Integer)
10
10
  attr_reader :address
@@ -19,10 +19,10 @@ module RPicSim
19
19
  def to_i
20
20
  address
21
21
  end
22
-
22
+
23
23
  # Returns a nice string representation of the label.
24
24
  def to_s
25
- "<Label %s address=0x%x>" % [name, address]
25
+ '<Label %s address=0x%x>' % [name, address]
26
26
  end
27
27
  end
28
- end
28
+ end
@@ -1,29 +1,50 @@
1
- class Memory
2
- # This object allows read and write access to the current data
1
+ module RPicSim
2
+ # This object allows read and write access to the data currently
3
3
  # stored in a memory space of the simulated device.
4
+ # Instances are usually retrieved from a {Sim} object by calling
5
+ # {Sim#ram}, {Sim#program_memory}, or {Sim#eeprom}.
4
6
  #
5
- # The behavior of this class differs depending on what kind of Memory
6
- # it represents, as shown in the table below:
7
+ # The behavior of +read_word+ and +write_word+ differs depending on
8
+ # what kind of Memory is being used, as shown in the table below:
7
9
  #
8
- # Address type Read/write chunk
9
- # Any type of RAM: Byte address 1 byte (8 bits)
10
- # Midrange Flash: Word address 1 word (14 bits)
11
- # PIC18 flash: Byte address 1 word (16 bits)
10
+ # Memory type Address type Read/write chunk
11
+ # RAM and EEPROM: Byte address 1 byte (8 bits)
12
+ # PIC18 program memory: Byte address 1 word (16 bits)
13
+ # Non-PIC18 program memory: Word address 1 word (12 or 14 bits)
12
14
  #
13
- # @param mplab_memory [Mplab::Memory]
14
- def initialize(mplab_memory)
15
- @mplab_memory = mplab_memory
16
- end
15
+ # The +read_byte+ and +write_byte+ methods use the same type of address as
16
+ # +read_word+ and +write_word+, but they can only read and write from the
17
+ # lower 8 bits of the word.
18
+ # The upper bits of the word, if there are any, are left unchanged.
19
+ # For RAM and EEPROM, +read_byte+ and +write_byte+ behave the same way as
20
+ # +read_word+ and +write_word+.
21
+ #
22
+ # For more information, see {file:Memories.md}.
23
+ class Memory
24
+ # @param mplab_memory [Mplab::Memory]
25
+ def initialize(mplab_memory)
26
+ @mplab_memory = mplab_memory
27
+ end
28
+
29
+ def read_byte(address)
30
+ @mplab_memory.read_byte(address)
31
+ end
32
+
33
+ def write_byte(address, value)
34
+ @mplab_memory.write_byte(address, value)
35
+ end
36
+
37
+ def read_word(address)
38
+ @mplab_memory.read_word(address)
39
+ end
40
+
41
+ def write_word(address, value)
42
+ @mplab_memory.write_word(address, value)
43
+ end
44
+
45
+ def valid_address?(address)
46
+ @mplab_memory.valid_address?(address)
47
+ end
17
48
 
18
- def write_word(address, value)
19
- @mplab_memory.write_word(address, value)
20
- end
21
-
22
- def read_word(address)
23
- @mplab_memory.read_word(address)
24
- end
25
-
26
- def is_valid_address?(address)
27
- @mplab_memory.is_valid_address?(address)
28
49
  end
29
- end
50
+ end
@@ -12,7 +12,7 @@ module RPicSim
12
12
  class MemoryWatcher
13
13
  attr_accessor :var_names_ignored
14
14
  attr_accessor :var_names_ignored_on_first_step
15
-
15
+
16
16
  # Creates a new instance.
17
17
  # @param sim [Sim]
18
18
  # @param memory [Mplab::MplabMemory] The memory to watch
@@ -23,20 +23,19 @@ module RPicSim
23
23
  vars.each do |var|
24
24
  var.addresses.each do |address|
25
25
  if @vars_by_address[address]
26
- raise "Variable %s overlaps with %s at 0x%x" %
26
+ raise 'Variable %s overlaps with %s at 0x%x' %
27
27
  [var, @vars_by_address[address], address]
28
28
  end
29
29
  @vars_by_address[address] = var
30
30
  end
31
31
  end
32
-
32
+
33
33
  @sim = sim
34
34
  @memory = memory
35
35
  @memory.on_change { |ar| handle_change(ar) }
36
-
36
+
37
37
  @vars_written = Set.new
38
38
  @var_names_ignored = default_var_names_ignored(sim.device)
39
- @var_names_ignored_on_first_step = default_var_names_ignored_on_first_step(sim.device)
40
39
  end
41
40
 
42
41
  # Generate a nice report of what variables have been written to since the
@@ -61,27 +60,13 @@ module RPicSim
61
60
  def clear
62
61
  @vars_written.clear
63
62
  end
64
-
65
- def default_var_names_ignored(device_name)
66
- # The datasheet says the PCLATH is not affected by pushing or popping the stack, but
67
- # we still get spurious events for it when a return instruction is executed.
68
63
 
69
- [:PCL, :PCLATH, :STATUS]
70
- end
71
-
72
- def default_var_names_ignored_on_first_step(device_name)
73
- #TODO: get rid of this stuff? people should just have a goto or something harmless at
74
- # the beginning of their program and take a single step like we do.
75
- [:PORTA, :LATA, :OSCCON, :PMCON2, :INTCON]
76
- end
77
-
78
64
  def handle_change(address_ranges)
79
65
  addresses = address_ranges.flat_map(&:to_a)
80
66
  vars = addresses.map { |a| @vars_by_address[a] || a }
81
67
 
82
68
  remove_vars(vars, @var_names_ignored)
83
- remove_vars(vars, @var_names_ignored_on_first_step) if @sim.cycle_count <= 1
84
-
69
+
85
70
  # The line below works because @vars_written is a Set, not a Hash.
86
71
  @vars_written.merge vars
87
72
  end
@@ -93,6 +78,13 @@ module RPicSim
93
78
  var_names_to_remove.include?(name)
94
79
  end
95
80
  end
96
-
81
+
82
+ def default_var_names_ignored(device_name)
83
+ # The datasheet says the PCLATH is not affected by pushing or popping the stack, but
84
+ # we still get spurious events for it when a return instruction is executed.
85
+
86
+ [:PCL, :PCLATH, :STATUS]
87
+ end
88
+
97
89
  end
98
- end
90
+ end
@@ -1,138 +1,57 @@
1
- require 'java'
2
- require 'pathname'
1
+ require_relative 'mplab/mplab_loader'
3
2
 
4
- module RPicSim
5
- module Mplab
6
- # Returns a Pathname object representing the directory of the MPLAB X we are using.
7
- # This can either come from the +RPICSIM_MPLABX+ environment variable or it can
8
- # be auto-detected by looking in the standard places that MPLAB X is installed.
9
- # @return [Pathname]
10
- def self.dir
11
- @dir ||= begin
12
- dir = ENV['RPICSIM_MPLABX']
3
+ module RPicSim::Mplab
4
+ MplabLoader.instance.load
13
5
 
14
- dir ||= [
15
- "C:/Program Files (x86)/Microchip/MPLABX/",
16
- "C:/Program Files/Microchip/MPLABX/",
17
- # TODO: add entries here for MPLAB X folders in Linux and Mac OS X
18
- ].detect { |d| File.directory?(d) }
19
-
20
- if !dir
21
- raise "Cannot find MPLABX. Install it in the standard location or " +
22
- "set the RPICSIM_MPASM environment variable to its full path."
23
- end
24
-
25
- if !File.directory?(dir)
26
- raise "MPLABX directory does not exist: #{dir}"
27
- end
28
-
29
- Pathname(dir)
30
- end
31
- end
32
-
33
- # Adds all the needed MPLAB X jar files to the classpath so we can use the
34
- # classes.
35
- def self.load_dependencies
36
- %w{ mplab_ide/mdbcore/modules/*.jar
37
- mplab_ide/mplablibs/modules/*.jar
38
- mplab_ide/mplablibs/modules/ext/*.jar
39
- mplab_ide/platform/lib/org-openide-util*.jar
40
- mplab_ide/platform/lib/org-openide-util.jar
41
- mplab_ide/mdbcore/modules/ext/org-openide-filesystems.jar
42
- }.each do |pattern|
43
- Dir.glob(dir + pattern).each do |jar_file|
44
- $CLASSPATH << jar_file
45
- end
46
- end
47
-
48
- # Do a quick test to make sure we can load some MPLAB X classes.
49
- # In case MPLAB X was uninstalled and its directory remains, this can provide
50
- # a useful error message to the user.
51
- begin
52
- org.openide.util.Lookup
53
- com.microchip.mplab.mdbcore.simulator.Simulator
54
- rescue NameError
55
- $stderr.puts "Failed to load MPLAB X classes.\n" +
56
- "MPLAB X dir: #{dir}\nClass path:\n" + $CLASSPATH.to_a.join("\n") + "\n\n"
57
- raise
58
- end
59
- end
60
-
61
- load_dependencies
62
-
63
-
64
- # JRuby makes it hard to access packages with capital letters in their names.
65
- # This is a workaround to let us access those packages.
66
- module CapitalizedPackages
67
- include_package "com.microchip.mplab.libs.MPLABDocumentLocator"
68
- end
6
+ def self.version
7
+ MplabLoader.instance.mplab_version
8
+ end
69
9
 
70
- # The com.microchip.mplab.libs.MPLABDocumentLocator.MPLABDocumentLocator class from MPLAB X.
71
- DocumentLocator = CapitalizedPackages::MPLABDocumentLocator
10
+ # JRuby makes it hard to access packages with capital letters in their names.
11
+ # This is a workaround to let us access those packages.
12
+ capitalized_packages = Module.new do
13
+ include_package 'com.microchip.mplab.libs.MPLABDocumentLocator'
14
+ end
72
15
 
73
- # Returns a string like "1.95" representing the version of MPLAB X we are using.
74
- # NOTE: You should probably NOT be doing this to work around flaws in MPLAB X.
75
- # Instead, you should add a new entry in flaws.rb and then use
76
- # RPicSim::Flaws[:FLAWNAME] to see if the flaw exists and choose the appropriate workaround.
77
- def self.version
78
- # This implementation is not pretty; I would prefer to just find the right function
79
- # to call.
80
- @mplabx_version ||= begin
81
- paths = Dir.glob(dir + "Uninstall_MPLAB_X_IDE_v*.dat")
82
- if paths.empty?
83
- raise "Cannot detect MPLAB X version. The Uninstall_MPLAB_X_IDE_v*.dat file was not found in #{mplabx_dir}."
84
- end
85
- match_data = paths.first.match(/v([0-9][0-9\.]*[0-9]+)\./)
86
- match_data[1]
87
- end
88
- end
16
+ # The com.microchip.mplab.libs.MPLABDocumentLocator.MPLABDocumentLocator class from MPLAB X.
17
+ DocumentLocator = capitalized_packages::MPLABDocumentLocator
89
18
 
90
- # Mutes the standard output, calls the given block, and then unmutes it.
91
- def self.mute_stdout
92
- begin
93
- orig = java.lang.System.out
94
- java.lang.System.setOut(java.io.PrintStream.new(NullOutputStream.new))
95
- yield
96
- ensure
97
- java.lang.System.setOut(orig)
98
- end
99
- end
19
+ Lookup = org.openide.util.Lookup
20
+ Mdbcore = com.microchip.mplab.mdbcore
100
21
 
101
- # Mutes a particular type of exception printed by NetBeans,
102
- # calls the given block, then unmutes it.
103
- def self.mute_exceptions
104
- log = java.util.logging.Logger.getLogger('org.openide.util.Exceptions')
105
- level = log.getLevel
106
- begin
107
- log.setLevel(java.util.logging.Level::OFF)
108
- yield
109
- ensure
110
- log.setLevel(level)
111
- end
112
- end
22
+ # Mutes the standard output, calls the given block, and then unmutes it.
23
+ def self.mute_stdout
24
+ orig = java.lang.System.out
25
+ java.lang.System.setOut(java.io.PrintStream.new(NullOutputStream.new))
26
+ yield
27
+ ensure
28
+ java.lang.System.setOut(orig)
29
+ end
113
30
 
114
- # The MCDisAsm class is the disassembly provided by MPLAB X.
115
- # We added this part so we could get access to its strategy object, but
116
- # we are not currently using that feature.
117
- class Java::ComMicrochipMplabMdbcoreDisasm::MCDisAsm
118
- field_accessor :strategy
31
+ # Mutes a particular type of exception printed by NetBeans,
32
+ # calls the given block, then unmutes it.
33
+ def self.mute_exceptions
34
+ log = java.util.logging.Logger.getLogger('org.openide.util.Exceptions')
35
+ level = log.getLevel
36
+ begin
37
+ log.setLevel(java.util.logging.Level::OFF)
38
+ yield
39
+ ensure
40
+ log.setLevel(level)
119
41
  end
42
+ end
120
43
 
121
- # This class helps us suppress the standard output temporarily.
122
- class NullOutputStream < Java::JavaIo::OutputStream
123
- def write(b)
124
- end
44
+ # This class helps us suppress the standard output temporarily.
45
+ class NullOutputStream < Java::JavaIo::OutputStream
46
+ def write(b)
125
47
  end
126
-
127
- Lookup = org.openide.util.Lookup
128
- Mdbcore = com.microchip.mplab.mdbcore
129
48
  end
130
-
131
49
  end
132
50
 
133
51
  # We want as much awareness as possible; if it becomes a problem we can change this.
134
52
  com.microchip.mplab.logger.MPLABLogger.mplog.setLevel(java.util.logging.Level::ALL)
135
53
 
54
+ # Require the rest of the RPicSim::Mplab Ruby classes now that MPLAB X has been loaded.
136
55
  require_relative 'mplab/mplab_pin'
137
56
  require_relative 'mplab/mplab_assembly'
138
57
  require_relative 'mplab/mplab_program_file'
@@ -3,9 +3,12 @@ require_relative 'mplab_simulator'
3
3
  require_relative 'mplab_disassembler'
4
4
 
5
5
  module RPicSim::Mplab
6
+ # This class wraps objects belonging to the abstract Java class
7
+ # com.microchip.mplab.mdbcore.assemblies.Assembly,
8
+ # which represent a collection of objects being used together.
6
9
  class MplabAssembly
7
10
  attr_reader :device
8
-
11
+
9
12
  def initialize(device)
10
13
  @device = device
11
14
  assembly_factory = Lookup.default.lookup(Mdbcore.assemblies.AssemblyFactory.java_class)
@@ -13,27 +16,27 @@ module RPicSim::Mplab
13
16
  @assembly = assembly_factory.create(device)
14
17
  end
15
18
  end
16
-
19
+
17
20
  # Connect the assembly to a simulator and debugger.
18
21
  def start_simulator_and_debugger(filename)
19
22
  # In MPLAB X v1.70, this line had to be before the call to SetTool, or else when we run
20
23
  # debugger.Connect we will get two lines of: [Fatal Error] :1:1: Premature end of file.
21
24
  simulator
22
-
23
- sim_meta = Mdbcore.platformtool.PlatformToolMetaManager.getTool("Simulator")
24
- @assembly.SetTool(sim_meta.configuration_object_id, sim_meta.class_name, sim_meta.flavor, "")
25
- if !sim_meta.getToolSupportForDevice(device).all? &:isSupported
26
- raise "Microchip's simulator does not support " + device + "."
25
+
26
+ sim_meta = Mdbcore.platformtool.PlatformToolMetaManager.getTool('Simulator')
27
+ @assembly.SetTool(sim_meta.configuration_object_id, sim_meta.class_name, sim_meta.flavor, '')
28
+ if !sim_meta.getToolSupportForDevice(device).all?(&:isSupported)
29
+ raise "Microchip's simulator does not support " + device + '.'
27
30
  end
28
- @assembly.SetHeader("") # The Microchip documentation doesn't say what this is.
31
+ @assembly.SetHeader('') # The Microchip documentation doesn't say what this is.
29
32
  debugger.Connect(Mdbcore.debugger.Debugger::CONNECTION_TYPE::DEBUGGER)
30
-
33
+
31
34
  # Load our firmware into the simulator.
32
35
  load_file(filename)
33
36
  debugger.Program(Mdbcore.debugger.Debugger::PROGRAM_OPERATION::AUTO_SELECT)
34
37
 
35
38
  check_for_errors
36
-
39
+
37
40
  nil
38
41
  end
39
42
 
@@ -55,17 +58,17 @@ module RPicSim::Mplab
55
58
  def disassembler
56
59
  @disassembler ||= MplabDisassembler.new lookup Mdbcore.disasm.DisAsm.java_class
57
60
  end
58
-
61
+
59
62
  def device_info
60
63
  @device_info ||= MplabDeviceInfo.new(@assembly.GetDevice)
61
64
  end
62
-
65
+
63
66
  private
64
67
  # Gets a com.microchip.mplab.mdbcore.debugger.Debugger object.
65
68
  def debugger
66
69
  lookup Mdbcore.debugger.Debugger.java_class
67
70
  end
68
-
71
+
69
72
  def loader
70
73
  lookup Mdbcore.loader.Loader.java_class
71
74
  end
@@ -73,7 +76,7 @@ module RPicSim::Mplab
73
76
  def lookup(klass)
74
77
  @assembly.getLookup.lookup klass
75
78
  end
76
-
79
+
77
80
  def check_for_errors
78
81
  warn_about_5C
79
82
  warn_about_path_retrieval
@@ -83,9 +86,11 @@ module RPicSim::Mplab
83
86
  def warn_about_5C
84
87
  # Detect a problem that once caused peripherals to load incorrectly.
85
88
  # More info: http://stackoverflow.com/q/15794170/28128
86
- f = DocumentLocator.java_class.resource("MPLABDocumentLocator.class").getFile()
87
- if f.include?("%5C")
88
- $stderr.puts "warning: A %5C character was detected in the MPLABDocumentLoator.class file location. This might cause errors in the Microchip code."
89
+ f = DocumentLocator.java_class.resource('MPLABDocumentLocator.class').getFile
90
+ if f.include?('%5C')
91
+ $stderr.puts 'warning: A %5C character was detected in the ' \
92
+ 'MPLABDocumentLoator.class file location. ' \
93
+ 'This might cause errors in the Microchip code.'
89
94
  end
90
95
  end
91
96
 
@@ -93,7 +98,7 @@ module RPicSim::Mplab
93
98
  # See spec/mplab_x/path_retrieval_spec.rb for more info.
94
99
  retrieval = com.microchip.mplab.open.util.pathretrieval.PathRetrieval
95
100
  path = retrieval.getPath(DocumentLocator.java_class)
96
- if !java.io.File.new(path).exists()
101
+ if !java.io.File.new(path).exists
97
102
  $stderr.puts "warning: MPLAB X will be looking for files at a bad path: #{path}"
98
103
  end
99
104
  end