Skip to content

Commit

Permalink
Add dimensional and type data to yxi outputs (#1964)
Browse files Browse the repository at this point in the history
* Tried to change yxi utils and output

Adds memory_type ((seq or comb), dimension count, dimension sizes, and
total size

TODO: Fix dpeendent import issues
TODO: Test & change runt outputs

* cfgs

* Add crf[...serialize...] in calyx-ir utils.

Can now build correctly when passing `--features serialize` flag.
Normal compilation does not work

* Update runt tests

* Refactoring

* run clippy

* update cargo.tomls to add an yxi feature

* formatting

* change toplevel xilinx backend to correctly get total size of memories

* change dockerfile to build with

* change dahlia checkout to latest commit in master in Dockerfile

* add --all-features to cargo build in .github/workflows/rust.yml

* Revert "change dockerfile to build with"

This reverts commit e71886a.

* revert dockerfile to pinned dahlia checkout

---------

Co-authored-by: Rachit Nigam <[email protected]>
  • Loading branch information
nathanielnrn and rachitnigam authored Mar 23, 2024
1 parent 1632dfd commit 7a2a236
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 63 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --all --manifest-path /home/calyx/interp/Cargo.toml
args: --all --all-features --manifest-path /home/calyx/interp/Cargo.toml

- name: Set location of cider binary
run: |
Expand Down Expand Up @@ -174,7 +174,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path /home/calyx/Cargo.toml
args: --all-features --manifest-path /home/calyx/Cargo.toml

# - name: Source code doc tests
# uses: actions-rs/cargo@v1
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ path = "src/main.rs"
[features]
default = []
serialize = ["calyx-ir/serialize", "serde/rc", "calyx-backend/sexp"]
yxi = ["serialize", "calyx-backend/yxi", "calyx-ir/yxi"]

[build-dependencies]
calyx-stdlib = { path = "calyx-stdlib", version = "0.7.1" }
Expand Down
1 change: 1 addition & 0 deletions calyx-backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ mlir = []
xilinx = ["dep:quick-xml"]
resources = ["dep:csv"]
sexp = ["dep:serde_with", "dep:serde_sexpr", "serde/rc", "calyx-ir/serialize"]
yxi = ["calyx-ir/yxi"]
3 changes: 3 additions & 0 deletions calyx-backend/src/backend_opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub enum BackendOpt {
Mlir,
Resources,
Sexp,
#[cfg(feature = "yxi")]
Yxi,
Firrtl,
PrimitiveUses,
Expand All @@ -29,6 +30,7 @@ fn backends() -> Vec<(&'static str, BackendOpt)> {
("mlir", BackendOpt::Mlir),
("resources", BackendOpt::Resources),
("sexp", BackendOpt::Sexp),
#[cfg(feature = "yxi")]
("yxi", BackendOpt::Yxi),
("firrtl", BackendOpt::Firrtl),
("primitive-uses", BackendOpt::PrimitiveUses),
Expand Down Expand Up @@ -73,6 +75,7 @@ impl ToString for BackendOpt {
Self::Verilog => "verilog",
Self::Xilinx => "xilinx",
Self::XilinxXml => "xilinx-xml",
#[cfg(feature = "yxi")]
Self::Yxi => "yxi",
Self::Calyx => "calyx",
Self::Firrtl => "firrtl",
Expand Down
5 changes: 4 additions & 1 deletion calyx-backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ mod firrtl;
mod primitive_uses;
mod traits;
mod verilog;
mod yxi;

pub use backend_opt::BackendOpt;
pub use firrtl::FirrtlBackend;
pub use primitive_uses::PrimitiveUsesBackend;
pub use traits::Backend;
pub use verilog::VerilogBackend;

#[cfg(feature = "yxi")]
mod yxi;
#[cfg(feature = "yxi")]
pub use yxi::YxiBackend;

#[cfg(feature = "mlir")]
Expand Down
18 changes: 9 additions & 9 deletions calyx-backend/src/xilinx/toplevel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl Backend for XilinxInterfaceBackend {
.find(|c| c.name == prog.entrypoint)
.unwrap();

let memories = ir::utils::external_memories_names(toplevel);
let memories = ir::utils::external_and_ref_memories_names(toplevel);
if memories.is_empty() {
return Err(Error::misc(
"Program has no memories marked with attribute @external.".to_owned() +
Expand All @@ -51,8 +51,8 @@ impl Backend for XilinxInterfaceBackend {
for (i, mem) in mem_info.iter().enumerate() {
modules.push(bram(
&format!("SINGLE_PORT_BRAM_{}", i),
mem.width,
mem.size,
mem.data_width,
mem.total_size,
mem.idx_sizes[0],
))
}
Expand All @@ -70,8 +70,8 @@ impl Backend for XilinxInterfaceBackend {
&format!("Memory_controller_axi_{}", i),
512,
64,
mem.width,
mem.size,
mem.data_width,
mem.total_size,
mem.idx_sizes[0],
))
}
Expand All @@ -97,17 +97,17 @@ impl Backend for XilinxInterfaceBackend {
// Gets all memory cells in top level marked external.
//Panics if not all memories are 1-d
fn external_1d_memories_cells(comp: &ir::Component) -> Vec<ir::RRC<ir::Cell>> {
let memories = ir::utils::external_memories_cells(comp);
let memories = ir::utils::external_and_ref_memories_cells(comp);
for memory in memories.iter() {
if !memory.borrow().is_primitive(Some("comb_mem_d1")) {
panic!("cell `{}' marked with `@external' but is not a comb_mem_d1. The AXI generator currently only supports `comb_mem_d1'", memory.borrow().name())
panic!("cell `{}' marked with `@external' or `ref` but is not a comb_mem_d1. The AXI generator currently only supports `comb_mem_d1'", memory.borrow().name())
}
}
memories
}

fn top_level(toplevel: &ir::Component) -> v::Module {
let memories = &ir::utils::external_memories_names(toplevel);
let memories = &ir::utils::external_and_ref_memories_names(toplevel);
let mem_info = &external_1d_memories_cells(toplevel).get_mem_info();
assert!(!memories.is_empty()); // At least 1 memory should exist within the toplevel
let mut module = v::Module::new("Toplevel");
Expand Down Expand Up @@ -190,7 +190,7 @@ fn top_level(toplevel: &ir::Component) -> v::Module {
let addr0 = format!("{}_addr0", mem);
let write_en = format!("{}_write_en", mem);
let done = format!("{}_done", mem);
let width = mem_info[idx].width;
let width = mem_info[idx].data_width;
module.add_decl(v::Decl::new_wire(&write_data, width));
module.add_decl(v::Decl::new_wire(&read_data, width));
module.add_decl(v::Decl::new_wire(&addr0, mem_info[idx].idx_sizes[0]));
Expand Down
20 changes: 14 additions & 6 deletions calyx-backend/src/yxi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::traits::Backend;
use calyx_ir as ir;
use calyx_ir::utils::GetMemInfo;
use calyx_ir::utils::{GetMemInfo, MemoryType};
use calyx_utils::CalyxResult;
use serde::Serialize;
/// Backend that generates the YXI Interface Definition Language.
Expand All @@ -18,8 +18,12 @@ struct ProgramInterface<'a> {
#[derive(Serialize)]
struct Memory<'a> {
name: &'a str,
width: u64,
size: u64, //number of cells in memory
memory_type: MemoryType,
data_width: u64,
dimensions: u64,
dimension_sizes: Vec<u64>,
total_size: u64, //number of cells in memory
idx_sizes: Vec<u64>,
}

impl Backend for YxiBackend {
Expand Down Expand Up @@ -48,16 +52,20 @@ impl Backend for YxiBackend {
.find(|comp| comp.name == prog.entrypoint)
.unwrap();

let memory_names = ir::utils::external_memories_names(toplevel);
let memory_names = ir::utils::external_and_ref_memories_names(toplevel);
let mem_infos = toplevel.get_mem_info();

let memories: Vec<Memory> = memory_names
.iter()
.zip(mem_infos.iter())
.map(|(memory_name, mem_info)| Memory {
name: memory_name,
width: mem_info.width,
size: mem_info.size,
memory_type: mem_info.memory_type,
data_width: mem_info.data_width,
dimensions: mem_info.dimensions,
dimension_sizes: mem_info.dimension_sizes.clone(),
total_size: mem_info.total_size,
idx_sizes: mem_info.idx_sizes.clone(),
})
.collect();

Expand Down
3 changes: 2 additions & 1 deletion calyx-ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ readme.workspace = true
[features]
default = []
serialize = ["serde/derive", "dep:serde_with", "calyx-utils/serialize", "calyx-frontend/serialize", "smallvec/serde", "serde/rc"]
yxi = ["serde/derive"]

[dependencies]
log.workspace = true
Expand All @@ -26,4 +27,4 @@ serde_with = { workspace = true, optional = true }
smallvec.workspace = true

calyx-utils.workspace = true
calyx-frontend.workspace = true
calyx-frontend.workspace = true
87 changes: 56 additions & 31 deletions calyx-ir/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,49 @@
//! Helpers used to examine calyx programs. Used in Xilinx and Yxi backends among others.
use super::{BoolAttr, Cell, Component, RRC};
// Returns Vec<String> of memory names
pub fn external_memories_names(comp: &Component) -> Vec<String> {
external_memories_cells(comp)
#[cfg(feature = "yxi")]
use serde::Serialize;

// Returns Vec<String> of `@external` or `ref` memory names
pub fn external_and_ref_memories_names(comp: &Component) -> Vec<String> {
external_and_ref_memories_cells(comp)
.iter()
.map(|cell_ref| cell_ref.borrow().name().to_string())
.collect()
}

// Gets all memory cells in top level marked external.
pub fn external_memories_cells(comp: &Component) -> Vec<RRC<Cell>> {
/// Gets all memory cells in top level marked `@external` or `ref`.
pub fn external_and_ref_memories_cells(comp: &Component) -> Vec<RRC<Cell>> {
comp.cells
.iter()
// find external memories
// find external and ref memories
.filter(|cell_ref| {
let cell = cell_ref.borrow();
cell.attributes.has(BoolAttr::External)
cell.attributes.has(BoolAttr::External) || cell.is_reference()
})
.cloned()
.collect()
}

#[cfg_attr(feature = "yxi", derive(Serialize))]
#[derive(Clone, Copy)]
pub enum MemoryType {
Combinational,
Sequential,
}

/// Parameters for std memories
pub struct MemInfo {
pub width: u64,
pub size: u64,
pub memory_type: MemoryType,
pub data_width: u64,
pub dimensions: u64,
//dimension sizes in order: d1, d2, etc.
pub dimension_sizes: Vec<u64>,
pub total_size: u64,
//idx port width, in case size is ambiguous
pub idx_sizes: Vec<u64>,
}

// Returns a vector of tuples containing external memory info of [comp] of form:
// Returns a vector of tuples containing memory info of [comp] of form:
// [(WIDTH, SIZE, IDX_SIZE)]
pub trait GetMemInfo {
fn get_mem_info(&self) -> Vec<MemInfo>;
Expand All @@ -40,46 +54,57 @@ impl GetMemInfo for Vec<RRC<Cell>> {
self.iter()
.map(|cr| {
let mem = cr.borrow();
let mem_size: u64;
let mut dimension_sizes: Vec<u64> = Vec::new();
let mut idx_sizes: Vec<u64> = Vec::new();
let idx_count: u64;
let dimensions: u64;
//let mem_cell_type = mem.prototype.get_name().unwrap(); //i.e. "comb_mem_d1"
let mem_type : MemoryType = if mem.is_comb_cell() {
MemoryType::Combinational
} else {
MemoryType::Sequential
};

match mem.prototype.get_name().unwrap().as_ref() {
"comb_mem_d1" | "seq_mem_d1" => {
mem_size = mem.get_parameter("SIZE").unwrap();
idx_count = 1;
dimension_sizes.push(mem.get_parameter("SIZE").unwrap());
dimensions = 1;
}
"comb_mem_d2" | "seq_mem_d2" => {
mem_size = mem.get_parameter("D0_SIZE").unwrap()
* mem.get_parameter("D1_SIZE").unwrap();
idx_count = 2;
dimension_sizes.push(mem.get_parameter("D0_SIZE").unwrap());
dimension_sizes.push(mem.get_parameter("D1_SIZE").unwrap());
dimensions = 2;
}
"comb_mem_d3" | "seq_mem_d3" => {
mem_size = mem.get_parameter("D0_SIZE").unwrap()
* mem.get_parameter("D1_SIZE").unwrap()
* mem.get_parameter("D2_SIZE").unwrap();
idx_count = 3;
dimension_sizes.push(mem.get_parameter("D0_SIZE").unwrap());
dimension_sizes.push(mem.get_parameter("D1_SIZE").unwrap());
dimension_sizes.push(mem.get_parameter("D2_SIZE").unwrap());
dimensions = 3;
}
"comb_mem_d4" | "seq_mem_d4" => {
mem_size = mem.get_parameter("D0_SIZE").unwrap()
* mem.get_parameter("D1_SIZE").unwrap()
* mem.get_parameter("D2_SIZE").unwrap()
* mem.get_parameter("D3_SIZE").unwrap();
idx_count = 4;
dimension_sizes.push(mem.get_parameter("D0_SIZE").unwrap());
dimension_sizes.push(mem.get_parameter("D1_SIZE").unwrap());
dimension_sizes.push(mem.get_parameter("D2_SIZE").unwrap());
dimension_sizes.push(mem.get_parameter("D3_SIZE").unwrap());
dimensions = 4;
}
_ => {
panic!("cell `{}' marked with `@external' but is not a memory primitive.", mem.name())
}
};
if idx_count == 1 {
if dimensions == 1 {
idx_sizes.push(mem.get_parameter("IDX_SIZE").unwrap());
} else {
for i in 1..idx_count {
for i in 0..dimensions {
idx_sizes.push(mem.get_parameter(format!("D{}_IDX_SIZE",i)).unwrap());
}
}
let total_size = dimension_sizes.clone().iter().product();
MemInfo {
width: mem.get_parameter("WIDTH").unwrap(),
size: mem_size,
memory_type: mem_type,
data_width: mem.get_parameter("WIDTH").unwrap(),
dimensions,
dimension_sizes,
total_size,
idx_sizes
}
})
Expand All @@ -89,6 +114,6 @@ impl GetMemInfo for Vec<RRC<Cell>> {

impl GetMemInfo for Component {
fn get_mem_info(&self) -> Vec<MemInfo> {
external_memories_cells(self).get_mem_info()
external_and_ref_memories_cells(self).get_mem_info()
}
}
5 changes: 4 additions & 1 deletion src/cmdline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
use argh::FromArgs;
#[cfg(feature = "serialize")]
use calyx_backend::SexpBackend;
#[cfg(feature = "yxi")]
use calyx_backend::YxiBackend;
use calyx_backend::{
xilinx::{XilinxInterfaceBackend, XilinxXmlBackend},
Backend, BackendOpt, FirrtlBackend, MlirBackend, PrimitiveUsesBackend,
ResourcesBackend, VerilogBackend, YxiBackend,
ResourcesBackend, VerilogBackend,
};
use calyx_ir as ir;
use calyx_utils::{CalyxResult, Error, OutputFile};
Expand Down Expand Up @@ -167,6 +169,7 @@ impl Opts {
let backend = XilinxXmlBackend;
backend.run(context, self.output)
}
#[cfg(feature = "yxi")]
BackendOpt::Yxi => {
let backend = YxiBackend;
backend.run(context, self.output)
Expand Down
Loading

0 comments on commit 7a2a236

Please sign in to comment.