origen 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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