Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create OBI-compatible icache wrapper #5

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ sources:
# package. Files in level 1 only depend on files in level 0, files in level 2 on files in
# levels 1 and 0, etc. Files within a level are ordered alphabetically.
# Level 0
- src/axi_burst_splitter_table.sv
- src/snitch_icache_pkg.sv
- src/riscv_instr_branch.sv
- src/multi_accept_rr_arb.sv
# Level 1
- src/l0_to_bypass.sv
colluca marked this conversation as resolved.
Show resolved Hide resolved
- src/snitch_axi_to_cache.sv
- src/snitch_icache_l0.sv
- src/snitch_icache_refill.sv
Expand All @@ -37,6 +39,8 @@ sources:
# Level 3
- src/snitch_icache.sv
- src/snitch_read_only_cache.sv
# Level 4
- src/obi_icache_wrap.sv
- target: test
files:
- test/snitch_icache_l0_tb.sv
Expand Down
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added
- Add statistics signals output for shared L1.
- Add OBI-request-compatible instruction cache variant.

## 0.1.1 - 28.06.2024
### Added
Expand Down
161 changes: 161 additions & 0 deletions src/axi_burst_splitter_table.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Copyright 2021 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51

// Author: Samuel Riedel <[email protected]>
//
// Adapted from the axi_burst_splitter authored by:
// Andreas Kurth <[email protected]>
// Florian Zaruba <[email protected]>
// Wolfgang Roenninger <[email protected]>

`include "common_cells/registers.svh"
/// Stores the burst length and the corresponding address offset for the axi_to_cache module.
/// Adapted from axi_burst_splitter_counters
module axi_burst_splitter_table #(
parameter int unsigned MaxTrans = 0,
parameter int unsigned IdWidth = 0,
parameter type offset_t = logic,
parameter type id_t = logic [IdWidth-1:0]
) (
input logic clk_i,
input logic rst_ni,

input id_t alloc_id_i,
input axi_pkg::len_t alloc_len_i,
input offset_t alloc_offset_i,
input logic alloc_req_i,
output logic alloc_gnt_o,

input id_t cnt_id_i,
output axi_pkg::len_t cnt_len_o,
output offset_t cnt_offset_o,
input logic cnt_set_err_i,
output logic cnt_err_o,
input logic cnt_len_dec_i,
input logic cnt_offset_inc_i,
input logic cnt_req_i,
output logic cnt_gnt_o
);
localparam int unsigned CntIdxWidth = (MaxTrans > 1) ? $clog2(MaxTrans) : 32'd1;

typedef logic [CntIdxWidth-1:0] cnt_idx_t;
typedef logic [$bits(axi_pkg::len_t):0] cnt_t;

cnt_idx_t cnt_free_idx, cnt_r_idx;
logic [MaxTrans-1:0] cnt_len_dec, cnt_offset_inc, cnt_free, cnt_set, err_d, err_q;
cnt_t cnt_len_inp;
cnt_t [MaxTrans-1:0] cnt_len_oup;
offset_t cnt_offset_inp;
offset_t [MaxTrans-1:0] cnt_offset_oup;
for (genvar i = 0; i < MaxTrans; i++) begin : gen_cnt
counter #(
.WIDTH ( $bits(cnt_t) )
) i_cnt_len (
.clk_i,
.rst_ni,
.clear_i ( 1'b0 ),
.en_i ( cnt_len_dec[i] ),
.load_i ( cnt_set[i] ),
.down_i ( 1'b1 ),
.d_i ( cnt_len_inp ),
.q_o ( cnt_len_oup[i] ),
.overflow_o ( ) // not used
);
counter #(
.WIDTH ( $bits(offset_t) )
) i_cnt_offset (
.clk_i,
.rst_ni,
.clear_i ( 1'b0 ),
.en_i ( cnt_offset_inc[i] ),
.load_i ( cnt_set[i] ),
.down_i ( 1'b0 ),
.d_i ( cnt_offset_inp ),
.q_o ( cnt_offset_oup[i] ),
.overflow_o ( ) // not used
);
assign cnt_free[i] = (cnt_len_oup[i] == '0);
end
assign cnt_len_inp = {1'b0, alloc_len_i} + 1;
assign cnt_offset_inp = alloc_offset_i;

lzc #(
.WIDTH ( MaxTrans ),
.MODE ( 1'b0 )
) i_lzc (
.in_i ( cnt_free ),
.cnt_o ( cnt_free_idx ),
.empty_o ( )
);

logic idq_inp_req, idq_inp_gnt;
logic idq_oup_gnt, idq_oup_valid, idq_oup_pop;
id_queue #(
.ID_WIDTH ( $bits(id_t) ),
.CAPACITY ( MaxTrans ),
.FULL_BW ( 1'b1 ),
.data_t ( cnt_idx_t )
) i_idq (
.clk_i,
.rst_ni,
.inp_id_i ( alloc_id_i ),
.inp_data_i ( cnt_free_idx ),
.inp_req_i ( idq_inp_req ),
.inp_gnt_o ( idq_inp_gnt ),
.exists_data_i ( '0 ),
.exists_mask_i ( '0 ),
.exists_req_i ( 1'b0 ),
.exists_o ( /* unused */ ),
.exists_gnt_o ( /* unused */ ),
.oup_id_i ( cnt_id_i ),
.oup_pop_i ( idq_oup_pop ),
.oup_req_i ( cnt_req_i ),
.oup_data_o ( cnt_r_idx ),
.oup_data_valid_o ( idq_oup_valid ),
.oup_gnt_o ( idq_oup_gnt )
);
logic [8:0] read_len;
assign idq_inp_req = alloc_req_i & alloc_gnt_o;
assign alloc_gnt_o = idq_inp_gnt & |(cnt_free);
assign cnt_gnt_o = idq_oup_gnt & idq_oup_valid;
assign read_len = cnt_len_oup[cnt_r_idx] - 1;
assign cnt_len_o = read_len[7:0];
assign cnt_offset_o = cnt_offset_oup[cnt_r_idx];
assign idq_oup_pop = cnt_req_i & cnt_gnt_o & cnt_len_dec_i & (cnt_len_o == 8'd0);

always_comb begin
cnt_len_dec = '0;
cnt_len_dec[cnt_r_idx] = cnt_req_i & cnt_gnt_o & cnt_len_dec_i;
end
always_comb begin
cnt_offset_inc = '0;
cnt_offset_inc[cnt_r_idx] = cnt_req_i & cnt_gnt_o & cnt_offset_inc_i;
end
always_comb begin
cnt_set = '0;
cnt_set[cnt_free_idx] = alloc_req_i & alloc_gnt_o;
end
always_comb begin
err_d = err_q;
cnt_err_o = err_q[cnt_r_idx];
if (cnt_req_i && cnt_gnt_o && cnt_set_err_i) begin
err_d[cnt_r_idx] = 1'b1;
cnt_err_o = 1'b1;
end
if (alloc_req_i && alloc_gnt_o) begin
err_d[cnt_free_idx] = 1'b0;
end
end

// registers
`FF(err_q, err_d, '0, clk_i, rst_ni)

`ifndef VERILATOR
// pragma translate_off
assume property (@(posedge clk_i) idq_oup_gnt |-> idq_oup_valid)
else begin $warning("Invalid output at ID queue, read not granted!"); $finish(); end
// pragma translate_on
`endif

endmodule
152 changes: 152 additions & 0 deletions src/l0_to_bypass.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2020 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51

// Fabian Schuiki <[email protected]>
// Florian Zaruba <[email protected]>

`include "common_cells/registers.svh"

// Translate register interface to refill requests.
// Used for bypassable accesses.
module l0_to_bypass #(
parameter snitch_icache_pkg::config_t CFG = '0
) (
input logic clk_i,
input logic rst_ni,

input logic [CFG.NR_FETCH_PORTS-1:0] in_valid_i,
output logic [CFG.NR_FETCH_PORTS-1:0] in_ready_o,
input logic [CFG.NR_FETCH_PORTS-1:0][CFG.FETCH_AW-1:0] in_addr_i,
output logic [CFG.NR_FETCH_PORTS-1:0][CFG.FETCH_DW-1:0] in_data_o,
output logic [CFG.NR_FETCH_PORTS-1:0] in_error_o,

output logic [CFG.FETCH_AW-1:0] refill_req_addr_o,
output logic refill_req_bypass_o,
output logic refill_req_valid_o,
input logic refill_req_ready_i,

input logic [CFG.LINE_WIDTH-1:0] refill_rsp_data_i,
input logic refill_rsp_error_i,
input logic refill_rsp_valid_i,
output logic refill_rsp_ready_o
);

assign refill_req_bypass_o = 1'b1;

logic [CFG.NR_FETCH_PORTS-1:0] in_valid;
logic [CFG.NR_FETCH_PORTS-1:0] in_ready;

typedef enum logic [1:0] {
Idle, RequestData, WaitResponse, PresentResponse
} state_e;
state_e [CFG.NR_FETCH_PORTS-1:0] state_d , state_q;

// Mask address so that it is aligned to the cache-line width.
logic [CFG.NR_FETCH_PORTS-1:0][CFG.FETCH_AW-1:0] in_addr_masked;
for (genvar i = 0; i < CFG.NR_FETCH_PORTS; i++) begin : gen_masked_addr
assign in_addr_masked[i] = {in_addr_i[i][CFG.FETCH_AW-1:CFG.LINE_ALIGN],
{CFG.LINE_ALIGN{1'b0}}};
end
stream_arbiter #(
.DATA_T ( logic [CFG.FETCH_AW-1:0] ),
.N_INP ( CFG.NR_FETCH_PORTS )
) i_stream_arbiter (
.clk_i,
.rst_ni,
.inp_data_i ( in_addr_masked ),
.inp_valid_i ( in_valid ),
.inp_ready_o ( in_ready ),
.oup_data_o ( refill_req_addr_o ),
.oup_valid_o ( refill_req_valid_o ),
.oup_ready_i ( refill_req_ready_i )
);

localparam int unsigned NrFetchPortsBin =
CFG.NR_FETCH_PORTS == 1 ? 1 : $clog2(CFG.NR_FETCH_PORTS);

logic [CFG.NR_FETCH_PORTS-1:0] rsp_fifo_mux;
logic [NrFetchPortsBin-1:0] onehot_mux;
logic [CFG.NR_FETCH_PORTS-1:0] rsp_fifo_pop;
logic rsp_fifo_full;

logic [CFG.NR_FETCH_PORTS-1:0] rsp_valid;
logic [CFG.NR_FETCH_PORTS-1:0] rsp_ready;

fifo_v3 #(
.DATA_WIDTH ( CFG.NR_FETCH_PORTS ),
.DEPTH ( 4 )
) rsp_fifo (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( rsp_fifo_full ),
.empty_o ( ),
.usage_o ( ),
.data_i ( {in_valid & in_ready} ),
.push_i ( |{in_valid & in_ready}),
.data_o ( rsp_fifo_mux ),
.pop_i ( |rsp_fifo_pop )
);


onehot_to_bin #(
.ONEHOT_WIDTH (CFG.NR_FETCH_PORTS)
) i_onehot_to_bin (
.onehot (rsp_fifo_mux),
.bin (onehot_mux)
);

assign rsp_ready = '1;

stream_demux #(
.N_OUP ( CFG.NR_FETCH_PORTS )
) i_stream_mux_miss_refill (
.inp_valid_i ( refill_rsp_valid_i ),
.inp_ready_o ( refill_rsp_ready_o ),
.oup_sel_i ( onehot_mux ),
.oup_valid_o ( rsp_valid ),
.oup_ready_i ( rsp_ready )
);

for (genvar i = 0; i < CFG.NR_FETCH_PORTS; i++) begin : gen_bypass_request
always_comb begin
state_d[i] = state_q[i];
in_ready_o[i] = 1'b0;
rsp_fifo_pop[i] = 1'b0;
in_valid[i] = 1'b0;
unique case (state_q[i])
// latch data when idle
Idle: if (in_valid_i[i]) state_d[i] = RequestData;
RequestData: begin
// check that there is still space for the response to be accepted.
if (!rsp_fifo_full) begin
in_valid[i] = 1'b1;
if (in_ready[i]) state_d[i] = WaitResponse;
end
end
WaitResponse: begin
if (rsp_valid[i]) begin
rsp_fifo_pop[i] = 1'b1;
state_d[i] = PresentResponse;
end
end
// The response will be served from the register and is valid for one cycle.
PresentResponse: begin
state_d[i] = Idle;
in_ready_o[i] = 1'b1;
end
default:;
endcase
end
logic [CFG.FILL_DW-1:0] fill_rsp_data;
assign fill_rsp_data =
refill_rsp_data_i >> (in_addr_i[i][CFG.LINE_ALIGN-1:CFG.FETCH_ALIGN] * CFG.FETCH_DW);
`FFLNR({in_data_o[i], in_error_o[i]}, {fill_rsp_data[CFG.FETCH_DW-1:0], refill_rsp_error_i},
rsp_valid[i], clk_i)
end

`FF(state_q, state_d, '{default: Idle})

endmodule
Loading
Loading