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