Commit ad1c4c32 authored by Jens Korinth's avatar Jens Korinth
Browse files

Support for manual AXI-mapping, postBuildOps fix

* postBuildActions can no longer use the Module instance directly, since
  Chisel3 prohibits its instantiation outside of a Driver
* added a configuration object of type Any instead, which will be passed
  to the postBuildActions - we do not actually need the Module instance,
  only the configuration options passed to it
* Chisel3 no longer supports renaming of top-level ports (setName has
  vanished); auto-inference of the AXI-interfaces is not possible
  without renaming
* fixed by adding interface definitions to CoreDefinition:
* each module may declare "known" interfaces; in this case the IP-XACT
  Tcl will match to the generated port names manually
parent fa4bfbeb
......@@ -4,11 +4,15 @@ import play.api.libs.json._
import play.api.libs.functional.syntax._
import chisel3.Module
/** Abstraction for known bus interfaces / pin groups. */
final case class Interface(name: String, kind: String)
/**
* Basic definition of a core for IP-XACT packaging.
**/
class CoreDefinition(val name: String, val vendor: String, val library: String, val version: String,
val root: String, val postBuildActions: Seq[Module => Unit] = Seq()) {
val root: String, val postBuildActions: Seq[Option[Any] => Unit] = Seq(),
val interfaces: Seq[Interface] = Seq()) {
import CoreDefinition._
def write(filename: String) : Boolean = try {
val fw = new java.io.FileWriter(filename)
......@@ -23,35 +27,43 @@ class CoreDefinition(val name: String, val vendor: String, val library: String,
* Contains methods for reading a core definition from Json.
**/
object CoreDefinition {
def apply(name: String, vendor: String, library: String, version: String, root: String): CoreDefinition =
new CoreDefinition(name, vendor, library, version, root)
def apply(name: String, vendor: String, library: String, version: String, root: String,
interfaces: Seq[Interface] = Seq()): CoreDefinition =
new CoreDefinition(name, vendor, library, version, root, interfaces = interfaces)
def withActions(name: String, vendor: String, library: String, version: String, root: String,
postBuildActions: Seq[Module => Unit]): CoreDefinition =
postBuildActions: Seq[Option[Any] => Unit], interfaces: Seq[Interface] = Seq()): CoreDefinition =
new CoreDefinition(name, vendor, library, version, root, postBuildActions)
def unapply(cd: CoreDefinition): Option[Tuple5[String, String, String, String, String]] =
Some((cd.name, cd.vendor, cd.library, cd.version, cd.root))
def unapply(cd: CoreDefinition): Option[Tuple6[String, String, String, String, String, Seq[Interface]]] =
Some((cd.name, cd.vendor, cd.library, cd.version, cd.root, cd.interfaces))
/** Provide automatic IP directory for given name. **/
def root(name: String): String =
java.nio.file.Paths.get(".").toAbsolutePath.resolveSibling("ip").resolve(name).toString
implicit val coreDefinitionWrites : Writes[CoreDefinition] = (
implicit val interfaceFormat: Format[Interface] = (
(JsPath \ "name").format[String] ~
(JsPath \ "kind").format[String]
) (Interface.apply _, unlift(Interface.unapply _))
implicit val coreDefinitionWrites: Writes[CoreDefinition] = (
(JsPath \ "name").write[String] ~
(JsPath \ "vendor").write[String] ~
(JsPath \ "library").write[String] ~
(JsPath \ "version").write[String] ~
(JsPath \ "root").write[String]
)(unlift(CoreDefinition.unapply))
(JsPath \ "root").write[String] ~
(JsPath \ "interfaces").write[Seq[Interface]]
)(unlift(CoreDefinition.unapply _))
implicit val coreDefinitionReads : Reads[CoreDefinition] = (
implicit val coreDefinitionReads: Reads[CoreDefinition] = (
(JsPath \ "name").read[String] ~
(JsPath \ "vendor").read[String] ~
(JsPath \ "library").read[String] ~
(JsPath \ "version").read[String] ~
(JsPath \ "root").read[String]
)(CoreDefinition.apply _)
(JsPath \ "root").read[String] ~
(JsPath \ "interfaces").readNullable[Seq[Interface]].map(_ getOrElse Seq[Interface]())
)(apply _)
/**
* Read CoreDefinition from file containing Json format.
......
......@@ -2,6 +2,13 @@ package chisel.packaging
import chisel3._
import scala.sys.process._
/** Module definition.
* @param config Optional, arbitrary configuration object, passed to post build actions.
* @param constr Module constructor function.
* @param core Core definition.
**/
final case class ModuleDef(config: Option[Any], constr: () => Module, core: CoreDefinition)
/**
* Abstract IP-XACT builder class:
* Objects can inherit from ModuleBuilder to automate the building
......@@ -12,17 +19,18 @@ import scala.sys.process._
**/
abstract class ModuleBuilder(packagingDir: String = "packaging") {
val chiselArgs = Array[String]()
val modules: List[(() => Module, CoreDefinition)]
/** List of modules to build. */
val modules: Seq[ModuleDef]
def main(args: Array[String]) {
assert ((modules map (_._2.name.toLowerCase)).toSet.size == modules.length, "module names must be unique")
val fm = modules filter (m => args.length == 0 || args.map(_.toLowerCase).contains(m._2.name.toLowerCase))
assert ((modules map (_.core.name.toLowerCase)).toSet.size == modules.length, "module names must be unique")
val fm = modules filter (m => args.length == 0 || args.map(_.toLowerCase).contains(m.core.name.toLowerCase))
assert (fm.length > 0, "no matching cores found for: " + args.mkString(", "))
fm foreach { m =>
Driver.execute(chiselArgs ++ Array("--target-dir", m._2.root), m._1)
m._2.postBuildActions map (fn => fn.apply(m._1()))
val json = "%s/%s.json".format(m._2.root, m._2.name)
m._2.write(json)
Driver.execute(chiselArgs ++ Array("--target-dir", m.core.root), m.constr)
m.core.postBuildActions map (fn => fn.apply(m.config))
val json = "%s/%s.json".format(m.core.root, m.core.name)
m.core.write(json)
"%s/package.py %s".format(packagingDir, json) !
}
}
......
__port_map = {
'AWID' : 'io_{0}_writeAddr_bits_id',
'AWADDR' : 'io_{0}_writeAddr_bits_addr',
'AWLEN' : 'io_{0}_writeAddr_bits_burst_len',
'AWSIZE' : 'io_{0}_writeAddr_bits_burst_size',
'AWBURST' : 'io_{0}_writeAddr_bits_burst_burst',
'AWLOCK' : 'io_{0}_writeAddr_bits_lock_lock',
'AWCACHE' : 'io_{0}_writeAddr_bits_cache_cache',
'AWPROT' : 'io_{0}_writeAddr_bits_prot_prot',
'AWQOS' : 'io_{0}_writeAddr_bits_qos',
'AWREGION' : 'io_{0}_writeAddr_bits_region',
'AWUSER' : 'io_{0}_writeAddr_bits_user',
'AWVALID' : 'io_{0}_writeAddr_valid',
'AWREADY' : 'io_{0}_writeAddr_ready',
'WID' : 'io_{0}_writeData_bits_id',
'WDATA' : 'io_{0}_writeData_bits_data',
'WSTRB' : 'io_{0}_writeData_bits_strb_strb',
'WLAST' : 'io_{0}_writeData_bits_last',
'WUSER' : 'io_{0}_writeData_bits_user',
'WVALID' : 'io_{0}_writeData_valid',
'WREADY' : 'io_{0}_writeData_ready',
'BID' : 'io_{0}_writeResp_bits_bid',
'BRESP' : 'io_{0}_writeResp_bits_bresp',
'BUSER' : 'io_{0}_writeResp_bits_buser',
'BVALID' : 'io_{0}_writeResp_valid',
'BREADY' : 'io_{0}_writeResp_ready',
'ARID' : 'io_{0}_readAddr_bits_id',
'ARADDR' : 'io_{0}_readAddr_bits_addr',
'ARLEN' : 'io_{0}_readAddr_bits_burst_len',
'ARSIZE' : 'io_{0}_readAddr_bits_burst_size',
'ARBURST' : 'io_{0}_readAddr_bits_burst_burst',
'ARLOCK' : 'io_{0}_readAddr_bits_lock_lock',
'ARCACHE' : 'io_{0}_readAddr_bits_cache_cache',
'ARPROT' : 'io_{0}_readAddr_bits_prot_prot',
'ARQOS' : 'io_{0}_readAddr_bits_qos',
'ARREGION' : 'io_{0}_readAddr_bits_region',
'ARUSER' : 'io_{0}_readAddr_bits_user',
'ARVALID' : 'io_{0}_readAddr_valid',
'ARREADY' : 'io_{0}_readAddr_ready',
'RID' : 'io_{0}_readData_bits_id',
'RDATA' : 'io_{0}_readData_bits_data',
'RRESP' : 'io_{0}_readData_bits_resp',
'RLAST' : 'io_{0}_readData_bits_last',
'RUSER' : 'io_{0}_readData_bits_user',
'RVALID' : 'io_{0}_readData_valid',
'RREADY' : 'io_{0}_readData_ready'
}
def get_port_dict(name):
return {k: v.format(name) for k, v in __port_map.items()}
......@@ -3,6 +3,7 @@
import json
import argparse
from subprocess import Popen, STDOUT, PIPE
import axi4
script = '''
create_project -force "{0}" /tmp
......@@ -19,6 +20,84 @@ ipx::save_core [ipx::current_core]
close_project -delete
'''
__interface_script = '''
set name "{0}"
set vendor "{1}"
set library "{2}"
set version "{3}"
set root "{4}"
set mainmod "{5}"
cd $root
create_project -in_memory $root
update_ip_catalog
ipx::create_core $vendor $library $name $version
set core [ipx::current_core]
set_property supported_families {{virtex7 Production qvirtex7 Production kintex7 Production kintex7l Production qkintex7 Production qkintex7l Production artix7 Production artix7l Production aartix7 Production qartix7 Production zynq Production qzynq Production azynq Production}} $core
#set_property core_revision 1 $core
set_property display_name $name $core
set_property description $name $core
set_property taxonomy $library $core
set fg [ipx::add_file_group -type synthesis "sources" $core]
set_property -dict [list model_name $name language Verilog] $fg
ipx::import_top_level_hdl -top_level_hdl_file $mainmod -verbose $core
set clk [ipx::add_bus_interface clock $core]
set_property type_name std_logic [ipx::add_port "clock" $core]
set_property -dict [list \
abstraction_type_vlnv {{xilinx.com:signal:clock_rtl:1.0}} \
bus_type_vlnv {{xilinx.com:signal:clock:1.0}}] $clk
set_property physical_name "clock" [ipx::add_port_map "CLK" $clk]
set rst [ipx::add_bus_interface reset $core]
set_property type_name std_logic [ipx::add_port "reset" $core]
set_property -dict [list \
abstraction_type_vlnv {{xilinx.com:signal:reset_rtl:1.0}} \
bus_type_vlnv {{xilinx.com:signal:reset:1.0}}] $rst
set_property value ACTIVE_HIGH [ipx::add_bus_parameter POLARITY $rst]
set_property physical_name "reset" [ipx::add_port_map "RST" $rst]
{6}
ipx::create_default_gui_files $core
ipx::update_checksums $core
ipx::save_core $core
ipx::archive_core "$root/$name.zip" $core
#close_project -delete
'''
__axi4Tcl = '''
set if [ipx::add_bus_interface "{0}" $core]
set_property -dict [list \
abstraction_type_vlnv {{xilinx.com:interface:axi4mm_rtl:1.0}} \
bus_type_vlnv {{xilinx.com:interface:axi4mm:1.0}} \
interface_mode {1}] $if
ipx::associate_bus_interfaces -busif "{0}" -clock clock -reset reset $core
foreach {{bport port}} {2} {{
puts "$bport $port"
set_property physical_name "$port" [ipx::add_port_map $bport $if]
}}
'''
def map_interface(name, kind):
if kind == 'axi4master':
script = __axi4Tcl.format(name, 'master', make_tcl_port_list(axi4.get_port_dict(name)))
return script
elif kind == 'axi4slave':
script = __axi4Tcl.format(name, 'slave', make_tcl_port_list(axi4.get_port_dict(name)))
return script
else:
print 'unknown interface: ' + kind
return ''
def make_tcl_port_list(d, f=lambda x: x):
return "[list " + ' '.join([k + ' ' + f(d[k]) for k in sorted(d.keys())]) + "]"
def read_json(jsonfile):
with open(jsonfile, 'r') as jf:
contents = jf.read()
......@@ -26,7 +105,13 @@ def read_json(jsonfile):
def make_vivado_script(jsonfile):
cd = read_json(jsonfile)
return script.format(cd['name'], cd['vendor'], cd['library'], cd['version'], cd['root'])
if ('interfaces' not in cd) or (len(cd['interfaces']) is 0):
#print 'found no interfaces, or empty interface list, using auto-inference'
return script.format(cd['name'], cd['vendor'], cd['library'], cd['version'], cd['root'])
else:
#print 'found interfaces: ', cd['interfaces']
ifs = '\n'.join([map_interface(i['name'], i['kind']) for i in cd['interfaces']])
return __interface_script.format(cd['name'], cd['vendor'], cd['library'], cd['version'], cd['root'], cd['root'] + '/' + cd['name'] + '.v', ifs)
def run_vivado(script):
p = Popen(['vivado', '-mode', 'tcl', '-nolog', '-nojournal'], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
......@@ -40,3 +125,4 @@ def parse_args():
args = parse_args()
run_vivado(make_vivado_script(args.json))
#print make_vivado_script(args.json)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment