rpicsim 0.1.0

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 +7 -0
  2. data/.yardopts +36 -0
  3. data/Gemfile +10 -0
  4. data/Introduction.md +64 -0
  5. data/LICENSE.txt +24 -0
  6. data/README.md +66 -0
  7. data/docs/ChangeLog.md +10 -0
  8. data/docs/Contributing.md +30 -0
  9. data/docs/Debugging.md +96 -0
  10. data/docs/DefiningSimulationClass.md +84 -0
  11. data/docs/DesignDecisions.md +48 -0
  12. data/docs/HowMPLABXIsFound.md +14 -0
  13. data/docs/IntegrationTesting.md +15 -0
  14. data/docs/IntroductionToRSpec.md +203 -0
  15. data/docs/IntroductionToRuby.md +90 -0
  16. data/docs/KnownIssues.md +204 -0
  17. data/docs/Labels.md +39 -0
  18. data/docs/MakingTestsRunFaster.md +29 -0
  19. data/docs/Manual.md +38 -0
  20. data/docs/PersistentExpectations.md +100 -0
  21. data/docs/Pins.md +143 -0
  22. data/docs/PreventingCallStackOverflow.md +94 -0
  23. data/docs/QuickStartGuide.md +85 -0
  24. data/docs/RSpecIntegration.md +119 -0
  25. data/docs/RamWatcher.md +46 -0
  26. data/docs/Running.md +129 -0
  27. data/docs/SFRs.md +71 -0
  28. data/docs/Stubbing.md +161 -0
  29. data/docs/SupportedCompilers.md +5 -0
  30. data/docs/SupportedDevices.md +14 -0
  31. data/docs/SupportedMPLABXVersions.md +12 -0
  32. data/docs/SupportedOperatingSystems.md +3 -0
  33. data/docs/UnitTesting.md +9 -0
  34. data/docs/Variables.md +140 -0
  35. data/lib/rpicsim.rb +15 -0
  36. data/lib/rpicsim/call_stack_info.rb +306 -0
  37. data/lib/rpicsim/flaws.rb +76 -0
  38. data/lib/rpicsim/instruction.rb +178 -0
  39. data/lib/rpicsim/label.rb +28 -0
  40. data/lib/rpicsim/memory.rb +29 -0
  41. data/lib/rpicsim/memory_watcher.rb +98 -0
  42. data/lib/rpicsim/mplab.rb +138 -0
  43. data/lib/rpicsim/mplab/mplab_assembly.rb +102 -0
  44. data/lib/rpicsim/mplab/mplab_device_info.rb +40 -0
  45. data/lib/rpicsim/mplab/mplab_disassembler.rb +23 -0
  46. data/lib/rpicsim/mplab/mplab_instruction.rb +32 -0
  47. data/lib/rpicsim/mplab/mplab_memory.rb +33 -0
  48. data/lib/rpicsim/mplab/mplab_nmmr_info.rb +21 -0
  49. data/lib/rpicsim/mplab/mplab_observer.rb +12 -0
  50. data/lib/rpicsim/mplab/mplab_pin.rb +61 -0
  51. data/lib/rpicsim/mplab/mplab_processor.rb +30 -0
  52. data/lib/rpicsim/mplab/mplab_program_file.rb +35 -0
  53. data/lib/rpicsim/mplab/mplab_register.rb +25 -0
  54. data/lib/rpicsim/mplab/mplab_sfr_info.rb +21 -0
  55. data/lib/rpicsim/mplab/mplab_simulator.rb +102 -0
  56. data/lib/rpicsim/pin.rb +61 -0
  57. data/lib/rpicsim/program_counter.rb +19 -0
  58. data/lib/rpicsim/program_file.rb +160 -0
  59. data/lib/rpicsim/register.rb +78 -0
  60. data/lib/rpicsim/rspec.rb +11 -0
  61. data/lib/rpicsim/rspec/be_predicate.rb +12 -0
  62. data/lib/rpicsim/rspec/helpers.rb +48 -0
  63. data/lib/rpicsim/rspec/persistent_expectations.rb +53 -0
  64. data/lib/rpicsim/rspec/sim_diagnostics.rb +51 -0
  65. data/lib/rpicsim/search.rb +20 -0
  66. data/lib/rpicsim/sim.rb +702 -0
  67. data/lib/rpicsim/stack_trace.rb +32 -0
  68. data/lib/rpicsim/variable.rb +236 -0
  69. data/lib/rpicsim/version.rb +3 -0
  70. metadata +114 -0
@@ -0,0 +1,76 @@
1
+ module RPicSim
2
+ module Flaws
3
+ # Represents a flaw in RPicSim, usually due to bugs or limitation of the
4
+ # MPLAB X classes we are using. Stores the name of the flaw and knowledge
5
+ # about what versions of MPLAB X it affects and how.
6
+ class Flaw
7
+ # Creates a new flaw with the specified name.
8
+ # @param name [Symbol] The name of this flaw.
9
+ def initialize(name)
10
+ @versions = {}
11
+ end
12
+
13
+ # Returns the effect of this flaw on the specified version of MPLAB X.
14
+ # This might just be true/false to indicate if the flaw is present or it
15
+ # might be a more complicated thing if there is more than one effect the
16
+ # the flaw can have.
17
+ #
18
+ # @param version [String] A version of MPLAB X, e.g. "1.95".
19
+ # @return effect
20
+ def effect(version)
21
+ if @versions.has_key? version
22
+ @versions[version]
23
+ else
24
+ @probable_affect_for_other_versions
25
+ end
26
+ end
27
+
28
+ # Records the effect this flaw has on a given version of MPLAB X.
29
+ def affects_version(version, effect)
30
+ @versions[version] = effect
31
+ end
32
+
33
+ # Records the effect that this flaw probably has in other versions of
34
+ # MPLAB X that have not been tested. This allows us to record our guesses
35
+ # about how the next version of MPLAB X will behave.
36
+ def probably_affects_other_versions(effect)
37
+ @probable_affect_for_other_versions = effect
38
+ end
39
+ end
40
+
41
+ @flaw_hash = {}
42
+ def self.[](name)
43
+ @flaw_hash[name].effect Mplab.version
44
+ end
45
+
46
+ def self.add(name)
47
+ @flaw_hash[name] = flaw = Flaw.new(name)
48
+ yield flaw
49
+ end
50
+
51
+ add(:fr_memory_attach_useless) do |flaw|
52
+ flaw.affects_version "1.85", false
53
+ flaw.affects_version "1.90", false
54
+ flaw.affects_version "1.95", true
55
+ flaw.affects_version "2.00", true
56
+ flaw.probably_affects_other_versions true
57
+ end
58
+
59
+ add(:firmware_cannot_write_user_id0) do |flaw|
60
+ flaw.affects_version "1.85", true
61
+ flaw.affects_version "1.90", true
62
+ flaw.affects_version "1.95", false
63
+ flaw.affects_version "2.00", false
64
+ flaw.probably_affects_other_versions false
65
+ end
66
+
67
+ add(:adc_midrange) do |flaw|
68
+ flaw.affects_version "1.85", :no_middle_values
69
+ flaw.affects_version "1.90", :bad_modulus
70
+ flaw.affects_version "1.95", :bad_modulus
71
+ flaw.affects_version "2.00", :bad_modulus
72
+ flaw.probably_affects_other_versions :bad_modulus
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,178 @@
1
+ module RPicSim
2
+ # Instances of this class represent a particular instruction at a particular
3
+ # address in program memory. This class takes low-level information about a
4
+ # disassembled instruction and produces high-level information about what that
5
+ # instruction is and how it behaves.
6
+ #
7
+ # Instances of this class have links to the other instructions that the instruction
8
+ # could lead to, so the instances form a graph. This graph is traversed by
9
+ # classes like {CallStackInfo} to get useful information about the firmware.
10
+ class Instruction
11
+ include Comparable
12
+
13
+ # The flash address of the instruction.
14
+ # For PIC18s this will be the byte address.
15
+ # For other PIC architectures, it will be the word address.
16
+ # @return (Integer)
17
+ attr_reader :address
18
+
19
+ # The opcode as a capitalized string (e.g. "MOVLW").
20
+ # @return (String)
21
+ attr_reader :opcode
22
+
23
+ # The operands of the instruction as a hash like { "k" => 92 }.
24
+ # @return (Hash)
25
+ attr_reader :operands
26
+
27
+ # The number of flash address units that this instruction takes.
28
+ # The units of this are the same as the units of {#address}.
29
+ # @return (Integer)
30
+ attr_reader :size
31
+
32
+ # Creates a new instruction.
33
+ # @param instruction_store some object such as {ProgramFile} that responds to #instruction and #address_description.
34
+ def initialize(address, instruction_store, opcode, operands, size, address_increment, string, properties)
35
+ @address = address
36
+ @instruction_store = instruction_store
37
+ @opcode = opcode
38
+ @operands = operands
39
+ @size = size
40
+ @address_increment = address_increment
41
+ @string = string
42
+
43
+ modules = {
44
+ conditional_skip: ConditionalSkip,
45
+ goto: Goto,
46
+ return: Return,
47
+ call: Call
48
+ }
49
+
50
+ properties.each do |p|
51
+ mod = modules[p]
52
+ if !mod
53
+ raise ArgumentError, "Invalid property: #{p.inspect}."
54
+ end
55
+ extend mod
56
+ end
57
+ end
58
+
59
+ # Compares this instruction to another using the addresses. This means you can
60
+ # call +.sort+ on an array of instructions to put them in order by address.
61
+ def <=>(other)
62
+ self.address <=> other.address
63
+ end
64
+
65
+ # Human-readable string representation of the instruction.
66
+ def to_s
67
+ "Instruction(#{@instruction_store.address_description(address)}, #{@string})"
68
+ end
69
+
70
+ def inspect
71
+ "#<#{self.class}:#{@instruction_store.address_description(address)}, #{@string}>"
72
+ end
73
+
74
+ # Returns info about all the instructions that this instruction could directly lead to
75
+ # (not counting interrupts, returns, and not accounting
76
+ # at all for what happens after the last word in flash is executed).
77
+ # For instructions that pop from the call stack like RETURN and RETFIE, this will be
78
+ # the empty array.
79
+ # @return [Array(Transition)]
80
+ def transitions
81
+ @transitions ||= generate_transitions
82
+ end
83
+
84
+ # Returns the transition from this instruction to the specified instruction
85
+ # or nil if no such transition exists.
86
+ # @return Transition
87
+ def transition_to(instruction)
88
+ @transitions.find { |t| t.next_instruction == instruction }
89
+ end
90
+
91
+ # Returns the addresses of all the instructions this instruction could directly lead to.
92
+ # @return [Array(Integer)]
93
+ def next_addresses
94
+ transitions.collect do |t|
95
+ t.next_instruction.address
96
+ end
97
+ end
98
+
99
+ private
100
+ # For certain opcodes, this method gets over-written.
101
+ def generate_transitions
102
+ [ advance(1) ]
103
+ end
104
+
105
+ # Makes a transition representing the default behavior: the microcontroller
106
+ # will increment the program counter and execute the next instruction in memory.
107
+ def advance(num)
108
+ transition(address + num * size)
109
+ end
110
+
111
+ def transition(addr, attrs={})
112
+ next_instruction = @instruction_store.instruction(addr)
113
+ Transition.new(self, next_instruction, attrs)
114
+ end
115
+
116
+ private
117
+ # Returns the address indicated by the operand 'k'.
118
+ # k is assumed to be a word address and it is assumed to be absolute
119
+ # k=0 is word 0 of memory, k=1 is word one.
120
+ # We need to multiply by the address increment because on PIC18
121
+ # flash addresses are actually byte-based instead of word-based.
122
+ def k_address
123
+ operands['k'] * @address_increment
124
+ end
125
+
126
+ ### Modules that modify the behavior of the instruction. ###
127
+
128
+
129
+ # This module is mixed into any {Instruction} that represents a goto or branch.
130
+ module Goto
131
+ def generate_transitions
132
+ # Assumption: The GOTO instruction's k operand is absolute on all architectures
133
+ [ transition(k_address, non_local: true) ]
134
+ end
135
+ end
136
+
137
+ # This module is mixed into any {Instruction} that represents a conditional skip
138
+ # A conditional skip is an instruction that might cause the next instruction to be
139
+ # skipped depending on some condition.
140
+ module ConditionalSkip
141
+ def generate_transitions
142
+ [ advance(1), advance(2) ]
143
+ end
144
+ end
145
+
146
+ # This module is mixed into any {Instruction} that represents a return from a subroutine.
147
+ module Return
148
+ def generate_transitions
149
+ []
150
+ end
151
+ end
152
+
153
+ # This module is mixed into any {Instruction} that represents a subroutine call.
154
+ module Call
155
+ def generate_transitions
156
+ [ transition(k_address, call_depth_change: 1), advance(1) ]
157
+ end
158
+ end
159
+
160
+ class Transition
161
+ attr_reader :next_instruction, :previous_instruction
162
+
163
+ def initialize(previous_instruction, next_instruction, attrs)
164
+ @previous_instruction = previous_instruction
165
+ @next_instruction = next_instruction
166
+ @attrs = attrs
167
+ end
168
+
169
+ def non_local?
170
+ @attrs.fetch(:non_local, false)
171
+ end
172
+
173
+ def call_depth_change
174
+ @attrs.fetch(:call_depth_change, 0)
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,28 @@
1
+ module RPicSim
2
+ # A very simple class that represents a label in firmware.
3
+ class Label
4
+ # The name of the label from the firmware.
5
+ # @return (Symbol)
6
+ attr_reader :name
7
+
8
+ # The address/value of the label from the firmware.
9
+ # @return (Integer)
10
+ attr_reader :address
11
+
12
+ # Makes a new label with the specified name and address.
13
+ def initialize(name, address)
14
+ @name = name
15
+ @address = address
16
+ end
17
+
18
+ # Returns the address of the label.
19
+ def to_i
20
+ address
21
+ end
22
+
23
+ # Returns a nice string representation of the label.
24
+ def to_s
25
+ "<Label %s address=0x%x>" % [name, address]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ class Memory
2
+ # This object allows read and write access to the current data
3
+ # stored in a memory space of the simulated device.
4
+ #
5
+ # The behavior of this class differs depending on what kind of Memory
6
+ # it represents, as shown in the table below:
7
+ #
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)
12
+ #
13
+ # @param mplab_memory [Mplab::Memory]
14
+ def initialize(mplab_memory)
15
+ @mplab_memory = mplab_memory
16
+ end
17
+
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
+ end
29
+ end
@@ -0,0 +1,98 @@
1
+ require 'set'
2
+
3
+ module RPicSim
4
+ # This class can attach to an MPLAB X memory class and watch for changes in it, and
5
+ # report those changes as a hash associating variable names to new values. This is
6
+ # very useful for writing tests that assert that not only were the desired variables
7
+ # written to, but also that no other variables were written to.
8
+ #
9
+ # This class does not analyze the current instruction being executed; it uses the
10
+ # Attach method of the MPLAB X memory classes. This means that it doesn't work properly
11
+ # in some versions of MPLAB X. See {file:docs/Flaws.textile}.
12
+ class MemoryWatcher
13
+ attr_accessor :var_names_ignored
14
+ attr_accessor :var_names_ignored_on_first_step
15
+
16
+ # Creates a new instance.
17
+ # @param sim [Sim]
18
+ # @param memory [Mplab::MplabMemory] The memory to watch
19
+ # @param vars [Array(Variable)]
20
+ def initialize(sim, memory, vars)
21
+ # Populate the @vars_by_address instance hash
22
+ @vars_by_address = {}
23
+ vars.each do |var|
24
+ var.addresses.each do |address|
25
+ if @vars_by_address[address]
26
+ raise "Variable %s overlaps with %s at 0x%x" %
27
+ [var, @vars_by_address[address], address]
28
+ end
29
+ @vars_by_address[address] = var
30
+ end
31
+ end
32
+
33
+ @sim = sim
34
+ @memory = memory
35
+ @memory.on_change { |ar| handle_change(ar) }
36
+
37
+ @vars_written = Set.new
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
+ end
41
+
42
+ # Generate a nice report of what variables have been written to since the
43
+ # last time {#clear} was called.
44
+ # @return [Hash] A hash where the keys are names (as symbols) of variables that were
45
+ # written to, and the value is the variable's current value.
46
+ # If an address was written to that does not correspond to a variable, the key for
47
+ # that will just be address as an integer.
48
+ def writes
49
+ hash = {}
50
+ @vars_written.each do |var|
51
+ if var.is_a? Integer
52
+ hash[var] = @memory.read_word(var)
53
+ else
54
+ hash[var.name] = var.value
55
+ end
56
+ end
57
+ hash
58
+ end
59
+
60
+ # Clears the record of what variables have been written to.
61
+ def clear
62
+ @vars_written.clear
63
+ 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
+
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
+ def handle_change(address_ranges)
79
+ addresses = address_ranges.flat_map(&:to_a)
80
+ vars = addresses.map { |a| @vars_by_address[a] || a }
81
+
82
+ remove_vars(vars, @var_names_ignored)
83
+ remove_vars(vars, @var_names_ignored_on_first_step) if @sim.cycle_count <= 1
84
+
85
+ # The line below works because @vars_written is a Set, not a Hash.
86
+ @vars_written.merge vars
87
+ end
88
+
89
+ private
90
+ def remove_vars(vars, var_names_to_remove)
91
+ vars.reject! do |key, val|
92
+ name = key.is_a?(Integer) ? key : key.name
93
+ var_names_to_remove.include?(name)
94
+ end
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,138 @@
1
+ require 'java'
2
+ require 'pathname'
3
+
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']
13
+
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
69
+
70
+ # The com.microchip.mplab.libs.MPLABDocumentLocator.MPLABDocumentLocator class from MPLAB X.
71
+ DocumentLocator = CapitalizedPackages::MPLABDocumentLocator
72
+
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
89
+
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
100
+
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
113
+
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
119
+ end
120
+
121
+ # This class helps us suppress the standard output temporarily.
122
+ class NullOutputStream < Java::JavaIo::OutputStream
123
+ def write(b)
124
+ end
125
+ end
126
+
127
+ Lookup = org.openide.util.Lookup
128
+ Mdbcore = com.microchip.mplab.mdbcore
129
+ end
130
+
131
+ end
132
+
133
+ # We want as much awareness as possible; if it becomes a problem we can change this.
134
+ com.microchip.mplab.logger.MPLABLogger.mplog.setLevel(java.util.logging.Level::ALL)
135
+
136
+ require_relative 'mplab/mplab_pin'
137
+ require_relative 'mplab/mplab_assembly'
138
+ require_relative 'mplab/mplab_program_file'