cross_origen 0.5.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 +7 -0
- data/config/application.rb +78 -0
- data/config/commands.rb +47 -0
- data/config/development.rb +17 -0
- data/config/environment.rb +35 -0
- data/config/users.rb +18 -0
- data/config/version.rb +8 -0
- data/lib/cross_origen/design_sync.rb +54 -0
- data/lib/cross_origen/headers.rb +21 -0
- data/lib/cross_origen/ip_xact.rb +243 -0
- data/lib/cross_origen/origen_format.rb +541 -0
- data/lib/cross_origen/ralf.rb +15 -0
- data/lib/cross_origen/test/dut.rb +51 -0
- data/lib/cross_origen/xml_doc.rb +252 -0
- data/lib/cross_origen.rb +108 -0
- data/templates/headers/default.h.erb +18 -0
- data/templates/ralf/_register.ralf.erb +21 -0
- data/templates/ralf/default.ralf.erb +37 -0
- data/templates/test/default.ralf.erb +1 -0
- data/templates/test/headers_default.h.erb +1 -0
- data/templates/test/ip_xact.xml.erb +1 -0
- data/templates/web/_history.md +547 -0
- data/templates/web/example.md.erb +73 -0
- data/templates/web/examples/ip_xact_export.md.erb +25 -0
- data/templates/web/examples/origen_export.md.erb +96 -0
- data/templates/web/examples/ralf_export.md.erb +18 -0
- data/templates/web/examples.md.erb +13 -0
- data/templates/web/index.md.erb +104 -0
- data/templates/web/layouts/_basic.html.erb +13 -0
- data/templates/web/layouts/_doc.html.erb +61 -0
- data/templates/web/partials/_navbar.html.erb +23 -0
- data/templates/web/release_notes.md.erb +5 -0
- metadata +117 -0
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'kramdown'
|
2
|
+
require 'sanitize'
|
3
|
+
|
4
|
+
module CrossOrigen
|
5
|
+
# This is the base class of all doc formats that are
|
6
|
+
# XML based
|
7
|
+
class XMLDoc
|
8
|
+
CreationInfo = Struct.new(:author, :date, :revision, :source)
|
9
|
+
|
10
|
+
ImportInfo = Struct.new(:name, :date)
|
11
|
+
|
12
|
+
attr_accessor :creation_info, :import_info
|
13
|
+
|
14
|
+
# These (in many cases illegal) tags will be forced to their valid equivalents
|
15
|
+
# These will be executed in the defined order, so for later xfrms you can for example
|
16
|
+
# assume that all 'rows' have already been converted to 'tr'
|
17
|
+
# valid equivalents
|
18
|
+
HTML_TRANSFORMS = {
|
19
|
+
'table/title' => 'caption',
|
20
|
+
'table//row' => 'tr',
|
21
|
+
'thead//entry' => 'th',
|
22
|
+
'table//entry' => 'td',
|
23
|
+
'td/p' => 'span',
|
24
|
+
'th/p' => 'span'
|
25
|
+
}
|
26
|
+
|
27
|
+
# This can be used to perform additional by-node transformation if required, normally
|
28
|
+
# this should be used if transform of a node attribute is required
|
29
|
+
HTML_TRANSFORMER = lambda do |env|
|
30
|
+
if env[:node_name] == 'td' || env[:node_name] == 'th'
|
31
|
+
if env[:node].attr('nameend')
|
32
|
+
first = env[:node].attr('namest').sub('col', '').to_i
|
33
|
+
last = env[:node].attr('nameend').sub('col', '').to_i
|
34
|
+
env[:node].set_attribute('colspan', (last - first + 1).to_s)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Defines the rules for sanitization of any HTML strings that will be converted
|
40
|
+
# to markdown for representation within Origen
|
41
|
+
HTML_SANITIZATION_CONFIG = {
|
42
|
+
# Only these tags will be allowed through, everything else will be stripped
|
43
|
+
# Note that this is applied after the transforms listed above
|
44
|
+
elements: %w(b em i strong u p ul ol li table tr td th tbody thead),
|
45
|
+
attributes: {
|
46
|
+
'td' => ['colspan'],
|
47
|
+
'th' => ['colspan']
|
48
|
+
},
|
49
|
+
# Not planning to allow any of these right now, but keeping around
|
50
|
+
# as an example of how to do so
|
51
|
+
#:protocols => {
|
52
|
+
# 'a' => {'href' => ['http', 'https', 'mailto']}
|
53
|
+
# }
|
54
|
+
transformers: HTML_TRANSFORMER
|
55
|
+
}
|
56
|
+
|
57
|
+
# Returns the object that included the CrossOrigen module
|
58
|
+
attr_reader :owner
|
59
|
+
|
60
|
+
def initialize(owner)
|
61
|
+
@owner = owner
|
62
|
+
@creation_info = CreationInfo.new
|
63
|
+
@import_info = ImportInfo.new
|
64
|
+
end
|
65
|
+
|
66
|
+
# Tries the given methods on the owner and returns the first one to return a value,
|
67
|
+
# ultimately returns nil if no value is found.
|
68
|
+
#
|
69
|
+
# To test an object other than the owner pass it as the first argument.
|
70
|
+
def try(*methods)
|
71
|
+
if methods.first.is_a?(Symbol)
|
72
|
+
obj = owner
|
73
|
+
else
|
74
|
+
obj = methods.shift
|
75
|
+
end
|
76
|
+
methods.each do |method|
|
77
|
+
if obj.respond_to?(method)
|
78
|
+
val = obj.send(method)
|
79
|
+
return val if val
|
80
|
+
end
|
81
|
+
end
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
# This returns the doc wrapped by a Nokogiri doc
|
86
|
+
def doc(path, _options = {})
|
87
|
+
require 'nokogiri'
|
88
|
+
|
89
|
+
File.open(path) do |f|
|
90
|
+
yield Nokogiri::XML(f)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def extract(element, path, options = {})
|
95
|
+
options = {
|
96
|
+
format: :string,
|
97
|
+
hex: false,
|
98
|
+
default: nil,
|
99
|
+
downcase: false,
|
100
|
+
return: :text,
|
101
|
+
# A value or array or values which are considered to be nil, if this is the value
|
102
|
+
# to be returned then nil will be returned instead
|
103
|
+
nil_on: false
|
104
|
+
}.merge(options)
|
105
|
+
node = element.at_xpath(path)
|
106
|
+
if node
|
107
|
+
if options[:format] == :string
|
108
|
+
str = node.send(options[:return]).strip
|
109
|
+
str = str.downcase if options[:downcase]
|
110
|
+
if options[:nil_on] && [options[:nil_on]].flatten.include?(str)
|
111
|
+
nil
|
112
|
+
else
|
113
|
+
str
|
114
|
+
end
|
115
|
+
elsif options[:format] == :integer
|
116
|
+
val = node.send(options[:return])
|
117
|
+
if val =~ /^0x(.*)/
|
118
|
+
Regexp.last_match[1].to_i(16)
|
119
|
+
elsif options[:hex]
|
120
|
+
val.to_i(16)
|
121
|
+
else
|
122
|
+
val.to_i(10)
|
123
|
+
end
|
124
|
+
else
|
125
|
+
fail "Unknown format: #{options[:format]}"
|
126
|
+
end
|
127
|
+
else
|
128
|
+
options[:default]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Freescale register descriptions are like the wild west, need to do some pre-screening
|
133
|
+
# to approach valid HTML before handing off to other off the shelf sanitizers
|
134
|
+
def pre_sanitize(html)
|
135
|
+
html = Nokogiri::HTML.fragment(html)
|
136
|
+
HTML_TRANSFORMS.each do |orig, new|
|
137
|
+
html.xpath(".//#{orig}").each { |node| node.name = new }
|
138
|
+
end
|
139
|
+
html.to_html
|
140
|
+
end
|
141
|
+
|
142
|
+
# Does its best to convert the given html fragment to markdown
|
143
|
+
#
|
144
|
+
# The final markdown may still contain some HTML tags, but any weird
|
145
|
+
# markup which may break a future markdown -> html conversion will
|
146
|
+
# be removed
|
147
|
+
def to_markdown(html, _options = {})
|
148
|
+
cleaned = html.scrub
|
149
|
+
cleaned = pre_sanitize(cleaned)
|
150
|
+
cleaned = Sanitize.fragment(cleaned, HTML_SANITIZATION_CONFIG)
|
151
|
+
Kramdown::Document.new(cleaned, input: :html).to_kramdown.strip
|
152
|
+
rescue
|
153
|
+
'The description could not be imported, the most likely cause of this is that it contained illegal HTML markup'
|
154
|
+
end
|
155
|
+
|
156
|
+
# Convert the given markdown string to HTML
|
157
|
+
def to_html(string, _options = {})
|
158
|
+
# Escape any " that are not already escaped
|
159
|
+
string.gsub!(/([^\\])"/, '\1\"')
|
160
|
+
# Escape any ' that are not already escaped
|
161
|
+
string.gsub!(/([^\\])'/, %q(\1\\\'))
|
162
|
+
html = Kramdown::Document.new(string, input: :kramdown).to_html
|
163
|
+
end
|
164
|
+
|
165
|
+
# fetch an XML snippet passed and extract and format the data
|
166
|
+
def fetch(xml, options = {})
|
167
|
+
options = {
|
168
|
+
type: String,
|
169
|
+
downcase: false,
|
170
|
+
symbolize: false,
|
171
|
+
strip: false,
|
172
|
+
squeeze: false,
|
173
|
+
squeeze_lines: false,
|
174
|
+
rm_specials: false,
|
175
|
+
whitespace: false,
|
176
|
+
get_text: false,
|
177
|
+
to_i: false,
|
178
|
+
to_html: false,
|
179
|
+
to_bool: false,
|
180
|
+
children: false,
|
181
|
+
to_dec: false,
|
182
|
+
to_f: false,
|
183
|
+
underscore: false
|
184
|
+
}.update(options)
|
185
|
+
options[:symbolize] = options[:to_sym] if options[:to_sym]
|
186
|
+
# Check for incompatible options
|
187
|
+
xml_orig = xml
|
188
|
+
numeric_methods = [:to_i, :to_f, :to_dec]
|
189
|
+
if options[:get_text] == true && options[:to_html] == true
|
190
|
+
fail 'Cannot use :get_text and :to_html options at the same time, exiting...'
|
191
|
+
end
|
192
|
+
if options[:symbolize] == true
|
193
|
+
fail 'Cannot convert to a number of any type and symbolize at the same time' if numeric_methods.reject { |arg| options[arg] == true }.size < 3
|
194
|
+
end
|
195
|
+
fail 'Cannot select multiple numeric conversion args at the same time' if numeric_methods.reject { |arg| options[arg] == true }.size < 2
|
196
|
+
if xml.nil?
|
197
|
+
Origen.log.debug 'XML data is nil!'
|
198
|
+
return nil
|
199
|
+
end
|
200
|
+
xml = xml.text if options[:get_text] == true
|
201
|
+
# Sometimes XML snippets get sent as nodes or as Strings
|
202
|
+
# Must skip this code if a String as it is designed to change
|
203
|
+
# the XML node into a string
|
204
|
+
unless xml.is_a? String
|
205
|
+
if options[:to_html] == true
|
206
|
+
if xml.children
|
207
|
+
# If there are children to this XMl node then grab the content there
|
208
|
+
if xml.children.empty? || options[:children] == false
|
209
|
+
xml = xml.to_html
|
210
|
+
else
|
211
|
+
xml = xml.children.to_html
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
unless xml.is_a? options[:type]
|
217
|
+
Origen.log.debug "XML data is not of correct type '#{options[:type]}'"
|
218
|
+
Origen.log.debug "xml is \n#{xml}"
|
219
|
+
return nil
|
220
|
+
end
|
221
|
+
if options[:type] == String
|
222
|
+
if xml.match(/\s+/) && options[:whitespace] == false
|
223
|
+
Origen.log.debug "XML data '#{xml}' cannot have white space"
|
224
|
+
return nil
|
225
|
+
end
|
226
|
+
xml.downcase! if options[:downcase] == true
|
227
|
+
xml = xml.underscore if options[:underscore] == true
|
228
|
+
xml.strip! if options[:strip] == true
|
229
|
+
xml.squeeze!(' ') if options[:squeeze] == true
|
230
|
+
xml = xml.squeeze_lines if options[:squeeze_lines] == true
|
231
|
+
xml.gsub!(/[^0-9A-Za-z]/, '_') if options[:rm_specials] == true
|
232
|
+
if options[:symbolize] == true
|
233
|
+
return xml.to_sym
|
234
|
+
elsif options[:to_i] == true
|
235
|
+
return xml.to_i
|
236
|
+
elsif options[:to_dec] == true
|
237
|
+
return xml.to_dec
|
238
|
+
elsif options[:to_f] == true
|
239
|
+
return xml.to_f
|
240
|
+
elsif [true, false].include?(xml.to_bool) && options[:to_bool] == true
|
241
|
+
# If the string can convert to Boolean then return TrueClass or FalseClass
|
242
|
+
return xml.to_bool
|
243
|
+
else
|
244
|
+
return xml
|
245
|
+
end
|
246
|
+
else
|
247
|
+
# No real examples yet of non-string content
|
248
|
+
return xml
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
data/lib/cross_origen.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'origen'
|
2
|
+
require_relative '../config/application.rb'
|
3
|
+
require_relative '../config/environment.rb'
|
4
|
+
|
5
|
+
module CrossOrigen
|
6
|
+
if RUBY_VERSION < '2.0.0'
|
7
|
+
require 'scrub_rb'
|
8
|
+
end
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
include Origen::Model
|
13
|
+
end
|
14
|
+
|
15
|
+
def instance_respond_to?(method_name)
|
16
|
+
public_methods.include?(method_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def cr_import(options = {})
|
20
|
+
file = cr_file(options)
|
21
|
+
cr_translator(file, options).import(file, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_ralf(options = {})
|
25
|
+
cr_ralf.owner_to_ralf(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_ip_xact(options = {})
|
29
|
+
cr_ip_xact.owner_to_xml(options)
|
30
|
+
end
|
31
|
+
alias_method :to_ipxact, :to_ip_xact
|
32
|
+
|
33
|
+
def to_origen(options = {})
|
34
|
+
options[:obj] = self
|
35
|
+
cr_to_origen(options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_header(options = {})
|
39
|
+
cr_headers.owner_to_header(options)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Tries the given methods and returns the first one to return a value,
|
43
|
+
# ultimately returns nil if no value is found.
|
44
|
+
def cr_try(*methods)
|
45
|
+
methods.each do |method|
|
46
|
+
if self.respond_to?(method)
|
47
|
+
val = send(method)
|
48
|
+
return val if val
|
49
|
+
end
|
50
|
+
end
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns an instance of the DesignSync interface
|
55
|
+
def cr_design_sync
|
56
|
+
@cr_design_sync ||= DesignSync.new(self)
|
57
|
+
end
|
58
|
+
|
59
|
+
def cr_headers
|
60
|
+
@cr_headers ||= Headers.new(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Creates Ruby files necessary to model all sub_blocks and registers found (recursively) owned by options[:obj]
|
64
|
+
# The Ruby files are created at options[:path] (app output directory by default)
|
65
|
+
def cr_to_origen(options = {})
|
66
|
+
options = {
|
67
|
+
obj: $dut,
|
68
|
+
path: Origen.app.config.output_directory
|
69
|
+
}.update(options)
|
70
|
+
# This method assumes and checks for $self to contain Origen::Model
|
71
|
+
error "ERROR: #{options[:obj].class} does not contain Origen::Model as required" unless options[:obj].class < Origen::Model
|
72
|
+
# Check to make sure there are sub_blocks or regs directly under $dut
|
73
|
+
error "ERROR: options[:obj]ect #{options[:obj].object_id} of class #{options[:obj].class} does not contain registers or sub_blocks" unless options[:obj].owns_registers? || options[:obj].instance_respond_to?(:sub_blocks)
|
74
|
+
OrigenFormat.new(options).export
|
75
|
+
end
|
76
|
+
|
77
|
+
def cr_ralf
|
78
|
+
@cr_ralf ||= Ralf.new(self)
|
79
|
+
end
|
80
|
+
|
81
|
+
def cr_ip_xact
|
82
|
+
@cr_ip_xact ||= IpXact.new(self)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
# Returns an instance of the translator for the format of the given file
|
88
|
+
def cr_translator(file, _options = {})
|
89
|
+
snippet = IO.read(file, 2000) # Read first 2000 characters
|
90
|
+
case snippet
|
91
|
+
when /spiritconsortium/
|
92
|
+
cr_ip_xact
|
93
|
+
else
|
94
|
+
fail "Unknown file format for file: #{file}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns a local path to the given file defined by the options.
|
99
|
+
def cr_file(options = {})
|
100
|
+
if options[:path]
|
101
|
+
options[:path]
|
102
|
+
elsif options[:vault]
|
103
|
+
cr_design_sync.fetch(options)
|
104
|
+
else
|
105
|
+
fail 'You must supply a :path or :vault option pointing to the import file!'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
% full_name = cr_try(:full_name, :ip_name, :name, :pdm_part_name) || self.class.to_s
|
2
|
+
//---------------------------------------------------------------------------------------------
|
3
|
+
// <%= full_name %> Registers Memory Map
|
4
|
+
//---------------------------------------------------------------------------------------------
|
5
|
+
% regs.each do |name, reg|
|
6
|
+
#define <%= name.to_s.upcase.ljust(30) %> (REG<%= reg.size %>(0x<%= reg.address.to_s(16).upcase %>))
|
7
|
+
% end
|
8
|
+
|
9
|
+
//---------------------------------------------------------------------------------------------
|
10
|
+
// <%= full_name %> Registers Bit Definition
|
11
|
+
//---------------------------------------------------------------------------------------------
|
12
|
+
% regs.each do |reg_name, reg|
|
13
|
+
// <%= reg_name.to_s.upcase %>
|
14
|
+
% reg.named_bits do |name, bits|
|
15
|
+
#define <%= "#{reg_name}_#{name}".upcase.ljust(30) %> (<%= bits.sort_by{ |bit| bit.position }.reverse.map{ |bit| "BIT#{bit.position}"}.join(" + ") %>)
|
16
|
+
% end
|
17
|
+
|
18
|
+
% end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
% reg = options[:reg]
|
2
|
+
register <%= options[:name].to_s.downcase %> (<%= reg.path(:relative_to => options[:parent]) + options[:reg_path_postfix] %>) @'h<%= reg.offset.to_s(16).upcase %> {
|
3
|
+
bytes <%= reg.size / 8 %>;
|
4
|
+
% reg.named_bits do |name, bits|
|
5
|
+
% if bits.size == 1
|
6
|
+
field <%= name %> ([<%= bits.position %>]) {
|
7
|
+
% else
|
8
|
+
field <%= name %> ([<%= bits.position + bits.size - 1 %>:<%= bits.position %>]) {
|
9
|
+
% end
|
10
|
+
% if bits.is_readable? && bits.is_writable?
|
11
|
+
access rw;
|
12
|
+
% elsif bits.is_writable?
|
13
|
+
access wo;
|
14
|
+
% else bits.is_writable?
|
15
|
+
access ro;
|
16
|
+
% end
|
17
|
+
bits <%= bits.size %>;
|
18
|
+
reset 'b<%= bits.reset_val.to_s(2) %>;
|
19
|
+
}
|
20
|
+
% end
|
21
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
% # The following options are available:
|
2
|
+
% options[:reg_path_postfix] ||= ""
|
3
|
+
%
|
4
|
+
system <%= (cr_try(:path, :ip_name, :name) || self.class.to_s.split("::").last).downcase %> {
|
5
|
+
% unless regs.empty?
|
6
|
+
% # Mapping this to the size of all registers, correct?
|
7
|
+
bytes <%= regs.inject(0){ |sum, reg| sum + reg[1].size } / 8 %>;
|
8
|
+
% regs.each do |reg_name, reg|
|
9
|
+
<%= render "register", options.merge(name: reg_name, reg: reg, parent: self, indent: 4) %>
|
10
|
+
|
11
|
+
% end
|
12
|
+
% end
|
13
|
+
|
14
|
+
% sub_blocks.each do |name, block|
|
15
|
+
% # Should this be the address?
|
16
|
+
block <%= name %> (<%= block.path(:relative_to => self) %>) @'h<%= block.reg_base_address.to_s(16).upcase %> {
|
17
|
+
% # Mapping this to the size of all registers, correct?
|
18
|
+
bytes <%= regs.inject(0){ |sum, reg| sum + reg[1].size } / 8 %>;
|
19
|
+
% block.regs.each do |reg_name, reg|
|
20
|
+
<%= render "register", options.merge(name: reg_name, reg: reg, parent: block, indent: 8) %>
|
21
|
+
|
22
|
+
% end
|
23
|
+
% block.domains.each do |domain_name, domain|
|
24
|
+
domain <%= domain_name %> {
|
25
|
+
bytes 1;
|
26
|
+
endian <%= domain.endian %>;
|
27
|
+
% block.regs.each do |reg_name, reg|
|
28
|
+
<%= render "register", options.merge(name: "#{reg_name}_#{domain_name}", reg: reg, parent: block, indent: 12) %>
|
29
|
+
|
30
|
+
% end
|
31
|
+
}
|
32
|
+
|
33
|
+
% end
|
34
|
+
}
|
35
|
+
|
36
|
+
% end
|
37
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= $dut.to_ralf %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= $dut.to_header %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= $dut.to_ip_xact format: :uvm %>
|