class Vertigo::TestBenchGenerator

Attributes

arch[RW]
ast[RW]
clk[RW]
entity[RW]
options[RW]
rst[RW]

Public Class Methods

new(options={}) click to toggle source
# File lib/vertigo/tb_generator.rb, line 10
def initialize options={}
  @options=options
  @supplemental_libs_h=options[:supplemental_libs_h]||{}
end

Public Instance Methods

comment(str) click to toggle source
# File lib/vertigo/tb_generator.rb, line 31
def comment str
  "-- #{str}"
end
gen_clock_and_reset() click to toggle source
# File lib/vertigo/tb_generator.rb, line 100
def gen_clock_and_reset
  code=Code.new
  code << line
  code << comment("clock and reset")
  code << line
  code << "#{@reset_name} <= '0','1' after 123 ns;"
  code.newline
  code << "#{@clk_name} <= not(#{@clk_name}) after HALF_PERIOD when running else #{@clk_name};"
  code
end
gen_code() click to toggle source
# File lib/vertigo/tb_generator.rb, line 35
def gen_code
  code=Code.new
  code << gen_header
  code << "library ieee;"
  code << "use ieee.std_logic_1164.all;"
  code << "use ieee.numeric_std.all;"
  code.newline
  code << "entity #{@entity_name}_tb is"
  code << "end entity;"
  code.newline
  code << "architecture bhv of #{@entity_name}_tb is"
  code.indent=2
  code << "constant HALF_PERIOD : time :=5 ns;"
  code.newline
  code << "signal #{@clk_name} : std_logic := '0';"
  code << "signal #{@reset_name} : std_logic := '0';"
  code.newline
  code << "signal running : boolean := true;"
  code.newline
  code << "procedure wait_cycles(n : natural) is "
  code << "begin"
  code.indent=4
  code << "for i in 0 to n-1 loop"
  code.indent=6
  code << "wait until rising_edge(#{@clk_name});"
  code.indent=4
  code << "end loop;"
  code.indent=2
  code << "end procedure;"
  code.newline
  code << "procedure toggle(signal s : inout std_logic) is"
  code << "begin"
  code << "  wait until rising_edge(clk);"
  code << "  s <=not(s);"
  code << "  wait until rising_edge(clk);"
  code << "  s <=not(s);"
  code << "end procedure;"
  code.newline
  @entity.ports.each do |port|
    port_name=port.name.str.ljust(@max_length_str)
    port_type=port.type.str
    code << "signal #{port_name} : #{port_type};" unless @excluded.include?(port)
  end
  code.indent=0
  code << "begin"
  code.indent=2
  code << gen_clock_and_reset
  code << instanciate_dut
  code << gen_stim_process
  code.indent=0
  code << "end bhv;"
  code.finalize
end
gen_header() click to toggle source
# File lib/vertigo/tb_generator.rb, line 89
def gen_header
  code=Code.new
  code << line
  code << "-- this file was generated automatically by Vertigo Ruby utility"
  code << "-- date : (d/m/y h:m) #{Time.now.strftime("%d/%m/%Y %k:%M")}"
  code << "-- author : Jean-Christophe Le Lann - 2014"
  code << line
  code.newline
  code
end
gen_stim_process() click to toggle source
# File lib/vertigo/tb_generator.rb, line 136
def gen_stim_process
  code=Code.new
  code << line
  code << comment("sequential stimuli")
  code << line
  code << "stim : process"
  code << "begin"
  code.indent=2
  code << "report \"running testbench for #{@entity_name}(#{@arch_name})\";"
  code << "report \"waiting for asynchronous reset\";"
  code << "wait until #{@reset_name}='1';"
  code << "wait_cycles(10);"
  code << "wait_cycles(200);"
  code << "report \"end of simulation\";"
  code << "running <= false;"
  code << "wait;"
  code.indent=0
  code << "end process;"
  code
end
generate_from(ast) click to toggle source
# File lib/vertigo/tb_generator.rb, line 15
def generate_from ast
  @ast=ast
  entity_arch=find_entity_arch()
  detecting_clk_and_reset(entity_arch)
  vhdl_tb=gen_code()
  @tb_name=@entity_name+"_tb"
  tb_filename=@tb_name+".vhd"
  File.open(tb_filename,'w'){|f| f.puts vhdl_tb}
  puts "=> generated testbench : #{tb_filename}" unless options[:mute]
  return tb_filename
end
instanciate_dut() click to toggle source
# File lib/vertigo/tb_generator.rb, line 111
def instanciate_dut
  code=Code.new
  code << line
  code << comment("Design Under Test")
  code << line
  code << "dut : entity work.#{@entity_name}(#{@arch_name})"
  code.indent=2
  code << "port map ("
  code.indent=4

  @entity.ports.each_with_index do |port,idx|
    port_name=port.name.str.ljust(@max_length_str)
    port_type=port.type.str
    if idx < @entity.ports.size-1
      code << "#{port_name} => #{port_name},"
    else
      code << "#{port_name} => #{port_name}"
    end
  end
  code.indent=2
  code << ");"
  code.indent=0
  code
end
line(n=80) click to toggle source
# File lib/vertigo/tb_generator.rb, line 27
def line n=80
  "-"*n
end

Private Instance Methods

detecting_clk_and_reset(entity_arch) click to toggle source
# File lib/vertigo/tb_generator.rb, line 177
def detecting_clk_and_reset entity_arch
  puts "=> detecting clock and reset" unless options[:mute]
  entity,arch=entity_arch
  inputs=entity.ports.select{|port| port.is_a?(Input)}
  @clk = inputs.sort_by{|input| levenshtein_distance(input.name.str,"clk")}.first
  @rst = inputs.sort_by{|input| levenshtein_distance(input.name.str,"reset_n")}.first
  puts "\t-most probable clk   : #{@clk.name.str}" unless options[:mute]
  puts "\t-most probable reset : #{@rst.name.str}" unless options[:mute]
  @max_length_str=entity.ports.map{|port| port.name.str.size}.max
  @excluded=[@clk,@rst]
  @reset_name=@rst.name.str
  @clk_name=@clk.name.str
end
find_entity_arch() click to toggle source
# File lib/vertigo/tb_generator.rb, line 158
def find_entity_arch
  @entity=ast.design_units.find{|du| du.is_a? Entity}
  if @entity.nil?
    puts msg="ERROR : no entity found"
    raise msg
  end
  puts "=> found entity '#{entity.name.str}'" unless options[:mute]
  @arch=ast.design_units.find{|du| du.is_a? Architecture}
  if @arch.nil?
    puts msg="ERROR : no architecture found"
    raise msg
  end

  puts "=> found architecture '#{arch.name.str}'" unless options[:mute]
  @entity_name=@entity.name.str
  @arch_name=@arch.name.str
  [@entity,@arch]
end
levenshtein_distance(s, t) click to toggle source
# File lib/vertigo/tb_generator.rb, line 191
def levenshtein_distance(s, t)
  m = s.length
  n = t.length
  return m if n == 0
  return n if m == 0
  d = Array.new(m+1) {Array.new(n+1)}

  (0..m).each {|i| d[i][0] = i}
  (0..n).each {|j| d[0][j] = j}
  (1..n).each do |j|
    (1..m).each do |i|
      d[i][j] = if s[i-1] == t[j-1] # adjust index into string
                  d[i-1][j-1]       # no operation required
                else
                  [ d[i-1][j]+1,    # deletion
                    d[i][j-1]+1,    # insertion
                    d[i-1][j-1]+1,  # substitution
                  ].min
                end
    end
  end
  d[m][n]
end