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
@@ -13,7 +13,7 @@ module RPicSim
|
|
13
13
|
# integration with RSpec.
|
14
14
|
module Helpers
|
15
15
|
include RPicSim::RSpec::PersistentExpectations
|
16
|
-
|
16
|
+
|
17
17
|
# This attribute allows you to type +sim+ in your specs instead of +@sim+ to
|
18
18
|
# get access to the {RPicSim::Sim} instance which represents the simulation.
|
19
19
|
# You must call {#start_sim} before using +sim+.
|
@@ -45,9 +45,24 @@ module RPicSim
|
|
45
45
|
# expecting main_output_pin: be_driving_high
|
46
46
|
#
|
47
47
|
# To remove an expectation on an object, just provide +nil+ for the matcher.
|
48
|
+
#
|
49
|
+
# If given a block, applies the new expectations, executes the
|
50
|
+
# block, then resets expectations to their former state.
|
51
|
+
# Expectation blocks may be nested and freely mixed with other
|
52
|
+
# calls to `expecting`.
|
48
53
|
def expecting(hash)
|
49
|
-
|
54
|
+
if block_given?
|
55
|
+
saved_expectations = expectations.clone
|
56
|
+
begin
|
57
|
+
expecting(hash)
|
58
|
+
yield
|
59
|
+
ensure
|
60
|
+
@expectations = saved_expectations
|
61
|
+
end
|
62
|
+
else
|
63
|
+
expectations.merge! hash
|
64
|
+
end
|
50
65
|
end
|
51
66
|
end
|
52
67
|
end
|
53
|
-
end
|
68
|
+
end
|
@@ -21,7 +21,7 @@ RSpec::Core::Formatters::BaseTextFormatter
|
|
21
21
|
# different functions so you can easily customize any of them without messing up
|
22
22
|
# the other ones.
|
23
23
|
class RSpec::Core::Formatters::BaseTextFormatter
|
24
|
-
|
24
|
+
alias_method :dump_backtrace_without_sim_diagnostics, :dump_backtrace
|
25
25
|
|
26
26
|
def dump_backtrace(example)
|
27
27
|
dump_backtrace_without_sim_diagnostics(example)
|
@@ -35,7 +35,7 @@ class RSpec::Core::Formatters::BaseTextFormatter
|
|
35
35
|
|
36
36
|
def dump_sim_cycle_count(example)
|
37
37
|
cycle_count = example.metadata[:sim_cycle_count] or return
|
38
|
-
output.puts
|
38
|
+
output.puts
|
39
39
|
output.printf long_padding + "Simulation cycle count: %d\n", cycle_count
|
40
40
|
end
|
41
41
|
|
@@ -44,8 +44,8 @@ class RSpec::Core::Formatters::BaseTextFormatter
|
|
44
44
|
# appropriate indentation.
|
45
45
|
def dump_sim_stack_trace(example)
|
46
46
|
sim_stack_trace = example.metadata[:sim_stack_trace] or return
|
47
|
-
output.puts
|
48
|
-
output.puts long_padding +
|
47
|
+
output.puts
|
48
|
+
output.puts long_padding + 'Simulation stack trace:'
|
49
49
|
sim_stack_trace.output(output, long_padding)
|
50
50
|
end
|
51
|
-
end
|
51
|
+
end
|
data/lib/rpicsim/search.rb
CHANGED
data/lib/rpicsim/sim.rb
CHANGED
@@ -1,47 +1,42 @@
|
|
1
|
-
# TODO: for performance, consider making people explicitly enable (or construct?) the ram_watcher
|
2
|
-
# before they can use it. See if this would affect the run time of rcs03a specs before committing to it.
|
3
|
-
|
4
1
|
require 'forwardable'
|
5
2
|
|
6
3
|
require_relative 'mplab'
|
7
4
|
require_relative 'flaws'
|
8
5
|
require_relative 'pin'
|
9
6
|
require_relative 'memory'
|
10
|
-
require_relative '
|
11
|
-
require_relative '
|
7
|
+
require_relative 'composite_memory'
|
8
|
+
require_relative 'storage/register'
|
9
|
+
require_relative 'variable_set'
|
12
10
|
require_relative 'program_counter'
|
13
11
|
require_relative 'label'
|
14
12
|
require_relative 'memory_watcher'
|
15
13
|
require_relative 'program_file'
|
14
|
+
require_relative 'stack_pointer'
|
16
15
|
require_relative 'stack_trace'
|
17
16
|
|
18
17
|
module RPicSim
|
19
18
|
# This class represents a PIC microcontroller simulation.
|
20
19
|
# This class keeps track of the state of the simulation and provides methods for
|
21
20
|
# running the simulation, reading the state, and changing the state.
|
22
|
-
# This the main class of RPicSim.
|
23
21
|
class Sim
|
24
22
|
|
25
23
|
# These methods should be called while defining a subclass of {Sim}.
|
26
24
|
module ClassDefinitionMethods
|
27
25
|
|
28
|
-
# Specifies what exact device the firmware runs on.
|
29
|
-
# from the COF file instead of requiring it to be specified in subclasses of {Sim}, but
|
30
|
-
# MPLAB X classes do not seem to make that easy.
|
26
|
+
# Specifies what exact device the firmware runs on.
|
31
27
|
# @param device [String] The device name, for example "PIC10F322".
|
32
|
-
def
|
28
|
+
def use_device(device)
|
33
29
|
@device = device
|
34
30
|
@assembly = Mplab::MplabAssembly.new(device)
|
35
|
-
@code_word_max_value = @assembly.device_info.code_word_max_value
|
36
31
|
end
|
37
32
|
|
38
33
|
# Specifies the path to the firmware file. The file can be a HEX or COF file, but
|
39
34
|
# COF is recommended so that you can access label addresses and other debugging information.
|
40
|
-
# You must call {#
|
41
|
-
def
|
42
|
-
raise "
|
35
|
+
# You must call {#use_device} before calling this.
|
36
|
+
def use_file(filename)
|
37
|
+
raise "The device needs to be specified before filename (e.g. 'use_device \"PIC10F322\"')" unless @device
|
43
38
|
@filename = filename
|
44
|
-
|
39
|
+
load_program_file
|
45
40
|
end
|
46
41
|
|
47
42
|
# Define a pin alias.
|
@@ -58,17 +53,19 @@ module RPicSim
|
|
58
53
|
self::Shortcuts.send(:define_method, our_name) { pin our_name }
|
59
54
|
end
|
60
55
|
|
61
|
-
# Define a
|
56
|
+
# Define a variable.
|
62
57
|
# @param name [Symbol] Specifies what you would like to call the variable.
|
63
58
|
# A method with this name will be added to your class's +Shortcuts+ module so it
|
64
59
|
# is available as a method on instances of your class and also in your RSpec tests.
|
65
60
|
# The method will return a {Variable} object that you can use to read or write the
|
66
61
|
# value of the actual variable in the simulation.
|
67
62
|
# @param type [Symbol] Specifies how to interpret the data in the variable and its size.
|
68
|
-
# For integers, it should be one of +:
|
63
|
+
# For integers, it should be one of +:uint8+, +:int8+, +:uint16+, +:int16+, +:uint24+, +:int24+, +:uint32+, +:int32+, or +:word+.
|
69
64
|
# The +s+ stands for signed and the +u+ stands for unsigned, and the number stands for the number
|
70
65
|
# of bits. All multi-byte integers are considered to be little Endian.
|
71
66
|
# @param opts [Hash] Specifies additional options. The options are:
|
67
|
+
# * +:memory+: Specifies the memory that the variable lives in.
|
68
|
+
# Valid values are +:ram+ (default), +:eeprom+, and +:program_memory+.
|
72
69
|
# * +:symbol+: By default, we look for a symbol with the same name as the variable and
|
73
70
|
# use that as the location of the variable. This option lets you specify a different
|
74
71
|
# symbol to look for in the firmware, so you could call the variable one thing in your
|
@@ -76,98 +73,15 @@ module RPicSim
|
|
76
73
|
# This option is ignored if +:address is specified.
|
77
74
|
# * +:address+: An integer to use as the address of the variable.
|
78
75
|
def def_var(name, type, opts={})
|
79
|
-
|
80
|
-
|
81
|
-
if !invalid_keys.empty?
|
82
|
-
raise ArgumentError, "Unrecognized options: #{invalid_keys.join(", ")}"
|
83
|
-
end
|
84
|
-
|
85
|
-
name = name.to_sym
|
86
|
-
|
87
|
-
if opts[:address]
|
88
|
-
address = opts[:address].to_i
|
89
|
-
else
|
90
|
-
symbol = (opts[:symbol] || name).to_sym
|
91
|
-
if symbol.to_s.include?('@')
|
92
|
-
raise "Limitations in Microchip's code prevent us from accessing " +
|
93
|
-
"variables with '@' in the name like '#{symbol}'"
|
94
|
-
end
|
95
|
-
address = @var_address[symbol] or raise "Cannot find variable named '#{symbol}'."
|
76
|
+
if @variable_set.nil?
|
77
|
+
raise 'The device and filename need to be specified before defining variables.'
|
96
78
|
end
|
97
79
|
|
98
|
-
|
99
|
-
when Class then type
|
100
|
-
when :u8 then VariableU8
|
101
|
-
when :s8 then VariableS8
|
102
|
-
when :u16 then VariableU16
|
103
|
-
when :s16 then VariableS16
|
104
|
-
when :u24 then VariableU24
|
105
|
-
when :s24 then VariableS24
|
106
|
-
when :u32 then VariableU32
|
107
|
-
when :s32 then VariableS32
|
108
|
-
else raise "Unknown type '#{type}'."
|
109
|
-
end
|
110
|
-
|
111
|
-
variable = klass.new(name, address)
|
112
|
-
variable.addresses.each do |address|
|
113
|
-
if @vars_by_address[address]
|
114
|
-
raise "Variable %s overlaps with %s at 0x%x" %
|
115
|
-
[variable, @vars_by_address[address], address]
|
116
|
-
end
|
117
|
-
@vars_by_address[address] = variable
|
118
|
-
end
|
119
|
-
@vars[name] = variable
|
80
|
+
@variable_set.def_var(name, type, opts)
|
120
81
|
|
121
82
|
self::Shortcuts.send(:define_method, name) { var name }
|
122
83
|
end
|
123
84
|
|
124
|
-
# Define a flash (program memory or user ID) variable.
|
125
|
-
# @param name [Symbol] Specifies what you would like to call the variable.
|
126
|
-
# A method with this name will be added to your class's +Shortcuts+ module so it
|
127
|
-
# is available as a method on instances of your class and also in your RSpec tests.
|
128
|
-
# The method will return a {Variable} object that you can use to read or write the
|
129
|
-
# value of the actual variable in the simulation.
|
130
|
-
# @param type [Symbol] Specifies how to interpret the data in the variable and its size.
|
131
|
-
# The only supported option current is +:word+, which represents a full word of flash.
|
132
|
-
# @param opts [Hash] Specifies additional options. The options are:
|
133
|
-
# * +:symbol+: By default, we look for a symbol with the same name as the variable and
|
134
|
-
# use that as the location of the variable. This option lets you specify a different
|
135
|
-
# symbol to look for in the firmware.
|
136
|
-
# * +:address+: An integer to use as the address of the variable.
|
137
|
-
def def_flash_var(name, type, opts={})
|
138
|
-
allowed_keys = [:symbol, :address]
|
139
|
-
invalid_keys = opts.keys - allowed_keys
|
140
|
-
if !invalid_keys.empty?
|
141
|
-
raise ArgumentError, "Unrecognized options: #{invalid_keys.join(", ")}"
|
142
|
-
end
|
143
|
-
|
144
|
-
name = name.to_sym
|
145
|
-
|
146
|
-
if opts[:address]
|
147
|
-
address = opts[:address].to_i
|
148
|
-
else
|
149
|
-
symbol = (opts[:symbol] || name).to_sym
|
150
|
-
if symbol.to_s.include?('@')
|
151
|
-
raise "Limitations in Microchip's code prevent us from accessing " +
|
152
|
-
"variables with '@' in the name like '#{symbol}'"
|
153
|
-
end
|
154
|
-
label = labels[symbol] or raise "Could not find label named '#{symbol}'."
|
155
|
-
address = label.address
|
156
|
-
end
|
157
|
-
|
158
|
-
klass = case type
|
159
|
-
when Class then type
|
160
|
-
when :word then VariableWord
|
161
|
-
else raise "Unknown type '#{type}'."
|
162
|
-
end
|
163
|
-
|
164
|
-
variable = klass.new(name, address)
|
165
|
-
variable.max_value = @code_word_max_value
|
166
|
-
@flash_vars[name] = variable
|
167
|
-
|
168
|
-
self::Shortcuts.send(:define_method, name) { flash_var name }
|
169
|
-
end
|
170
|
-
|
171
85
|
end
|
172
86
|
|
173
87
|
# These are class methods that you can call on subclasses of {Sim}.
|
@@ -183,13 +97,9 @@ module RPicSim
|
|
183
97
|
# pin names (like :RB3). These aliases are defined by {ClassDefinitionMethods#def_pin}.
|
184
98
|
attr_reader :pin_aliases
|
185
99
|
|
186
|
-
# A
|
187
|
-
#
|
188
|
-
attr_reader :
|
189
|
-
|
190
|
-
# A hash that associates flash variable names to (unbound) {Variable} objects.
|
191
|
-
# The variables are defined by {ClassDefinitionMethods#def_flash_var}.
|
192
|
-
attr_reader :flash_vars
|
100
|
+
# A {VariableSet} that holds information about all the variables that were defined
|
101
|
+
# with {ClassDefinitionMethods#def_var def_var}.
|
102
|
+
attr_reader :variable_set
|
193
103
|
|
194
104
|
# A hash that associates label names as symbols to {Label} objects.
|
195
105
|
attr_reader :labels
|
@@ -202,19 +112,20 @@ module RPicSim
|
|
202
112
|
def inherited(subclass)
|
203
113
|
subclass.instance_eval do
|
204
114
|
@pin_aliases = {}
|
205
|
-
@vars = {}
|
206
|
-
@vars_by_address = {}
|
207
|
-
@flash_vars = {}
|
208
115
|
const_set :Shortcuts, Module.new
|
209
116
|
include self::Shortcuts
|
210
117
|
end
|
211
|
-
|
212
118
|
end
|
213
119
|
|
214
|
-
def
|
120
|
+
def load_program_file
|
215
121
|
@program_file = ProgramFile.new(@filename, @device)
|
216
|
-
@var_address = program_file.var_addresses
|
217
122
|
@labels = program_file.labels
|
123
|
+
|
124
|
+
@variable_set = VariableSet.new
|
125
|
+
@variable_set.address_increment = program_file.address_increment
|
126
|
+
@variable_set.def_memory_type :ram, program_file.symbols_in_ram
|
127
|
+
@variable_set.def_memory_type :program_memory, program_file.symbols_in_program_memory
|
128
|
+
@variable_set.def_memory_type :eeprom, program_file.symbols_in_eeprom
|
218
129
|
end
|
219
130
|
|
220
131
|
end
|
@@ -231,26 +142,31 @@ module RPicSim
|
|
231
142
|
# instead of +sim.cycle_count+.
|
232
143
|
ForwardedMethods = [
|
233
144
|
:cycle_count,
|
145
|
+
:eeprom,
|
234
146
|
:every_step,
|
235
|
-
:flash_var,
|
236
147
|
:goto,
|
237
148
|
:label,
|
238
149
|
:location_address,
|
239
|
-
:
|
150
|
+
:new_ram_watcher,
|
240
151
|
:pc,
|
241
152
|
:pc_description,
|
242
153
|
:pin,
|
243
|
-
:
|
154
|
+
:program_file,
|
155
|
+
:program_memory,
|
156
|
+
:ram,
|
244
157
|
:run_cycles,
|
245
158
|
:run_steps,
|
246
159
|
:run_subroutine,
|
247
160
|
:run_to,
|
248
161
|
:run_to_cycle_count,
|
249
|
-
:
|
250
|
-
:
|
162
|
+
:reg,
|
163
|
+
:stack_contents,
|
164
|
+
:stack_push,
|
165
|
+
:stack_trace,
|
251
166
|
:step,
|
252
167
|
:var,
|
253
168
|
:wreg,
|
169
|
+
:stack_pointer,
|
254
170
|
:stkptr,
|
255
171
|
]
|
256
172
|
|
@@ -265,20 +181,40 @@ module RPicSim
|
|
265
181
|
# @return [RPicSim::ProgramCounter]
|
266
182
|
attr_reader :pc
|
267
183
|
|
268
|
-
# Returns a {
|
269
|
-
# @return [MemoryWatcher]
|
270
|
-
attr_reader :ram_watcher
|
271
|
-
|
272
|
-
# Returns a {Register} object corresponding to WREG. You can use this
|
184
|
+
# Returns a {Variable} object corresponding to WREG. You can use this
|
273
185
|
# to read and write the value of the W register.
|
274
186
|
# @return [Register]
|
275
187
|
attr_reader :wreg
|
276
188
|
|
277
|
-
# Returns a {
|
278
|
-
# this to read and write the value of the stack pointer.
|
189
|
+
# Returns a {Variable} object corresponding to the stack pointer register.
|
190
|
+
# You can use this to read and write the value of the stack pointer.
|
279
191
|
# @return [Register]
|
280
192
|
attr_reader :stkptr
|
281
193
|
|
194
|
+
# Returns a {StackPointer} object that is like {#stkptr} but it works
|
195
|
+
# consistently across all PIC devices. The initial value is always 0
|
196
|
+
# when the stack is empty and it points to the first unused space in
|
197
|
+
# the stack.
|
198
|
+
# @return [StackPointer]
|
199
|
+
attr_reader :stack_pointer
|
200
|
+
|
201
|
+
# Returns a {Memory} object that allows direct reading and writing of the
|
202
|
+
# bytes in the simulated RAM.
|
203
|
+
# @return [Memory]
|
204
|
+
attr_reader :ram
|
205
|
+
|
206
|
+
# Returns a {Memory} object that allows direct reading and writing of the
|
207
|
+
# data in the program memory.
|
208
|
+
# Besides the main program, the program memory also contains the
|
209
|
+
# configuration words and the user IDs.
|
210
|
+
# @return [Memory]
|
211
|
+
attr_reader :program_memory
|
212
|
+
|
213
|
+
# Returns a {Memory} object that allows direct reading and writing of the
|
214
|
+
# bytes in the simulated EEPROM.
|
215
|
+
# @return [Memory]
|
216
|
+
attr_reader :eeprom
|
217
|
+
|
282
218
|
# Returns a string like "PIC10F322" specifying the PIC device number.
|
283
219
|
# @return [String]
|
284
220
|
def device; self.class.device; end
|
@@ -294,33 +230,39 @@ module RPicSim
|
|
294
230
|
@simulator = @assembly.simulator
|
295
231
|
@processor = @simulator.processor
|
296
232
|
|
297
|
-
|
298
|
-
@fr_memory = Memory.new @simulator.fr_memory
|
299
|
-
@sfr_memory = Memory.new @simulator.sfr_memory
|
300
|
-
@nmmr_memory = Memory.new @simulator.nmmr_memory
|
301
|
-
@program_memory = Memory.new @simulator.program_memory
|
302
|
-
@stack_memory = Memory.new @simulator.stack_memory
|
303
|
-
@test_memory = Memory.new @simulator.test_memory
|
304
|
-
|
305
|
-
@pc = ProgramCounter.new(@simulator.processor)
|
306
|
-
|
307
|
-
@step_callbacks = []
|
308
|
-
|
233
|
+
initialize_memories
|
309
234
|
initialize_pins
|
310
235
|
initialize_sfrs_and_nmmrs
|
311
236
|
initialize_vars
|
312
|
-
initialize_flash_vars
|
313
237
|
|
314
|
-
@
|
238
|
+
@pc = ProgramCounter.new @simulator.processor
|
239
|
+
|
240
|
+
@step_callbacks = []
|
241
|
+
|
242
|
+
@stack_pointer = StackPointer.new(stkptr)
|
315
243
|
end
|
316
244
|
|
317
245
|
private
|
318
246
|
|
247
|
+
def initialize_memories
|
248
|
+
# Set up our stores and helper objects.
|
249
|
+
@ram = Memory.new @simulator.fr_memory
|
250
|
+
@eeprom = Memory.new @simulator.eeprom_memory
|
251
|
+
@sfr_memory = Memory.new @simulator.sfr_memory
|
252
|
+
@nmmr_memory = Memory.new @simulator.nmmr_memory
|
253
|
+
@stack_memory = Memory.new @simulator.stack_memory
|
254
|
+
|
255
|
+
# config_memory must be before test_memory, because test_memory provides
|
256
|
+
# bad values for the configuration words.
|
257
|
+
@program_memory = Memory.new CompositeMemory.new [
|
258
|
+
@simulator.program_memory,
|
259
|
+
@simulator.config_memory,
|
260
|
+
@simulator.test_memory,
|
261
|
+
]
|
262
|
+
end
|
263
|
+
|
319
264
|
def initialize_pins
|
320
|
-
pins = @simulator.pins.
|
321
|
-
|
322
|
-
#pins.reject! { |p| p.to_s == "VDD" } or raise "Failed to filter out VDD pin."
|
323
|
-
#pins.reject! { |p| p.to_s == "VSS" } or raise "Failed to filter out VSS pin."
|
265
|
+
pins = @simulator.pins.map { |mplab_pin| Pin.new(mplab_pin) }
|
324
266
|
|
325
267
|
@pins_by_name = {}
|
326
268
|
pins.each do |pin|
|
@@ -330,45 +272,32 @@ module RPicSim
|
|
330
272
|
end
|
331
273
|
|
332
274
|
self.class.pin_aliases.each do |our_name, datasheet_name|
|
333
|
-
|
275
|
+
@pins_by_name[our_name] = @pins_by_name[datasheet_name] or raise "Pin #{datasheet_name} not found."
|
334
276
|
end
|
335
277
|
end
|
336
278
|
|
337
279
|
def initialize_vars
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
def initialize_flash_vars
|
345
|
-
@flash_vars = {}
|
346
|
-
memories = [@program_memory, @test_memory]
|
347
|
-
self.class.flash_vars.each do |name, unbound_var|
|
348
|
-
possible_memories = memories.select { |m| m.is_valid_address?(unbound_var.address) }
|
349
|
-
if possible_memories.empty?
|
350
|
-
raise "Flash variable has an invalid address: #{unbound_var.inspect}"
|
351
|
-
elsif possible_memories.size > 1
|
352
|
-
raise "Flash variable's address is valid in both program memory and test memory. Not sure which memory to use: #{unbound_var.inspect}."
|
353
|
-
end
|
354
|
-
|
355
|
-
@flash_vars[name] = unbound_var.bind(possible_memories.first)
|
356
|
-
end
|
280
|
+
memories = {
|
281
|
+
ram: ram,
|
282
|
+
program_memory: program_memory,
|
283
|
+
eeprom: eeprom,
|
284
|
+
}
|
285
|
+
@vars = self.class.variable_set.bind(memories)
|
357
286
|
end
|
358
287
|
|
359
288
|
def initialize_sfrs_and_nmmrs
|
360
289
|
@sfrs = {}
|
361
290
|
@assembly.device_info.sfrs.each do |sfr|
|
362
|
-
@sfrs[sfr.name.to_sym] = Register.new @processor.get_sfr(sfr.name), @sfr_memory, sfr.width
|
291
|
+
@sfrs[sfr.name.to_sym] = Variable.new Storage::Register.new @processor.get_sfr(sfr.name), @sfr_memory, sfr.width
|
363
292
|
end
|
364
|
-
|
293
|
+
|
365
294
|
@nmmrs = {}
|
366
295
|
@assembly.device_info.nmmrs.each do |nmmr|
|
367
|
-
@nmmrs[nmmr.name.to_sym] = Register.new @processor.get_nmmr(nmmr.name), @nmmr_memory, nmmr.width
|
296
|
+
@nmmrs[nmmr.name.to_sym] = Variable.new Storage::Register.new @processor.get_nmmr(nmmr.name), @nmmr_memory, nmmr.width
|
368
297
|
end
|
369
298
|
|
370
|
-
@wreg =
|
371
|
-
@stkptr =
|
299
|
+
@wreg = reg(:WREG)
|
300
|
+
@stkptr = reg(:STKPTR)
|
372
301
|
end
|
373
302
|
|
374
303
|
public
|
@@ -381,51 +310,29 @@ module RPicSim
|
|
381
310
|
@pins_by_name[name.to_sym] or raise ArgumentError, "Cannot find pin named '#{name}'."
|
382
311
|
end
|
383
312
|
|
384
|
-
# Returns a {
|
385
|
-
#
|
386
|
-
#
|
387
|
-
# @return [Register]
|
388
|
-
def sfr(name)
|
389
|
-
@sfrs[name.to_sym] or raise ArgumentError, "Cannot find SFR named '#{name}'."
|
390
|
-
end
|
391
|
-
|
392
|
-
# Returns a {Register} object if an SFR or NMMR by that name is found,
|
393
|
-
# or raises an exception.
|
313
|
+
# Returns a {Variable} object if a Special Function Register (SFR) or
|
314
|
+
# Non-Memory-Mapped Register (NMMR) by that name is found.
|
315
|
+
# If the register cannot be found, this method raises an exception.
|
394
316
|
# @param name [Symbol] The name from the datasheet.
|
395
317
|
# @return [Register]
|
396
|
-
def
|
318
|
+
def reg(name)
|
397
319
|
name = name.to_sym
|
398
320
|
@sfrs[name] || @nmmrs[name] or raise ArgumentError, "Cannot find SFR or NMMR named '#{name}'."
|
399
321
|
end
|
400
322
|
|
401
|
-
# Returns a {
|
402
|
-
#
|
403
|
-
# @param name [Symbol] The name from the datasheet.
|
404
|
-
# @return [Register]
|
405
|
-
def nmmr(name)
|
406
|
-
@nmmrs[name.to_sym] or raise ArgumentError, "Cannot find NMMR named '#{name}'."
|
407
|
-
end
|
408
|
-
|
409
|
-
# Returns a {Variable} object if a RAM variable by that name is found,
|
410
|
-
# or raises an exception.
|
323
|
+
# Returns a {Variable} object if a variable by that name is found.
|
324
|
+
# If the variable cannot be found, this method raises an exception.
|
411
325
|
# @return [Variable]
|
412
326
|
def var(name)
|
413
327
|
@vars[name.to_sym] or raise ArgumentError, "Cannot find var named '#{name}'."
|
414
328
|
end
|
415
329
|
|
416
|
-
# Returns a {Variable} object if a flash (program memory) variable by that name is found,
|
417
|
-
# or raises an exception.
|
418
|
-
# @return [Variable]
|
419
|
-
def flash_var(name)
|
420
|
-
@flash_vars[name.to_sym] or raise ArgumentError, "Cannot find flash var named '#{name}'."
|
421
|
-
end
|
422
|
-
|
423
330
|
# Returns a {Label} object if a program label by that name is found.
|
424
331
|
# The name is specified in the code that defined the label. If you are using a C compiler,
|
425
332
|
# you will probably need to prefix the name with an underscore.
|
426
333
|
# @return [Label]
|
427
334
|
def label(name)
|
428
|
-
|
335
|
+
program_file.label(name)
|
429
336
|
end
|
430
337
|
|
431
338
|
# Returns the number of instruction cycles simulated in this simulation.
|
@@ -488,10 +395,10 @@ module RPicSim
|
|
488
395
|
def run_to(conditions, opts={})
|
489
396
|
conditions = Array(conditions)
|
490
397
|
if conditions.empty?
|
491
|
-
raise ArgumentError,
|
398
|
+
raise ArgumentError, 'Must specify at least one condition.'
|
492
399
|
end
|
493
400
|
|
494
|
-
condition_procs = conditions.
|
401
|
+
condition_procs = conditions.map(&method(:convert_condition_to_proc))
|
495
402
|
|
496
403
|
allowed_keys = [:cycle_limit, :cycles]
|
497
404
|
invalid_keys = opts.keys - allowed_keys
|
@@ -500,7 +407,7 @@ module RPicSim
|
|
500
407
|
end
|
501
408
|
|
502
409
|
if opts[:cycles] && opts[:cycle_limit]
|
503
|
-
raise ArgumentError,
|
410
|
+
raise ArgumentError, 'Cannot specify both :cycles and :cycle_limit.'
|
504
411
|
end
|
505
412
|
|
506
413
|
start_cycle = cycle_count
|
@@ -514,7 +421,7 @@ module RPicSim
|
|
514
421
|
end
|
515
422
|
|
516
423
|
# Loop as long as none of the conditions are satisfied.
|
517
|
-
while !(met_condition_index = condition_procs.find_index
|
424
|
+
while !(met_condition_index = condition_procs.find_index(&:call))
|
518
425
|
if max_cycle && cycle_count >= max_cycle
|
519
426
|
raise "Failed to reach #{conditions.inspect} after #{cycle_count - start_cycle} cycles."
|
520
427
|
end
|
@@ -530,7 +437,7 @@ module RPicSim
|
|
530
437
|
end
|
531
438
|
|
532
439
|
# Return the argument that specified the condition that was satisfied.
|
533
|
-
|
440
|
+
met_condition
|
534
441
|
end
|
535
442
|
|
536
443
|
# Gets the address of the specified location in program memory.
|
@@ -587,9 +494,7 @@ module RPicSim
|
|
587
494
|
# given cycle count.
|
588
495
|
# @param count [Integer]
|
589
496
|
def run_to_cycle_count(count)
|
590
|
-
while cycle_count < count
|
591
|
-
step
|
592
|
-
end
|
497
|
+
step while cycle_count < count
|
593
498
|
end
|
594
499
|
|
595
500
|
# Simulates a return instruction being executed by popping the top value off
|
@@ -597,35 +502,35 @@ module RPicSim
|
|
597
502
|
# This can be useful for speeding up your tests when you have a very slow function
|
598
503
|
# and just want to skip it.
|
599
504
|
def return
|
600
|
-
if
|
601
|
-
raise
|
505
|
+
if stack_pointer.value == 0
|
506
|
+
raise 'Cannot return because stack is empty.'
|
602
507
|
end
|
603
508
|
|
604
509
|
# Simulate popping the stack.
|
605
|
-
|
606
|
-
pc.value = @stack_memory.read_word(
|
510
|
+
stack_pointer.value -= 1
|
511
|
+
pc.value = @stack_memory.read_word(stack_pointer.value)
|
607
512
|
end
|
608
513
|
|
609
514
|
# Generates a friendly human-readable string description of where the
|
610
515
|
# program counter is currently using the symbol table.
|
611
516
|
def pc_description
|
612
|
-
|
517
|
+
program_file.address_description(pc.value)
|
613
518
|
end
|
614
519
|
|
615
520
|
# Pushes the given address onto the simulated call stack.
|
616
521
|
def stack_push(value)
|
617
|
-
if !@stack_memory.
|
618
|
-
raise "Simulated stack is full (stack pointer = #{
|
522
|
+
if !@stack_memory.valid_address?(stack_pointer.value)
|
523
|
+
raise "Simulated stack is full (stack pointer = #{stack_pointer.value})."
|
619
524
|
end
|
620
525
|
|
621
|
-
@stack_memory.write_word(
|
622
|
-
|
526
|
+
@stack_memory.write_word(stack_pointer.value, value)
|
527
|
+
stack_pointer.value += 1
|
623
528
|
end
|
624
529
|
|
625
530
|
# Gets the contents of the stack as an array of integers.
|
626
531
|
# @return [Array(Integer)] An array of integers.
|
627
532
|
def stack_contents
|
628
|
-
(0...
|
533
|
+
(0...stack_pointer.value).map do |n|
|
629
534
|
@stack_memory.read_word(n)
|
630
535
|
end
|
631
536
|
end
|
@@ -636,17 +541,20 @@ module RPicSim
|
|
636
541
|
# @return [StackTrace]
|
637
542
|
def stack_trace
|
638
543
|
# The stack stores return addresses, not call addresses.
|
639
|
-
# We get the call addresses by
|
640
|
-
#
|
641
|
-
addresses = stack_contents.
|
642
|
-
|
643
|
-
|
544
|
+
# We get the call addresses by subtracting the address increment,
|
545
|
+
# which is the number of address units that each word of program memory takes up.
|
546
|
+
addresses = stack_contents.map do |return_address|
|
547
|
+
return_address - address_increment
|
548
|
+
end
|
549
|
+
addresses << pc.value
|
550
|
+
entries = addresses.map do |address|
|
551
|
+
StackTraceEntry.new address, program_file.address_description(address)
|
644
552
|
end
|
645
553
|
StackTrace.new(entries)
|
646
554
|
end
|
647
555
|
|
648
556
|
def inspect
|
649
|
-
"#<#{self.class}:0x%x, #{pc_description},
|
557
|
+
"#<#{self.class}:0x%x, #{pc_description}, stack_pointer = #{stack_pointer.value}>" % object_id
|
650
558
|
end
|
651
559
|
|
652
560
|
# Converts the specified condition into a Proc that, when called, will return a
|
@@ -674,13 +582,13 @@ module RPicSim
|
|
674
582
|
Proc.new { pc.value == c }
|
675
583
|
|
676
584
|
when :return
|
677
|
-
current_val =
|
585
|
+
current_val = stack_pointer.value
|
678
586
|
if current_val == 0
|
679
|
-
raise
|
587
|
+
raise 'The stack pointer is 0; waiting for a return would be strange and might not work.'
|
680
588
|
else
|
681
589
|
target_val = current_val - 1
|
682
590
|
end
|
683
|
-
Proc.new {
|
591
|
+
Proc.new { stack_pointer.value == target_val }
|
684
592
|
|
685
593
|
when Label
|
686
594
|
convert_condition_to_proc c.address
|
@@ -693,10 +601,27 @@ module RPicSim
|
|
693
601
|
end
|
694
602
|
end
|
695
603
|
|
604
|
+
# Creates and returns a {MemoryWatcher} object configured to watch for
|
605
|
+
# changes to RAM. For more information, see {file:RamWatcher.md}.
|
606
|
+
# @return [MemoryWatcher]
|
607
|
+
def new_ram_watcher
|
608
|
+
MemoryWatcher.new(self, @simulator.fr_memory, @ram_vars.values + @sfrs.values)
|
609
|
+
end
|
610
|
+
|
696
611
|
def shortcuts
|
697
612
|
self.class::Shortcuts
|
698
613
|
end
|
614
|
+
|
615
|
+
# Returns the {RPicSim::ProgramFile} representing the firmware being simulated.
|
616
|
+
# @return [ProgramFile]
|
617
|
+
def program_file
|
618
|
+
self.class.program_file
|
619
|
+
end
|
620
|
+
|
621
|
+
private
|
622
|
+
def address_increment
|
623
|
+
@assembly.device_info.code_address_increment
|
624
|
+
end
|
699
625
|
end
|
700
626
|
|
701
627
|
end
|
702
|
-
|