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

Add Vivado XSim as a simulator #2363

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 39 additions & 20 deletions calyx-backend/src/verilog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ fn emit_component<F: io::Write>(
if let Some(check) =
emit_guard_disjoint_check(dst, asgns, &pool, true)
{
writeln!(f, "always_comb begin")?;
writeln!(f, "always_ff @(posedge clk) begin")?;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain this change? On the face of it, it is turning combinational logic into sequential logic and I'm not sure why I would expect this to be correct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah this has to do with the difference between cycle based simulators like verilator (and icarus) and time step based simulators (like xsim and modelsim). Cycle based simulators evaluate all assignments in a cycle first and then all asserts. Time step based simulators progress through assignments in continuous small time steps, meaning all values in the cycle may not be evaluated before the asserts are evaluated. At some point during the cycle while evaluations are occurring it might be the case that one one-hot value has updated but another hasn't (essentially like a propagation delay) which results in multiple values being high at the same time. Basically checking the asserts combinationally is too strict for a timing based simulator to be able to handle properly. This version should also behave nearly identically on cycle based simulators because the entire cycle is evaluated before asserts are.

writeln!(f, " {check}")?;
writeln!(f, "end")?;
}
Expand Down Expand Up @@ -763,16 +763,17 @@ fn emit_guard<F: std::io::Write>(
//==========================================
/// Generates code of the form:
/// ```
/// string DATA;
/// `define STRINGIFY(x) `"x`"
/// string data = `STRINGIFY(`DATA);
/// int CODE;
/// initial begin
/// CODE = $value$plusargs("DATA=%s", DATA);
/// $display("DATA: %s", DATA);
/// $readmemh({DATA, "/<mem_name>.dat"}, <mem_name>.mem);
/// CODE = $value$plusargs("data=%s", data);
/// $display("data: %s", data);
/// $readmemh($sformatf("%s/<mem_name>.dat", data), <mem_name>.mem);
/// ...
/// end
/// final begin
/// $writememh({DATA, "/<mem_name>.out"}, <mem_name>.mem);
/// $writememh($sformatf("%s/<mem_name>.out", data), <mem_name>.mem);
/// end
/// ```
fn memory_read_write(comp: &ir::Component) -> Vec<v::Stmt> {
Expand Down Expand Up @@ -805,14 +806,17 @@ fn memory_read_write(comp: &ir::Component) -> Vec<v::Stmt> {
}

// Import futil helper library.
let data_decl = v::Stmt::new_rawstr("string DATA;".to_string());
let stringify_decl =
v::Stmt::new_rawstr("`define STRINGIFY(x) `\"x`\"".to_string());
let data_decl =
v::Stmt::new_rawstr("string data = `STRINGIFY(`DATA);".to_string());
let code_decl = v::Stmt::new_rawstr("int CODE;".to_string());

let plus_args = v::Sequential::new_blk_assign(
v::Expr::Ref("CODE".to_string()),
v::Expr::new_call(
"$value$plusargs",
vec![v::Expr::new_str("DATA=%s"), v::Expr::new_ref("DATA")],
vec![v::Expr::new_str("data=%s"), v::Expr::new_ref("data")],
),
);

Expand All @@ -824,8 +828,8 @@ fn memory_read_write(comp: &ir::Component) -> Vec<v::Stmt> {
.add_seq(v::Sequential::new_seqexpr(v::Expr::new_call(
"$display",
vec![
v::Expr::new_str("DATA (path to meminit files): %s"),
v::Expr::new_ref("DATA"),
v::Expr::new_str("data (path to meminit files): %s"),
v::Expr::new_ref("data"),
],
)));

Expand All @@ -834,12 +838,19 @@ fn memory_read_write(comp: &ir::Component) -> Vec<v::Stmt> {
initial_block.add_seq(v::Sequential::new_seqexpr(v::Expr::new_call(
"$readmemh",
vec![
v::Expr::Concat(v::ExprConcat {
exprs: vec![
v::Expr::new_str(&format!("/{}.dat", name)),
v::Expr::new_ref("DATA"),
// v::Expr::Concat(v::ExprConcat {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason we're keeping this around as a comment?

// exprs: vec![
// v::Expr::new_str(&format!("/{}.dat", name)),
// v::Expr::new_ref("data"),
// ],
// })
v::Expr::new_call(
"$sformatf",
vec![
v::Expr::new_str(&format!("%s/{}.dat", name)),
v::Expr::new_ref("data"),
],
}),
),
v::Expr::new_ipath(&format!("{}.{}", name, mem_access_str)),
],
)));
Expand All @@ -852,18 +863,26 @@ fn memory_read_write(comp: &ir::Component) -> Vec<v::Stmt> {
final_block.add_seq(v::Sequential::new_seqexpr(v::Expr::new_call(
"$writememh",
vec![
v::Expr::Concat(v::ExprConcat {
exprs: vec![
v::Expr::new_str(&format!("/{}.out", name)),
v::Expr::new_ref("DATA"),
// v::Expr::Concat(v::ExprConcat {
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too

// exprs: vec![
// v::Expr::new_str(&format!("/{}.out", name)),
// v::Expr::new_ref("data"),
// ],
// }),
v::Expr::new_call(
"$sformatf",
vec![
v::Expr::new_str(&format!("%s/{}.out", name)),
v::Expr::new_ref("data"),
],
}),
),
v::Expr::new_ipath(&format!("{}.{}", name, mem_access_str)),
],
)));
});

vec![
stringify_decl,
data_decl,
code_decl,
v::Stmt::new_parallel(v::Parallel::new_process(initial_block)),
Expand Down
17 changes: 7 additions & 10 deletions fud2/rsrc/tb.sv
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ localparam RESET_CYCLES = 3;

// Cycle counter. Make this signed to catch errors with cycle simulation
// counts.
logic signed [63:0] cycle_count;
logic signed [63:0] cycle_count = 64'd0;

always_ff @(posedge clk) begin
cycle_count <= cycle_count + 1;
Expand All @@ -35,15 +35,15 @@ string OUT;
// Disable VCD tracing
int NOTRACE;
// Maximum number of cycles to simulate
longint CYCLE_LIMIT;
longint cycle_limit = `CYCLE_LIMIT;
// Dummy variable to track value returned by $value$plusargs
int CODE;

initial begin
CODE = $value$plusargs("OUT=%s", OUT);
CODE = $value$plusargs("CYCLE_LIMIT=%d", CYCLE_LIMIT);
if (CYCLE_LIMIT != 0) begin
$display("cycle limit set to %d", CYCLE_LIMIT);
CODE = $value$plusargs("cycle_limit=%d", cycle_limit);
if (cycle_limit != 0) begin
$display("cycle limit set to %d", cycle_limit);
end
CODE = $value$plusargs("NOTRACE=%d", NOTRACE);
if (NOTRACE == 0) begin
Expand All @@ -55,10 +55,7 @@ initial begin
end

// Initial values
go = 0;
clk = 0;
reset = 1;
cycle_count = 0;

forever begin
#10 clk = ~clk;
Expand All @@ -67,8 +64,8 @@ initial begin
// cycle.
$display("Simulated %d cycles", cycle_count - RESET_CYCLES - 1);
$finish;
end else if (cycle_count != 0 && cycle_count == CYCLE_LIMIT + RESET_CYCLES) begin
$display("reached limit of %d cycles", CYCLE_LIMIT);
end else if (cycle_count != 0 && cycle_count == cycle_limit + RESET_CYCLES) begin
$display("reached limit of %d cycles", cycle_limit);
$finish;
end
end
Expand Down
87 changes: 87 additions & 0 deletions fud2/rsrc/xsim.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
proc lshift listVar {
upvar 1 $listVar L
set r [lindex $L 0]
set L [lreplace $L [set L 0] 0]
return $r
}

#-------------------------------------------------------
# Process command line arguments
#-------------------------------------------------------
set error 0
set help 0
set cycle_limit 50000
set data ""
set verilog {}
set args $argv
# if {[llength $args] == 0} { incr help }; # Uncomment if necessary
while {[llength $args]} {
set flag [lshift args]
switch -exact -- $flag {
-i -
-ip-tcl {
set ip_script [lshift args]
}
-d -
-data {
set data [lshift args]
}
-c -
-cycle-limit {
set cycle_limit [lshift args]
}
-h -
-help {
incr help
}
default {
if {[string match "-*" $flag]} {
puts " ERROR - option '$flag' is not a valid option."
incr error
} else {
lappend verilog $flag
}
}
}
}

if {$help} {
set callerflag [lindex [info level [expr [info level] -1]] 0]
# <-- HELP
puts [format {
Usage: %s
[-ports|-p <listOfPorts>]
[-verbose|-v]
[-help|-h]

Description: xxxxxxxxxxxxxxxxxxx.
xxxxxxxxxxxxxxxxxxx.
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably provide a helpful description for this script here.


Example:
%s -port xxxxxxxxxxxxxxx

} $callerflag $callerflag ]
# HELP -->
return -code ok {}
}

# Check validity of arguments. Increment $error to generate an error

if {$error} {
return -code error {Oops, something is not correct}
}

set dir [pwd]

create_project -force prj1
add_files $verilog
if {[info exists ip_script]} {
source $ip_script
}
set_property top toplevel [get_fileset sim_1]
set_property -name {xsim.simulate.runtime} -value {all} -objects [get_filesets sim_1]
puts $cycle_limit
set_property verilog_define [subst {CYCLE_LIMIT=$cycle_limit DATA=$dir/$data}] [get_filesets sim_1]
launch_simulation
close_project
return -code ok {}
2 changes: 1 addition & 1 deletion fud2/scripts/rtl_sim.rhai
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn sim_setup(e) {
// Rule for simulation execution.
e.rule(
"sim-run",
"./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out",
"./$bin $args > $out",
);

// More shared configuration.
Expand Down
4 changes: 2 additions & 2 deletions fud2/scripts/verilator.rhai
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ fn verilator_setup(e) {
e.config_var_or("cycle-limit", "sim.cycle_limit", "500000000");
e.rule(
"verilator-compile-standalone-tb",
"$verilator $in tb.sv --trace --binary --top-module toplevel -fno-inline -Mdir $out-dir",
"$verilator $in tb.sv -DCYCLE_LIMIT=$cycle-limit -DDATA=$datadir --trace --binary --top-module toplevel -fno-inline -Mdir $out-dir",
);
e.rule(
"verilator-compile-custom-tb",
"$verilator $in tb.sv memories.sv --trace --binary --top-module toplevel -fno-inline -Mdir $out-dir",
"$verilator $in tb.sv memories.sv -DCYCLE_LIMIT=$cycle-limit -DDATA=$datadir --trace --binary --top-module toplevel -fno-inline -Mdir $out-dir",
);
e.rule("cp", "cp $in $out");
}
Expand Down
2 changes: 1 addition & 1 deletion fud2/scripts/xilinx.rhai
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn xilinx_setup(e) {
);
e.arg("pool", "console"); // Lets Ninja stream the tool output "live."

// Compile an `.xo` file to an `.xclbin` file, which is where the actual EDA work occurs.
// Compile an `.xo` ile to an `.xclbin` file, which is where the actual EDA work occurs.
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug

e.config_var_or("xilinx-mode", "xilinx.mode", "hw_emu");
e.config_var_or("platform", "xilinx.device", "xilinx_u50_gen3x16_xdma_201920_3");
e.rule(
Expand Down
85 changes: 85 additions & 0 deletions fud2/scripts/xsim.rhai
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import "rtl_sim" as sim;
import "testbench" as tb;
import "calyx" as c;

export const xsim_setup = xsim_setup;
fn xsim_setup(e) {
e.config_var("vivado-dir", "xilinx.vivado");
e.config_var_or("cycle-limit", "sim.cycle_limit", "500000000");
e.rsrc("xsim.tcl");
let has_tcl = !e.config_or("xsim.ip_tcl", "").is_empty();
if has_tcl {
let tcl_name = e.config_val("xsim.ip_tcl");
let tcl_path = e.external_path(tcl_name);
e.var_("ip_tcl", tcl_path);
print(tcl_path);
e.rule(
"xsim-standalone-tb",
"$vivado-dir/bin/vivado -mode batch -source xsim.tcl -tclargs -c $cycle-limit -d $datadir -i $ip_tcl tb.sv verilog.sv > $out",
);
e.rule(
"xsim-custom-tb",
"$vivado-dir/bin/vivado -mode batch -source xsim.tcl -tclargs -c $cycle-limit -d $datadir -i $ip_tcl tb.sv verilog.sv memories.sv > $out",
);
} else {
e.rule(
"xsim-standalone-tb",
"$vivado-dir/bin/vivado -mode batch -source xsim.tcl -tclargs -c $cycle-limit -d $datadir tb.sv verilog.sv > $out",
);
e.rule(
"xsim-custom-tb",
"$vivado-dir/bin/vivado -mode batch -source xsim.tcl -tclargs -c $cycle-limit -d $datadir tb.sv verilog.sv memories.sv > $out",
);
}
}

export const xsim_build = xsim_build;
fn xsim_build(e, input, output, datadir, standalone_tb) {
if standalone_tb {
e.build_cmd(
[output],
"xsim-standalone-tb",
[input],
["tb.sv", "xsim.tcl", datadir],
);
} else {
e.build_cmd(
[output],
"xsim-custom-tb",
[input],
["tb.sv", "memories.sv", "xsim.tcl", datadir],
);
}
}

op(
"xsim",
[sim::sim_setup, tb::standalone_setup, xsim_setup],
c::verilog_state,
sim::dat,
|e, input, output| {
xsim_build(e, input, "sim.log", "$datadir", true);
e.build_cmd(
[output],
"json-data",
["$datadir", "sim.log"],
["json-dat.py"],
);
},
);

op(
"xsim-refmem",
[sim::sim_setup, tb::custom_setup, xsim_setup],
tb::verilog_refmem,
sim::dat,
|e, input, output| {
xsim_build(e, input, "sim.log", "$datadir", false);
e.build_cmd(
[output],
"json-data",
["$datadir", "sim.log"],
["json-dat.py"],
);
}
);
Loading