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
@@ -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
|