use crate::traits::Backend;
use calyx_ir as ir;
use calyx_utils::CalyxResult;
use serde::Serialize;
#[derive(Default)]
pub struct XilinxXmlBackend;
#[derive(Serialize)]
#[serde(rename = "root")]
struct Root<'a> {
#[serde(rename = "@versionMajor")]
version_major: u64,
#[serde(rename = "@versionMinor")]
version_minor: u64,
kernel: Kernel<'a>,
}
#[derive(Serialize)]
#[serde(rename = "kernel")]
struct Kernel<'a> {
#[serde(rename = "@name")]
name: &'a str,
#[serde(rename = "@language")]
language: &'a str,
#[serde(rename = "@vlnv")]
vlnv: &'a str,
#[serde(rename = "@preferredWorkGroupSizeMultiple")]
preferred_work_group_size_multiple: u64,
#[serde(rename = "@workGroupSize")]
work_group_size: u64,
#[serde(rename = "@interrupt")]
interrupt: bool,
#[serde(rename = "@hwControlProtocol")]
hw_control_protocol: &'a str,
ports: Ports<'a>,
args: Args<'a>,
}
#[derive(Serialize)]
struct Ports<'a> {
port: Vec<Port<'a>>,
}
impl<'a> From<Vec<Port<'a>>> for Ports<'a> {
fn from(port: Vec<Port<'a>>) -> Self {
Ports { port }
}
}
#[derive(Serialize)]
#[serde(rename = "port")]
struct Port<'a> {
#[serde(rename = "@name")]
name: &'a str,
#[serde(rename = "@mode")]
mode: &'a str,
#[serde(rename = "@range")]
range: &'a str,
#[serde(rename = "@dataWidth")]
data_width: u64,
#[serde(rename = "@portType")]
port_type: &'a str,
#[serde(rename = "@base")]
base: &'a str,
}
#[derive(Serialize)]
struct Args<'a> {
arg: Vec<Arg<'a>>,
}
impl<'a> From<Vec<Arg<'a>>> for Args<'a> {
fn from(arg: Vec<Arg<'a>>) -> Self {
Args { arg }
}
}
#[derive(Serialize)]
struct Arg<'a> {
#[serde(rename = "@name")]
name: &'a str,
#[serde(rename = "@addressQualifier")]
address_qualifier: u64,
#[serde(rename = "@id")]
id: u64,
#[serde(rename = "@port")]
port: &'a str,
#[serde(rename = "@size")]
size: &'a str,
#[serde(rename = "@offset")]
offset: &'a str,
#[serde(rename = "@type")]
typ: &'a str,
#[serde(rename = "@hostOffset")]
host_offset: &'a str,
#[serde(rename = "@hostSize")]
host_size: &'a str,
}
impl Backend for XilinxXmlBackend {
fn name(&self) -> &'static str {
"xilinx-xml"
}
fn validate(_ctx: &ir::Context) -> CalyxResult<()> {
Ok(())
}
fn link_externs(
_prog: &ir::Context,
_write: &mut calyx_utils::OutputFile,
) -> CalyxResult<()> {
Ok(())
}
fn emit(
prog: &ir::Context,
file: &mut calyx_utils::OutputFile,
) -> CalyxResult<()> {
let toplevel = prog
.components
.iter()
.find(|comp| comp.name == prog.entrypoint)
.unwrap();
let mut ports = vec![Port {
name: "s_axi_control",
mode: "slave",
range: "0x1000",
data_width: 32,
port_type: "addressable",
base: "0x0",
}];
let mut args = vec![Arg {
name: "timeout",
address_qualifier: 0,
id: 0,
port: "s_axi_control",
size: "0x4",
offset: "0x010",
typ: "uint",
host_offset: "0x0",
host_size: "0x4",
}];
let memories: Vec<(String, String)> = toplevel
.cells
.iter()
.filter(|cell_ref| {
cell_ref
.borrow()
.get_attribute(ir::BoolAttr::External)
.is_some()
})
.enumerate()
.map(|(i, cell_ref)| {
(cell_ref.borrow().name().to_string(), format!("m{}_axi", i))
})
.collect();
let memories_ref: Vec<(&str, &str)> = memories
.iter()
.map(|(x, y)| (x.as_ref(), y.as_ref()))
.collect();
let offsets: Vec<String> = (0..memories.len())
.map(|i| format!("{:#x}", 0x18 + (8 * i)))
.collect();
for (i, (name, axi_name)) in memories_ref.iter().enumerate() {
ports.push(Port {
name: axi_name,
mode: "master",
range: "0xFFFFFFFFFFFFFFFF",
data_width: 512,
port_type: "addressable",
base: "0x0",
});
args.push(Arg {
name,
address_qualifier: 1,
id: (i + 1) as u64,
port: axi_name,
size: "0x8",
offset: &offsets[i],
typ: "int*",
host_offset: "0x0",
host_size: "0x8",
});
}
let root = Root {
version_major: 1,
version_minor: 6,
kernel: Kernel {
name: "Toplevel",
language: "ip_c",
vlnv: "capra.cs.cornell.edu:kernel:Toplevel:1.0",
preferred_work_group_size_multiple: 0,
work_group_size: 1,
interrupt: false,
hw_control_protocol: "ap_ctrl_hs",
ports: ports.into(),
args: args.into(),
},
};
write!(
file.get_write(),
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n{}",
quick_xml::se::to_string(&root).expect("XML Serialization failed")
)?;
Ok(())
}
}