origen 0.1.3 → 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.
@@ -1,103 +1,109 @@
1
- # rubocop:disable Style/RescueModifier:
2
- def name_audit(name)
3
- return name if name.nil?
4
- return nil unless name.is_a?(Symbol) || name.is_a?(String)
5
- if name == :inspect
6
- Origen.log.debug ':inspect is a reserved spec name'
7
- return nil
8
- end
9
- if name.match(/^\d/)
10
- Origen.log.debug "Spec #{name} starts with a number"
11
- return nil
12
- end
13
- if name.match(/\s+/)
14
- Origen.log.debug "Spec #{name} contains white space, removing it"
15
- name.delete!(/\s+/)
16
- end
17
- name.is_a?(String) ? name.downcase.to_sym : name
18
- end
19
-
20
- # Check that min, max are not mixed with typ. If a user wants
21
- # a baseline value for a spec use target as it will not be
22
- # checked against pass/fail
23
- def limits_ok?
24
- status = true
25
- if (@min.exp.to_s.include? '/') || (@max.exp.to_s.include? '/')
26
- return status
27
- end
28
- if @min.exp.nil? ^ @max.exp.nil?
29
- @limit_type = :single_sided
30
- if @typ.exp
31
- # status = false
32
- Origen.log.debug "Spec #{@name} has a typical limit defined with either min or max. They are mutually exclusive, use 'target' when using min or max"
33
- end
34
- elsif @min.exp && @max.exp
35
- @limit_type = :double_sided
36
- # Both min and max must be numerical to compare them
37
- if @min.value.is_a?(Numeric) && @max.value.is_a?(Numeric)
38
- # Check that min and max make sense
39
- if @max.value <= @min.value || @min.value >= @max.value
40
- status = false
41
- Origen.log.debug "Spec #{@name} has min (#{@min.value}) and max (#{@max.value}) reversed"
1
+ module Origen
2
+ module Specs
3
+ module Checkers
4
+ # rubocop:disable Style/RescueModifier:
5
+ def name_audit(name)
6
+ return name if name.nil?
7
+ return nil unless name.is_a?(Symbol) || name.is_a?(String)
8
+ if name == :inspect
9
+ Origen.log.debug ':inspect is a reserved spec name'
10
+ return nil
11
+ end
12
+ if name.match(/^\d/)
13
+ Origen.log.debug "Spec #{name} starts with a number"
14
+ return nil
15
+ end
16
+ if name.match(/\s+/)
17
+ Origen.log.debug "Spec #{name} contains white space, removing it"
18
+ name.delete!(/\s+/)
19
+ end
20
+ name.is_a?(String) ? name.downcase.to_sym : name
42
21
  end
43
- # Check that target is OK
44
- unless @target.nil?
45
- if @target.value <= @min.value || @target.value >= @max.value
46
- status = false
47
- Origen.log.debug "Spec #{@name} has a target (#{@target.value}) that is not within the min (#{@min.value}) and max #{@max.value}) values"
22
+
23
+ # Check that min, max are not mixed with typ. If a user wants
24
+ # a baseline value for a spec use target as it will not be
25
+ # checked against pass/fail
26
+ def limits_ok?
27
+ status = true
28
+ if (@min.exp.to_s.include? '/') || (@max.exp.to_s.include? '/')
29
+ return status
30
+ end
31
+ if @min.exp.nil? ^ @max.exp.nil?
32
+ @limit_type = :single_sided
33
+ if @typ.exp
34
+ # status = false
35
+ Origen.log.debug "Spec #{@name} has a typical limit defined with either min or max. They are mutually exclusive, use 'target' when using min or max"
36
+ end
37
+ elsif @min.exp && @max.exp
38
+ @limit_type = :double_sided
39
+ # Both min and max must be numerical to compare them
40
+ if @min.value.is_a?(Numeric) && @max.value.is_a?(Numeric)
41
+ # Check that min and max make sense
42
+ if @max.value <= @min.value || @min.value >= @max.value
43
+ status = false
44
+ Origen.log.debug "Spec #{@name} has min (#{@min.value}) and max (#{@max.value}) reversed"
45
+ end
46
+ # Check that target is OK
47
+ unless @target.nil?
48
+ if @target.value <= @min.value || @target.value >= @max.value
49
+ status = false
50
+ Origen.log.debug "Spec #{@name} has a target (#{@target.value}) that is not within the min (#{@min.value}) and max #{@max.value}) values"
51
+ end
52
+ end
53
+ end
48
54
  end
55
+ status
49
56
  end
50
- end
51
- end
52
- status
53
- end
54
57
 
55
- def get_mode
56
- spec_mode = nil
57
- if current_mode.nil?
58
- if self == Origen.top_level
59
- spec_mode = :global
60
- else
61
- spec_mode = :local
62
- end
63
- else
64
- spec_mode = current_mode.name
65
- end
66
- spec_mode
67
- end
58
+ def get_mode
59
+ spec_mode = nil
60
+ if current_mode.nil?
61
+ if self == Origen.top_level
62
+ spec_mode = :global
63
+ else
64
+ spec_mode = :local
65
+ end
66
+ else
67
+ spec_mode = current_mode.name
68
+ end
69
+ spec_mode
70
+ end
68
71
 
69
- def evaluate_limit(limit)
70
- return limit if limit.is_a?(Numeric) || limit.is_a?(Symbol)
71
- return nil if limit.nil?
72
- limit.gsub!("\n", ' ')
73
- limit.scrub!
74
- result = false
75
- if !!(limit.match(/^\d+\.\d+$/)) || !!(limit.match(/^-\d+\.\d+$/))
76
- result = Float(limit).round(4) rescue false # Use the same four digits of accuracy as the Spec model
77
- elsif !!(limit.match(/\d+\.\d+\s+\d+\.\d+/)) # workaround for multiple specs authoring bug
78
- Origen.log.debug "Found two numbers without an operator in the limit string '#{limit}', choosing the first..."
79
- first_number = limit.match(/(\d+\.\d+)\s+\d+\.\d+/).captures.first
80
- result = Float(first_number).round(4) rescue false # Use the same four digits of accuracy as the Spec model
81
- # elsif !!(limit.match(/^tbd$/i)) # unique case of TBD or To Be Determined, will convert to symbol
82
- # limit = limit.downcase.to_sym
83
- else
84
- result = Integer(limit) rescue false
85
- end
86
- if result == false
87
- # Attempt to eval the limit because users could write a limit like "3.3 + 50.mV"
88
- # which would not work with the code above but should eval to a number 3.35
89
- begin
90
- result = eval(limit)
91
- return result.round(4) if result.is_a? Numeric
92
- rescue SyntaxError, NameError, TypeError
93
- Origen.log.debug "Limit '#{limit}' had to be rescued, storing it as a #{limit.class}"
94
- if limit.is_a? Symbol
95
- return limit
72
+ def evaluate_limit(limit)
73
+ return limit if limit.is_a?(Numeric) || limit.is_a?(Symbol)
74
+ return nil if limit.nil?
75
+ limit.gsub!("\n", ' ')
76
+ limit.scrub!
77
+ result = false
78
+ if !!(limit.match(/^\d+\.\d+$/)) || !!(limit.match(/^-\d+\.\d+$/))
79
+ result = Float(limit).round(4) rescue false # Use the same four digits of accuracy as the Spec model
80
+ elsif !!(limit.match(/\d+\.\d+\s+\d+\.\d+/)) # workaround for multiple specs authoring bug
81
+ Origen.log.debug "Found two numbers without an operator in the limit string '#{limit}', choosing the first..."
82
+ first_number = limit.match(/(\d+\.\d+)\s+\d+\.\d+/).captures.first
83
+ result = Float(first_number).round(4) rescue false # Use the same four digits of accuracy as the Spec model
84
+ # elsif !!(limit.match(/^tbd$/i)) # unique case of TBD or To Be Determined, will convert to symbol
85
+ # limit = limit.downcase.to_sym
96
86
  else
97
- return "#{limit}"
87
+ result = Integer(limit) rescue false
98
88
  end
89
+ if result == false
90
+ # Attempt to eval the limit because users could write a limit like "3.3 + 50.mV"
91
+ # which would not work with the code above but should eval to a number 3.35
92
+ begin
93
+ result = eval(limit)
94
+ return result.round(4) if result.is_a? Numeric
95
+ rescue SyntaxError, NameError, TypeError
96
+ Origen.log.debug "Limit '#{limit}' had to be rescued, storing it as a #{limit.class}"
97
+ if limit.is_a? Symbol
98
+ return limit
99
+ else
100
+ return "#{limit}"
101
+ end
102
+ end
103
+ else
104
+ return result
105
+ end
106
+ end
99
107
  end
100
- else
101
- return result
102
108
  end
103
109
  end
@@ -1,12 +1,32 @@
1
1
  module Origen
2
2
  module Specs
3
- # This class is used to store spec note information used to document IP
3
+ # Ruby Data Class that contains Creation Information for the IP Block
4
4
  class Creation_Info
5
- attr_accessor :author, :date, :revision, :source, :tool, :tool_version
5
+ attr_accessor :author, :date, :revision, :source, :tool, :tool_version, :ip_version
6
6
 
7
- def initialize(author, date, src_info = {}, tool_info = {})
7
+ # Initialize the Creation Info block to store data for latest version of the file.
8
+ #
9
+ # ==== Parameters
10
+ #
11
+ # * author # Author/Subject Matter Expert for the IP Block
12
+ # * date # Date that the File was released to Downstream Audiences
13
+ # ==== Source Information
14
+ #
15
+ # * :revision # Revision Information
16
+ # * :source # Where the Information came from
17
+ #
18
+ # ==== Tool Info
19
+ #
20
+ # * :tool # Tool that created the initial XML file
21
+ # * :version # Version of the Tool that created the XML file
22
+ #
23
+ # ==== Example
24
+ #
25
+ # Creation_Info.new("author", "07/10/2015", :revision => "5.4", :source => "CSV", :tool => "oRiGeN", :tool_version => "0.0.6")
26
+ def initialize(author, date, ip_version, src_info = {}, tool_info = {})
8
27
  @author = author
9
28
  @date = date
29
+ @ip_version = ip_version
10
30
  @revision = src_info[:revision]
11
31
  @source = src_info[:source]
12
32
  @tool = tool_info[:tool]
@@ -3,7 +3,8 @@ module Origen
3
3
  class Spec
4
4
  autoload :Note, 'origen/specs/note'
5
5
  autoload :Exhibit, 'origen/specs/exhibit'
6
- require_relative 'checkers'
6
+ include Checkers
7
+ extend Checkers
7
8
 
8
9
  SpecAttribute = Struct.new(:name, :type, :required, :author, :description)
9
10
 
@@ -106,6 +107,12 @@ module Origen
106
107
  fail "Spec #{name} failed the limits audit!" unless limits_ok?
107
108
  end
108
109
 
110
+ def inspect
111
+ $dut.send(:specs_to_table_string, [self])
112
+ rescue
113
+ super
114
+ end
115
+
109
116
  def method_missing(method, *args, &block)
110
117
  ivar = "@#{method.to_s.gsub('=', '')}"
111
118
  ivar_sym = ":#{ivar}"
@@ -66,16 +66,25 @@ module Origen
66
66
  end
67
67
 
68
68
  def name
69
- @name || @id
69
+ @name || name_from_rc || @id
70
+ end
71
+
72
+ def name_from_rc
73
+ RevisionControl::Git.user_name
70
74
  end
71
75
 
72
76
  def email
73
- return @email if @email
74
- if Origen.site_config.email_domain
75
- "#{id}@#{Origen.site_config.email_domain}"
77
+ @email || email_from_rc || begin
78
+ if Origen.site_config.email_domain
79
+ "#{id}@#{Origen.site_config.email_domain}"
80
+ end
76
81
  end
77
82
  end
78
83
 
84
+ def email_from_rc
85
+ RevisionControl::Git.user_email
86
+ end
87
+
79
88
  # Fetch user data from the FSL application directory
80
89
  #
81
90
  # @example
@@ -8,5 +8,6 @@ module Origen
8
8
  autoload :TimeAndDate, 'origen/utility/time_and_date'
9
9
  autoload :InputCapture, 'origen/utility/input_capture'
10
10
  autoload :BlockArgs, 'origen/utility/block_args'
11
+ autoload :FileDiff, 'origen/utility/file_diff.rb'
11
12
  end
12
13
  end
@@ -101,9 +101,13 @@ module Origen
101
101
  if @comment_char
102
102
  # Screen off any inline comments at the end of line
103
103
  begin
104
- [@comment_char].flatten.each do |_char|
105
- if line =~ /(.*)#{@char}.*/
106
- return Regexp.last_match[1].strip
104
+ [@comment_char].flatten.each do |comchar|
105
+ unless line =~ /^\s*#{comchar}/
106
+ if line =~ /(.*)\s*#{comchar}.*/
107
+ return Regexp.last_match[1].strip
108
+ else
109
+ return line.strip
110
+ end
107
111
  end
108
112
  end
109
113
  # This rescue is a crude way to guard against non-ASCII files that find
@@ -0,0 +1,213 @@
1
+ module Origen
2
+ module Utility
3
+ module FileDiff
4
+ class InputFile < Array
5
+ attr_accessor :pointer
6
+
7
+ def initialize
8
+ self.pointer = 0
9
+ end
10
+
11
+ def current_line
12
+ self[pointer]
13
+ end
14
+
15
+ def advance_pointer!
16
+ self.pointer += 1
17
+ end
18
+
19
+ def find_current_line_in(other)
20
+ index = (other[other.pointer..-1] || []).index(current_line)
21
+ index.nil? ? nil : other.pointer + index
22
+ end
23
+ end
24
+
25
+ class OutputFile < Array
26
+ class Line < String
27
+ attr_accessor :type, :original_number
28
+ def initialize(type, input_file)
29
+ self.type = type
30
+ return unless input_file
31
+ replace(input_file.current_line)
32
+ self.original_number = input_file.pointer + 1
33
+ input_file.advance_pointer!
34
+ end
35
+ end
36
+
37
+ def add_line(type, input_file = nil)
38
+ push(Line.new(type, input_file))
39
+ end
40
+ end
41
+
42
+ # Diff Processor (Origen::Utility::Processor) provides an easy way to diff the contents of two files
43
+ # and display the differences as an HTML file or a TXT file.
44
+ # Very basic functionality, but can be expanded to add more features in the future.
45
+ # Comments are not ignored for now (maybe a future enhancement)
46
+ # Each difference is displayed in a different color in the HTML page
47
+ # Legend:
48
+ # - New: Light Green
49
+ # - Modified: Light Gray
50
+ # - Deleted: Pink
51
+ # Usage:
52
+ # processor = Origen::Utility::FileDiff::Processor.new("#{Origen.root}/left.txt", "#{Origen.root}/right.txt")
53
+ #
54
+ # To Generate a HTML file (diff.html) showing the differences
55
+ # Origen::Utility::FileDiff::Formatter::Html.new(processor.process!, "#{Origen.root}/diff.html").format
56
+ #
57
+ # To Generate a TXT file (diff.txt) showing the differences
58
+ # Origen::Utility::FileDiff::Formatter::Text.new(processor.process!, "#{Origen.root}/diff.txt").format
59
+ class Processor
60
+ attr_accessor :source, :target
61
+ attr_accessor :source_output, :target_output
62
+ def initialize(source_file_name, target_file_name)
63
+ self.source = InputFile.new
64
+ self.target = InputFile.new
65
+ self.source_output = OutputFile.new
66
+ self.target_output = OutputFile.new
67
+ IO.readlines(source_file_name).each do |line|
68
+ source << line
69
+ end
70
+ IO.readlines(target_file_name).each do |line|
71
+ target << line
72
+ end
73
+ end
74
+
75
+ def handle_exactly_matched
76
+ source_output.add_line(:unchanged, source)
77
+ target_output.add_line(:unchanged, target)
78
+ end
79
+
80
+ def handle_line_changed
81
+ source_output.add_line(:changed, source)
82
+ target_output.add_line(:changed, target)
83
+ end
84
+
85
+ def handle_block_added(size)
86
+ size.times do
87
+ source_output.add_line(:added) # Empty line in the left side of the diff
88
+ target_output.add_line(:added, target)
89
+ end
90
+ end
91
+
92
+ def handle_block_deleted(size)
93
+ size.times do
94
+ source_output.add_line(:deleted, source)
95
+ target_output.add_line(:deleted) # Empty line in the right side of the diff
96
+ end
97
+ end
98
+
99
+ def process!
100
+ while source.pointer < source.size && target.pointer < target.size
101
+ matched = source.find_current_line_in(target)
102
+ if matched
103
+ if matched > target.pointer
104
+ deleted = target.find_current_line_in(source)
105
+ handle_block_deleted(deleted - source.pointer) if deleted
106
+ end
107
+ handle_block_added(matched - target.pointer)
108
+ handle_exactly_matched
109
+ else
110
+ found = target.find_current_line_in(source)
111
+ if found
112
+ handle_block_deleted(found - source.pointer)
113
+ else
114
+ handle_line_changed
115
+ end
116
+ end
117
+ end
118
+ handle_block_deleted(source.size - source.pointer)
119
+ handle_block_added(target.size - target.pointer)
120
+
121
+ self
122
+ end
123
+ end
124
+
125
+ module Formatter
126
+ class Base
127
+ attr_accessor :source_output, :target_output, :file
128
+ def initialize(processed_diff, output_file_name)
129
+ self.source_output = processed_diff.source_output
130
+ self.target_output = processed_diff.target_output
131
+ self.file = File.open(output_file_name, 'w')
132
+ end
133
+ end
134
+
135
+ class Html < Base
136
+ def format
137
+ tag(:style) { content('td{vertical-align: middle} pre{margin: 0px} .added{background-color: lightgreen;}.deleted{background-color: pink;}.changed{background-color: lightgray;}.line{background-color: lightblue}') }
138
+ tag :table, cellpaddig: 0, cellspacing: 0 do
139
+ source_output.each_with_index do |src, i|
140
+ tgt = target_output[i]
141
+ tag :tr do
142
+ tag(:td, class: :line) { tag(:pre) { content(src.original_number) } }
143
+ tag(:td, class: src.type) { tag(:pre) { content(src) } }
144
+ tag(:td, class: :line) { tag(:pre) { content(tgt.original_number) } }
145
+ tag(:td, class: tgt.type) { tag(:pre) { content(tgt) } }
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ private
152
+
153
+ def tag(name, options = {}, &block)
154
+ file.puts %(<#{name})
155
+ file.puts options.collect { |attribute, value| %(#{attribute}="#{value}") }
156
+ file.puts '>'
157
+ yield
158
+ file.puts "</#{name}>"
159
+ end
160
+
161
+ def content(inner_text)
162
+ file.puts(inner_text.to_s == '' ? '&nbsp;' : inner_text)
163
+ end
164
+ end
165
+
166
+ class Text < Base
167
+ def format
168
+ pointer = 0
169
+ while pointer < target_output.size
170
+ size = 1
171
+ type = source_output[pointer].type
172
+ case type
173
+ when :added
174
+ added(pointer, size = get_block_size(pointer, :added))
175
+ when :deleted
176
+ deleted(pointer, size = get_block_size(pointer, :deleted))
177
+ when :changed
178
+ changed(pointer, size = get_block_size(pointer, :changed))
179
+ end
180
+ file.puts unless type == :unchanged
181
+ pointer += size
182
+ end
183
+ end
184
+
185
+ private
186
+
187
+ def get_block_size(pointer, type)
188
+ size = 1
189
+ size += 1 while target_output[pointer + size].type == type
190
+ size
191
+ end
192
+
193
+ def added(pointer, size)
194
+ file.puts(target_output[pointer].original_number)
195
+ 0.upto(size - 1) { |i| file.puts("+ #{target_output[pointer + i]}") }
196
+ end
197
+
198
+ def deleted(pointer, size)
199
+ file.puts(source_output[pointer].original_number)
200
+ 0.upto(size - 1) { |i| file.puts("- #{source_output[pointer + i]}") }
201
+ end
202
+
203
+ def changed(pointer, size)
204
+ file.puts("#{source_output[pointer].original_number},#{target_output[pointer].original_number}")
205
+ 0.upto(size - 1) { |i| file.puts("source<< #{source_output[pointer + i]}") }
206
+ file.puts('=======')
207
+ 0.upto(size - 1) { |i| file.puts("target>> #{target_output[pointer + i]}") }
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end