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