topstitch

Struct PortSlice

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

Represents a slice of a port, which may be on a module definition or on a module instance.

A slice is a defined as a contiguous range of bits from msb down to lsb, inclusive. A slice can be a single bit on the port (msb equal to lsb), the entire port, or any range in between.

Implementations§

Source§

impl PortSlice

Source

pub fn subdivide(&self, n: usize) -> Vec<Self>

Divides a port slice into n parts of equal bit width, return a vector of n port slices. For example, if a port is 8 bits wide and n is 2, the port will be divided into 2 slices of 4 bits each: port[3:0] and port[7:4]. This method panics if the port width is not divisible by n.

Source

pub fn export_as(&self, name: impl AsRef<str>) -> Port

Create a new port called name on the parent module and connects it to this port slice.

The exact behavior depends on whether this is a port slice on a module definition or a module instance. If this is a port slice on a module definition, a new port is created on the same module definition, with the same width, but opposite direction. For example, suppose that this is a port slice a on a module definition that is an 8-bit input; calling export_as("y") will create an 8-bit output on the same module definition called y.

If, on the other hand, this is a port slice on a module instance, a new port will be created on the module definition containing the instance, with the same width and direction. For example, if this is an 8-bit input port x on a module instance, calling export_as("y") will create a new 8-bit input port y on the module definition that contains the instance.

Source

pub fn export(&self) -> Port

Same as export_as(), but the new port is created with the same name as the port being exported. As a result, this method can only be used with ports on module instances. The method will panic if called on a port slice on a module definition.

Source§

impl PortSlice

Source

pub fn connect<T: ConvertibleToPortSlice>(&self, other: &T)

Connects this port slice to another port or port slice. Performs some upfront checks to make sure that the connection is valid in terms of width and directionality. Panics if any of these checks fail.

Examples found in repository?
examples/features.rs (line 36)
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 connect_pipeline<T: ConvertibleToPortSlice>( &self, other: &T, pipeline: PipelineConfig, )

Source

pub fn tieoff<T: Into<BigInt>>(&self, value: T)

Ties off this port slice to the given constant value, specified as a BigInt or type that can be converted to a BigInt.

Examples found in repository?
examples/stub.rs (line 34)
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 33)
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 unused(&self)

Marks this port slice as unused, meaning that if it is an module instance output or module definition input, validation will not fail if the slice drives nothing. In fact, validation will fail if the slice drives anything.

Examples found in repository?
examples/features.rs (line 37)
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());
}

Trait Implementations§

Source§

impl Clone for PortSlice

Source§

fn clone(&self) -> PortSlice

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

impl ConvertibleToPortSlice for PortSlice

Source§

impl Debug for PortSlice

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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.