rpicsim 0.1.0 → 0.2.1
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 +4 -4
- data/.yardopts +1 -1
- data/Gemfile +7 -6
- data/Introduction.md +3 -1
- data/README.md +2 -2
- data/docs/ChangeLog.md +6 -0
- data/docs/Contributing.md +1 -1
- data/docs/DefiningSimulationClass.md +11 -10
- data/docs/HowMPLABXIsFound.md +1 -1
- data/docs/IntegrationTesting.md +1 -1
- data/docs/IntroductionToRSpec.md +8 -5
- data/docs/IntroductionToRuby.md +2 -2
- data/docs/KnownIssues.md +46 -57
- data/docs/Labels.md +5 -4
- data/docs/Manual.md +1 -1
- data/docs/Memories.md +70 -0
- data/docs/PersistentExpectations.md +31 -2
- data/docs/Pins.md +5 -7
- data/docs/PreventingCallStackOverflow.md +4 -6
- data/docs/QuickStartGuide.md +5 -5
- data/docs/RSpecIntegration.md +2 -2
- data/docs/RamWatcher.md +22 -11
- data/docs/Running.md +4 -6
- data/docs/Stubbing.md +4 -4
- data/docs/SupportedDevices.md +2 -11
- data/docs/SupportedMPLABXVersions.md +1 -0
- data/docs/SupportedOperatingSystems.md +3 -2
- data/docs/UnitTesting.md +1 -1
- data/docs/Variables.md +81 -25
- data/lib/rpicsim.rb +0 -12
- data/lib/rpicsim/call_stack_info.rb +43 -47
- data/lib/rpicsim/composite_memory.rb +53 -0
- data/lib/rpicsim/flaws.rb +34 -22
- data/lib/rpicsim/instruction.rb +204 -48
- data/lib/rpicsim/label.rb +4 -4
- data/lib/rpicsim/memory.rb +44 -23
- data/lib/rpicsim/memory_watcher.rb +14 -22
- data/lib/rpicsim/mplab.rb +38 -119
- data/lib/rpicsim/mplab/mplab_assembly.rb +23 -18
- data/lib/rpicsim/mplab/mplab_device_info.rb +9 -9
- data/lib/rpicsim/mplab/mplab_disassembler.rb +5 -6
- data/lib/rpicsim/mplab/mplab_instruction.rb +87 -16
- data/lib/rpicsim/mplab/mplab_loader.rb +106 -0
- data/lib/rpicsim/mplab/mplab_memory.rb +19 -6
- data/lib/rpicsim/mplab/mplab_nmmr_info.rb +4 -4
- data/lib/rpicsim/mplab/mplab_observer.rb +15 -10
- data/lib/rpicsim/mplab/mplab_pin.rb +3 -3
- data/lib/rpicsim/mplab/mplab_processor.rb +5 -5
- data/lib/rpicsim/mplab/mplab_program_file.rb +29 -17
- data/lib/rpicsim/mplab/mplab_register.rb +5 -5
- data/lib/rpicsim/mplab/mplab_sfr_info.rb +4 -4
- data/lib/rpicsim/mplab/mplab_simulator.rb +27 -30
- data/lib/rpicsim/pin.rb +6 -6
- data/lib/rpicsim/program_counter.rb +3 -3
- data/lib/rpicsim/program_file.rb +39 -81
- data/lib/rpicsim/rspec/be_predicate.rb +1 -1
- data/lib/rpicsim/rspec/helpers.rb +1 -1
- data/lib/rpicsim/rspec/persistent_expectations.rb +17 -2
- data/lib/rpicsim/rspec/sim_diagnostics.rb +5 -5
- data/lib/rpicsim/search.rb +1 -1
- data/lib/rpicsim/sim.rb +153 -228
- data/lib/rpicsim/stack_pointer.rb +41 -0
- data/lib/rpicsim/stack_trace.rb +1 -1
- data/lib/rpicsim/storage/memory_integer.rb +235 -0
- data/lib/rpicsim/{register.rb → storage/register.rb} +18 -18
- data/lib/rpicsim/variable.rb +25 -211
- data/lib/rpicsim/variable_set.rb +93 -0
- data/lib/rpicsim/version.rb +2 -2
- metadata +9 -4
- data/docs/SFRs.md +0 -71
@@ -2,39 +2,39 @@ require_relative 'mplab_sfr_info'
|
|
2
2
|
require_relative 'mplab_nmmr_info'
|
3
3
|
|
4
4
|
module RPicSim::Mplab
|
5
|
-
# DeviceInfo is a wrapper for the
|
6
|
-
# about the target PIC device.
|
5
|
+
# DeviceInfo is a wrapper for the com.microchip.mplab.crownkingx.xPIC class
|
6
|
+
# which gives us information about the target PIC device.
|
7
7
|
class MplabDeviceInfo
|
8
8
|
# Makes a new DeviceInfo object.
|
9
9
|
# @param xpic [com.microchip.mplab.crownkingx.xPIC]
|
10
10
|
def initialize(xpic)
|
11
11
|
@xpic = xpic
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def code_word_max_value
|
15
15
|
# Assumption: the initial value is the same as the maximum value
|
16
16
|
# because all bits start as 1.
|
17
17
|
@xpic.getMemTraits.getCodeWordTraits.getInitValue
|
18
18
|
end
|
19
|
-
|
20
|
-
# The number that a
|
21
|
-
# advance to the next word of
|
19
|
+
|
20
|
+
# The number that a code-space address increases by when you
|
21
|
+
# advance to the next word of code space.
|
22
22
|
# For PIC18s this is 2.
|
23
23
|
# For other architectures this is 1.
|
24
24
|
def code_address_increment
|
25
25
|
@xpic.getMemTraits.getCodeWordTraits.getAddrInc
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def sfrs
|
29
29
|
@sfrs ||= @xpic.getAddrOntoSFR.map do |addr, node|
|
30
30
|
MplabSfrInfo.new addr, com.microchip.crownking.edc.Register.new(node)
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
def nmmrs
|
35
35
|
@xpic.getIDOntoCoreNMMR.map do |id, node|
|
36
36
|
MplabNmmrInfo.new id, com.microchip.crownking.edc.Register.new(node)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
|
-
end
|
40
|
+
end
|
@@ -6,18 +6,17 @@ module RPicSim::Mplab
|
|
6
6
|
def initialize(disasm)
|
7
7
|
@disasm = disasm
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def disassemble(address)
|
11
11
|
# To avoid showing a large, difficult to understand Java trace, we
|
12
12
|
# catch the InvalidInstructionException here.
|
13
13
|
begin
|
14
14
|
instr = @disasm.Disassemble(address, nil, nil)
|
15
|
-
rescue Java::ComMicrochipMplabMdbcoreDisasm::InvalidInstructionException
|
16
|
-
#
|
17
|
-
|
18
|
-
raise "Invalid instruction at address 0x%x." % address
|
15
|
+
rescue Java::ComMicrochipMplabMdbcoreDisasm::InvalidInstructionException
|
16
|
+
# The instruction is invalid.
|
17
|
+
return :invalid
|
19
18
|
end
|
20
19
|
MplabInstruction.new instr
|
21
20
|
end
|
22
21
|
end
|
23
|
-
end
|
22
|
+
end
|
@@ -1,32 +1,103 @@
|
|
1
1
|
module RPicSim::Mplab
|
2
|
+
# This class wraps a com.microchip.mplab.mdbcore.disasm.Instruction, which
|
3
|
+
# represents a disassembled instruction from Microchip's disassembler.
|
2
4
|
class MplabInstruction
|
5
|
+
attr_reader :opcode
|
6
|
+
|
3
7
|
# @param instruction [com.microchip.mplab.mdbcore.disasm.Instruction]
|
4
8
|
def initialize(instruction)
|
5
9
|
@instruction = instruction
|
10
|
+
|
11
|
+
@opcode = @instruction.opcode
|
12
|
+
@string = @instruction.instruction
|
13
|
+
|
14
|
+
# Fix a typo in MPLAB X.
|
15
|
+
if @opcode == 'RBLRD+*'
|
16
|
+
@opcode = 'TBLRD+*'
|
17
|
+
@string = @string.gsub('RBLRD', 'TBLRD')
|
18
|
+
end
|
19
|
+
@opcode.freeze
|
6
20
|
end
|
7
|
-
|
8
|
-
def opcode
|
9
|
-
@instruction.opcode
|
10
|
-
end
|
11
|
-
|
21
|
+
|
12
22
|
def instruction_string
|
13
|
-
@
|
23
|
+
@string
|
14
24
|
end
|
15
|
-
|
25
|
+
|
16
26
|
def operands
|
17
27
|
@operands ||= operands_hash(@instruction.operands)
|
18
28
|
end
|
19
|
-
|
20
|
-
#
|
21
|
-
|
22
|
-
|
29
|
+
|
30
|
+
# Returns the size of the instruction in the same units that are used
|
31
|
+
# for program memory addresses. (Bytes for the PIC18, otherwise words.)
|
32
|
+
# @param address_increment The number of address units per word of
|
33
|
+
# program memory in this architecture. See {MplabDeviceInfo#code_address_increment}.
|
34
|
+
def compute_size(address_increment)
|
35
|
+
if RPicSim::Flaws[:instruction_inc_is_in_byte_units]
|
36
|
+
# Convert the increment, which is the number of bytes, into 'size',
|
37
|
+
# which is the same units as the program memory address space.
|
38
|
+
if address_increment == 1
|
39
|
+
# Non-PIC18 architectures: program memory addresses are in terms of words
|
40
|
+
# so we divide by two to convert from bytes to words.
|
41
|
+
@instruction.inc / 2
|
42
|
+
elsif address_increment == 2
|
43
|
+
# PIC18 architecture: No change necessary because both are in terms
|
44
|
+
# of bytes.
|
45
|
+
@instruction.inc
|
46
|
+
else
|
47
|
+
raise "Cannot handle address increment value of #{@address_increment}."
|
48
|
+
end
|
49
|
+
else
|
50
|
+
# inc is in the same units as the code space addresses.
|
51
|
+
@instruction.inc
|
52
|
+
end
|
23
53
|
end
|
24
|
-
|
54
|
+
|
25
55
|
private
|
56
|
+
|
26
57
|
def operands_hash(map)
|
27
|
-
|
28
|
-
|
29
|
-
|
58
|
+
operands = convert_map_to_hash(map)
|
59
|
+
fix_signed_fields(operands)
|
60
|
+
operands
|
61
|
+
end
|
62
|
+
|
63
|
+
def convert_map_to_hash(map)
|
64
|
+
# Convert from Map<String, Integer> to a Ruby hash
|
65
|
+
# with symbols as keys instead of strings.
|
66
|
+
operands = {}
|
67
|
+
map.each do |operand_name, value|
|
68
|
+
operands[operand_name.to_sym] = value
|
69
|
+
end
|
70
|
+
operands
|
71
|
+
end
|
72
|
+
|
73
|
+
# Warning: This mutates the supplied hash.
|
74
|
+
def fix_signed_fields(operands)
|
75
|
+
case opcode
|
76
|
+
when 'BC', 'BN', 'BNC', 'BNN', 'BNOV', 'BNZ', 'BOV', 'BZ'
|
77
|
+
convert_if_present(operands, :n, 8) # PIC18
|
78
|
+
when 'RCALL'
|
79
|
+
convert_if_present(operands, :n, 11) # PIC18
|
80
|
+
when 'BRA'
|
81
|
+
convert_if_present(operands, :n, 11) # PIC18
|
82
|
+
convert_if_present(operands, :k, 9) # enhanced midrange
|
83
|
+
when 'ADDFSR'
|
84
|
+
convert_if_present(operands, :k, 6) # enhanced midrange
|
85
|
+
end
|
86
|
+
|
87
|
+
operands
|
88
|
+
end
|
89
|
+
|
90
|
+
def convert_if_present(operands, name, bits)
|
91
|
+
operands[name] = convert_unsigned_to_signed(operands[name], bits) if operands[name]
|
92
|
+
end
|
93
|
+
|
94
|
+
def convert_unsigned_to_signed(unsigned, bits)
|
95
|
+
if unsigned >= (1 << (bits - 1))
|
96
|
+
unsigned - (1 << bits)
|
97
|
+
else
|
98
|
+
unsigned
|
99
|
+
end
|
30
100
|
end
|
101
|
+
|
31
102
|
end
|
32
|
-
end
|
103
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'pathname'
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
module RPicSim::Mplab
|
6
|
+
# This class helps find MPLAB X on the disk, add it to the Java class path
|
7
|
+
# so we can use it from JRuby, and figure out what version of MPLAB X we
|
8
|
+
# are using.
|
9
|
+
#
|
10
|
+
# It should not be confused with com.microchip.mplab.mdbcore.loader.Loader,
|
11
|
+
# which is used for loading program files.
|
12
|
+
class MplabLoader
|
13
|
+
include Singleton
|
14
|
+
|
15
|
+
# Adds all the needed MPLAB X jar files to the classpath so we can use the
|
16
|
+
# classes.
|
17
|
+
def load
|
18
|
+
%w{ mdbcore/modules/*.jar
|
19
|
+
mplablibs/modules/*.jar
|
20
|
+
mplablibs/modules/ext/*.jar
|
21
|
+
platform/lib/org-openide-util*.jar
|
22
|
+
platform/lib/org-openide-util.jar
|
23
|
+
mdbcore/modules/ext/org-openide-filesystems.jar
|
24
|
+
}.each do |pattern|
|
25
|
+
Dir.glob(jar_dir + pattern).each do |jar_file|
|
26
|
+
$CLASSPATH << jar_file
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Do a quick test to make sure we can load some MPLAB X classes.
|
31
|
+
# In case MPLAB X was uninstalled and its directory remains, this can provide
|
32
|
+
# a useful error message to the user.
|
33
|
+
begin
|
34
|
+
org.openide.util.Lookup
|
35
|
+
com.microchip.mplab.mdbcore.simulator.Simulator
|
36
|
+
rescue NameError
|
37
|
+
$stderr.puts "Failed to load MPLAB X classes.\n" +
|
38
|
+
"MPLAB X dir: #{dir}\nMPLAB X jar dir: #{jar_dir}\nClass path:\n" + $CLASSPATH.to_a.join("\n") + "\n\n"
|
39
|
+
raise
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a string like "1.95" representing the version of MPLAB X we are using.
|
44
|
+
# NOTE: You should probably NOT be calling this to work around flaws in MPLAB X.
|
45
|
+
# Instead, you should add a new entry in flaws.rb and then use
|
46
|
+
# RPicSim::Flaws[:FLAWNAME] to see if the flaw exists and choose the appropriate workaround.
|
47
|
+
def mplab_version
|
48
|
+
# This implementation is not pretty; I would prefer to just find the right function
|
49
|
+
# to call.
|
50
|
+
@mplab_version ||= begin
|
51
|
+
glob_pattern = 'Uninstall_MPLAB_X_IDE_v*'
|
52
|
+
paths = Dir.glob(dir + glob_pattern)
|
53
|
+
if paths.empty?
|
54
|
+
raise "Cannot detect MPLAB X version. No file matching #{glob_pattern} found in #{dir}."
|
55
|
+
end
|
56
|
+
matches = paths.map { |p| p.match(/IDE_v([0-9][0-9\.]*[0-9]+)\./) }.compact
|
57
|
+
match_data = matches.first
|
58
|
+
if !match_data
|
59
|
+
raise "Failed to get version number from #{paths.inspect}."
|
60
|
+
end
|
61
|
+
match_data[1]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Returns a Pathname object representing the directory of the MPLAB X we are using.
|
68
|
+
# This can either come from the +RPICSIM_MPLABX+ environment variable or it can
|
69
|
+
# be auto-detected by looking in the standard places that MPLAB X is installed.
|
70
|
+
# @return [Pathname]
|
71
|
+
def dir
|
72
|
+
@dir ||= begin
|
73
|
+
dir = ENV['RPICSIM_MPLABX'] || auto_detect_mplab_dir
|
74
|
+
raise "MPLABX directory does not exist: #{dir}" if !File.directory?(dir)
|
75
|
+
Pathname(dir)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def auto_detect_mplab_dir
|
80
|
+
# Default installation directories for MPLAB X:
|
81
|
+
candidates = [
|
82
|
+
'C:/Program Files (x86)/Microchip/MPLABX/', # 64-bit Windows
|
83
|
+
'C:/Program Files/Microchip/MPLABX/', # 32-bit Windows
|
84
|
+
'/opt/microchip/mplabx/', # Linux
|
85
|
+
'/Applications/microchip/mplabx/', # Mac OS X
|
86
|
+
]
|
87
|
+
dir = candidates.find { |d| File.directory?(d) }
|
88
|
+
raise cannot_find_mplab_error if !dir
|
89
|
+
dir
|
90
|
+
end
|
91
|
+
|
92
|
+
def cannot_find_mplab_error
|
93
|
+
'Cannot find MPLABX. Install it in the standard location or ' +
|
94
|
+
'set the RPICSIM_MPLABX environment variable to its full path.'
|
95
|
+
end
|
96
|
+
|
97
|
+
def jar_dir
|
98
|
+
@jar_dir ||= if (dir + 'mplab_ide.app').exist?
|
99
|
+
# Mac OS X
|
100
|
+
dir + 'mplab_ide.app/Contents/Resources/mplab_ide'
|
101
|
+
else
|
102
|
+
dir + 'mplab_ide'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -6,20 +6,33 @@ module RPicSim::Mplab
|
|
6
6
|
def initialize(memory)
|
7
7
|
@memory = memory
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
|
+
def read_byte(address)
|
11
|
+
array = [0].to_java(:byte)
|
12
|
+
@memory.Read(address, 1, array)
|
13
|
+
array.ubyte_get(0)
|
14
|
+
end
|
15
|
+
|
16
|
+
def write_byte(address, byte)
|
17
|
+
array = Java.byte[1].new
|
18
|
+
array.ubyte_set(0, byte)
|
19
|
+
@memory.Write(address, 1, array)
|
20
|
+
byte
|
21
|
+
end
|
22
|
+
|
10
23
|
def write_word(address, value)
|
11
24
|
@memory.WriteWord(address, value)
|
12
25
|
value
|
13
26
|
end
|
14
|
-
|
27
|
+
|
15
28
|
def read_word(address)
|
16
29
|
@memory.ReadWord(address)
|
17
30
|
end
|
18
|
-
|
19
|
-
def
|
31
|
+
|
32
|
+
def valid_address?(address)
|
20
33
|
@memory.IsValidAddress(address)
|
21
34
|
end
|
22
|
-
|
35
|
+
|
23
36
|
def on_change(&callback)
|
24
37
|
MplabObserver.new(@memory) do |event|
|
25
38
|
next if event.EventType != Mdbcore.memory::MemoryEvent::EVENTS::MEMORY_CHANGED
|
@@ -30,4 +43,4 @@ module RPicSim::Mplab
|
|
30
43
|
end
|
31
44
|
end
|
32
45
|
end
|
33
|
-
end
|
46
|
+
end
|
@@ -1,21 +1,21 @@
|
|
1
1
|
module RPicSim::Mplab
|
2
2
|
class MplabNmmrInfo
|
3
3
|
attr_reader :id
|
4
|
-
|
4
|
+
|
5
5
|
# @param id The id of the register. This is like an address.
|
6
6
|
# @param register [com.microchip.crownking.edc.Register]
|
7
7
|
def initialize(id, register)
|
8
8
|
@id = id
|
9
9
|
@register = register
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
# Returns how many bits the register has.
|
13
13
|
def width
|
14
14
|
@register.width
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def name
|
18
18
|
@register.name
|
19
19
|
end
|
20
20
|
end
|
21
|
-
end
|
21
|
+
end
|
@@ -1,12 +1,17 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module RPicSim::Mplab
|
2
|
+
# This class implements the com.microchip.mplab.util.observers.Observer
|
3
|
+
# interface, so we can easily receive events from objects that support
|
4
|
+
# sending updates to observers.
|
5
|
+
class MplabObserver
|
6
|
+
include Java::ComMicrochipMplabUtilObservers::Observer
|
3
7
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
def initialize(subject, &callback)
|
9
|
+
@callback = callback
|
10
|
+
subject.Attach(self, nil)
|
11
|
+
end
|
12
|
+
|
13
|
+
def Update(event)
|
14
|
+
@callback.call(event)
|
15
|
+
end
|
11
16
|
end
|
12
|
-
end
|
17
|
+
end
|
@@ -3,7 +3,7 @@ module RPicSim::Mplab
|
|
3
3
|
# Initializes a new Pin object to wrap the given PinPhysical.
|
4
4
|
# @param pin_physical [com.microchip.mplab.mdbcore.simulator.PinPhysical]
|
5
5
|
def initialize(pin_physical)
|
6
|
-
raise ArgumentError,
|
6
|
+
raise ArgumentError, 'pin_physical is nil' if pin_physical.nil?
|
7
7
|
@pin_physical = pin_physical
|
8
8
|
end
|
9
9
|
|
@@ -47,12 +47,12 @@ module RPicSim::Mplab
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def names
|
50
|
-
@pin_physical.
|
50
|
+
@pin_physical.map(&:name)
|
51
51
|
end
|
52
52
|
|
53
53
|
def name
|
54
54
|
@pin_physical.pinName
|
55
|
-
end
|
55
|
+
end
|
56
56
|
|
57
57
|
PinState = Mdbcore.simulator.Pin::PinState # HIGH or LOW
|
58
58
|
IoState = Mdbcore.simulator.Pin::IOState # INPUT or OUTPUT
|
@@ -6,25 +6,25 @@ module RPicSim::Mplab
|
|
6
6
|
def initialize(processor)
|
7
7
|
@processor = processor
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def get_pc
|
11
11
|
@processor.getPC
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def set_pc(value)
|
15
15
|
@processor.setPC(value)
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def get_sfr(name)
|
19
19
|
reg = @processor.getSFRSet.getSFR(name)
|
20
20
|
raise "Cannot find SFR named '#{name}'." if !reg
|
21
21
|
MplabRegister.new(reg)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def get_nmmr(name)
|
25
25
|
reg = @processor.getNMMRSet.getNMMR(name)
|
26
26
|
raise "Cannot find NMMR named '#{name}'." if !reg
|
27
27
|
MplabRegister.new(reg)
|
28
28
|
end
|
29
29
|
end
|
30
|
-
end
|
30
|
+
end
|