topstitch

Struct ModDef

Source
pub struct ModDef { /* private fields */ }
Expand description

Represents a module definition, like module <mod_def_name> ... endmodule in Verilog.

Implementations§

Source§

impl ModDef

Source

pub fn new(name: impl AsRef<str>) -> ModDef

Creates a new module definition with the given name.

Examples found in repository?
examples/demo.rs (line 23)
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
Hide additional examples
examples/features.rs (line 14)
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());
}
Source

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?
examples/stub.rs (line 28)
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());
}
Source

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?
examples/demo.rs (lines 13-18)
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());
}
Source

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?
examples/stub.rs (lines 13-18)
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());
}
Source

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.

Source

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.

Source

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?
examples/demo.rs (line 31)
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
Hide additional examples
examples/features.rs (line 20)
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());
}
Source

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?
examples/stub.rs (line 30)
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
Hide additional examples
examples/features.rs (line 35)
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());
}
Source

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.

Source

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.

Source

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.

Source

pub fn get_name(&self) -> String

Returns the name of this module definition.

Source

pub fn get_instances(&self) -> Vec<ModInst>

Returns a vector of all module instances within this module definition.

Source

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.

Source

pub fn set_usage(&self, usage: Usage)

Configures how this module definition should be used when validating and/or emitting Verilog.

Source

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?
examples/demo.rs (line 27)
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());
}
Source

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.

Source

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?
examples/stub.rs (line 43)
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
Hide additional examples
examples/demo.rs (line 56)
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());
}
examples/features.rs (line 123)
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());
}
Source

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.

Source

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].

Source

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?
examples/features.rs (line 59)
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());
}
Source

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.

Source

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.

Source

pub fn def_intf_from_regex( &self, name: impl AsRef<str>, search: impl AsRef<str>, replace: impl AsRef<str>, ) -> Intf

Source

pub fn def_intf_from_regexes( &self, name: impl AsRef<str>, regexes: &[(&str, &str)], ) -> Intf

Source

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?
examples/features.rs (line 65)
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());
}
Source

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?
examples/features.rs (line 49)
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());
}
Source

pub fn feedthrough_pipeline( &self, input_name: impl AsRef<str>, output_name: impl AsRef<str>, width: usize, pipeline: PipelineConfig, )

Source

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.

Source

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?
examples/stub.rs (line 23)
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
Hide additional examples
examples/demo.rs (line 19)
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());
}
Source

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§

Source§

impl Clone for ModDef

Source§

fn clone(&self) -> ModDef

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dst: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.