Skip to content

Commit

Permalink
Add bypass_reg and skid_buffer primitives to core (#2324)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewb1999 authored Nov 1, 2024
1 parent 1f31905 commit c455733
Show file tree
Hide file tree
Showing 14 changed files with 615 additions and 8 deletions.
51 changes: 48 additions & 3 deletions docs/libraries/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ such as registers and basic bitwise operations.

---

## Numerical Operators
## State Elements

### `std_reg<WIDTH>`

Expand All @@ -28,11 +28,56 @@ A `WIDTH`-wide register.
**Outputs:**

- `out: WIDTH` - The value contained in the register.
- `done: 1` - The register's done signal. Set high for one cycle after writing a
new value.
- `done: 1` - The register's done signal. Set high for one cycle after writing
a new value.

---

### `std_skid_buffer<WIDTH>`

A `WIDTH`-wide non-pipelined skid buffer. Used to ensure data is not lost
during handshakes.

**Inputs:**

- `in: WIDTH` - An input value to the skid buffer `WIDTH`-bits.
- `i_valid: 1` - The one bit input valid signal. Indicates that the data
provided on the `in` wire is valid.
- `i_ready: 1` - The one bit input ready signal. Indicates that the follower is
ready to recieve data from the `out` wire.

**Outputs:**

- `out: WIDTH` - The value contained in the register.
- `o_valid: 1` - The one bit output valid signal. Indicates that the data
provided on the `out` wire is valid.
- `o_ready: 1` - The one bit output ready signal. Indicates that the skid buffer
is ready to recieve data on the `in` wire.

---

### `std_bypass_reg<WIDTH>`

A `WIDTH`-wide bypass register.

**Inputs:**

- `in: WIDTH` - An input value to the bypass register `WIDTH`-bits.
- `write_en: 1` - The one bit write enabled signal. Indicates that the bypass
register should store the value on the `in` wire.

**Outputs:**

- `out: WIDTH` - The value of the bypass register. When `write_en` is asserted
the value of `in` is bypassed to `out`. Otherwise `out` is equal to the last
value written to the register.
- `done: 1` - The bypass register's done signal. Set high for one cycle after
`write_en` is asserted.

---

## Numerical Operators

### `std_const<WIDTH,VAL>`

A constant WIDTH-bit value with value VAL.
Expand Down
26 changes: 25 additions & 1 deletion primitives/core.futil
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ extern "core.sv" {
comb primitive std_bit_slice<"share"=1>[IN_WIDTH, START_IDX, END_IDX, OUT_WIDTH](@data in: IN_WIDTH) -> (out: OUT_WIDTH);


/// Logical operators
/// Logical Operators
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);
Expand All @@ -24,4 +24,28 @@ extern "core.sv" {
comb primitive std_le<"share"=1>[WIDTH](@data left: WIDTH, @data right: WIDTH) -> (out: 1);
comb primitive std_rsh<"share"=1>[WIDTH](@data left: WIDTH, @data right: WIDTH) -> (out: WIDTH);
comb primitive std_mux<"share"=1>[WIDTH](@data cond: 1, @data tru: WIDTH, @data fal: WIDTH) -> (out: WIDTH);

// Skid Buffers
primitive std_skid_buffer<"share"=1>[WIDTH](
@data in: WIDTH,
i_valid : 1,
i_ready : 1,
@clk clk: 1,
@reset reset: 1
) -> (
@stable out: WIDTH,
o_valid : 1,
o_ready : 1
);

// Bypass Register
primitive std_bypass_reg<"share"=1>[WIDTH](
@data in: WIDTH,
@go write_en: 1,
@clk clk: 1,
@reset reset: 1
) -> (
@stable out: WIDTH,
@done done: 1
);
}
70 changes: 69 additions & 1 deletion primitives/core.sv
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ module std_bit_slice #(
input wire logic [IN_WIDTH-1:0] in,
output logic [OUT_WIDTH-1:0] out
);
assign out = in[END_IDX:START_IDX];
assign out = in[END_IDX:START_IDX];

`ifdef VERILATOR
always_comb begin
Expand All @@ -230,3 +230,71 @@ module std_bit_slice #(
`endif

endmodule

module std_skid_buffer #(
parameter WIDTH = 32
)(
input wire logic [WIDTH-1:0] in,
input wire logic i_valid,
input wire logic i_ready,
input wire logic clk,
input wire logic reset,
output logic [WIDTH-1:0] out,
output logic o_valid,
output logic o_ready
);
logic [WIDTH-1:0] val;
logic bypass_rg;
always @(posedge clk) begin
// Reset
if (reset) begin
// Internal Registers
val <= '0;
bypass_rg <= 1'b1;
end
// Out of reset
else begin
// Bypass state
if (bypass_rg) begin
if (!i_ready && i_valid) begin
val <= in; // Data skid happened, store to buffer
bypass_rg <= 1'b0; // To skid mode
end
end
// Skid state
else begin
if (i_ready) begin
bypass_rg <= 1'b1; // Back to bypass mode
end
end
end
end

assign o_ready = bypass_rg;
assign out = bypass_rg ? in : val;
assign o_valid = bypass_rg ? i_valid : 1'b1;
endmodule

module std_bypass_reg #(
parameter WIDTH = 32
)(
input wire logic [WIDTH-1:0] in,
input wire logic write_en,
input wire logic clk,
input wire logic reset,
output logic [WIDTH-1:0] out,
output logic done
);
logic [WIDTH-1:0] val;
assign out = write_en ? in : val;

always_ff @(posedge clk) begin
if (reset) begin
val <= 0;
done <= 0;
end else if (write_en) begin
val <= in;
done <= 1'd1;
end else done <= 1'd0;
end
endmodule
1 change: 1 addition & 0 deletions runt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ name = "correctness static timing"
paths = [
"tests/correctness/*.futil",
"tests/correctness/ref-cells/*.futil",
"tests/correctness/skid-buffers/*.futil",
"tests/correctness/static-interface/*.futil",
]
cmd = """
Expand Down
70 changes: 69 additions & 1 deletion tests/backend/verilog/memory-with-external-attribute.expect
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ module std_bit_slice #(
input wire logic [IN_WIDTH-1:0] in,
output logic [OUT_WIDTH-1:0] out
);
assign out = in[END_IDX:START_IDX];
assign out = in[END_IDX:START_IDX];

`ifdef VERILATOR
always_comb begin
Expand All @@ -469,6 +469,74 @@ module std_bit_slice #(

endmodule

module std_skid_buffer #(
parameter WIDTH = 32
)(
input wire logic [WIDTH-1:0] in,
input wire logic i_valid,
input wire logic i_ready,
input wire logic clk,
input wire logic reset,
output logic [WIDTH-1:0] out,
output logic o_valid,
output logic o_ready
);
logic [WIDTH-1:0] val;
logic bypass_rg;
always @(posedge clk) begin
// Reset
if (reset) begin
// Internal Registers
val <= '0;
bypass_rg <= 1'b1;
end
// Out of reset
else begin
// Bypass state
if (bypass_rg) begin
if (!i_ready && i_valid) begin
val <= in; // Data skid happened, store to buffer
bypass_rg <= 1'b0; // To skid mode
end
end
// Skid state
else begin
if (i_ready) begin
bypass_rg <= 1'b1; // Back to bypass mode
end
end
end
end

assign o_ready = bypass_rg;
assign out = bypass_rg ? in : val;
assign o_valid = bypass_rg ? i_valid : 1'b1;
endmodule

module std_bypass_reg #(
parameter WIDTH = 32
)(
input wire logic [WIDTH-1:0] in,
input wire logic write_en,
input wire logic clk,
input wire logic reset,
output logic [WIDTH-1:0] out,
output logic done
);
logic [WIDTH-1:0] val;
assign out = write_en ? in : val;

always_ff @(posedge clk) begin
if (reset) begin
val <= 0;
done <= 0;
end else if (write_en) begin
val <= in;
done <= 1'd1;
end else done <= 1'd0;
end
endmodule

module undef #(
parameter WIDTH = 32
) (
Expand Down
26 changes: 26 additions & 0 deletions tests/correctness/skid-buffers/bypass-reg.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"in": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
],
"out": [
1,
2,
3,
4,
5,
5,
5,
8,
9,
10
]
}
64 changes: 64 additions & 0 deletions tests/correctness/skid-buffers/bypass-reg.futil
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import "primitives/core.futil";
import "primitives/memories/seq.futil";

component main() -> () {
cells {
@external in = seq_mem_d1(32, 10, 4);
b = std_bypass_reg(32);
i_reg = std_reg(4);
add0 = std_add(4);
neq0 = std_neq(4);
neq1 = std_neq(4);
and0 = std_and(1);
@external out = seq_mem_d1(32, 10, 4);
}

wires {
static<1> group init {
i_reg.write_en = 1'b1;
i_reg.in = 4'b0;
}

static<1> group incr {
i_reg.write_en = 1'b1;
i_reg.in = add0.out;
add0.left = i_reg.out;
add0.right = 4'b1;
}

static<1> group load {
in.addr0 = i_reg.out;
in.content_en = 1'b1;
}

static<1> group bypass {
neq0.left = i_reg.out;
neq0.right = 4'd5;
neq1.left = i_reg.out;
neq1.right = 4'd6;
and0.left = neq0.out;
and0.right = neq1.out;
b.write_en = and0.out;
b.in = in.read_data;
}

static<1> group store {
out.write_en = 1'b1;
out.content_en = 1'b1;
out.addr0 = i_reg.out;
out.write_data = b.out;
}
}

control {
init;
static repeat 10 {
static seq {
load;
bypass;
store;
incr;
}
}
}
}
Loading

0 comments on commit c455733

Please sign in to comment.