###############################################################
|
###############################################################
|
#
|
#
|
# File: hdl_coder.rb
|
# File: hdl_coder.rb
|
#
|
#
|
# Author: Christian Hättich
|
# Author: Christian Hättich
|
#
|
#
|
# Project: System-On-Chip Maker
|
# Project: System-On-Chip Maker
|
#
|
#
|
# Target: Linux / Windows / Mac
|
# Target: Linux / Windows / Mac
|
#
|
#
|
# Language: ruby
|
# Language: ruby
|
#
|
#
|
#
|
#
|
###############################################################
|
###############################################################
|
#
|
#
|
#
|
#
|
# Copyright (C) 2014 Christian Hättich - feddischson [ at ] opencores.org
|
# Copyright (C) 2014 Christian Hättich - feddischson [ at ] opencores.org
|
#
|
#
|
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
# the Free Software Foundation, either version 3 of the License, or
|
# the Free Software Foundation, either version 3 of the License, or
|
# (at your option) any later version.
|
# (at your option) any later version.
|
#
|
#
|
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
#
|
#
|
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
# along with this program. If not, see .
|
# along with this program. If not, see .
|
#
|
#
|
#
|
#
|
###############################################################
|
###############################################################
|
#
|
#
|
# Description:
|
# Description:
|
# This file contains two HDL-coders:
|
# This file contains two HDL-coders:
|
# - VHDLCoder
|
# - VHDLCoder
|
# - VerilogCoder (not implemented, yet)
|
# - VerilogCoder (not implemented, yet)
|
#
|
#
|
#
|
#
|
#
|
#
|
###############################################################
|
###############################################################
|
|
|
module SOCMaker
|
module SOCMaker
|
class HDLCoder
|
class HDLCoder
|
|
|
def initialize
|
def initialize
|
@decl_part = ""; # declaration
|
@decl_part = ""; # declaration
|
@asgn_part = ""; # assignment
|
@asgn_part = ""; # assignment
|
@inst_part = ""; # instantiation
|
@inst_part = ""; # instantiation
|
|
|
|
|
|
|
end
|
end
|
|
|
|
|
end
|
end
|
|
|
|
|
class VerilogCoder < HDLCoder
|
class VerilogCoder < HDLCoder
|
#TODO
|
#TODO
|
|
#
|
|
#
|
|
|
|
def filename( name )
|
|
return name + ".v"
|
|
end
|
|
|
end
|
end
|
|
|
|
|
class VHDLCoder < HDLCoder
|
class VHDLCoder < HDLCoder
|
|
|
|
|
|
|
|
|
#
|
#
|
# Add a component declaration to the declaration-string @decl_part
|
# Add a component declaration to the declaration-string @decl_part
|
# This for example looks like
|
# This for example looks like
|
# component <> is
|
# component <> is
|
# generic(
|
# generic(
|
# g1 : ...
|
# g1 : ...
|
# g2 : ...
|
# g2 : ...
|
# ...
|
# ...
|
# );
|
# );
|
# port(
|
# port(
|
# p1 : ...
|
# p1 : ...
|
# p2 : ...
|
# p2 : ...
|
# p3 : ...
|
# p3 : ...
|
# ...
|
# ...
|
# )
|
# )
|
# end component <>;
|
# end component <>;
|
#
|
#
|
# In addition, we add some VHDL comments (author, mail, license)
|
# In addition, we add some VHDL comments (author, mail, license)
|
#
|
#
|
def add_core_declaration( core_name, core_spec )
|
def add_core_component( core_name, core_spec )
|
|
|
@decl_part << "--\n"
|
@decl_part << "--\n"
|
@decl_part << "-- core author: #{core_spec.author} - #{core_spec.authormail}\n"
|
@decl_part << "-- core author: #{core_spec.author} - #{core_spec.authormail}\n"
|
@decl_part << "-- license: #{core_spec.license}\n"
|
@decl_part << "-- license: #{core_spec.license}\n"
|
@decl_part << "--\n"
|
@decl_part << "--\n"
|
@decl_part << "component #{core_spec.toplevel} is\n"
|
@decl_part << "component #{core_spec.toplevel} is\n"
|
generic_str = entity_generic_str( core_spec );
|
generic_str = entity_generic_str( core_spec );
|
@decl_part << "generic ( #{ generic_str });\n" if generic_str.size > 0
|
@decl_part << "generic ( #{ generic_str });\n" if generic_str.size > 0
|
@decl_part << "port( \n" << entity_port_str( core_spec ) <<" );\n"
|
@decl_part << "port( \n" << entity_port_str( core_spec ) <<" );\n"
|
@decl_part << "end component #{core_spec.toplevel};\n"
|
@decl_part << "end component #{core_spec.toplevel};\n"
|
entity_generic_str( core_spec )
|
#entity_generic_str( core_spec )
|
end
|
end
|
|
|
|
def filename( name )
|
|
return name + ".vhd"
|
|
end
|
|
|
|
|
def entity_generic_str( core )
|
def entity_generic_str( core )
|
|
|
generic_str = ""
|
generic_str = ""
|
core.generics do |gen_name, gen_type, gen_val, is_last|
|
core.generics do |gen_name, gen_type, gen_val, is_last|
|
generic_str << gen_name << " : " << gen_type << " := " << gen_val.to_s
|
generic_str << gen_name << " : " << gen_type << " := " << gen_val.to_s
|
generic_str << ";" unless is_last
|
generic_str << ";" unless is_last
|
generic_str << "\n"
|
generic_str << "\n"
|
end
|
end
|
return generic_str
|
return generic_str
|
end
|
end
|
|
|
|
|
#
|
#
|
# Create a string, which lists all signals of 'core'
|
# Create a string, which lists all signals of 'core'
|
# We iterate over all interface:
|
# We iterate over all interface:
|
# For each interface, we iterate over all ports:
|
# For each interface, we iterate over all ports:
|
# For each port, we lookup the definition in the 'soc_lib' and
|
# For each port, we lookup the definition in the 'soc_lib' and
|
# add the VHDL code according to the definition as string to
|
# add the VHDL code according to the definition as string to
|
# port_string
|
# port_string
|
#
|
#
|
def entity_port_str( core )
|
def entity_port_str( core )
|
port_string = ""
|
port_string = ""
|
|
|
core.ports do |port_name, port_dir, port_len, is_last |
|
core.ports do |port_name, port_dir, port_len, is_last |
|
|
|
# The string we are add in every iteration looks for example like
|
# The string we are add in every iteration looks for example like
|
# myportname1 : out std_logic_vector( 6-1 downto 0 )
|
# myportname1 : out std_logic_vector( 6-1 downto 0 )
|
# or
|
# or
|
# myportname2 : in std_logic
|
# myportname2 : in std_logic
|
#
|
#
|
port_string << port_name.to_s << " : "
|
port_string << port_name.to_s << " : "
|
|
|
|
puts port_name.to_s + ": dir: " + port_dir.to_s + ", len: " + port_len.to_s
|
|
|
|
|
# port direction
|
# port direction
|
if port_dir == 2
|
if port_dir == 2
|
port_string << " inout "
|
port_string << " inout "
|
elsif port_dir == 1
|
elsif port_dir == 1
|
port_string << " in "
|
port_string << " in "
|
else
|
else
|
port_string << " out "
|
port_string << " out "
|
end
|
end
|
|
|
# port type / length
|
# port type / length
|
if( port_len.is_a?( String ) ||
|
if( port_len.is_a?( String ) ||
|
( port_len.is_a?( Fixnum ) && port_len > 1 )
|
( port_len.is_a?( Fixnum ) && port_len > 1 )
|
)
|
)
|
port_string << " std_logic_vector( #{port_len}-1 downto 0 ) "
|
port_string << " std_logic_vector( #{port_len}-1 downto 0 ) "
|
elsif ( port_len.is_a?( Fixnum ) && port_len == 1 )
|
elsif ( port_len.is_a?( Fixnum ) && port_len == 1 )
|
port_string << " std_logic "
|
port_string << " std_logic "
|
else
|
else
|
puts "FAILED " + port_len
|
puts "FAILED " + port_len.to_s #TODO
|
end
|
end
|
|
|
# end of the line
|
# end of the line
|
port_string << ";" unless is_last
|
port_string << ";" unless is_last
|
port_string << "\n"
|
port_string << "\n"
|
end
|
end
|
return port_string
|
return port_string
|
end
|
end
|
|
|
|
|
def add_core_inst( inst_name, inst )
|
def add_core_instance( inst_name, inst )
|
|
|
@inst_part << inst_name << " : " << inst.defn.toplevel << "\n"
|
@inst_part << inst_name << " : " << inst.defn.toplevel << "\n"
|
generic_str = ""
|
generic_str = ""
|
inst.generics do |generic, type, value, is_last|
|
inst.generics do |generic, type, value, is_last|
|
generic_str << "#{generic} => #{value}"
|
generic_str << "#{generic} => #{value}"
|
generic_str << "," unless is_last
|
generic_str << "," unless is_last
|
generic_str << "\n"
|
generic_str << "\n"
|
end
|
end
|
@inst_part << "generic map( \n#{generic_str} )\n" if generic_str.size > 0
|
@inst_part << "generic map( \n#{generic_str} )\n" if generic_str.size > 0
|
port_str = ""
|
port_str = ""
|
inst.ports do |port_name, length, dir, is_last|
|
inst.ports do |port_name, dir, length, is_last|
|
port_str << "#{port_name} => #{inst_name}_#{port_name}"
|
port_str << "#{port_name} => #{inst_name}_#{port_name}"
|
port_str << "," unless is_last
|
port_str << "," unless is_last
|
port_str << "\n"
|
port_str << "\n"
|
if length > 1
|
if length > 1
|
@decl_part << "signal #{inst_name}_#{port_name} : std_logic_vector( #{length}-1 downto 0 );\n"
|
@decl_part << "signal #{inst_name}_#{port_name} : std_logic_vector( #{length}-1 downto 0 );\n"
|
else
|
else
|
@decl_part << "signal #{inst_name}_#{port_name} : std_logic;\n"
|
@decl_part << "signal #{inst_name}_#{port_name} : std_logic;\n"
|
end
|
end
|
end
|
end
|
@inst_part << "port map( \n#{port_str} );\n\n\n" if port_str.size > 0
|
@inst_part << "port map( \n#{port_str} );\n\n\n" if port_str.size > 0
|
|
|
end
|
end
|
|
|
|
|
|
|
|
def add_ifc_connection( ifc_spec, ifc_name, length, src_inst, dst_inst, src_ifc, dst_ifc )
|
|
|
|
###
|
|
#
|
|
# declaration
|
def ifc_declaration( ifc_spec, ifc_name, length )
|
#
|
|
#
|
ifc_spec.ports.each do |port_name, port|
|
ifc_spec.ports.each do |port_name, port|
|
|
|
@decl_part << "signal #{ifc_name}_#{port_name.to_s} : "
|
@decl_part << "signal #{ifc_name}_#{port_name.to_s} : "
|
if length[ port_name ] > 1
|
if length[ port_name ] > 1
|
@decl_part << " std_logic_vector( #{length[ port_name ]}-1 downto 0 ) "
|
@decl_part << " std_logic_vector( #{length[ port_name ]}-1 downto 0 ) "
|
else
|
else
|
@decl_part << " std_logic "
|
@decl_part << " std_logic "
|
end
|
end
|
# end of the line
|
# end of the line
|
@decl_part << ";\n"
|
@decl_part << ";\n"
|
end
|
end
|
|
|
end
|
|
|
|
def ifc_assignment( ifc_spec, ifc_name, length, src_inst, dst_inst, src_ifc, dst_ifc )
|
|
|
|
|
###
|
|
#
|
|
# assignment
|
|
#
|
|
#
|
ifc_spec.ports.each do |port_name, port_dir|
|
ifc_spec.ports.each do |port_name, port_dir|
|
if port_dir == 0
|
if port_dir == 0
|
src_inst_sel = src_inst
|
src_inst_sel = src_inst
|
dst_inst_sel = dst_inst
|
dst_inst_sel = dst_inst
|
src_ifc_sel = src_ifc
|
src_ifc_sel = src_ifc
|
dst_ifc_sel = dst_ifc
|
dst_ifc_sel = dst_ifc
|
else
|
else
|
src_inst_sel = dst_inst
|
src_inst_sel = dst_inst
|
dst_inst_sel = src_inst
|
dst_inst_sel = src_inst
|
src_ifc_sel = dst_ifc
|
src_ifc_sel = dst_ifc
|
dst_ifc_sel = src_ifc
|
dst_ifc_sel = src_ifc
|
end
|
end
|
|
|
|
|
# length == 0 means, that no
|
# length == 0 means, that no
|
# signal is assigned to this connection
|
# signal is assigned to this connection
|
if length[ port_name ] > 0
|
if length[ port_name ] > 0
|
|
|
port_tmp_name = "#{ifc_name}_#{port_name.to_s}"
|
port_tmp_name = "#{ifc_name}_#{port_name.to_s}"
|
|
|
|
|
# combine all sources
|
# combine all sources
|
tmp = "#{port_tmp_name} <= "
|
tmp = "#{port_tmp_name} <= "
|
|
assigned = false
|
|
|
# loop over instances
|
# loop over instances
|
src_inst_sel.each_with_index do |(inst_name, inst), i|
|
src_inst_sel.each_with_index do |(inst_name, inst), i|
|
( tmp_name, port) = inst.get_port( src_ifc_sel[ inst_name ], port_name )
|
( tmp_name, port) = inst.get_port( src_ifc_sel[ inst_name ], port_name )
|
if port != nil
|
if port != nil
|
tmp << "\"" + "0" * ( length[ port_name ] - port[ :len ] ) + "\" & " if port[ :len ] < length[ port_name ]
|
tmp << "\"" + "0" * ( length[ port_name ] - port[ :len ] ) + "\" & " if port[ :len ] < length[ port_name ]
|
tmp << "#{inst_name}_#{tmp_name}"
|
tmp << "#{inst_name}_#{tmp_name}"
|
tmp << " and \n" unless i == src_inst_sel.size-1
|
tmp << " and \n" unless i == src_inst_sel.size-1
|
|
assigned = true
|
end
|
end
|
|
|
end
|
end
|
tmp << ";\n"
|
tmp << ";\n"
|
@asgn_part << tmp
|
@asgn_part << tmp if assigned
|
|
|
|
|
|
puts src_inst_sel.size
|
|
puts tmp
|
|
|
|
|
# assign to destination
|
|
tmp = ""
|
tmp = ""
|
|
assigned = false
|
|
# assign to destination
|
dst_inst_sel.each_with_index do |(inst_name, inst), i|
|
dst_inst_sel.each_with_index do |(inst_name, inst), i|
|
|
if not inst == nil #TODO
|
( tmp_name, port) = inst.get_port( dst_ifc_sel[ inst_name ], port_name )
|
( tmp_name, port) = inst.get_port( dst_ifc_sel[ inst_name ], port_name )
|
if port != nil
|
if port != nil
|
tmp << "#{inst_name}_#{tmp_name} <= #{port_tmp_name}"
|
tmp << "#{inst_name}_#{tmp_name} <= #{port_tmp_name}"
|
tmp << "( #{port[ :len ]}-1 downto 0 )" if port[ :len ] > 1
|
tmp << "( #{port[ :len ]}-1 downto 0 )" if port[ :len ] > 1
|
tmp << ";\n"
|
tmp << ";\n"
|
|
assigned = true
|
end
|
end
|
end
|
end
|
@asgn_part << tmp
|
|
end
|
end
|
|
@asgn_part << tmp if assigned
|
end
|
end
|
|
|
|
end
|
|
|
end
|
end
|
|
|
|
|
|
|
# TODO: add sig list as argument (or interface list) for entity description
|
def get_hdl_code( soc, entity_name )
|
def get_entity( soc, entity_name )
|
|
add_toplevel_sig( soc, entity_name )
|
add_toplevel_sig( soc, entity_name )
|
entity_str = SOCMaker::conf[ :LIC ].split(/\n/).map{ |s| "-- "+s }.join("\n") + "\n"
|
entity_str = SOCMaker::conf[ :LIC ].split(/\n/).map{ |s| "-- "+s }.join("\n") + "\n"
|
entity_str << "-- Auto-Generated by #{SOCMaker::conf[ :app_name ]} \n"
|
entity_str << "-- Auto-Generated by #{SOCMaker::conf[ :app_name ]} \n"
|
entity_str << "-- Date: #{Time.now}\n"
|
entity_str << "-- Date: #{Time.now}\n"
|
entity_str << SOCMaker::conf[ :vhdl_include ] + "\n"
|
entity_str << SOCMaker::conf[ :vhdl_include ] + "\n"
|
entity_str << "entity #{entity_name} is \n"
|
entity_str << "entity #{entity_name} is \n"
|
tmp = entity_port_str( soc )
|
tmp = entity_port_str( soc )
|
entity_str << "port( \n" << tmp << " );\n" if tmp.size > 0
|
entity_str << "port( \n" << tmp << " );\n" if tmp.size > 0
|
entity_str << "end entity #{entity_name};\n\n\n"
|
entity_str << "end entity #{entity_name};\n\n\n"
|
entity_str << "ARCHITECTURE IMPL of #{entity_name} is \n"
|
entity_str << "ARCHITECTURE IMPL of #{entity_name} is \n"
|
entity_str << @decl_part
|
entity_str << @decl_part
|
entity_str << "\n\n"
|
entity_str << "\n\n"
|
entity_str << "begin"
|
entity_str << "begin"
|
entity_str << "\n\n"
|
entity_str << "\n\n"
|
entity_str << "--"
|
entity_str << "--"
|
entity_str << "-- assignments "
|
entity_str << "-- assignments "
|
entity_str << "--"
|
entity_str << "--"
|
entity_str << "\n\n"
|
entity_str << "\n\n"
|
entity_str << @asgn_part
|
entity_str << @asgn_part
|
entity_str << "\n\n"
|
entity_str << "\n\n"
|
entity_str << "--"
|
entity_str << "--"
|
entity_str << "-- instances "
|
entity_str << "-- instances "
|
entity_str << "--"
|
entity_str << "--"
|
entity_str << "\n\n"
|
entity_str << "\n\n"
|
entity_str << @inst_part
|
entity_str << @inst_part
|
entity_str << "end ARCHITECTURE IMPL;"
|
entity_str << "end ARCHITECTURE IMPL;"
|
return entity_str
|
return entity_str
|
end
|
end
|
|
|
def add_toplevel_sig( soc, entity_name )
|
def add_toplevel_sig( soc, entity_name )
|
soc.ports do |port_name, length, dir, is_last|
|
soc.ports do |port_name, dir, length, is_last|
|
|
if dir == 0
|
@asgn_part << "#{port_name} <= #{entity_name}_#{port_name}"
|
@asgn_part << "#{port_name} <= #{entity_name}_#{port_name}"
|
@asgn_part << "," unless is_last
|
else
|
@asgn_part << "\n"
|
@asgn_part << "#{entity_name}_#{port_name} <= #{port_name} "
|
|
end
|
|
@asgn_part << ";\n"
|
if length > 1
|
if length > 1
|
@decl_part << "signal #{entity_name}_#{port_name} : std_logic_vector( #{length}-1 downto 0 );\n"
|
@decl_part << "signal #{entity_name}_#{port_name} : std_logic_vector( #{length}-1 downto 0 );\n"
|
else
|
else
|
@decl_part << "signal #{entity_name}_#{port_name} : std_logic;\n"
|
@decl_part << "signal #{entity_name}_#{port_name} : std_logic;\n"
|
end
|
end
|
end
|
end
|
end
|
end
|
|
|
|
|
|
|
def asgn_str( ifc_spec, con, ifc_name, core1, core1_name, core2, core2_name )
|
def asgn_str( ifc_spec, con, ifc_name, core1, core1_name, core2, core2_name )
|
port_string = ""
|
port_string = ""
|
|
|
{ core1 => [ con[ :ifc1 ], :src1 ],
|
{ core1 => [ con[ :ifc1 ], :src1 ],
|
core2 => [ con[ :ifc2 ], :src2 ] }.each do| core, tmp |
|
core2 => [ con[ :ifc2 ], :src2 ] }.each do| core, tmp |
|
|
|
core.ports( tmp[0] ) do | port_spec_name, port_name, port_dir |
|
core.ports( tmp[0] ) do | port_spec_name, port_name, port_dir |
|
|
|
if port_dir == 0
|
if port_dir == 0
|
port_string << "#{ifc_name}_#{port_spec_name}"
|
port_string << "#{ifc_name}_#{port_spec_name}"
|
port_string << " <= "
|
port_string << " <= "
|
port_string << "#{con[ tmp[1] ]}_#{port_name}"
|
port_string << "#{con[ tmp[1] ]}_#{port_name}"
|
port_string << ";\n"
|
port_string << ";\n"
|
else
|
else
|
port_string << "#{con[ tmp[1] ]}_#{port_name}"
|
port_string << "#{con[ tmp[1] ]}_#{port_name}"
|
port_string << " <= "
|
port_string << " <= "
|
port_string << "#{ifc_name}_#{port_spec_name }"
|
port_string << "#{ifc_name}_#{port_spec_name }"
|
port_string << ";\n"
|
port_string << ";\n"
|
end
|
end
|
end
|
end
|
|
|
end
|
end
|
|
|
return port_string
|
return port_string
|
end
|
end
|
|
|
|
|
|
|
|
|
|
|
end
|
end
|
end
|
end
|
|
|
# vim: noai:ts=2:sw=2
|
# vim: noai:ts=2:sw=2
|
|
|