#!/usr/bin/ruby -Ks
#
# vhd2tbtmpl.rb
# v0.0.7
# 20121120
# akinobu nishi
#

require 'kconv'
win_flag = 0
if(win_flag == 1)
	require 'Win32API' # 20120619 add
end

print("input vhdl file ex)test.vhd\n")
vhdl_name	= STDIN.gets.chomp
p((File::dirname(vhdl_name)))
vhdl_name_buf = vhdl_name.sub(File::dirname(vhdl_name),"").sub("\\","") # 20121108 add if linux then "/"?,if windows then "\\"
if(File::dirname(vhdl_name) == ".") # current directory
	out_vhdl_name = "tb_" + vhdl_name # 20121108 del
else # 絶対パス
	out_vhdl_name = File::dirname(vhdl_name) + "\\" + "tb_" + vhdl_name_buf # 20121108 add if linux then "/"?,if windows then "\\"
end
csv_name	= vhdl_name.sub(".vhdl",".csv").sub(".vhd",".csv") # 20121119 add vhdlをcsvで吐き出す。
##########
# 初期化
file_out = File.open(out_vhdl_name,"w")
file_out.close
file_entity_out = File.open("entity_out.vhd","w")
file_entity_out.close
file_csv_out = File.open(csv_name,"w") # 20121119 add
file_csv_out.close # 20121119 add
##########

generic_flag = 0

def vhdl_name(f_name_ref)
  @f_name_ref = f_name_ref.chomp
end
def comment_check(din)
	if((din2 = din.lstrip) =~ /\A--/)
		data	= nil
	else
		data	= din
	end
	return data
end

def library_use_check(din)
	if(((din_lstrip = din.lstrip.downcase) =~ /\Alibrary/) || ((din_lstrip = din.lstrip.downcase) =~ /\Ause/))
		data = din
	else
		data	= nil
	end
	return data
end

def entity_check(din)
	if((din_lstrip = din.lstrip.downcase) =~ /\Aentity/)
		din2 = din_lstrip.downcase.sub("entity","")
		din3 = din2.sub("is","")
		# data = din3
		data = 1
	else
		data	= 0
	end
	return data
end

def generic_check(din)
	# if(din != nil)
		# din.each_byte {|c| sum = sum + c}
	# end

	if((din_lstrip = din.lstrip.downcase) =~ /\Ageneric/)
		din2 = din_lstrip.downcase.sub("generic","")
		din3 = din2.sub("is","")
		# data = din3
		data = 1
	else
		data	= 0
	end
	return data
end

def port_check(din)
	# if(din != nil)
		# din.each_byte {|c| sum = sum + c}
	# end

	if((din_lstrip = din.lstrip.downcase) =~ /\Aport/)
		din2 = din_lstrip.downcase.sub("port","")
		din3 = din2.sub("is","")
		# data = din3
		data = 1
	else
		data	= 0
	end
	return data
end

def file_out(name,type,data)
file_out = File.open(name,type)
file_out.write(data)
file_out.close
end

## init
st = 1
st_entity = 1
entity_data = []
cnt = 0
##
in_file = File.open(vhdl_name, "r")
while din = in_file.gets	# 1行ずつデータ読み込み nilまで
	if(check = din.downcase.include?("entity"))
		st = 2
	end
	case st
	when 1
		dout = library_use_check(din)
	when 2
		case st_entity
		when 1
			dout = ""
			file_out("entity_out.vhd","a",din)
			cnt += 1
			if(check = din.downcase.include?("end"))
				st_entity = 2
			end
		when 2
			# if(check = din.include?("generic"))
				# dout = ""
				# st_entity = 3
		# print("i")
			# end
			# if(check = din.include?("port"))
				# dout = ""
				# st_entity = 4
		# print("j")
			# end
		# when 3
			# gen_data = generic_check
			# if(gen_data == "end")
				# st_entity = 4
			# end
		# when 4
			# port_data = port_check
			# if(port_data == "end")
				# st_entity = 5
			# end
		else
		end
	else
	end

	file_out(out_vhdl_name,"a",dout)

end

st = 1
din_buf = []
comment_flag = 0
# data_gen_buf = []
# data_port_buf = []
data_gen_buf = Array.new(100000) # 20120927 change 10 to 100000
data_gen_buf.each_index{ |y|
data_gen_buf[y] = Array.new(3, "") # ""で初期化 
}
data_port_buf = Array.new(100000)  # 20120927 change 10 to 100000
data_port_buf.each_index{ |y|
data_port_buf[y] = Array.new(3, "") # ""で初期化 
}
entity_name = ""
check_str = ""
i = 0
j = 0
in_file = File.open("entity_out.vhd", "r")
while din = in_file.getc	# 1文字ずつデータ読み込み nilまで
	din = din.chr
	din_buf = [din_buf,din]
	din2 = din_buf.to_s
	if(check = din2.include?("--"))
		comment_flag = 1
	else
	end
	if(comment_flag == 1)
		if(din == "\n")
			din_buf = []
			comment_flag = 0
		end
	end

	if(comment_flag == 0)
		case st
		when 1
			check_str = "entity"
			if(check = din2.downcase.include?(check_str))
				st = 2
				din_buf = []
			end
		when 2
			check_str = " "
			if(din == check_str)
				# entity_name = din2.strip
				din_buf = []
				st = 3
			end
		when 3
			check_str = "is"
			if(check = din2.downcase.include?(check_str))
				din2 = din2.downcase # 20121108 add
				din2.sub!(check_str,"")
				entity_name = din2.strip
				st = 4
				din_buf = []
				p(entity_name)
			end
		when 4
			i = 0
			j = 0
			check_str = "generic"
			if((check = generic_check(din2)) == 1)
			# if(check = din2.downcase.include?(check_str))
				st = 5
				din_buf = []
				generic_flag = 1 # 20120916 add
			end
			check_str = "port"
			if((check = port_check(din2)) == 1)
			# if(check = din2.downcase.include?(check_str))
				st = 6
				din_buf = []
			end
		when 5
			check_str = "("
			if(din == check_str)
				data1 = din2.strip
				din_buf = []
				st = 51
			end
		when 51
			check_str = ":"
			if(din == check_str)
				din2.sub!(check_str,"")
				data_gen_buf[i][j] = din2.strip
				j += 1
				din_buf = []
				st = 52
			end
			check_str = ");"
			if(check = din2.downcase.include?(check_str))
				st = 4
				din_buf = []
			end
		when 52
			check_str = ":="
			if(check = din2.downcase.include?(check_str))
				din2.sub!(check_str,"")
				data_gen_buf[i][j] = din2.strip
				j += 1
				din_buf = []
				st = 53
			end
			check_str = "\n"
			if(din == check_str)
				din2.sub!(check_str,"")
				data_gen_buf[i][j] = din2.strip
				j += 1
				din_buf = []
				st = 4
			end
			
		when 53
			check_str = "\n"
			if(din == check_str)
				din2.sub!(check_str,"")
				data_gen_buf[i][j] = din2.strip
				i_gen_buf = i
				j = 0
				din_buf = []
				st = 54
			end
			check_str = ";"
			if(din == check_str)
				din2.sub!(check_str,"")
				data_gen_buf[i][j] = din2.strip
				i += 1
				j = 0
				din_buf = []
				st = 51
			end
		when 54
			i = 0
			j = 0
			check_str = ");"
			if(check = din2.include?(check_str))
				din_buf = []
				st = 4
			end
		when 6
			check_str = "("
			if(din == check_str)
				data1 = din2.strip
				din_buf = []
				st = 61
			end
		when 61
			check_str = ":"
			if(din == check_str)
				din2.sub!(check_str,"")
				data_port_buf[i][j] = din2.strip # j = 0
				j += 1
				din_buf = []
				st = 62
			end
			check_str = ");"
			if(check = din2.downcase.include?(check_str))
				st = 7
				din_buf = []
			end
		when 62
			check_str = "in out inout"
			if((check = din2.downcase.include?("in")) || (check = din2.downcase.include?("out")) || (check = din2.downcase.include?("inout")))
				data_port_buf[i][j] = din2.strip # j = 1
				j += 1
				din_buf = []
				st = 63
			end
		when 63
			check_str = "\n"
			if(din == check_str)
				din2.sub!(check_str,"")
				data_port_buf[i][j] = din2.strip
				i_port_buf = i
				j = 0
				din_buf = []
				st = 64
			end
			check_str = ";"
			if(din == check_str)
				din2.sub!(check_str,"")
				data_port_buf[i][j] = din2.strip # j = 2
				p(data_port_buf[i][j])
				i += 1
				j = 0
				din_buf = []
				st = 61
			end
		when 64
			check_str = ");"
			if(check = din2.include?(check_str))
				din_buf = []
				st = 7
			end
		when 7

		else
		end

	end
end	
print("\n")
	
data = "entity tb_" + entity_name +" is
end tb_" + entity_name + ";
architecture tb_" + entity_name + "_arch of tb_" + entity_name + " is\n\n"

file_out(out_vhdl_name,"a",data)

for i in 0..i_port_buf
	data = "signal " + data_port_buf[i][0] + "		: " + data_port_buf[i][2] + ";\n"
	file_out(out_vhdl_name,"a",data)
end

data = "COMPONENT " + entity_name + "\n"
file_out(out_vhdl_name,"a",data)

if(generic_flag == 1) # 20120916 add
data = "
	generic(
"
file_out(out_vhdl_name,"a",data)

if(data_gen_buf[0][0] != "") # 20120619 add
	for i in 0..i_gen_buf
		if(i == i_gen_buf)
			data = "		" + data_gen_buf[i][0] + "	: " + data_gen_buf[i][1] + " := " + data_gen_buf[i][2] + "
	"
		else
			data = "		" + data_gen_buf[i][0] + "	: " + data_gen_buf[i][1] + " := " + data_gen_buf[i][2] + ";
	"
		end
		file_out(out_vhdl_name,"a",data)
	end
end
data = "		);"
file_out(out_vhdl_name,"a",data)
end

data = "
	PORT (
"
file_out(out_vhdl_name,"a",data)

for i in 0..i_port_buf
	if(i == i_port_buf)
		data = "	" + data_port_buf[i][0] + " 			: " + data_port_buf[i][1] + " " + data_port_buf[i][2] + "\n"
	else
		data = "	" + data_port_buf[i][0] + " 			: " + data_port_buf[i][1] + " " + data_port_buf[i][2] + ";\n"
	end
	file_out(out_vhdl_name,"a",data)
end

data = "	);
END COMPONENT;

BEGIN
"
file_out(out_vhdl_name,"a",data)

data = 	"i1 : " + entity_name + "\n"
file_out(out_vhdl_name,"a",data)

if(generic_flag == 1) # 20120916 add
data = "
	generic map(
"
file_out(out_vhdl_name,"a",data)

if(data_gen_buf[0][0] != "") # 20120619 add
	for i in 0..i_gen_buf
		if(i == i_gen_buf)
			data = "		" + data_gen_buf[i][0] + " => " + data_gen_buf[i][2] + "
	"
		else
			data = "		" + data_gen_buf[i][0] + " => " + data_gen_buf[i][2] + ",
	"
		end
		file_out(out_vhdl_name,"a",data)
	end
end
data = "	)"
file_out(out_vhdl_name,"a",data)

end
data = "
	PORT MAP (
"
file_out(out_vhdl_name,"a",data)

for i in 0..i_port_buf
	if(i == i_port_buf)
		data = "	" + data_port_buf[i][0] + " 			=> " + data_port_buf[i][0] + "\n"
	else
		data = "	" + data_port_buf[i][0] + " 			=> " + data_port_buf[i][0] + ",\n"
	end
	file_out(out_vhdl_name,"a",data)
end
data = "	);

	init1 : process                                               
	begin                                                        
		rst_n <= '0';
		wait for 3000 ns;
		rst_n <= '1';
		wait;
	END process init1;                                           

	init2 : process -- 10MHz = 100us/1cycle
	begin
		 loop
			clk <= '0';
			wait for 50 ns;
			clk <= '1';
			wait for 50 ns;
		end loop;
	end process init2;

"
file_out(out_vhdl_name,"a",data)

# 20121119 add
data = "

	process(clk,rst_n)
	begin
		if(rst_n = '0')then
			-- if this parameter is included clk and rst_n(reset), please delete it.
"
file_out(out_vhdl_name,"a",data)

for i in 0..i_port_buf
	if(data_port_buf[i][0].downcase != "clk" && data_port_buf[i][0].downcase != "rst_n" )
		if(data_port_buf[i][1].downcase == "in")
			if(data_port_buf[i][2].downcase == "std_logic")
				data = "			" + data_port_buf[i][0] + " 			<= " + "'0';" + "\n"
			elsif(check = data_port_buf[i][2].downcase.include?("std_logic_vector"))
				data = "			" + data_port_buf[i][0] + " 			<= " + "(others => '0');" + "\n"
			elsif(check = data_port_buf[i][2].downcase.include?("signed"))
				data = "			" + data_port_buf[i][0] + " 			<= " + "(others => '0');" + "\n"
			else
				data = "			" + data_port_buf[i][0] + " 			<= " + "(others => '0');" + "\n"
			end
			file_out(out_vhdl_name,"a",data)
		elsif(data_port_buf[i][1].downcase == "inout")
			if(data_port_buf[i][2].downcase == "std_logic")
				data = "			" + data_port_buf[i][0] + " 			<= " + "'Z';" + "\n"
			else
				data = "			" + data_port_buf[i][0] + " 			<= " + "(others => 'Z');" + "\n"
			end
			file_out(out_vhdl_name,"a",data)
		else
		end
	end
end
data = "
		elsif(clk'event and clk = '1')then
			-- please write your code in this area.
		
		end if;
	end process;

"
file_out(out_vhdl_name,"a",data)

data = "
END tb_" + entity_name + "_arch;
"
file_out(out_vhdl_name,"a",data)

##########
# 20121119 add for csv
##########
buf = []
bus_buf = ""
data = '"Name"' + ',"入出力"'+ ',"備考"' + "\n"
file_out(csv_name,"a",data)
for i in 0..i_port_buf
	if(data_port_buf[i][2].downcase != "std_logic")
		buf = data_port_buf[i][2].downcase.sub("(",",").sub("downto",",").sub(" to ",",").sub(")",",").split(/\s*,\s*/)
		p(buf)
		if(buf[1] != nil && buf[2] != nil)
			bus_buf = '[' + buf[1] + '..' + buf[2] + ']' # ex) [11..0]
		else
			bus_buf = ''
		end
	else
		bus_buf = ''
	end
	data = '"'+ data_port_buf[i][0] + bus_buf + '"' + ',"'+ data_port_buf[i][1].downcase + '"'+ ',"' + '"' + "\n" # 20121120 add downcase
	# data = '"'+ data_port_buf[i][0] + '"' + ',"'+ data_port_buf[i][1] + '"'+ ',"'+ data_port_buf[i][2] + '"' + "\n"
	file_out(csv_name,"a",data)
end
##########
p("vhd2tbtmpl end") # 20121119

# 20120619 add
if(win_flag == 1)
	print("テストベンチ作成完了。" + out_vhdl_name + "が出力されました。\n")
	print("終了するには何かキーを押してください。\n")
	kbhit = Win32API.new('msvcrt','_kbhit',[],'l')
	while true
	   if kbhit.call != 0
		  break
	   end
	end
	puts "\nキーが押されました"
else
	print("testbench template was genarated. " + out_vhdl_name + ".\n")

end

