diff --git a/docs/libraries/core.md b/docs/libraries/core.md index 235492f233..7f6ef94947 100644 --- a/docs/libraries/core.md +++ b/docs/libraries/core.md @@ -119,7 +119,19 @@ Slice out the lower OUT_WIDTH bits of an IN_WIDTH-bit value. Computes - `out: OUT_WIDTH` - The lower OUT_WIDTH bits of `in` --- +### `std_bit_slice` +Extract the bit-string starting at `START_IDX` and ending at `END_IDX - 1` from `in`. +This is computed as `in[END_IDX:START_IDX]`.`OUT_WIDTH` must be specified to +be `END_WIDTH - START_WITH` wide when instantiating the module. + +**Inputs:** +- `in: IN_WIDTH` - An IN_WIDTH-bit value + +**Outputs:** + +- `out: OUT_WIDTH` - The value of the bit-string `in[START_IDX:END_IDX]` +--- ### `std_pad` Given an IN_WIDTH-bit input, zero pad from the left to an output of diff --git a/interp/src/primitives/combinational.rs b/interp/src/primitives/combinational.rs index ce246fef0f..ecc907d88d 100644 --- a/interp/src/primitives/combinational.rs +++ b/interp/src/primitives/combinational.rs @@ -538,6 +538,10 @@ comb_primitive!(StdPad[IN_WIDTH, OUT_WIDTH](r#in: IN_WIDTH) -> (out: OUT_WIDTH) Ok(r#in.ext(OUT_WIDTH as usize)) }); +comb_primitive!(StdBitSlice[IN_WIDTH, START_IDX, END_IDX, OUT_WIDTH](r#in: IN_WIDTH) -> (out: OUT_WIDTH) { + Ok(r#in.slice(END_IDX as usize, START_IDX as usize)) +}); + // ===================== Unsynthesizeable Operations ====================== comb_primitive!(StdUnsynMult[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH) { Ok(Value::from(left.as_unsigned() * right.as_unsigned(), WIDTH)) diff --git a/interp/src/structures/environment.rs b/interp/src/structures/environment.rs index c3526566b2..85d6ee2174 100644 --- a/interp/src/structures/environment.rs +++ b/interp/src/structures/environment.rs @@ -251,6 +251,9 @@ impl InterpreterState { Box::new(combinational::StdFpSlt::new(params, cell_qin)) } // Resizing ops + "std_bit_slice" => { + Box::new(combinational::StdBitSlice::new(params, cell_qin)) + } "std_slice" => { Box::new(combinational::StdSlice::new(params, cell_qin)) } diff --git a/interp/src/structures/values.rs b/interp/src/structures/values.rs index a66e552c1f..eddfb463c4 100644 --- a/interp/src/structures/values.rs +++ b/interp/src/structures/values.rs @@ -639,7 +639,7 @@ impl Value { } /// Returns a value containing the sliced region \[lower,upper\] - pub fn slice(self, upper_idx: usize, lower_idx: usize) -> Self { + pub fn slice(&self, upper_idx: usize, lower_idx: usize) -> Self { assert!(upper_idx >= lower_idx); assert!(upper_idx < self.vec.len()); diff --git a/primitives/core.futil b/primitives/core.futil index 7965cc5e9e..2f48fca680 100644 --- a/primitives/core.futil +++ b/primitives/core.futil @@ -5,6 +5,8 @@ extern "core.sv" { comb primitive std_slice<"share"=1>[IN_WIDTH, OUT_WIDTH](@data in: IN_WIDTH) -> (out: OUT_WIDTH); comb primitive std_pad<"share"=1>[IN_WIDTH, OUT_WIDTH](@data in: IN_WIDTH) -> (out: OUT_WIDTH); comb primitive std_cat<"share"=1>[LEFT_WIDTH, RIGHT_WIDTH, OUT_WIDTH](@data left: LEFT_WIDTH, @data right: RIGHT_WIDTH) -> (out: OUT_WIDTH); + comb primitive std_bit_slice<"share"=1>[IN_WIDTH, START_IDX, END_IDX, OUT_WIDTH](@data in: IN_WIDTH) -> (out: OUT_WIDTH); + /// Logical operators comb primitive std_not<"share"=1>[WIDTH](@data in: WIDTH) -> (out: WIDTH); diff --git a/primitives/core.sv b/primitives/core.sv index 2080574918..261571622f 100644 --- a/primitives/core.sv +++ b/primitives/core.sv @@ -217,4 +217,27 @@ module std_mux #( assign out = cond ? tru : fal; endmodule -`default_nettype wire +module std_bit_slice #( + parameter IN_WIDTH = 32, + parameter START_IDX = 0, + parameter END_IDX = 31, + parameter OUT_WIDTH = 32 +)( + input wire logic [IN_WIDTH-1:0] in, + output logic [OUT_WIDTH-1:0] out +); + assign out = in[END_IDX:START_IDX]; + + `ifdef VERILATOR + always_comb begin + if (START_IDX < 0 || END_IDX > IN_WIDTH-1) + $error( + "std_bit_slice: Slice range out of bounds\n", + "IN_WIDTH: %0d", IN_WIDTH, + "START_IDX: %0d", START_IDX, + "END_IDX: %0d", END_IDX, + ); + end + `endif + +endmodule diff --git a/tests/backend/verilog/memory-with-external-attribute.expect b/tests/backend/verilog/memory-with-external-attribute.expect index b428e9e236..4c08fe3ba9 100644 --- a/tests/backend/verilog/memory-with-external-attribute.expect +++ b/tests/backend/verilog/memory-with-external-attribute.expect @@ -455,7 +455,30 @@ module std_mux #( assign out = cond ? tru : fal; endmodule -`default_nettype wire +module std_bit_slice #( + parameter IN_WIDTH = 32, + parameter START_IDX = 0, + parameter END_IDX = 31, + parameter OUT_WIDTH = 32 +)( + input wire logic [IN_WIDTH-1:0] in, + output logic [OUT_WIDTH-1:0] out +); + assign out = in[END_IDX:START_IDX]; + + `ifdef VERILATOR + always_comb begin + if (START_IDX < 0 || END_IDX > IN_WIDTH-1) + $error( + "std_bit_slice: Slice range out of bounds\n", + "IN_WIDTH: %0d", IN_WIDTH, + "START_IDX: %0d", START_IDX, + "END_IDX: %0d", END_IDX, + ); + end + `endif + +endmodule module undef #( parameter WIDTH = 32 diff --git a/tests/correctness/std-bit-slice.expect b/tests/correctness/std-bit-slice.expect new file mode 100644 index 0000000000..5e2718b09d --- /dev/null +++ b/tests/correctness/std-bit-slice.expect @@ -0,0 +1,5 @@ +{ + "mem": [ + 9 + ] +} diff --git a/tests/correctness/std-bit-slice.futil b/tests/correctness/std-bit-slice.futil new file mode 100644 index 0000000000..3c78cb6e9c --- /dev/null +++ b/tests/correctness/std-bit-slice.futil @@ -0,0 +1,28 @@ +import "primitives/core.futil"; +import "primitives/memories/comb.futil"; + +component main(@go go: 1) -> (@done done: 1) { + cells { + @external mem = comb_mem_d1(32, 1, 1); + slice = std_bit_slice(32, 10, 14, 4); + pad = std_pad(4, 32); + } + + wires { + group bit_slice { + slice.in = 32'b00000000000000110010010110110000; + + pad.in = slice.out; + + mem.addr0 = 1'b0; + mem.write_data = pad.out; + mem.write_en = 1'b1; + + bit_slice[done] = mem.done; + } + } + + control { + bit_slice; + } +} \ No newline at end of file diff --git a/tests/correctness/std-bit-slice.futil.data b/tests/correctness/std-bit-slice.futil.data new file mode 100644 index 0000000000..012ed5a3f5 --- /dev/null +++ b/tests/correctness/std-bit-slice.futil.data @@ -0,0 +1,10 @@ +{ + "mem": { + "data": [0], + "format": { + "numeric_type": "bitnum", + "is_signed": false, + "width": 32 + } + } +} diff --git a/tests/import/a.expect b/tests/import/a.expect index ea78479398..93e1abff6d 100644 --- a/tests/import/a.expect +++ b/tests/import/a.expect @@ -14,6 +14,7 @@ extern "/calyx/primitives/core.sv" { comb primitive std_slice<"share"=1>[IN_WIDTH, OUT_WIDTH](@data in: IN_WIDTH) -> (out: OUT_WIDTH); comb primitive std_pad<"share"=1>[IN_WIDTH, OUT_WIDTH](@data in: IN_WIDTH) -> (out: OUT_WIDTH); comb primitive std_cat<"share"=1>[LEFT_WIDTH, RIGHT_WIDTH, OUT_WIDTH](@data left: LEFT_WIDTH, @data right: RIGHT_WIDTH) -> (out: OUT_WIDTH); + comb primitive std_bit_slice<"share"=1>[IN_WIDTH, START_IDX, END_IDX, OUT_WIDTH](@data in: IN_WIDTH) -> (out: OUT_WIDTH); comb primitive std_not<"share"=1>[WIDTH](@data in: WIDTH) -> (out: WIDTH); comb primitive std_and<"share"=1>[WIDTH](@data left: WIDTH, @data right: WIDTH) -> (out: WIDTH); comb primitive std_or<"share"=1>[WIDTH](@data left: WIDTH, @data right: WIDTH) -> (out: WIDTH);