* Switch to AXI structs

* Fix problems with ID width mismatches

* 📝 Update CHANGELOG
This commit is contained in:
Florian Zaruba 2018-10-17 16:30:58 +02:00 committed by GitHub
parent 8468544156
commit 25a0470df6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 654 additions and 284 deletions

2
.gitignore vendored
View file

@ -22,4 +22,4 @@ build/
*.vcd
*.log
*.out
work-ver/*
work-*/*

View file

@ -11,10 +11,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Commit log feature
- Support for A-Extension
- Preliminary FP support
- Provisioned `aw_top` signal for close to memory atomics
### Changed
- core_id / cluster_id inputs have been merged to hard_id input
- Three AXI ports have been merged into one
### 3.0.0

View file

@ -35,6 +35,7 @@ ariane_pkg := include/riscv_pkg.sv \
include/std_cache_pkg.sv \
src/axi/src/axi_pkg.sv \
include/axi_intf.sv \
include/ariane_axi_pkg.sv \
src/fpu/src/pkg/fpnew_pkg.vhd \
src/fpu/src/pkg/fpnew_fmts_pkg.vhd \
src/fpu/src/pkg/fpnew_comps_pkg.vhd \
@ -75,6 +76,9 @@ src := $(filter-out src/ariane_regfile.sv, $(wildcard src/*.sv)) \
src/common_cells/src/deprecated/pulp_sync.sv \
src/common_cells/src/deprecated/find_first_one.sv \
src/common_cells/src/rstgen_bypass.sv \
src/common_cells/src/stream_mux.sv \
src/common_cells/src/stream_demux.sv \
src/util/axi_connect.sv \
src/axi/src/axi_cut.sv \
src/axi/src/axi_join.sv \
src/fpga-support/rtl/SyncSpRamBeNx64.sv \
@ -82,6 +86,7 @@ src := $(filter-out src/ariane_regfile.sv, $(wildcard src/*.sv)) \
src/common_cells/src/cdc_2phase.sv \
src/common_cells/src/spill_register.sv \
src/common_cells/src/sync_wedge.sv \
src/common_cells/src/fifo_v3.sv \
src/common_cells/src/fifo_v2.sv \
src/common_cells/src/fifo_v1.sv \
src/common_cells/src/lzc.sv \

103
include/ariane_axi_pkg.sv Normal file
View file

@ -0,0 +1,103 @@
/* Copyright 2018 ETH Zurich and University of Bologna.
* Copyright and related rights are licensed under the Solderpad Hardware
* License, Version 0.51 (the License); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
* or agreed to in writing, software, hardware and materials distributed under
* this License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* File: ariane_axi_pkg.sv
* Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
* Date: 17.8.2018
*
* Description: Contains Ariane's AXI ports, does not contain user ports
*/
package ariane_axi;
// 4 is recommended by AXI standard, so lets stick to it, do not change
localparam IdWidth = 4;
localparam UserWidth = 1;
localparam AddrWidth = 64;
localparam DataWidth = 64;
localparam StrbWidth = DataWidth / 8;
typedef logic [IdWidth-1:0] id_t;
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [StrbWidth-1:0] strb_t;
typedef logic [UserWidth-1:0] user_t;
// AW Channel
typedef struct packed {
id_t id;
addr_t addr;
axi_pkg::len_t len;
axi_pkg::size_t size;
axi_pkg::burst_t burst;
logic lock;
axi_pkg::cache_t cache;
axi_pkg::prot_t prot;
axi_pkg::qos_t qos;
axi_pkg::region_t region;
axi_pkg::atop_t atop;
} aw_chan_t;
// W Channel
typedef struct packed {
data_t data;
strb_t strb;
logic last;
} w_chan_t;
// B Channel
typedef struct packed {
id_t id;
axi_pkg::resp_t resp;
} b_chan_t;
// AR Channel
typedef struct packed {
id_t id;
addr_t addr;
axi_pkg::len_t len;
axi_pkg::size_t size;
axi_pkg::burst_t burst;
logic lock;
axi_pkg::cache_t cache;
axi_pkg::prot_t prot;
axi_pkg::qos_t qos;
axi_pkg::region_t region;
} ar_chan_t;
// R Channel
typedef struct packed {
id_t id;
data_t data;
axi_pkg::resp_t resp;
logic last;
} r_chan_t;
typedef struct packed {
aw_chan_t aw;
logic aw_valid;
w_chan_t w;
logic w_valid;
logic b_ready;
ar_chan_t ar;
logic ar_valid;
logic r_ready;
} req_t;
typedef struct packed {
logic aw_ready;
logic ar_ready;
logic w_ready;
logic b_valid;
b_chan_t b;
logic r_valid;
r_chan_t r;
} resp_t;
endpackage

View file

@ -20,27 +20,23 @@ import instruction_tracer_pkg::*;
//pragma translate_on
module ariane #(
parameter logic [63:0] CACHE_START_ADDR = 64'h8000_0000, // address on which to decide whether the request is cache-able or not
parameter int unsigned AXI_ID_WIDTH = 10, // minimum 1
parameter int unsigned AXI_USER_WIDTH = 1 // minimum 1
)(
input logic clk_i,
input logic rst_ni,
// Core ID, Cluster ID and boot address are considered more or less static
input logic [63:0] boot_addr_i, // reset boot address
input logic [63:0] hart_id_i, // hart id in a multicore environment (reflected in a CSR)
// Instruction memory interface
AXI_BUS.Master instr_if,
// Data memory interface
AXI_BUS.Master data_if, // data cache refill port
AXI_BUS.Master bypass_if, // bypass axi port (disabled cache or uncacheable access)
// Interrupt inputs
input logic [1:0] irq_i, // level sensitive IR lines, mip & sip (async)
input logic ipi_i, // inter-processor interrupts (async)
// Timer facilities
input logic time_irq_i, // timer interrupt in (async)
input logic debug_req_i // debug request (async)
);
parameter logic [63:0] CACHE_START_ADDR = 64'h8000_0000 // address on which to decide whether the request is cache-able or not
)(
input logic clk_i,
input logic rst_ni,
// Core ID, Cluster ID and boot address are considered more or less static
input logic [63:0] boot_addr_i, // reset boot address
input logic [63:0] hart_id_i, // hart id in a multicore environment (reflected in a CSR)
// memory side, AXI Master
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i,
// Interrupt inputs
input logic [1:0] irq_i, // level sensitive IR lines, mip & sip (async)
input logic ipi_i, // inter-processor interrupts (async)
// Timer facilities
input logic time_irq_i, // timer interrupt in (async)
input logic debug_req_i // debug request (async)
);
// ------------------------------------------
// Global Signals
@ -564,6 +560,7 @@ module ariane #(
// to D$
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.priv_lvl_i ( priv_lvl ),
// I$
.icache_en_i ( icache_en_csr ),
.icache_flush_i ( icache_flush_ctrl_cache ),
@ -584,9 +581,8 @@ module ariane #(
.dcache_req_ports_i ( dcache_req_ports_ex_cache ),
.dcache_req_ports_o ( dcache_req_ports_cache_ex ),
// memory side
.icache_data_if ( instr_if ),
.dcache_data_if ( data_if ),
.dcache_bypass_if ( bypass_if )
.axi_req_o ( axi_req_o ),
.axi_resp_i ( axi_resp_i )
);
// -------------------

@ -1 +1 @@
Subproject commit 328cbe05a42a31aae6f57f780351a2ba22954fef
Subproject commit 50c23985e4ea063dd4aa919101ce16c4e48624ac

View file

@ -42,7 +42,8 @@ module axi_adapter #(
output logic [63:0] critical_word_o,
output logic critical_word_valid_o,
// AXI port
AXI_BUS.Master axi
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i
);
localparam BURST_SIZE = DATA_WIDTH/64-1;
localparam ADDR_INDEX = ($clog2(DATA_WIDTH/64) > 0) ? $clog2(DATA_WIDTH/64) : 1;
@ -62,50 +63,47 @@ module axi_adapter #(
always_comb begin : axi_fsm
// Default assignments
axi.aw_valid = 1'b0;
axi.aw_addr = addr_i;
axi.aw_prot = 3'b0;
axi.aw_region = 4'b0;
axi.aw_len = 8'b0;
axi.aw_size = {1'b0, size_i};
axi.aw_burst = (type_i == SINGLE_REQ) ? 2'b00 : 2'b01; // fixed size for single request and incremental transfer for everything else
axi.aw_lock = 1'b0;
axi.aw_cache = 4'b0;
axi.aw_qos = 4'b0;
axi.aw_id = id_i;
axi.aw_user = '0;
axi_req_o.aw_valid = 1'b0;
axi_req_o.aw.addr = addr_i;
axi_req_o.aw.prot = 3'b0;
axi_req_o.aw.region = 4'b0;
axi_req_o.aw.len = 8'b0;
axi_req_o.aw.size = {1'b0, size_i};
axi_req_o.aw.burst = (type_i == SINGLE_REQ) ? 2'b00 : 2'b01; // fixed size for single request and incremental transfer for everything else
axi_req_o.aw.lock = 1'b0;
axi_req_o.aw.cache = 4'b0;
axi_req_o.aw.qos = 4'b0;
axi_req_o.aw.id = id_i;
axi_req_o.aw.atop = '0; // currently not used
axi.ar_valid = 1'b0;
axi_req_o.ar_valid = 1'b0;
// in case of a single request or wrapping transfer we can simply begin at the address, if we want to request a cache-line
// with an incremental transfer we need to output the corresponding base address of the cache line
axi.ar_addr = (CRITICAL_WORD_FIRST || type_i == SINGLE_REQ) ? addr_i : { addr_i[63:DCACHE_BYTE_OFFSET], {{DCACHE_BYTE_OFFSET}{1'b0}}};
axi.ar_prot = 3'b0;
axi.ar_region = 4'b0;
axi.ar_len = 8'b0;
axi.ar_size = {1'b0, size_i}; // 8 bytes
axi.ar_burst = (type_i == SINGLE_REQ) ? 2'b00 : (CRITICAL_WORD_FIRST ? 2'b10 : 2'b01); // wrapping transfer in case of a critical word first strategy
axi.ar_lock = 1'b0;
axi.ar_cache = 4'b0;
axi.ar_qos = 4'b0;
axi.ar_id = id_i;
axi.ar_user = '0;
axi_req_o.ar.addr = (CRITICAL_WORD_FIRST || type_i == SINGLE_REQ) ? addr_i : { addr_i[63:DCACHE_BYTE_OFFSET], {{DCACHE_BYTE_OFFSET}{1'b0}}};
axi_req_o.ar.prot = 3'b0;
axi_req_o.ar.region = 4'b0;
axi_req_o.ar.len = 8'b0;
axi_req_o.ar.size = {1'b0, size_i}; // 8 bytes
axi_req_o.ar.burst = (type_i == SINGLE_REQ) ? 2'b00 : (CRITICAL_WORD_FIRST ? 2'b10 : 2'b01); // wrapping transfer in case of a critical word first strategy
axi_req_o.ar.lock = 1'b0;
axi_req_o.ar.cache = 4'b0;
axi_req_o.ar.qos = 4'b0;
axi_req_o.ar.id = id_i;
axi.w_valid = 1'b0;
axi.w_data = wdata_i[0];
axi.w_strb = be_i[0];
axi.w_user = '0;
axi.w_last = 1'b0;
axi_req_o.w_valid = 1'b0;
axi_req_o.w.data = wdata_i[0];
axi_req_o.w.strb = be_i[0];
axi_req_o.w.last = 1'b0;
axi.b_ready = 1'b0;
axi.r_ready = 1'b0;
axi_req_o.b_ready = 1'b0;
axi_req_o.r_ready = 1'b0;
gnt_o = 1'b0;
gnt_id_o = id_i;
valid_o = 1'b0;
id_o = axi.r_id;
id_o = axi_resp_i.r.id;
// rdata_o = axi.r_data;
critical_word_o = axi.r_data;
critical_word_o = axi_resp_i.r.data;
critical_word_valid_o = 1'b0;
rdata_o = cache_line_q;
@ -126,51 +124,51 @@ module axi_adapter #(
// write
if (we_i) begin
// the data is valid
axi.aw_valid = 1'b1;
axi.w_valid = 1'b1;
axi_req_o.aw_valid = 1'b1;
axi_req_o.w_valid = 1'b1;
// its a single write
if (type_i == SINGLE_REQ) begin
// only a single write so the data is already the last one
axi.w_last = 1'b1;
axi_req_o.w.last = 1'b1;
// single req can be granted here
gnt_o = axi.aw_ready & axi.w_ready;
case ({axi.aw_ready, axi.w_ready})
gnt_o = axi_resp_i.aw_ready & axi_resp_i.w_ready;
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
2'b11: state_d = WAIT_B_VALID;
2'b01: state_d = WAIT_AW_READY;
2'b10: state_d = WAIT_LAST_W_READY;
default: state_d = IDLE;
endcase
// its a request for the whole cache line
end else begin
axi.aw_len = BURST_SIZE; // number of bursts to do
axi.w_data = wdata_i[0];
axi.w_strb = be_i[0];
axi_req_o.aw.len = BURST_SIZE; // number of bursts to do
axi_req_o.w.data = wdata_i[0];
axi_req_o.w.strb = be_i[0];
if (axi.w_ready)
if (axi_resp_i.w_ready)
cnt_d = BURST_SIZE - 1;
else
cnt_d = BURST_SIZE;
case ({axi.aw_ready, axi.w_ready})
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
2'b11: state_d = WAIT_LAST_W_READY;
2'b01: state_d = WAIT_LAST_W_READY_AW_READY;
2'b10: state_d = WAIT_LAST_W_READY;
default:;
endcase
end
// read
end else begin
axi.ar_valid = 1'b1;
gnt_o = axi.ar_ready;
axi_req_o.ar_valid = 1'b1;
gnt_o = axi_resp_i.ar_ready;
if (type_i != SINGLE_REQ) begin
axi.ar_len = BURST_SIZE;
axi_req_o.ar.len = BURST_SIZE;
cnt_d = BURST_SIZE;
end
if (axi.ar_ready) begin
if (axi_resp_i.ar_ready) begin
state_d = (type_i == SINGLE_REQ) ? WAIT_R_VALID : WAIT_R_VALID_MULTIPLE;
addr_offset_d = addr_i[ADDR_INDEX-1+3:3];
end
@ -180,32 +178,33 @@ module axi_adapter #(
// ~> from single write
WAIT_AW_READY: begin
axi.aw_valid = 1'b1;
if (axi.aw_ready) begin
axi_req_o.aw_valid = 1'b1;
if (axi_resp_i.aw_ready) begin
gnt_o = 1'b1;
state_d = WAIT_B_VALID;
end
end
end
// ~> we need to wait for an aw_ready and there is at least one outstanding write
WAIT_LAST_W_READY_AW_READY: begin
axi.w_valid = 1'b1;
axi.w_last = (cnt_q == '0);
axi_req_o.w_valid = 1'b1;
axi_req_o.w.last = (cnt_q == '0);
if (type_i == SINGLE_REQ) begin
axi.w_data = wdata_i[0];
axi.w_strb = be_i[0];
axi_req_o.w.data = wdata_i[0];
axi_req_o.w.strb = be_i[0];
end else begin
axi.w_data = wdata_i[BURST_SIZE-cnt_q];
axi.w_strb = be_i[BURST_SIZE-cnt_q];
axi_req_o.w.data = wdata_i[BURST_SIZE-cnt_q];
axi_req_o.w.strb = be_i[BURST_SIZE-cnt_q];
end
axi.aw_valid = 1'b1;
axi_req_o.aw_valid = 1'b1;
// we are here because we want to write a cache line
axi.aw_len = BURST_SIZE;
axi_req_o.aw.len = BURST_SIZE;
// we got an aw_ready
case ({axi.aw_ready, axi.w_ready})
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
// we got an aw ready
2'b01: begin
// are there any outstanding transactions?
@ -233,10 +232,10 @@ module axi_adapter #(
// ~> all data has already been sent, we are only waiting for the aw_ready
WAIT_AW_READY_BURST: begin
axi.aw_valid = 1'b1;
axi.aw_len = BURST_SIZE;
axi_req_o.aw_valid = 1'b1;
axi_req_o.aw.len = BURST_SIZE;
if (axi.aw_ready) begin
if (axi_resp_i.aw_ready) begin
state_d = WAIT_B_VALID;
gnt_o = 1'b1;
end
@ -244,32 +243,32 @@ module axi_adapter #(
// ~> from write, there is an outstanding write
WAIT_LAST_W_READY: begin
axi.w_valid = 1'b1;
axi_req_o.w_valid = 1'b1;
if (type_i != SINGLE_REQ) begin
axi.w_data = wdata_i[BURST_SIZE-cnt_q];
axi.w_strb = be_i[BURST_SIZE-cnt_q];
axi_req_o.w.data = wdata_i[BURST_SIZE-cnt_q];
axi_req_o.w.strb = be_i[BURST_SIZE-cnt_q];
end
// this is the last write
if (cnt_q == '0) begin
axi.w_last = 1'b1;
if (axi.w_ready) begin
if (cnt_q == '0) begin
axi_req_o.w.last = 1'b1;
if (axi_resp_i.w_ready) begin
state_d = WAIT_B_VALID;
gnt_o = 1'b1;
end
end else if (axi.w_ready) begin
end else if (axi_resp_i.w_ready) begin
cnt_d = cnt_q - 1;
end
end
// ~> finish write transaction
WAIT_B_VALID: begin
axi.b_ready = 1'b1;
id_o = axi.b_id;
axi_req_o.b_ready = 1'b1;
id_o = axi_resp_i.b.id;
// Write is valid
if (axi.b_valid) begin
if (axi_resp_i.b_valid) begin
state_d = IDLE;
valid_o = 1'b1;
end
@ -283,35 +282,35 @@ module axi_adapter #(
index = BURST_SIZE-cnt_q;
// reads are always wrapping here
axi.r_ready = 1'b1;
axi_req_o.r_ready = 1'b1;
// this is the first read a.k.a the critical word
if (axi.r_valid) begin
if (axi_resp_i.r_valid) begin
if (CRITICAL_WORD_FIRST) begin
// this is the first word of a cacheline read, e.g.: the word which was causing the miss
if (state_q == WAIT_R_VALID_MULTIPLE && cnt_q == BURST_SIZE) begin
critical_word_valid_o = 1'b1;
critical_word_o = axi.r_data;
critical_word_o = axi_resp_i.r.data;
end
end else begin
// check if the address offset matches - then we are getting the critical word
if (index == addr_offset_q) begin
critical_word_valid_o = 1'b1;
critical_word_o = axi.r_data;
critical_word_o = axi_resp_i.r.data;
end
end
// this is the last read
if (axi.r_last) begin
id_d = axi.r_id;
if (axi_resp_i.r.last) begin
id_d = axi_resp_i.r.id;
state_d = COMPLETE_READ;
end
// save the word
if (state_q == WAIT_R_VALID_MULTIPLE) begin
cache_line_d[index] = axi.r_data;
cache_line_d[index] = axi_resp_i.r.data;
end else
cache_line_d[0] = axi.r_data;
cache_line_d[0] = axi_resp_i.r.data;
// Decrease the counter
cnt_d = cnt_q - 1;

View file

@ -19,9 +19,7 @@ import ariane_pkg::*;
import std_cache_pkg::*;
module miss_handler #(
parameter int unsigned NR_PORTS = 3,
parameter int unsigned AXI_ID_WIDTH = 10,
parameter int unsigned AXI_USER_WIDTH = 1
parameter int unsigned NR_PORTS = 3
)(
input logic clk_i,
input logic rst_ni,
@ -35,14 +33,19 @@ module miss_handler #(
output logic [NR_PORTS-1:0] bypass_gnt_o,
output logic [NR_PORTS-1:0] bypass_valid_o,
output logic [NR_PORTS-1:0][63:0] bypass_data_o,
AXI_BUS.Master bypass_if,
// AXI port
output ariane_axi::req_t axi_bypass_o,
input ariane_axi::resp_t axi_bypass_i,
// Miss handling (~> cacheline refill)
output logic [NR_PORTS-1:0] miss_gnt_o,
output logic [NR_PORTS-1:0] active_serving_o,
output logic [63:0] critical_word_o,
output logic critical_word_valid_o,
AXI_BUS.Master data_if,
output ariane_axi::req_t axi_data_o,
input ariane_axi::resp_t axi_data_i,
input logic [NR_PORTS-1:0][55:0] mshr_addr_i,
output logic [NR_PORTS-1:0] mshr_addr_matches_o,
@ -513,8 +516,8 @@ module miss_handler #(
logic valid_bypass_fsm;
logic [63:0] data_bypass_fsm;
logic [$clog2(NR_PORTS)-1:0] id_fsm_bypass;
logic [AXI_ID_WIDTH-1:0] id_bypass_fsm;
logic [AXI_ID_WIDTH-1:0] gnt_id_bypass_fsm;
logic [3:0] id_bypass_fsm;
logic [3:0] gnt_id_bypass_fsm;
arbiter #(
.NR_PORTS ( NR_PORTS ),
@ -547,35 +550,39 @@ module miss_handler #(
);
axi_adapter #(
.DATA_WIDTH ( 64 ),
.AXI_ID_WIDTH ( AXI_ID_WIDTH )
.DATA_WIDTH ( 64 ),
.AXI_ID_WIDTH ( 4 )
) i_bypass_axi_adapter (
.req_i ( req_fsm_bypass_valid ),
.type_i ( SINGLE_REQ ),
.gnt_o ( gnt_bypass_fsm ),
.addr_i ( req_fsm_bypass_addr ),
.we_i ( req_fsm_bypass_we ),
.wdata_i ( req_fsm_bypass_wdata ),
.be_i ( req_fsm_bypass_be ),
.size_i ( req_fsm_bypass_size ),
.id_i ( {{{AXI_ID_WIDTH-$clog2(NR_PORTS)}{1'b0}}, id_fsm_bypass} ),
.valid_o ( valid_bypass_fsm ),
.rdata_o ( data_bypass_fsm ),
.gnt_id_o ( gnt_id_bypass_fsm ),
.id_o ( id_bypass_fsm ),
.critical_word_o ( ), // not used for single requests
.critical_word_valid_o ( ), // not used for single requests
.axi ( bypass_if ),
.*
.clk_i,
.rst_ni,
.req_i ( req_fsm_bypass_valid ),
.type_i ( SINGLE_REQ ),
.gnt_o ( gnt_bypass_fsm ),
.addr_i ( req_fsm_bypass_addr ),
.we_i ( req_fsm_bypass_we ),
.wdata_i ( req_fsm_bypass_wdata ),
.be_i ( req_fsm_bypass_be ),
.size_i ( req_fsm_bypass_size ),
.id_i ( {2'b10, id_fsm_bypass} ),
.valid_o ( valid_bypass_fsm ),
.rdata_o ( data_bypass_fsm ),
.gnt_id_o ( gnt_id_bypass_fsm ),
.id_o ( id_bypass_fsm ),
.critical_word_o ( ), // not used for single requests
.critical_word_valid_o ( ), // not used for single requests
.axi_req_o ( axi_bypass_o ),
.axi_resp_i ( axi_bypass_i )
);
// ----------------------
// Cache Line AXI Refill
// ----------------------
axi_adapter #(
.DATA_WIDTH ( DCACHE_LINE_WIDTH ),
.AXI_ID_WIDTH ( AXI_ID_WIDTH )
.DATA_WIDTH ( DCACHE_LINE_WIDTH ),
.AXI_ID_WIDTH ( 4 )
) i_miss_axi_adapter (
.clk_i,
.rst_ni,
.req_i ( req_fsm_miss_valid ),
.type_i ( req_fsm_miss_req ),
.gnt_o ( gnt_miss_fsm ),
@ -584,13 +591,15 @@ module miss_handler #(
.wdata_i ( req_fsm_miss_wdata ),
.be_i ( req_fsm_miss_be ),
.size_i ( req_fsm_miss_size ),
.id_i ( '0 ),
.id_i ( 4'b1100 ),
.gnt_id_o ( ), // open
.valid_o ( valid_miss_fsm ),
.rdata_o ( data_miss_fsm ),
.id_o ( ),
.axi ( data_if ),
.*
.critical_word_o,
.critical_word_valid_o,
.axi_req_o ( axi_data_o ),
.axi_resp_i ( axi_data_i )
);
// -----------------
@ -631,13 +640,6 @@ module miss_handler #(
miss_req_size [i] = miss_req.size;
end
end
//pragma translate_off
initial begin
assert (AXI_ID_WIDTH >= $clog2(NR_PORTS)) else $fatal (1, "AXI ID Width needs to be larger than number of requestors");
end
//pragma translate_on
endmodule
// --------------

View file

@ -1,16 +1,12 @@
// Copyright (c) 2018 ETH Zurich, University of Bologna
// All rights reserved.
//
// This code is under development and not yet released to the public.
// Until it is released, the code is under the copyright of ETH Zurich and
// the University of Bologna, and may contain confidential and/or unpublished
// work. Any reuse/redistribution is strictly forbidden without written
// permission from ETH Zurich.
//
// Bug fixes and contributions will eventually be released under the
// SolderPad open hardware license in the context of the PULP platform
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
// University of Bologna.
// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>, ETH Zurich
// Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
@ -22,11 +18,11 @@ import ariane_pkg::*;
import std_cache_pkg::*;
module std_cache_subsystem #(
parameter int unsigned AXI_ID_WIDTH = 10,
parameter logic [63:0] CACHE_START_ADDR = 64'h4000_0000
)(
input logic clk_i,
input logic rst_ni,
input riscv::priv_lvl_t priv_lvl_i,
// I$
input logic icache_en_i, // enable icache (or bypass e.g: in debug mode)
input logic icache_flush_i, // flush the icache, flush and kill have to be asserted together
@ -50,74 +46,257 @@ module std_cache_subsystem #(
input dcache_req_i_t [2:0] dcache_req_ports_i, // to/from LSU
output dcache_req_o_t [2:0] dcache_req_ports_o, // to/from LSU
// memory side
AXI_BUS.Master icache_data_if, // I$ refill port
AXI_BUS.Master dcache_data_if, // D$ refill port
AXI_BUS.Master dcache_bypass_if // bypass axi port (disabled D$ or uncacheable access)
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i
);
std_icache #(
) i_icache (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( icache_flush_i ),
.en_i ( icache_en_i ),
.miss_o ( icache_miss_o ),
.areq_i ( icache_areq_i ),
.areq_o ( icache_areq_o ),
.dreq_i ( icache_dreq_i ),
.dreq_o ( icache_dreq_o ),
.axi ( icache_data_if )
);
ariane_axi::req_t axi_req_icache;
ariane_axi::resp_t axi_resp_icache;
ariane_axi::req_t axi_req_bypass;
ariane_axi::resp_t axi_resp_bypass;
ariane_axi::req_t axi_req_data;
ariane_axi::resp_t axi_resp_data;
std_icache i_icache (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.priv_lvl_i ( priv_lvl_i ),
.flush_i ( icache_flush_i ),
.en_i ( icache_en_i ),
.miss_o ( icache_miss_o ),
.areq_i ( icache_areq_i ),
.areq_o ( icache_areq_o ),
.dreq_i ( icache_dreq_i ),
.dreq_o ( icache_dreq_o ),
.axi_req_o ( axi_req_icache ),
.axi_resp_i ( axi_resp_icache )
);
// decreasing priority
// Port 0: PTW
// Port 1: Load Unit
// Port 2: Store Unit
std_nbdcache #(
.AXI_ID_WIDTH ( AXI_ID_WIDTH ),
.CACHE_START_ADDR ( CACHE_START_ADDR )
) i_nbdcache (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.clk_i,
.rst_ni,
.enable_i ( dcache_enable_i ),
.flush_i ( dcache_flush_i ),
.flush_ack_o ( dcache_flush_ack_o ),
.miss_o ( dcache_miss_o ),
.data_if ( dcache_data_if ),
.bypass_if ( dcache_bypass_if ),
.axi_bypass_o ( axi_req_bypass ),
.axi_bypass_i ( axi_resp_bypass ),
.axi_data_o ( axi_req_data ),
.axi_data_i ( axi_resp_data ),
.req_ports_i ( dcache_req_ports_i ),
.req_ports_o ( dcache_req_ports_o ),
.amo_req_i ( amo_req_i ),
.amo_resp_o ( amo_resp_o )
.amo_req_i,
.amo_resp_o
);
// -----------------------
// Arbitrate AXI Ports
// -----------------------
logic [1:0] aw_select, ar_select, w_select;
logic aw_arbiter_valid, ar_arbiter_valid, w_arbiter_valid;
logic aw_enable_d, aw_enable_q;
logic ar_enable_d, ar_enable_q;
logic w_enable_d, w_enable_q;
// AW Channel
rrarbiter #(
.NUM_REQ ( 3 ),
.LOCK_IN ( 1 )
) i_rrarbiter_aw (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.en_i ( aw_enable_q ),
.req_i ( {axi_req_icache.aw_valid, axi_req_bypass.aw_valid, axi_req_data.aw_valid} ),
.ack_o ( ),
.vld_o ( aw_arbiter_valid ),
.idx_o ( aw_select )
);
stream_mux #(
.DATA_T ( ariane_axi::aw_chan_t ),
.N_INP ( 3 )
) i_stream_mux_aw (
.inp_data_i ( {axi_req_icache.aw, axi_req_bypass.aw, axi_req_data.aw} ),
.inp_valid_i ( {axi_req_icache.aw_valid, axi_req_bypass.aw_valid, axi_req_data.aw_valid} ),
.inp_ready_o ( {axi_resp_icache.aw_ready, axi_resp_bypass.aw_ready, axi_resp_data.aw_ready} ),
.inp_sel_i ( aw_select ),
.oup_data_o ( axi_req_o.aw ),
.oup_valid_o ( axi_req_o.aw_valid ),
.oup_ready_i ( axi_resp_i.aw_ready )
);
// AR Channel
rrarbiter #(
.NUM_REQ ( 3 ),
.LOCK_IN ( 1 )
) i_rrarbiter_ar (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.en_i ( ar_enable_q ),
.req_i ( {axi_req_icache.ar_valid, axi_req_bypass.ar_valid, axi_req_data.ar_valid} ),
.ack_o ( ),
.vld_o ( ar_arbiter_valid ),
.idx_o ( ar_select )
);
stream_mux #(
.DATA_T ( ariane_axi::ar_chan_t ),
.N_INP ( 3 )
) i_stream_mux_ar (
.inp_data_i ( {axi_req_icache.ar, axi_req_bypass.ar, axi_req_data.ar} ),
.inp_valid_i ( {axi_req_icache.ar_valid, axi_req_bypass.ar_valid, axi_req_data.ar_valid} ),
.inp_ready_o ( {axi_resp_icache.ar_ready, axi_resp_bypass.ar_ready, axi_resp_data.ar_ready} ),
.inp_sel_i ( ar_select ),
.oup_data_o ( axi_req_o.ar ),
.oup_valid_o ( axi_req_o.ar_valid ),
.oup_ready_i ( axi_resp_i.ar_ready )
);
// W Channel
rrarbiter #(
.NUM_REQ ( 3 ),
.LOCK_IN ( 1 )
) i_rrarbiter_w (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.en_i ( w_enable_q ),
.req_i ( {axi_req_icache.w_valid, axi_req_bypass.w_valid, axi_req_data.w_valid} ),
.ack_o ( ),
.vld_o ( w_arbiter_valid ),
.idx_o ( w_select )
);
stream_mux #(
.DATA_T ( ariane_axi::w_chan_t ),
.N_INP ( 3 )
) i_stream_mux_w (
.inp_data_i ( {axi_req_icache.w, axi_req_bypass.w, axi_req_data.w} ),
.inp_valid_i ( {axi_req_icache.w_valid, axi_req_bypass.w_valid, axi_req_data.w_valid} ),
.inp_ready_o ( {axi_resp_icache.w_ready, axi_resp_bypass.w_ready, axi_resp_data.w_ready} ),
.inp_sel_i ( w_select ),
.oup_data_o ( axi_req_o.w ),
.oup_valid_o ( axi_req_o.w_valid ),
.oup_ready_i ( axi_resp_i.w_ready )
);
always_comb begin
aw_enable_d = aw_enable_q;
ar_enable_d = ar_enable_q;
w_enable_d = w_enable_q;
// freeze the arbiter
if (aw_arbiter_valid) aw_enable_d = 1'b0;
if (ar_arbiter_valid) ar_enable_d = 1'b0;
if (w_arbiter_valid) w_enable_d = 1'b0;
// we've got a valid transaction, free the arbitration decision
if (axi_resp_i.aw_ready && axi_req_o.aw_valid) aw_enable_d = 1'b1;
if (axi_resp_i.ar_ready && axi_req_o.ar_valid) ar_enable_d = 1'b1;
if (axi_resp_i.w_ready && axi_req_o.w_valid) w_enable_d = 1'b1;
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
aw_enable_q <= 1'b1;
ar_enable_q <= 1'b1;
w_enable_q <= 1'b1;
end else begin
aw_enable_q <= aw_enable_d;
ar_enable_q <= ar_enable_d;
w_enable_q <= w_enable_d;
end
end
// Route responses based on ID
// 0000 -> I$
// 10[00|10|01|11] -> Bypass
// 1111 -> D$
// R Channel
assign axi_resp_icache.r = axi_resp_i.r;
assign axi_resp_bypass.r = axi_resp_i.r;
assign axi_resp_data.r = axi_resp_i.r;
logic [1:0] r_select;
always_comb begin
r_select = 0;
unique case (axi_resp_i.r.id)
4'b0000: r_select = 2; // icache
4'b1111: r_select = 0; // dcache
4'b1000, 4'b1001, 4'b1010, 4'b1011: r_select = 1; // bypass
default: r_select = 0;
endcase
end
stream_demux #(
.N_OUP ( 3 )
) i_stream_demux_r (
.inp_valid_i ( axi_resp_i.r_valid ),
.inp_ready_o ( axi_req_o.r_ready ),
.oup_sel_i ( r_select ),
.oup_valid_o ( {axi_resp_icache.r_valid, axi_resp_bypass.r_valid, axi_resp_data.r_valid} ),
.oup_ready_i ( {axi_req_icache.r_ready, axi_req_bypass.r_ready, axi_req_data.r_ready} )
);
// B Channel
logic [1:0] b_select;
assign axi_resp_icache.b = axi_resp_i.b;
assign axi_resp_bypass.b = axi_resp_i.b;
assign axi_resp_data.b = axi_resp_i.b;
always_comb begin
b_select = 0;
unique case (axi_resp_i.b.id)
4'b0000: b_select = 2; // icache
4'b1111: b_select = 0; // dcache
4'b1000, 4'b1001, 4'b1010, 4'b1011: b_select = 1; // bypass
default: b_select = 0;
endcase
end
stream_demux #(
.N_OUP ( 3 )
) i_stream_demux_b (
.inp_valid_i ( axi_resp_i.b_valid ),
.inp_ready_o ( axi_req_o.b_ready ),
.oup_sel_i ( b_select ),
.oup_valid_o ( {axi_resp_icache.b_valid, axi_resp_bypass.b_valid, axi_resp_data.b_valid} ),
.oup_ready_i ( {axi_req_icache.b_ready, axi_req_bypass.b_ready, axi_req_data.b_ready} )
);
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
`ifndef VERILATOR
a_invalid_instruction_fetch: assert property (
@(posedge clk_i) disable iff (~rst_ni) icache_dreq_o.valid |-> (|icache_dreq_o.data) !== 1'hX)
else $warning(1,"[l1 dcache] reading invalid instructions: vaddr=%08X, data=%08X",
@(posedge clk_i) disable iff (~rst_ni) icache_dreq_o.valid |-> (|icache_dreq_o.data) !== 1'hX)
else $warning(1,"[l1 dcache] reading invalid instructions: vaddr=%08X, data=%08X",
icache_dreq_o.vaddr, icache_dreq_o.data);
a_invalid_write_data: assert property (
@(posedge clk_i) disable iff (~rst_ni) dcache_req_ports_i[2].data_req |-> |dcache_req_ports_i[2].data_be |-> (|dcache_req_ports_i[2].data_wdata) !== 1'hX)
else $warning(1,"[l1 dcache] writing invalid data: paddr=%016X, be=%02X, data=%016X",
@(posedge clk_i) disable iff (~rst_ni) dcache_req_ports_i[2].data_req |-> |dcache_req_ports_i[2].data_be |-> (|dcache_req_ports_i[2].data_wdata) !== 1'hX)
else $warning(1,"[l1 dcache] writing invalid data: paddr=%016X, be=%02X, data=%016X",
{dcache_req_ports_i[2].address_tag, dcache_req_ports_i[2].address_index}, dcache_req_ports_i[2].data_be, dcache_req_ports_i[2].data_wdata);
generate
generate
for(genvar j=0; j<2; j++) begin
a_invalid_read_data: assert property (
@(posedge clk_i) disable iff (~rst_ni) dcache_req_ports_o[j].data_rvalid |-> (|dcache_req_ports_o[j].data_rdata) !== 1'hX)
else $warning(1,"[l1 dcache] reading invalid data on port %01d: data=%016X",
@(posedge clk_i) disable iff (~rst_ni) dcache_req_ports_o[j].data_rvalid |-> (|dcache_req_ports_o[j].data_rdata) !== 1'hX)
else $warning(1,"[l1 dcache] reading invalid data on port %01d: data=%016X",
j, dcache_req_ports_o[j].data_rdata);
end
endgenerate
endgenerate
`endif
//pragma translate_on

View file

@ -16,10 +16,10 @@
import ariane_pkg::*;
import std_cache_pkg::*;
module std_icache #(
)(
module std_icache (
input logic clk_i,
input logic rst_ni,
input riscv::priv_lvl_t priv_lvl_i,
input logic flush_i, // flush the icache, flush and kill have to be asserted together
input logic en_i, // enable icache
@ -30,8 +30,9 @@ module std_icache #(
// data requests
input icache_dreq_i_t dreq_i,
output icache_dreq_o_t dreq_o,
// refill port
AXI_BUS.Master axi
// AXI refill port
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i
);
localparam int unsigned ICACHE_BYTE_OFFSET = $clog2(ICACHE_LINE_WIDTH/8); // 3
@ -134,42 +135,40 @@ module std_icache #(
dreq_o.data |= cl_sel[i];
end
// ------------------
// AXI Plumbing
// ------------------
assign axi.aw_valid = '0;
assign axi.aw_addr = '0;
assign axi.aw_prot = '0;
assign axi.aw_region = '0;
assign axi.aw_len = '0;
assign axi.aw_size = 3'b000;
assign axi.aw_burst = 2'b00;
assign axi.aw_lock = '0;
assign axi.aw_cache = '0;
assign axi.aw_qos = '0;
assign axi.aw_id = '0;
assign axi.aw_user = '0;
// instruction cache is read-only
assign axi_req_o.aw_valid = '0;
assign axi_req_o.aw.addr = '0;
assign axi_req_o.aw.prot = '0;
assign axi_req_o.aw.region = '0;
assign axi_req_o.aw.len = '0;
assign axi_req_o.aw.size = 3'b000;
assign axi_req_o.aw.burst = 2'b00;
assign axi_req_o.aw.lock = '0;
assign axi_req_o.aw.cache = '0;
assign axi_req_o.aw.qos = '0;
assign axi_req_o.aw.id = '0;
assign axi_req_o.aw.atop = '0;
assign axi_req_o.w_valid = '0;
assign axi_req_o.w.data = '0;
assign axi_req_o.w.strb = '0;
assign axi_req_o.w.last = 1'b0;
assign axi_req_o.b_ready = 1'b0;
assign axi.w_valid = '0;
assign axi.w_data = '0;
assign axi.w_strb = '0;
assign axi.w_user = '0;
assign axi.w_last = 1'b0;
assign axi.b_ready = 1'b0;
// set protection flag, MSB -> instruction fetch, LSB -> privileged access or not
assign axi_req_o.ar.prot = {1'b1, 1'b0, (priv_lvl_i == riscv::PRIV_LVL_M)};
assign axi_req_o.ar.region = '0;
assign axi_req_o.ar.len = (2**NR_AXI_REFILLS) - 1;
assign axi_req_o.ar.size = 3'b011;
assign axi_req_o.ar.burst = 2'b01;
assign axi_req_o.ar.lock = '0;
assign axi_req_o.ar.cache = '0;
assign axi_req_o.ar.qos = '0;
assign axi_req_o.ar.id = '0;
assign axi.ar_prot = '0;
assign axi.ar_region = '0;
assign axi.ar_len = (2**NR_AXI_REFILLS) - 1;
assign axi.ar_size = 3'b011;
assign axi.ar_burst = 2'b01;
assign axi.ar_lock = '0;
assign axi.ar_cache = '0;
assign axi.ar_qos = '0;
assign axi.ar_id = '0;
assign axi.ar_user = '0;
assign axi.r_ready = 1'b1;
assign axi_req_o.r_ready = 1'b1;
assign data_be = be;
assign data_wdata = wdata;
@ -178,7 +177,6 @@ module std_icache #(
assign addr = (state_q==FLUSH) ? cnt_q : vaddr_d[ICACHE_INDEX_WIDTH-1:ICACHE_BYTE_OFFSET];
// ------------------
// Cache Ctrl
// ------------------
@ -208,8 +206,8 @@ module std_icache #(
update_lfsr = 1'b0;
miss_o = 1'b0;
axi.ar_valid = 1'b0;
axi.ar_addr = '0;
axi_req_o.ar_valid = 1'b0;
axi_req_o.ar.addr = '0;
areq_o.fetch_req = 1'b0;
areq_o.fetch_vaddr = vaddr_q;
@ -319,15 +317,15 @@ module std_icache #(
end
// ~> request a cache-line refill
REFILL, WAIT_KILLED_REFILL: begin
axi.ar_valid = 1'b1;
axi.ar_addr[ICACHE_INDEX_WIDTH+ICACHE_TAG_WIDTH-1:0] = {tag_q, vaddr_q[ICACHE_INDEX_WIDTH-1:ICACHE_BYTE_OFFSET], {ICACHE_BYTE_OFFSET{1'b0}}};
axi_req_o.ar_valid = 1'b1;
axi_req_o.ar.addr[ICACHE_INDEX_WIDTH+ICACHE_TAG_WIDTH-1:0] = {tag_q, vaddr_q[ICACHE_INDEX_WIDTH-1:ICACHE_BYTE_OFFSET], {ICACHE_BYTE_OFFSET{1'b0}}};
burst_cnt_d = '0;
if (dreq_i.kill_s2)
state_d = WAIT_KILLED_REFILL;
// we need to finish this AXI transfer
if (axi.ar_ready)
if (axi_resp_i.ar_ready)
state_d = (dreq_i.kill_s2 || (state_q == WAIT_KILLED_REFILL)) ? WAIT_KILLED_AXI_R_RESP : WAIT_AXI_R_RESP;
end
// ~> wait for the read response
@ -336,11 +334,11 @@ module std_icache #(
req = evict_way_q;
vld_req = evict_way_q;
if (axi.r_valid) begin
if (axi_resp_i.r_valid) begin
we = 1'b1;
tag_wdata.tag = tag_q;
tag_wdata.valid = 1'b1;
wdata[burst_cnt_q] = axi.r_data;
wdata[burst_cnt_q] = axi_resp_i.r.data;
// enable the right write path
be[burst_cnt_q] = '1;
// increase burst count
@ -350,11 +348,11 @@ module std_icache #(
if (dreq_i.kill_s2)
state_d = WAIT_KILLED_AXI_R_RESP;
if (axi.r_valid && axi.r_last) begin
if (axi_resp_i.r_valid && axi_resp_i.r.last) begin
state_d = (dreq_i.kill_s2) ? IDLE : REDO_REQ;
end
if ((state_q == WAIT_KILLED_AXI_R_RESP) && axi.r_last && axi.r_valid)
if ((state_q == WAIT_KILLED_AXI_R_RESP) && axi_resp_i.r.last && axi_resp_i.r_valid)
state_d = IDLE;
end
// ~> redo the request,
@ -450,7 +448,7 @@ module std_icache #(
//pragma translate_off
`ifndef VERILATOR
initial begin
assert ($bits(axi.aw_addr) == 64)
assert ($bits(axi_req_o.aw.addr) == 64)
else $fatal(1, "[icache] Ariane needs a 64-bit bus");
end

View file

@ -16,7 +16,6 @@ import ariane_pkg::*;
import std_cache_pkg::*;
module std_nbdcache #(
parameter int unsigned AXI_ID_WIDTH = 10,
parameter logic [63:0] CACHE_START_ADDR = 64'h8000_0000
)(
input logic clk_i, // Clock
@ -33,8 +32,10 @@ module std_nbdcache #(
input dcache_req_i_t [2:0] req_ports_i, // request ports
output dcache_req_o_t [2:0] req_ports_o, // request ports
// Cache AXI refill port
AXI_BUS.Master data_if,
AXI_BUS.Master bypass_if
output ariane_axi::req_t axi_data_o,
input ariane_axi::resp_t axi_data_i,
output ariane_axi::req_t axi_bypass_o,
input ariane_axi::resp_t axi_bypass_i
);
// -------------------------------
@ -126,14 +127,13 @@ module std_nbdcache #(
// Miss Handling Unit
// ------------------
miss_handler #(
.AXI_ID_WIDTH ( AXI_ID_WIDTH ),
.NR_PORTS ( 3 )
) i_miss_handler (
.flush_i ( flush_i ),
.busy_i ( |busy ),
// AMOs
.amo_req_i ( amo_req_i ),
.amo_resp_o ( amo_resp_o ),
.amo_req_i ( amo_req_i ),
.amo_resp_o ( amo_resp_o ),
.miss_req_i ( miss_req ),
.miss_gnt_o ( miss_gnt ),
.bypass_gnt_o ( bypass_gnt ),
@ -151,8 +151,10 @@ module std_nbdcache #(
.be_o ( be [0] ),
.data_o ( wdata [0] ),
.we_o ( we [0] ),
.bypass_if,
.data_if,
.axi_bypass_o,
.axi_bypass_i,
.axi_data_o,
.axi_data_i,
.*
);
@ -252,7 +254,7 @@ module std_nbdcache #(
//pragma translate_off
initial begin
assert ($bits(data_if.aw_addr) == 64) else $fatal(1, "Ariane needs a 64-bit bus");
assert ($bits(axi_data_o.aw.addr) == 64) else $fatal(1, "Ariane needs a 64-bit bus");
assert (DCACHE_LINE_WIDTH/64 inside {2, 4, 8, 16}) else $fatal(1, "Cache line size needs to be a power of two multiple of 64");
end
//pragma translate_on

@ -1 +1 @@
Subproject commit 21a060d2c2c75173312b82cc72db96a2c62e66c5
Subproject commit 3b18d9ce9889fb140cd39c8d7c86b3a6cc68872e

View file

@ -20,8 +20,9 @@ module dm_sba (
input logic clk_i, // Clock
input logic rst_ni,
input logic dmactive_i, // synchronous reset active low
AXI_BUS.Master axi_master,
// AXI port
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i,
input logic [63:0] sbaddress_i,
input logic sbaddress_write_valid_i,
@ -150,7 +151,8 @@ module dm_sba (
.id_o ( ),
.critical_word_o ( ), // not needed here
.critical_word_valid_o ( ), // not needed here
.axi ( axi_master )
.axi_req_o,
.axi_resp_i
);

View file

@ -33,7 +33,10 @@ module dm_top #(
input logic [NrHarts-1:0] unavailable_i, // communicate whether the hart is unavailable (e.g.: power down)
AXI_BUS.Slave axi_slave, // bus slave, for an execution based technique
AXI_BUS.Master axi_master, // bus master, for system bus accesses
// bus master, for system bus accesses
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i,
// Connection to DTM - compatible to RocketChip Debug Module
input logic dmi_rst_ni,
input logic dmi_req_valid_i,
@ -145,7 +148,8 @@ module dm_top #(
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.dmactive_i ( dmactive_o ),
.axi_master,
.axi_req_o,
.axi_resp_i,
.sbaddress_i ( sbaddress_csrs_sba ),
.sbaddress_o ( sbaddress_sba_csrs ),
.sbaddress_write_valid_i ( sbaddress_write_valid ),

@ -1 +1 @@
Subproject commit 20cccce06886ef21f263e63dbb38b008ff87419e
Subproject commit a3ba269c0fc6cfcee6f81e5d9af018a08e479d2b

67
src/util/axi_connect.sv Normal file
View file

@ -0,0 +1,67 @@
// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Description: Connects SV AXI interface to structs used by Ariane
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
module axi_connect (
input ariane_axi::req_t axi_req_i,
output ariane_axi::resp_t axi_resp_o,
AXI_BUS.out master
);
assign master.aw_id = axi_req_i.aw.id;
assign master.aw_addr = axi_req_i.aw.addr;
assign master.aw_len = axi_req_i.aw.len;
assign master.aw_size = axi_req_i.aw.size;
assign master.aw_burst = axi_req_i.aw.burst;
assign master.aw_lock = axi_req_i.aw.lock;
assign master.aw_cache = axi_req_i.aw.cache;
assign master.aw_prot = axi_req_i.aw.prot;
assign master.aw_qos = axi_req_i.aw.qos;
assign master.aw_region = axi_req_i.aw.region;
assign master.aw_user = '0;
assign master.aw_valid = axi_req_i.aw_valid;
assign axi_resp_o.aw_ready = master.aw_ready;
assign master.w_data = axi_req_i.w.data;
assign master.w_strb = axi_req_i.w.strb;
assign master.w_last = axi_req_i.w.last;
assign master.w_user = '0;
assign master.w_valid = axi_req_i.w_valid;
assign axi_resp_o.w_ready = master.w_ready;
assign axi_resp_o.b.id = master.b_id;
assign axi_resp_o.b.resp = master.b_resp;
assign axi_resp_o.b_valid = master.b_valid;
assign master.b_ready = axi_req_i.b_ready;
assign master.ar_id = axi_req_i.ar.id;
assign master.ar_addr = axi_req_i.ar.addr;
assign master.ar_len = axi_req_i.ar.len;
assign master.ar_size = axi_req_i.ar.size;
assign master.ar_burst = axi_req_i.ar.burst;
assign master.ar_lock = axi_req_i.ar.lock;
assign master.ar_cache = axi_req_i.ar.cache;
assign master.ar_prot = axi_req_i.ar.prot;
assign master.ar_qos = axi_req_i.ar.qos;
assign master.ar_region = axi_req_i.ar.region;
assign master.ar_user = '0;
assign master.ar_valid = axi_req_i.ar_valid;
assign axi_resp_o.ar_ready = master.ar_ready;
assign axi_resp_o.r.id = master.r_id;
assign axi_resp_o.r.data = master.r_data;
assign axi_resp_o.r.resp = master.r_resp;
assign axi_resp_o.r.last = master.r_last;
assign axi_resp_o.r_valid = master.r_valid;
assign master.r_ready = axi_req_i.r_ready;
endmodule

View file

@ -13,15 +13,15 @@
// Date: 15.08.2018
// Description: SRAM wrapper for FPGA (requires the fpga-support submodule)
//
// Note: the wrapped module contains two different implementations for
// ALTERA and XILINX tools, since these follow different coding styles for
// inferrable RAMS with byte enable. define `FPGA_TARGET_XILINX or
// Note: the wrapped module contains two different implementations for
// ALTERA and XILINX tools, since these follow different coding styles for
// inferrable RAMS with byte enable. define `FPGA_TARGET_XILINX or
// `FPGA_TARGET_ALTERA in your build environment (default is ALTERA)
module sram #(
parameter DATA_WIDTH = 64,
parameter NUM_WORDS = 1024,
parameter OUT_REGS = 0 // enables output registers in FPGA macro (read lat = 2)
parameter OUT_REGS = 0 // enables output registers in FPGA macro (read lat = 2)
)(
input logic clk_i,
input logic rst_ni,
@ -52,23 +52,24 @@ end
genvar k;
generate
for (k = 0; k<(DATA_WIDTH+63)/64; k++) begin
for (k = 0; k<(DATA_WIDTH+63)/64; k++) begin
// unused byte-enable segments (8bits) are culled by the tool
SyncSpRamBeNx64 #(
.ADDR_WIDTH($clog2(NUM_WORDS)),
.DATA_DEPTH(NUM_WORDS),
.OUT_REGS (0)
.DATA_DEPTH(NUM_WORDS),
.OUT_REGS (0),
.SIM_INIT (2)
) i_ram (
.Clk_CI ( clk_i ),
.Rst_RBI ( rst_ni ),
.CSel_SI ( req_i ),
.WrEn_SI ( we_i ),
.BEn_SI ( be_aligned[k*8 +: 8] ),
.WrData_DI ( wdata_aligned[k*64 +: 64] ),
.Addr_DI ( addr_i ),
.WrData_DI ( wdata_aligned[k*64 +: 64] ),
.Addr_DI ( addr_i ),
.RdData_DO ( rdata_aligned[k*64 +: 64] )
);
end
);
end
endgenerate
endmodule : sram

View file

@ -15,15 +15,15 @@
module ariane_testharness #(
parameter logic [63:0] CACHE_START_ADDR = 64'h8000_0000, // address on which to decide whether the request is cache-able or not
parameter int unsigned AXI_ID_WIDTH = 10,
parameter int unsigned AXI_ID_WIDTH = 4,
parameter int unsigned AXI_USER_WIDTH = 1,
parameter int unsigned AXI_ADDRESS_WIDTH = 64,
parameter int unsigned AXI_DATA_WIDTH = 64,
parameter int unsigned NUM_WORDS = 2**24 // memory size
)(
input logic clk_i,
input logic rst_ni,
output logic [31:0] exit_o
input logic clk_i,
input logic rst_ni,
output logic [31:0] exit_o
);
// disable test-enable
@ -70,7 +70,7 @@ module ariane_testharness #(
assign test_en = 1'b0;
assign ndmreset_n = ~ndmreset ;
localparam NB_SLAVE = 4;
localparam NB_SLAVE = 2;
localparam NB_MASTER = 4;
localparam AXI_ID_WIDTH_SLAVES = AXI_ID_WIDTH + $clog2(NB_SLAVE);
@ -166,6 +166,9 @@ module ariane_testharness #(
.exit ( dmi_exit )
);
ariane_axi::req_t axi_sba_req;
ariane_axi::resp_t axi_sba_resp;
// debug module
dm_top #(
// current implementation only supports 1 hart
@ -182,8 +185,9 @@ module ariane_testharness #(
.dmactive_o ( ), // active debug session
.debug_req_o ( debug_req_core ),
.unavailable_i ( '0 ),
.axi_master ( slave[3] ),
.axi_slave ( master[3] ),
.axi_req_o ( axi_sba_req ),
.axi_resp_i ( axi_sba_resp ),
.dmi_rst_ni ( rst_ni ),
.dmi_req_valid_i ( debug_req_valid ),
.dmi_req_ready_o ( debug_req_ready ),
@ -193,6 +197,9 @@ module ariane_testharness #(
.dmi_resp_o ( debug_resp )
);
axi_connect i_axi_connect_sba (.axi_req_i(axi_sba_req), .axi_resp_o(axi_sba_resp), .master(slave[1]));
// ---------------
// ROM
// ---------------
@ -311,10 +318,11 @@ module ariane_testharness #(
// ---------------
// Core
// ---------------
ariane_axi::req_t axi_ariane_req;
ariane_axi::resp_t axi_ariane_resp;
ariane #(
.CACHE_START_ADDR ( CACHE_START_ADDR ),
.AXI_ID_WIDTH ( AXI_ID_WIDTH ),
.AXI_USER_WIDTH ( AXI_USER_WIDTH )
.CACHE_START_ADDR ( CACHE_START_ADDR )
) i_ariane (
.clk_i ( clk_i ),
.rst_ni ( ndmreset_n ),
@ -324,10 +332,12 @@ module ariane_testharness #(
.ipi_i ( ipi ),
.time_irq_i ( timer_irq ),
.debug_req_i ( debug_req_core ),
.data_if ( slave[2] ),
.bypass_if ( slave[1] ),
.instr_if ( slave[0] )
.axi_req_o ( axi_ariane_req ),
.axi_resp_i ( axi_ariane_resp )
);
axi_connect i_axi_connect_ariane (.axi_req_i(axi_ariane_req), .axi_resp_o(axi_ariane_resp), .master(slave[0]));
endmodule