origen_spi 0.1.1 → 0.2.0
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 +5 -5
- data/bin/fix_my_workspace +100 -100
- data/config/application.rb +103 -103
- data/config/boot.rb +24 -24
- data/config/commands.rb +87 -87
- data/config/version.rb +8 -8
- data/lib/origen_spi.rb +19 -19
- data/lib/origen_spi/driver.rb +374 -367
- data/lib/tasks/origen_spi.rake +6 -6
- data/pattern/clear_flags_test.rb +17 -17
- data/pattern/clock_test.rb +38 -38
- data/pattern/keep_ss_active.rb +24 -24
- data/pattern/overlay_test.rb +17 -17
- data/pattern/shift_test.rb +66 -66
- data/templates/web/index.md.erb +87 -87
- data/templates/web/layouts/_basic.html.erb +13 -13
- data/templates/web/partials/_navbar.html.erb +20 -20
- data/templates/web/release_notes.md.erb +5 -5
- metadata +3 -3
data/config/commands.rb
CHANGED
@@ -1,87 +1,87 @@
|
|
1
|
-
# This file should be used to extend the origen with application specific commands
|
2
|
-
|
3
|
-
# Map any command aliases here, for example to allow 'origen ex' to refer to a
|
4
|
-
# command called execute you would add a reference as shown below:
|
5
|
-
aliases ={
|
6
|
-
# "ex" => "execute",
|
7
|
-
}
|
8
|
-
|
9
|
-
# The requested command is passed in here as @command, this checks it against
|
10
|
-
# the above alias table and should not be removed.
|
11
|
-
@command = aliases[@command] || @command
|
12
|
-
|
13
|
-
# Now branch to the specific task code
|
14
|
-
case @command
|
15
|
-
|
16
|
-
# (Working) example of how to create an application specific comment, here to generate
|
17
|
-
# a tags file for you application to enable method definition lookup and similar within
|
18
|
-
# editors/IDEs
|
19
|
-
when "tags"
|
20
|
-
# Here the logic is just written in-line, alternatively it could be written in a
|
21
|
-
# dedicated file and required here, e.g.
|
22
|
-
#require "origen_spi/commands/my_command" # Would load file lib/origen_spi/commands/my_command.rb
|
23
|
-
Dir.chdir Origen.root do
|
24
|
-
system("ripper-tags -R")
|
25
|
-
end
|
26
|
-
# You must always exit upon successfully capturing and executing a command to prevent
|
27
|
-
# control flowing back to Origen
|
28
|
-
exit 0
|
29
|
-
|
30
|
-
## Example of how to make a command to run unit tests, this simply invokes RSpec on
|
31
|
-
## the spec directory
|
32
|
-
when "specs"
|
33
|
-
require "rspec"
|
34
|
-
exit RSpec::Core::Runner.run(['spec'])
|
35
|
-
|
36
|
-
## Example of how to make a command to run diff-based tests
|
37
|
-
when "examples", "test"
|
38
|
-
Origen.load_application
|
39
|
-
status = 0
|
40
|
-
|
41
|
-
ARGV = %w(pattern/clock_test.rb -t default.rb -e default.rb -r approved)
|
42
|
-
load "#{Origen.top}/lib/origen/commands/generate.rb"
|
43
|
-
|
44
|
-
ARGV = %w(pattern/shift_test.rb -t default.rb -e default.rb -r approved)
|
45
|
-
load "#{Origen.top}/lib/origen/commands/generate.rb"
|
46
|
-
|
47
|
-
ARGV = %w(pattern/overlay_test.rb -t default.rb -e default.rb -r approved)
|
48
|
-
load "#{Origen.top}/lib/origen/commands/generate.rb"
|
49
|
-
|
50
|
-
ARGV = %w(pattern/keep_ss_active.rb -t default.rb -e default.rb -r approved)
|
51
|
-
load "#{Origen.top}/lib/origen/commands/generate.rb"
|
52
|
-
|
53
|
-
ARGV = %w(pattern/clear_flags_test.rb -t default.rb -e default.rb -r approved)
|
54
|
-
load "#{Origen.top}/lib/origen/commands/generate.rb"
|
55
|
-
|
56
|
-
if Origen.app.stats.changed_files == 0 &&
|
57
|
-
Origen.app.stats.new_files == 0 &&
|
58
|
-
Origen.app.stats.changed_patterns == 0 &&
|
59
|
-
Origen.app.stats.new_patterns == 0
|
60
|
-
|
61
|
-
Origen.app.stats.report_pass
|
62
|
-
else
|
63
|
-
Origen.app.stats.report_fail
|
64
|
-
status = 1
|
65
|
-
end
|
66
|
-
puts
|
67
|
-
if @command == "test"
|
68
|
-
Origen.app.unload_target!
|
69
|
-
require "rspec"
|
70
|
-
result = RSpec::Core::Runner.run(['spec'])
|
71
|
-
status = status == 1 ? 1 : result
|
72
|
-
end
|
73
|
-
exit status # Exit with a 1 on the event of a failure per std unix result codes
|
74
|
-
|
75
|
-
# Always leave an else clause to allow control to fall back through to the
|
76
|
-
# Origen command handler.
|
77
|
-
else
|
78
|
-
# You probably want to also add the your commands to the help shown via
|
79
|
-
# origen -h, you can do this by assigning the required text to @application_commands
|
80
|
-
# before handing control back to Origen.
|
81
|
-
@application_commands = <<-EOT
|
82
|
-
tags Build a tags file for this app
|
83
|
-
EOT
|
84
|
-
# specs Run the specs (tests), -c will enable coverage
|
85
|
-
# examples Run the examples (tests), -c will enable coverage
|
86
|
-
# test Run both specs and examples, -c will enable coverage
|
87
|
-
end
|
1
|
+
# This file should be used to extend the origen with application specific commands
|
2
|
+
|
3
|
+
# Map any command aliases here, for example to allow 'origen ex' to refer to a
|
4
|
+
# command called execute you would add a reference as shown below:
|
5
|
+
aliases ={
|
6
|
+
# "ex" => "execute",
|
7
|
+
}
|
8
|
+
|
9
|
+
# The requested command is passed in here as @command, this checks it against
|
10
|
+
# the above alias table and should not be removed.
|
11
|
+
@command = aliases[@command] || @command
|
12
|
+
|
13
|
+
# Now branch to the specific task code
|
14
|
+
case @command
|
15
|
+
|
16
|
+
# (Working) example of how to create an application specific comment, here to generate
|
17
|
+
# a tags file for you application to enable method definition lookup and similar within
|
18
|
+
# editors/IDEs
|
19
|
+
when "tags"
|
20
|
+
# Here the logic is just written in-line, alternatively it could be written in a
|
21
|
+
# dedicated file and required here, e.g.
|
22
|
+
#require "origen_spi/commands/my_command" # Would load file lib/origen_spi/commands/my_command.rb
|
23
|
+
Dir.chdir Origen.root do
|
24
|
+
system("ripper-tags -R")
|
25
|
+
end
|
26
|
+
# You must always exit upon successfully capturing and executing a command to prevent
|
27
|
+
# control flowing back to Origen
|
28
|
+
exit 0
|
29
|
+
|
30
|
+
## Example of how to make a command to run unit tests, this simply invokes RSpec on
|
31
|
+
## the spec directory
|
32
|
+
when "specs"
|
33
|
+
require "rspec"
|
34
|
+
exit RSpec::Core::Runner.run(['spec'])
|
35
|
+
|
36
|
+
## Example of how to make a command to run diff-based tests
|
37
|
+
when "examples", "test"
|
38
|
+
Origen.load_application
|
39
|
+
status = 0
|
40
|
+
|
41
|
+
ARGV = %w(pattern/clock_test.rb -t default.rb -e default.rb -r approved)
|
42
|
+
load "#{Origen.top}/lib/origen/commands/generate.rb"
|
43
|
+
|
44
|
+
ARGV = %w(pattern/shift_test.rb -t default.rb -e default.rb -r approved)
|
45
|
+
load "#{Origen.top}/lib/origen/commands/generate.rb"
|
46
|
+
|
47
|
+
ARGV = %w(pattern/overlay_test.rb -t default.rb -e default.rb -r approved)
|
48
|
+
load "#{Origen.top}/lib/origen/commands/generate.rb"
|
49
|
+
|
50
|
+
ARGV = %w(pattern/keep_ss_active.rb -t default.rb -e default.rb -r approved)
|
51
|
+
load "#{Origen.top}/lib/origen/commands/generate.rb"
|
52
|
+
|
53
|
+
ARGV = %w(pattern/clear_flags_test.rb -t default.rb -e default.rb -r approved)
|
54
|
+
load "#{Origen.top}/lib/origen/commands/generate.rb"
|
55
|
+
|
56
|
+
if Origen.app.stats.changed_files == 0 &&
|
57
|
+
Origen.app.stats.new_files == 0 &&
|
58
|
+
Origen.app.stats.changed_patterns == 0 &&
|
59
|
+
Origen.app.stats.new_patterns == 0
|
60
|
+
|
61
|
+
Origen.app.stats.report_pass
|
62
|
+
else
|
63
|
+
Origen.app.stats.report_fail
|
64
|
+
status = 1
|
65
|
+
end
|
66
|
+
puts
|
67
|
+
if @command == "test"
|
68
|
+
Origen.app.unload_target!
|
69
|
+
require "rspec"
|
70
|
+
result = RSpec::Core::Runner.run(['spec'])
|
71
|
+
status = status == 1 ? 1 : result
|
72
|
+
end
|
73
|
+
exit status # Exit with a 1 on the event of a failure per std unix result codes
|
74
|
+
|
75
|
+
# Always leave an else clause to allow control to fall back through to the
|
76
|
+
# Origen command handler.
|
77
|
+
else
|
78
|
+
# You probably want to also add the your commands to the help shown via
|
79
|
+
# origen -h, you can do this by assigning the required text to @application_commands
|
80
|
+
# before handing control back to Origen.
|
81
|
+
@application_commands = <<-EOT
|
82
|
+
tags Build a tags file for this app
|
83
|
+
EOT
|
84
|
+
# specs Run the specs (tests), -c will enable coverage
|
85
|
+
# examples Run the examples (tests), -c will enable coverage
|
86
|
+
# test Run both specs and examples, -c will enable coverage
|
87
|
+
end
|
data/config/version.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
module OrigenSpi
|
2
|
-
MAJOR = 0
|
3
|
-
MINOR =
|
4
|
-
BUGFIX =
|
5
|
-
DEV = nil
|
6
|
-
|
7
|
-
VERSION = [MAJOR, MINOR, BUGFIX].join(".") + (DEV ? ".pre#{DEV}" : '')
|
8
|
-
end
|
1
|
+
module OrigenSpi
|
2
|
+
MAJOR = 0
|
3
|
+
MINOR = 2
|
4
|
+
BUGFIX = 0
|
5
|
+
DEV = nil
|
6
|
+
|
7
|
+
VERSION = [MAJOR, MINOR, BUGFIX].join(".") + (DEV ? ".pre#{DEV}" : '')
|
8
|
+
end
|
data/lib/origen_spi.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
require 'origen'
|
2
|
-
require 'origen_testers'
|
3
|
-
require_relative '../config/application.rb'
|
4
|
-
module OrigenSpi
|
5
|
-
# THIS FILE SHOULD ONLY BE USED TO LOAD RUNTIME DEPENDENCIES
|
6
|
-
# If this plugin has any development dependencies (e.g. dummy DUT or other models that are only used
|
7
|
-
# for testing), then these should be loaded from config/boot.rb
|
8
|
-
|
9
|
-
# Example of how to explicitly require a file
|
10
|
-
# require "origen_spi/my_file"
|
11
|
-
|
12
|
-
# Load all files in the lib/origen_spi directory.
|
13
|
-
# Note that there is no problem from requiring a file twice (Ruby will ignore
|
14
|
-
# the second require), so if you have a file that must be required first, then
|
15
|
-
# explicitly require it up above and then let this take care of the rest.
|
16
|
-
Dir.glob("#{File.dirname(__FILE__)}/origen_spi/**/*.rb").sort.each do |file|
|
17
|
-
require file
|
18
|
-
end
|
19
|
-
end
|
1
|
+
require 'origen'
|
2
|
+
require 'origen_testers'
|
3
|
+
require_relative '../config/application.rb'
|
4
|
+
module OrigenSpi
|
5
|
+
# THIS FILE SHOULD ONLY BE USED TO LOAD RUNTIME DEPENDENCIES
|
6
|
+
# If this plugin has any development dependencies (e.g. dummy DUT or other models that are only used
|
7
|
+
# for testing), then these should be loaded from config/boot.rb
|
8
|
+
|
9
|
+
# Example of how to explicitly require a file
|
10
|
+
# require "origen_spi/my_file"
|
11
|
+
|
12
|
+
# Load all files in the lib/origen_spi directory.
|
13
|
+
# Note that there is no problem from requiring a file twice (Ruby will ignore
|
14
|
+
# the second require), so if you have a file that must be required first, then
|
15
|
+
# explicitly require it up above and then let this take care of the rest.
|
16
|
+
Dir.glob("#{File.dirname(__FILE__)}/origen_spi/**/*.rb").sort.each do |file|
|
17
|
+
require file
|
18
|
+
end
|
19
|
+
end
|
data/lib/origen_spi/driver.rb
CHANGED
@@ -1,367 +1,374 @@
|
|
1
|
-
module OrigenSpi
|
2
|
-
class Driver
|
3
|
-
include Origen::Model
|
4
|
-
|
5
|
-
# Dut pin that serves as the spi clock
|
6
|
-
# @example
|
7
|
-
# spi_instance.sclk_pin = dut.pin(:p1)
|
8
|
-
attr_reader :sclk_pin
|
9
|
-
|
10
|
-
# Dut pin that serves as the master out slave in pin
|
11
|
-
# @example
|
12
|
-
# spi_instance.mosi_pin = dut.pin(:p2)
|
13
|
-
attr_reader :mosi_pin
|
14
|
-
|
15
|
-
# Dut pin that serves as the master in slave out
|
16
|
-
# @example
|
17
|
-
# spi_instance.miso_pin = dut.pin(:p3)
|
18
|
-
attr_reader :miso_pin
|
19
|
-
|
20
|
-
# Dut pin that serves as the slave select
|
21
|
-
# @example
|
22
|
-
# spi_instance.ss_pin = dut.pin(:p4)
|
23
|
-
# @example
|
24
|
-
# spi_instance.ss_pin = nil # no ss pin
|
25
|
-
attr_reader :ss_pin
|
26
|
-
|
27
|
-
# clock format
|
28
|
-
#
|
29
|
-
# available options are:
|
30
|
-
# :rl # return low - data changes while sclk is low, latches on rising edge
|
31
|
-
# :rh # return high
|
32
|
-
#
|
33
|
-
# @example
|
34
|
-
# spi_instance.clk_format = :rl
|
35
|
-
attr_reader :clk_format
|
36
|
-
|
37
|
-
# pin state corresponding to slave select active
|
38
|
-
# @example
|
39
|
-
# spi_instance.ss_active = 0
|
40
|
-
attr_reader :ss_active
|
41
|
-
|
42
|
-
# time between ss_active and the first spi clock
|
43
|
-
#
|
44
|
-
# hash containing the wait time
|
45
|
-
#
|
46
|
-
# @example
|
47
|
-
# spi_instance.clk_wait_time = {time_in_us: 0} # no delay
|
48
|
-
# spi_instance.clk_wait_time = {time_in_ms: 1} # 1ms delay
|
49
|
-
# spi_instance.clk_wait_time = {time_in_cycles: 2} # 2 cycle delay
|
50
|
-
attr_reader :clk_wait_time
|
51
|
-
|
52
|
-
# number of tester cycles per spi clock
|
53
|
-
attr_reader :clk_multiple
|
54
|
-
|
55
|
-
# cycle on which miso compares are placed (0 is the first cycle)
|
56
|
-
attr_reader :miso_compare_cycle
|
57
|
-
|
58
|
-
# data order
|
59
|
-
#
|
60
|
-
# available options are:
|
61
|
-
# :msb0 - MSB is shifted first
|
62
|
-
# :lsb0
|
63
|
-
#
|
64
|
-
# @example
|
65
|
-
# spi_instance.data_order = :lsb0
|
66
|
-
attr_reader :data_order
|
67
|
-
|
68
|
-
# internal attribute
|
69
|
-
attr_reader :settings_validated
|
70
|
-
|
71
|
-
# options hash can configure
|
72
|
-
#
|
73
|
-
# @example
|
74
|
-
# options = {
|
75
|
-
# sclk_pin: dut.pin(:p1),
|
76
|
-
# mosi_pin: dut.pin(:p2),
|
77
|
-
# miso_pin: dut.pin(:p3),
|
78
|
-
# ss_pin: dut.pin(:p4),
|
79
|
-
# clk_format: :rl,
|
80
|
-
# ss_active: 0,
|
81
|
-
# clk_wait_time: {time_in_cycles: 2},
|
82
|
-
# clk_multiple: 2,
|
83
|
-
# miso_compare_cycle: 1,
|
84
|
-
# data_order: :msb0
|
85
|
-
# }
|
86
|
-
# spi = OrigenSpi::Driver.new(options)
|
87
|
-
def initialize(options = {})
|
88
|
-
options = {
|
89
|
-
sclk_pin: nil,
|
90
|
-
mosi_pin: nil,
|
91
|
-
miso_pin: nil,
|
92
|
-
ss_pin: nil,
|
93
|
-
clk_format: :nr,
|
94
|
-
ss_active: 0,
|
95
|
-
clk_wait_time: { time_in_us: 0 },
|
96
|
-
clk_multiple: 2,
|
97
|
-
data_order: :msb0
|
98
|
-
}.merge(options)
|
99
|
-
|
100
|
-
@sclk_pin = options[:sclk_pin]
|
101
|
-
@mosi_pin = options[:mosi_pin]
|
102
|
-
@miso_pin = options[:miso_pin]
|
103
|
-
@ss_pin = options[:ss_pin]
|
104
|
-
@clk_format = options[:clk_format]
|
105
|
-
@ss_active = options[:ss_active]
|
106
|
-
@clk_wait_time = options[:clk_wait_time]
|
107
|
-
@clk_multiple = options[:clk_multiple]
|
108
|
-
@miso_compare_cycle = options[:miso_compare_cycle] ? options[:miso_compare_cycle] : @clk_multiple - 1
|
109
|
-
@data_order = options[:data_order]
|
110
|
-
@settings_validated = false
|
111
|
-
end
|
112
|
-
|
113
|
-
def sclk_pin=(pin)
|
114
|
-
@sclk_pin = pin
|
115
|
-
@settings_validated = false
|
116
|
-
end
|
117
|
-
|
118
|
-
def mosi_pin=(pin)
|
119
|
-
@mosi_pin = pin
|
120
|
-
@settings_validated = false
|
121
|
-
end
|
122
|
-
|
123
|
-
def miso_pin=(pin)
|
124
|
-
@miso_pin = pin
|
125
|
-
@settings_validated = false
|
126
|
-
end
|
127
|
-
|
128
|
-
def ss_pin=(pin)
|
129
|
-
@ss_pin = pin
|
130
|
-
@settings_validated = false
|
131
|
-
end
|
132
|
-
|
133
|
-
def clk_format=(format)
|
134
|
-
if format == :nr || format == :rl || format == :rh
|
135
|
-
@clk_format = format
|
136
|
-
else
|
137
|
-
Origen.log.error "Invalid clock format for OrigenSpi::Driver -> #{format}"
|
138
|
-
Origen.log.error 'Valid formats are :rl, :rh'
|
139
|
-
end
|
140
|
-
@settings_validated = false
|
141
|
-
end
|
142
|
-
|
143
|
-
def ss_active=(state)
|
144
|
-
if state == 0 || state == 1
|
145
|
-
@ss_active = state
|
146
|
-
else
|
147
|
-
Origen.log.error 'Invalid OrigenSpi::Driver.ss_active state (valid states are 0 and 1)'
|
148
|
-
end
|
149
|
-
@settings_validated = false
|
150
|
-
end
|
151
|
-
|
152
|
-
def clk_wait_time=(clk_wait)
|
153
|
-
@clk_wait_time = clk_wait
|
154
|
-
@settings_validated = false
|
155
|
-
end
|
156
|
-
|
157
|
-
def clk_multiple=(mult)
|
158
|
-
@clk_multiple = mult
|
159
|
-
@settings_validated = false
|
160
|
-
end
|
161
|
-
|
162
|
-
def miso_compare_cycle=(cycle)
|
163
|
-
@miso_compare_cycle = cycle
|
164
|
-
@settings_validated = false
|
165
|
-
end
|
166
|
-
|
167
|
-
def data_order=(order)
|
168
|
-
if order == :msb0 || order == :lsb0
|
169
|
-
@data_order = order
|
170
|
-
else
|
171
|
-
Origen.log.error "Invalid OrigenSpi::Driver.data_order -> #{order}, (use :msb0 or :lsb0)"
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
# Check settings
|
176
|
-
def validate_settings
|
177
|
-
unless @settings_validated
|
178
|
-
settings_valid = true
|
179
|
-
|
180
|
-
# check that clock and miso are provided
|
181
|
-
unless @sclk_pin.is_a?(Origen::Pins::Pin)
|
182
|
-
settings_valid = false
|
183
|
-
Origen.log.error 'OrigenSpi::Driver.sclk_pin must be an Origen pin object'
|
184
|
-
end
|
185
|
-
|
186
|
-
unless @clk_format == :rl || @clk_format == :rh
|
187
|
-
settings_valid = false
|
188
|
-
Origen.log.error 'OrigenSpi::Driver.clk_format must be one of :rl, :rh'
|
189
|
-
end
|
190
|
-
|
191
|
-
unless @ss_active == 0 || @ss_active == 1
|
192
|
-
settings_valid = false
|
193
|
-
Origen.log.error 'OrigenSpi::Driver.ss_active must be either 0 or 1'
|
194
|
-
end
|
195
|
-
|
196
|
-
@clk_multiple = 1 if @clk_multiple < 1
|
197
|
-
@half_cycle = @clk_multiple / 2
|
198
|
-
|
199
|
-
@miso_compare_cycle = @clk_multiple - 1 if @miso_compare_cycle > @clk_multiple - 1
|
200
|
-
|
201
|
-
unless @data_order == :msb0 || @data_order == :lsb0
|
202
|
-
settings_valid = false
|
203
|
-
Origen.log.error 'OrigenSpi::Driver.data_order must be either :msb0 or :lsb0'
|
204
|
-
end
|
205
|
-
|
206
|
-
@settings_validated = settings_valid
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
# Run a spi clock cycle
|
211
|
-
#
|
212
|
-
# This method can be used to clock the spi port without specifying shift data
|
213
|
-
def sclk_cycle
|
214
|
-
validate_settings
|
215
|
-
cc 'OrigenSpi::Driver - Issue a clock cycle'
|
216
|
-
@sclk_pin.restore_state do
|
217
|
-
@sclk_pin.drive @clk_format == :rl ? 0 : 1
|
218
|
-
@half_cycle.times { tester.cycle }
|
219
|
-
@sclk_pin.drive @clk_format == :rl ? 1 : 0
|
220
|
-
@half_cycle.upto(@clk_multiple - 1) { tester.cycle }
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
# Shift a spi packet
|
225
|
-
#
|
226
|
-
# Overlay and capture is specified through reg.overlay and reg.store
|
227
|
-
#
|
228
|
-
# @example
|
229
|
-
# spi_instance.shift master_out: out_reg, master_in: in_cmp_reg
|
230
|
-
# @example
|
231
|
-
# spi_instance.shift master_out: 0x7a, size: 32
|
232
|
-
# @example
|
233
|
-
# spi_instance.shift master_in: in_cmp_reg
|
234
|
-
def shift(options = {})
|
235
|
-
options = {
|
236
|
-
master_out: 0,
|
237
|
-
keep_ss_active: false
|
238
|
-
}.merge(options)
|
239
|
-
|
240
|
-
validate_settings
|
241
|
-
options = validate_options(options)
|
242
|
-
out_reg = build_output_packet(options).bits
|
243
|
-
in_reg = build_input_packet(options).bits
|
244
|
-
|
245
|
-
# reverse bit order if :msb0
|
246
|
-
if @data_order == :msb0
|
247
|
-
out_reg = out_reg.reverse
|
248
|
-
in_reg = in_reg.reverse
|
249
|
-
end
|
250
|
-
|
251
|
-
# set ss active
|
252
|
-
@ss_pin.drive @ss_active unless @ss_pin.nil?
|
253
|
-
|
254
|
-
# apply wait time if requested
|
255
|
-
tester.wait @clk_wait_time unless @clk_wait_time == { time_in_us: 0 }
|
256
|
-
|
257
|
-
# now do the shifting
|
258
|
-
0.upto(out_reg.size - 1) do |bit|
|
259
|
-
#
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
end
|
279
|
-
|
280
|
-
#
|
281
|
-
@
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
end
|
321
|
-
|
322
|
-
#
|
323
|
-
#
|
324
|
-
#
|
325
|
-
def
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
end
|
364
|
-
options
|
365
|
-
|
366
|
-
|
367
|
-
|
1
|
+
module OrigenSpi
|
2
|
+
class Driver
|
3
|
+
include Origen::Model
|
4
|
+
|
5
|
+
# Dut pin that serves as the spi clock
|
6
|
+
# @example
|
7
|
+
# spi_instance.sclk_pin = dut.pin(:p1)
|
8
|
+
attr_reader :sclk_pin
|
9
|
+
|
10
|
+
# Dut pin that serves as the master out slave in pin
|
11
|
+
# @example
|
12
|
+
# spi_instance.mosi_pin = dut.pin(:p2)
|
13
|
+
attr_reader :mosi_pin
|
14
|
+
|
15
|
+
# Dut pin that serves as the master in slave out
|
16
|
+
# @example
|
17
|
+
# spi_instance.miso_pin = dut.pin(:p3)
|
18
|
+
attr_reader :miso_pin
|
19
|
+
|
20
|
+
# Dut pin that serves as the slave select
|
21
|
+
# @example
|
22
|
+
# spi_instance.ss_pin = dut.pin(:p4)
|
23
|
+
# @example
|
24
|
+
# spi_instance.ss_pin = nil # no ss pin
|
25
|
+
attr_reader :ss_pin
|
26
|
+
|
27
|
+
# clock format
|
28
|
+
#
|
29
|
+
# available options are:
|
30
|
+
# :rl # return low - data changes while sclk is low, latches on rising edge
|
31
|
+
# :rh # return high
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# spi_instance.clk_format = :rl
|
35
|
+
attr_reader :clk_format
|
36
|
+
|
37
|
+
# pin state corresponding to slave select active
|
38
|
+
# @example
|
39
|
+
# spi_instance.ss_active = 0
|
40
|
+
attr_reader :ss_active
|
41
|
+
|
42
|
+
# time between ss_active and the first spi clock
|
43
|
+
#
|
44
|
+
# hash containing the wait time
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# spi_instance.clk_wait_time = {time_in_us: 0} # no delay
|
48
|
+
# spi_instance.clk_wait_time = {time_in_ms: 1} # 1ms delay
|
49
|
+
# spi_instance.clk_wait_time = {time_in_cycles: 2} # 2 cycle delay
|
50
|
+
attr_reader :clk_wait_time
|
51
|
+
|
52
|
+
# number of tester cycles per spi clock
|
53
|
+
attr_reader :clk_multiple
|
54
|
+
|
55
|
+
# cycle on which miso compares are placed (0 is the first cycle)
|
56
|
+
attr_reader :miso_compare_cycle
|
57
|
+
|
58
|
+
# data order
|
59
|
+
#
|
60
|
+
# available options are:
|
61
|
+
# :msb0 - MSB is shifted first
|
62
|
+
# :lsb0
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# spi_instance.data_order = :lsb0
|
66
|
+
attr_reader :data_order
|
67
|
+
|
68
|
+
# internal attribute
|
69
|
+
attr_reader :settings_validated
|
70
|
+
|
71
|
+
# options hash can configure
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# options = {
|
75
|
+
# sclk_pin: dut.pin(:p1),
|
76
|
+
# mosi_pin: dut.pin(:p2),
|
77
|
+
# miso_pin: dut.pin(:p3),
|
78
|
+
# ss_pin: dut.pin(:p4),
|
79
|
+
# clk_format: :rl,
|
80
|
+
# ss_active: 0,
|
81
|
+
# clk_wait_time: {time_in_cycles: 2},
|
82
|
+
# clk_multiple: 2,
|
83
|
+
# miso_compare_cycle: 1,
|
84
|
+
# data_order: :msb0
|
85
|
+
# }
|
86
|
+
# spi = OrigenSpi::Driver.new(options)
|
87
|
+
def initialize(options = {})
|
88
|
+
options = {
|
89
|
+
sclk_pin: nil,
|
90
|
+
mosi_pin: nil,
|
91
|
+
miso_pin: nil,
|
92
|
+
ss_pin: nil,
|
93
|
+
clk_format: :nr,
|
94
|
+
ss_active: 0,
|
95
|
+
clk_wait_time: { time_in_us: 0 },
|
96
|
+
clk_multiple: 2,
|
97
|
+
data_order: :msb0
|
98
|
+
}.merge(options)
|
99
|
+
|
100
|
+
@sclk_pin = options[:sclk_pin]
|
101
|
+
@mosi_pin = options[:mosi_pin]
|
102
|
+
@miso_pin = options[:miso_pin]
|
103
|
+
@ss_pin = options[:ss_pin]
|
104
|
+
@clk_format = options[:clk_format]
|
105
|
+
@ss_active = options[:ss_active]
|
106
|
+
@clk_wait_time = options[:clk_wait_time]
|
107
|
+
@clk_multiple = options[:clk_multiple]
|
108
|
+
@miso_compare_cycle = options[:miso_compare_cycle] ? options[:miso_compare_cycle] : @clk_multiple - 1
|
109
|
+
@data_order = options[:data_order]
|
110
|
+
@settings_validated = false
|
111
|
+
end
|
112
|
+
|
113
|
+
def sclk_pin=(pin)
|
114
|
+
@sclk_pin = pin
|
115
|
+
@settings_validated = false
|
116
|
+
end
|
117
|
+
|
118
|
+
def mosi_pin=(pin)
|
119
|
+
@mosi_pin = pin
|
120
|
+
@settings_validated = false
|
121
|
+
end
|
122
|
+
|
123
|
+
def miso_pin=(pin)
|
124
|
+
@miso_pin = pin
|
125
|
+
@settings_validated = false
|
126
|
+
end
|
127
|
+
|
128
|
+
def ss_pin=(pin)
|
129
|
+
@ss_pin = pin
|
130
|
+
@settings_validated = false
|
131
|
+
end
|
132
|
+
|
133
|
+
def clk_format=(format)
|
134
|
+
if format == :nr || format == :rl || format == :rh
|
135
|
+
@clk_format = format
|
136
|
+
else
|
137
|
+
Origen.log.error "Invalid clock format for OrigenSpi::Driver -> #{format}"
|
138
|
+
Origen.log.error 'Valid formats are :rl, :rh'
|
139
|
+
end
|
140
|
+
@settings_validated = false
|
141
|
+
end
|
142
|
+
|
143
|
+
def ss_active=(state)
|
144
|
+
if state == 0 || state == 1
|
145
|
+
@ss_active = state
|
146
|
+
else
|
147
|
+
Origen.log.error 'Invalid OrigenSpi::Driver.ss_active state (valid states are 0 and 1)'
|
148
|
+
end
|
149
|
+
@settings_validated = false
|
150
|
+
end
|
151
|
+
|
152
|
+
def clk_wait_time=(clk_wait)
|
153
|
+
@clk_wait_time = clk_wait
|
154
|
+
@settings_validated = false
|
155
|
+
end
|
156
|
+
|
157
|
+
def clk_multiple=(mult)
|
158
|
+
@clk_multiple = mult
|
159
|
+
@settings_validated = false
|
160
|
+
end
|
161
|
+
|
162
|
+
def miso_compare_cycle=(cycle)
|
163
|
+
@miso_compare_cycle = cycle
|
164
|
+
@settings_validated = false
|
165
|
+
end
|
166
|
+
|
167
|
+
def data_order=(order)
|
168
|
+
if order == :msb0 || order == :lsb0
|
169
|
+
@data_order = order
|
170
|
+
else
|
171
|
+
Origen.log.error "Invalid OrigenSpi::Driver.data_order -> #{order}, (use :msb0 or :lsb0)"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Check settings
|
176
|
+
def validate_settings
|
177
|
+
unless @settings_validated
|
178
|
+
settings_valid = true
|
179
|
+
|
180
|
+
# check that clock and miso are provided
|
181
|
+
unless @sclk_pin.is_a?(Origen::Pins::Pin)
|
182
|
+
settings_valid = false
|
183
|
+
Origen.log.error 'OrigenSpi::Driver.sclk_pin must be an Origen pin object'
|
184
|
+
end
|
185
|
+
|
186
|
+
unless @clk_format == :rl || @clk_format == :rh
|
187
|
+
settings_valid = false
|
188
|
+
Origen.log.error 'OrigenSpi::Driver.clk_format must be one of :rl, :rh'
|
189
|
+
end
|
190
|
+
|
191
|
+
unless @ss_active == 0 || @ss_active == 1
|
192
|
+
settings_valid = false
|
193
|
+
Origen.log.error 'OrigenSpi::Driver.ss_active must be either 0 or 1'
|
194
|
+
end
|
195
|
+
|
196
|
+
@clk_multiple = 1 if @clk_multiple < 1
|
197
|
+
@half_cycle = @clk_multiple / 2
|
198
|
+
|
199
|
+
@miso_compare_cycle = @clk_multiple - 1 if @miso_compare_cycle > @clk_multiple - 1
|
200
|
+
|
201
|
+
unless @data_order == :msb0 || @data_order == :lsb0
|
202
|
+
settings_valid = false
|
203
|
+
Origen.log.error 'OrigenSpi::Driver.data_order must be either :msb0 or :lsb0'
|
204
|
+
end
|
205
|
+
|
206
|
+
@settings_validated = settings_valid
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Run a spi clock cycle
|
211
|
+
#
|
212
|
+
# This method can be used to clock the spi port without specifying shift data
|
213
|
+
def sclk_cycle
|
214
|
+
validate_settings
|
215
|
+
cc 'OrigenSpi::Driver - Issue a clock cycle'
|
216
|
+
@sclk_pin.restore_state do
|
217
|
+
@sclk_pin.drive @clk_format == :rl ? 0 : 1
|
218
|
+
@half_cycle.times { tester.cycle }
|
219
|
+
@sclk_pin.drive @clk_format == :rl ? 1 : 0
|
220
|
+
@half_cycle.upto(@clk_multiple - 1) { tester.cycle }
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Shift a spi packet
|
225
|
+
#
|
226
|
+
# Overlay and capture is specified through reg.overlay and reg.store
|
227
|
+
#
|
228
|
+
# @example
|
229
|
+
# spi_instance.shift master_out: out_reg, master_in: in_cmp_reg
|
230
|
+
# @example
|
231
|
+
# spi_instance.shift master_out: 0x7a, size: 32
|
232
|
+
# @example
|
233
|
+
# spi_instance.shift master_in: in_cmp_reg
|
234
|
+
def shift(options = {})
|
235
|
+
options = {
|
236
|
+
master_out: 0,
|
237
|
+
keep_ss_active: false
|
238
|
+
}.merge(options)
|
239
|
+
|
240
|
+
validate_settings
|
241
|
+
options = validate_options(options)
|
242
|
+
out_reg = build_output_packet(options).bits
|
243
|
+
in_reg = build_input_packet(options).bits
|
244
|
+
|
245
|
+
# reverse bit order if :msb0
|
246
|
+
if @data_order == :msb0
|
247
|
+
out_reg = out_reg.reverse
|
248
|
+
in_reg = in_reg.reverse
|
249
|
+
end
|
250
|
+
|
251
|
+
# set ss active
|
252
|
+
@ss_pin.drive @ss_active unless @ss_pin.nil?
|
253
|
+
|
254
|
+
# apply wait time if requested
|
255
|
+
tester.wait @clk_wait_time unless @clk_wait_time == { time_in_us: 0 }
|
256
|
+
|
257
|
+
# now do the shifting
|
258
|
+
0.upto(out_reg.size - 1) do |bit|
|
259
|
+
# note which bit this is
|
260
|
+
if @data_order == :msb0
|
261
|
+
thisbit = out_reg.size - 1 - bit
|
262
|
+
else
|
263
|
+
thisbit = bit
|
264
|
+
end
|
265
|
+
|
266
|
+
# park the clock
|
267
|
+
@sclk_pin.drive @clk_format == :rl ? 0 : 1
|
268
|
+
@miso_pin.dont_care unless @miso_pin.nil?
|
269
|
+
|
270
|
+
# setup the bit to be driven, prep for overlay if requested
|
271
|
+
overlay_options = {}
|
272
|
+
unless @mosi_pin.nil?
|
273
|
+
@mosi_pin.drive out_reg[bit].data
|
274
|
+
if out_reg[bit].has_overlay?
|
275
|
+
overlay_options[:pins] = @mosi_pin
|
276
|
+
overlay_options[:overlay_str] = out_reg[bit].overlay_str
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# advance to clock active edge
|
281
|
+
@half_cycle.times do |c|
|
282
|
+
handle_miso c, in_reg[bit], thisbit
|
283
|
+
cycle overlay_options
|
284
|
+
overlay_options[:change_data] = false unless overlay_options == {}
|
285
|
+
end
|
286
|
+
|
287
|
+
# drive the clock to active
|
288
|
+
@sclk_pin.drive @clk_format == :rl ? 1 : 0
|
289
|
+
|
290
|
+
# advance to the end of the sclk cycle checking for appropriate miso compare placement
|
291
|
+
@half_cycle.upto(@clk_multiple - 1) do |c|
|
292
|
+
handle_miso c, in_reg[bit], thisbit
|
293
|
+
cycle overlay_options
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# park the clock, mask miso, clear ss
|
298
|
+
@sclk_pin.drive @clk_format == :rl ? 0 : 1
|
299
|
+
@miso_pin.dont_care unless @miso_pin.nil?
|
300
|
+
@mosi_pin.drive 0 unless @mosi_pin.nil?
|
301
|
+
@ss_pin.drive @ss_active == 1 ? 0 : 1 unless @ss_pin.nil? || options[:keep_ss_active]
|
302
|
+
|
303
|
+
# clear flags if registers were provided
|
304
|
+
options[:master_out].clear_flags if options[:master_out].respond_to?(:clear_flags)
|
305
|
+
options[:master_in].clear_flags if options[:master_in].respond_to?(:clear_flags)
|
306
|
+
end
|
307
|
+
|
308
|
+
# Internal method
|
309
|
+
#
|
310
|
+
# Set the state of miso
|
311
|
+
def handle_miso(c, bit, bit_number)
|
312
|
+
unless @miso_pin.nil?
|
313
|
+
if c == @miso_compare_cycle
|
314
|
+
@miso_pin.assert bit.data, meta: { position: bit_number } if bit.is_to_be_read?
|
315
|
+
tester.store_next_cycle @miso_pin if bit.is_to_be_stored?
|
316
|
+
else
|
317
|
+
@miso_pin.dont_care
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Internal method
|
323
|
+
#
|
324
|
+
# Issue a tester cycle, conditionally with overlay options supplied
|
325
|
+
def cycle(overlay_options = {})
|
326
|
+
overlay_options == {} ? tester.cycle : (tester.cycle overlay: overlay_options)
|
327
|
+
end
|
328
|
+
|
329
|
+
# Build the shift out packet
|
330
|
+
#
|
331
|
+
# This is an internal method used by the shift method
|
332
|
+
def build_output_packet(options)
|
333
|
+
out_reg = Origen::Registers::Reg.dummy(options[:size])
|
334
|
+
if options[:master_out].respond_to?(:data)
|
335
|
+
out_reg.copy_all(options[:master_out])
|
336
|
+
else
|
337
|
+
out_reg.write options[:master_out]
|
338
|
+
end
|
339
|
+
out_reg
|
340
|
+
end
|
341
|
+
|
342
|
+
# Build the input packet
|
343
|
+
#
|
344
|
+
# This is an internal method used by the shift method
|
345
|
+
def build_input_packet(options)
|
346
|
+
in_reg = Origen::Registers::Reg.dummy(options[:size])
|
347
|
+
if options[:master_in].respond_to?(:data)
|
348
|
+
in_reg.copy_all(options[:master_in])
|
349
|
+
else
|
350
|
+
unless options[:master_in].nil?
|
351
|
+
in_reg.write options[:master_in]
|
352
|
+
in_reg.read
|
353
|
+
end
|
354
|
+
end
|
355
|
+
in_reg
|
356
|
+
end
|
357
|
+
|
358
|
+
# Internal method that logs errors in options passed to the shift method
|
359
|
+
def validate_options(options)
|
360
|
+
# check that size of the packet can be determined
|
361
|
+
unless options[:size]
|
362
|
+
options[:size] = options[:master_in].size if options[:master_in].respond_to?(:data)
|
363
|
+
end
|
364
|
+
unless options[:size]
|
365
|
+
options[:size] = options[:master_out].size if options[:master_out].respond_to?(:data)
|
366
|
+
end
|
367
|
+
unless options[:size]
|
368
|
+
Origen.log.error "OrigenSpi::Driver can't determine the packet size"
|
369
|
+
exit
|
370
|
+
end
|
371
|
+
options
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|