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
@@ -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