katcp 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/katcp/client/roach.rb +195 -32
- data/lib/katcp/client.rb +1 -1
- data/lib/katcp/response.rb +5 -18
- data/lib/katcp/util.rb +5 -21
- data/lib/katcp/version.rb +1 -1
- metadata +4 -4
data/lib/katcp/client/roach.rb
CHANGED
@@ -4,12 +4,132 @@ require 'katcp/client'
|
|
4
4
|
module KATCP
|
5
5
|
|
6
6
|
# Facilitates talking to <tt>tcpborphserver2</tt>, a KATCP server
|
7
|
-
# implementation that runs on ROACH boards.
|
8
|
-
#
|
7
|
+
# implementation that runs on ROACH boards. In addition to providing
|
8
|
+
# convenience wrappers around <tt>tcpborphserver2</tt> requests, it also adds
|
9
|
+
# the following features:
|
9
10
|
#
|
10
|
-
#
|
11
|
+
# * Hash-like access to read and write gateware devices (e.g. software
|
12
|
+
# registers, shared BRAM, etc.) via methods #[] and #[]=.
|
13
|
+
#
|
14
|
+
# * Each RoachClient instance dynamically adds (and removes) reader and
|
15
|
+
# writer attributes (i.e. methods) for gateware devices as the FPGA is
|
16
|
+
# programmed (or de-programmed). This not only provides for very clean and
|
17
|
+
# readable code, it also provides convenient tab completion in irb for
|
18
|
+
# gateware specific device names.
|
19
|
+
#
|
20
|
+
# * Word based instead of byte based data offsets and counts. KATCP::Client
|
21
|
+
# data transfer methods #read and #write deal with byte based offsets and
|
22
|
+
# counts. These methods in KATCP::RoachClient deal with word based offsets
|
23
|
+
# and counts since ROACH transfers (currently) require word alignment in
|
24
|
+
# both offsets and counts. To use byte based offsets and counts
|
25
|
+
# explicitly, use <tt>request(:read, ...)</tt> instead of
|
26
|
+
# <tt>read(...)</tt> etc.
|
11
27
|
class RoachClient < Client
|
12
28
|
|
29
|
+
# Returns an Array of Strings representing the device names from the
|
30
|
+
# current design. The Array will be empty if the currently programmed
|
31
|
+
# gateware has no devices (very rare, if even possible) or if no gateware
|
32
|
+
# is currently programmed.
|
33
|
+
attr_reader :devices
|
34
|
+
|
35
|
+
# Creates a RoachClient that connects to a KATCP server at +remote_host+ on
|
36
|
+
# +remote_port+. If +local_host+ and +local_port+ are specified, then
|
37
|
+
# those parameters are used on the local end to establish the connection.
|
38
|
+
def initialize(remote_host, remote_port=7147, local_host=nil, local_port=nil)
|
39
|
+
super(remote_host, remote_port, local_host, local_port)
|
40
|
+
# List of all devices
|
41
|
+
@devices = [];
|
42
|
+
# List of dynamically defined device attrs (readers only, writers implied)
|
43
|
+
@device_attrs = [];
|
44
|
+
define_device_attrs
|
45
|
+
end
|
46
|
+
|
47
|
+
# Dynamically define attributes (i.e. methods) for gateware devices, if
|
48
|
+
# currently programmed.
|
49
|
+
def define_device_attrs # :nodoc
|
50
|
+
# First undefine existing device attrs
|
51
|
+
undefine_device_attrs
|
52
|
+
# Define nothing if FPGA not programmed
|
53
|
+
return unless programmed?
|
54
|
+
# Dynamically define accessors for all devices (i.e. registers, BRAMs,
|
55
|
+
# etc.) except those whose names conflict with existing methods.
|
56
|
+
@devices = listdev.lines[0..-2].map {|l| l[1]}
|
57
|
+
@devices.sort!
|
58
|
+
@devices.each do |dev|
|
59
|
+
# TODO sanitize dev in case it is invalid method name
|
60
|
+
|
61
|
+
# Define methods unless they conflict with existing methods
|
62
|
+
if ! respond_to?(dev) && ! respond_to?("#{dev}=")
|
63
|
+
# Save attr name
|
64
|
+
@device_attrs << dev
|
65
|
+
# Dynamically define methods
|
66
|
+
instance_eval <<-"_end"
|
67
|
+
def #{dev}(*args)
|
68
|
+
read('#{dev}', *args)
|
69
|
+
end
|
70
|
+
def #{dev}=(*args)
|
71
|
+
write('#{dev}', 0, *args)
|
72
|
+
end
|
73
|
+
_end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
protected :define_device_attrs
|
80
|
+
|
81
|
+
# Undefine any attributes (i.e. methods) that were previously defined
|
82
|
+
# dynamically.
|
83
|
+
def undefine_device_attrs # :nodoc
|
84
|
+
@device_attrs.each do |dev|
|
85
|
+
instance_eval <<-"_end"
|
86
|
+
class << self
|
87
|
+
remove_method '#{dev}'
|
88
|
+
remove_method '#{dev}='
|
89
|
+
end
|
90
|
+
_end
|
91
|
+
end
|
92
|
+
@device_attrs.clear
|
93
|
+
@devices.clear
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
protected :undefine_device_attrs
|
98
|
+
|
99
|
+
# Returns +true+ if the current design has a device named +device+.
|
100
|
+
def has_device?(device)
|
101
|
+
@devices.include?(device.to_s)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Provides Hash-like querying for a device named +device+.
|
105
|
+
alias has_key? has_device?
|
106
|
+
|
107
|
+
# call-seq:
|
108
|
+
# bulkread(register_name) -> Integer
|
109
|
+
# bulkread(register_name, word_offset) -> Integer
|
110
|
+
# bulkread(register_name, word_offset, word_count) -> NArray.int(word_count)
|
111
|
+
#
|
112
|
+
# Reads a +word_count+ words starting at +word_offset+ offset from
|
113
|
+
# register (or block RAM) named by +register_name+. Returns an Integer
|
114
|
+
# unless +word_count+ is given in which case it returns an
|
115
|
+
# NArray.int(word_count).
|
116
|
+
#
|
117
|
+
# Equivalent to #read, but uses a bulkread request rather than a read
|
118
|
+
# request.
|
119
|
+
def bulkread(register_name, *args)
|
120
|
+
byte_offset = 4 * (args[0] || 0)
|
121
|
+
byte_count = 4 * (args[1] || 1)
|
122
|
+
raise 'word count must be non-negative' if byte_count < 0
|
123
|
+
resp = request(:bulkread, register_name, byte_offset, byte_count)
|
124
|
+
raise resp.to_s unless resp.ok?
|
125
|
+
data = resp.lines[0..-2].map{|l| l[1]}.join
|
126
|
+
if args.length <= 1
|
127
|
+
data.unpack('N')[0]
|
128
|
+
else
|
129
|
+
data.to_na(NArray::INT).ntoh
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
13
133
|
# call-seq:
|
14
134
|
# delbof(image_file) -> KATCP::Response
|
15
135
|
#
|
@@ -48,22 +168,56 @@ module KATCP
|
|
48
168
|
#
|
49
169
|
# Programs a gateware image specified by +image_file+. If +image_file+ is
|
50
170
|
# omitted, de-programs the FPGA.
|
171
|
+
#
|
172
|
+
# Whenever the FPGA is programmed, reader and writer attributes (i.e.
|
173
|
+
# methods) are defined for every device listed by #listdev except for
|
174
|
+
# device names that conflict with an already existing method names.
|
175
|
+
#
|
176
|
+
# Whenever the FPGA is de-programmed (or re-programmed), existing
|
177
|
+
# attributes that were dynamically defined for the previous design are
|
178
|
+
# removed.
|
51
179
|
def progdev(*args)
|
52
180
|
request(:progdev, *args)
|
181
|
+
define_device_attrs
|
182
|
+
end
|
183
|
+
|
184
|
+
# Returns true if currently programmed (specifically, it is equivalent to
|
185
|
+
# <tt>status.ok?</tt>).
|
186
|
+
def programmed?
|
187
|
+
status.ok?
|
53
188
|
end
|
54
189
|
|
55
190
|
# call-seq:
|
56
|
-
# read(register_name) ->
|
57
|
-
# read(register_name,
|
58
|
-
# read(register_name,
|
191
|
+
# read(register_name) -> Integer
|
192
|
+
# read(register_name, word_offset) -> Integer
|
193
|
+
# read(register_name, word_offset, word_count) -> NArray.int(word_count)
|
194
|
+
#
|
195
|
+
# Reads one or +word_count+ words starting at +word_offset+ offset from
|
196
|
+
# register (or block RAM) named by +register_name+. Returns an Integer
|
197
|
+
# unless +word_count+ is given in which case it returns an
|
198
|
+
# NArray.int(word_count).
|
59
199
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
200
|
+
# Note that KATCP::Client#read deals with byte based offsets and counts,
|
201
|
+
# but all reads on the ROACH must be word aligned and an integer number of
|
202
|
+
# words long, so KATCP::RoachClient#read deals with word based offsets and
|
203
|
+
# counts.
|
63
204
|
def read(register_name, *args)
|
64
|
-
|
205
|
+
byte_offset = 4 * (args[0] || 0)
|
206
|
+
byte_count = 4 * (args[1] || 1)
|
207
|
+
raise 'word count must be non-negative' if byte_count < 0
|
208
|
+
resp = request(:read, register_name, byte_offset, byte_count)
|
209
|
+
raise resp.to_s unless resp.ok?
|
210
|
+
data = resp.payload
|
211
|
+
if args.length <= 1
|
212
|
+
data.unpack('N')[0]
|
213
|
+
else
|
214
|
+
data.to_na(NArray::INT).ntoh
|
215
|
+
end
|
65
216
|
end
|
66
217
|
|
218
|
+
alias wordread read
|
219
|
+
alias [] read
|
220
|
+
|
67
221
|
# call-seq:
|
68
222
|
# status -> KATCP::Response
|
69
223
|
#
|
@@ -97,7 +251,7 @@ module KATCP
|
|
97
251
|
#
|
98
252
|
# Stop a tgtap instance.
|
99
253
|
def tap_stop(register_name)
|
100
|
-
|
254
|
+
request(:tap_stop, register_name)
|
101
255
|
end
|
102
256
|
|
103
257
|
# call-seq:
|
@@ -112,30 +266,39 @@ module KATCP
|
|
112
266
|
end
|
113
267
|
|
114
268
|
# call-seq:
|
115
|
-
#
|
116
|
-
#
|
117
|
-
# wordread(register_name, word_offset, length) -> KATCP::Response
|
118
|
-
#
|
119
|
-
# Reads word(s) from +register_name+.
|
120
|
-
def wordread(register_name, *args)
|
121
|
-
request(:wordread, register_name, *args)
|
122
|
-
end
|
123
|
-
|
124
|
-
# call-seq:
|
125
|
-
# wordwrite(register_name, word_offset, payload[, ...]) -> KATCP::Response
|
269
|
+
# write(register_name, data) -> self
|
270
|
+
# write(register_name, word_offset, data) -> self
|
126
271
|
#
|
127
|
-
#
|
128
|
-
|
129
|
-
|
272
|
+
# Write +data+ to +word_offset+ (0 if not given) in register named
|
273
|
+
# +register_name+. The +data+ argument can be a String containing raw
|
274
|
+
# bytes (byte length must be multiple of 4), NArray.int, Array of integer
|
275
|
+
# values, or other object that responds to #to_i.
|
276
|
+
def write(register_name, *args)
|
277
|
+
word_offset = (args.length > 1) ? args.shift : 0
|
278
|
+
byte_offset = 4 * word_offset
|
279
|
+
args.flatten!
|
280
|
+
args.map! do |a|
|
281
|
+
case a
|
282
|
+
when String; a
|
283
|
+
when NArray; a.hton.to_s
|
284
|
+
when Array; a.pack('N*')
|
285
|
+
else [a.to_i].pack('N*')
|
286
|
+
end
|
287
|
+
end
|
288
|
+
data = args.join
|
289
|
+
byte_count = data.length
|
290
|
+
if byte_count % 4 != 0
|
291
|
+
raise "data length of #{byte_count} bytes is not a multiple of 4 bytes"
|
292
|
+
elsif byte_count == 0
|
293
|
+
warn "writing 0 bytes to #{register_name}"
|
294
|
+
end
|
295
|
+
resp = request(:write, register_name, register_offset, data)
|
296
|
+
raise resp.to_s unless resp.ok?
|
297
|
+
self
|
130
298
|
end
|
131
299
|
|
132
|
-
|
133
|
-
|
134
|
-
#
|
135
|
-
# Write a given payload to an offset in a register.
|
136
|
-
def write(register_name, register_offset, data_payload)
|
137
|
-
request(:write, register_name, register_offset, data_payload)
|
138
|
-
end
|
300
|
+
alias wordwrite write
|
301
|
+
alias []= write
|
139
302
|
|
140
303
|
end # class RoachClient
|
141
304
|
end # module KATCP
|
data/lib/katcp/client.rb
CHANGED
@@ -45,7 +45,7 @@ module KATCP
|
|
45
45
|
# "\n".
|
46
46
|
while line = @socket.gets("\n") do
|
47
47
|
# Split line into words and unescape each word
|
48
|
-
words = line.split.map! {|w| w.katcp_unescape!}
|
48
|
+
words = line.chomp.split(/[ \t]+/).map! {|w| w.katcp_unescape!}
|
49
49
|
# Handle requests, replies, and informs based on first character of
|
50
50
|
# first word.
|
51
51
|
case words[0][0,1]
|
data/lib/katcp/response.rb
CHANGED
@@ -162,26 +162,13 @@ module KATCP
|
|
162
162
|
end
|
163
163
|
|
164
164
|
# call-seq:
|
165
|
-
# payload -> String
|
166
|
-
# payload(:to_i) -> Integer or nil
|
167
|
-
# payload(:unpack, 'N*') -> Array_of_Integers or nil
|
168
|
-
# payload(:to_na, NArray::INT) -> NArray_of_ints or nil
|
169
|
-
# etc...
|
165
|
+
# payload -> String
|
170
166
|
#
|
171
167
|
# Returns contents of reply line ("words" joined by spaces) after status
|
172
|
-
# word if <tt>ok?</tt> returns true. Returns
|
173
|
-
# false or no payload exists.
|
174
|
-
|
175
|
-
|
176
|
-
# For example, passing <tt>:to_i</tt> will result in conversion of payload
|
177
|
-
# String to Integer via String#to_i. The String class can be
|
178
|
-
# monkey-patched as needed for additional conversion options.
|
179
|
-
def payload(*args)
|
180
|
-
if ok?
|
181
|
-
s = @lines[-1][2..-1].join(' ')
|
182
|
-
return s if args.empty?
|
183
|
-
s.send(*args)
|
184
|
-
end
|
168
|
+
# word if <tt>ok?</tt> returns true. Returns an empty string if
|
169
|
+
# <tt>ok?</tt> is false or no payload exists.
|
170
|
+
def payload
|
171
|
+
ok? ? @lines[-1][2..-1].join(' ') : ''
|
185
172
|
end
|
186
173
|
|
187
174
|
# call-seq:
|
data/lib/katcp/util.rb
CHANGED
@@ -8,7 +8,7 @@ class String
|
|
8
8
|
def katcp_escape!
|
9
9
|
empty? ? self[0..-1] = '\@' : self.gsub!(/[\\ \0\n\r\e\t]/) do |s|
|
10
10
|
case s
|
11
|
-
when "\\": '
|
11
|
+
when "\\": '\\\\'
|
12
12
|
when " " : '\_'
|
13
13
|
when "\0": '\0'
|
14
14
|
when "\n": '\n'
|
@@ -29,7 +29,7 @@ class String
|
|
29
29
|
def katcp_unescape!
|
30
30
|
self == '\@' ? self[0..-1] = '' : self.gsub!(/\\[\\_0nret]/) do |s|
|
31
31
|
case s
|
32
|
-
when '
|
32
|
+
when '\\\\': "\\"
|
33
33
|
when '\_': " "
|
34
34
|
when '\0': "\0"
|
35
35
|
when '\n': "\n"
|
@@ -48,26 +48,10 @@ class String
|
|
48
48
|
|
49
49
|
# call-seq:
|
50
50
|
# to_na(typecode) -> NArray
|
51
|
-
# to_na(typecode,
|
52
|
-
# to_na(typecode,size[, ...]) -> NArray
|
53
|
-
# to_na(typecode,size[, ...], byteswap) -> NArray
|
51
|
+
# to_na(typecode, size[, ...]) -> NArray
|
54
52
|
#
|
55
|
-
# Convert String to NArray accoring to +typecode
|
56
|
-
# given by Symbol +byteswap+. Pass +nil+ for +byteswap+ to skip the byte
|
57
|
-
# swap conversion.
|
58
|
-
#
|
59
|
-
# Because, as of this writing, KATCP servers typically run on big endian
|
60
|
-
# systems which return binary payload data in network byte order, the default
|
61
|
-
# +byteswap+ is <tt>:ntoh</tt>, which converts from network byte order to
|
62
|
-
# host (i.e. native) order.
|
53
|
+
# Convert String to NArray accoring to +typecode+.
|
63
54
|
def to_na(typecode, *args)
|
64
|
-
|
65
|
-
byteswap = :ntoh
|
66
|
-
if !args.empty? && (Symbol === args[-1] || args[-1].nil?)
|
67
|
-
byteswap = args.pop
|
68
|
-
end
|
69
|
-
na = NArray.to_na(self, typecode, *args)
|
70
|
-
na = na.send(byteswap) if byteswap
|
71
|
-
na
|
55
|
+
NArray.to_na(self, typecode, *args)
|
72
56
|
end
|
73
57
|
end
|
data/lib/katcp/version.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- David MacMahon
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-10-21 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -59,7 +59,7 @@ rdoc_options:
|
|
59
59
|
- -m
|
60
60
|
- README
|
61
61
|
- --title
|
62
|
-
- Ruby/KATCP 0.0.
|
62
|
+
- Ruby/KATCP 0.0.3 Documentation
|
63
63
|
require_paths:
|
64
64
|
- lib
|
65
65
|
required_ruby_version: !ruby/object:Gem::Requirement
|