rpicsim 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|