Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mem width fix #1096

Merged
merged 10 commits into from
Jul 14, 2022
55 changes: 39 additions & 16 deletions src/backend/xilinx/memory_axi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub trait MemoryInterface {
bus_data_width: u64,
bus_addr_width: u64,
data_width: u64,
memory_size: u64,
addr_width: u64,
) -> v::Module;
}

Expand Down Expand Up @@ -98,11 +100,11 @@ impl MemoryInterface for AxiInterface {
name: &str,
bus_data_width: u64,
bus_addr_width: u64,
// address_width: u64,
data_width: u64,
memory_size: u64,
addr_width: u64,
) -> v::Module {
let mut module = v::Module::new(name);
let memory_size = 32;
let memory_size_bits: u64 = utils::math::bits_needed_for(memory_size); // TODO make memory size parametric
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully memory_size_bits == addr_width… maybe adding an assert here to check that would be cool?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually slightly changed in #1103.
One thing that occurred to me on the subject is that because calyx-memory has the width of it's address port defined through an argument, there could be a (perhaps strange? Not sure how common this would be) case where there is a wider address port than needed for the size of a memory. In that case the two wouldn't be equal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that makes sense; thanks for clarifying! Yeah, it's technically true that the Calyx memory primitives could declare a larger address space than they actually need. It would be weird, but it would be a mistake to assert that it's impossible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mostly a consequence of not being able to specify things like ADDR0: $clog2(SIZE) in the parameters of a calyx primitive


module.add_input("ACLK", 1);
Expand All @@ -121,7 +123,7 @@ impl MemoryInterface for AxiInterface {
// BRAM interface
module.add_input("WRITE_DATA", data_width);
module.add_output("READ_DATA", data_width);
module.add_input("ADDR", memory_size_bits);
module.add_input("ADDR", addr_width);
module.add_input("WE", 1);
module.add_output("DONE", 1);

Expand Down Expand Up @@ -162,12 +164,21 @@ impl MemoryInterface for AxiInterface {
));

// bram reading / writing logic
bram_logic(&axi4, &mut module, &mode_fsm, "read_txn_count".into());
bram_logic(
name,
&axi4,
&mut module,
&mode_fsm,
"read_txn_count".into(),
data_width,
addr_width,
);
module.add_stmt(v::Parallel::Assign(
"READ_DATA".into(),
"bram_read_data".into(),
));

//TODO(nathanielnrn) change this to use addr_width? In `x_done` assignment fix
let offset_size_bits = memory_size_bits + 1;

// synchronise channels
Expand All @@ -189,6 +200,8 @@ impl MemoryInterface for AxiInterface {

// addresses are byte addressed which means addresses are computed as
// base + (offset << shift_by)
// TODO(nathanielnrn): Fix the burst size and shifting values based on
// pynq(?) input? Or memory size? unclear.
let shift_by = 2;
let burst_size: i32 = utils::math::bits_needed_for(32 / 8) as i32;

Expand Down Expand Up @@ -233,7 +246,7 @@ impl MemoryInterface for AxiInterface {
);

module.add_stmt(axi4.write_address.assign("ID", 0));
// assign shift to a wire to circumvent vast order of operations issues
// assign shift to a wire to circumvent `vast` order of operations issues
let send_shift = "send_shift";
module.add_decl(v::Decl::new_wire(send_shift, bus_addr_width));
let mut concat = v::ExprConcat::default();
Expand Down Expand Up @@ -293,18 +306,23 @@ fn module_mode_fsm(module: &mut v::Module) -> fsm::LinearFsm {
}

fn bram_logic(
name: &str, //assumed to be of form [Memory_controller_axi_<suffix>]
axi4: &AxiInterface,
module: &mut v::Module,
mode_fsm: &fsm::LinearFsm,
txn_count: v::Expr,
data_width: u64,
addr_width: u64,
) {
module.add_decl(v::Decl::new_wire("bram_addr", 5));
module.add_decl(v::Decl::new_wire("bram_write_data", 32));
module.add_decl(v::Decl::new_wire("bram_addr", addr_width));
module.add_decl(v::Decl::new_wire("bram_write_data", data_width));
module.add_decl(v::Decl::new_wire("bram_we", 1));
module.add_decl(v::Decl::new_wire("bram_read_data", 32));
module.add_decl(v::Decl::new_wire("bram_read_data", data_width));
module.add_decl(v::Decl::new_wire("bram_done", 1));

let mut ram_instance = v::Instance::new("bram", "SINGLE_PORT_BRAM");
let suffix_idx = "Memory_controller_axi_".len();
let suffix = &name[suffix_idx..];
Comment on lines +322 to +323
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big deal, but it could be ever so slightly nicer to take suffix in specifically as a parameter?

let mut ram_instance =
v::Instance::new("bram", &format!("SINGLE_PORT_BRAM_{}", suffix));
ram_instance.connect_ref("ACLK", "ACLK");
ram_instance.connect_ref("ADDR", "bram_addr");
ram_instance.connect_ref("Din", "bram_write_data");
Expand All @@ -315,9 +333,9 @@ fn bram_logic(
module.add_stmt(v::Parallel::Assign("DONE".into(), "bram_done".into()));

// bram address logic
let copy_address = v::Expr::new_slice("copy_addr_offset", 4, 0);
let copy_address = v::Expr::new_slice("copy_addr_offset", 4, 0); // XXX(nathanielnrn) does this need to use parameterization?
let bram_address: v::Expr = "ADDR".into();
let send_address = v::Expr::new_slice("send_addr_offset", 4, 0);
let send_address = v::Expr::new_slice("send_addr_offset", 4, 0); // same here
let mux_address = v::Expr::new_mux(
v::Expr::new_logical_and(
axi4.read_data.handshake(),
Expand Down Expand Up @@ -349,7 +367,7 @@ fn bram_logic(
let copy_data: v::Expr = v::Expr::new_index_slice(
&axi4.read_data.get("DATA"),
v::Expr::new_mul(txn_count, 32),
32, /* bram data width */
data_width as u32, // bram data width
);
let bram_data: v::Expr = "WRITE_DATA".into();
let mux_data = v::Expr::new_mux(
Expand Down Expand Up @@ -387,8 +405,13 @@ fn incr_addr(
module.add_stmt(always);
}

pub fn bram(data_width: u64, size: u64, addr_width: u64) -> v::Module {
let mut module = v::Module::new("SINGLE_PORT_BRAM");
pub fn bram(
name: &str,
data_width: u64,
size: u64,
addr_width: u64,
) -> v::Module {
let mut module = v::Module::new(name);
module.add_input("ACLK", 1);
module.add_input("ADDR", addr_width);
module.add_input("Din", data_width);
Expand Down Expand Up @@ -420,7 +443,7 @@ pub fn bram(data_width: u64, size: u64, addr_width: u64) -> v::Module {
"Dout".into(),
v::Expr::new_index_expr("ram_core", "ADDR"),
));
//add a simple assign <String1> = <String2>
//add a simple assign Done = done_reg
module.add_stmt(v::Parallel::Assign("Done".into(), "done_reg".into()));
module
}
58 changes: 50 additions & 8 deletions src/backend/xilinx/toplevel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,37 @@ impl Backend for XilinxInterfaceBackend {
" Please make sure that at least one memory is marked as @external."));
}

let mem_info = get_mem_info(toplevel);

let mut modules = vec![
// XXX(nathanielnrn) what defines top_level address_width and data_width?
top_level(12, 32, &memories),
bram(32, 32, 5),
axi::AxiInterface::control_module("Control_axi", 12, 32, &memories),
];
for (i, _mem) in memories.iter().enumerate() {
modules.push(bram(
&format!("SINGLE_PORT_BRAM_{}", i),
mem_info[i].0,
mem_info[i].1,
mem_info[i].2,
))
}

modules.push(axi::AxiInterface::control_module(
"Control_axi",
// XXX(nathanielnrn) these match numbers above, unclear where they're from
12,
32,
&memories,
));

for (i, _mem) in memories.iter().enumerate() {
modules.push(axi::AxiInterface::memory_module(
&format!("Memory_controller_axi_{}", i),
512,
64,
32,
mem_info[i].0,
mem_info[i].1,
mem_info[i].2,
))
}

Expand All @@ -79,13 +98,36 @@ impl Backend for XilinxInterfaceBackend {
}
}

fn external_memories(comp: &ir::Component) -> Vec<String> {
// find external memories
fn external_memories_cells(
comp: &ir::Component,
) -> Vec<calyx::ir::RRC<ir::Cell>> {
comp.cells
.iter()
.filter(|cell_ref| {
matches!(cell_ref.borrow().get_attribute("external"), Some(&1))
// find external memories
.filter(|cell_ref| cell_ref.borrow().attributes.has("external"))
.cloned()
.collect()
}

// Returns a vector of tuples containing external memory info of form:
// (WIDTH, SIZE, IDX_SIZE)
fn get_mem_info(comp: &ir::Component) -> Vec<(u64, u64, u64)> {
external_memories_cells(comp)
.iter()
.map(|cell_ref| {
(
cell_ref.borrow().get_parameter("WIDTH").unwrap(),
cell_ref.borrow().get_parameter("SIZE").unwrap(),
cell_ref.borrow().get_parameter("IDX_SIZE").unwrap(),
)
})
.collect()
}

// Returns Vec<String> of memory names
fn external_memories(comp: &ir::Component) -> Vec<String> {
external_memories_cells(comp)
.iter()
.map(|cell_ref| cell_ref.borrow().name().to_string())
.collect()
}
Expand Down Expand Up @@ -171,7 +213,7 @@ fn top_level(
let done = format!("{}_done", mem);
module.add_decl(v::Decl::new_wire(&write_data, data_width));
module.add_decl(v::Decl::new_wire(&read_data, data_width));
module.add_decl(v::Decl::new_wire(&addr0, 5));
module.add_decl(v::Decl::new_wire(&addr0, address_width));
module.add_decl(v::Decl::new_wire(&write_en, 1));
module.add_decl(v::Decl::new_wire(&done, 1));

Expand Down