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
@@ -1,35 +1,47 @@
|
|
1
1
|
module RPicSim::Mplab
|
2
2
|
# This class creates and wraps a com.microchip.mplab.mdbcore.program.interfaces.IProgramFile
|
3
3
|
class MplabProgramFile
|
4
|
+
EepromRange = 0xF00000..0xFFFFFF
|
5
|
+
|
4
6
|
def initialize(filename, device)
|
5
7
|
raise "File does not exist: #{filename}" if !File.exist?(filename) # Avoid a Java exception.
|
6
|
-
|
7
|
-
if !File.realdirpath(filename).split(
|
8
|
-
raise
|
9
|
-
|
8
|
+
|
9
|
+
if !File.realdirpath(filename).split('/').include?('dist')
|
10
|
+
raise 'The file must be inside a directory named dist or else the MCLoader ' +
|
11
|
+
'class will throw an exception saying that it cannot find the COF file.'
|
10
12
|
end
|
11
|
-
|
12
|
-
factory = Lookup.getDefault.lookup(Mdbcore.program.spi.IProgramFileProviderFactory.java_class)
|
13
|
+
|
14
|
+
factory = Lookup.getDefault.lookup(Mdbcore.program.spi.IProgramFileProviderFactory.java_class)
|
13
15
|
@program_file = factory.getProvider(filename, device)
|
14
16
|
@program_file.Load
|
15
17
|
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
|
-
|
18
|
+
|
23
19
|
def symbols_in_ram
|
24
20
|
@symbols_in_ram ||= Hash[
|
25
21
|
symbols.select { |s| s.m_lType == 0 }.map { |s| [s.m_Symbol.to_sym, s.address] }
|
26
22
|
]
|
27
23
|
end
|
28
|
-
|
24
|
+
|
25
|
+
def symbols_in_program_memory
|
26
|
+
@symbols_in_code_space ||= Hash[
|
27
|
+
symbols
|
28
|
+
.select { |s| s.m_lType != 0 && !EepromRange.include?(s.address) }
|
29
|
+
.map { |s| [s.m_Symbol.to_sym, s.address] }
|
30
|
+
]
|
31
|
+
end
|
32
|
+
|
33
|
+
def symbols_in_eeprom
|
34
|
+
@symbols_in_eeprom ||= Hash[
|
35
|
+
symbols
|
36
|
+
.select { |s| s.m_lType != 0 && EepromRange.include?(s.address) }
|
37
|
+
.map { |s| [s.m_Symbol.to_sym, s.address - EepromRange.min] }
|
38
|
+
]
|
39
|
+
end
|
40
|
+
|
29
41
|
private
|
30
|
-
|
42
|
+
|
31
43
|
def symbols
|
32
44
|
@program_file.getSymbolTable.getSymbols(0, 0)
|
33
45
|
end
|
34
|
-
end
|
35
|
-
end
|
46
|
+
end
|
47
|
+
end
|
@@ -4,22 +4,22 @@ module RPicSim::Mplab
|
|
4
4
|
def initialize(register)
|
5
5
|
@register = register
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
def write(value)
|
9
9
|
@register.write(value)
|
10
10
|
value
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def read
|
14
14
|
@register.read
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def name
|
18
18
|
@register.getName
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def address
|
22
22
|
@register.getAddress
|
23
23
|
end
|
24
24
|
end
|
25
|
-
end
|
25
|
+
end
|
@@ -1,21 +1,21 @@
|
|
1
1
|
module RPicSim::Mplab
|
2
2
|
class MplabSfrInfo
|
3
3
|
attr_reader :address
|
4
|
-
|
4
|
+
|
5
5
|
# @param address The address of the register.
|
6
6
|
# @param register [com.microchip.crownking.edc.Register]
|
7
7
|
def initialize(address, register)
|
8
8
|
@address = address
|
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
|
@@ -2,10 +2,10 @@ require_relative 'mplab_memory'
|
|
2
2
|
require_relative 'mplab_processor'
|
3
3
|
|
4
4
|
module RPicSim::Mplab
|
5
|
-
#
|
6
|
-
#
|
5
|
+
# This class is a wrapper for thecom.microchip.mplab.mdbcore.simulator.Simulator
|
6
|
+
# class, which helps manage running a simulation.
|
7
7
|
class MplabSimulator
|
8
|
-
# Makes a new
|
8
|
+
# Makes a new MplabSimulator object.
|
9
9
|
# @param simulator [com.microchip.mplab.mdbcore.simulator.Simulator]
|
10
10
|
def initialize(simulator)
|
11
11
|
@simulator = simulator
|
@@ -18,7 +18,7 @@ module RPicSim::Mplab
|
|
18
18
|
def fr_memory
|
19
19
|
@fr_memory ||= MplabMemory.new data_store.getFileMemory
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def sfr_memory
|
23
23
|
@sfr_memory ||= MplabMemory.new data_store.getSFRMemory
|
24
24
|
end
|
@@ -26,25 +26,33 @@ module RPicSim::Mplab
|
|
26
26
|
def nmmr_memory
|
27
27
|
@nmmr_memory ||= MplabMemory.new data_store.getNMMRMemory
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def program_memory
|
31
31
|
@program_memory ||= MplabMemory.new data_store.getProgMemory
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
def stack_memory
|
35
35
|
@stack_memory ||= MplabMemory.new data_store.getStackMemory
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def test_memory
|
39
39
|
@test_memory ||= MplabMemory.new data_store.getTestMemory
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
|
+
def config_memory
|
43
|
+
@config_memory ||= MplabMemory.new data_store.getCFGMemory
|
44
|
+
end
|
45
|
+
|
46
|
+
def eeprom_memory
|
47
|
+
@eeprom_memory ||= MplabMemory.new data_store.getEEDataMemory
|
48
|
+
end
|
49
|
+
|
42
50
|
def processor
|
43
51
|
@processor ||= MplabProcessor.new data_store.getProcessor
|
44
52
|
end
|
45
|
-
|
53
|
+
|
46
54
|
def pins
|
47
|
-
pin_descs = (0...data_store.getNumPins).
|
55
|
+
pin_descs = (0...data_store.getNumPins).map { |i| data_store.getPinDesc(i) }
|
48
56
|
|
49
57
|
pin_set = data_store.getProcessor.getPinSet
|
50
58
|
|
@@ -56,11 +64,11 @@ module RPicSim::Mplab
|
|
56
64
|
pin_set.getPin name # Trigger the lazy loading.
|
57
65
|
end
|
58
66
|
|
59
|
-
|
67
|
+
(0...pin_set.getNumPins).map do |i|
|
60
68
|
MplabPin.new pin_set.getPin(i)
|
61
69
|
end
|
62
70
|
end
|
63
|
-
|
71
|
+
|
64
72
|
def check_peripherals
|
65
73
|
check_peripherals_in_data_store
|
66
74
|
check_peripheral_set
|
@@ -71,32 +79,21 @@ module RPicSim::Mplab
|
|
71
79
|
def data_store
|
72
80
|
@simulator.getDataStore
|
73
81
|
end
|
74
|
-
|
82
|
+
|
75
83
|
def check_peripherals_in_data_store
|
76
84
|
if data_store.getNumPeriphs == 0
|
77
|
-
raise
|
85
|
+
raise 'MPLAB X failed to load any peripheral descriptions into the data store.'
|
78
86
|
end
|
79
87
|
end
|
80
88
|
|
81
89
|
def check_peripheral_set
|
82
90
|
peripherals = data_store.getProcessor.getPeripheralSet
|
83
91
|
if peripherals.getNumPeripherals == 0
|
84
|
-
raise
|
92
|
+
raise 'MPLAB X failed to load any peripherals into the PeripheralSet.'
|
85
93
|
end
|
86
94
|
end
|
87
|
-
|
88
|
-
#
|
89
|
-
#
|
90
|
-
# PBADEN_PCFG
|
91
|
-
# config
|
92
|
-
# CONFIG3H.PBADEN
|
93
|
-
#
|
94
|
-
#def check_missing_peripherals
|
95
|
-
# peripherals = data_store.getProcessor.getPeripheralSet
|
96
|
-
# if peripherals.getMissingPeripherals.to_a.size > 0
|
97
|
-
# raise "This device has missing peripherals: " + peripherals.getMissingReasons().to_a.inspect
|
98
|
-
# end
|
99
|
-
#end
|
100
|
-
|
95
|
+
|
96
|
+
# Note: if you are troubleshooting missing peripherals, you could check:
|
97
|
+
# data_store.getProcessor.getPeripheralSet.getMissingPeripherals.to_a
|
101
98
|
end
|
102
|
-
end
|
99
|
+
end
|
data/lib/rpicsim/pin.rb
CHANGED
@@ -6,7 +6,7 @@ module RPicSim
|
|
6
6
|
# Initializes a new Pin object to wrap the given MplabPin.
|
7
7
|
# @param mplab_pin [Mplab::MplabPin]
|
8
8
|
def initialize(mplab_pin)
|
9
|
-
raise ArgumentError,
|
9
|
+
raise ArgumentError, 'mplab_pin is nil' if mplab_pin.nil?
|
10
10
|
@mplab_pin = mplab_pin
|
11
11
|
end
|
12
12
|
|
@@ -26,12 +26,12 @@ module RPicSim
|
|
26
26
|
def output?
|
27
27
|
@mplab_pin.output?
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
# Returns true if the pin is currently configured to be an input.
|
31
31
|
def input?
|
32
32
|
!@mplab_pin.output?
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
# Returns true if the pin is currently configured to be an output and
|
36
36
|
# it is driving high.
|
37
37
|
def driving_high?
|
@@ -49,13 +49,13 @@ module RPicSim
|
|
49
49
|
def names
|
50
50
|
@mplab_pin.names
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
def to_s
|
54
54
|
@mplab_pin.name
|
55
|
-
end
|
55
|
+
end
|
56
56
|
|
57
57
|
def inspect
|
58
|
-
|
58
|
+
'#<%s %s>' % [self.class, to_s]
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
data/lib/rpicsim/program_file.rb
CHANGED
@@ -7,33 +7,50 @@ module RPicSim
|
|
7
7
|
class ProgramFile
|
8
8
|
attr_reader :filename
|
9
9
|
attr_reader :device
|
10
|
-
|
10
|
+
|
11
|
+
attr_reader :address_increment
|
12
|
+
|
11
13
|
# @param filename [String] The path to the program file.
|
12
14
|
# @param device [String] The name of the device the file is for (e.g. "PIC10F322").
|
13
15
|
def initialize(filename, device)
|
14
16
|
@filename = filename
|
15
17
|
@device = device
|
18
|
+
|
16
19
|
@mplab_program_file = Mplab::MplabProgramFile.new(filename, device)
|
17
|
-
|
20
|
+
|
18
21
|
@assembly = Mplab::MplabAssembly.new(device)
|
19
22
|
@assembly.load_file(filename)
|
20
23
|
@address_increment = @assembly.device_info.code_address_increment
|
21
|
-
|
24
|
+
|
22
25
|
@instructions = []
|
23
26
|
end
|
24
|
-
|
27
|
+
|
25
28
|
# Returns a hash associating RAM variable names (as symbols) to their addresses.
|
26
29
|
# @return (Hash)
|
27
|
-
def
|
28
|
-
@
|
30
|
+
def symbols_in_ram
|
31
|
+
@mplab_program_file.symbols_in_ram
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns a hash associating program memory symbol names (as Ruby symbols)
|
35
|
+
# to their addresses.
|
36
|
+
# @return (Hash)
|
37
|
+
def symbols_in_program_memory
|
38
|
+
@mplab_program_file.symbols_in_program_memory
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a hash associating EEPROM memory symbol names (as Ruby symbols)
|
42
|
+
# to their addresses.
|
43
|
+
# @return (Hash)
|
44
|
+
def symbols_in_eeprom
|
45
|
+
@mplab_program_file.symbols_in_eeprom
|
29
46
|
end
|
30
|
-
|
47
|
+
|
31
48
|
# Returns a hash associating program memory label names (as symbols) to their addresses.
|
32
49
|
# @return (Hash)
|
33
50
|
def labels
|
34
51
|
@labels ||= begin
|
35
52
|
hash = {}
|
36
|
-
|
53
|
+
symbols_in_program_memory.each do |name, address|
|
37
54
|
hash[name] = Label.new(name, address)
|
38
55
|
end
|
39
56
|
hash
|
@@ -50,7 +67,7 @@ module RPicSim
|
|
50
67
|
if !label
|
51
68
|
raise ArgumentError, message_for_label_not_found(name)
|
52
69
|
end
|
53
|
-
|
70
|
+
label
|
54
71
|
end
|
55
72
|
|
56
73
|
# Generates a friendly human-readable string description of the given address in
|
@@ -58,19 +75,19 @@ module RPicSim
|
|
58
75
|
# @param address [Integer] An address in program memory.
|
59
76
|
# @return [String]
|
60
77
|
def address_description(address)
|
61
|
-
desc = address < 0 ? address.to_s : (
|
78
|
+
desc = address < 0 ? address.to_s : ('0x%04x' % [address])
|
62
79
|
reference_points = labels.values.reject { |label| label.address > address }
|
63
|
-
label = reference_points.max_by
|
64
|
-
|
80
|
+
label = reference_points.max_by(&:address)
|
81
|
+
|
65
82
|
if label
|
66
83
|
offset = address - label.address
|
67
|
-
desc <<
|
68
|
-
desc <<
|
84
|
+
desc << ' = ' + label.name.to_s
|
85
|
+
desc << '+%#x' % [offset] if offset != 0
|
69
86
|
end
|
70
|
-
|
71
|
-
|
87
|
+
|
88
|
+
desc
|
72
89
|
end
|
73
|
-
|
90
|
+
|
74
91
|
# Gets an {Instruction} object representing the PIC instruction at the given
|
75
92
|
# address in program memory.
|
76
93
|
# @param address [Integer]
|
@@ -78,7 +95,7 @@ module RPicSim
|
|
78
95
|
def instruction(address)
|
79
96
|
@instructions[address] ||= make_instruction(address)
|
80
97
|
end
|
81
|
-
|
98
|
+
|
82
99
|
private
|
83
100
|
def message_for_label_not_found(name)
|
84
101
|
message = "Cannot find label named '#{name}'."
|
@@ -87,74 +104,15 @@ module RPicSim
|
|
87
104
|
name.to_s.start_with?(label_sym.to_s)
|
88
105
|
end
|
89
106
|
if !maybe_intended_labels.empty?
|
90
|
-
message <<
|
91
|
-
maybe_intended_labels.join(
|
107
|
+
message << ' MPASM truncates labels. You might have meant: ' +
|
108
|
+
maybe_intended_labels.join(', ') + '.'
|
92
109
|
end
|
93
110
|
message
|
94
111
|
end
|
95
112
|
|
96
113
|
def make_instruction(address)
|
97
114
|
mplab_instruction = @assembly.disassembler.disassemble(address)
|
98
|
-
|
99
|
-
# Convert the increment, which is the number of bytes, into 'size',
|
100
|
-
# which is the same units as the flash address space.
|
101
|
-
if @address_increment == 1
|
102
|
-
# Non-PIC18 architectures: flash addresses are in terms of words
|
103
|
-
# so we devide by two to convert from bytes to words.
|
104
|
-
size = mplab_instruction.inc / 2
|
105
|
-
elsif @address_increment == 2
|
106
|
-
# PIC18 architecture: No change necessary because both are in terms
|
107
|
-
# of bytes.
|
108
|
-
size = mplab_instruction.inc
|
109
|
-
else
|
110
|
-
raise "Cannot handle address increment value of #{@address_increment}."
|
111
|
-
end
|
112
|
-
|
113
|
-
# TODO: add support for all other 8-bit PIC architectures
|
114
|
-
properties = Array case mplab_instruction.opcode
|
115
|
-
when 'ADDWF'
|
116
|
-
when 'ANDWF'
|
117
|
-
when 'CPFSEQ' then [:conditional_skip]
|
118
|
-
when 'CLRF'
|
119
|
-
when 'CLRW'
|
120
|
-
when 'COMF'
|
121
|
-
when 'DECF'
|
122
|
-
when 'DECFSZ' then [:conditional_skip]
|
123
|
-
when 'INCF'
|
124
|
-
when 'INCFSZ' then [:conditional_skip]
|
125
|
-
when 'IORWF'
|
126
|
-
when 'MOVWF'
|
127
|
-
when 'MOVF'
|
128
|
-
when 'NOP'
|
129
|
-
when 'RLF'
|
130
|
-
when 'RRF'
|
131
|
-
when 'SUBWF'
|
132
|
-
when 'SWAPF'
|
133
|
-
when 'XORWF'
|
134
|
-
when 'BCF'
|
135
|
-
when 'BSF'
|
136
|
-
when 'BTFSC' then [:conditional_skip]
|
137
|
-
when 'BTFSS' then [:conditional_skip]
|
138
|
-
when 'ADDLW'
|
139
|
-
when 'ANDLW'
|
140
|
-
when 'CALL' then [:call]
|
141
|
-
when 'CLRWDT'
|
142
|
-
when 'GOTO' then [:goto]
|
143
|
-
when 'IORLW'
|
144
|
-
when 'MOVLW'
|
145
|
-
when 'RETFIE' then [:return]
|
146
|
-
when 'RETLW' then [:return]
|
147
|
-
when 'RETURN' then [:return]
|
148
|
-
when 'SLEEP'
|
149
|
-
when 'XORLW'
|
150
|
-
else
|
151
|
-
raise "Unrecognized opcode #{mplab_instruction.opcode} " +
|
152
|
-
"(#{address_description(address)}, operands #{mplab_instruction.operands.inspect})."
|
153
|
-
end
|
154
|
-
|
155
|
-
Instruction.new(address, self, mplab_instruction.opcode,
|
156
|
-
mplab_instruction.operands, size, @address_increment,
|
157
|
-
mplab_instruction.instruction_string, properties)
|
115
|
+
Instruction.new(mplab_instruction, address, @address_increment, self)
|
158
116
|
end
|
159
117
|
end
|
160
|
-
end
|
118
|
+
end
|