-
Notifications
You must be signed in to change notification settings - Fork 53
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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")?; | ||
writeln!(f, " {check}")?; | ||
writeln!(f, "end")?; | ||
} | ||
|
@@ -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> { | ||
|
@@ -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")], | ||
), | ||
); | ||
|
||
|
@@ -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"), | ||
], | ||
))); | ||
|
||
|
@@ -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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)), | ||
], | ||
))); | ||
|
@@ -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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)), | ||
|
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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( | ||
|
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"], | ||
); | ||
} | ||
); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.