-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* first pass at adding dynamic memories * add dyn-mems to primitve library * add note that dyn.sv implementation is a short term solution * Update `yxi` to support `dyn_mem`s (#2112) * add yxi support for dyn-mems in calyx-ir-utils * make clippy happy * add yxi test for dyn-mems * formatting
- Loading branch information
1 parent
a1d99c2
commit bb73c16
Showing
6 changed files
with
518 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Single-ported memories with dynamic read and write latencies. | ||
extern "dyn.sv" { | ||
primitive dyn_mem_d1[WIDTH, SIZE, IDX_SIZE]( | ||
@clk clk: 1, | ||
@reset reset: 1, | ||
@write_together(1) @data addr0: IDX_SIZE, | ||
@write_together(1) @go(1) content_en: 1, | ||
// Write ports | ||
@write_together(2) write_en: 1, | ||
@write_together(2) @data write_data: WIDTH | ||
) -> ( | ||
@stable read_data: WIDTH, | ||
@done(1) done: 1 | ||
); | ||
|
||
primitive dyn_mem_d2[WIDTH, D0_SIZE, D1_SIZE, D0_IDX_SIZE, D1_IDX_SIZE]( | ||
@clk clk: 1, | ||
@reset reset: 1, | ||
@write_together(1) @data addr0: D0_IDX_SIZE, | ||
@write_together(1) @data addr1: D1_IDX_SIZE, | ||
@write_together(1) @go(1) content_en: 1, | ||
// Write ports | ||
@write_together(2) write_en: 1, | ||
@write_together(2) @data write_data: WIDTH | ||
) -> ( | ||
@stable read_data: WIDTH, | ||
@done(1) done: 1 | ||
); | ||
|
||
primitive dyn_mem_d3[WIDTH, D0_SIZE, D1_SIZE, D2_SIZE, D0_IDX_SIZE, D1_IDX_SIZE, D2_IDX_SIZE]( | ||
@clk clk: 1, | ||
@reset reset: 1, | ||
@write_together(1) @data addr0: D0_IDX_SIZE, | ||
@write_together(1) @data addr1: D1_IDX_SIZE, | ||
@write_together(1) @data addr2: D2_IDX_SIZE, | ||
@write_together(1) @go(1) content_en: 1, | ||
// Write ports | ||
@write_together(2) write_en: 1, | ||
@write_together(2) @data write_data: WIDTH | ||
) -> ( | ||
@stable read_data: WIDTH, | ||
@done(1) done: 1 | ||
); | ||
|
||
primitive dyn_mem_d4[WIDTH, D0_SIZE, D1_SIZE, D2_SIZE, D3_SIZE, D0_IDX_SIZE, D1_IDX_SIZE, D2_IDX_SIZE, D3_IDX_SIZE]( | ||
@clk clk: 1, | ||
@reset reset: 1, | ||
@write_together(1) @data addr0: D0_IDX_SIZE, | ||
@write_together(1) @data addr1: D1_IDX_SIZE, | ||
@write_together(1) @data addr2: D2_IDX_SIZE, | ||
@write_together(1) @data addr3: D3_IDX_SIZE, | ||
@write_together(1) @go(1) content_en: 1, | ||
// Write ports | ||
@write_together(2) write_en: 1, | ||
@write_together(2) @data write_data: WIDTH | ||
) -> ( | ||
@stable read_data: WIDTH, | ||
@done(1) done: 1 | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
/** | ||
Implements a memory with sequential reads and writes. | ||
- Both reads and writes are not guaranteed to have a given latency. | ||
- Attempting to read and write at the same time is an error. | ||
- The out signal is registered to the last value requested by the read_en signal. | ||
- The out signal is undefined once write_en is asserted. | ||
NOTE(nate): In practice we expect this implementation to be single cycle, | ||
but should not be relied on as such. | ||
In particular we probably eventually want to have `dyn_mems` exist as "virtual operators." | ||
Which have a flexible latency, where the compiler can decide upon actual latency. | ||
See #2111 (PR introducing this: https://github.com/calyxir/calyx/pull/2111) | ||
and a more in depth discussion #1151 (https://github.com/calyxir/calyx/issues/1151) | ||
*/ | ||
module dyn_mem_d1 #( | ||
parameter WIDTH = 32, | ||
parameter SIZE = 16, | ||
parameter IDX_SIZE = 4 | ||
) ( | ||
// Common signals | ||
input wire logic clk, | ||
input wire logic reset, | ||
input wire logic [IDX_SIZE-1:0] addr0, | ||
input wire logic content_en, | ||
output logic done, | ||
|
||
// Read signal | ||
output logic [ WIDTH-1:0] read_data, | ||
|
||
// Write signals | ||
input wire logic [ WIDTH-1:0] write_data, | ||
input wire logic write_en | ||
); | ||
// Internal memory | ||
logic [WIDTH-1:0] mem[SIZE-1:0]; | ||
|
||
// Register for the read output | ||
logic [WIDTH-1:0] read_out; | ||
assign read_data = read_out; | ||
|
||
// Read value from the memory | ||
always_ff @(posedge clk) begin | ||
if (reset) begin | ||
read_out <= '0; | ||
end else if (content_en && !write_en) begin | ||
/* verilator lint_off WIDTH */ | ||
read_out <= mem[addr0]; | ||
end else if (content_en && write_en) begin | ||
// Explicitly clobber the read output when a write is performed | ||
read_out <= 'x; | ||
end else begin | ||
read_out <= read_out; | ||
end | ||
end | ||
|
||
// Propagate the done signal | ||
always_ff @(posedge clk) begin | ||
if (reset) begin | ||
done <= '0; | ||
end else if (content_en) begin | ||
done <= '1; | ||
end else begin | ||
done <= '0; | ||
end | ||
end | ||
|
||
// Write value to the memory | ||
always_ff @(posedge clk) begin | ||
if (!reset && content_en && write_en) | ||
mem[addr0] <= write_data; | ||
end | ||
|
||
// Check for out of bounds access | ||
`ifdef VERILATOR | ||
always_comb begin | ||
if (content_en && !write_en) | ||
if (addr0 >= SIZE) | ||
$error( | ||
"comb_mem_d1: Out of bounds access\n", | ||
"addr0: %0d\n", addr0, | ||
"SIZE: %0d", SIZE | ||
); | ||
end | ||
`endif | ||
endmodule | ||
|
||
module dyn_mem_d2 #( | ||
parameter WIDTH = 32, | ||
parameter D0_SIZE = 16, | ||
parameter D1_SIZE = 16, | ||
parameter D0_IDX_SIZE = 4, | ||
parameter D1_IDX_SIZE = 4 | ||
) ( | ||
// Common signals | ||
input wire logic clk, | ||
input wire logic reset, | ||
input wire logic [D0_IDX_SIZE-1:0] addr0, | ||
input wire logic [D1_IDX_SIZE-1:0] addr1, | ||
input wire logic content_en, | ||
output logic done, | ||
|
||
// Read signal | ||
output logic [WIDTH-1:0] read_data, | ||
|
||
// Write signals | ||
input wire logic write_en, | ||
input wire logic [ WIDTH-1:0] write_data | ||
); | ||
wire [D0_IDX_SIZE+D1_IDX_SIZE-1:0] addr; | ||
assign addr = addr0 * D1_SIZE + addr1; | ||
|
||
dyn_mem_d1 #(.WIDTH(WIDTH), .SIZE(D0_SIZE * D1_SIZE), .IDX_SIZE(D0_IDX_SIZE+D1_IDX_SIZE)) mem | ||
(.clk(clk), .reset(reset), .addr0(addr), | ||
.content_en(content_en), .read_data(read_data), .write_data(write_data), .write_en(write_en), | ||
.done(done)); | ||
endmodule | ||
|
||
module dyn_mem_d3 #( | ||
parameter WIDTH = 32, | ||
parameter D0_SIZE = 16, | ||
parameter D1_SIZE = 16, | ||
parameter D2_SIZE = 16, | ||
parameter D0_IDX_SIZE = 4, | ||
parameter D1_IDX_SIZE = 4, | ||
parameter D2_IDX_SIZE = 4 | ||
) ( | ||
// Common signals | ||
input wire logic clk, | ||
input wire logic reset, | ||
input wire logic [D0_IDX_SIZE-1:0] addr0, | ||
input wire logic [D1_IDX_SIZE-1:0] addr1, | ||
input wire logic [D2_IDX_SIZE-1:0] addr2, | ||
input wire logic content_en, | ||
output logic done, | ||
|
||
// Read signal | ||
output logic [WIDTH-1:0] read_data, | ||
|
||
// Write signals | ||
input wire logic write_en, | ||
input wire logic [ WIDTH-1:0] write_data | ||
); | ||
wire [D0_IDX_SIZE+D1_IDX_SIZE+D2_IDX_SIZE-1:0] addr; | ||
assign addr = addr0 * (D1_SIZE * D2_SIZE) + addr1 * (D2_SIZE) + addr2; | ||
|
||
dyn_mem_d1 #(.WIDTH(WIDTH), .SIZE(D0_SIZE * D1_SIZE * D2_SIZE), .IDX_SIZE(D0_IDX_SIZE+D1_IDX_SIZE+D2_IDX_SIZE)) mem | ||
(.clk(clk), .reset(reset), .addr0(addr), | ||
.content_en(content_en), .read_data(read_data), .write_data(write_data), .write_en(write_en), | ||
.done(done)); | ||
endmodule | ||
|
||
module dyn_mem_d4 #( | ||
parameter WIDTH = 32, | ||
parameter D0_SIZE = 16, | ||
parameter D1_SIZE = 16, | ||
parameter D2_SIZE = 16, | ||
parameter D3_SIZE = 16, | ||
parameter D0_IDX_SIZE = 4, | ||
parameter D1_IDX_SIZE = 4, | ||
parameter D2_IDX_SIZE = 4, | ||
parameter D3_IDX_SIZE = 4 | ||
) ( | ||
// Common signals | ||
input wire logic clk, | ||
input wire logic reset, | ||
input wire logic [D0_IDX_SIZE-1:0] addr0, | ||
input wire logic [D1_IDX_SIZE-1:0] addr1, | ||
input wire logic [D2_IDX_SIZE-1:0] addr2, | ||
input wire logic [D3_IDX_SIZE-1:0] addr3, | ||
input wire logic content_en, | ||
output logic done, | ||
|
||
// Read signal | ||
output logic [WIDTH-1:0] read_data, | ||
|
||
// Write signals | ||
input wire logic write_en, | ||
input wire logic [ WIDTH-1:0] write_data | ||
); | ||
wire [D0_IDX_SIZE+D1_IDX_SIZE+D2_IDX_SIZE+D3_IDX_SIZE-1:0] addr; | ||
assign addr = addr0 * (D1_SIZE * D2_SIZE * D3_SIZE) + addr1 * (D2_SIZE * D3_SIZE) + addr2 * (D3_SIZE) + addr3; | ||
|
||
dyn_mem_d1 #(.WIDTH(WIDTH), .SIZE(D0_SIZE * D1_SIZE * D2_SIZE * D3_SIZE), .IDX_SIZE(D0_IDX_SIZE+D1_IDX_SIZE+D2_IDX_SIZE+D3_IDX_SIZE)) mem | ||
(.clk(clk), .reset(reset), .addr0(addr), | ||
.content_en(content_en), .read_data(read_data), .write_data(write_data), .write_en(write_en), | ||
.done(done)); | ||
endmodule |
Oops, something went wrong.