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,102 @@
1
+ require_relative 'mplab_device_info'
2
+ require_relative 'mplab_simulator'
3
+ require_relative 'mplab_disassembler'
4
+
5
+ module RPicSim::Mplab
6
+ class MplabAssembly
7
+ attr_reader :device
8
+
9
+ def initialize(device)
10
+ @device = device
11
+ assembly_factory = Lookup.default.lookup(Mdbcore.assemblies.AssemblyFactory.java_class)
12
+ RPicSim::Mplab.mute_stdout do
13
+ @assembly = assembly_factory.create(device)
14
+ end
15
+ end
16
+
17
+ # Connect the assembly to a simulator and debugger.
18
+ def start_simulator_and_debugger(filename)
19
+ # In MPLAB X v1.70, this line had to be before the call to SetTool, or else when we run
20
+ # debugger.Connect we will get two lines of: [Fatal Error] :1:1: Premature end of file.
21
+ 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 + "."
27
+ end
28
+ @assembly.SetHeader("") # The Microchip documentation doesn't say what this is.
29
+ debugger.Connect(Mdbcore.debugger.Debugger::CONNECTION_TYPE::DEBUGGER)
30
+
31
+ # Load our firmware into the simulator.
32
+ load_file(filename)
33
+ debugger.Program(Mdbcore.debugger.Debugger::PROGRAM_OPERATION::AUTO_SELECT)
34
+
35
+ check_for_errors
36
+
37
+ nil
38
+ end
39
+
40
+ def load_file(filename)
41
+ loader.Load(filename)
42
+ end
43
+
44
+ def debugger_step
45
+ debugger.StepInstr
46
+ end
47
+
48
+ # Gets a com.microchip.mplab.mdbcore.simulator.Simulator object.
49
+ def simulator
50
+ @simulator ||= MplabSimulator.new lookup Mdbcore.simulator.Simulator.java_class
51
+ end
52
+
53
+ # Gets a com.microchip.mplab.mdbcore.disasm.Disasm object which we can
54
+ # use to disassemble the program binary.
55
+ def disassembler
56
+ @disassembler ||= MplabDisassembler.new lookup Mdbcore.disasm.DisAsm.java_class
57
+ end
58
+
59
+ def device_info
60
+ @device_info ||= MplabDeviceInfo.new(@assembly.GetDevice)
61
+ end
62
+
63
+ private
64
+ # Gets a com.microchip.mplab.mdbcore.debugger.Debugger object.
65
+ def debugger
66
+ lookup Mdbcore.debugger.Debugger.java_class
67
+ end
68
+
69
+ def loader
70
+ lookup Mdbcore.loader.Loader.java_class
71
+ end
72
+
73
+ def lookup(klass)
74
+ @assembly.getLookup.lookup klass
75
+ end
76
+
77
+ def check_for_errors
78
+ warn_about_5C
79
+ warn_about_path_retrieval
80
+ @simulator.check_peripherals
81
+ end
82
+
83
+ def warn_about_5C
84
+ # Detect a problem that once caused peripherals to load incorrectly.
85
+ # 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
+ end
90
+ end
91
+
92
+ def warn_about_path_retrieval
93
+ # See spec/mplab_x/path_retrieval_spec.rb for more info.
94
+ retrieval = com.microchip.mplab.open.util.pathretrieval.PathRetrieval
95
+ path = retrieval.getPath(DocumentLocator.java_class)
96
+ if !java.io.File.new(path).exists()
97
+ $stderr.puts "warning: MPLAB X will be looking for files at a bad path: #{path}"
98
+ end
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,40 @@
1
+ require_relative 'mplab_sfr_info'
2
+ require_relative 'mplab_nmmr_info'
3
+
4
+ module RPicSim::Mplab
5
+ # DeviceInfo is a wrapper for the MPLAB xPIC class which gives us information
6
+ # about the target PIC device.
7
+ class MplabDeviceInfo
8
+ # Makes a new DeviceInfo object.
9
+ # @param xpic [com.microchip.mplab.crownkingx.xPIC]
10
+ def initialize(xpic)
11
+ @xpic = xpic
12
+ end
13
+
14
+ def code_word_max_value
15
+ # Assumption: the initial value is the same as the maximum value
16
+ # because all bits start as 1.
17
+ @xpic.getMemTraits.getCodeWordTraits.getInitValue
18
+ end
19
+
20
+ # The number that a flash address increases when you
21
+ # advance to the next word of flash.
22
+ # For PIC18s this is 2.
23
+ # For other architectures this is 1.
24
+ def code_address_increment
25
+ @xpic.getMemTraits.getCodeWordTraits.getAddrInc
26
+ end
27
+
28
+ def sfrs
29
+ @sfrs ||= @xpic.getAddrOntoSFR.map do |addr, node|
30
+ MplabSfrInfo.new addr, com.microchip.crownking.edc.Register.new(node)
31
+ end
32
+ end
33
+
34
+ def nmmrs
35
+ @xpic.getIDOntoCoreNMMR.map do |id, node|
36
+ MplabNmmrInfo.new id, com.microchip.crownking.edc.Register.new(node)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'mplab_instruction'
2
+
3
+ module RPicSim::Mplab
4
+ class MplabDisassembler
5
+ # @param disasm [com.microchip.mplab.mdbcore.disasm.DisAsm]
6
+ def initialize(disasm)
7
+ @disasm = disasm
8
+ end
9
+
10
+ def disassemble(address)
11
+ # To avoid showing a large, difficult to understand Java trace, we
12
+ # catch the InvalidInstructionException here.
13
+ begin
14
+ instr = @disasm.Disassemble(address, nil, nil)
15
+ rescue Java::ComMicrochipMplabMdbcoreDisasm::InvalidInstructionException => e
16
+ # TODO: actually this should not be an exception because it is expected;
17
+ # so probably you should return nil
18
+ raise "Invalid instruction at address 0x%x." % address
19
+ end
20
+ MplabInstruction.new instr
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ module RPicSim::Mplab
2
+ class MplabInstruction
3
+ # @param instruction [com.microchip.mplab.mdbcore.disasm.Instruction]
4
+ def initialize(instruction)
5
+ @instruction = instruction
6
+ end
7
+
8
+ def opcode
9
+ @instruction.opcode
10
+ end
11
+
12
+ def instruction_string
13
+ @instruction.instruction
14
+ end
15
+
16
+ def operands
17
+ @operands ||= operands_hash(@instruction.operands)
18
+ end
19
+
20
+ # The number of bytes that the instruction takes.
21
+ def inc
22
+ @instruction.inc
23
+ end
24
+
25
+ private
26
+ def operands_hash(map)
27
+ # Convert from Map<String, Integer> to a Ruby hash.
28
+ # TODO: use symbols for keys instead of strings
29
+ map.to_hash
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'mplab_observer'
2
+
3
+ module RPicSim::Mplab
4
+ class MplabMemory
5
+ # @param memory Should implement the interface com.microchip.mplab.mdbcore.memory.Memory
6
+ def initialize(memory)
7
+ @memory = memory
8
+ end
9
+
10
+ def write_word(address, value)
11
+ @memory.WriteWord(address, value)
12
+ value
13
+ end
14
+
15
+ def read_word(address)
16
+ @memory.ReadWord(address)
17
+ end
18
+
19
+ def is_valid_address?(address)
20
+ @memory.IsValidAddress(address)
21
+ end
22
+
23
+ def on_change(&callback)
24
+ MplabObserver.new(@memory) do |event|
25
+ next if event.EventType != Mdbcore.memory::MemoryEvent::EVENTS::MEMORY_CHANGED
26
+ address_ranges = event.AffectedAddresses.map do |mr|
27
+ mr.Address...(mr.Address+mr.Size)
28
+ end
29
+ yield address_ranges
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ module RPicSim::Mplab
2
+ class MplabNmmrInfo
3
+ attr_reader :id
4
+
5
+ # @param id The id of the register. This is like an address.
6
+ # @param register [com.microchip.crownking.edc.Register]
7
+ def initialize(id, register)
8
+ @id = id
9
+ @register = register
10
+ end
11
+
12
+ # Returns how many bits the register has.
13
+ def width
14
+ @register.width
15
+ end
16
+
17
+ def name
18
+ @register.name
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ class MplabObserver
2
+ include Java::comMicrochipMplabUtilObservers::Observer
3
+
4
+ def initialize(subject, &callback)
5
+ @callback = callback
6
+ subject.Attach(self, nil)
7
+ end
8
+
9
+ def Update(event)
10
+ @callback.call(event)
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ module RPicSim::Mplab
2
+ class MplabPin
3
+ # Initializes a new Pin object to wrap the given PinPhysical.
4
+ # @param pin_physical [com.microchip.mplab.mdbcore.simulator.PinPhysical]
5
+ def initialize(pin_physical)
6
+ raise ArgumentError, "pin_physical is nil" if pin_physical.nil?
7
+ @pin_physical = pin_physical
8
+ end
9
+
10
+ def set_low
11
+ @pin_physical.externalSet PinState::LOW
12
+ end
13
+
14
+ def set_high
15
+ @pin_physical.externalSet PinState::HIGH
16
+ end
17
+
18
+ def set_analog(value)
19
+ @pin_physical.externalSetAnalogValue value
20
+ end
21
+
22
+ # Returns true if the pin is currently configured to be an output,
23
+ # or false if it is configured as an input. Raises an exception
24
+ # if MPLAB X claims the state is neither, which we think is
25
+ # impossible.
26
+ def output?
27
+ io_state = @pin_physical.getIOState
28
+ case io_state
29
+ when IoState::OUTPUT then true
30
+ when IoState::INPUT then false
31
+ else
32
+ raise "Invalid IO state: #{io_state}"
33
+ end
34
+ end
35
+
36
+ # Returns true if the pin is currently in a "high" state, or false
37
+ # if it is in a "low" state. Raises an exception if MPLAB X
38
+ # claims the state is neither, which we think is impossible.
39
+ def high?
40
+ pin_state = @pin_physical.get
41
+ case pin_state
42
+ when PinState::HIGH then true
43
+ when PinState::LOW then false
44
+ else
45
+ raise "Invalid pin state: #{pin_state}"
46
+ end
47
+ end
48
+
49
+ def names
50
+ @pin_physical.collect(&:name)
51
+ end
52
+
53
+ def name
54
+ @pin_physical.pinName
55
+ end
56
+
57
+ PinState = Mdbcore.simulator.Pin::PinState # HIGH or LOW
58
+ IoState = Mdbcore.simulator.Pin::IOState # INPUT or OUTPUT
59
+ private_constant :PinState, :IoState
60
+ end
61
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'mplab_register'
2
+
3
+ module RPicSim::Mplab
4
+ class MplabProcessor
5
+ # @param processor [com.microchip.mplab.mdbcore.simulator.Processor]
6
+ def initialize(processor)
7
+ @processor = processor
8
+ end
9
+
10
+ def get_pc
11
+ @processor.getPC
12
+ end
13
+
14
+ def set_pc(value)
15
+ @processor.setPC(value)
16
+ end
17
+
18
+ def get_sfr(name)
19
+ reg = @processor.getSFRSet.getSFR(name)
20
+ raise "Cannot find SFR named '#{name}'." if !reg
21
+ MplabRegister.new(reg)
22
+ end
23
+
24
+ def get_nmmr(name)
25
+ reg = @processor.getNMMRSet.getNMMR(name)
26
+ raise "Cannot find NMMR named '#{name}'." if !reg
27
+ MplabRegister.new(reg)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ module RPicSim::Mplab
2
+ # This class creates and wraps a com.microchip.mplab.mdbcore.program.interfaces.IProgramFile
3
+ class MplabProgramFile
4
+ def initialize(filename, device)
5
+ raise "File does not exist: #{filename}" if !File.exist?(filename) # Avoid a Java exception.
6
+
7
+ if !File.realdirpath(filename).split("/").include?("dist")
8
+ raise "The file must be inside a directory named dist or else the MCLoader " +
9
+ "class will throw an exception saying that it cannot find the COF file."
10
+ end
11
+
12
+ factory = Lookup.getDefault.lookup(Mdbcore.program.spi.IProgramFileProviderFactory.java_class)
13
+ @program_file = factory.getProvider(filename, device)
14
+ @program_file.Load
15
+ end
16
+
17
+ def symbols_in_code_space
18
+ @symbols_in_code_space ||= Hash[
19
+ symbols.select { |s| s.m_lType != 0 }.map { |s| [s.m_Symbol.to_sym, s.address] }
20
+ ]
21
+ end
22
+
23
+ def symbols_in_ram
24
+ @symbols_in_ram ||= Hash[
25
+ symbols.select { |s| s.m_lType == 0 }.map { |s| [s.m_Symbol.to_sym, s.address] }
26
+ ]
27
+ end
28
+
29
+ private
30
+
31
+ def symbols
32
+ @program_file.getSymbolTable.getSymbols(0, 0)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ module RPicSim::Mplab
2
+ class MplabRegister
3
+ # @param register [com.microchip.mplab.mdbcore.simulator.Register]
4
+ def initialize(register)
5
+ @register = register
6
+ end
7
+
8
+ def write(value)
9
+ @register.write(value)
10
+ value
11
+ end
12
+
13
+ def read
14
+ @register.read
15
+ end
16
+
17
+ def name
18
+ @register.getName
19
+ end
20
+
21
+ def address
22
+ @register.getAddress
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ module RPicSim::Mplab
2
+ class MplabSfrInfo
3
+ attr_reader :address
4
+
5
+ # @param address The address of the register.
6
+ # @param register [com.microchip.crownking.edc.Register]
7
+ def initialize(address, register)
8
+ @address = address
9
+ @register = register
10
+ end
11
+
12
+ # Returns how many bits the register has.
13
+ def width
14
+ @register.width
15
+ end
16
+
17
+ def name
18
+ @register.name
19
+ end
20
+ end
21
+ end