pub struct ModDef { /* private fields */ }Expand description
Represents a module definition, like module <mod_def_name> ... endmodule
in Verilog.
Implementations§
Source§impl ModDef
impl ModDef
Sourcepub fn new(name: impl AsRef<str>) -> ModDef
pub fn new(name: impl AsRef<str>) -> ModDef
Creates a new module definition with the given name.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
// Import the adder module definition from a Verilog file
let adder_8_bit = ModDef::from_verilog_file(
"adder",
&examples.join("input").join("adder.sv"),
true,
false,
);
let adder_9_bit = adder_8_bit.parameterize(&[("W", 9)], None, None);
// Create a top-level module definition
let top = ModDef::new("top");
// Instantiate adders in a tree
let i00 = top.instantiate(&adder_8_bit, Some("i00"), None);
let i01 = top.instantiate(&adder_8_bit, Some("i01"), None);
let i11 = top.instantiate(&adder_9_bit, Some("i11"), None);
let a = top.add_port("in0", i00.get_port("a").io());
let b = top.add_port("in1", i00.get_port("b").io());
let c = top.add_port("in2", i01.get_port("a").io());
let sum = top.add_port("sum", i11.get_port("sum").io());
// Wire together adders in a tree
a.connect(&i00.get_port("a"));
i00.get_port("b").connect(&b); // order doesn't matter
c.connect(&i01.get_port("a"));
i01.get_port("b").tieoff(42); // required because unconnected inputs are not allowed
i00.get_port("sum").connect(&i11.get_port("a"));
i01.get_port("sum").connect(&i11.get_port("b"));
// Connect the final adder output the top-level output
sum.connect(&i11.get_port("sum"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("top.sv");
top.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}More examples
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let block = ModDef::new("Block");
///////////////////////
// Basic connections //
///////////////////////
let a = block.add_port("a", Input(8));
let b = block.add_port("b", Input(64));
let c = block.add_port("c", Output(16));
block.add_port("d", Output(32)); // will show how this can be retrieved by name
let e = block.add_port("e", Input(1));
let f = block.add_port("f", Input(16));
let g = block.add_port("g", Output(8));
let h = block.add_port("h", Output(8));
// this would be an error due to width mismatch
// a.connect(&stub.get_port("c"));
a.connect(&c.slice(7, 0));
c.slice(15, 8).tieoff(0); // without this, will get an error, "Stub.c is not fully driven."
let d = block.get_port("d");
b.slice(31, 0).connect(&d);
b.slice(63, 32).unused(); // without this, will get an error, "Stub.b is not fully used."
e.unused(); // without this, will get an error, "Stub.e is not fully used."
let f_array = f.subdivide(2);
f_array[0].connect(&g);
f_array[1].connect(&h);
//////////////////
// Feedthroughs //
//////////////////
block.feedthrough("ft_in", "ft_out", 128);
////////////////
// Interfaces //
////////////////
// connect by matching function name
block.add_port("a_intf_data", Input(8));
block.add_port("a_intf_valid", Output(1));
let a_intf = block.def_intf_from_prefix("a_intf", "a_intf_");
block.add_port("b_intf_data", Output(8));
block.add_port("b_intf_valid", Input(1));
block.def_intf_from_prefix("b_intf", "b_intf_"); // will show how to retrieve this
a_intf.connect(&block.get_intf("b_intf"), false);
// connect by "crossover" (using regex connection)
block.add_port("c_intf_data_tx", Output(8));
block.add_port("c_intf_data_rx", Input(8));
block.add_port("c_intf_valid_tx", Output(1));
block.add_port("c_intf_valid_rx", Input(1));
let c_intf = block.def_intf_from_prefix("c_intf", "c_intf_");
block.add_port("d_intf_data_tx", Output(8));
block.add_port("d_intf_data_rx", Input(8));
block.add_port("d_intf_valid_tx", Output(1));
block.add_port("d_intf_valid_rx", Input(1));
let d_intf = block.def_intf_from_prefix("d_intf", "d_intf_");
c_intf.crossover(&d_intf, "^(.*)_tx$", "^(.*)_rx$");
// interface subdivision
block.add_port("e_intf_data_tx", Output(16));
block.add_port("e_intf_data_rx", Input(16));
block.add_port("e_intf_valid_tx", Output(2));
block.add_port("e_intf_valid_rx", Input(2));
let e_intf = block.def_intf_from_prefix("e_intf", "e_intf_");
block.add_port("f_intf_data_tx", Output(8));
block.add_port("f_intf_data_rx", Input(8));
block.add_port("f_intf_valid_tx", Output(1));
block.add_port("f_intf_valid_rx", Input(1));
let f_intf = block.def_intf_from_prefix("f_intf", "f_intf_");
block.add_port("g_intf_data_tx", Output(8));
block.add_port("g_intf_data_rx", Input(8));
block.add_port("g_intf_valid_tx", Output(1));
block.add_port("g_intf_valid_rx", Input(1));
let g_intf = block.def_intf_from_prefix("g_intf", "g_intf_");
let e_intf_array = e_intf.subdivide(2);
e_intf_array[0].crossover(&f_intf, "^(.*)_tx$", "^(.*)_rx$");
e_intf_array[1].crossover(&g_intf, "^(.*)_tx$", "^(.*)_rx$");
// marking interfaces as unused
block.add_port("h_intf_data_tx", Output(8));
block.add_port("h_intf_data_rx", Input(8));
block.add_port("h_intf_valid_tx", Output(1));
block.add_port("h_intf_valid_rx", Input(1));
let h_intf = block.def_intf_from_prefix("h_intf", "h_intf_");
h_intf.unused(); // marks all inputs as unused
h_intf.tieoff(0); // ties off all outputs to 0
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("block.sv");
block.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn stub(&self, name: impl AsRef<str>) -> ModDef
pub fn stub(&self, name: impl AsRef<str>) -> ModDef
Returns a new module definition with the given name, using the same ports and interfaces as the original module. The new module has no instantiations or internal connections.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let input = examples.join("input");
// Parse the Verilog sources. Note that multiple sources can be specified.
let block = ModDef::from_verilog_files(
"block",
&[&input.join("pack.sv"), &input.join("block.sv")],
false,
false,
);
// Parameterization is optional; it's shown here to illustrate the feature. The
// parameterize() function returns a new ModDef with the given parameter values.
// Unspecified parameters will use their default values.
let block_parameterized = block.parameterize(&[("N", 32)], None, None);
// Create a stub for the parameterized block. Try replacing
// "block_parameterized" with "block" - it will still work, using default
// parameter values.
let stub = block_parameterized.stub("stub");
let a = stub.get_port("a");
let b_array = stub.get_port("b").subdivide(2);
a.connect(&b_array[0]);
b_array[1].tieoff(0);
stub.get_port("c").connect(&stub.get_port("d"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("stub.sv");
stub.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn from_verilog_file(
name: impl AsRef<str>,
verilog: &Path,
ignore_unknown_modules: bool,
skip_unsupported: bool,
) -> Self
pub fn from_verilog_file( name: impl AsRef<str>, verilog: &Path, ignore_unknown_modules: bool, skip_unsupported: bool, ) -> Self
Creates a new module definition from a Verilog file. The name
parameter is the name of the module to extract from the Verilog file,
and verilog is the path to the Verilog file. If
ignore_unknown_modules is true, do not panic if the Verilog file
instantiates modules whose definitions cannot be found. This is often
useful because only the interface of module name needs to be
extracted; its contents do not need to be interpreted. If
skip_unsupported is true, do not panic if the interface of module
name contains unsupported features; simply skip these ports. This is
occasionally useful when prototyping.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
// Import the adder module definition from a Verilog file
let adder_8_bit = ModDef::from_verilog_file(
"adder",
&examples.join("input").join("adder.sv"),
true,
false,
);
let adder_9_bit = adder_8_bit.parameterize(&[("W", 9)], None, None);
// Create a top-level module definition
let top = ModDef::new("top");
// Instantiate adders in a tree
let i00 = top.instantiate(&adder_8_bit, Some("i00"), None);
let i01 = top.instantiate(&adder_8_bit, Some("i01"), None);
let i11 = top.instantiate(&adder_9_bit, Some("i11"), None);
let a = top.add_port("in0", i00.get_port("a").io());
let b = top.add_port("in1", i00.get_port("b").io());
let c = top.add_port("in2", i01.get_port("a").io());
let sum = top.add_port("sum", i11.get_port("sum").io());
// Wire together adders in a tree
a.connect(&i00.get_port("a"));
i00.get_port("b").connect(&b); // order doesn't matter
c.connect(&i01.get_port("a"));
i01.get_port("b").tieoff(42); // required because unconnected inputs are not allowed
i00.get_port("sum").connect(&i11.get_port("a"));
i01.get_port("sum").connect(&i11.get_port("b"));
// Connect the final adder output the top-level output
sum.connect(&i11.get_port("sum"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("top.sv");
top.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn from_verilog_files(
name: impl AsRef<str>,
verilog: &[&Path],
ignore_unknown_modules: bool,
skip_unsupported: bool,
) -> Self
pub fn from_verilog_files( name: impl AsRef<str>, verilog: &[&Path], ignore_unknown_modules: bool, skip_unsupported: bool, ) -> Self
Creates a new module definition from a list of Verilog files. The name
parameter is the name of the module to extract from the Verilog sources,
and verilog is an array of paths of Verilog sources. If
ignore_unknown_modules is true, do not panic if the Verilog file
instantiates modules whose definitions cannot be found. This is often
useful because only the interface of module name needs to be
extracted; its contents do not need to be interpreted. If
skip_unsupported is true, do not panic if the interface of module
name contains unsupported features; simply skip these ports. This is
occasionally useful when prototyping.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let input = examples.join("input");
// Parse the Verilog sources. Note that multiple sources can be specified.
let block = ModDef::from_verilog_files(
"block",
&[&input.join("pack.sv"), &input.join("block.sv")],
false,
false,
);
// Parameterization is optional; it's shown here to illustrate the feature. The
// parameterize() function returns a new ModDef with the given parameter values.
// Unspecified parameters will use their default values.
let block_parameterized = block.parameterize(&[("N", 32)], None, None);
// Create a stub for the parameterized block. Try replacing
// "block_parameterized" with "block" - it will still work, using default
// parameter values.
let stub = block_parameterized.stub("stub");
let a = stub.get_port("a");
let b_array = stub.get_port("b").subdivide(2);
a.connect(&b_array[0]);
b_array[1].tieoff(0);
stub.get_port("c").connect(&stub.get_port("d"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("stub.sv");
stub.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn from_verilog(
name: impl AsRef<str>,
verilog: impl AsRef<str>,
ignore_unknown_modules: bool,
skip_unsupported: bool,
) -> Self
pub fn from_verilog( name: impl AsRef<str>, verilog: impl AsRef<str>, ignore_unknown_modules: bool, skip_unsupported: bool, ) -> Self
Creates a new module definition from Verilog source code. The name
parameter is the name of the module to extract from the Verilog code,
and verilog is a string containing Verilog code. If
ignore_unknown_modules is true, do not panic if the Verilog file
instantiates modules whose definitions cannot be found. This is often
useful because only the interface of module name needs to be
extracted; its contents do not need to be interpreted. If
skip_unsupported is true, do not panic if the interface of module
name contains unsupported features; simply skip these ports. This is
occasionally useful when prototyping.
Sourcepub fn from_verilog_using_slang(
name: impl AsRef<str>,
cfg: &SlangConfig<'_>,
skip_unsupported: bool,
) -> Self
pub fn from_verilog_using_slang( name: impl AsRef<str>, cfg: &SlangConfig<'_>, skip_unsupported: bool, ) -> Self
Creates a new module definition from Verilog sources. The name
parameter is the name of the module to extract from Verilog code, and
cfg is a SlangConfig struct specifying source files, include
directories, etc. If skip_unsupported is true, do not panic if the
interface of module name contains unsupported features; simply skip
these ports. This is occasionally useful when prototyping.
Sourcepub fn add_port(&self, name: impl AsRef<str>, io: IO) -> Port
pub fn add_port(&self, name: impl AsRef<str>, io: IO) -> Port
Adds a port to the module definition with the given name. The direction
and width are specfied via the io parameter.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
// Import the adder module definition from a Verilog file
let adder_8_bit = ModDef::from_verilog_file(
"adder",
&examples.join("input").join("adder.sv"),
true,
false,
);
let adder_9_bit = adder_8_bit.parameterize(&[("W", 9)], None, None);
// Create a top-level module definition
let top = ModDef::new("top");
// Instantiate adders in a tree
let i00 = top.instantiate(&adder_8_bit, Some("i00"), None);
let i01 = top.instantiate(&adder_8_bit, Some("i01"), None);
let i11 = top.instantiate(&adder_9_bit, Some("i11"), None);
let a = top.add_port("in0", i00.get_port("a").io());
let b = top.add_port("in1", i00.get_port("b").io());
let c = top.add_port("in2", i01.get_port("a").io());
let sum = top.add_port("sum", i11.get_port("sum").io());
// Wire together adders in a tree
a.connect(&i00.get_port("a"));
i00.get_port("b").connect(&b); // order doesn't matter
c.connect(&i01.get_port("a"));
i01.get_port("b").tieoff(42); // required because unconnected inputs are not allowed
i00.get_port("sum").connect(&i11.get_port("a"));
i01.get_port("sum").connect(&i11.get_port("b"));
// Connect the final adder output the top-level output
sum.connect(&i11.get_port("sum"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("top.sv");
top.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}More examples
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let block = ModDef::new("Block");
///////////////////////
// Basic connections //
///////////////////////
let a = block.add_port("a", Input(8));
let b = block.add_port("b", Input(64));
let c = block.add_port("c", Output(16));
block.add_port("d", Output(32)); // will show how this can be retrieved by name
let e = block.add_port("e", Input(1));
let f = block.add_port("f", Input(16));
let g = block.add_port("g", Output(8));
let h = block.add_port("h", Output(8));
// this would be an error due to width mismatch
// a.connect(&stub.get_port("c"));
a.connect(&c.slice(7, 0));
c.slice(15, 8).tieoff(0); // without this, will get an error, "Stub.c is not fully driven."
let d = block.get_port("d");
b.slice(31, 0).connect(&d);
b.slice(63, 32).unused(); // without this, will get an error, "Stub.b is not fully used."
e.unused(); // without this, will get an error, "Stub.e is not fully used."
let f_array = f.subdivide(2);
f_array[0].connect(&g);
f_array[1].connect(&h);
//////////////////
// Feedthroughs //
//////////////////
block.feedthrough("ft_in", "ft_out", 128);
////////////////
// Interfaces //
////////////////
// connect by matching function name
block.add_port("a_intf_data", Input(8));
block.add_port("a_intf_valid", Output(1));
let a_intf = block.def_intf_from_prefix("a_intf", "a_intf_");
block.add_port("b_intf_data", Output(8));
block.add_port("b_intf_valid", Input(1));
block.def_intf_from_prefix("b_intf", "b_intf_"); // will show how to retrieve this
a_intf.connect(&block.get_intf("b_intf"), false);
// connect by "crossover" (using regex connection)
block.add_port("c_intf_data_tx", Output(8));
block.add_port("c_intf_data_rx", Input(8));
block.add_port("c_intf_valid_tx", Output(1));
block.add_port("c_intf_valid_rx", Input(1));
let c_intf = block.def_intf_from_prefix("c_intf", "c_intf_");
block.add_port("d_intf_data_tx", Output(8));
block.add_port("d_intf_data_rx", Input(8));
block.add_port("d_intf_valid_tx", Output(1));
block.add_port("d_intf_valid_rx", Input(1));
let d_intf = block.def_intf_from_prefix("d_intf", "d_intf_");
c_intf.crossover(&d_intf, "^(.*)_tx$", "^(.*)_rx$");
// interface subdivision
block.add_port("e_intf_data_tx", Output(16));
block.add_port("e_intf_data_rx", Input(16));
block.add_port("e_intf_valid_tx", Output(2));
block.add_port("e_intf_valid_rx", Input(2));
let e_intf = block.def_intf_from_prefix("e_intf", "e_intf_");
block.add_port("f_intf_data_tx", Output(8));
block.add_port("f_intf_data_rx", Input(8));
block.add_port("f_intf_valid_tx", Output(1));
block.add_port("f_intf_valid_rx", Input(1));
let f_intf = block.def_intf_from_prefix("f_intf", "f_intf_");
block.add_port("g_intf_data_tx", Output(8));
block.add_port("g_intf_data_rx", Input(8));
block.add_port("g_intf_valid_tx", Output(1));
block.add_port("g_intf_valid_rx", Input(1));
let g_intf = block.def_intf_from_prefix("g_intf", "g_intf_");
let e_intf_array = e_intf.subdivide(2);
e_intf_array[0].crossover(&f_intf, "^(.*)_tx$", "^(.*)_rx$");
e_intf_array[1].crossover(&g_intf, "^(.*)_tx$", "^(.*)_rx$");
// marking interfaces as unused
block.add_port("h_intf_data_tx", Output(8));
block.add_port("h_intf_data_rx", Input(8));
block.add_port("h_intf_valid_tx", Output(1));
block.add_port("h_intf_valid_rx", Input(1));
let h_intf = block.def_intf_from_prefix("h_intf", "h_intf_");
h_intf.unused(); // marks all inputs as unused
h_intf.tieoff(0); // ties off all outputs to 0
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("block.sv");
block.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn get_port(&self, name: impl AsRef<str>) -> Port
pub fn get_port(&self, name: impl AsRef<str>) -> Port
Returns the port on this module definition with the given name; panics if a port with that name does not exist.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let input = examples.join("input");
// Parse the Verilog sources. Note that multiple sources can be specified.
let block = ModDef::from_verilog_files(
"block",
&[&input.join("pack.sv"), &input.join("block.sv")],
false,
false,
);
// Parameterization is optional; it's shown here to illustrate the feature. The
// parameterize() function returns a new ModDef with the given parameter values.
// Unspecified parameters will use their default values.
let block_parameterized = block.parameterize(&[("N", 32)], None, None);
// Create a stub for the parameterized block. Try replacing
// "block_parameterized" with "block" - it will still work, using default
// parameter values.
let stub = block_parameterized.stub("stub");
let a = stub.get_port("a");
let b_array = stub.get_port("b").subdivide(2);
a.connect(&b_array[0]);
b_array[1].tieoff(0);
stub.get_port("c").connect(&stub.get_port("d"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("stub.sv");
stub.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}More examples
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let block = ModDef::new("Block");
///////////////////////
// Basic connections //
///////////////////////
let a = block.add_port("a", Input(8));
let b = block.add_port("b", Input(64));
let c = block.add_port("c", Output(16));
block.add_port("d", Output(32)); // will show how this can be retrieved by name
let e = block.add_port("e", Input(1));
let f = block.add_port("f", Input(16));
let g = block.add_port("g", Output(8));
let h = block.add_port("h", Output(8));
// this would be an error due to width mismatch
// a.connect(&stub.get_port("c"));
a.connect(&c.slice(7, 0));
c.slice(15, 8).tieoff(0); // without this, will get an error, "Stub.c is not fully driven."
let d = block.get_port("d");
b.slice(31, 0).connect(&d);
b.slice(63, 32).unused(); // without this, will get an error, "Stub.b is not fully used."
e.unused(); // without this, will get an error, "Stub.e is not fully used."
let f_array = f.subdivide(2);
f_array[0].connect(&g);
f_array[1].connect(&h);
//////////////////
// Feedthroughs //
//////////////////
block.feedthrough("ft_in", "ft_out", 128);
////////////////
// Interfaces //
////////////////
// connect by matching function name
block.add_port("a_intf_data", Input(8));
block.add_port("a_intf_valid", Output(1));
let a_intf = block.def_intf_from_prefix("a_intf", "a_intf_");
block.add_port("b_intf_data", Output(8));
block.add_port("b_intf_valid", Input(1));
block.def_intf_from_prefix("b_intf", "b_intf_"); // will show how to retrieve this
a_intf.connect(&block.get_intf("b_intf"), false);
// connect by "crossover" (using regex connection)
block.add_port("c_intf_data_tx", Output(8));
block.add_port("c_intf_data_rx", Input(8));
block.add_port("c_intf_valid_tx", Output(1));
block.add_port("c_intf_valid_rx", Input(1));
let c_intf = block.def_intf_from_prefix("c_intf", "c_intf_");
block.add_port("d_intf_data_tx", Output(8));
block.add_port("d_intf_data_rx", Input(8));
block.add_port("d_intf_valid_tx", Output(1));
block.add_port("d_intf_valid_rx", Input(1));
let d_intf = block.def_intf_from_prefix("d_intf", "d_intf_");
c_intf.crossover(&d_intf, "^(.*)_tx$", "^(.*)_rx$");
// interface subdivision
block.add_port("e_intf_data_tx", Output(16));
block.add_port("e_intf_data_rx", Input(16));
block.add_port("e_intf_valid_tx", Output(2));
block.add_port("e_intf_valid_rx", Input(2));
let e_intf = block.def_intf_from_prefix("e_intf", "e_intf_");
block.add_port("f_intf_data_tx", Output(8));
block.add_port("f_intf_data_rx", Input(8));
block.add_port("f_intf_valid_tx", Output(1));
block.add_port("f_intf_valid_rx", Input(1));
let f_intf = block.def_intf_from_prefix("f_intf", "f_intf_");
block.add_port("g_intf_data_tx", Output(8));
block.add_port("g_intf_data_rx", Input(8));
block.add_port("g_intf_valid_tx", Output(1));
block.add_port("g_intf_valid_rx", Input(1));
let g_intf = block.def_intf_from_prefix("g_intf", "g_intf_");
let e_intf_array = e_intf.subdivide(2);
e_intf_array[0].crossover(&f_intf, "^(.*)_tx$", "^(.*)_rx$");
e_intf_array[1].crossover(&g_intf, "^(.*)_tx$", "^(.*)_rx$");
// marking interfaces as unused
block.add_port("h_intf_data_tx", Output(8));
block.add_port("h_intf_data_rx", Input(8));
block.add_port("h_intf_valid_tx", Output(1));
block.add_port("h_intf_valid_rx", Input(1));
let h_intf = block.def_intf_from_prefix("h_intf", "h_intf_");
h_intf.unused(); // marks all inputs as unused
h_intf.tieoff(0); // ties off all outputs to 0
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("block.sv");
block.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn get_port_slice(
&self,
name: impl AsRef<str>,
msb: usize,
lsb: usize,
) -> PortSlice
pub fn get_port_slice( &self, name: impl AsRef<str>, msb: usize, lsb: usize, ) -> PortSlice
Returns a slice of the port on this module definition with the given
name, from msb down to lsb, inclusive; panics if a port with that
name does not exist.
Sourcepub fn get_ports(&self, prefix: Option<&str>) -> Vec<Port>
pub fn get_ports(&self, prefix: Option<&str>) -> Vec<Port>
Returns a vector of all ports on this module definition with the given
prefix. If prefix is None, returns all ports.
Sourcepub fn stub_recursive(&self, regex: impl AsRef<str>)
pub fn stub_recursive(&self, regex: impl AsRef<str>)
Walk through all instances within this module definition, marking those
whose names match the given regex with the usage
Usage::EmitStubAndStop. Repeat recursively for all instances whose
names do not match this regex.
Sourcepub fn get_instances(&self) -> Vec<ModInst>
pub fn get_instances(&self) -> Vec<ModInst>
Returns a vector of all module instances within this module definition.
Sourcepub fn get_instance(&self, name: impl AsRef<str>) -> ModInst
pub fn get_instance(&self, name: impl AsRef<str>) -> ModInst
Returns the module instance within this module definition with the given name; panics if an instance with that name does not exist.
Sourcepub fn set_usage(&self, usage: Usage)
pub fn set_usage(&self, usage: Usage)
Configures how this module definition should be used when validating and/or emitting Verilog.
Sourcepub fn instantiate(
&self,
moddef: &ModDef,
name: Option<&str>,
autoconnect: Option<&[&str]>,
) -> ModInst
pub fn instantiate( &self, moddef: &ModDef, name: Option<&str>, autoconnect: Option<&[&str]>, ) -> ModInst
Instantiate a module, using the provided instance name. autoconnect is
an optional list of port names to automatically connect between the
parent module and the instantiated module. This feature does not make
any connections between module instances.
As an example, suppose that the parent module has a port named clk and
the instantiated module has a port named clk. Passing
autoconnect=Some(&["clk"]) will automatically connect the two ports.
It will not automatically connect the clk port on this module
instance to the clk port on any other module instances.
It’s OK if some or all of the autoconnect names do not exist in
the parent module and/or instantiated module; TopStitch will not panic
in this case.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
// Import the adder module definition from a Verilog file
let adder_8_bit = ModDef::from_verilog_file(
"adder",
&examples.join("input").join("adder.sv"),
true,
false,
);
let adder_9_bit = adder_8_bit.parameterize(&[("W", 9)], None, None);
// Create a top-level module definition
let top = ModDef::new("top");
// Instantiate adders in a tree
let i00 = top.instantiate(&adder_8_bit, Some("i00"), None);
let i01 = top.instantiate(&adder_8_bit, Some("i01"), None);
let i11 = top.instantiate(&adder_9_bit, Some("i11"), None);
let a = top.add_port("in0", i00.get_port("a").io());
let b = top.add_port("in1", i00.get_port("b").io());
let c = top.add_port("in2", i01.get_port("a").io());
let sum = top.add_port("sum", i11.get_port("sum").io());
// Wire together adders in a tree
a.connect(&i00.get_port("a"));
i00.get_port("b").connect(&b); // order doesn't matter
c.connect(&i01.get_port("a"));
i01.get_port("b").tieoff(42); // required because unconnected inputs are not allowed
i00.get_port("sum").connect(&i11.get_port("a"));
i01.get_port("sum").connect(&i11.get_port("b"));
// Connect the final adder output the top-level output
sum.connect(&i11.get_port("sum"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("top.sv");
top.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn instantiate_array(
&self,
moddef: &ModDef,
dimensions: &[usize],
prefix: Option<&str>,
autoconnect: Option<&[&str]>,
) -> Vec<ModInst>
pub fn instantiate_array( &self, moddef: &ModDef, dimensions: &[usize], prefix: Option<&str>, autoconnect: Option<&[&str]>, ) -> Vec<ModInst>
Create one or more instances of a module, using the provided dimensions.
For example, if dimensions is &[3], TopStitch will create a 1D array
of 3 instances, called <mod_def_name>_i_0, <mod_def_name>_i_1,
<mod_def_name>_i_2. If dimensions is &[2, 3], TopStitch will
create a 2x3 array of instances, called <mod_def_name>_i_0_0,
<mod_def_name>_i_0_1, <mod_def_name>_i_0_2, <mod_def_name>_i_1_0,
etc. If provided, the optional prefix argument sets the prefix used in
naming instances to something other than <mod_def_name>_i_.
autoconnect has the same meaning as in instantiate(): if provided,
it is a list of port names to automatically connect between the parent
module and the instantiated module. For example, if the parent module
has a port named clk and the instantiated module has a port named
clk, passing Some(&["clk"]) will automatically connect the two
ports.
Sourcepub fn emit_to_file(&self, path: &Path, validate: bool)
pub fn emit_to_file(&self, path: &Path, validate: bool)
Writes Verilog code for this module definition to the given file path.
If validate is true, validate the module definition before emitting
Verilog.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let input = examples.join("input");
// Parse the Verilog sources. Note that multiple sources can be specified.
let block = ModDef::from_verilog_files(
"block",
&[&input.join("pack.sv"), &input.join("block.sv")],
false,
false,
);
// Parameterization is optional; it's shown here to illustrate the feature. The
// parameterize() function returns a new ModDef with the given parameter values.
// Unspecified parameters will use their default values.
let block_parameterized = block.parameterize(&[("N", 32)], None, None);
// Create a stub for the parameterized block. Try replacing
// "block_parameterized" with "block" - it will still work, using default
// parameter values.
let stub = block_parameterized.stub("stub");
let a = stub.get_port("a");
let b_array = stub.get_port("b").subdivide(2);
a.connect(&b_array[0]);
b_array[1].tieoff(0);
stub.get_port("c").connect(&stub.get_port("d"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("stub.sv");
stub.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}More examples
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
// Import the adder module definition from a Verilog file
let adder_8_bit = ModDef::from_verilog_file(
"adder",
&examples.join("input").join("adder.sv"),
true,
false,
);
let adder_9_bit = adder_8_bit.parameterize(&[("W", 9)], None, None);
// Create a top-level module definition
let top = ModDef::new("top");
// Instantiate adders in a tree
let i00 = top.instantiate(&adder_8_bit, Some("i00"), None);
let i01 = top.instantiate(&adder_8_bit, Some("i01"), None);
let i11 = top.instantiate(&adder_9_bit, Some("i11"), None);
let a = top.add_port("in0", i00.get_port("a").io());
let b = top.add_port("in1", i00.get_port("b").io());
let c = top.add_port("in2", i01.get_port("a").io());
let sum = top.add_port("sum", i11.get_port("sum").io());
// Wire together adders in a tree
a.connect(&i00.get_port("a"));
i00.get_port("b").connect(&b); // order doesn't matter
c.connect(&i01.get_port("a"));
i01.get_port("b").tieoff(42); // required because unconnected inputs are not allowed
i00.get_port("sum").connect(&i11.get_port("a"));
i01.get_port("sum").connect(&i11.get_port("b"));
// Connect the final adder output the top-level output
sum.connect(&i11.get_port("sum"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("top.sv");
top.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let block = ModDef::new("Block");
///////////////////////
// Basic connections //
///////////////////////
let a = block.add_port("a", Input(8));
let b = block.add_port("b", Input(64));
let c = block.add_port("c", Output(16));
block.add_port("d", Output(32)); // will show how this can be retrieved by name
let e = block.add_port("e", Input(1));
let f = block.add_port("f", Input(16));
let g = block.add_port("g", Output(8));
let h = block.add_port("h", Output(8));
// this would be an error due to width mismatch
// a.connect(&stub.get_port("c"));
a.connect(&c.slice(7, 0));
c.slice(15, 8).tieoff(0); // without this, will get an error, "Stub.c is not fully driven."
let d = block.get_port("d");
b.slice(31, 0).connect(&d);
b.slice(63, 32).unused(); // without this, will get an error, "Stub.b is not fully used."
e.unused(); // without this, will get an error, "Stub.e is not fully used."
let f_array = f.subdivide(2);
f_array[0].connect(&g);
f_array[1].connect(&h);
//////////////////
// Feedthroughs //
//////////////////
block.feedthrough("ft_in", "ft_out", 128);
////////////////
// Interfaces //
////////////////
// connect by matching function name
block.add_port("a_intf_data", Input(8));
block.add_port("a_intf_valid", Output(1));
let a_intf = block.def_intf_from_prefix("a_intf", "a_intf_");
block.add_port("b_intf_data", Output(8));
block.add_port("b_intf_valid", Input(1));
block.def_intf_from_prefix("b_intf", "b_intf_"); // will show how to retrieve this
a_intf.connect(&block.get_intf("b_intf"), false);
// connect by "crossover" (using regex connection)
block.add_port("c_intf_data_tx", Output(8));
block.add_port("c_intf_data_rx", Input(8));
block.add_port("c_intf_valid_tx", Output(1));
block.add_port("c_intf_valid_rx", Input(1));
let c_intf = block.def_intf_from_prefix("c_intf", "c_intf_");
block.add_port("d_intf_data_tx", Output(8));
block.add_port("d_intf_data_rx", Input(8));
block.add_port("d_intf_valid_tx", Output(1));
block.add_port("d_intf_valid_rx", Input(1));
let d_intf = block.def_intf_from_prefix("d_intf", "d_intf_");
c_intf.crossover(&d_intf, "^(.*)_tx$", "^(.*)_rx$");
// interface subdivision
block.add_port("e_intf_data_tx", Output(16));
block.add_port("e_intf_data_rx", Input(16));
block.add_port("e_intf_valid_tx", Output(2));
block.add_port("e_intf_valid_rx", Input(2));
let e_intf = block.def_intf_from_prefix("e_intf", "e_intf_");
block.add_port("f_intf_data_tx", Output(8));
block.add_port("f_intf_data_rx", Input(8));
block.add_port("f_intf_valid_tx", Output(1));
block.add_port("f_intf_valid_rx", Input(1));
let f_intf = block.def_intf_from_prefix("f_intf", "f_intf_");
block.add_port("g_intf_data_tx", Output(8));
block.add_port("g_intf_data_rx", Input(8));
block.add_port("g_intf_valid_tx", Output(1));
block.add_port("g_intf_valid_rx", Input(1));
let g_intf = block.def_intf_from_prefix("g_intf", "g_intf_");
let e_intf_array = e_intf.subdivide(2);
e_intf_array[0].crossover(&f_intf, "^(.*)_tx$", "^(.*)_rx$");
e_intf_array[1].crossover(&g_intf, "^(.*)_tx$", "^(.*)_rx$");
// marking interfaces as unused
block.add_port("h_intf_data_tx", Output(8));
block.add_port("h_intf_data_rx", Input(8));
block.add_port("h_intf_valid_tx", Output(1));
block.add_port("h_intf_valid_rx", Input(1));
let h_intf = block.def_intf_from_prefix("h_intf", "h_intf_");
h_intf.unused(); // marks all inputs as unused
h_intf.tieoff(0); // ties off all outputs to 0
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("block.sv");
block.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn emit(&self, validate: bool) -> String
pub fn emit(&self, validate: bool) -> String
Returns Verilog code for this module definition as a string. If
validate is true, validate the module definition before emitting
Verilog.
Sourcepub fn def_intf(
&self,
name: impl AsRef<str>,
mapping: IndexMap<String, (String, usize, usize)>,
) -> Intf
pub fn def_intf( &self, name: impl AsRef<str>, mapping: IndexMap<String, (String, usize, usize)>, ) -> Intf
Defines an interface with the given name. mapping is a map from
function names to tuples of (port_name, msb, lsb). For example, if
mapping is {"data": ("a_data", 3, 0), "valid": ("a_valid", 1, 1)},
this defines an interface with two functions, data and valid, where
the data function is provided by the port slice a_data[3:0] and the
valid function is provided by the port slice [1:1].
Sourcepub fn def_intf_from_prefix(
&self,
name: impl AsRef<str>,
prefix: impl AsRef<str>,
) -> Intf
pub fn def_intf_from_prefix( &self, name: impl AsRef<str>, prefix: impl AsRef<str>, ) -> Intf
Defines an interface with the given name, where the function names are
derived from the port names by stripping a common prefix. For example,
if the module has ports a_data, a_valid, b_data, and b_valid,
calling def_intf_from_prefix("a_intf", "a_") will define an interface
with functions data and valid, where data is provided by the full
port a_data and valid is provided by the full port a_valid.
Examples found in repository?
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let block = ModDef::new("Block");
///////////////////////
// Basic connections //
///////////////////////
let a = block.add_port("a", Input(8));
let b = block.add_port("b", Input(64));
let c = block.add_port("c", Output(16));
block.add_port("d", Output(32)); // will show how this can be retrieved by name
let e = block.add_port("e", Input(1));
let f = block.add_port("f", Input(16));
let g = block.add_port("g", Output(8));
let h = block.add_port("h", Output(8));
// this would be an error due to width mismatch
// a.connect(&stub.get_port("c"));
a.connect(&c.slice(7, 0));
c.slice(15, 8).tieoff(0); // without this, will get an error, "Stub.c is not fully driven."
let d = block.get_port("d");
b.slice(31, 0).connect(&d);
b.slice(63, 32).unused(); // without this, will get an error, "Stub.b is not fully used."
e.unused(); // without this, will get an error, "Stub.e is not fully used."
let f_array = f.subdivide(2);
f_array[0].connect(&g);
f_array[1].connect(&h);
//////////////////
// Feedthroughs //
//////////////////
block.feedthrough("ft_in", "ft_out", 128);
////////////////
// Interfaces //
////////////////
// connect by matching function name
block.add_port("a_intf_data", Input(8));
block.add_port("a_intf_valid", Output(1));
let a_intf = block.def_intf_from_prefix("a_intf", "a_intf_");
block.add_port("b_intf_data", Output(8));
block.add_port("b_intf_valid", Input(1));
block.def_intf_from_prefix("b_intf", "b_intf_"); // will show how to retrieve this
a_intf.connect(&block.get_intf("b_intf"), false);
// connect by "crossover" (using regex connection)
block.add_port("c_intf_data_tx", Output(8));
block.add_port("c_intf_data_rx", Input(8));
block.add_port("c_intf_valid_tx", Output(1));
block.add_port("c_intf_valid_rx", Input(1));
let c_intf = block.def_intf_from_prefix("c_intf", "c_intf_");
block.add_port("d_intf_data_tx", Output(8));
block.add_port("d_intf_data_rx", Input(8));
block.add_port("d_intf_valid_tx", Output(1));
block.add_port("d_intf_valid_rx", Input(1));
let d_intf = block.def_intf_from_prefix("d_intf", "d_intf_");
c_intf.crossover(&d_intf, "^(.*)_tx$", "^(.*)_rx$");
// interface subdivision
block.add_port("e_intf_data_tx", Output(16));
block.add_port("e_intf_data_rx", Input(16));
block.add_port("e_intf_valid_tx", Output(2));
block.add_port("e_intf_valid_rx", Input(2));
let e_intf = block.def_intf_from_prefix("e_intf", "e_intf_");
block.add_port("f_intf_data_tx", Output(8));
block.add_port("f_intf_data_rx", Input(8));
block.add_port("f_intf_valid_tx", Output(1));
block.add_port("f_intf_valid_rx", Input(1));
let f_intf = block.def_intf_from_prefix("f_intf", "f_intf_");
block.add_port("g_intf_data_tx", Output(8));
block.add_port("g_intf_data_rx", Input(8));
block.add_port("g_intf_valid_tx", Output(1));
block.add_port("g_intf_valid_rx", Input(1));
let g_intf = block.def_intf_from_prefix("g_intf", "g_intf_");
let e_intf_array = e_intf.subdivide(2);
e_intf_array[0].crossover(&f_intf, "^(.*)_tx$", "^(.*)_rx$");
e_intf_array[1].crossover(&g_intf, "^(.*)_tx$", "^(.*)_rx$");
// marking interfaces as unused
block.add_port("h_intf_data_tx", Output(8));
block.add_port("h_intf_data_rx", Input(8));
block.add_port("h_intf_valid_tx", Output(1));
block.add_port("h_intf_valid_rx", Input(1));
let h_intf = block.def_intf_from_prefix("h_intf", "h_intf_");
h_intf.unused(); // marks all inputs as unused
h_intf.tieoff(0); // ties off all outputs to 0
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("block.sv");
block.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn def_intf_from_name_underscore(&self, name: impl AsRef<str>) -> Intf
pub fn def_intf_from_name_underscore(&self, name: impl AsRef<str>) -> Intf
Defines an interface with the given name, where the function names are
derived from the port names by stripping the prefix <name>_. For
example, if the module has ports a_data, a_valid, b_data, and
b_valid, calling def_intf_from_prefix("a") will define an
interface with functions data and valid, where data is provided by
the full port a_data and valid is provided by the full port
a_valid.
Sourcepub fn def_intf_from_prefixes(
&self,
name: impl AsRef<str>,
prefixes: &[&str],
strip_prefix: bool,
) -> Intf
pub fn def_intf_from_prefixes( &self, name: impl AsRef<str>, prefixes: &[&str], strip_prefix: bool, ) -> Intf
Defines an interface with the given name, where the signals to be
included are identified by those that start with one of the provided
prefixies. Function names are either the signal names themselves (if
strip_prefix is false) or by stripping the prefix (if strip_prefix
is true). For example, if the module has ports a_data, a_valid,
b_data, and b_valid, calling def_intf_from_prefixes("intf", &["a_", "b_"], false) will define an interface with functions a_data,
a_valid, b_data, and b_valid, where each function is provided by
the corresponding port.
pub fn def_intf_from_regex( &self, name: impl AsRef<str>, search: impl AsRef<str>, replace: impl AsRef<str>, ) -> Intf
pub fn def_intf_from_regexes( &self, name: impl AsRef<str>, regexes: &[(&str, &str)], ) -> Intf
Sourcepub fn get_intf(&self, name: impl AsRef<str>) -> Intf
pub fn get_intf(&self, name: impl AsRef<str>) -> Intf
Returns the interface with the given name; panics if an interface with that name does not exist.
Examples found in repository?
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let block = ModDef::new("Block");
///////////////////////
// Basic connections //
///////////////////////
let a = block.add_port("a", Input(8));
let b = block.add_port("b", Input(64));
let c = block.add_port("c", Output(16));
block.add_port("d", Output(32)); // will show how this can be retrieved by name
let e = block.add_port("e", Input(1));
let f = block.add_port("f", Input(16));
let g = block.add_port("g", Output(8));
let h = block.add_port("h", Output(8));
// this would be an error due to width mismatch
// a.connect(&stub.get_port("c"));
a.connect(&c.slice(7, 0));
c.slice(15, 8).tieoff(0); // without this, will get an error, "Stub.c is not fully driven."
let d = block.get_port("d");
b.slice(31, 0).connect(&d);
b.slice(63, 32).unused(); // without this, will get an error, "Stub.b is not fully used."
e.unused(); // without this, will get an error, "Stub.e is not fully used."
let f_array = f.subdivide(2);
f_array[0].connect(&g);
f_array[1].connect(&h);
//////////////////
// Feedthroughs //
//////////////////
block.feedthrough("ft_in", "ft_out", 128);
////////////////
// Interfaces //
////////////////
// connect by matching function name
block.add_port("a_intf_data", Input(8));
block.add_port("a_intf_valid", Output(1));
let a_intf = block.def_intf_from_prefix("a_intf", "a_intf_");
block.add_port("b_intf_data", Output(8));
block.add_port("b_intf_valid", Input(1));
block.def_intf_from_prefix("b_intf", "b_intf_"); // will show how to retrieve this
a_intf.connect(&block.get_intf("b_intf"), false);
// connect by "crossover" (using regex connection)
block.add_port("c_intf_data_tx", Output(8));
block.add_port("c_intf_data_rx", Input(8));
block.add_port("c_intf_valid_tx", Output(1));
block.add_port("c_intf_valid_rx", Input(1));
let c_intf = block.def_intf_from_prefix("c_intf", "c_intf_");
block.add_port("d_intf_data_tx", Output(8));
block.add_port("d_intf_data_rx", Input(8));
block.add_port("d_intf_valid_tx", Output(1));
block.add_port("d_intf_valid_rx", Input(1));
let d_intf = block.def_intf_from_prefix("d_intf", "d_intf_");
c_intf.crossover(&d_intf, "^(.*)_tx$", "^(.*)_rx$");
// interface subdivision
block.add_port("e_intf_data_tx", Output(16));
block.add_port("e_intf_data_rx", Input(16));
block.add_port("e_intf_valid_tx", Output(2));
block.add_port("e_intf_valid_rx", Input(2));
let e_intf = block.def_intf_from_prefix("e_intf", "e_intf_");
block.add_port("f_intf_data_tx", Output(8));
block.add_port("f_intf_data_rx", Input(8));
block.add_port("f_intf_valid_tx", Output(1));
block.add_port("f_intf_valid_rx", Input(1));
let f_intf = block.def_intf_from_prefix("f_intf", "f_intf_");
block.add_port("g_intf_data_tx", Output(8));
block.add_port("g_intf_data_rx", Input(8));
block.add_port("g_intf_valid_tx", Output(1));
block.add_port("g_intf_valid_rx", Input(1));
let g_intf = block.def_intf_from_prefix("g_intf", "g_intf_");
let e_intf_array = e_intf.subdivide(2);
e_intf_array[0].crossover(&f_intf, "^(.*)_tx$", "^(.*)_rx$");
e_intf_array[1].crossover(&g_intf, "^(.*)_tx$", "^(.*)_rx$");
// marking interfaces as unused
block.add_port("h_intf_data_tx", Output(8));
block.add_port("h_intf_data_rx", Input(8));
block.add_port("h_intf_valid_tx", Output(1));
block.add_port("h_intf_valid_rx", Input(1));
let h_intf = block.def_intf_from_prefix("h_intf", "h_intf_");
h_intf.unused(); // marks all inputs as unused
h_intf.tieoff(0); // ties off all outputs to 0
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("block.sv");
block.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn feedthrough(
&self,
input_name: impl AsRef<str>,
output_name: impl AsRef<str>,
width: usize,
)
pub fn feedthrough( &self, input_name: impl AsRef<str>, output_name: impl AsRef<str>, width: usize, )
Punches a feedthrough through this module definition with the given
input and output names and width. This will create two new ports on the
module definition, input_name[width-1:0] and output_name[width-1:0],
and connect them together.
Examples found in repository?
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let block = ModDef::new("Block");
///////////////////////
// Basic connections //
///////////////////////
let a = block.add_port("a", Input(8));
let b = block.add_port("b", Input(64));
let c = block.add_port("c", Output(16));
block.add_port("d", Output(32)); // will show how this can be retrieved by name
let e = block.add_port("e", Input(1));
let f = block.add_port("f", Input(16));
let g = block.add_port("g", Output(8));
let h = block.add_port("h", Output(8));
// this would be an error due to width mismatch
// a.connect(&stub.get_port("c"));
a.connect(&c.slice(7, 0));
c.slice(15, 8).tieoff(0); // without this, will get an error, "Stub.c is not fully driven."
let d = block.get_port("d");
b.slice(31, 0).connect(&d);
b.slice(63, 32).unused(); // without this, will get an error, "Stub.b is not fully used."
e.unused(); // without this, will get an error, "Stub.e is not fully used."
let f_array = f.subdivide(2);
f_array[0].connect(&g);
f_array[1].connect(&h);
//////////////////
// Feedthroughs //
//////////////////
block.feedthrough("ft_in", "ft_out", 128);
////////////////
// Interfaces //
////////////////
// connect by matching function name
block.add_port("a_intf_data", Input(8));
block.add_port("a_intf_valid", Output(1));
let a_intf = block.def_intf_from_prefix("a_intf", "a_intf_");
block.add_port("b_intf_data", Output(8));
block.add_port("b_intf_valid", Input(1));
block.def_intf_from_prefix("b_intf", "b_intf_"); // will show how to retrieve this
a_intf.connect(&block.get_intf("b_intf"), false);
// connect by "crossover" (using regex connection)
block.add_port("c_intf_data_tx", Output(8));
block.add_port("c_intf_data_rx", Input(8));
block.add_port("c_intf_valid_tx", Output(1));
block.add_port("c_intf_valid_rx", Input(1));
let c_intf = block.def_intf_from_prefix("c_intf", "c_intf_");
block.add_port("d_intf_data_tx", Output(8));
block.add_port("d_intf_data_rx", Input(8));
block.add_port("d_intf_valid_tx", Output(1));
block.add_port("d_intf_valid_rx", Input(1));
let d_intf = block.def_intf_from_prefix("d_intf", "d_intf_");
c_intf.crossover(&d_intf, "^(.*)_tx$", "^(.*)_rx$");
// interface subdivision
block.add_port("e_intf_data_tx", Output(16));
block.add_port("e_intf_data_rx", Input(16));
block.add_port("e_intf_valid_tx", Output(2));
block.add_port("e_intf_valid_rx", Input(2));
let e_intf = block.def_intf_from_prefix("e_intf", "e_intf_");
block.add_port("f_intf_data_tx", Output(8));
block.add_port("f_intf_data_rx", Input(8));
block.add_port("f_intf_valid_tx", Output(1));
block.add_port("f_intf_valid_rx", Input(1));
let f_intf = block.def_intf_from_prefix("f_intf", "f_intf_");
block.add_port("g_intf_data_tx", Output(8));
block.add_port("g_intf_data_rx", Input(8));
block.add_port("g_intf_valid_tx", Output(1));
block.add_port("g_intf_valid_rx", Input(1));
let g_intf = block.def_intf_from_prefix("g_intf", "g_intf_");
let e_intf_array = e_intf.subdivide(2);
e_intf_array[0].crossover(&f_intf, "^(.*)_tx$", "^(.*)_rx$");
e_intf_array[1].crossover(&g_intf, "^(.*)_tx$", "^(.*)_rx$");
// marking interfaces as unused
block.add_port("h_intf_data_tx", Output(8));
block.add_port("h_intf_data_rx", Input(8));
block.add_port("h_intf_valid_tx", Output(1));
block.add_port("h_intf_valid_rx", Input(1));
let h_intf = block.def_intf_from_prefix("h_intf", "h_intf_");
h_intf.unused(); // marks all inputs as unused
h_intf.tieoff(0); // ties off all outputs to 0
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("block.sv");
block.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}pub fn feedthrough_pipeline( &self, input_name: impl AsRef<str>, output_name: impl AsRef<str>, width: usize, pipeline: PipelineConfig, )
Sourcepub fn wrap(&self, def_name: Option<&str>, inst_name: Option<&str>) -> ModDef
pub fn wrap(&self, def_name: Option<&str>, inst_name: Option<&str>) -> ModDef
Instantiates this module definition within a new module definition, and returns the new module definition. The new module definition has all of the same ports as the original module, which are connected directly to ports with the same names on the instance of the original module.
Sourcepub fn parameterize(
&self,
parameters: &[(&str, i32)],
def_name: Option<&str>,
inst_name: Option<&str>,
) -> ModDef
pub fn parameterize( &self, parameters: &[(&str, i32)], def_name: Option<&str>, inst_name: Option<&str>, ) -> ModDef
Returns a new module definition that is a variant of this module
definition, where the given parameters have been overridden from their
default values. For example, if the module definition has a parameter
WIDTH with a default value of 32, calling parameterize(&[("WIDTH", 64)]) will return a new module definition with the same ports and
instances, but with the parameter WIDTH set to 64. This is
implemented by creating a wrapper module that instantiates the original
module with the given parameters. The name of the wrapper module
defaults to
<original_mod_def_name>_<param_name_0>_<param_value_0>_<param_name_1>_<param_value_1>_. ..; this can be overridden via the optional def_name argument. The
instance name of the original module within the wrapper is
<original_mod_def_name>_i; this can be overridden via the optional
inst_name argument.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
let input = examples.join("input");
// Parse the Verilog sources. Note that multiple sources can be specified.
let block = ModDef::from_verilog_files(
"block",
&[&input.join("pack.sv"), &input.join("block.sv")],
false,
false,
);
// Parameterization is optional; it's shown here to illustrate the feature. The
// parameterize() function returns a new ModDef with the given parameter values.
// Unspecified parameters will use their default values.
let block_parameterized = block.parameterize(&[("N", 32)], None, None);
// Create a stub for the parameterized block. Try replacing
// "block_parameterized" with "block" - it will still work, using default
// parameter values.
let stub = block_parameterized.stub("stub");
let a = stub.get_port("a");
let b_array = stub.get_port("b").subdivide(2);
a.connect(&b_array[0]);
b_array[1].tieoff(0);
stub.get_port("c").connect(&stub.get_port("d"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("stub.sv");
stub.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}More examples
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
fn main() {
// Path to the "examples" folder
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
// Import the adder module definition from a Verilog file
let adder_8_bit = ModDef::from_verilog_file(
"adder",
&examples.join("input").join("adder.sv"),
true,
false,
);
let adder_9_bit = adder_8_bit.parameterize(&[("W", 9)], None, None);
// Create a top-level module definition
let top = ModDef::new("top");
// Instantiate adders in a tree
let i00 = top.instantiate(&adder_8_bit, Some("i00"), None);
let i01 = top.instantiate(&adder_8_bit, Some("i01"), None);
let i11 = top.instantiate(&adder_9_bit, Some("i11"), None);
let a = top.add_port("in0", i00.get_port("a").io());
let b = top.add_port("in1", i00.get_port("b").io());
let c = top.add_port("in2", i01.get_port("a").io());
let sum = top.add_port("sum", i11.get_port("sum").io());
// Wire together adders in a tree
a.connect(&i00.get_port("a"));
i00.get_port("b").connect(&b); // order doesn't matter
c.connect(&i01.get_port("a"));
i01.get_port("b").tieoff(42); // required because unconnected inputs are not allowed
i00.get_port("sum").connect(&i11.get_port("a"));
i01.get_port("sum").connect(&i11.get_port("b"));
// Connect the final adder output the top-level output
sum.connect(&i11.get_port("sum"));
// Emit the final Verilog code
let output_dir = examples.join("output");
std::fs::create_dir_all(&output_dir).expect("should be possible to create output dir");
let output_file = output_dir.join("top.sv");
top.emit_to_file(&output_file, true);
eprintln!("Emitted to output file: {}", output_file.display());
}Sourcepub fn validate(&self)
pub fn validate(&self)
Validates this module hierarchically; panics if any errors are found.
Validation primarily consists of checking that all inputs are driven
exactly once, and all outputs are used at least once, unless
specifically marked as unused. Validation behavior is controlled via the
usage setting. If this module has the usage EmitDefinitionAndDescend,
validation descends into each of those module definitions before
validating the module. If this module definition has a usage other than
EmitDefinitionAndDescend, it is not validated, and the modules it
instantiates are not validated.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for ModDef
impl !RefUnwindSafe for ModDef
impl !Send for ModDef
impl !Sync for ModDef
impl Unpin for ModDef
impl !UnwindSafe for ModDef
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more