use std::fs;
use std::io;
use std::path::Path;
use crate::export_config::FpgaExportConfig;
use crate::model::Model;
use crate::passes::{OptimizedModel, optimize, optimize_default};
use crate::tune::Tune;
pub mod argmax;
pub mod board_shell;
pub mod bram;
pub mod conv2d;
pub mod conv2d_parallel;
pub mod dense;
pub mod io_ports;
pub mod maxpool;
pub mod relu;
pub mod requant;
pub mod synth;
pub mod top;
pub mod weight_unpack;
pub struct Artifact {
pub rel_path: String,
pub content: String,
}
pub struct LayerArtifacts {
pub module_name: String,
pub instance_name: String,
pub out_len: usize,
pub sv: Artifact,
pub mems: Vec<Artifact>,
}
pub fn emit_model(model: &Model, out_dir: &Path) -> io::Result<()> {
emit_with_config(model, &FpgaExportConfig::default(), out_dir)
}
pub fn emit_model_tuned(model: &Model, tune: &Tune, out_dir: &Path) -> io::Result<()> {
let cfg = FpgaExportConfig::default().with_tune(*tune);
emit_with_config(model, &cfg, out_dir)
}
pub fn emit_with_config(model: &Model, cfg: &FpgaExportConfig, out_dir: &Path) -> io::Result<()> {
let opt = optimize(model, &cfg.tune);
emit_optimized_with_config(&opt, cfg, out_dir)
}
pub fn emit_optimized(opt: &OptimizedModel, out_dir: &Path) -> io::Result<()> {
emit_optimized_with_config(opt, &FpgaExportConfig::default(), out_dir)
}
pub fn emit_optimized_with_config(
opt: &OptimizedModel,
cfg: &FpgaExportConfig,
out_dir: &Path,
) -> io::Result<()> {
fs::create_dir_all(out_dir.join("primitives"))?;
fs::create_dir_all(out_dir.join("layers"))?;
fs::create_dir_all(out_dir.join("weights"))?;
let mut arts = collect_artifacts_io(opt, &cfg.io);
arts.extend(synth::collect_synth_artifacts(&opt.model, cfg));
if cfg.emit_board_shell {
if let Some(a) = board_shell::collect_board_shell(&opt.model, &cfg.hw_target, &cfg.io) {
arts.push(a);
}
}
for a in &arts {
let path = out_dir.join(&a.rel_path);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(&path, &a.content)?;
if a.rel_path == "synth.sh" {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&path, perms)?;
}
}
}
Ok(())
}
pub fn collect_artifacts(model: &Model) -> Vec<Artifact> {
collect_artifacts_io(
&optimize_default(model),
&crate::export_config::IoConfig::default(),
)
}
pub fn collect_artifacts_opt(opt: &OptimizedModel) -> Vec<Artifact> {
collect_artifacts_io(opt, &crate::export_config::IoConfig::default())
}
pub fn collect_artifacts_io(
opt: &OptimizedModel,
io: &crate::export_config::IoConfig,
) -> Vec<Artifact> {
let mut out: Vec<Artifact> = vec![
Artifact {
rel_path: "primitives/block_rom.sv".into(),
content: bram::emit_block_rom(),
},
Artifact {
rel_path: "primitives/block_ram.sv".into(),
content: bram::emit_block_ram(),
},
Artifact {
rel_path: "primitives/requant_q31.sv".into(),
content: requant::emit_requant_q31(),
},
Artifact {
rel_path: "primitives/requant_q15.sv".into(),
content: requant::emit_requant_q15(),
},
];
out.push(Artifact {
rel_path: "primitives/weight_unpack.sv".into(),
content: weight_unpack::emit(),
});
let mut layers = Vec::with_capacity(opt.model.layers.len());
for (idx, layer) in opt.model.layers.iter().enumerate() {
let hints = opt.hints_for(idx);
if hints.elided {
continue;
}
let la = match layer {
crate::model::Layer::Conv2d { .. } => {
if hints.parallelism > 1 || hints.ic_parallelism > 1 {
conv2d_parallel::emit(layer, hints)
} else {
conv2d::emit(layer, hints)
}
}
crate::model::Layer::Dense { .. } => dense::emit(layer, hints),
crate::model::Layer::Relu { .. } => relu::emit(layer),
crate::model::Layer::MaxPool2d { .. } => maxpool::emit(layer),
crate::model::Layer::Argmax { .. } => argmax::emit(layer),
};
out.push(la.sv);
out.extend(la.mems);
layers.push(LayerHandle {
module_name: la.module_name,
instance_name: la.instance_name,
out_len: la.out_len,
layer: layer.clone(),
hints: hints.clone(),
});
}
out.push(top::emit(
&opt.model,
&layers,
&opt.tune,
&opt.arena_bank,
io,
));
out.push(Artifact {
rel_path: "tb.sv".into(),
content: top::emit_tb(&opt.model, io),
});
out
}
pub use crate::passes::Hints as LayerHints;
pub use crate::passes::optimize as run_passes;
pub use crate::tune::{OptTarget as TuneTarget, Tune as TuneConfig};
pub struct LayerHandle {
pub module_name: String,
pub instance_name: String,
pub out_len: usize,
pub layer: crate::model::Layer,
pub hints: crate::passes::Hints,
}