art-decomp 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -34,7 +34,7 @@ Jeweler::Tasks.new do |gem|
34
34
 
35
35
  gem.files.exclude 'kiss/**/*'
36
36
 
37
- gem.add_dependency 'teejayvanslyke-gazer'
37
+ gem.add_dependency 'rcapture'
38
38
  gem.add_dependency 'trollop'
39
39
 
40
40
  gem.add_development_dependency 'diff-lcs'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.4.0
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative '../lib/art-decomp'
5
+
6
+ file = ARGV.first
7
+ name = File.basename(file).gsub /[^a-z0-9]/, '_'
8
+ puts ArtDecomp::FSM.from_kiss(file).to_vhdl name
@@ -37,6 +37,7 @@ end
37
37
 
38
38
 
39
39
  require 'set'
40
+ require 'yaml'
40
41
 
41
42
  require_relative 'core/enumerable'
42
43
  require_relative 'core/file'
@@ -65,3 +66,4 @@ require_relative 'art-decomp/uv_relevance_generator'
65
66
  require_relative 'art-decomp/uv_generator/braindead'
66
67
  require_relative 'art-decomp/uv_generator/general_relevance'
67
68
  require_relative 'art-decomp/uv_generator/unique_relevance'
69
+ require_relative 'art-decomp/vhdl'
@@ -1,7 +1,7 @@
1
1
  module ArtDecomp class Bipainter
2
2
 
3
3
  def initialize beta_q, beta_v, seps
4
- raise 'non-disjoint beta_v' if beta_v.ints.pairs.any? { |a, b| a & b != 0 }
4
+ raise 'non-disjoint beta_v' unless beta_v.ints.pairs.all? { |a, b| (a & b).zero? }
5
5
  @beta_v = beta_v
6
6
  @qv_colours = {}
7
7
  @g_colours = {}
@@ -35,13 +35,13 @@ module ArtDecomp class Bipainter
35
35
  @qv_forbidden.default_proc = proc { |h, k| h[k] = Set[] }
36
36
  end
37
37
 
38
- def restore!
39
- @g_colours = Marshal.load @backup[:g_colours]
40
- @g_forbidden = Marshal.load @backup[:g_forbidden]
41
- @qv_colours = Marshal.load @backup[:qv_colours]
42
- @qv_forbidden = Marshal.load @backup[:qv_forbidden]
43
- @g_forbidden.default_proc = proc { |h, k| h[k] = Set[] }
44
- @qv_forbidden.default_proc = proc { |h, k| h[k] = Set[] }
38
+ def colour_g! g_vertex, colour
39
+ return if @g_colours[g_vertex] == colour
40
+ raise PaintingError if @g_colours[g_vertex] and @g_colours[g_vertex] != colour
41
+ raise PaintingError if @g_forbidden[g_vertex].include? colour
42
+ @g_colours[g_vertex] = colour
43
+ @g_graph.adjacent(g_vertex).each { |adjacent| forbid_g! adjacent, colour }
44
+ siblings_of(g_vertex).each { |sibling| colour_g! sibling, colour }
45
45
  end
46
46
 
47
47
  def colour_g_vertex! g_vertex
@@ -66,6 +66,25 @@ module ArtDecomp class Bipainter
66
66
  colour_g_vertex! g_vertex if g_vertex
67
67
  end
68
68
 
69
+ def colour_qv! qv_vertex, colour
70
+ return if @qv_colours[qv_vertex] == colour
71
+ raise PaintingError if @qv_colours[qv_vertex] and @qv_colours[qv_vertex] != colour
72
+ raise PaintingError if @qv_forbidden[qv_vertex].include? colour
73
+ @qv_colours[qv_vertex] = colour
74
+ @qv_graph.adjacent(qv_vertex).each { |adjacent| forbid_qv! adjacent, colour }
75
+ if @qv_colours.any? { |q, col| q != qv_vertex and col == colour }
76
+ @g_graph.vertices.select { |g| g & qv_vertex == g }.each do |g_vertex|
77
+ v_parent = @beta_v.ints.find { |v| v & g_vertex == g_vertex }
78
+ @g_graph.adjacent(g_vertex).select { |g| v_parent & g == g and qv_vertex & g != g }.each do |neighbour|
79
+ @qv_graph.vertices.select { |q| q & neighbour == neighbour }.each do |q_parent|
80
+ forbid_qv! q_parent, colour
81
+ end
82
+ end
83
+ siblings_of(g_vertex).each { |sibling| sync_colours g_vertex, sibling }
84
+ end
85
+ end
86
+ end
87
+
69
88
  def colour_qv_vertex! qv_vertex
70
89
  begin
71
90
  backup!
@@ -92,32 +111,17 @@ module ArtDecomp class Bipainter
92
111
  @qv_forbidden[qv_vertex] << colour
93
112
  end
94
113
 
95
- def colour_g! g_vertex, colour
96
- return if @g_colours[g_vertex] == colour
97
- raise PaintingError if @g_colours[g_vertex] and @g_colours[g_vertex] != colour
98
- raise PaintingError if @g_forbidden[g_vertex].include? colour
99
- @g_colours[g_vertex] = colour
100
- @g_graph.adjacent(g_vertex).each { |adjacent| forbid_g! adjacent, colour }
101
- siblings_of(g_vertex).each { |sibling| colour_g! sibling, colour }
114
+ def painted?
115
+ @qv_graph.vertices == @qv_colours.keys.to_set and @g_graph.vertices == @g_colours.keys.to_set
102
116
  end
103
117
 
104
- def colour_qv! qv_vertex, colour
105
- return if @qv_colours[qv_vertex] == colour
106
- raise PaintingError if @qv_colours[qv_vertex] and @qv_colours[qv_vertex] != colour
107
- raise PaintingError if @qv_forbidden[qv_vertex].include? colour
108
- @qv_colours[qv_vertex] = colour
109
- @qv_graph.adjacent(qv_vertex).each { |adjacent| forbid_qv! adjacent, colour }
110
- if @qv_colours.any? { |q, col| q != qv_vertex and col == colour }
111
- @g_graph.vertices.select { |g| g & qv_vertex == g }.each do |g_vertex|
112
- v_parent = @beta_v.ints.find { |v| v & g_vertex == g_vertex }
113
- @g_graph.adjacent(g_vertex).select { |g| v_parent & g == g and qv_vertex & g != g }.each do |neighbour|
114
- @qv_graph.vertices.select { |q| q & neighbour == neighbour }.each do |q_parent|
115
- forbid_qv! q_parent, colour
116
- end
117
- end
118
- siblings_of(g_vertex).each { |sibling| sync_colours g_vertex, sibling }
119
- end
120
- end
118
+ def restore!
119
+ @g_colours = Marshal.load @backup[:g_colours]
120
+ @g_forbidden = Marshal.load @backup[:g_forbidden]
121
+ @qv_colours = Marshal.load @backup[:qv_colours]
122
+ @qv_forbidden = Marshal.load @backup[:qv_forbidden]
123
+ @g_forbidden.default_proc = proc { |h, k| h[k] = Set[] }
124
+ @qv_forbidden.default_proc = proc { |h, k| h[k] = Set[] }
121
125
  end
122
126
 
123
127
  def siblings_of g_vertex
@@ -135,8 +139,4 @@ module ArtDecomp class Bipainter
135
139
  end
136
140
  end
137
141
 
138
- def painted?
139
- @qv_graph.vertices == @qv_colours.keys.to_set and @g_graph.vertices == @g_colours.keys.to_set
140
- end
141
-
142
142
  end end
@@ -43,9 +43,9 @@ module ArtDecomp class Executable
43
43
  @non_disjoint = opts[:non_disjoint]
44
44
  @deep_ndj = opts[:deep_ndj]
45
45
 
46
- @uv_gens = opts[:uv].map { |gen| eval "UVGenerator::#{gen}" }
47
- @qu_gens = opts[:qu].map { |gen| eval "QuGenerator::#{gen}" }
48
- @qv_gens = opts[:qv].map { |gen| eval "QvGenerator::#{gen}" }
46
+ @uv_gens = opts[:uv].map { |gen| UVGenerator.const_get gen }
47
+ @qu_gens = opts[:qu].map { |gen| QuGenerator.const_get gen }
48
+ @qv_gens = opts[:qv].map { |gen| QvGenerator.const_get gen }
49
49
 
50
50
  if opts[:log_given]
51
51
  require_relative 'logging'
@@ -1,14 +1,27 @@
1
1
  module ArtDecomp class FSM
2
2
 
3
+ attr_reader :codes
4
+
3
5
  def self.from_kiss kiss
4
6
  kiss = File.read kiss unless kiss.index "\n"
5
7
  inputs, outputs, state, next_state = [], [], [], []
8
+ codes = Hash[kiss.lines.grep(/^\.code [^*]/).map(&:split).map { |_, state, code| [state.to_sym, code.to_sym] }]
9
+ codes = Hash[kiss.lines.grep(/^# States\./).map(&:split).map { |_, state, code| [state[7..-1].to_sym, code.to_sym] }] if codes.empty?
6
10
  kiss.lines do |line|
7
11
  case line
8
12
  when /^\s*[01-]+\s+\S+\s+\S+\s+[01-]+\s*$/ then ins, st, nxt, outs = *line.split
9
13
  when /^\s*[01-]+\s+[01-]+\s*$/ then st, nxt, ins, outs = DontCare, DontCare, *line.split
14
+ when /^\.end_kiss$/ then break
10
15
  else next
11
16
  end
17
+ next if [DontCare, '*'].include? nxt and outs =~ /^-*$/
18
+ if line =~ /^[01-]+\s+[01-]+$/ and not codes.empty?
19
+ size = codes.values.first.size
20
+ st = ins[-size..-1] == '-' * size ? DontCare : codes.invert[ins[-size..-1].to_sym]
21
+ nxt = outs[0...size] == '-' * size ? DontCare : codes.invert[outs[0...size].to_sym]
22
+ ins = ins[0...-size]
23
+ outs = outs[size..-1]
24
+ end
12
25
  inputs << ins.split(//).map(&:to_sym)
13
26
  outputs << outs.split(//).map(&:to_sym)
14
27
  state << (st == '*' ? DontCare : st.to_sym)
@@ -22,11 +35,11 @@ module ArtDecomp class FSM
22
35
  inputs << inputs[i]
23
36
  outputs << outputs[i]
24
37
  end if state.index DontCare
25
- new inputs.transpose, outputs.transpose, state, next_state
38
+ new inputs.transpose, outputs.transpose, state, next_state, codes
26
39
  end
27
40
 
28
- def initialize inputs, outputs, state, next_state
29
- @inputs, @outputs, @state, @next_state = inputs.freeze, outputs.freeze, state.freeze, next_state.freeze
41
+ def initialize inputs, outputs, state, next_state, codes = {}
42
+ @inputs, @outputs, @state, @next_state, @codes = inputs.freeze, outputs.freeze, state.freeze, next_state.freeze, codes.freeze
30
43
  end
31
44
 
32
45
  def == other
@@ -99,6 +112,14 @@ module ArtDecomp class FSM
99
112
  "#{@inputs.size}/#{@outputs.size}+#{(@state.uniq - [DontCare]).size}s"
100
113
  end
101
114
 
115
+ def structure
116
+ structure = Hash.new { |state, input| state[input] = {} }
117
+ @state.each_index do |row|
118
+ structure[@state[row]][@inputs.transpose[row].join] = {:next_state => @next_state[row], :output => @outputs.transpose[row].join}
119
+ end
120
+ structure
121
+ end
122
+
102
123
  def to_kiss
103
124
  st = @state.map { |e| e == DontCare ? '*' : e }
104
125
  nxt = @next_state.map { |e| e == DontCare ? '*' : e }
@@ -108,6 +129,10 @@ module ArtDecomp class FSM
108
129
  KISS.new(cols.transpose.map(&:join)).formatted
109
130
  end
110
131
 
132
+ def to_vhdl name
133
+ VHDL.new(self).vhdl name
134
+ end
135
+
111
136
  def truth_table?
112
137
  @state.all? { |s| s == DontCare } and @next_state.all? { |ns| ns == DontCare }
113
138
  end
@@ -3,23 +3,23 @@ module ArtDecomp class Graph
3
3
  def initialize blanket, seps
4
4
  vertices = blanket.ints.dup
5
5
  vertices.delete_if { |this| vertices.any? { |other| other != this and other & this == this } }
6
- @edges = Hash[vertices.map { |vertex| [vertex, Set[]] }]
6
+ @neighbours = Hash[vertices.map { |vertex| [vertex, Set[]] }]
7
7
  relevant = Hash[vertices.map { |v| [v, seps.select { |s| v&s != 0 and v&s != s }.to_set] }]
8
8
  vertices.pairs.each do |a, b|
9
9
  if (relevant[a] & relevant[b]).any? { |s| a&s != b&s }
10
- @edges[a] << b
11
- @edges[b] << a
10
+ @neighbours[a] << b
11
+ @neighbours[b] << a
12
12
  end
13
13
  end
14
14
  end
15
15
 
16
16
  def adjacent *vertices
17
- vertices.map { |vertex| @edges[vertex] }.inject(:|) - vertices
17
+ vertices.map { |vertex| @neighbours[vertex] }.inject(:|) - vertices
18
18
  end
19
19
 
20
20
  def blanket_from_colouring
21
21
  colours = {}
22
- @edges.keys.sort_by { |vert| [-degree(vert), vert] }.each do |vertex|
22
+ @neighbours.keys.sort_by { |vert| [-degree(vert), vert] }.each do |vertex|
23
23
  forbidden = adjacent(vertex).map { |vert| colours[vert] }.to_set
24
24
  # FIXME: consider selecting colours on the least-popular-first basis
25
25
  colour = :a
@@ -32,21 +32,21 @@ module ArtDecomp class Graph
32
32
  end
33
33
 
34
34
  def complete?
35
- @edges.values.map(&:size).inject(:+) == @edges.size * (@edges.size - 1)
35
+ @neighbours.values.map(&:size).inject(:+) == @neighbours.size * (@neighbours.size - 1)
36
36
  end
37
37
 
38
38
  def degree vertex
39
- @edges[vertex].size
39
+ @neighbours[vertex].size
40
40
  end
41
41
 
42
42
  def edges
43
- @edges.map { |v, adjacents| adjacents.map { |adj| Set[v, adj] } }.flatten.to_set
43
+ @neighbours.map { |v, adjacents| adjacents.map { |adj| Set[v, adj] } }.flatten.to_set
44
44
  end
45
45
 
46
46
  def merge_by_edge_labels!
47
- return self if @edges.size == 1
48
- pins = @edges.size.log2_ceil
49
- until @edges.size.log2_ceil < pins
47
+ return self if @neighbours.size == 1
48
+ pins = @neighbours.size.log2_ceil
49
+ until @neighbours.size.log2_ceil < pins
50
50
  # FIXME: edge labels can/should be cached from previous computations
51
51
  a, b = *edges.sort_by { |edge| yield *edge }.first
52
52
  merge! a, b
@@ -55,9 +55,9 @@ module ArtDecomp class Graph
55
55
  end
56
56
 
57
57
  def merge_by_vertex_degrees!
58
- pins = @edges.size.log2_ceil
59
- until @edges.size.log2_ceil < pins or complete?
60
- a, b = *@edges.keys.sort_by { |v| -degree(v) }.pairs.find { |v1, v2| not @edges[v1].include? v2 }
58
+ pins = @neighbours.size.log2_ceil
59
+ until @neighbours.size.log2_ceil < pins or complete?
60
+ a, b = *@neighbours.keys.sort_by { |v| -degree(v) }.pairs.find { |v1, v2| not @neighbours[v1].include? v2 }
61
61
  merge! a, b
62
62
  end
63
63
  self
@@ -69,7 +69,7 @@ module ArtDecomp class Graph
69
69
  end
70
70
 
71
71
  def vertices
72
- @edges.keys.to_set
72
+ @neighbours.keys.to_set
73
73
  end
74
74
 
75
75
  private
@@ -77,10 +77,10 @@ module ArtDecomp class Graph
77
77
  def merge! a, b
78
78
  new = a | b
79
79
  adjs = adjacent(a) | adjacent(b)
80
- adjs.each { |adj| @edges[adj] << new }
81
- @edges[new] = adjs
82
- @edges.delete(a).each { |adj| @edges[adj].delete a } unless a == new
83
- @edges.delete(b).each { |adj| @edges[adj].delete b } unless b == new
80
+ adjs.each { |adj| @neighbours[adj] << new }
81
+ @neighbours[new] = adjs
82
+ @neighbours.delete(a).each { |adj| @neighbours[adj].delete a } unless a == new
83
+ @neighbours.delete(b).each { |adj| @neighbours[adj].delete b } unless b == new
84
84
  end
85
85
 
86
86
  end end
@@ -1,54 +1,71 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'gazer'
4
3
  require 'logger'
4
+ require 'rcapture'
5
5
 
6
- module ArtDecomp class Logging < Gazer::Aspect::Base
6
+ module ArtDecomp class Logging
7
7
 
8
- def self.level
9
- @@log.level
10
- end
8
+ class << self
11
9
 
12
- def self.level= level
13
- @@log.level = level
10
+ def level
11
+ @log.level
14
12
  end
15
13
 
16
- def self.log= log
17
- @@log = Logger.new log
18
- @@log.level = Logger::INFO
19
- @@log.formatter = proc { |sev, date, name, msg| "#{date} #{msg}\n" }
20
- @@indent = ''
21
- apply!
22
- @@start = Time.now
14
+ def level= level
15
+ @log.level = level
23
16
  end
24
17
 
25
- def self.off
26
- # FIXME: if instances can be unadvised, do that and close @@log
27
- @@log = Logger.new '/dev/null'
18
+ def log= log
19
+ @start = Time.now
20
+ @log = Logger.new log
21
+ @log.level = Logger::INFO
22
+ @log.formatter = proc do |sev, date, name, msg|
23
+ secs = (Time.now - @start).ceil
24
+ time = "#{secs / 60 / 60}h#{(secs / 60 % 60).to_s.rjust 2}m#{(secs % 60).to_s.rjust 2}s"
25
+ best = @best.nil? ? '' : @best.to_s + 'c'
26
+ path = @path.nil? ? '' : '/' + @path
27
+ "#{time.rjust 10} #{best.rjust 4} ·#{path.ljust 10} #{msg}\n"
28
+ end
29
+ add_logging
28
30
  end
29
31
 
30
- after instances_of(Executable) => :run do |point|
31
- secs = (Time.now - @@start).to_i
32
- @@log.info "#{point.object.best ? "final best decomposition: #{point.object.best} cells" : 'no final decomposition'}; done in #{secs}s (#{secs / 60 / 60}h #{secs / 60 % 60}m #{secs % 60}s)"
32
+ def off
33
+ # FIXME: if methods can be uncaptured, do that and close @log
34
+ @log = Logger.new '/dev/null'
33
35
  end
34
36
 
35
- before instances_of(Executable) => :decompositions do |point|
36
- @@indent = ' ' * (point.object.iters - point.args[1])
37
- path = point.args[2][point.object.dir.size+1..-1]
38
- archs = point.object.archs.map(&:to_s).sort.reverse.join '+'
39
- @@log.info "#{@@indent}FSM #{point.args[0].stats} → #{archs} (#{path}) with #{point.object.gens} – #{point.object.best ? "best so far: #{point.object.best} cells" : 'no decomposition so far'}"
40
- end
37
+ private
41
38
 
42
- before instances_of(UVGenerator.constants.map { |c| eval("UVGenerator::#{c}") }) => :uv_pairs do |point|
43
- @@log.info "#{@@indent} UV with #{point.object.class.to_s.split('::').last}"
44
- end
39
+ def add_logging
40
+ uv_gens = UVGenerator.constants.map { |c| UVGenerator.const_get c }
41
+ qu_gens = QuGenerator.constants.map { |c| QuGenerator.const_get c }
45
42
 
46
- before instances_of(QuGenerator.constants.map { |c| eval("QuGenerator::#{c}") }) => :blankets do |point|
47
- @@log.info "#{@@indent} U = #{point.args[1].sort.inspect}, V = #{point.args[2].sort.inspect}, Qu with #{point.object.class.to_s.split('::').last}"
48
- end
43
+ (uv_gens + qu_gens + [Executable]).each { |c| c.class_eval { include RCapture::Interceptable } }
49
44
 
50
- before instances_of(QvGenerator.constants.map { |c| eval("QvGenerator::#{c}") }) => :blankets do |point|
51
- @@log.debug "#{@@indent} |Qu| = #{point.args[3].size}, Qv+G with #{point.object.class.to_s.split('::').last}"
45
+ Executable.capture_pre :methods => :decompositions do |point|
46
+ @best = point.sender.best
47
+ @path = point.args[2][point.sender.dir.size+1..-1]
48
+ @log.info "#{point.args[0].stats} with #{point.sender.gens}"
49
+ end
50
+
51
+ uv_gens.each do |uv_gen|
52
+ uv_gen.capture_pre :methods => :uv_pairs do |point|
53
+ @uv_gen = uv_gen.to_s.split('::').last
54
+ end
55
+ end
56
+
57
+ qu_gens.each do |qu_gen|
58
+ qu_gen.capture_pre :methods => :blankets do |point|
59
+ @log.debug "#{point.args[1].sort.join(' ').ljust 10} #{point.args[2].sort.join(' ').ljust 10} via #{@uv_gen} with #{qu_gen.to_s.split('::').last}"
60
+ end
61
+ end
62
+
63
+ Executable.capture_post :methods => :run do |point|
64
+ @best = point.sender.best
65
+ @log.info "took #{(Time.now - @start).ceil}s"
66
+ end
52
67
  end
53
68
 
69
+ end
70
+
54
71
  end end
@@ -2,13 +2,12 @@ module ArtDecomp class UVGenerator::Braindead
2
2
 
3
3
  def uv_pairs fsm, archs
4
4
  input_count = fsm.input_count
5
- inputs = (0...input_count).to_a
6
5
  max_v_size = archs.map(&:pins).max
7
6
  Enumerator.new do |yielder|
8
7
  (0...2**input_count).each do |vector|
9
8
  u, v = Set[], Set[]
10
9
  input_count.times do |bit|
11
- (vector[bit].zero? ? u : v) << inputs[bit]
10
+ (vector[bit].zero? ? u : v) << bit
12
11
  end
13
12
  yielder.yield fsm.expand_x(v), u, v if v.size <= max_v_size
14
13
  end
@@ -0,0 +1,72 @@
1
+ module ArtDecomp class VHDL
2
+
3
+ def initialize fsm
4
+ @fsm = fsm
5
+ end
6
+
7
+ def vhdl name
8
+ structure = @fsm.structure
9
+ logic = structure[DontCare].map do |input, results|
10
+ results[:next_state] = "s#{results[:next_state]}".to_sym if results[:next_state] =~ /^\d+$/
11
+ [
12
+ " if std_match(input, \"#{input}\") then next_state <= #{results[:next_state]}; output <= \"#{results[:output]}\";",
13
+ ' else',
14
+ ]
15
+ end
16
+ structure.delete DontCare
17
+ logic << ' case current_state is'
18
+ logic += structure.map do |state, transitions|
19
+ state = "s#{state}".to_sym if state =~ /^\d+$/
20
+ [
21
+ " when #{state} =>",
22
+ transitions.map.with_index do |(input, results), i|
23
+ results[:next_state] = "s#{results[:next_state]}".to_sym if results[:next_state] =~ /^\d+$/
24
+ " #{'els' if i > 0}if std_match(input, \"#{input}\") then next_state <= #{results[:next_state]}; output <= \"#{results[:output]}\";"
25
+ end,
26
+ ' end if;',
27
+ ]
28
+ end
29
+ logic << " when others => next_state <= \"#{'-' * @fsm.codes.values.first.size}\"; output <= \"#{'-' * structure.first.last.first.last[:output].size}\";" unless @fsm.codes.empty?
30
+ logic << ' end case;'
31
+ logic << ' end if;' if logic.flatten.include? ' else'
32
+ if @fsm.codes.empty?
33
+ states = [
34
+ " type state is (#{structure.keys.join ', '});",
35
+ ' signal current_state, next_state: state;',
36
+ ]
37
+ default_state = structure.keys.first
38
+ else
39
+ states = @fsm.codes.map do |state, code|
40
+ state = "s#{state}".to_sym if state =~ /^\d+$/
41
+ " constant #{state}: std_logic_vector(#{code.size - 1} downto 0) := \"#{code}\";"
42
+ end
43
+ states << " signal current_state, next_state: std_logic_vector(#{@fsm.codes.first.last.size - 1} downto 0);"
44
+ default_state = "\"#{'-' * @fsm.codes.values.first.size}\""
45
+ end
46
+ <<-VHDL
47
+ library ieee;
48
+ use ieee.numeric_std.all;
49
+ use ieee.std_logic_1164.all;
50
+ entity #{name} is
51
+ port(
52
+ clock: in std_logic;
53
+ input: in std_logic_vector(#{structure.first.last.first.first.size - 1} downto 0);
54
+ output: out std_logic_vector(#{structure.first.last.first.last[:output].size - 1} downto 0)
55
+ );
56
+ end #{name};
57
+ architecture behaviour of #{name} is
58
+ #{states.join "\n"}
59
+ begin
60
+ process(clock) begin
61
+ if rising_edge(clock) then current_state <= next_state;
62
+ end if;
63
+ end process;
64
+ process(input, current_state) begin
65
+ next_state <= #{default_state}; output <= "#{'-' * structure.first.last.first.last[:output].size}";
66
+ #{logic.join "\n"}
67
+ end process;
68
+ end behaviour;
69
+ VHDL
70
+ end
71
+
72
+ end end