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.
- checksums.yaml +7 -0
- data/.yardopts +36 -0
- data/Gemfile +10 -0
- data/Introduction.md +64 -0
- data/LICENSE.txt +24 -0
- data/README.md +66 -0
- data/docs/ChangeLog.md +10 -0
- data/docs/Contributing.md +30 -0
- data/docs/Debugging.md +96 -0
- data/docs/DefiningSimulationClass.md +84 -0
- data/docs/DesignDecisions.md +48 -0
- data/docs/HowMPLABXIsFound.md +14 -0
- data/docs/IntegrationTesting.md +15 -0
- data/docs/IntroductionToRSpec.md +203 -0
- data/docs/IntroductionToRuby.md +90 -0
- data/docs/KnownIssues.md +204 -0
- data/docs/Labels.md +39 -0
- data/docs/MakingTestsRunFaster.md +29 -0
- data/docs/Manual.md +38 -0
- data/docs/PersistentExpectations.md +100 -0
- data/docs/Pins.md +143 -0
- data/docs/PreventingCallStackOverflow.md +94 -0
- data/docs/QuickStartGuide.md +85 -0
- data/docs/RSpecIntegration.md +119 -0
- data/docs/RamWatcher.md +46 -0
- data/docs/Running.md +129 -0
- data/docs/SFRs.md +71 -0
- data/docs/Stubbing.md +161 -0
- data/docs/SupportedCompilers.md +5 -0
- data/docs/SupportedDevices.md +14 -0
- data/docs/SupportedMPLABXVersions.md +12 -0
- data/docs/SupportedOperatingSystems.md +3 -0
- data/docs/UnitTesting.md +9 -0
- data/docs/Variables.md +140 -0
- data/lib/rpicsim.rb +15 -0
- data/lib/rpicsim/call_stack_info.rb +306 -0
- data/lib/rpicsim/flaws.rb +76 -0
- data/lib/rpicsim/instruction.rb +178 -0
- data/lib/rpicsim/label.rb +28 -0
- data/lib/rpicsim/memory.rb +29 -0
- data/lib/rpicsim/memory_watcher.rb +98 -0
- data/lib/rpicsim/mplab.rb +138 -0
- data/lib/rpicsim/mplab/mplab_assembly.rb +102 -0
- data/lib/rpicsim/mplab/mplab_device_info.rb +40 -0
- data/lib/rpicsim/mplab/mplab_disassembler.rb +23 -0
- data/lib/rpicsim/mplab/mplab_instruction.rb +32 -0
- data/lib/rpicsim/mplab/mplab_memory.rb +33 -0
- data/lib/rpicsim/mplab/mplab_nmmr_info.rb +21 -0
- data/lib/rpicsim/mplab/mplab_observer.rb +12 -0
- data/lib/rpicsim/mplab/mplab_pin.rb +61 -0
- data/lib/rpicsim/mplab/mplab_processor.rb +30 -0
- data/lib/rpicsim/mplab/mplab_program_file.rb +35 -0
- data/lib/rpicsim/mplab/mplab_register.rb +25 -0
- data/lib/rpicsim/mplab/mplab_sfr_info.rb +21 -0
- data/lib/rpicsim/mplab/mplab_simulator.rb +102 -0
- data/lib/rpicsim/pin.rb +61 -0
- data/lib/rpicsim/program_counter.rb +19 -0
- data/lib/rpicsim/program_file.rb +160 -0
- data/lib/rpicsim/register.rb +78 -0
- data/lib/rpicsim/rspec.rb +11 -0
- data/lib/rpicsim/rspec/be_predicate.rb +12 -0
- data/lib/rpicsim/rspec/helpers.rb +48 -0
- data/lib/rpicsim/rspec/persistent_expectations.rb +53 -0
- data/lib/rpicsim/rspec/sim_diagnostics.rb +51 -0
- data/lib/rpicsim/search.rb +20 -0
- data/lib/rpicsim/sim.rb +702 -0
- data/lib/rpicsim/stack_trace.rb +32 -0
- data/lib/rpicsim/variable.rb +236 -0
- data/lib/rpicsim/version.rb +3 -0
- 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,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
|