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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/Gemfile +7 -6
  4. data/Introduction.md +3 -1
  5. data/README.md +2 -2
  6. data/docs/ChangeLog.md +6 -0
  7. data/docs/Contributing.md +1 -1
  8. data/docs/DefiningSimulationClass.md +11 -10
  9. data/docs/HowMPLABXIsFound.md +1 -1
  10. data/docs/IntegrationTesting.md +1 -1
  11. data/docs/IntroductionToRSpec.md +8 -5
  12. data/docs/IntroductionToRuby.md +2 -2
  13. data/docs/KnownIssues.md +46 -57
  14. data/docs/Labels.md +5 -4
  15. data/docs/Manual.md +1 -1
  16. data/docs/Memories.md +70 -0
  17. data/docs/PersistentExpectations.md +31 -2
  18. data/docs/Pins.md +5 -7
  19. data/docs/PreventingCallStackOverflow.md +4 -6
  20. data/docs/QuickStartGuide.md +5 -5
  21. data/docs/RSpecIntegration.md +2 -2
  22. data/docs/RamWatcher.md +22 -11
  23. data/docs/Running.md +4 -6
  24. data/docs/Stubbing.md +4 -4
  25. data/docs/SupportedDevices.md +2 -11
  26. data/docs/SupportedMPLABXVersions.md +1 -0
  27. data/docs/SupportedOperatingSystems.md +3 -2
  28. data/docs/UnitTesting.md +1 -1
  29. data/docs/Variables.md +81 -25
  30. data/lib/rpicsim.rb +0 -12
  31. data/lib/rpicsim/call_stack_info.rb +43 -47
  32. data/lib/rpicsim/composite_memory.rb +53 -0
  33. data/lib/rpicsim/flaws.rb +34 -22
  34. data/lib/rpicsim/instruction.rb +204 -48
  35. data/lib/rpicsim/label.rb +4 -4
  36. data/lib/rpicsim/memory.rb +44 -23
  37. data/lib/rpicsim/memory_watcher.rb +14 -22
  38. data/lib/rpicsim/mplab.rb +38 -119
  39. data/lib/rpicsim/mplab/mplab_assembly.rb +23 -18
  40. data/lib/rpicsim/mplab/mplab_device_info.rb +9 -9
  41. data/lib/rpicsim/mplab/mplab_disassembler.rb +5 -6
  42. data/lib/rpicsim/mplab/mplab_instruction.rb +87 -16
  43. data/lib/rpicsim/mplab/mplab_loader.rb +106 -0
  44. data/lib/rpicsim/mplab/mplab_memory.rb +19 -6
  45. data/lib/rpicsim/mplab/mplab_nmmr_info.rb +4 -4
  46. data/lib/rpicsim/mplab/mplab_observer.rb +15 -10
  47. data/lib/rpicsim/mplab/mplab_pin.rb +3 -3
  48. data/lib/rpicsim/mplab/mplab_processor.rb +5 -5
  49. data/lib/rpicsim/mplab/mplab_program_file.rb +29 -17
  50. data/lib/rpicsim/mplab/mplab_register.rb +5 -5
  51. data/lib/rpicsim/mplab/mplab_sfr_info.rb +4 -4
  52. data/lib/rpicsim/mplab/mplab_simulator.rb +27 -30
  53. data/lib/rpicsim/pin.rb +6 -6
  54. data/lib/rpicsim/program_counter.rb +3 -3
  55. data/lib/rpicsim/program_file.rb +39 -81
  56. data/lib/rpicsim/rspec/be_predicate.rb +1 -1
  57. data/lib/rpicsim/rspec/helpers.rb +1 -1
  58. data/lib/rpicsim/rspec/persistent_expectations.rb +17 -2
  59. data/lib/rpicsim/rspec/sim_diagnostics.rb +5 -5
  60. data/lib/rpicsim/search.rb +1 -1
  61. data/lib/rpicsim/sim.rb +153 -228
  62. data/lib/rpicsim/stack_pointer.rb +41 -0
  63. data/lib/rpicsim/stack_trace.rb +1 -1
  64. data/lib/rpicsim/storage/memory_integer.rb +235 -0
  65. data/lib/rpicsim/{register.rb → storage/register.rb} +18 -18
  66. data/lib/rpicsim/variable.rb +25 -211
  67. data/lib/rpicsim/variable_set.rb +93 -0
  68. data/lib/rpicsim/version.rb +2 -2
  69. metadata +9 -4
  70. 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("/").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."
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
- # DeviceInfo is a wrapper for the MPLAB xPIC class which gives us information
6
- # about the target PIC device.
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 DeviceInfo object.
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).collect { |i| data_store.getPinDesc(i) }
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
- pins = (0...pin_set.getNumPins).collect do |i|
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 "MPLAB X failed to load any peripheral descriptions into the data store."
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 "MPLAB X failed to load any peripherals into the PeripheralSet."
92
+ raise 'MPLAB X failed to load any peripherals into the PeripheralSet.'
85
93
  end
86
94
  end
87
-
88
- # This is commented out because MPLAB X v2.00 with the PIC18F25K50
89
- # reports three missing peripherals:
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
@@ -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, "mplab_pin is nil" if mplab_pin.nil?
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
- "#<%s %s>" % [self.class, to_s]
58
+ '#<%s %s>' % [self.class, to_s]
59
59
  end
60
60
  end
61
61
  end
@@ -7,13 +7,13 @@ module RPicSim
7
7
  def initialize(processor)
8
8
  @processor = processor
9
9
  end
10
-
10
+
11
11
  def value
12
12
  @processor.get_pc
13
13
  end
14
-
14
+
15
15
  def value=(val)
16
16
  @processor.set_pc(val)
17
17
  end
18
18
  end
19
- end
19
+ end
@@ -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 var_addresses
28
- @var_addresses ||= @mplab_program_file.symbols_in_ram
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
- @mplab_program_file.symbols_in_code_space.each do |name, address|
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
- return label
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 : ("0x%04x" % [address])
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 &:address
64
-
80
+ label = reference_points.max_by(&:address)
81
+
65
82
  if label
66
83
  offset = address - label.address
67
- desc << " = " + label.name.to_s
68
- desc << "+%#x" % [offset] if offset != 0
84
+ desc << ' = ' + label.name.to_s
85
+ desc << '+%#x' % [offset] if offset != 0
69
86
  end
70
-
71
- return desc
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 << " MPASM truncates labels. You might have meant: " +
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